diff --git a/packages/webpack/package.json b/packages/webpack/package.json index c98247dfdf..a3d7955588 100644 --- a/packages/webpack/package.json +++ b/packages/webpack/package.json @@ -46,6 +46,7 @@ "vue-loader": "^15.9.6", "vue-style-loader": "^4.1.2", "vue-template-compiler": "^2.6.12", + "watchpack": "^2.1.1", "webpack": "^4.46.0", "webpack-bundle-analyzer": "^4.4.0", "webpack-dev-middleware": "^4.1.0", diff --git a/packages/webpack/src/config/base.js b/packages/webpack/src/config/base.js index 586e6455b3..42449046c2 100644 --- a/packages/webpack/src/config/base.js +++ b/packages/webpack/src/config/base.js @@ -16,6 +16,7 @@ import { TARGETS, isUrl, urlJoin, getPKG, tryResolve, requireModule, resolveModu import PerfLoader from '../utils/perf-loader' import StyleLoader from '../utils/style-loader' import WarningIgnorePlugin from '../plugins/warning-ignore' +import { Watchpack2Plugin } from '../plugins/watchpack' import { reservedVueTags } from '../utils/reserved-tags' export default class WebpackBaseConfig { @@ -476,6 +477,8 @@ export default class WebpackBaseConfig { })) } + plugins.push(new Watchpack2Plugin()) + return plugins } diff --git a/packages/webpack/src/plugins/watchpack.js b/packages/webpack/src/plugins/watchpack.js new file mode 100644 index 0000000000..5a49d3b2ea --- /dev/null +++ b/packages/webpack/src/plugins/watchpack.js @@ -0,0 +1,116 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Base: https://github.com/webpack/webpack/blob/v4.46.0/lib/node/NodeWatchFileSystem.js (Tobias Koppers @sokra) +*/ +import Watchpack from 'watchpack' +import objectToMap from 'webpack/lib/util/objectToMap' + +export class Watchpack2Plugin { + apply (compiler) { + if (compiler.watchFileSystem && compiler.watchFileSystem.watcher) { + compiler.watchFileSystem.watcher.close() + } + compiler.watchFileSystem = new NodeWatchFileSystem( + compiler.inputFileSystem + ) + } +} + +export class NodeWatchFileSystem { + constructor (inputFileSystem) { + this.inputFileSystem = inputFileSystem + this.watcherOptions = { + aggregateTimeout: 0 + } + this.watcher = new Watchpack(this.watcherOptions) + } + + watch (files, dirs, missing, startTime, options, callback, callbackUndelayed) { + if (!Array.isArray(files)) { + throw new TypeError("Invalid arguments: 'files'") + } + if (!Array.isArray(dirs)) { + throw new TypeError("Invalid arguments: 'dirs'") + } + if (!Array.isArray(missing)) { + throw new TypeError("Invalid arguments: 'missing'") + } + if (typeof callback !== 'function') { + throw new TypeError("Invalid arguments: 'callback'") + } + if (typeof startTime !== 'number' && startTime) { + throw new Error("Invalid arguments: 'startTime'") + } + if (typeof options !== 'object') { + throw new TypeError("Invalid arguments: 'options'") + } + if (typeof callbackUndelayed !== 'function' && callbackUndelayed) { + throw new Error("Invalid arguments: 'callbackUndelayed'") + } + const oldWatcher = this.watcher + this.watcher = new Watchpack(options) + + if (callbackUndelayed) { + this.watcher.once('change', callbackUndelayed) + } + const cachedFiles = files + const cachedDirs = dirs + this.watcher.once('aggregated', (_changes, _removals) => { + const removals = Array.from(_removals) + const changes = Array.from(_changes).concat(removals) + if (this.inputFileSystem && this.inputFileSystem.purge) { + this.inputFileSystem.purge(changes) + } + const times = objectToMap(this.watcher.getTimes()) + files = new Set(files) + dirs = new Set(dirs) + missing = new Set(missing) + callback( + null, + changes.filter(file => files.has(file)).sort(), + changes.filter(file => dirs.has(file)).sort(), + changes.filter(file => missing.has(file)).sort(), + times, + times, + new Set(removals.filter(file => files.has(file))) + ) + }) + + this.watcher.watch( + cachedFiles.concat(missing), + cachedDirs.concat(missing), + startTime + ) + + if (oldWatcher) { + oldWatcher.close() + } + return { + close: () => { + if (this.watcher) { + this.watcher.close() + this.watcher = null + } + }, + pause: () => { + if (this.watcher) { + this.watcher.pause() + } + }, + getFileTimestamps: () => { + if (this.watcher) { + return objectToMap(this.watcher.getTimes()) + } else { + return new Map() + } + }, + getContextTimestamps: () => { + if (this.watcher) { + return objectToMap(this.watcher.getTimes()) + } else { + return new Map() + } + } + } + } +} diff --git a/yarn.lock b/yarn.lock index 39f9830f7d..eb4e1de5d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6696,6 +6696,11 @@ glob-to-regexp@^0.3.0: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + glob@7.1.6, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -13546,6 +13551,14 @@ watchpack@^1.7.4: chokidar "^3.4.1" watchpack-chokidar2 "^2.0.1" +watchpack@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7" + integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + wcwidth@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"