Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
OSUblake

Using NPM and Webpack to import TweenLite, ScrollToPlugin, Draggable, ES6

Recommended Posts

OUTDATED!

 

These webpack issues are getting out of hand, so I made a little demo to help you get started.

 

But first, have you considered not importing GSAP? I would suggest using a CDN instead as your app will load much faster since it's not included in the bundle and can be pulled directly from the user's browser cache. GSAP is available on both cdnjs and jsDelivr. JsDelivr is kind of unique in that you can bundle all your files up in a single HTTP request!

 

And dependencies that are not resolved by webpack can still become dependencies of the output by using the externals option. For example, if you are using React you could do this using the unpkg CDN.

<script src="https://unpkg.com/react@15.3.1/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.3.1/dist/react-dom.js"></script>

And then in your webpack.config, you would configure those libraries like this...

module.exports = {

  externals: {
    "react": "React",
    "react-dom": "ReactDOM"
  },
  ...
};

If that's not an option for you, here's the repo I made. To run it, install webpack and the dev-server first npm install webpack webpack-dev-server -g, and then npm install to install the demo. Use npm start to start the dev-server on port 8080. If you're wondering where my gsap import is, there isn't one. I made TweenMax global using the ProvidePlugin.

 

 

https://github.com/OSUblake/gsap-webpack

  • Like 6
Link to post
Share on other sites

Thanks! There are so many issues created by webpack that you'd think GSAP is broken, but it's not. Webpack is just very unintuitive. It made as much sense to me as the source code to Apollo 11 guidance computer the first couple of times I used it. I eventually had to take an online course to fully understand it.

 

But most of these problems wouldn't exist if people would just a CDN for most of their dependencies. I don't get why everybody is so hellbent on importing a bunch of small modules. If you think you're somehow reducing the file size or optimizing your code, you might want to take a look at the code it generates. 

 

This is a must read, revealing the true cost of modularizing your code.

https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/

 

 

  • Like 6
Link to post
Share on other sites

Is there a way of using CDN's without having access to the webpack.config file? I'm using facebook's create-react-app.

Link to post
Share on other sites

I'm not familiar with create-react-app, but my understanding is that it should be entirely possible to just use <script> tags that point to CDN files for GSAP in your app. I didn't think that required tweaks to the webpack config file, but I'm sure there are others here who have more experience with that and could confirm. Have you tried just using a <script> tag? 

  • Like 1
Link to post
Share on other sites

Create React App always allows the option to eject (https://github.com/facebookincubator/create-react-app#converting-to-a-custom-setup) and then you can customise the webpack config as much as desired. 

 

If you are hesitant to eject, you can simply modify public/index.html, this is used as the basis for the page. When you run npm start or npm build (for production) you will notice that the index.html (and any changes you make) are included in the builds (including adding refs to cdns)

  • Like 3
Link to post
Share on other sites

Cjke, thanks for the reply but I don't think that is right. When Webpack bundles all the js it will look for all the dependencies. If a dependency is included via a CDN it can't 'see it' and will throw an error. I think the only way to get around it is via the webpack.config file where you can say 'ignore this dependency' as it is included via a CDN and will work during runtime.

Link to post
Share on other sites

Thanks Blake!

 

Here is an update to your GitHub package.json and the webpack.config.js to make them compatible with Webpack 2

 

package.json:

{
  "version": "1.0.0",
  "name": "gsap-webpack",
  "private": true,
  "scripts": {
    "start": "NODE_ENV=development&& webpack-dev-server --inline"
  },
  "devDependencies": {
    "autoprefixer": "^6.7.7",
    "babel-core": "^6.24.0",
    "babel-loader": "^7.0.0-beta.1",
    "babel-preset-es2015": "^6.24.0",
    "css-loader": "^0.28.0",
    "html-loader": "^0.4.3",
    "node-sass": "^4.5.2",
    "sass-loader": "^6.0.3",
    "style-loader": "^0.17.0",
    "webpack": "^2.3.3",
    "webpack-dev-server": "^2.4.2"
  },
  "dependencies": {
    "gsap": "^1.19.1"
  }
}

webpack.config.js


var path = require("path");
var webpack = require("webpack");

var root = __dirname;

var gsapPath = "/node_modules/gsap/src/uncompressed/";

module.exports = {
  watch: true,
  context: path.resolve(root, "app"),
  entry: "./app.js",

  output: {
    path: "/build/js/",
    publicPath: "/wwwroot/js/",
    filename: "main.js"
  },

  devServer: {
    contentBase: "wwwroot"
  },

  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },
      {
        test: /\.html$/,
        exclude: /node_modules/,
        loader: "html-loader"
      },
      {
        test: /\.css$/,
        exclude: /node_modules/,
        loader: "style-loader!css-loader!autoprefixer-loader"
      },
      {
        test: /\.scss$/,
        exclude: /node_modules/,
        loader: "style-loader!css-loader!autoprefixer-loader!sass-loader"
      }
    ]
  },

  plugins: [
    new webpack.ProvidePlugin({
      TweenMax: "gsap"
    })
  ],

  resolve: {

    extensions: [".js"],

    alias: {
      "TweenLite": "gsap",
      "CSSPlugin": "gsap",
      "Draggable": path.join(root, gsapPath + "utils/Draggable.js"),
      "ScrollToPlugin": path.join(root, gsapPath + "plugins/ScrollToPlugin.js")
    }
  }
};
  • Like 2
