const nodeObjectHash = require('node-object-hash');
|
const parseJson = require('parse-json');
|
|
const serial = require('./util/serial');
|
const pluginCompat = require('./util/plugin-compat');
|
const relateContext = require('./util/relate-context');
|
const { parityCacheFromCache, pushParityWriteOps } = require('./util/parity');
|
|
const serialJsonKey = {
|
freeze(arg, value, extra) {
|
return JSON.parse(arg);
|
},
|
thaw(arg, frozen, extra) {
|
return JSON.stringify(arg);
|
},
|
};
|
|
const serialJson = {
|
freeze(arg, value, extra) {
|
return JSON.stringify(arg);
|
},
|
thaw(arg, frozen, extra) {
|
return JSON.parse(arg);
|
},
|
};
|
|
const serialObjectAssign = serial.objectAssign;
|
|
const serialResolveOptionsKey = serialObjectAssign({
|
context: serial.path,
|
userRequest: serial.request,
|
options: serialObjectAssign({
|
request: serial.request,
|
}),
|
});
|
|
const serialResolveKey = serialObjectAssign({
|
context: serial.path,
|
request: serial.request,
|
});
|
|
const serialNormalModuleResolveKey = serial.pipe(
|
serialJsonKey,
|
{
|
freeze(arg, key, extra) {
|
if (Array.isArray(arg)) {
|
return [
|
arg[0],
|
serial.path.freeze(arg[1], arg[1], extra),
|
serial.request.freeze(arg[2], arg[2], extra),
|
];
|
} else if (!arg.request) {
|
return serialResolveOptionsKey.freeze(arg, arg, extra);
|
} else {
|
return serialResolveKey.freeze(arg, arg, extra);
|
}
|
},
|
thaw(arg, frozen, extra) {
|
if (Array.isArray(arg)) {
|
return [
|
arg[0],
|
serial.path.thaw(arg[1], arg[1], extra),
|
serial.request.thaw(arg[2], arg[2], extra),
|
];
|
} else if (!arg.request) {
|
return serialResolveOptionsKey.thaw(arg, arg, extra);
|
} else {
|
return serialResolveKey.thaw(arg, arg, extra);
|
}
|
},
|
},
|
serialJson,
|
);
|
|
const serialNormalModuleId = {
|
freeze(arg, module, extra) {
|
return (
|
id.substring(0, 24) + serial.request.freeze(id.substring(24), id, extra)
|
);
|
},
|
thaw(arg, frozen, extra) {
|
return (
|
id.substring(0, 24) + serial.request.thaw(id.substring(24), id, extra)
|
);
|
},
|
};
|
|
const serialResolveContext = serialObjectAssign({
|
identifier: serialNormalModuleId,
|
resource: serialNormalModuleId,
|
});
|
|
const serialResolveNormal = serialObjectAssign({
|
context: serial.path,
|
request: serial.request,
|
userRequest: serial.request,
|
rawRequest: serial.request,
|
resource: serial.request,
|
loaders: serial.loaders,
|
resourceResolveData: serial.objectAssign({
|
context: serial.created({
|
issuer: serial.request,
|
resolveOptions: serial.identity,
|
}),
|
path: serial.path,
|
request: serial.request,
|
descriptionFilePath: serial.path,
|
descriptionFileRoot: serial.path,
|
}),
|
});
|
|
const serialResolve = {
|
freeze(arg, module, extra) {
|
if (arg.type === 'context') {
|
return serialResolveContext.freeze(arg, arg, extra);
|
}
|
return serialResolveNormal.freeze(arg, arg, extra);
|
},
|
thaw(arg, frozen, extra) {
|
if (arg.type === 'context') {
|
return serialResolveContext.thaw(arg, arg, extra);
|
}
|
return serialResolveNormal.thaw(arg, arg, extra);
|
},
|
};
|
|
class ModuleResolverCache {
|
apply(compiler) {
|
let moduleResolveCache = {};
|
let parityCache = {};
|
|
let moduleResolveCacheChange = [];
|
|
let moduleResolveCacheSerializer;
|
|
const compilerHooks = pluginCompat.hooks(compiler);
|
|
compilerHooks._hardSourceCreateSerializer.tap(
|
'HardSource - ModuleResolverCache',
|
(cacheSerializerFactory, cacheDirPath) => {
|
moduleResolveCacheSerializer = cacheSerializerFactory.create({
|
name: 'module-resolve',
|
type: 'data',
|
autoParse: true,
|
cacheDirPath,
|
});
|
},
|
);
|
|
compilerHooks._hardSourceResetCache.tap(
|
'HardSource - ModuleResolverCache',
|
() => {
|
moduleResolveCache = {};
|
parityCache = {};
|
},
|
);
|
|
compilerHooks._hardSourceReadCache.tapPromise(
|
'HardSource - ModuleResolverCache',
|
({
|
contextKeys,
|
contextValues,
|
contextNormalPath,
|
contextNormalRequest,
|
contextNormalModuleId,
|
copyWithDeser,
|
}) => {
|
function contextNormalModuleResolveKey(compiler, key) {
|
if (key.startsWith('__hardSource_parityToken')) {
|
return key;
|
}
|
const parsed = parseJson(key);
|
if (Array.isArray(parsed)) {
|
return JSON.stringify([
|
parsed[0],
|
contextNormalPath(compiler, parsed[1]),
|
parsed[2],
|
]);
|
} else {
|
return JSON.stringify(
|
Object.assign({}, parsed, {
|
context: contextNormalPath(compiler, parsed.context),
|
}),
|
);
|
}
|
}
|
|
function contextNormalModuleResolve(compiler, resolved, key) {
|
if (key.startsWith('__hardSource_parityToken')) {
|
parityCache[key] = resolved;
|
return;
|
}
|
if (typeof resolved === 'string') {
|
resolved = parseJson(resolved);
|
}
|
if (resolved.type === 'context') {
|
return Object.assign({}, resolved, {
|
identifier: contextNormalModuleId(compiler, resolved.identifier),
|
resource: contextNormalRequest(compiler, resolved.resource),
|
});
|
}
|
return serialResolveNormal.thaw(resolved, resolved, {
|
compiler,
|
});
|
}
|
|
return moduleResolveCacheSerializer
|
.read()
|
.then(contextKeys(compiler, contextNormalModuleResolveKey))
|
.then(contextValues(compiler, contextNormalModuleResolve))
|
.then(copyWithDeser.bind(null, moduleResolveCache));
|
},
|
);
|
|
compilerHooks._hardSourceParityCache.tap(
|
'HardSource - ModuleResolverCache',
|
parityRoot => {
|
parityCacheFromCache('ModuleResolver', parityRoot, parityCache);
|
},
|
);
|
|
compilerHooks._hardSourceVerifyCache.tapPromise(
|
'HardSource - ModuleResolverCache',
|
() =>
|
compiler.__hardSource_missingVerify.then(() => {
|
const missingCache = compiler.__hardSource_missingCache;
|
|
// Invalidate resolve cache items.
|
Object.keys(moduleResolveCache).forEach(key => {
|
const resolveKey = parseJson(key);
|
const resolveItem = moduleResolveCache[key];
|
let normalId = 'normal';
|
if (resolveItem.resolveOptions) {
|
normalId = `normal-${new nodeObjectHash({ sort: false }).hash(
|
resolveItem.resolveOptions,
|
)}`;
|
}
|
if (resolveItem.type === 'context') {
|
const contextMissing =
|
missingCache.context[
|
JSON.stringify([
|
resolveKey.context,
|
resolveItem.resource.split('?')[0],
|
])
|
];
|
if (!contextMissing || contextMissing.invalid) {
|
resolveItem.invalid = true;
|
resolveItem.invalidReason = 'resolved context invalid';
|
}
|
} else {
|
const normalMissing =
|
missingCache[normalId] &&
|
missingCache[normalId][
|
JSON.stringify([
|
resolveKey[1],
|
resolveItem.resource.split('?')[0],
|
])
|
];
|
if (!normalMissing || normalMissing.invalid) {
|
resolveItem.invalid = true;
|
resolveItem.invalidReason = `resolved normal invalid${
|
normalMissing
|
? ` ${normalMissing.invalidReason}`
|
: ': resolve entry not in cache'
|
}`;
|
}
|
resolveItem.loaders.forEach(loader => {
|
if (typeof loader === 'object') {
|
if (loader.loader != null) {
|
loader = loader.loader;
|
} else {
|
// Convert { "0": "b", "1": "a", "2": "r" } into "bar"
|
loader = Object.assign([], loader).join('');
|
}
|
}
|
// Loaders specified in a dependency are searched for from the
|
// context of the module containing that dependency.
|
let loaderMissing =
|
missingCache.loader[
|
JSON.stringify([resolveKey[1], loader.split('?')[0]])
|
];
|
if (!loaderMissing) {
|
// webpack searches for rule based loaders from the project
|
// context.
|
loaderMissing =
|
missingCache.loader[
|
JSON.stringify([
|
// compiler may be a Watching instance, which refers to the
|
// compiler
|
(compiler.options || compiler.compiler.options).context,
|
loader.split('?')[0],
|
])
|
];
|
}
|
if (!loaderMissing || loaderMissing.invalid) {
|
resolveItem.invalid = true;
|
resolveItem.invalidReason = 'resolved loader invalid';
|
}
|
});
|
}
|
});
|
}),
|
);
|
|
compilerHooks.compilation.tap(
|
'HardSource - ModuleResolverCache',
|
compilation => {
|
compilation.__hardSourceModuleResolveCache = moduleResolveCache;
|
compilation.__hardSourceModuleResolveCacheChange = moduleResolveCacheChange;
|
},
|
);
|
|
compilerHooks._hardSourceWriteCache.tapPromise(
|
'HardSource - ModuleResolverCache',
|
(
|
compilation,
|
{ relateNormalPath, relateNormalModuleId, relateNormalRequest },
|
) => {
|
if (compilation.compiler.parentCompilation) {
|
const moduleResolveOps = [];
|
pushParityWriteOps(compilation, moduleResolveOps);
|
|
return moduleResolveCacheSerializer.write(moduleResolveOps);
|
}
|
|
const moduleResolveOps = [];
|
|
function relateNormalModuleResolveKey(compiler, key) {
|
const parsed = parseJson(key);
|
if (Array.isArray(parsed)) {
|
return JSON.stringify([
|
parsed[0],
|
relateNormalPath(compiler, parsed[1]),
|
relateContext.relateAbsoluteRequest(parsed[1], parsed[2]),
|
]);
|
} else {
|
if (!parsed.request) {
|
return JSON.stringify(
|
Object.assign({}, parsed, {
|
context: relateNormalPath(compiler, parsed.context),
|
userRequest: relateContext.relateAbsoluteRequest(
|
parsed.context,
|
parsed.userRequest,
|
),
|
options: Object.assign({}, parsed.options, {
|
request: relateContext.relateAbsoluteRequest(
|
parsed.context,
|
parsed.options.request,
|
),
|
}),
|
}),
|
);
|
} else {
|
return JSON.stringify(
|
Object.assign({}, parsed, {
|
context: relateNormalPath(compiler, parsed.context),
|
request: relateContext.relateAbsoluteRequest(
|
parsed.context,
|
parsed.request,
|
),
|
}),
|
);
|
}
|
}
|
}
|
|
function relateNormalModuleResolve(compiler, resolved) {
|
if (resolved.type === 'context') {
|
return Object.assign({}, resolved, {
|
identifier: relateNormalModuleId(compiler, resolved.identifier),
|
resource: relateNormalRequest(compiler, resolved.resource),
|
});
|
}
|
return serialResolveNormal.freeze(resolved, resolved, {
|
compiler,
|
});
|
}
|
|
moduleResolveCacheChange
|
.reduce((carry, value) => {
|
if (!carry.includes(value)) {
|
carry.push(value);
|
}
|
return carry;
|
}, [])
|
.forEach(key => {
|
// console.log(key, moduleResolveCache[key]);
|
// moduleResolveCache[key] && console.log(relateNormalModuleResolveKey(compiler, key));
|
// moduleResolveCache[key] && console.log(relateNormalModuleResolve(compiler, moduleResolveCache[key]));
|
moduleResolveOps.push({
|
key: relateNormalModuleResolveKey(compiler, key),
|
value: moduleResolveCache[key]
|
? relateNormalModuleResolve(compiler, moduleResolveCache[key])
|
: null,
|
});
|
});
|
|
moduleResolveCacheChange = [];
|
|
pushParityWriteOps(compilation, moduleResolveOps);
|
|
return moduleResolveCacheSerializer.write(moduleResolveOps);
|
},
|
);
|
}
|
}
|
|
module.exports = ModuleResolverCache;
|