From 9d6cd69caf662da682aafc1736ac873319ddeff1 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sat, 16 Dec 2017 18:52:14 -0500 Subject: [PATCH 01/14] docs(api): reformat index page at 80 characters / line --- src/content/api/index.md | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/content/api/index.md b/src/content/api/index.md index 9ae94b03c03b..817fe57831e9 100644 --- a/src/content/api/index.md +++ b/src/content/api/index.md @@ -5,39 +5,55 @@ contributors: - tbroadley --- -A variety of interfaces are available to customize the compilation process. Some features overlap between interfaces, e.g. a configuration option may be available via a CLI flag, while others exist only through a single interface. The following high-level information should get you started. +A variety of interfaces are available to customize the compilation process. +Some features overlap between interfaces, e.g. a configuration option may be +available via a CLI flag, while others exist only through a single interface. +The following high-level information should get you started. ## CLI -The Command Line Interface (CLI) to configure and interact with your build. It is especially useful in the case of early prototyping and profiling. For the most part, the CLI is simply used to kick off the process using a configuration file and a few flags (e.g. `--env`). +The Command Line Interface (CLI) to configure and interact with your build. It +is especially useful in the case of early prototyping and profiling. For the +most part, the CLI is simply used to kick off the process using a configuration +file and a few flags (e.g. `--env`). [Learn more!](/api/cli) ## Module -When processing modules with webpack, it is important to understand the different module syntaxes -- specifically the [methods](/api/module-methods) and [variables](/api/module-variables) -- that are supported. +When processing modules with webpack, it is important to understand the +different module syntaxes -- specifically the [methods](/api/module-methods) +and [variables](/api/module-variables) -- that are supported. [Learn more!](/api/module-methods) ## Node -While most users can get away with just using the CLI along with a configuration file, more fine-grained control of the compilation can be achieved via the Node interface. This includes passing multiple configurations, programmatically running or watching, and collecting stats. +While most users can get away with just using the CLI along with a +configuration file, more fine-grained control of the compilation can be +achieved via the Node interface. This includes passing multiple configurations, +programmatically running or watching, and collecting stats. [Learn more!](/api/node) ## Loaders -Loaders are transformations that are applied to the source code of a module. They are written as functions that accept source code as a parameter and return a new version of that code with transformations applied. +Loaders are transformations that are applied to the source code of a module. +They are written as functions that accept source code as a parameter and return +a new version of that code with transformations applied. [Learn more!](/api/loaders) ## Plugins -The plugin interface provided by webpack allows users to tap directly into the compilation process. Plugins can register handlers on lifecycle hooks that run at different points in the compilation process. When each hook is executed, the plugin will have full access to the current state of the compilation. +The plugin interface allows users to tap directly into the compilation process. +Plugins can register handlers on lifecycle hooks that run at different points +throughout a compilation. When each hook is executed, the plugin will have full +access to the current state of the compilation. [Learn more!](/api/plugins) From 9f5fc55823a1f68bc0b2de0b5a6b8ad296f8a1f7 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sat, 16 Dec 2017 18:56:07 -0500 Subject: [PATCH 02/14] docs(api): reformat node api docs and add more details on the compiler This moves some of the content from `compiler.md` and brings it into the main node api documentation in order to allow `compiler.md` to become `compiler-hooks.md` which fits better in the context of plugins. --- src/content/api/node.md | 153 +++++++++++++++++++++++++++++----------- 1 file changed, 110 insertions(+), 43 deletions(-) diff --git a/src/content/api/node.md b/src/content/api/node.md index 5668bf201bfa..613064a2455f 100644 --- a/src/content/api/node.md +++ b/src/content/api/node.md @@ -48,34 +48,47 @@ webpack({ T> The `err` object **will not** include compilation errors and those must be handled separately using `stats.hasErrors()` which will be covered in detail in [Error Handling](#error-handling) section of this guide. The `err` object will only contain webpack-related issues, such as misconfiguration, etc. -**Note** that you can provide the `webpack` function with an array of configurations: +T> You can provide the `webpack` function with an array of configurations. See +the [MultiCompiler](#multicompiler) section below for more information. -``` js-with-links -webpack([ - { /* Configuration Object */ }, - { /* Configuration Object */ }, - { /* Configuration Object */ } -], (err, [stats](#stats-object)) => { - // ... -}); -``` -T> webpack will **not** run the multiple configurations in parallel. Each configuration is only processed after the previous one has finished processing. To have webpack process them in parallel, you can use a third-party solution like [parallel-webpack](https://www.npmjs.com/package/parallel-webpack). +## Compiler Instance +If you don’t pass the `webpack` runner function a callback, it will return a +webpack `Compiler` instance. This instance can be used to manually trigger the +webpack runner or have it build and watch for changes, much like the +[CLI](/api/cli/). The `Compiler` instance provides the following methods: -## Compiler Instance +- `.run(callback)` +- `.watch(watchOptions, handler)` + +Typically, only one master `Compiler` instance is created, although child +compilers can be created in order to delegate specific tasks. The `Compiler` is +ultimately just a function which performs bare minimum functionality to keep a +lifecycle running. It delegates all the loading, bundling, and writing work to +registered plugins. -If you don’t pass the `webpack` runner function a callback, it will return a webpack `Compiler` instance. This instance can be used to manually trigger the webpack runner or have it build and watch for changes. Much like the [CLI](/api/cli/) Api. The `Compiler` instance provides the following methods: +The `hooks` property on a `Compiler` instance is used to register a plugin to +any hook event in the `Compiler`'s lifecycle. The [`WebpackOptionsDefaulter`] +(https://github.com/webpack/webpack/blob/master/lib/WebpackOptionsDefaulter.js) +and [`WebpackOptionsApply`](https://github.com/webpack/webpack/blob/master/lib/WebpackOptionsApply.js) +utilities are used by webpack to configure its `Compiler` instance with all the +built-in plugins. -* `.run(callback)` -* `.watch(watchOptions, handler)` +The `run` method is then used to kickstart all compilation work. Upon +completion, the given `callback` function is executed. The final logging of +stats and errors should be done in this `callback` function. -W> The API only supports a single concurrent compilation at a time. When using `run`, wait for it to finish before calling `run` or `watch` again. When using `watch`, call `close` and wait for it to finish before calling `run` or `watch` again. Concurrent compilations will corrupt the output files. +W> The API only supports a single concurrent compilation at a time. When using +`run`, wait for it to finish before calling `run` or `watch` again. When using +`watch`, call `close` and wait for it to finish before calling `run` or `watch` +again. Concurrent compilations will corrupt the output files. ## Run -Calling the `run` method on the `Compiler` instance is much like the quick run method mentioned above: +Calling the `run` method on the `Compiler` instance is much like the quick run +method mentioned above: ``` js-with-links const webpack = require("webpack"); @@ -92,13 +105,15 @@ compiler.run((err, [stats](#stats-object)) => { ## Watching -Calling the `watch` method, triggers the webpack runner, but then watches for changes (much like CLI: `webpack --watch`), as soon as webpack detects a change, runs again. Returns an instance of `Watching`. +Calling the `watch` method, triggers the webpack runner, but then watches for +changes (much like CLI: `webpack --watch`), as soon as webpack detects a +change, runs again. Returns an instance of `Watching`. -``` js-with-links +``` js watch(watchOptions, callback) ``` -``` js-with-links-with-details +``` js-with-links const webpack = require("webpack"); const compiler = webpack({ @@ -106,24 +121,28 @@ const compiler = webpack({ }); const watching = compiler.watch({ -
/* [watchOptions](/configuration/watch/#watchoptions) */ + // Example [watchOptions](/configuration/watch/#watchoptions) aggregateTimeout: 300, poll: undefined -
}, (err, [stats](#stats-object)) => { // Print watch/build result here... console.log(stats); }); ``` -`Watching` options are [covered in detail here](/configuration/watch/#watchoptions). +`Watching` options are covered in detail +[here](/configuration/watch/#watchoptions). -W> Filesystem inaccuracies may trigger multiple builds for a single change. So, in the example above, the `console.log` statement may fire multiple times for a single modification. Users should expect this behavior and may check `stats.hash` to see if the file hash has actually changed. +W> Filesystem inaccuracies may trigger multiple builds for a single change. So, +in the example above, the `console.log` statement may fire multiple times for a +single modification. Users should expect this behavior and may check +`stats.hash` to see if the file hash has actually changed. ### Close `Watching` -The `watch` method returns a `Watching` instance that exposes `.close(callback)` method. Calling this method will end watching: +The `watch` method returns a `Watching` instance that exposes +`.close(callback)` method. Calling this method will end watching: ``` js watching.close(() => { @@ -131,12 +150,14 @@ watching.close(() => { }); ``` -T> It’s not allowed to watch or run again before the existing watcher has been closed or invalidated. +W> It’s not allowed to watch or run again before the existing watcher has been +closed or invalidated. ### Invalidate `Watching` -Using `watching.invalidate`, you can manually invalidate the current compiling round, without stopping the watch process: +Using `watching.invalidate`, you can manually invalidate the current compiling +round, without stopping the watch process: ``` js watching.invalidate(); @@ -145,32 +166,40 @@ watching.invalidate(); ## Stats Object -The `stats` object that is passed as a second argument of the [`webpack()`](#webpack-) callback, is a good source of information about the code compilation process. It includes: +The `stats` object that is passed as a second argument of the +[`webpack()`](#webpack-) callback, is a good source of information about the +code compilation process. It includes: -* Errors and Warnings (if any) -* Timings -* Module and Chunk information +- Errors and Warnings (if any) +- Timings +- Module and Chunk information -The [webpack CLI](/api/cli) uses this information to display a nicely formatted output in your console. +The [webpack CLI](/api/cli) uses this information to display nicely formatted +output in your console. -T> When using the [`MultiCompiler`](/api/plugins/compiler#multicompiler), a `MultiStats` instance is returned that fulfills the same interface as `stats`, i.e. the methods described below. +T> When using the [`MultiCompiler`](/api/plugins/compiler#multicompiler), a +`MultiStats` instance is returned that fulfills the same interface as `stats`, +i.e. the methods described below. This `stats` object exposes the following methods: ### `stats.hasErrors()` -Can be used to check if there were errors while compiling. Returns `true` or `false`. +Can be used to check if there were errors while compiling. Returns `true` or +`false`. ### `stats.hasWarnings()` -Can be used to check if there were warnings while compiling. Returns `true` or `false`. +Can be used to check if there were warnings while compiling. Returns `true` or +`false`. ### `stats.toJson(options)` -Returns compilation information as a JSON object. `options` can be either a string (a preset) or an object for more granular control: +Returns compilation information as a JSON object. `options` can be either a +string (a preset) or an object for more granular control: ``` js-with-links stats.toJson("minimal"); // [more options: "verbose", etc](/configuration/stats). @@ -183,20 +212,22 @@ stats.toJson({ }); ``` -All available options and presets are described in [Stats documentation](/configuration/stats) +All available options and presets are described in the stats [documentation](/configuration/stats). -> Here’s [an example of this function’s output](https://github.com/webpack/analyse/blob/master/app/pages/upload/example.json) +> Here’s an [example] +(https://github.com/webpack/analyse/blob/master/app/pages/upload/example.json) +of this function’s output. ### `stats.toString(options)` -Returns a formatted string of the compilation information (similar to [CLI](/api/cli) output). +Returns a formatted string of the compilation information (similar to +[CLI](/api/cli) output). Options are the same as [`stats.toJson(options)`](/api/node#stats-tojson-options-) with one addition: ``` js stats.toString({ - // ... // Add console colors colors: true }); @@ -223,6 +254,30 @@ webpack({ ``` +## MultiCompiler + +The `MultiCompiler` module allows webpack to run multiple configurations in +separate compilers. If the `options` parameter in the webpack's NodeJS api is +an array of options, webpack applies separate compilers and calls the +`callback` method at the end of each compiler execution. + +``` js-with-links +var webpack = require('webpack'); + +webpack([ + { entry: './index1.js', output: { filename: 'bundle1.js' } }, + { entry: './index2.js', output: { filename: 'bundle2.js' } } +], (err, [stats](#stats-object)) => { + process.stdout.write(stats.toString() + "\n"); +}) +``` + +W> Multiple configurations will __not be run in parallel__. Each +configuration is only processed after the previous one has finished +processing. To process them in parallel, you can use a third-party solution +like [parallel-webpack](https://www.npmjs.com/package/parallel-webpack). + + ## Error Handling For a good error handling, you need to account for these three types of errors: @@ -264,7 +319,13 @@ webpack({ ## Custom File Systems -By default, webpack reads files and writes files to disk using a normal file system. However, it is possible to change the input or output behavior using a different kind of file system (memory, webDAV, etc). To accomplish this, one can change the `inputFileSystem` or `outputFileSystem`. For example, you can replace the default `outputFileSystem` with [`memory-fs`](https://github.com/webpack/memory-fs) to write files to memory instead of to disk: +By default, webpack reads files and writes files to disk using a normal file +system. However, it is possible to change the input or output behavior using a +different kind of file system (memory, webDAV, etc). To accomplish this, one +can change the `inputFileSystem` or `outputFileSystem`. For example, you can +replace the default `outputFileSystem` with +[`memory-fs`](https://github.com/webpack/memory-fs) to write files to memory +instead of to disk: ``` js const MemoryFS = require("memory-fs"); @@ -280,6 +341,12 @@ compiler.run((err, stats) => { }); ``` -Note that this is what [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware), used by [webpack-dev-server](https://github.com/webpack/webpack-dev-server) and many other packages, uses to mysteriously hide your files but continue serving them up to the browser! +Note that this is what +[webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware), +used by [webpack-dev-server](https://github.com/webpack/webpack-dev-server) +and many other packages, uses to mysteriously hide your files but continue +serving them up to the browser! -T> The output file system you provide needs to be compatible with Node’s own [`fs`](https://nodejs.org/api/fs.html) interface, which requires the `mkdirp` and `join` helper methods. +T> The output file system you provide needs to be compatible with Node’s own +[`fs`](https://nodejs.org/api/fs.html) interface, which requires the `mkdirp` +and `join` helper methods. From 296b0853c030f44464d36b6492e80ed9e89dac07 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sun, 17 Dec 2017 10:46:40 -0500 Subject: [PATCH 03/14] docs(api): reformat/rewrite the lead-in plugin docs and remove `tapable.md` This moves some of the `Tapable` discussion to the lead-in `plugins.md` to prevent doc duplication. Instead of redocumenting the `tapable` package, we should focus on improving the README and simply link to there throughout our docs. As I've mentioned before, we can also create a "Utilities" section for things other than "Loaders" and "Plugins" that we want to dynamically pull in. --- src/content/api/plugins.md | 122 ++++++++++++++++++++++--------------- src/content/api/tapable.md | 64 ------------------- 2 files changed, 73 insertions(+), 113 deletions(-) delete mode 100644 src/content/api/tapable.md diff --git a/src/content/api/plugins.md b/src/content/api/plugins.md index 7d1f17ba4196..abf794144fae 100644 --- a/src/content/api/plugins.md +++ b/src/content/api/plugins.md @@ -2,75 +2,99 @@ title: Plugin API group: Plugins sort: 0 +contributors: + - thelarkinn + - pksjce + - e-cloud --- -T> For a high-level introduction to writing plugins, start with [writing a plugin](/contribute/writing-a-plugin). +Plugins are a key piece of the webpack ecosystem and provide the community with +a powerful way to tap into webpack's compilation process. A plugin is able to +[hook]() into key events that are fired throughout each compilation. Every step +of the way, the plugin will have full access to the `compiler` and, when +applicable, the current `compilation`. -Many objects in webpack extend the `Tapable` class, which exposes a `plugin` method. And with the `plugin` method, plugins can inject custom build steps. You will see `compiler.plugin` and `compilation.plugin` used a lot. Essentially, each one of these plugin calls binds a callback to fire at specific steps throughout the build process. +T> For a high-level introduction to writing plugins, start with +[writing a plugin](/contribute/writing-a-plugin). -There are two types of plugin interfaces... +Let's start by going over `tapable` utility, which provides the backbone of +webpack's plugin interface. -__Timing Based__ -- sync (default): The plugin runs synchronously and returns its output. -- async: The plugin runs asynchronously and uses the give `callback` to return its output. -- parallel: The handlers are invoked in parallel. +## Tapable -__Return Value__ +This small library is a core utility in webpack but can also be used elsewhere +to provide a similar plugin interface. Many objects in webpack extend the +`Tapable` class. The class exposes `tap`, `tapAsync`, and `tapPromise` methods +which plugins can use to inject custom build steps that will be fired +throughout a compilation. -- not bailing (default): No return value. -- bailing: The handlers are invoked in order until one handler returns something. -- parallel bailing: The handlers are invoked in parallel (async). The first returned value (by order) is significant. -- waterfall: Each handler gets the result value of the last handler as an argument. +Please see the [documentation](https://github.com/webpack/tapable) to learn +more. An understanding of the three `tap` methods, as well as the hooks that +provide them is crucial. The objects that extend `Tapable` (e.g. the compiler), +the hooks they provide, and each hook's type (e.g. the `SyncHook`) will be +noted. -A plugin is installed once as webpack starts up. webpack installs a plugin by calling its `apply` method, and passes a reference to the webpack `compiler` object. You may then call `compiler.plugin` to access asset compilations and their individual build steps. An example would look like this: -__my-plugin.js__ +## Plugin Types + +Depending on the hooks used and `tap` methods applied, plugins can function in +a number of different ways. The way this works is closely related to the +[hooks](https://github.com/webpack/tapable#tapable) provided by `Tapable`. The +[compiler hooks]() each note the underlying `Tapable` hook indicating which +`tap` methods are available. + +So depending which event you `tap` into, the plugin may run differently. For +example, when hooking into `compile` stage, only the synchronous `tap` method +can be used: ``` js -function MyPlugin(options) { - // Configure your plugin with options... -} - -MyPlugin.prototype.apply = function(compiler) { - compiler.plugin("compile", function(params) { - console.log("The compiler is starting to compile..."); - }); - - compiler.plugin("compilation", function(compilation) { - console.log("The compiler is starting a new compilation..."); - - compilation.plugin("optimize", function() { - console.log("The compilation is starting to optimize files..."); - }); - }); - - compiler.plugin("emit", function(compilation, callback) { - console.log("The compilation is going to emit files..."); - callback(); - }); -}; - -module.exports = MyPlugin; +compiler.hooks.compile.tap('MyPlugin', params => { + console.log('Synchronously tapping the compile hook.') +}) ``` -__webpack.config.js__ +However, for `run` which utilizes the `AsyncHook`, we can utilize `tapAsync` +or `tapPromise` (as well as `tap`): ``` js -plugins: [ - new MyPlugin({ - options: 'nada' +compiler.hooks.run.tapAsync('MyPlugin', (compiler, callback) => { + console.log('Asynchronously tapping the run hook.') + callback() +}) + +compiler.hooks.run.tapPromise('MyPlugin', compiler => { + return new Promise(resolve => setTimeout(resolve, 1000)).then(() => { + console.log('Asynchronously tapping the run hook with a delay.') }) -] +}) ``` +The moral of the story is that there are a variety of ways to `hook` into the +`compiler`, each allowing your plugin run as it sees fit. + + +## Custom Hooks + +In order to add a new hook to the compilation for other plugins to `tap` into, +simply `require` the necessary hook class from `tapable` and create one: + +``` js +const SyncHook = require('tapable').SyncHook; + +// Within the `apply` method... +if (compiler.hooks.myCustomHook) throw new Error('Already in use'); +compiler.hooks.myCustomHook = new SyncHook(['a', 'b', 'c']) + +// Wherever/whenever you'd like to trigger the hook... +compiler.hooks.myCustomHook.call(a, b, c); +``` -## Tapable & Tapable Instances +Again, see the [documentation]() for `tapable` to learn more about the different +hook classes and how they work. -The plugin architecture is mainly possible for webpack due to an internal library named `Tapable`. -**Tapable Instances** are classes in the webpack source code which have been extended or mixed in from class `Tapable`. -For plugin authors, it is important to know which are the `Tapable` instances in the webpack source code. These instances provide a variety of event hooks into which custom plugins can be attached. -Hence, throughout this section are a list of all of the webpack `Tapable` instances (and their event hooks), which plugin authors can utilize. +## Next Steps -For more information on `Tapable` visit the [complete overview](/api/tapable) or the [tapable repository](https://github.com/webpack/tapable). +See the [compiler hooks]() section for a detailed listing of all the available +`compiler` hooks and the parameters they make available. diff --git a/src/content/api/tapable.md b/src/content/api/tapable.md deleted file mode 100644 index 990a02e68298..000000000000 --- a/src/content/api/tapable.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Tapable -group: Plugins -sort: 1 -contributors: - - thelarkinn - - pksjce - - e-cloud ---- - -[Tapable](https://github.com/webpack/tapable) is a small library that allows you to add and apply plugins to a javascript module. It can be inherited or mixed in to other modules. It is similar to NodeJS's `EventEmitter` class, focusing on custom event emission and manipulation. However, in addition to this, `Tapable` allows you to have access to the "emittee" or "producer" of the event through callbacks arguments. - -`Tapable` has four groups of member functions: - -- `plugin(name:string, handler:function)`: This allows a custom plugin to register into a **Tapable instance**'s event. This acts similar to the `on()` method of the `EventEmitter`, which is used for registering a handler/listener to do something when the signal/event happens. -- `apply(…pluginInstances: (AnyPlugin|function)[])`: `AnyPlugin` should be a class (or, rarely, an object) that has an `apply` method, or just a function with some registration code inside. This method is just to **apply** plugins' definition, so that the real event listeners can be registered into the _Tapable_ instance's registry. -- `applyPlugins*(name:string, …)`: The _Tapable_ instance can apply all the plugins under a particular hash using these functions. This group of methods act like the `emit()` method of the `EventEmitter`, controlling event emission meticulously using various strategies. -- `mixin(pt: Object)`: a simple method to extend `Tapable`'s prototype as a mixin rather than inheritance. - -The different `applyPlugins*` methods cover the following use cases: - -- Plugins can run serially. -- Plugins can run in parallel. -- Plugins can run one after the other but taking input from the previous plugin (waterfall). -- Plugins can run asynchronously. -- Quit running plugins on bail: that is, once one plugin returns non-`undefined`, jump out of the run flow and return _the return of that plugin_. This sounds like `once()` of `EventEmitter` but is totally different. - - -## Example - -One of webpack's _Tapable_ instances, [Compiler](/api/compiler), is responsible for compiling the webpack configuration object and returning a [Compilation](/api/compilation) instance. When the Compilation instance runs, it creates the required bundles. - -See below for a simplified version of how this looks using `Tapable`: - -__node_modules/webpack/lib/Compiler.js__ - -``` js -var Tapable = require("tapable"); - -function Compiler() { - Tapable.call(this); -} - -Compiler.prototype = Object.create(Tapable.prototype); -``` - -Now to write a plugin on the compiler, - -__my-custom-plugin.js__ - -``` js -function CustomPlugin() {} -CustomPlugin.prototype.apply = function(compiler) { - compiler.plugin('emit', pluginFunction); -} -``` - -The compiler executes the plugin at the appropriate point in its lifecycle by - -__node_modules/webpack/lib/Compiler.js__ - -``` js -this.apply*("emit",options) // will fetch all plugins under 'emit' name and run them. -``` From d51d011d7a49eb04d665b3cf35660712669b380c Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sun, 17 Dec 2017 10:48:49 -0500 Subject: [PATCH 04/14] docs(api): remove `module-factories.md` and `template.md` These guides were full of todos and probably lead to more confusion than clarity. We can discuss more and add them back in once there's a clear flow for how to incorporate and document them. --- src/content/api/module-factories.md | 44 ----------------------------- src/content/api/template.md | 43 ---------------------------- 2 files changed, 87 deletions(-) delete mode 100644 src/content/api/module-factories.md delete mode 100644 src/content/api/template.md diff --git a/src/content/api/module-factories.md b/src/content/api/module-factories.md deleted file mode 100644 index fd2471ab1e7c..000000000000 --- a/src/content/api/module-factories.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Module Factories -group: Plugins -sort: 5 ---- - -?> Lead in... - -## `NormalModuleFactory` - -`before-resolve(data)` async waterfall - -Before the factory starts resolving. The `data` object has these properties: - -* `context`: The absolute path of the directory for resolving. -* `request`: The request of the expression. - -Plugins are allowed to modify the object or to pass a new similar object to the callback. - -`after-resolve(data)` async waterfall - -After the factory has resolved the request. The `data` object has these properties: - -* `request`: The resolved request. It acts as an identifier for the NormalModule. -* `userRequest`: The request the user entered. It's resolved, but does not contain pre or post loaders. -* `rawRequest`: The unresolved request. -* `loaders`: A array of resolved loaders. This is passed to the NormalModule and they will be executed. -* `resource`: The resource. It will be loaded by the NormalModule. -* `parser`: The parser that will be used by the NormalModule. - - -## `ContextModuleFactory` - -`before-resolve(data)` async waterfall - -?> Add documentation. - -`after-resolve(data)` async waterfall - -?> Add documentation. - -`alternatives(options: Array)` async waterfall - -?> Add documentation. diff --git a/src/content/api/template.md b/src/content/api/template.md deleted file mode 100644 index 3824eafe9e8b..000000000000 --- a/src/content/api/template.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Templates -group: Plugins -sort: 8 ---- - -## MainTemplate - -### `startup(source, module, hash)` - -``` js -compilation.mainTemplate.plugin('startup', function(source, module, hash) { - if (!module.chunks.length && source.indexOf('__ReactStyle__') === -1) { - var originName = module.origins && module.origins.length ? module.origins[0].name : 'main'; - return [ - 'if (typeof window !== "undefined") {', - ' window.__ReactStyle__ = ' + JSON.stringify(classNames[originName]) + ';', - '}' - ].join('\n') + source; - } - return source; -}); -``` - - -## ModuleTemplate - -?> Document the `ModuleTemplate`... - - -## ChunkTemplate - -?> Document the `ChunkTemplate`... - - -## FunctionModuleTemplate - -?> Document the `FunctionModuleTemplate`... - - -## HotUpdateChunkTemplate - -?> Document the `HotUpdateChunkTemplate`... From 2bb28d11719f9167572f05bb92d9ee1b8dfa50d9 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sun, 17 Dec 2017 16:02:58 -0500 Subject: [PATCH 05/14] docs(api): reformat line length in plugins.md --- src/content/api/plugins.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/api/plugins.md b/src/content/api/plugins.md index abf794144fae..9e50c335c63b 100644 --- a/src/content/api/plugins.md +++ b/src/content/api/plugins.md @@ -90,8 +90,8 @@ compiler.hooks.myCustomHook = new SyncHook(['a', 'b', 'c']) compiler.hooks.myCustomHook.call(a, b, c); ``` -Again, see the [documentation]() for `tapable` to learn more about the different -hook classes and how they work. +Again, see the [documentation]() for `tapable` to learn more about the +different hook classes and how they work. ## Next Steps From d1e539262d8702603a7d5098872e0dc54a4622a5 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sun, 17 Dec 2017 22:49:47 -0500 Subject: [PATCH 06/14] docs(api): rewrite, rename, and resort the `compiler.md` page This page now focuses specifically on the `hooks` as all the non-plugin related content was moved to `node.md`. I updated the hooks to the new syntax, added all undocumented hooks, reformatted each hook as a section to allow more breathing room, and clarified exactly which `tapable` hook is used for each section. Still need to get more info on some of the new hooks before we can ship this... --- src/content/api/compiler-hooks.md | 244 ++++++++++++++++++++++++++++++ src/content/api/compiler.md | 136 ----------------- 2 files changed, 244 insertions(+), 136 deletions(-) create mode 100644 src/content/api/compiler-hooks.md delete mode 100644 src/content/api/compiler.md diff --git a/src/content/api/compiler-hooks.md b/src/content/api/compiler-hooks.md new file mode 100644 index 000000000000..db7d17e5ef28 --- /dev/null +++ b/src/content/api/compiler-hooks.md @@ -0,0 +1,244 @@ +--- +title: Compiler Hooks +group: Plugins +sort: 1 +contributors: + - rishantagarwal +--- + +The `Compiler` module is the main engine that creates a compilation instance +with all the options passed through the [CLI]() or [Node API](). It extends the +`Tapable` class in order to register and call plugins. Most user facing plugins +are first registered on the `Compiler`. + +T> This module is exposed as `webpack.Compiler` and can be used directly.See +[this example](https://github.com/pksjce/webpack-internal-examples/tree/master/compiler-example) +for more information. + + +## Watching + +The `Compiler` supports [watching](/api/node/#watching) which monitors the file +system and recompiles as files change. When in watch mode, the compiler will +emit the additional events such as `watchRun`, `watchClose`, and `invalid`. +This is typically used in [development](/guides/development), usually under +the hood of tools like `webpack-dev-server`, so that the developer doesn't +need to re-compile manually every time. Watch mode can also be entered via the +[CLI](/api/cli/#watch-options). + + +## Hooks + +The following lifecycle hooks are exposed by the `Compiler` and can be acessed +as such: + +``` js +compiler.hooks.someHook.tap(...) +``` + +Depending on the hook type, `tapAsync` and `tapPromise` may also be available. + + +### `entryOption` + +`SyncBailHook` + +Executes a plugin after the `entry` configuration has been processed. + + +### `afterPlugins` + +`SyncHook` + +Runs a plugin after setting up initial set of plugins. + +Parameters: `compiler` + + +### `afterResolvers` + +`SyncHook` + +Executes a plugin after resolver setup is complete. + +Parameters: `compiler` + + +### `environment` + +`SyncHook` + +Runs a plugin before the environment is prepared. + + +### `afterEnvironment` + +`SyncHook` + +Executes a plugin a environment setup is complete. + + +### `beforeRun` + +`AsyncSeriesHook` + +Adds a hook right before `compiler.run()` is executed. + +Parameters: `compiler` + + +### `run` + +`AsyncSeriesHook` + +Hook into the compiler before it begins reading records. + +Parameters: `compiler` + + +### `watchRun` + +`AsyncSeriesHook` + +Executes a plugin during watch mode after a new compilation is triggered +but before the compilation is actually started. + +Parameters: `compiler` + + +### `normalModuleFactory` + +`SyncHook` + +Runs a plugin after a `NormalModuleFactory` is created. + +Parameters: `normalModuleFactory` + + +### `contextModuleFactory` + +Runs a plugin after a `ContextModuleFactory` is created. + +Parameters: `contextModuleFactory` + + +### `beforeCompile` + +`AsyncSeriesHook` + +Executes a plugin after compilation parameters are created. + +Parameters: `compilationParams` + + +### `compile` + +`SyncHook` + +Hook into the compiler before a new compilation is created. + +Parameters: `compilationParams` + + +### `thisCompilation` + +`SyncHook` + +Executed before emitting the `compilation` event (see below). + +Parameters: `compilation` + + +### `compilation` + +`SyncHook` + +Runs a plugin after a compilation has been created. + +Parameters: `compilation` + + +### `make` + +`AsyncParallelHook` + +... + +Parameters: `compilation` + + +### `afterCompile` + +`AsyncSeriesHook` + +... + +Parameters: `compilation` + + +### `shouldEmit` + +`SyncBailHook` + +Can return true/false at this point + +Parameters: `compilation` + + +### `needAdditionalPass` + +`SyncBailHook` + +... + + +### `emit` + +`AsyncSeriesHook` + +Before emitting assets to output dir + +Parameters: `compilation` + + +### `afterEmit` + +`AsyncSeriesHook` + +After emitting assets to output dir + +Parameters: `compilation` + + +### `done` + +`SyncHook` + +Completion of compile + +Parameters: `stats` + + +### `failed` + +`SyncHook` + +Failure of compile + +Parameters: `error` + + +### `invalid` + +`SyncHook` + +After invalidating a watch compile + +Parameters: `fileName`, `changeTime` + + +### `watchClose` + +`SyncHook` + +After stopping a watch compile diff --git a/src/content/api/compiler.md b/src/content/api/compiler.md deleted file mode 100644 index bc303a0a78cb..000000000000 --- a/src/content/api/compiler.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -title: Compiler -group: Plugins -sort: 2 -contributors: - - rishantagarwal ---- - -The `Compiler` module of webpack is the main engine that creates a compilation instance with all the options passed through webpack CLI or `webpack` api or webpack configuration file. - -It is exported by `webpack` api under `webpack.Compiler`. - -The compiler is used by webpack by instantiating it and then calling the `run` method. Below is a trivial example of how one might use the `Compiler`. In fact, this is really close to how webpack itself uses it. - -[__compiler-example__](https://github.com/pksjce/webpack-internal-examples/tree/master/compiler-example) - -```javascript -// Can be imported from webpack package -import {Compiler} from 'webpack'; - -// Create a new compiler instance -const compiler = new Compiler(); - -// Populate all required options -compiler.options = {...}; - -// Creating a plugin. -class LogPlugin { - apply (compiler) { - compiler.plugin('should-emit', compilation => { - console.log('should I emit?'); - return true; - }) - } -} - -// Apply the compiler to the plugin -new LogPlugin().apply(compiler); - -/* Add other supporting plugins */ - -// Callback to be executed after run is complete -const callback = (err, stats) => { - console.log('Compiler has finished execution.'); - // Display stats... -}; - -// call run on the compiler along with the callback -compiler.run(callback); -``` - -The `Compiler` is what we call a `Tapable` instance. By this, we mean that it mixes in `Tapable` class to imbibe functionality to register and call plugins on itself. Most user facing plugins are first registered on the `Compiler`. The working of a Compiler can be condensed into the following highlights - -- Usually there is one master instance of Compiler. Child compilers can be created for delegating specific tasks. -- A lot of the complexity in creating a compiler goes into populating all the relevant options for it. -- `webpack` has [`WebpackOptionsDefaulter`](https://github.com/webpack/webpack/blob/master/lib/WebpackOptionsDefaulter.js) and [`WebpackOptionsApply`](https://github.com/webpack/webpack/blob/master/lib/WebpackOptionsApply.js) specifically designed to provide the `Compiler` with all the initial data it requires. -- The `Compiler` is ultimately just a function which performs bare minimum functionality to keep a lifecycle running. It delegates all the loading/bundling/writing work to various plugins. -- `new LogPlugin(args).apply(compiler)` registers the plugin to any particular hook event in the `Compiler`'s lifecycle. -- The `Compiler` exposes a `run` method which kickstarts all compilation work for `webpack`. When that is done, it should call the passed in `callback` function. All the tail end work of logging stats and errors are done in this callback function. - - -## Watching - -The `Compiler` supports "watch mode" which monitors the file system and recompiles as files change. When in watch mode, the compiler will emit the additional events ["watch-run", "watch-close", and "invalid"](#event-hooks). This is typically used in [development](/guides/development), usually under the hood of tools like `webpack-dev-server`, so that the developer doesn't need to re-compile manually every time. - -For more details about watch mode, see the [Node.js API documentation](/api/node/#watching) or the [CLI watch options](/api/cli/#watch-options). - - -## MultiCompiler - -This module, MultiCompiler, allows webpack to run multiple configurations in separate compiler. -If the `options` parameter in the webpack's NodeJS api is an array of options, webpack applies separate compilers and calls the `callback` method at the end of each compiler execution. - -```javascript -var webpack = require('webpack'); - -var config1 = { - entry: './index1.js', - output: {filename: 'bundle1.js'} -} -var config2 = { - entry: './index2.js', - output: {filename:'bundle2.js'} -} - -webpack([config1, config2], (err, stats) => { - process.stdout.write(stats.toString() + "\n"); -}) -``` - - -## Event Hooks - -This a reference guide to all the event hooks exposed by the `Compiler`. - -Event name | Reason | Params | Type ------------------------------ | --------------------------------------- | ------------------------- | ---------- -__`entry-option`__ | - | - | bailResult -__`after-plugins`__ | After setting up initial set of plugins | `compiler` | sync -__`after-resolvers`__ | After setting up the resolvers | `compiler` | sync -__`environment`__ | - | - | sync -__`after-environment`__ | Environment setup complete | - | sync -__`before-run`__ | `compiler.run()` starts | `compiler` | async -__`run`__ | Before reading records | `compiler` | async -__`watch-run`__ | Before starting compilation after watch | `compiler` | async -__`normal-module-factory`__ | After creating a `NormalModuleFactory` | `normalModuleFactory` | sync -__`context-module-factory`__ | After creating a `ContextModuleFactory` | `contextModuleFactory` | sync -__`before-compile`__ | Compilation parameters created | `compilationParams` | async -__`compile`__ | Before creating new compilation | `compilationParams` | sync -__`this-compilation`__ | Before emitting `compilation` event | `compilation` | sync -__`compilation`__ | Compilation creation completed | `compilation` | sync -__`make`__ | - | `compilation` | parallel -__`after-compile`__ | - | `compilation` | async -__`should-emit`__ | Can return true/false at this point | `compilation` | bailResult -__`need-additional-pass`__ | - | - | bailResult -__`emit`__ | Before emitting assets to output dir | `compilation` | async -__`after-emit`__ | After emitting assets to output dir | `compilation` | async -__`done`__ | Completion of compile | `stats` | sync -__`failed`__ | Failure of compile | `error` | sync -__`invalid`__ | After invalidating a watch compile | `fileName`, `changeTime` | sync -__`watch-close`__ | After stopping a watch compile | - | sync - - -## Usage - -Here's an example of an asynchronous `emit` event handler: - -```javascript -compiler.plugin("emit", function(compilation, callback) { - // Do something async... - setTimeout(function() { - console.log("Done with async work..."); - callback(); - }, 1000); -}); -``` From 3ce506a1a3003dbf979bacd5814c910b8a2a08fc Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sun, 17 Dec 2017 22:57:26 -0500 Subject: [PATCH 07/14] docs(api): rewrite, rename, and resort the `compilation.md` page This page now uses the same section setup as `compiler-hooks.md` and is up to date with the latest hooks available in the webpack 4 alpha release. --- src/content/api/compilation-hooks.md | 632 +++++++++++++++++++++++++++ src/content/api/compilation.md | 296 ------------- 2 files changed, 632 insertions(+), 296 deletions(-) create mode 100644 src/content/api/compilation-hooks.md delete mode 100644 src/content/api/compilation.md diff --git a/src/content/api/compilation-hooks.md b/src/content/api/compilation-hooks.md new file mode 100644 index 000000000000..933a8238dc43 --- /dev/null +++ b/src/content/api/compilation-hooks.md @@ -0,0 +1,632 @@ +--- +title: Compilation Hooks +group: Plugins +sort: 2 +--- + +The `Compilation` module is used by the `Compiler` to create new compilations +(or builds). A `Compilation` instance has access to all modules and their +dependencies (most of which are circular references). It is the literal +compilation of all the modules in the dependency graph of an application. +During the compilation phase, modules are loaded, sealed, optimized, chunked, +hashed and restored. + +The `Compilation` class also extends `Tapable` and provides the following +lifecycle hooks. They can be tapped the same way as compiler hooks: + +``` js +compilation.hooks.someHook.tap(...) +``` + +As with the compiler, `tapAsync` and `tapPromise` may also be available +depending on the type of hook. + + +### `buildModule` + +`SyncHook` + +Triggered before a module build has started. + +Parameters: `module` + + +### `rebuildModule` + +`SyncHook` + +Fired before rebuilding a module. + +Parameters: `module` + + +### `failedModule` + +`SyncHook` + +Run when a module build has failed. + +Parameters: `module` `error` + + +### `succeedModule` + +`SyncHook` + +Executed when a module has been built successfully. + +Parameters: `module` + + +### `finishModules` + +`SyncHook` + +... + +Parameters: `modules` + + +### `finishRebuildingModule` + +`SyncHook` + +... + +Parameters: `module` + + +### `seal` + +`SyncHook` + +Fired when the compilation stops accepting new modules. + + +### `unseal` + +`SyncHook` + +... + + +### `optimizeDependenciesBasic` + +`SyncBailHook` + +... + +Parameters: `modules` + + +### `optimizeDependencies` + +`SyncBailHook` + +... + +Parameters: `modules` + + +### `optimizeDependenciesAdvanced` + +`SyncBailHook` + +... + +Parameters: `modules` + + +### `afterOptimizeDependencies` + +`SyncHook` + +... + +Parameters: `modules` + + +### `optimize` + +`SyncHook` + +Triggered at the beginning of the optimization phase. + + +### `optimizeModulesBasic` + +`SyncBailHook` + +... + +Parameters: `modules` + + +### `optimizeModules` + +`SyncBailHook` + +... + +Parameters: `modules` + + +### `optimizeModulesAdvanced` + +`SyncBailHook` + +... + +Parameters: `modules` + + +### `afterOptimizeModules` + +`SyncHook` + +... + +Parameters: `modules` + + +### `optimizeChunksBasic` + +`SyncBailHook` + +... + +Parameters: `chunks` + + +### `optimizeChunks` + +`SyncBailHook` + +Optimize the chunks. + +Parameters: `chunks` + + +### `optimizeChunksAdvanced` + +`SyncBailHook` + +... + +Parameters: `chunks` + + +### `afterOptimizeChunks` + +`SyncHook` + +Fired after chunk optimization has completed. + +Parameters: `chunks` + + +### `optimizeTree` + +`AsyncSeriesHook` + +Optimize the dependency tree asynchronously. + +Parameters: `chunks` `modules` + + +### `afterOptimizeTree` + +`SyncHook` + +... + +Parameters: `chunks` `modules` + + +### `optimizeChunkModulesBasic` + +`SyncBailHook` + +... + +Parameters: `chunks` `modules` + + +### `optimizeChunkModules` + +`SyncBailHook` + +... + +Parameters: `chunks` `modules` + + +### `optimizeChunkModulesAdvanced` + +`SyncBailHook` + +... + +Parameters: `chunks` `modules` + + +### `afterOptimizeChunkModules` + +`SyncHook` + +... + +Parameters: `chunks` `modules` + + +### `shouldRecord` + +`SyncBailHook` + +... + + +### `reviveModules` + +`SyncHook` + +Restore module information from records. + +Parameters: `modules` `records` + + +### `optimizeModuleOrder` + +`SyncHook` + +Sort the modules in from most to least important. + +Parameters: `modules` + + +### `advancedOptimizeModuleOrder` + +`SyncHook` + +... + +Parameters: `modules` + + +### `beforeModuleIds` + +`SyncHook` + +... + +Paramters: `modules` + + +### `moduleIds` + +`SyncHook` + +... + +Parameters: `modules` + + +### `optimizeModuleIds` + +`SyncHook` + +... + +Paramters: `chunks` + + +### `afterOptimizeModuleIds` + +`SyncHook` + +... + +Paramters: `chunks` + + +### `reviveChunks` + +`SyncHook` + +Restore chunk information from records. + +Parameters: `modules` `records` + + +### `optimizeChunkOrder` + +`SyncHook` + +Sort the chunks in from most to least important. + +Parameters: `chunks` + + +### `beforeOptimizeChunkIds` + +`SyncHook` + +Fired before chunk `id` optimization. + +Paramters: `chunks` + + +### `optimizeChunkIds` + +`SyncHook` + +Optimize the `id` of each chunk. + +Parameters: `chunks` + + +### `afterOptimizeChunkIds` + +`SyncHook` + +Triggered after chunk `id` optimization has finished. + +Paramters: `chunks` + + +### `recordModules` + +`SyncHook` + +Store module info to the records. + +Parameters: `modules` `records` + + +### `recordChunks` + +`SyncHook` + +Store chunk info to the records. + +Parameters: `chunks` `records` + + +### `beforeHash` + +`SyncHook` + +Before the compilation is hashed. + + +### `afterHash` + +`SyncHook` + +After the compilation is hashed. + + +### `recordHash` + +`SyncHook` + +... + +Parameters: `records` + + +### `record` + +`SyncHook` + +Store information about the `compilation` to the `records`. + +Parameters: `compilation` `records` + + +### `beforeModuleAssets` + +`SyncHook` + +... + + +### `shouldGenerateChunkAssets` + +`SyncBailHook` + +... + + +### `beforeChunkAssets` + +`SyncHook` + +Before creating the chunk assets. + + +### `additionalChunkAssets` + +`SyncHook` + +Create additional assets for the chunks. + +Parameters: `chunks` + + +### `records` + +`SyncHook` + +... + +Parameters: `compilation` `records` + + +### `additionalAssets` + +`AsyncSeriesHook` + +Create additional assets for the compilation. This hook can be used to download +an image, for example: + +``` js +compilation.hooks.additionalAssets.tapAsync('MyPlugin', callback => { + download('https://img.shields.io/npm/v/webpack.svg', function(resp) { + if(resp.status === 200) { + compilation.assets['webpack-version.svg'] = toAsset(resp); + callback(); + } else { + callback(new Error('[webpack-example-plugin] Unable to download the image')); + } + }) +}); +``` + + +### `optimizeChunkAssets` + +`AsyncSeriesHook` + +Optimize any chunk assets. The assets are stored in `compilation.assets`. A +`Chunk` has a property `files` which points to all files created by a chunk. +Any additional chunk assets are stored in `compilation.additionalChunkAssets`. + +Parameters: `chunks` + +Here's an example that simply adds a banner to each chunk. + +``` js +compilation.hooks + .optimizeChunkAssets + .tapAsync('MyPlugin', (chunks, callback) => { + chunks.forEach(chunk => { + chunk.files.forEach(file => { + compilation.assets[file] = new ConcatSource( + '\/**Sweet Banner**\/', + '\n', + compilation.assets[file] + ); + }); + }); + + callback(); + }); +``` + + +### `afterOptimizeChunkAssets` + +`SyncHook` + +The chunk assets have been optimized. + +Parameters: `chunks` + +Here's an example plugin from [@boopathi](https://github.com/boopathi) that outputs exactly what went into each chunk. + +``` js +compilation.hooks.afterOptimizeChunkAssets.tap(chunks => { + chunks.forEach(chunk => { + console.log({ + id: chunk.id, + name: chunk.name, + includes: chunk.modules.map(module => module.request) + }); + }); +}); +``` + + +### `optimizeAssets` + +`AsyncSeriesHook` + +Optimize all assets stored in `compilation.assets`. + +Parameters: `assets` + + +### `afterOptimizeAssets` + +`SyncHook` + +The assets has been optimized. + +Parameters: `assets` + + +### `needAdditionalSeal` + +`SyncBailHook` + +... + + +### `afterSeal` + +`AsyncSeriesHook` + +... + + +### `chunkHash` + +`SyncHook` + +... + +Parameters: `chunk` `chunkHash` + + +### `moduleAsset` + +`SyncHook` + +An asset from a module was added to the compilation. + +Parameters: `module` `filename` + + +### `chunkAsset` + +`SyncHook` + +An asset from a chunk was added to the compilation. + +Parameters: `chunk` `filename` + + +### `assetPath` + +`SyncWaterfallHook` + +... + +Parameters: `filename` `data` + + +### `needAdditionalPass` + +`SyncBailHook` + +... + + +### `childCompiler` + +`SyncHook` + +... + +Parameters: `childCompiler` `compilerName` `compilerIndex` + + +### `normalModuleLoader` + +`SyncHook` + +The normal module loader is the function that actually loads all the modules +in the module graph (one-by-one). + +Parameters: `loaderContext` `module` diff --git a/src/content/api/compilation.md b/src/content/api/compilation.md deleted file mode 100644 index 56e0d1c1448a..000000000000 --- a/src/content/api/compilation.md +++ /dev/null @@ -1,296 +0,0 @@ ---- -title: Compilation -group: Plugins -sort: 3 ---- - -The Compilation instance extends from the compiler i.e. `compiler.compilation`. It is the literal compilation of all the objects in the require graph. This object has access to all the modules and their dependencies (most of which are circular references). In the compilation phase, modules are loaded, sealed, optimized, chunked, hashed and restored, etc. This would be the main lifecycle of any operations of the compilation. - -``` js -compiler.plugin("compilation", function(compilation) { - // the main compilation instance - // all subsequent methods are derived from compilation.plugin -}); -``` - - -## `normal-module-loader` - -The normal module loader, is the function that actually loads all the modules in the module graph (one-by-one). - -``` js -compilation.plugin('normal-module-loader', function(loaderContext, module) { - // this is where all the modules are loaded - // one by one, no dependencies are created yet -}); -``` - - -## `seal` - -The sealing of the compilation has started. - -``` js -compilation.plugin('seal', function() { - // you are not accepting any more modules - // no arguments -}); -``` - - -## `optimize` - -Optimize the compilation. - -``` js -compilation.plugin('optimize', function() { - // webpack is begining the optimization phase - // no arguments -}); -``` - - -## `optimize-tree(chunks, modules)` async - -Async optimization of the tree. - -``` js -compilation.plugin('optimize-tree', function(chunks, modules) { - -}); -``` - -### `optimize-modules(modules: Module[])` - -Optimize the modules. - -``` js -compilation.plugin('optimize-modules', function(modules) { - // handle to the modules array during tree optimization -}); -``` - - -## `after-optimize-modules(modules: Module[])` - -Optimizing the modules has finished. - - -## `optimize-chunks(chunks: Chunk[])` - -Optimize the chunks. - -```javascript -//optimize chunks may be run several times in a compilation - -compilation.plugin('optimize-chunks', function(chunks) { - // Unless you've specified multiple entries in your config - // there's only one chunk at this point - chunks.forEach(function (chunk) { - // Chunks have circular references to their modules - chunk.modules.forEach(function (module){ - // module.loaders, module.rawRequest, module.dependencies, etc. - }); - }); -}); -``` - -## `after-optimize-chunks(chunks: Chunk[])` - -Optimizing the chunks has finished. - - -## `revive-modules(modules: Module[], records)` - -Restore module info from records. - - -## `optimize-module-order(modules: Module[])` - -Sort the modules in order of importance. The first is the most important module. It will get the smallest id. - - -## `optimize-module-ids(modules: Module[])` - -Optimize the module ids. - - -## `after-optimize-module-ids(modules: Module[])` - -Optimizing the module ids has finished. - - -## `record-modules(modules: Module[], records)` - -Store module info to the records. - - -## `revive-chunks(chunks: Chunk[], records)` - -Restore chunk info from records. - - -## `optimize-chunk-order(chunks: Chunk[])` - -Sort the chunks in order of importance. The first is the most important chunk. It will get the smallest id. - - -## `optimize-chunk-ids(chunks: Chunk[])` - -Optimize the chunk ids. - - -## `after-optimize-chunk-ids(chunks: Chunk[])` - -Optimizing the chunk ids has finished. - - -## `record-chunks(chunks: Chunk[], records)` - -Store chunk info to the records. - - -## `before-hash` - -Before the compilation is hashed. - - -## `after-hash` - -After the compilation is hashed. - - -## `before-chunk-assets` - -Before creating the chunk assets. - - -## `additional-chunk-assets(chunks: Chunk[])` - -Create additional assets for the chunks. - - -## `record(compilation, records)` - -Store info about the compilation to the records - - -## `additional-assets` async - -Create additional assets for the compilation - -Here's an example that downloads an image. - -``` js -compiler.plugin('compilation', function(compilation) { - compilation.plugin('additional-assets', function(callback) { - download('https://img.shields.io/npm/v/webpack.svg', function(resp) { - if(resp.status === 200) { - compilation.assets['webpack-version.svg'] = toAsset(resp); - callback(); - } else { - callback(new Error('[webpack-example-plugin] Unable to download the image')); - } - }) - }); -}); -``` - - -## `optimize-chunk-assets(chunks: Chunk[])` async - -Optimize the assets for the chunks. - -The assets are stored in `this.assets`, but not all of them are chunk assets. A `Chunk` has a property `files` which points to all files created by this chunk. The additional chunk assets are stored in `this.additionalChunkAssets`. - -Here's an example that simply adds a banner to each chunk. - -``` js -compilation.plugin("optimize-chunk-assets", function(chunks, callback) { - chunks.forEach(function(chunk) { - chunk.files.forEach(function(file) { - compilation.assets[file] = new ConcatSource("\/**Sweet Banner**\/", "\n", compilation.assets[file]); - }); - }); - callback(); -}); -``` - -## `after-optimize-chunk-assets(chunks: Chunk[])` - -The chunk assets have been optimized. Here's an example plugin from [@boopathi](https://github.com/boopathi) that outputs exactly what went into each chunk. - -``` js -var PrintChunksPlugin = function() {}; - -PrintChunksPlugin.prototype.apply = function(compiler) { - compiler.plugin('compilation', function(compilation, params) { - compilation.plugin('after-optimize-chunk-assets', function(chunks) { - console.log(chunks.map(function(c) { - return { - id: c.id, - name: c.name, - includes: c.modules.map(function(m) { - return m.request; - }) - }; - })); - }); - }); -}; -``` - - -## `optimize-assets(assets: Object{name: Source})` async - -Optimize all assets. - -The assets are stored in `this.assets`. - - -## `after-optimize-assets(assets: Object{name: Source})` - -The assets has been optimized. - - -## `build-module(module)` - -Before a module build has started. - -``` js -compilation.plugin('build-module', function(module){ - console.log('About to build: ', module); -}); -``` - - -## `succeed-module(module)` - -A module has been built successfully. - -``` js -compilation.plugin('succeed-module', function(module){ - console.log('Successfully built: ', module); -}); -``` - - -## `failed-module(module)` - -The module build has failed. - -``` js -compilation.plugin('failed-module', function(module){ - console.log('Failed to build: ', module); -}); -``` - - -## `module-asset(module, filename)` - -An asset from a module was added to the compilation. - - -## `chunk-asset(chunk, filename)` - -An asset from a chunk was added to the compilation. From 7fc8edd7a96164407f97be560e1978900941c3af Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sun, 17 Dec 2017 22:58:11 -0500 Subject: [PATCH 08/14] docs(api): fix sorting and start to reformat the last two plugin pages --- src/content/api/parser.md | 18 ++++++++++++++++-- src/content/api/resolver.md | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/content/api/parser.md b/src/content/api/parser.md index b8a679228a3a..54c0d0862ae5 100644 --- a/src/content/api/parser.md +++ b/src/content/api/parser.md @@ -1,12 +1,12 @@ --- title: Parser group: Plugins -sort: 8 +sort: 4 --- The parser instance takes a String and callback and will return an expression when there's a match. -```javascript +``` js compiler.parser.plugin("var rewire", function (expr) { //if you original module has 'var rewire' //you now have a handle on the expresssion object @@ -14,68 +14,82 @@ compiler.parser.plugin("var rewire", function (expr) { }); ``` + ## `program(ast)` bailing General purpose plugin interface for the AST of a code fragment. + ## `statement(statement: Statement)` bailing General purpose plugin interface for the statements of the code fragment. + ## `call (expr: Expression)` bailing `abc(1)` => `call abc` `a.b.c(1)` => `call a.b.c` + ## `expression (expr: Expression)` bailing `abc` => `expression abc` `a.b.c` => `expression a.b.c` + ## `expression ?:(expr: Expression)` bailing `(abc ? 1 : 2)` => `expression ?!` Return a boolean value to omit parsing of the wrong path. + ## `typeof (expr: Expression)` bailing `typeof a.b.c` => `typeof a.b.c` + ## `statement if(statement: Statement)` bailing `if(abc) {}` => `statement if` Return a boolean value to omit parsing of the wrong path. + ## `label (statement: Statement)` bailing `xyz: abc` => `label xyz` + ## `var (statement: Statement)` bailing `var abc, def` => `var abc` + `var def` Return `false` to not add the variable to the known definitions. + ## `evaluate (expr: Expression)` bailing Evaluate an expression. + ## `evaluate typeof (expr: Expression)` bailing Evaluate the type of an identifier. + ## `evaluate Identifier (expr: Expression)` bailing Evaluate a identifier that is a free var. + ## `evaluate defined Identifier (expr: Expression)` bailing Evaluate a identifier that is a defined var. + ## `evaluate CallExpression .(expr: Expression)` bailing Evaluate a call to a member function of a successfully evaluated expression. diff --git a/src/content/api/resolver.md b/src/content/api/resolver.md index cacaf69a24dc..e4ab9ba9c06a 100644 --- a/src/content/api/resolver.md +++ b/src/content/api/resolver.md @@ -1,7 +1,7 @@ --- title: Resolver group: Plugins -sort: 5 +sort: 3 --- There are three types of resolvers, each used for different types of modules: From 4b851919e84c908d9ba114f8e54d4d963312dca4 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Wed, 27 Dec 2017 12:09:31 -0500 Subject: [PATCH 09/14] docs(api): begin rewrite of the resolver page --- src/content/api/resolver.md | 102 +++++++++++------------------------- 1 file changed, 30 insertions(+), 72 deletions(-) diff --git a/src/content/api/resolver.md b/src/content/api/resolver.md index e4ab9ba9c06a..0800bca0d58d 100644 --- a/src/content/api/resolver.md +++ b/src/content/api/resolver.md @@ -1,91 +1,49 @@ --- -title: Resolver +title: Resolvers group: Plugins sort: 3 --- -There are three types of resolvers, each used for different types of modules: +Resolvers are created using the `enhanced-resolve` package. The `Resolver` +class extends the `tapable` class and uses `tapable` to provide a few hooks. +The `enhanced-resolve` package can be used directly to create new resolvers, +however any [`compiler` instance]() has a few resolver instances that can be +tapped into. -- `compiler.resolvers.normal`: Resolve a normal module. -- `compiler.resolvers.context`: Resolve a context module. -- `compiler.resolvers.loader`: Resolve a loader. +Before reading on, make sure you at least skim through the +[`enhanced-resolve`]() and [`tapable`]() documentation. -Any plugin should use `this.fileSystem` as fileSystem, as it's cached. It only has async named functions, but they may behave sync, if the user uses a sync file system implementation (i. e. in enhanced-require). -To join paths any plugin should use `this.join`. It normalizes the paths. There is a `this.normalize` too. +## Types -A bailing async `forEach` implementation is available on `this.forEachBail(array, iterator, callback)`. +There are three types of built-in resolvers available on the `compiler` class: -To pass the request to other resolving plugins, use the `this.doResolve(types: String|String[], request: Request, message: String, callback)` method. `types` are multiple possible request types that are tested in order of preference. +- Normal: Resolves a module via an absolute or relative path. +- Context: Resolves a module within a given context. +- Loader: Resolves a webpack [loader](/loaders). -``` js -interface Request { - path: String // The current directory of the request - request: String // The current request string - query: String // The query string of the request, if any - module: boolean // The request begins with a module - directory: boolean // The request points to a directory - file: boolean // The request points to a file - resolved: boolean // The request is resolved/done - // undefined means false for boolean fields -} +Depending on need, any one of these built-in resolver used by the `compiler` +can be customized via plugins as such: -// Examples -// from /home/user/project/file.js: require("../test?charset=ascii") -{ - path: "/home/user/project", - request: "../test", - query: "?charset=ascii" -} -// from /home/user/project/file.js: require("test/test/") -{ - path: "/home/user/project", - request: "test/test/", - module: true, - directory: true -} +``` js +compiler.resolverFactory.plugin('resolver [type]', resolver => { + resolver.hooks.resolve.tapAsync('MyPlugin', params => { + // ... + }) +}) ``` +Where `[type]` is one of the three resolvers mention above, specified as: -## `resolve(context: String, request: String)` - -Before the resolving process starts. - - -## `resolve-step(types: String[], request: Request)` - -Before a single step in the resolving process starts. - - -## `module(request: Request)` async waterfall - -A module request is found and should be resolved. - - -## `directory(request: Request)` async waterfall - -A directory request is found and should be resolved. - - -## `file(request: Request)` async waterfall - -A file request is found and should be resolved. - - -## The plugins may offer more extensions points - -Here is a list what the default plugins in webpack offer. They are all `(request: Request)` async waterfall. - -The process for normal modules and contexts is `module -> module-module -> directory -> file`. - -The process for loaders is `module -> module-loader-module -> module-module -> directory -> file`. - - -## `module-module` +- `normal` +- `context` +- `loader` -A module should be looked up in a specified directory. `path` contains the directory. +See the `enhanced-resolve` [documentation]() for a full list of hooks and +descriptions. -## `module-loader-module` (only for loaders) +## Configuration Options -Used before module templates are applied to the module name. The process continues with `module-module`. +Briefly discuss and link to resolver-related configuration docs. Clarify how +the options affect the three different resolvers... From 3e60e9cf3b702e94110cf26f3b7767b95b10ca13 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Wed, 27 Dec 2017 16:57:57 -0500 Subject: [PATCH 10/14] docs(config): fix header nesting in resolve.md --- src/content/configuration/resolve.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/configuration/resolve.md b/src/content/configuration/resolve.md index 7a5107706963..17c7c1d2ea96 100644 --- a/src/content/configuration/resolve.md +++ b/src/content/configuration/resolve.md @@ -243,7 +243,7 @@ unsafeCache: /src\/utilities/ W> Changes to cached paths may cause failure in rare cases. -## `resolve.plugins` +### `resolve.plugins` A list of additional resolve plugins which should be applied. It allows plugins such as [`DirectoryNamedWebpackPlugin`](https://www.npmjs.com/package/directory-named-webpack-plugin). @@ -254,7 +254,7 @@ plugins: [ ``` -## `resolve.symlinks` +### `resolve.symlinks` `boolean` @@ -265,7 +265,7 @@ symlinks: true ``` -## `resolve.cachePredicate` +### `resolve.cachePredicate` `function` From 40a6df06bd6231f963493c4a6740b9ccf87e992e Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Wed, 27 Dec 2017 16:58:22 -0500 Subject: [PATCH 11/14] docs(api): rename resolvers page for clarity and finish rewrite --- src/content/api/{resolver.md => resolvers.md} | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) rename src/content/api/{resolver.md => resolvers.md} (67%) diff --git a/src/content/api/resolver.md b/src/content/api/resolvers.md similarity index 67% rename from src/content/api/resolver.md rename to src/content/api/resolvers.md index 0800bca0d58d..d059d2ecc93b 100644 --- a/src/content/api/resolver.md +++ b/src/content/api/resolvers.md @@ -45,5 +45,13 @@ descriptions. ## Configuration Options -Briefly discuss and link to resolver-related configuration docs. Clarify how -the options affect the three different resolvers... +The resolvers mentioned above can also be customized via a configuration file +with the [`resolve`]() or [`resolveLoader`]() options. These options allow +users to change the resolving behavior through a variety of options including +through resolve `plugins`. + +The resolver plugins, e.g. [`DirectoryNamedPlugin`] +(https://github.com/shaketbaby/directory-named-webpack-plugin), can be included +directly in `resolve.plugins` rather than using standard plugins. Note that the +`resolve` configuration affects the `normal` and `context` resolvers while +`resolveLoader` is used to modify the `loader` resolver. From e6b5801839f4be88524fc173605ac4aa62a2775f Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sun, 31 Dec 2017 01:02:35 -0500 Subject: [PATCH 12/14] docs(api): make minor corrections to plugin docs --- src/content/api/compilation-hooks.md | 4 ++-- src/content/api/compiler-hooks.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/api/compilation-hooks.md b/src/content/api/compilation-hooks.md index 933a8238dc43..9f59d15f5310 100644 --- a/src/content/api/compilation-hooks.md +++ b/src/content/api/compilation-hooks.md @@ -5,7 +5,7 @@ sort: 2 --- The `Compilation` module is used by the `Compiler` to create new compilations -(or builds). A `Compilation` instance has access to all modules and their +(or builds). A `compilation` instance has access to all modules and their dependencies (most of which are circular references). It is the literal compilation of all the modules in the dependency graph of an application. During the compilation phase, modules are loaded, sealed, optimized, chunked, @@ -18,7 +18,7 @@ lifecycle hooks. They can be tapped the same way as compiler hooks: compilation.hooks.someHook.tap(...) ``` -As with the compiler, `tapAsync` and `tapPromise` may also be available +As with the `compiler`, `tapAsync` and `tapPromise` may also be available depending on the type of hook. diff --git a/src/content/api/compiler-hooks.md b/src/content/api/compiler-hooks.md index db7d17e5ef28..ef592c8a2a5c 100644 --- a/src/content/api/compiler-hooks.md +++ b/src/content/api/compiler-hooks.md @@ -29,7 +29,7 @@ need to re-compile manually every time. Watch mode can also be entered via the ## Hooks -The following lifecycle hooks are exposed by the `Compiler` and can be acessed +The following lifecycle hooks are exposed by the `compiler` and can be accessed as such: ``` js From 40ec2197bb0d65e49f93120169bb77d707e50813 Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Sun, 31 Dec 2017 01:05:14 -0500 Subject: [PATCH 13/14] docs(api): rewrite the parser documentation Improve lead-in to give a little more context on what the parser does. Add the latest hooks and use the same layout as the other pages in the section. --- src/content/api/parser.md | 301 +++++++++++++++++++++++++++++++++----- 1 file changed, 262 insertions(+), 39 deletions(-) diff --git a/src/content/api/parser.md b/src/content/api/parser.md index 54c0d0862ae5..2a289978dea6 100644 --- a/src/content/api/parser.md +++ b/src/content/api/parser.md @@ -4,92 +4,315 @@ group: Plugins sort: 4 --- -The parser instance takes a String and callback and will return an expression when there's a match. +The `parser` instance, found in the `compiler`, is used to parse each module +being processed by webpack. The `parser` is yet another webpack class that +extends `tapable` and provides a variety of `tapable` hooks that can be used by +plugin authors to customize the parsing process. + +The `parser` is found within [module factories]() and therefore takes little +more work to access: ``` js -compiler.parser.plugin("var rewire", function (expr) { - //if you original module has 'var rewire' - //you now have a handle on the expresssion object - return true; -}); +compiler.hooks.normalModuleFactory.tap(factory => { + factory.hooks.parser.tap((parser, options) => { + parser.hooks.someHook.tap(...) + }) +}) ``` +As with the `compiler`, `tapAsync` and `tapPromise` may also be available +depending on the type of hook. + -## `program(ast)` bailing +## Hooks -General purpose plugin interface for the AST of a code fragment. +The following lifecycle hooks are exposed by the `parser` and can be accessed +as such: -## `statement(statement: Statement)` bailing +### evaluateTypeof -General purpose plugin interface for the statements of the code fragment. +`SyncBailHook` +Evaluate the type of an identifier. -## `call (expr: Expression)` bailing +Parameters: `expression` -`abc(1)` => `call abc` -`a.b.c(1)` => `call a.b.c` +### evaluate +`SyncBailHook` -## `expression (expr: Expression)` bailing +Evaluate an expression. -`abc` => `expression abc` +Parameters: `expression` -`a.b.c` => `expression a.b.c` +### evaluateIdentifier -## `expression ?:(expr: Expression)` bailing +`SyncBailHook` -`(abc ? 1 : 2)` => `expression ?!` +Evaluate an identifier that is a free variable. -Return a boolean value to omit parsing of the wrong path. +Parameters: `expression` -## `typeof (expr: Expression)` bailing +### evaluateDefinedIdentifier -`typeof a.b.c` => `typeof a.b.c` +`SyncBailHook` +Evaluate an identifier that is a defined variable. -## `statement if(statement: Statement)` bailing +Parameters: `expression` -`if(abc) {}` => `statement if` -Return a boolean value to omit parsing of the wrong path. +### evaluateCallExpressionMember +`SyncBailHook` -## `label (statement: Statement)` bailing +Evaluate a call to a member function of a successfully evaluated expression. -`xyz: abc` => `label xyz` +Parameters: `expression` `param` -## `var (statement: Statement)` bailing +### statement -`var abc, def` => `var abc` + `var def` +`SyncBailHook` -Return `false` to not add the variable to the known definitions. +General purpose hook that is called when parsing statements in a code fragment. +Parameters: `statement` -## `evaluate (expr: Expression)` bailing -Evaluate an expression. +### statementIf +`SyncBailHook` -## `evaluate typeof (expr: Expression)` bailing +... -Evaluate the type of an identifier. +Parameters: `statement` -## `evaluate Identifier (expr: Expression)` bailing +### label -Evaluate a identifier that is a free var. +`SyncBailHook` +... -## `evaluate defined Identifier (expr: Expression)` bailing +Parameters: `statement` -Evaluate a identifier that is a defined var. +### import -## `evaluate CallExpression .(expr: Expression)` bailing +`SyncBailHook` -Evaluate a call to a member function of a successfully evaluated expression. +... + +Parameters: `statement` `source` + + +### importSpecifier + +`SyncBailHook` + +... + +Parameters: `statement` `source` `exportName` `identifierName` + + +### export + +`SyncBailHook` + +... + +Parameters: `statement` + + +### exportImport + +`SyncBailHook` + +... + +Parameters: `statement` `source` + + +### exportDeclaration + +`SyncBailHook` + +... + +Parameters: `statement` `declaration` + + +### exportExpression + +`SyncBailHook` + +... + +Parameters: `statement` `declaration` + + +### exportSpecifier + +`SyncBailHook` + +... + +Parameters: `statement` `identifierName` `exportName` `index` + + +### exportImportSpecifier + +`SyncBailHook` + +... + +Parameters: `statement` `source` `identifierName` `exportName` `index` + + +### varDeclaration + +`SyncBailHook` + +... + +Parameters: `declaration` + + +### varDeclarationLet + +`SyncBailHook` + +... + +Parameters: `declaration` + + +### varDeclarationConst + +`SyncBailHook` + +... + +Parameters: `declaration` + + +### varDeclarationVar + +`SyncBailHook` + +... + +Parameters: `declaration` + + +### canRename + +`SyncBailHook` + +... + +Parameters: `initExpression` + + +### rename + +`SyncBailHook` + +... + +Parameters: `initExpression` + + +### assigned + +`SyncBailHook` + +... + +Parameters: `expression` + + +### assign + +`SyncBailHook` + +... + +Parameters: `expression` + + +### typeof + +`SyncBailHook` + +... + +Parameters: `expression` + + +### call + +`SyncBailHook` + +... + +Parameters: `expression` + + +### callAnyMember + +`SyncBailHook` + +... + +Parameters: `expression` + + +### new + +`SyncBailHook` + +... + +Parameters: `expression` + + +### expression + +`SyncBailHook` + +... + +Parameters: `expression` + + +### expressionAnyMember + +`SyncBailHook` + +... + +Parameters: `expression` + + +### expressionConditionalOperator + +`SyncBailHook` + +... + +Parameters: `expression` + + +### program + +`SyncBailHook` + +Get access to the abstract syntax tree (AST) of a code fragment + +Parameters: `ast` `comments` From 63e8fa15bcecf0f3a32d2a83e640b9036e1684fa Mon Sep 17 00:00:00 2001 From: Greg Venech Date: Tue, 6 Feb 2018 23:45:42 -0500 Subject: [PATCH 14/14] docs(api): update some of the compiler hook descriptions --- src/content/api/compilation-hooks.md | 8 ++++---- src/content/api/compiler-hooks.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/content/api/compilation-hooks.md b/src/content/api/compilation-hooks.md index 9f59d15f5310..2adf68b0ac55 100644 --- a/src/content/api/compilation-hooks.md +++ b/src/content/api/compilation-hooks.md @@ -62,7 +62,7 @@ Parameters: `module` `SyncHook` -... +All modules have been built. Parameters: `modules` @@ -71,7 +71,7 @@ Parameters: `modules` `SyncHook` -... +A module has been rebuilt. Parameters: `module` @@ -87,7 +87,7 @@ Fired when the compilation stops accepting new modules. `SyncHook` -... +Fired when a compilation begins accepting new modules. ### `optimizeDependenciesBasic` @@ -103,7 +103,7 @@ Parameters: `modules` `SyncBailHook` -... +Fired at the beginning of dependency optimization. Parameters: `modules` diff --git a/src/content/api/compiler-hooks.md b/src/content/api/compiler-hooks.md index ef592c8a2a5c..a3e3f5cf26d0 100644 --- a/src/content/api/compiler-hooks.md +++ b/src/content/api/compiler-hooks.md @@ -214,7 +214,7 @@ Parameters: `compilation` `SyncHook` -Completion of compile +Compilation has completed. Parameters: `stats` @@ -223,7 +223,7 @@ Parameters: `stats` `SyncHook` -Failure of compile +Compilation has failed. Parameters: `error` @@ -232,7 +232,7 @@ Parameters: `error` `SyncHook` -After invalidating a watch compile +Watch compilation has been invalidated. Parameters: `fileName`, `changeTime` @@ -241,4 +241,4 @@ Parameters: `fileName`, `changeTime` `SyncHook` -After stopping a watch compile +Watch mode has stopped.