Link to post
Share on other sites

The config in this post is updated (as of 2018-01-09):

  • Fixed Autoprefixer setup. Production Build now Autoprefixes the CSS properly.
  •  

- removed unused babel presets and update the babel.rc file

- dev build (and server) would get "out of memory" with prolonged use. Solved by updating the package.jason script with

--max_old_space_size=2048

- added GitRevisionPlugin

 

Production Build processes all links correctly: 

  • path aliases sorted (for the most part)
    • vue-loader issue has a workaround
    •  I have a vue-loader/or webpack path alias issue filed with Juho and Evan hopefully I will know an answer soon.
  • programmatic image src links now processed and hashed correctly.
  • programmatically called modals and modal-slideshow src links now processed and hashed correctly.

 

UPDATED - ExtractTextPlugin path is sorted. The CSS is now output to the assets/css directory. All of CSS path aliases are resolved. A shout out to Juho (one of the webpack contributors for helping me sort this check out. Checkout his free online webpack book, see SurviveJS link below).

 

Here is a link to a great step-by-step Webpack 2 book (it's a free online version - Note the author is selling epub PDF etc. and sharing the money with the developer of Webpack) https://survivejs.com/webpack/foreword/

 

Here is a link to a very thorough Webpack 2 Video Tutorial by Emil Oberg:  https://www.youtube.com/watch?v=eWmkBNBTbMM

 

My config below is a hybrid between Emil's approach, SurviveJS's approach and a Laracast tip with splitting vendor and app JS into separate files.

 

Here is my complete webpack.config.js file, it may help others:

// DeprecationWarning: loaderUtils.parseQuery() received a non-string value
// which can be problematic, see https://github.com/webpack/loader-utils/issues/56
// parseQuery() will be replaced with getOptions() in the next major version of loader-utils.

// Caused by babel-loader v6.4.1 stable
// Updated to babel-loader 7.x see Babel Loader Documentation https://github.com/babel/babel-loader
// process.traceDeprecation = true;

// removing Jquery from project/ webpack config, the project dependencies -- Also, in eslintrc.js, jquery is now set to false.

var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var HTMLWebpackPlugin = require('html-webpack-plugin');
var PreloadWebpackPlugin = require('preload-webpack-plugin');
var ResourceHintWebpackPlugin = require('resource-hints-webpack-plugin');
var FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
// var DashboardPlugin = require('webpack-dashboard/plugin');
var PrerenderSpaPlugin = require('prerender-spa-plugin');
var CopyWebpackPlugin = require('copy-webpack-plugin');  
var GitRevisionPlugin = require('git-revision-webpack-plugin');
let FaviconsWebpackPlugin = require('favicons-webpack-plugin');

const DEVELOPMENT = process.env.NODE_ENV === 'development';
const PRODUCTION = process.env.NODE_ENV === 'production';

const build = PRODUCTION
  ? [
    './assets/main.js', // updated to reflect production build output
  ]
  : [
    './assets/main.js', // updated to reflect production build output
    'webpack/hot/dev-server',
    'webpack-dev-server/client?http://localhost:8080',
  ];


const plugins = PRODUCTION
  ?   [
    new webpack.BannerPlugin({
      banner: new GitRevisionPlugin().version(),
    }),
    new webpack.optimize.UglifyJsPlugin(),
    new ExtractTextPlugin({
      filename: 'assets/css/styles-[contenthash:6].css', // this will put the css in asset/css directoy.
                                                         // You also need to resolve the path in the
                                                         // ExtractTextPlugin.extract see const cssLoader below
      allChunks: true
    }),
    new HTMLWebpackPlugin({
      template: 'index-template.html'
    }),
    new PreloadWebpackPlugin({
      // For settings see - https://github.com/googlechrome/preload-webpack-plugin
      // Below is the default setting
      rel: 'preload',
      as: 'script',
      include: 'asyncChunks',
    }),
      // For setting see - https://github.com/jantimon/favicons-webpack-plugin
    new FaviconsWebpackPlugin({
      logo: './assets/img/ui-elements/favicon-hainis.png',
      inject: true, // Inject the html into the html-webpack-plugin
      persistentCache: true,  // Generate a cache file with control hashes and
                              // don't rebuild the favicons until those hashes change

      // which icons should be generated (see https://github.com/haydenbleasel/favicons#usage)
      icons: {
        android: false, // Set to true for Production
        appleIcon: false, // Set to true for Production
        appleStartup: false, // Set to true for Production
        coast: false,
        favicons: true,
        firefox: false, // Set to true for Production
        opengraph: false,
        twitter: false,
        yandex: false,
        windows: false,
      },
    }),
    new ResourceHintWebpackPlugin(), // Add only once see documentation
    // new DashboardPlugin(), // if you want a fancy dashboard in terminal 
                              // uncomment the DashboardPlugin line and the global var
                              // Also, see the package.json file to modify the "build" script
    new webpack.optimize.CommonsChunkPlugin({
          names: ['vendor']
    }), // split vendor library from app code
    new FriendlyErrorsWebpackPlugin(),
    new CopyWebpackPlugin([
        // need to copy sprite sheets to assets folder since the links can't be hashed in tha canvas call
        { from: 'assets/img/sprite-sheets', to: 'assets/img/sprite-sheets' }
    ]),
    new webpack.ProvidePlugin({
      // axios: "axios", // don't define here and define in the component - it's one OR the other
       /* $: "jquery",
        jQuery: "jquery" */ // removing jquery from project
    }),
  ]
  : [
    new webpack.HotModuleReplacementPlugin(), // disabled Vue-JS has HMR built in.
    // new DashboardPlugin(), // if you want a fancy dashboard in terminal 
                              // uncomment the DashboardPlugin line and the global var
                              // Also, see the package.json file to modify the "dev" script
    // FaviconsWebpackPlugin only rendered during production build
    new FriendlyErrorsWebpackPlugin(),
    new webpack.ProvidePlugin({
      // axios: "axios", // don't define here and define in the component - it's one OR the other
       /* $: "jquery",
        jQuery: "jquery" */ // removing jquery from project
    }),
  ];


plugins.push(
  new webpack.DefinePlugin({
    DEVELOPMENT: JSON.stringify(DEVELOPMENT),
    PRODUCTION: JSON.stringify(PRODUCTION),
  })
);


const imageLoaderQuery = {
  bypassOnDebug: true,
  mozjpeg: {
    progressive: true,
  },
  gifsicle: {
    interlaced: false,
    optimizationLevel: 3,
    colors: 256,
    buffer: false, // 'buffer' // set boolean if you wan to buffer.
  },
  optipng: {
    optimizationLevel: 4,
  },
  svgo:{
    plugins: [
      {
        removeViewBox: false,
        removeEmptyAttrs: false,
      },
    ],
  },
  pngquant: {
    quality: '75-90',
    speed: 3,
  },
}; // END imageLoaderQuery

const cssIdentifier = PRODUCTION ? '[hash:base64:6]' : '[path][name]---[local]';

const projectMap = PRODUCTION ? '#source-map' : '#eval-source-map';

const cssLoader = PRODUCTION
  ? ExtractTextPlugin.extract({
    publicPath: '../../', // since the ExtractTextPlugin is saving 
                          // the CSS file to assets/css directory (see const plugins above) 
                          // you need to resolve the publicPath to take this into account.
    use: ['css-loader?minimize&localIdentName=' + cssIdentifier, 
          {
            loader: 'postcss-loader',
            options: {
            config: {
                path: 'postcss.config.js'
              } // END config
            } // END options
          }
         ],
  })
  :   ['style-loader', 'css-loader?localIdentName=' + cssIdentifier, 
        {
          loader: 'postcss-loader',
          options: {
            config: {
              path: 'postcss.config.js'
            } // END config
          } // END options
        }
      ];


module.exports = {
  devtool: projectMap,
  entry: { build, vendor: ['vue', /* 'jquery',  'axios',*/ 'gsap/TweenMax', 'gsap/ScrollToPlugin'] },
  resolve: {
    // IMPORTANT - keep in mind that path values are relative to the file you are writing in
    // Need to research how to create an alias to the root of the build or dist folder

    alias: { // chnaged dev folder structure to match production output - updated path aliases to reflect change - all files link properly in dev and prod
      'vue$': 'vue/dist/vue.esm.js',
      modernizr$: path.resolve(__dirname, "./.modernizrrc"),
      assets: path.resolve(__dirname, './assets'),
      components: path.resolve(__dirname, './assets/components'),
      css: path.resolve(__dirname, './assets/css'),
      fonts: path.resolve(__dirname, './assets/fonts'),
      img: path.resolve(__dirname, './assets/img'),
      js: path.resolve(__dirname, './assets/js'),
      modals: path.resolve(__dirname, './assets/modals'),
      scss: path.resolve(__dirname, './assets/scss'),
      sections: path.resolve(__dirname, './assets/sections'),
    },
  },
  plugins: plugins,
  module: {
    rules: [{
      enforce: 'pre', // with this eslint will not process files done by babel-loader
      test: /\.(vue|js)$/, // /\.js$/,
      loader: 'eslint-loader',
      exclude: /node_modules/,
      options: {
        emitWarning: true,
        // community formatter
        formatter: require('eslint-friendly-formatter'),
        // cache: DEVELOPMENT,        
        fix: true, // Set to true for eslint autofixing
        // quite: true, // Process report errors only and ignore warnings
      },
    }, {
        test: /\.vue$/,
        loader: 'vue-loader', // Used for Vue Templates. Also Hot Module Replacement only works with .vue files
        options: {
          loaders: {
          }
          // other vue-loader options go here
        }
      }, {
        test: /\.modernizrrc.js$/,
        use: [ 'modernizr-loader' ]
      }, {
        test: /\.modernizrrc(\.json)?$/,
        use: [ 'modernizr-loader', 'json-loader' ]
      }, {
        test: /\.json$/,
        loader: 'json-loader', // Used for Vue Templates. Also Hot Module Replacement only works with .vue files
        options: {
          loaders: {
          }
        }
      }, {
      // Conditions
      test: /\.js$/,
      // include: PATHS.app,
      exclude: /node_modules/,

      // Actions
      use: {
        loader: 'babel-loader',
        options: {
          // IMPORTANT - Need to set presets in .babelrc for Vue-Loader to work
          // If not it will throw an an error - Unexpected EOF at line 15 column 1 of the JSON5 data. Still to read: ""
          babelrc: true,
          cacheDirectory: true,
          presets: [ 
            // ['latest', {
            //   'es2015': { 'modules': false }
            // }]
          ],
        },
      },
    }, {
      test: /\.(jpe?g|png|gif)$/i,
      use: [
        'url-loader?limit=10000&name=assets/img/[name]-[hash:6].[ext]',
        'image-webpack-loader?${JSON.stringify(imageLoaderQuery)}',
      ],
    }, {
      test: /\.(svg)$/i,
      use: [
        'file-loader?name=assets/img/[name]-[hash:6].[ext]',
        'image-webpack-loader?${JSON.stringify(imageLoaderQuery)}',
      ],
      exclude: /fonts/,
    }, {  
      test: /\.svg$/, 
      use: 'url-loader?limit=10000&mimetype=image/svg+xml&name=assets/fonts/[name].[ext]',
    }, {  
      test: /\.woff$/, 
      use: 'url-loader?limit=10000&mimetype=application/font-woff&name=assets/fonts/[name].[ext]', 
    }, {  
      test: /\.woff2$/, 
      use: 'url-loader?limit=10000&mimetype=application/font-woff2&name=assets/fonts/[name].[ext]', 
    }, {  
      test: /\.otf$/, 
      use: 'url-loader?limit=10000&mimetype=font/opentype&name=assets/fonts/[name].[ext]', 
    }, {  
      test: /\.ttf$/, 
      use: 'url-loader?limit=10000&mimetype=font/ttf&name=assets/fonts/[name].[ext]', 
    }, {  
      test: /\.eot$/, 
      use: 'file-loader?mimetype=application/vnd.ms-fontobject&name=assets/fonts/[name].[ext]', 
    },  {
      test: /\.(css|scss)$/,
      use: cssLoader,
    }], // END rules
  },
  output: {
    path: path.join(__dirname, './dist'),
    publicPath: PRODUCTION ? './' : '/dist/', // changed from '/'
    filename: PRODUCTION ? '[name].min.js' : '[name].js',
  },
}; // END module.exports


Here is the package.json file:

{
  "name": "hainis-vue-js-main",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "browserslist": [
    "last 5 versions",
    "> 1%",
    "ie > 9"
  ],
  "//": [
    "COMMENT - NOTE: If you want a fancy dashboard in the terminal add 'webpack-dashboard -- ' at the beginning of the desired script below",
    "Also, uncomment the DashboardPlugin var and plugin within the webpack config file"
  ],
  "scripts": {
    "preinstall": "mkdir node_modules && touch node_modules/.metadata_never_index",
    "build": "rimraf dist && mkdir dist && touch dist/.metadata_never_index && cross-env NODE_ENV=production webpack --env production --progress --hide-modules",
    "dev": "nodemon --watch webpack.config.js --watch dev-server.js --exec \"cross-env NODE_ENV=development node --max_old_space_size=2048 dev-server.js --env development\"",
    "lint:js": "eslint src/ webpack.*.js --cache --ignore-path .gitignore file.js --format 'node_modules/eslint-friendly-formatter' -- --fix || true",
    "lint:vue": "eslint src/ webpack.*.js --ext=js,vue --cache --ignore-path .gitignore file.js --format 'node_modules/eslint-friendly-formatter' -- --fix || true"
  },
  "events": {
    "restart": "osascript -e 'display notification \"app restarted\" with title \"nodemon\"'"
  },
  "keywords": [
    "webpack",
    "tutorial",
    "uppsalajs",
    "meetup"
  ],
  "author": "Jim Hainis",
  "license": "ISC",
  "devDependencies": {
    "autoprefixer": "^7.1.1",
    "babel-core": "^6.25.0",
    "babel-loader": "^7.1.1",
    "babel-preset-latest": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "copy-webpack-plugin": "^4.0.1",
    "cross-env": "^5.0.1",
    "css-loader": "^0.28.4",
    "eslint": "^4.1.1",
    "eslint-friendly-formatter": "^3.0.0",
    "eslint-loader": "^1.8.0",
    "eslint-plugin-html": "^3.0.0",
    "eslint-plugin-vue": "^3.5.0",
    "extract-text-webpack-plugin": "^2.1.2",
    "favicons-webpack-plugin": "0.0.7",
    "file-loader": "^0.11.2",
    "friendly-errors-webpack-plugin": "^1.6.1",
    "git-revision-webpack-plugin": "^2.5.1",
    "html-webpack-plugin": "^2.29.0",
    "http-server": "^0.10.0",
    "image-webpack-loader": "^3.3.1",
    "json-loader": "^0.5.7",
    "modernizr": "^3.5.0",
    "modernizr-loader": "^1.0.1",
    "multipage-webpack-plugin": "^0.3.0",
    "nodemon": "^1.11.0",
    "postcss-loader": "^2.0.6",
    "preload-webpack-plugin": "^1.2.2",
    "prerender-spa-plugin": "^2.0.1",
    "resource-hints-webpack-plugin": "0.0.1",
    "rimraf": "^2.6.1",
    "style-loader": "^0.18.2",
    "url-loader": "^0.5.9",
    "vue-loader": "^13.0.2",
    "vue-router": "^2.7.0",
    "vue-template-compiler": "^2.3.4",
    "webpack": "^3.0.0",
    "webpack-dashboard": "^0.4.0",
    "webpack-dev-server": "^2.5.0"
  },
  "dependencies": {
    "axios": "^0.16.2",
    "gsap": "^1.20.2",
    "vue": "^2.3.4"
  }
}

Here is the .eslintrc file (needs to be at the root level along with the webpack config):

module.exports = {
  env: {
    browser: true,
    commonjs: true,
    es6: true,
    node: true,
  },
  plugins: [
        "html"
  ],
  extends: 'eslint:recommended',
  parserOptions: {
    sourceType: 'module',
  },
  rules: {
    'comma-dangle': ['error', 'always-multiline'],
    indent: ['error', 2],
    'linebreak-style': ['error', 'unix'],
    quotes: ['error', 'single'],
    semi: ['error', 'always'],
    'no-unused-vars': ['warn'],
    'no-console': 0,
  },
};

Here is the .gitignore file (keep at the root level along with the webpack config):

# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so

# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip

# Logs and databases #
######################
*.log
*.sql
*.sqlite

# OS generated files #
######################
#OSX Mac
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
# Windows image file caches
ehthumbs.db
Thumbs.db
# Recycle Bin used on file shares
$RECYCLE.BIN/


# Images #
##########
# Best practice is to keep small files 
# that dont't change often under normal 
# Git version control.
# For Large File Storage see Git LFS - https://git-lfs.github.com/
# http://softwareengineering.stackexchange.com/questions/80962/should-images-be-stored-in-a-git-repository/80966
# *.jpg
# *.jpeg
# *.png
# *.gif
# *.psd
# *.ai
# *.svg
# *.eps
# *.tif

# Project Files and Folders #
#############################

# Sublime Text
*.sublime-project
*.sublime-workspace

# dependencies
/node_modules
/bower_components

# testing
coverage

# production
/build
/dist

# misc
.env
npm-debug.log
/*.log
*.orig

# Webpack Items #
#################
.bundle/
/z_backups
/z_code_chunks
*.bundle
/host
/port
/webpack_port
/webpack_livereload
/localhost.* 

Here is the .babelrc file (keep at the root level along with the webpack config):

/* *********************************************************

IMPORTANT - Need to set presets in this .babelrc file NOT in the 
			Webpack Config for Vue-Loader to work.
			If not it will throw an an error:
			Unexpected EOF at line 15 column 1 of the JSON5 data. Still to read: ""

NOTE: removed "babel-preset-es2015": "^6.24.0", 
from package.jason since "babel-preset-latest": "^6.24.1" it includes:
 - es2015, 
 - es2016 
 - es2017
 - see https://babeljs.io/docs/plugins/preset-latest/

Also "babel-preset-stage-0": "^6.22.0" includes:
 - preset-stage-1
 - preset-stage-2
 - preset-stage-3
 - see https://babeljs.io/docs/plugins/preset-stage-0/

********************************************************* */

{
  "presets": [
    ["latest", { "es2015": { "modules": false } }],
    ["stage-0"]
  ]
}

Here is the postcss.config.js file (keep at the root level with webpack config):

module.exports = {
  plugins: [
    require('autoprefixer')({ 
    /* options */

    /*

    	browsers (array): list of browsers query (like last 2 versions), which are supported in your project. We recommend to use browserslist config or 	browserslist key in package.json, rather than this option to share browsers with other tools. See Browserslist docs for available queries and default value.
	env (string): environment for Browserslist.
	cascade (boolean): should Autoprefixer use Visual Cascade, if CSS is uncompressed. Default: true
	add (boolean): should Autoprefixer add prefixes. Default is true.
	remove (boolean): should Autoprefixer [remove outdated] prefixes. Default is true.
	supports (boolean): should Autoprefixer add prefixes for @supports parameters. Default is true.
	flexbox (boolean|string): should Autoprefixer add prefixes for flexbox properties. With "no-2009" value Autoprefixer will add prefixes only for final and IE versions of specification. Default is true.
	grid (boolean): should Autoprefixer add IE prefixes for Grid Layout properties. Default is false.
	stats (object): custom usage statistics for > 10% in my stats browsers query.
	
	*/

	}) // END options
  ]
};

I hope that helps some others out, thanks in advance to anyone who helps with the path aliases.

 

All the best

  • Like 2
Link to post
Share on other sites

Well, the above config works great for the dev build but the assets are not loading properly with the production build. Also, image assets that are dynamically added are not being processed. I have to do a bit more research. When I figure it out I will update the config for everyone.

 

Off the top of my head I'm thinking it has to do with the path aliases, I may need to setup a conditional ternary for the path aliases as well.

 

This is on the back burner for now since the dev build along with hot reloading and the dev server etc. are all working properly. 

 

If by chance anyone knows the fix please reach out and I will make the appropriate changes.

 

Thanks in advance to anyone who helps.

Link to post
Share on other sites

Thanks for the help.

 

Yes, but the path aliases are Not broken out this way (at least that is what I am thinking).

 

early on the config:

 

const DEVELOPMENT = process.env.NODE_ENV === 'development';
const PRODUCTION = process.env.NODE_ENV === 'production';

const build = PRODUCTION
  ? [
    './src/main.js',
  ]
  : [
    './src/main.js',
    'webpack/hot/dev-server',
    'webpack-dev-server/client?http://localhost:8080',
  ];

 

I got the main.js script to load properly by changing the output module from this:

publicPath: PRODUCTION ? '/' : '/dist/',

 to this:

publicPath: PRODUCTION ? '' : '/dist/',

 

I'm thinking I need to set a ternary for the aliases, something like this (I tried but it didn't work):

 

