diff --git a/index.js b/index.js index 36fae67f..7de4d282 100644 --- a/index.js +++ b/index.js @@ -378,15 +378,6 @@ class HardSourceWebpackPlugin { }); } - function runVerify(_compiler) { - if (!active) { - return Promise.resolve(); - } - - const stats = {}; - return pluginCompat.promise(compiler, '_hardSourceVerifyCache', []); - } - compilerHooks.watchRun.tapPromise( 'HardSource - index - readOrReset', runReadOrReset, @@ -515,6 +506,15 @@ class HardSourceWebpackPlugin { new ChalkLoggerPlugin(this.options.info).apply(compiler); + function runVerify(_compiler) { + if (!active) { + return Promise.resolve(); + } + + const stats = {}; + return pluginCompat.promise(compiler, '_hardSourceVerifyCache', []); + } + compilerHooks.watchRun.tapPromise('HardSource - index - verify', runVerify); compilerHooks.run.tapPromise('HardSource - index - verify', runVerify); diff --git a/lib/TransformNormalModuleFactoryPlugin.js b/lib/TransformNormalModuleFactoryPlugin.js index 6a872fc3..9653a0b8 100644 --- a/lib/TransformNormalModuleFactoryPlugin.js +++ b/lib/TransformNormalModuleFactoryPlugin.js @@ -33,6 +33,41 @@ class NormalModuleFactoryPlugin { // compilation.__hardSourceFileMd5s = fileMd5s; // compilation.__hardSourceCachedMd5s = cachedMd5s; + const compilationHooks = pluginCompat.hooks(compilation); + compilationHooks.buildModule.tap( + 'HardSource - TransformNormalModuleFactoryPlugin', + module => { + if (module.constructor.name === 'NormalModule') { + const _createLoaderContext = module.createLoaderContext; + module.__hardSource_resolved = {}; + module.createLoaderContext = (...args) => { + const loaderContext = _createLoaderContext.call( + module, + ...args, + ); + const _resolve = loaderContext.resolve; + loaderContext.resolve = (context, request, callback) => { + _resolve.call( + loaderContext, + context, + request, + (err, result) => { + module.__hardSource_resolved[ + JSON.stringify({ context, request }) + ] = { + resource: result, + resolveOptions: module.resolveOptions, + }; + callback(err, result); + }, + ); + }; + return loaderContext; + }; + } + }, + ); + const normalModuleFactoryHooks = pluginCompat.hooks( normalModuleFactory, ); diff --git a/lib/TransformNormalModulePlugin.js b/lib/TransformNormalModulePlugin.js index 5f2f409b..cababd7d 100644 --- a/lib/TransformNormalModulePlugin.js +++ b/lib/TransformNormalModulePlugin.js @@ -1,6 +1,8 @@ const NormalModule = require('webpack/lib/NormalModule'); const Module = require('webpack/lib/Module'); +const nodeObjectHash = require('node-object-hash'); + const logMessages = require('./util/log-messages'); const { relateNormalPath, @@ -11,6 +13,44 @@ const { const pluginCompat = require('./util/plugin-compat'); const serial = require('./util/serial'); +const serialResolveRequest = serial.created({ + context: serial.path, + request: serial.request, +}); + +const serialResolved = serial.created({ + // context: serial.path, + // request: serial.request, + // userRequest: serial.request, + // rawRequest: serial.request, + resource: serial.request, + resolveOptions: serial.identity, + // loaders: serial.loaders, +}); + +const serialResolvedMap = { + freeze(arg, module, extra) { + const resolved = []; + for (const key in arg) { + const thawedKey = JSON.parse(key); + resolved.push([ + serialResolveRequest.freeze(thawedKey, thawedKey, extra), + serialResolved.freeze(arg[key], arg[key], extra), + ]); + } + return resolved; + }, + thaw(arg, frozen, extra) { + const resolved = {}; + for (const item of arg) { + const key = serialResolveRequest.thaw(item[0], item[0], extra); + const value = serialResolved.thaw(item[1], item[1], extra); + resolved[JSON.stringify(key)] = value; + } + return resolved; + }, +}; + const serialNormalModule4 = serial.serial('NormalModule', { constructor: serial.constructed(NormalModule, { data: serial.pipe( @@ -124,6 +164,8 @@ const serialNormalModule4 = serial.serial('NormalModule', { _buildHash: serial.identity, hash: serial.identity, _lastSuccessfulBuildMeta: serial.identity, + + __hardSource_resolved: serialResolvedMap, }), dependencyBlock: serial.dependencyBlock, @@ -159,6 +201,9 @@ const needRebuild4 = function() { } const fileHashes = this.__hardSourceFileMd5s; const cachedHashes = this.__hardSourceCachedMd5s; + const resolvedLast = this.__hardSource_resolved; + const missingCache = this.__hardSource_missingCache; + for (const file of this.buildInfo.fileDependencies) { if (!cachedHashes[file] || fileHashes[file] !== cachedHashes[file]) { this.cacheItem.invalid = true; @@ -173,7 +218,33 @@ const needRebuild4 = function() { return true; } } - return false; + + let resolvedNeedRebuild = false; + for (const _resolveKey in resolvedLast) { + const resolveKey = JSON.parse(_resolveKey); + const resolved = resolvedLast[_resolveKey]; + let normalId = 'normal'; + if (resolved.resolveOptions) { + normalId = `normal-${new nodeObjectHash({ sort: false }).hash( + resolved.resolveOptions, + )}`; + } + const resolvedMissing = + missingCache[normalId] && + missingCache[normalId][ + JSON.stringify([resolveKey.context, resolved.resource.split('?')[0]]) + ]; + if (!resolvedMissing || resolvedMissing.invalid) { + resolved.invalid = true; + resolved.invalidReason = `resolved normal invalid${ + resolvedMissing + ? ` ${resolvedMissing.invalidReason}` + : ': resolve entry not in cache' + }`; + resolvedNeedRebuild = true; + } + } + return resolvedNeedRebuild; }; const serialNormalModule3 = serial.serial('NormalModule', { @@ -275,6 +346,8 @@ const serialNormalModule3 = serial.serial('NormalModule', { warnings: serial.moduleWarning, errors: serial.moduleError, _source: serial.source, + + __hardSource_resolved: serialResolvedMap, }), hash: { @@ -320,6 +393,9 @@ const needRebuild3 = function() { } const fileHashes = this.__hardSourceFileMd5s; const cachedHashes = this.__hardSourceCachedMd5s; + const resolvedLast = this.__hardSource_resolved; + const missingCache = this.__hardSource_missingCache; + for (const file of this.fileDependencies) { if (!cachedHashes[file] || fileHashes[file] !== cachedHashes[file]) { this.cacheItem.invalid = true; @@ -334,7 +410,34 @@ const needRebuild3 = function() { return true; } } - return false; + + let resolvedNeedRebuild = false; + for (const _resolveKey in resolvedLast) { + const resolveKey = JSON.parse(_resolveKey); + const resolved = resolvedLast[_resolveKey]; + let normalId = 'normal'; + if (resolved.resolveOptions) { + normalId = `normal-${new nodeObjectHash({ sort: false }).hash( + resolved.resolveOptions, + )}`; + } + const resolvedMissing = + missingCache[normalId] && + missingCache[normalId][ + JSON.stringify([resolveKey.context, resolved.resource.split('?')[0]]) + ]; + if (!resolvedMissing || resolvedMissing.invalid) { + resolved.invalid = true; + resolved.invalidReason = `resolved normal invalid${ + resolvedMissing + ? ` ${resolvedMissing.invalidReason}` + : ': resolve entry not in cache' + }`; + resolvedNeedRebuild = true; + } + } + + return resolvedNeedRebuild; }; const cacheable = module => @@ -498,6 +601,7 @@ class TransformNormalModulePlugin { m.cacheItem = frozen; m.__hardSourceFileMd5s = compilation.__hardSourceFileMd5s; m.__hardSourceCachedMd5s = compilation.__hardSourceCachedMd5s; + m.__hardSource_missingCache = compiler.__hardSource_missingCache; m.needRebuild = needRebuild; // Unbuild if there is no cache. The module will be rebuilt. Not diff --git a/lib/util/plugin-compat.js b/lib/util/plugin-compat.js index 0b218d8c..5c9a3904 100644 --- a/lib/util/plugin-compat.js +++ b/lib/util/plugin-compat.js @@ -17,6 +17,7 @@ const knownPluginRegistrations = { Compilation: { needAdditionalPass: ['sync', []], succeedModule: ['sync', ['module']], + buildModule: ['sync', ['module']], }, Compiler: { afterCompile: ['asyncSerial', ['compilation']], diff --git a/tests/fixtures/loader-custom-resolve-request/fab/index.js b/tests/fixtures/loader-custom-resolve-request/fab/index.js new file mode 100644 index 00000000..79424e7d --- /dev/null +++ b/tests/fixtures/loader-custom-resolve-request/fab/index.js @@ -0,0 +1,3 @@ +module.exports = function(n) { + return n * (n > 1 ? n - 1 : 1); +}; diff --git a/tests/fixtures/loader-custom-resolve-request/fib.js b/tests/fixtures/loader-custom-resolve-request/fib.js new file mode 100644 index 00000000..bf0205d4 --- /dev/null +++ b/tests/fixtures/loader-custom-resolve-request/fib.js @@ -0,0 +1,3 @@ +module.exports = function(n) { + return n + (n > 0 ? n - 2 : 0); +}; \ No newline at end of file diff --git a/tests/fixtures/loader-custom-resolve-request/index.js b/tests/fixtures/loader-custom-resolve-request/index.js new file mode 100644 index 00000000..d787238f --- /dev/null +++ b/tests/fixtures/loader-custom-resolve-request/index.js @@ -0,0 +1,3 @@ +var fib = require('./loader!./fib'); + +console.log(fib(3)); diff --git a/tests/fixtures/loader-custom-resolve-request/loader.js b/tests/fixtures/loader-custom-resolve-request/loader.js new file mode 100644 index 00000000..11c81af6 --- /dev/null +++ b/tests/fixtures/loader-custom-resolve-request/loader.js @@ -0,0 +1,7 @@ +module.exports = function(source) { + this.cacheable && this.cacheable(); + return new Promise( + (f, e) => this.resolve(this.context, './fab', (er, v) => er ? e(er) : f(v)) + ) + .then(v => JSON.stringify(v)); +}; diff --git a/tests/fixtures/loader-custom-resolve-request/webpack.config.js b/tests/fixtures/loader-custom-resolve-request/webpack.config.js new file mode 100644 index 00000000..da370314 --- /dev/null +++ b/tests/fixtures/loader-custom-resolve-request/webpack.config.js @@ -0,0 +1,18 @@ +var HardSourceWebpackPlugin = require('../../..'); + +module.exports = { + context: __dirname, + entry: './index.js', + output: { + path: __dirname + '/tmp', + filename: 'main.js', + }, + plugins: [ + new HardSourceWebpackPlugin({ + cacheDirectory: 'cache', + environmentHash: { + root: __dirname + '/../../..', + }, + }), + ], +}; diff --git a/tests/fixtures/plugin-mini-css-extract-change/index.css b/tests/fixtures/plugin-mini-css-extract-change/index.css index 805f9c0c..e7c946d2 100644 --- a/tests/fixtures/plugin-mini-css-extract-change/index.css +++ b/tests/fixtures/plugin-mini-css-extract-change/index.css @@ -1,3 +1,3 @@ .hello { - color: blue; + color: red; } \ No newline at end of file diff --git a/tests/loaders-webpack-3.js b/tests/loaders-webpack-3.js index d8c02d20..9b1c5057 100644 --- a/tests/loaders-webpack-3.js +++ b/tests/loaders-webpack-3.js @@ -11,3 +11,26 @@ describeWP(3)('loader webpack 3 use', function() { itCompilesTwice('loader-worker-1dep', {exportStats: true}); }); + +describeWP(3)('loader webpack 3 use - builds changes', function() { + + itCompilesChange('loader-custom-resolve-request', { + 'fab.js': null, + }, { + 'fab.js': 'bar', + }, function(output) { + expect(output.run1['main.js'].toString()).to.match(/fab(\/|\\\\|\\\\\\\\)index\.js/); + expect(output.run2['main.js'].toString()).to.match(/fab\.js/); + }); + + itCompilesChange('loader-custom-resolve-request', { + 'fab.js': 'bar', + }, { + 'fab.js': null, + }, function(output) { + expect(output.run1['main.js'].toString()).to.match(/fab\.js/); + expect(output.run2['main.js'].toString()).to.match(/fab(\/|\\\\|\\\\\\\\)index\.js/); + }); + +}); +