webpack 4.0.0-alpha.5 feedback

Complete changelog for 4.0.0-alpha.5 compared to version 3:

Items changed from 4.0.0-alpha.4 to 4.0.0-alpha.5 are prefixed with [new]

Big changes

  • Environment
    • Node.js 4 is no longer supported. Source Code was upgraded to a higher ecmascript version.
  • Usage
    • You have to choose (mode or --mode) between two modes now: production or development
      • production enables all kind of optimizations to generate optimized bundles
      • development enables comments and hint for development and enables the eval devtool
        • WIP: addition hints in development mode
      • production doesn’t support watching, development is optimized for fast incremental rebuilds
      • production also enables module concatenating (Scope Hoisting)
      • You can configure this in detail with the flags in optimization.* (build your custom mode)
      • process.env.NODE_ENV are set to production or development (only in built code, not in config)
      • There is a hidden none mode which disables everything
  • Syntax
    • import() always returns a namespace object. CommonJS modules are wrapped into the default export
      • This probably breaks your code, if you used to import CommonJs with import()
  • Configuration
    • You no longer need to use these plugins:
      • NoEmitOnErrorsPlugin -> optimization.noEmitOnErrors (on by default in production mode)
      • ModuleConcatenationPlugin -> optimization.concatenateModules (on by default in production mode)
      • NamedModulesPlugin -> optimization.namedModules (on by default in develoment mode)
    • [new] CommonsChunkPlugin was removed -> optimization.splitChunks
  • JSON
    • webpack now handles JSON natively
      • You may need to add type: "javascript/auto" when transforming JSON via loader to JS
      • Just using JSON without loader should still work
    • allows to import JSON via ESM syntax
      • unused exports elimination for JSON modules
  • Optimization
    • Upgrade uglifyjs-webpack-plugin to v1
      • ES15 support

Big features

  • Modules
    • webpack now supports these module types:
      • javascript/auto: (The default one in webpack 3) Javascript module with all module systems enabled: CommonJS, AMD, ESM
      • javascript/esm: EcmaScript modules, all other module system are not available
      • javascript/dynamic: Only CommonJS and, EcmaScript modules are not available
      • json: JSON data, it’s available via require and import
      • webassembly/experimental: WebAssembly modules (currently experimental)
    • javascript/esm handles ESM more strict compared to javascript/auto:
      • Imported names need to exist on imported module
      • Non-ESM can only imported via default import, everything else (including namespace import) emit errors
    • In .mjs modules
      • javascript/esm is used
    • WebAssembly modules
      • can import other modules (JS and WASM)
      • Exports from WebAssembly modules are validated by ESM import
        • You’ll get a warning/error when trying to import a non-existing export from WASM
  • Optimization
    • sideEffects: false is now supported in package.json
    • Instead of a JSONP function a JSONP array is used -> async support
    • [new] New optimization.splitChunks option was introduced
      Details: https://gist.github.com/sokra/1522d586b8e5c0f5072d7565c2bee693
    • [new] Dead branches are now removed by webpack itself
      • Before: Uglify removed the dead code
      • Now: webpack removes the dead code (in some cases)
      • This prevents crashing when import() occur in a dead branch
  • Syntax
    • webpackInclude and webpackExclude are supported by the magic comment for import(). They allow to filter files when using a dynamic expression.
    • [new] Using System.import() now emits a warning
      • You can disable the warning with Rule.parser.system: true
      • You can disable System.import with Rule.parser.system: false
  • Configuration
    • Resolving can now be configured with module.rules[].resolve. It’s merged with the global configuration.
    • optimization.minimize has been added to switch minimizing on/off
      • By default: on in production mode, off in development mode
    • optimization.minimizer has been added to configurate minimizers and options
    • many config options which support placeholders now also support a function
  • Usage
    • Some Plugin options are now validated
      • WIP: Better output, no process exit, stack trace, more plugins
    • CLI has been move to webpack-cli, you need to install webpack-cli to use the CLI
    • The ProgressPlugin (--progress) now displays plugin names
      • At least for plugins migrated to the new plugin system
  • Performance
    • UglifyJs now caches and parallizes by default
    • Multiple performance improvements, especially for faster incremental rebuilds
    • performance improvement for RemoveParentModulesPlugin
  • Stats
    • Stats can display modules nested in concatenated modules