const pathalias = PRODUCTION
  ? [
      'vue$': 'vue/dist/vue.esm.js',
      src: path.resolve(__dirname, './assets'),
     // the other paths... now resolve to './assets'
  ]
  : [
      'vue$': 'vue/dist/vue.esm.js',
      src: path.resolve(__dirname, './src'),
      // the original paths that reolve to './src'
    
  ];

 

I tried pushing putting the const pathalias into alias, I also tried pushing a string

// Tried passing in the object with just the const 
// Also tried with a stringify push
  pathalias.push(
  new webpack.DefinePlugin({
    DEVELOPMENT: JSON.stringify(DEVELOPMENT),
    PRODUCTION: JSON.stringify(PRODUCTION),
  })
);

// in the module export
alias: {
     
      pathalias

    },

they both get the same error:

src: path.resolve(__dirname, 'assets'),
   ^
SyntaxError: Unexpected token :

 

 

Link to post
Share on other sites

Just quickly looking at the code you posted, here are some of the problems...

 

// You have an array with object syntax
const pathalias = PRODUCTION
  ? [
      'vue$': 'vue/dist/vue.esm.js',
      src: path.resolve(__dirname, './assets'),
     // the other paths... now resolve to './assets'
  ]
  : [
      'vue$': 'vue/dist/vue.esm.js',
      src: path.resolve(__dirname, './src'),
      // the original paths that reolve to './src'
    
  ];

