Skip to content

Commit 2d0df51

Browse files
authored
feat: store low level resolutions from built module (#392)
NormalModule can resolve other files while building. Store these resolutions and compare them in future builds to determine if the module should be rebuilt.
1 parent 25d660b commit 2d0df51

File tree

11 files changed

+209
-12
lines changed

11 files changed

+209
-12
lines changed

index.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -378,15 +378,6 @@ class HardSourceWebpackPlugin {
378378
});
379379
}
380380

381-
function runVerify(_compiler) {
382-
if (!active) {
383-
return Promise.resolve();
384-
}
385-
386-
const stats = {};
387-
return pluginCompat.promise(compiler, '_hardSourceVerifyCache', []);
388-
}
389-
390381
compilerHooks.watchRun.tapPromise(
391382
'HardSource - index - readOrReset',
392383
runReadOrReset,
@@ -515,6 +506,15 @@ class HardSourceWebpackPlugin {
515506

516507
new ChalkLoggerPlugin(this.options.info).apply(compiler);
517508

509+
function runVerify(_compiler) {
510+
if (!active) {
511+
return Promise.resolve();
512+
}
513+
514+
const stats = {};
515+
return pluginCompat.promise(compiler, '_hardSourceVerifyCache', []);
516+
}
517+
518518
compilerHooks.watchRun.tapPromise('HardSource - index - verify', runVerify);
519519
compilerHooks.run.tapPromise('HardSource - index - verify', runVerify);
520520

lib/TransformNormalModuleFactoryPlugin.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,41 @@ class NormalModuleFactoryPlugin {
3333
// compilation.__hardSourceFileMd5s = fileMd5s;
3434
// compilation.__hardSourceCachedMd5s = cachedMd5s;
3535

36+
const compilationHooks = pluginCompat.hooks(compilation);
37+
compilationHooks.buildModule.tap(
38+
'HardSource - TransformNormalModuleFactoryPlugin',
39+
module => {
40+
if (module.constructor.name === 'NormalModule') {
41+
const _createLoaderContext = module.createLoaderContext;
42+
module.__hardSource_resolved = {};
43+
module.createLoaderContext = (...args) => {
44+
const loaderContext = _createLoaderContext.call(
45+
module,
46+
...args,
47+
);
48+
const _resolve = loaderContext.resolve;
49+
loaderContext.resolve = (context, request, callback) => {
50+
_resolve.call(
51+
loaderContext,
52+
context,
53+
request,
54+
(err, result) => {
55+
module.__hardSource_resolved[
56+
JSON.stringify({ context, request })
57+
] = {
58+
resource: result,
59+
resolveOptions: module.resolveOptions,
60+
};
61+
callback(err, result);
62+
},
63+
);
64+
};
65+
return loaderContext;
66+
};
67+
}
68+
},
69+
);
70+
3671
const normalModuleFactoryHooks = pluginCompat.hooks(
3772
normalModuleFactory,
3873
);

lib/TransformNormalModulePlugin.js

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
const NormalModule = require('webpack/lib/NormalModule');
22
const Module = require('webpack/lib/Module');
33

4+
const nodeObjectHash = require('node-object-hash');
5+
46
const logMessages = require('./util/log-messages');
57
const {
68
relateNormalPath,
@@ -11,6 +13,44 @@ const {
1113
const pluginCompat = require('./util/plugin-compat');
1214
const serial = require('./util/serial');
1315

16+
const serialResolveRequest = serial.created({
17+
context: serial.path,
18+
request: serial.request,
19+
});
20+
21+
const serialResolved = serial.created({
22+
// context: serial.path,
23+
// request: serial.request,
24+
// userRequest: serial.request,
25+
// rawRequest: serial.request,
26+
resource: serial.request,
27+
resolveOptions: serial.identity,
28+
// loaders: serial.loaders,
29+
});
30+
31+
const serialResolvedMap = {
32+
freeze(arg, module, extra) {
33+
const resolved = [];
34+
for (const key in arg) {
35+
const thawedKey = JSON.parse(key);
36+
resolved.push([
37+
serialResolveRequest.freeze(thawedKey, thawedKey, extra),
38+
serialResolved.freeze(arg[key], arg[key], extra),
39+
]);
40+
}
41+
return resolved;
42+
},
43+
thaw(arg, frozen, extra) {
44+
const resolved = {};
45+
for (const item of arg) {
46+
const key = serialResolveRequest.thaw(item[0], item[0], extra);
47+
const value = serialResolved.thaw(item[1], item[1], extra);
48+
resolved[JSON.stringify(key)] = value;
49+
}
50+
return resolved;
51+
},
52+
};
53+
1454
const serialNormalModule4 = serial.serial('NormalModule', {
1555
constructor: serial.constructed(NormalModule, {
1656
data: serial.pipe(
@@ -124,6 +164,8 @@ const serialNormalModule4 = serial.serial('NormalModule', {
124164
_buildHash: serial.identity,
125165
hash: serial.identity,
126166
_lastSuccessfulBuildMeta: serial.identity,
167+
168+
__hardSource_resolved: serialResolvedMap,
127169
}),
128170

129171
dependencyBlock: serial.dependencyBlock,
@@ -159,6 +201,9 @@ const needRebuild4 = function() {
159201
}
160202
const fileHashes = this.__hardSourceFileMd5s;
161203
const cachedHashes = this.__hardSourceCachedMd5s;
204+
const resolvedLast = this.__hardSource_resolved;
205+
const missingCache = this.__hardSource_missingCache;
206+
162207
for (const file of this.buildInfo.fileDependencies) {
163208
if (!cachedHashes[file] || fileHashes[file] !== cachedHashes[file]) {
164209
this.cacheItem.invalid = true;
@@ -173,7 +218,33 @@ const needRebuild4 = function() {
173218
return true;
174219
}
175220
}
176-
return false;
221+
222+
let resolvedNeedRebuild = false;
223+
for (const _resolveKey in resolvedLast) {
224+
const resolveKey = JSON.parse(_resolveKey);
225+
const resolved = resolvedLast[_resolveKey];
226+
let normalId = 'normal';
227+
if (resolved.resolveOptions) {
228+
normalId = `normal-${new nodeObjectHash({ sort: false }).hash(
229+
resolved.resolveOptions,
230+
)}`;
231+
}
232+
const resolvedMissing =
233+
missingCache[normalId] &&
234+
missingCache[normalId][
235+
JSON.stringify([resolveKey.context, resolved.resource.split('?')[0]])
236+
];
237+
if (!resolvedMissing || resolvedMissing.invalid) {
238+
resolved.invalid = true;
239+
resolved.invalidReason = `resolved normal invalid${
240+
resolvedMissing
241+
? ` ${resolvedMissing.invalidReason}`
242+
: ': resolve entry not in cache'
243+
}`;
244+
resolvedNeedRebuild = true;
245+
}
246+
}
247+
return resolvedNeedRebuild;
177248
};
178249

179250
const serialNormalModule3 = serial.serial('NormalModule', {
@@ -275,6 +346,8 @@ const serialNormalModule3 = serial.serial('NormalModule', {
275346
warnings: serial.moduleWarning,
276347
errors: serial.moduleError,
277348
_source: serial.source,
349+
350+
__hardSource_resolved: serialResolvedMap,
278351
}),
279352

280353
hash: {
@@ -320,6 +393,9 @@ const needRebuild3 = function() {
320393
}
321394
const fileHashes = this.__hardSourceFileMd5s;
322395
const cachedHashes = this.__hardSourceCachedMd5s;
396+
const resolvedLast = this.__hardSource_resolved;
397+
const missingCache = this.__hardSource_missingCache;
398+
323399
for (const file of this.fileDependencies) {
324400
if (!cachedHashes[file] || fileHashes[file] !== cachedHashes[file]) {
325401
this.cacheItem.invalid = true;
@@ -334,7 +410,34 @@ const needRebuild3 = function() {
334410
return true;
335411
}
336412
}
337-
return false;
413+
414+
let resolvedNeedRebuild = false;
415+
for (const _resolveKey in resolvedLast) {
416+
const resolveKey = JSON.parse(_resolveKey);
417+
const resolved = resolvedLast[_resolveKey];
418+
let normalId = 'normal';
419+
if (resolved.resolveOptions) {
420+
normalId = `normal-${new nodeObjectHash({ sort: false }).hash(
421+
resolved.resolveOptions,
422+
)}`;
423+
}
424+
const resolvedMissing =
425+
missingCache[normalId] &&
426+
missingCache[normalId][
427+
JSON.stringify([resolveKey.context, resolved.resource.split('?')[0]])
428+
];
429+
if (!resolvedMissing || resolvedMissing.invalid) {
430+
resolved.invalid = true;
431+
resolved.invalidReason = `resolved normal invalid${
432+
resolvedMissing
433+
? ` ${resolvedMissing.invalidReason}`
434+
: ': resolve entry not in cache'
435+
}`;
436+
resolvedNeedRebuild = true;
437+
}
438+
}
439+
440+
return resolvedNeedRebuild;
338441
};
339442

340443
const cacheable = module =>
@@ -498,6 +601,7 @@ class TransformNormalModulePlugin {
498601
m.cacheItem = frozen;
499602
m.__hardSourceFileMd5s = compilation.__hardSourceFileMd5s;
500603
m.__hardSourceCachedMd5s = compilation.__hardSourceCachedMd5s;
604+
m.__hardSource_missingCache = compiler.__hardSource_missingCache;
501605
m.needRebuild = needRebuild;
502606

503607
// Unbuild if there is no cache. The module will be rebuilt. Not

lib/util/plugin-compat.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const knownPluginRegistrations = {
1717
Compilation: {
1818
needAdditionalPass: ['sync', []],
1919
succeedModule: ['sync', ['module']],
20+
buildModule: ['sync', ['module']],
2021
},
2122
Compiler: {
2223
afterCompile: ['asyncSerial', ['compilation']],
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = function(n) {
2+
return n * (n > 1 ? n - 1 : 1);
3+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = function(n) {
2+
return n + (n > 0 ? n - 2 : 0);
3+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var fib = require('./loader!./fib');
2+
3+
console.log(fib(3));
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = function(source) {
2+
this.cacheable && this.cacheable();
3+
return new Promise(
4+
(f, e) => this.resolve(this.context, './fab', (er, v) => er ? e(er) : f(v))
5+
)
6+
.then(v => JSON.stringify(v));
7+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
var HardSourceWebpackPlugin = require('../../..');
2+
3+
module.exports = {
4+
context: __dirname,
5+
entry: './index.js',
6+
output: {
7+
path: __dirname + '/tmp',
8+
filename: 'main.js',
9+
},
10+
plugins: [
11+
new HardSourceWebpackPlugin({
12+
cacheDirectory: 'cache',
13+
environmentHash: {
14+
root: __dirname + '/../../..',
15+
},
16+
}),
17+
],
18+
};
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
.hello {
2-
color: blue;
2+
color: red;
33
}

0 commit comments

Comments
 (0)