Features

  • Configuration
    • Module type is automatically choosen for mjs, json and wasm extensions. Other extensions need to be configured via module.rules[].type
    • Incorrect options.dependencies configurations now throw error
    • Rule.ident is now allowed by schema
    • sideEffects can be overriden via module.rules
    • output.hashFunction can now be a Constructor to a custom hash function
      • You can provide a non-cryto hash function for performance reasons
    • add output.globalObject config option to allow to choose the global object reference in runtime exitCode
  • Runtime
    • Error for chunk loading now includes more information and two new properties type and request.
  • Performance
    • webpacks AST can be passed directly from loader to webpack to avoid extra parsing
    • Unused modules are no longer unnecessarly concatenated
    • [new] Add a ProfilingPlugin which write a (Chrome) profile file which includes timings of plugins
    • [new] Migrate to using for of instead of forEach
    • [new] Migrate to using Map and Set instead of Objects
    • [new] Migrate to using includes instead of indexOf
    • [new] Replaced some RegExp with string methods
    • [new] Queue don’t enqueues the same job twice
  • Optimization
    • When using more than 25 exports mangled export names are shorter.
    • script tags are no longer text/javascript and async as this are the default values (saves a few bytes)
    • The concatenated module now generates a bit less code
    • constant replacements now don’t need __webpack_require__ and argument is omitted
  • Defaults
    • webpack now looks for the .wasm, .mjs, .js and .json extensions in this order
    • output.pathinfo is now on by default in develoment mode
    • in-memory caching is now off by default in production
    • entry defaults to ./src
    • output.path defaults to ./dist
    • Use production defaults when omiting the mode option
  • Usage
    • Add detailed progress reporting to SourceMapDevToolPlugin
  • Stats
    • Sizes are now shown in kiB instead of kB in Stats
    • [new] entrypoints are now shows by default in Stats
    • [new] chunks now display <{parent}> >{children}< and ={siblings}= in Stats
  • Syntax
    • A resource query is supported in context
    • Parser now understand new Foo() expressions
    • Parser can now evaluate **, &, |, ^, >>>, >>, << and ~
    • Referencing entry point name in import() now emits a error instead of a warning
    • [new] Upgraded to acorn 5

Bugfixes

  • Generated comments no longer break on */
  • webpack no longer modifies the passed options object
  • Compiler “watch-run” hook now has the Compiler as first parameter
  • add chunkCallbackName to the schema to allow configurating WebWorker template
  • Using module.id/loaded now correctly bails out of Module Concatentation (Scope Hoisting)
  • OccurenceOrderPlugin now sorts modules in correct order (instead of reversed)
  • timestamps for files are read from watcher when calling Watching.invalidate
  • Avoid using process.exit and use process.exitCode instead
  • fix bluebird warning in webpack/hot/signal
  • fix incorrect -! behavior with post loaders
  • add run and watchRun hooks for MultiCompiler
  • [new] this is now undefined in ESM
  • [new] Initial chunks no longer contribute to chunkhash of runtime chunk and hashes don’t occur in chunk hash map
  • [new] VariableDeclaration are correctly identified as var, const or let
  • [new] Parser now parse the source code with the correct source type (module/script) when the module type javascript/dynamic or javascript/module is used.

Internal changes

  • Replaced plugin calls with tap calls (new plugin system)
  • Migrated many deprecated plugins to new plugin system API
  • added buildMeta.exportsType: "default" for json modules
  • Remove unused methods from Parser (parserStringArray, parserCalculatedStringArray)
  • Remove ability to clear BasicEvaluatedExpression and to have multiple types

Removed features

  • removed module.loaders
  • removed loaderContext.options
  • removed Compilation.notCacheable flag
  • removed NoErrorsPlugin
  • removed Dependency.isEqualResource
  • removed NewWatchingPlugin
  • [new] removed CommonsChunkPlugin