// So it should be something like this
const pathalias = PRODUCTION
  ? {
      'vue$': 'vue/dist/vue.esm.js',
      src: path.resolve(__dirname, './assets'),
     // the other paths... now resolve to './assets'
  }
  : {
      'vue$': 'vue/dist/vue.esm.js',
      src: path.resolve(__dirname, './src'),
      // the original paths that reolve to './src'
    
  };

 

 

// DEVELOPMENT and PRODUCTION are booleans
// const DEVELOPMENT = process.env.NODE_ENV === 'development';
// const PRODUCTION = process.env.NODE_ENV === 'production';
new webpack.DefinePlugin({
 DEVELOPMENT: JSON.stringify(DEVELOPMENT),
 PRODUCTION: JSON.stringify(PRODUCTION),
})

// So it's evaluating to something like this
new webpack.DefinePlugin({
 DEVELOPMENT: "true",
 PRODUCTION: "false",
})

 

// Assuming pathalias is an object, this is putting one level down
alias: {     
  pathalias
}

// So it should be should be like this
alias: pathalias

 

If want to copy an object over to another object, you can use Object.assign(). It works just like jQuery's $.extend() method.

 

But forgot all that. The best way to solve this is to have 2 separate config files, one for development and one for production, and use the environment variable to determine which file to load. 

 

  • Like 2