Breaking changes for plugins/loaders

  • new plugin system
    • plugin method is backward-compatible
    • Plugins should use Compiler.hooks.xxx.tap(<plugin name>, fn) now
  • New major version of enhanced-resolve
  • Templates for chunks may now generate multiple assets
  • Chunk.chunks/parents/blocks are no longer Arrays. A Set is used internally and there are methods to access it.
  • Parser.scope.renames and Parser.scope.definitions are no longer Objects/Arrays, but Map/Sets.
  • Parser uses StackedSetMap (LevelDB-like datastructure) instead of Arrays
  • Compiler.options is no longer set while applying plugins
  • Harmony Dependencies has changed because of refactoring
  • Dependency.getReference() may now return a weak property. Dependency.weak is now used by the Dependency base class and returned in the base impl of getReference()
  • Constructor arguments changed for all Modules
  • Merged options into options object for ContextModule and resolveDependencies
  • Changed and renamed dependencies for `import()
  • Moved Compiler.resolvers into Compiler.resolverFactory accessible with plugins
  • Dependency.isEqualResource has been replaced with Dependency.getResourceIdentifier
  • Methods on Template are now static
  • A new RuntimeTemplate class has been added and outputOptions and requestShortener has been moved to this class
    • Many methods has been updated to use the RuntimeTemplate instead
    • We plan to move code which accesses the runtime to this new class
  • Module.meta has been replaced with Module.buildMeta
  • Module.buildInfo and Module.factoryMeta have been added
  • Some properties of Module have been moved into the new objects
  • added loaderContext.rootContext which points to the context options. Loaders may use it to make stuff relative to the application root.
  • add this.hot flag to loader context when HMR is enabled
  • buildMeta.harmony has been replaced with buildMeta.exportsType: "namespace
  • [new] The chunk graph has changed:
    • Before: Chunks were connected with parent-child-relationships.
    • Now: ChunkGroups are connected with parent-child-relationships. ChunkGroups contain Chunks in order.
    • Before: AsyncDependenciesBlocks reference a list of Chunks in order.
    • Now: AsyncDependenciesBlocks reference a single ChunkGroup.

Migration

Node.js

If you are still using Node.js 4 or smaller:

Install Node.js 8. You could also install Node.js 6 but it’s slower (2x).

CLI

To use the command line interface:

yarn add -D webpack-cli

or

npm install -D webpack-cli

Configuration

  // webpack.production.config.js
  module.exports = {
+   mode: "production",
    plugins: [
-     new UglifyJsPlugin(/* ... */),
-     new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
-     new webpack.optimize.ModuleConcatenationPlugin(),
-     new webpack.NoEmitOnErrorsPlugin()
    ]
  }
  // webpack.development.config.js
  module.exports = {
+   mode: "development",
    plugins: [
-     new webpack.NamedModulesPlugin(),
-     new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
    ]
  }

import() CommonJs

import() now always returns the namespace object and not the exports directly for non-ESM.

- import("./commonjs").then(exports => {
+ import("./commonjs").then({ default: exports } => {
    // ...
  })

Note: ESM modules are not affected.

Note: Babel transpiled ESM (__esModule: true) will work in .js modules (javascript/auto), but not in .mjs modules.

Note: For JSON the same is true, but as JSON modules also export each property as exports you could pick property via destruction.

CommonsChunkPlugin

The following guides try to give 1 to 1 translations for the CommonsChunkPlugin. This does not neccessary fit bet pratices.

Best practise

optimization: {
  splitChunks: {
    chunks: "all"
  }
}

You need to generate the HTML from stats json. Use the entrypoints property from Stats to know which files to include for each entrypoint.

Initial vendor chunk

entry: {
  vendor: ["react", "lodash", "angular", ...]
}
new CommonsChunkPlugin({
  name: "vendors",
})
entry: {
  vendor: ["react", "lodash", "angular", ...]
},
optimization: {
  splitChunks: {
    cacheGroups: {
      vendor: {
        chunks: "initial",
        test: "vendor",
        name: "vendor",
        enforce: true
      }
    }
  }
}

Alternative:

optimization: {
  splitChunks: {
    cacheGroups: {
      vendor: {
        chunks: "initial",
        test: path.resolve(__dirname, "node_modules")
        name: "vendor",
        enforce: true
      }
    }
  }
}

Difference: This includes all used modules from node_modules and allows tree shaking.

Note: You don’t need to specify a entry for the alternative.

Commons chunk

new CommonsChunkPlugin({
  name: "commons",
  minChunks: 3
})
optimization: {
  splitChunks: {
    cacheGroups: {
      commons: {
        chunks: "initial",
        minChunks: 3,
        name: "commons",
        enforce: true
      }
    }
  }
}

Better vendor chunk

new CommonsChunkPlugin({
  name: "vendors",
  minChunks: module => {
    return module.resource && /react|angluar|lodash/.test(module.resource);
  }
})
optimization: {
  splitChunks: {
    cacheGroups: {
      vendor: {
        test: /react|angluar|lodash/
        chunks: "initial",
        name: "vendor",
        enforce: true
      }
    }
  }
}

Async commons chunk

new CommonsChunkPlugin({
  async: true,
  // ...
})

Just omit it. The defaults already do such optimization.

Deep Children

Anything with deepChildren: true and no async.

See configuration above but use chunks: "all".

Loaders consuming JSON

  module.rules: [
    {
      test: /\.special\.json$/,
+     type: "javascript/auto",
      use: "special-loader"
    }
  ]

Author: Fantashit

1 thought on “webpack 4.0.0-alpha.5 feedback

  1. Super cool, list of changes is amazing ❤️ it even has a thing that I was about to request last week but didn’t have time yet to create an issue (“[new] Dead branches are now removed by webpack itself”)

Comments are closed.