Link to post
Share on other sites

Thanks Blake,

 

I was thinking about separating the configs.

 

I just tried the following (it works in that things start to compile but it throws errors Can't resolve 'path/for/each/alias'

alias: {
      'vue$': 'vue/dist/vue.esm.js',
      src: PRODUCTION ? path.resolve(__dirname, './assets') : path.resolve(__dirname, './src'),
      components: PRODUCTION ? path.resolve(__dirname, './assets/components') : path.resolve(__dirname, './src/assets/components'),
      css: PRODUCTION ? path.resolve(__dirname, './assets/css') : path.resolve(__dirname, './src/assets/css'),
      fonts: PRODUCTION ? path.resolve(__dirname, './assets/fonts') : path.resolve(__dirname, './src/assets/fonts'),
      img: PRODUCTION ? path.resolve(__dirname, './assets/img') : path.resolve(__dirname, './src/assets/img'),
      js: PRODUCTION ? path.resolve(__dirname, './assets/js') : path.resolve(__dirname, './src/assets/js'),
      modals: PRODUCTION ? path.resolve(__dirname, './assets/modals') : path.resolve(__dirname, './src/assets/modals'),
      scss: PRODUCTION ? path.resolve(__dirname, './assets/scss') : path.resolve(__dirname, './src/assets/scss'),
          },

 

 

That said, maybe I'm thinking about path aliases in the wrong way for the production build.

I was thinking the alias resolve for both the source to process and the source to view become one in the same. With the above ternary the development build loads  properly but the production throws the error. It tries to compile... but it's trying to find a source that doesn't exist yet - so my logic is wrong. The alias are good for the source but the paths are wrong form when things need to link back/view in the production/dist build.

 

Scratching my head...picking into the code now to see what path is being attached... 

 

Even if I split the config I need to get my head wrapped around how to resolve the path alias for each environment.

 

I will check back in later... I'm taking my wife out for lunch and a movie... need to break off... before she breaks me...

 

Thanks for responding, on a Sunday of all days!

 

 

Link to post
Share on other sites

This post no longer has the correct information - see the updated config and the GitHub repo history for full details.

Guardians of the Galaxy was decent.. cool, soundtrack... 

 

I solved the first half of the problem, path aliases.

 

Second issue: all images called by a dynamic import path are not processed - copy-webpack-plugin may be the solution

 

Here is the breakdown (for anyone interested):

 

There was no need for a conditional ternary for the path aliases.

 

Looking at the production build the file path is getting an extra 'assets/css' added to the the beginning of the path name... only thing in my config that is set with this was/is the ExtractTextPlugin.

 

In an effort to keep the build folder organized I specified an output path for the file like so:

new ExtractTextPlugin({
      filename: 'assets/css/styles-[contenthash:6].css',
    }),

 

The above does indeed output the css files to the assets/css folder - everything is nice and tidy...but it also prepends all the paths in the CSS file. So the path was coming up assets/css/assets/img/filename.png etc.

 

By changing the output path to just the file name, all of the images/resource files link properly for both dev and production build.  So code this works:

new ExtractTextPlugin({
      filename: 'styles-[contenthash:6].css',
    }),

 

but my styles are outputted to the root folder now... off to see if I can figure out how to keep the file in the css folder.  Also,  I need to solve the second part which is that all images called by a dynamic import path are not processed. I will probably use copy-webpack-plugin to solve the second issue.

 

I will update the Config post above when all is done.

 

SOLVED second issue with copy-webpack-plugin and changing the dev folder structure to match the build output.

 

Updating the original Config above... when I find a CSS ExtraxctTextPlugin solution I will update again for now EVERYTHING WORKS.

 

Sorry for the brain dump but this may help others who are learning.

 

Link to post
Share on other sites

Updated the config to sort having the css file output to the assets/css folder, thanks to Juho (one of the webpack contributors).

All dynamically loaded components and all programmatically created links and src files are processed and hashed correctly - see the update full config earlier in this thread/topic.

 

Okay, one last tweak, I am looking to sort:

 

How to get webpack and vue-loader (Juho thinks its a vue-loader issue) to process the images and hash resolve the links and image names for caching in dynamically loaded vue components. I will cross post this in a Vue GSAP thread I started as well (see below).

 

Right now the above config is using Copy Webpack Plugin to copy over all of the images in the source folder. This gets the images I need but it doen't take advantage of hashtags and caching. Also, currently I have all the images being copied which results in duplicate files (the ones that webpack did process with hashes and the originals being copied over via the Copy Webpack Plugin).

 

I can sort the duplicate issue by making a subdirectory called 'dynamic-img' in the image folder then resolving all the paths in the code to the new folder. Finally setting Copy Webpack Plugin to only copy this directory. I will test and update the config for everyone. 

 

Very odd behavior with Copy Webpack Plugin I need to research more...

 

When I create the subdirectory it copies the files like I outlined above (lined through), but then it breaks the links and hashed files that are copied into the image folder. If i keep it the Copy Webpack Plugin settings to copy to { from: assets/img, to: assets/img } then all the links for all images work if I set it to just copy files from the subdirectory example { from: 'assets/img/dynamic-img', to: 'assets/img/dynamic-img' } then the links are broken.

 

Here is a github repo (note it is set to Copy Webpack Plugin is set to assets/img which works but it is a hack):

https://github.com/jh-thank-you/webpack-w-gsap

 

I know this is not a specific GSAP deal but a lot of people using GSAP are using webpack so I hope what I am doing is useful. 

 

Thanks for reading/any help.

 

 

Link to post
Share on other sites

SOLVED

Okay so npm scripting was the way to go... (http://www.marcusoft.net/2015/08/pre-and-post-hooks-for-npm-scripting.html also see https://docs.npmjs.com/misc/scripts) changed the package.json file added a "preinstall" script and then modified the production "build" script.

 

  • Changed the package.json scripts so that ".metadata_never_index" file is added to the "node_modules" folder on npm install. Also updated the "build" script to add  ".metadata_never_index" as well.
  • ".metadata_never_index" this tells Mac OSX Spotlight application to NOT index the parent folder that contains the ".metadata_never_index" file.
  • You can use the following terminal script to add  ".metadata_never_index" to any "nodes_modules" and "bower_modules" located in the target directory - replace "/path/to/projects" with the path to the directory you wish the script to search through.

    Note: if the folders were already indexed by Spotlight adding ".metadata_never_index" to the root of the folder will not delete the previous index file; for that you will need to target the directory manually through Spotlight's Preference Pane/Privacy Settings (see attached photo). You will need need to add the folder to the privacy settings then close out of system preferences for it to take effect. This will remove the index file for that directory... you can then go back into the preference pane and remove the folder from the privacy list - now that the ".metadata_never_index" exists at the folder's root level Spotlight will know NOT to index it... this way you won't have a bunch of individual "node_modules" folders in your privacy list. Moving forward make sure to include the scripts provided above before you npm install otherwise Spotlight will index it automatically.
     
  • find /path/to/projects -type d \( -name "node_modules" -o -name "bower_modules" \) -exec touch "{}/.metadata_never_index" \;

     

"scripts": {
    "preinstall": "mkdir node_modules && touch node_modules/.metadata_never_index",
    "build": "rimraf dist && mkdir dist && touch dist/.metadata_never_index && cross-env NODE_ENV=production webpack --env production --progress --hide-modules",
    "dev": "nodemon --watch webpack.config.js --watch dev-server.js --exec \"cross-env NODE_ENV=development node --max_old_space_size=2048 dev-server.js --env development\"",
    "lint:js": "eslint src/ webpack.*.js --cache --ignore-path .gitignore file.js --format 'node_modules/eslint-friendly-formatter' -- --fix || true"
  },

See the full config here: 

 

 

I'm looking to make one more tweak to the config.

 

Has anyone setup their config to automatically create a ".metadata_never_index" in the node_modules and dist folder? If so could you share the config so I can see how to set this up?

 

Here is a link to a terminal command for Mac OSX to add this file after the folders exist:
https://apple.stackexchange.com/questions/247019/prevent-spotlight-from-indexing-folders-with-a-certain-name
here is the terminal command from that post:

find /path/to/projects -type d \( -name "node_modules" -o -name "bower_modules" \) -exec touch "{}/.metadata_never_index" \;

 

Feature Request for npm: npm/npm#15346
Ember-CLI discussion: ember-cli/ember-cli#2338

 

Juho (webpack contributor)  suggested a "git hook"... I've never set this up before... googling around now for info... any help/guidance or a good link/how-to would be very much appreciated.


All the best.

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×