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,
|
relateNormalRequest,
|
relateNormalPathSet,
|
relateNormalLoaders,
|
} = require('./util/relate-context');
|
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 serialJson = {
|
freeze(arg, value, extra) {
|
return JSON.parse(arg);
|
},
|
thaw(arg, frozen, extra) {
|
return JSON.stringify(arg);
|
},
|
};
|
|
const serialMap = serial.map;
|
|
const serialResolvedMap = serial.map(
|
serial.pipe(
|
{ freeze: serialJson.freeze, thaw: serial.identity.thaw },
|
serialResolveRequest,
|
{ freeze: serial.identity.freeze, thaw: serialJson.thaw },
|
),
|
serialResolved,
|
);
|
|
const serialResourceHashMap = serial.map(serial.request, serial.identity);
|
|
const serialNormalConstructor4 = serial.constructed(NormalModule, {
|
data: serial.pipe(
|
{ freeze: (arg, module) => module, thaw: arg => arg },
|
serial.created({
|
type: serial.identity,
|
request: serial.request,
|
userRequest: serial.request,
|
rawRequest: serial.request,
|
loaders: serial.loaders,
|
resource: serial.path,
|
parser: serial.parser,
|
generator: serial.generator,
|
resolveOptions: serial.identity,
|
}),
|
),
|
});
|
|
const serialNormalModuleExtra4 = {
|
freeze() {},
|
thaw(arg, frozen, extra, methods) {
|
extra.module = arg;
|
return arg;
|
},
|
};
|
|
const serialNormalIdentifier4 = {
|
freeze(arg, module, extra, methods) {
|
return serial.request.freeze(module.identifier(), null, extra, methods);
|
},
|
thaw(arg) {
|
return arg;
|
},
|
};
|
|
const serialNormalAssigned4 = serial.assigned({
|
factoryMeta: serial.identity,
|
issuer: serial.pipe(
|
{
|
freeze(arg, { issuer }) {
|
return issuer && typeof issuer === 'object'
|
? issuer.identifier()
|
: issuer;
|
},
|
thaw(arg, frozen, extra) {
|
return arg;
|
},
|
},
|
serial.request,
|
{
|
freeze(arg) {
|
return arg;
|
},
|
thaw(arg, frozen, { compilation }) {
|
if (compilation.modules) {
|
for (const module of compilation.modules) {
|
if (
|
module &&
|
typeof module.identifier === 'function' &&
|
module.identifier() === arg
|
) {
|
return module;
|
}
|
}
|
for (const cacheId in compilation.cache) {
|
const module = compilation.cache[cacheId];
|
if (
|
module &&
|
typeof module.identifier === 'function' &&
|
module.identifier() === arg
|
) {
|
return module;
|
}
|
}
|
}
|
return arg;
|
},
|
},
|
),
|
useSourceMap: serial.identity,
|
lineToLine: serial.identity,
|
});
|
|
const serialNormalOriginExtra4 = {
|
freeze() {},
|
thaw(arg, frozen, extra) {
|
if (typeof arg.issuer === 'object') {
|
extra.origin = arg.issuer;
|
}
|
return arg;
|
},
|
};
|
|
const serialNormalBuild4 = serial.assigned({
|
built: serial.identity,
|
buildTimestamp: serial.identity,
|
buildMeta: serial.identity,
|
buildInfo: serial.created({
|
assets: serial.moduleAssets,
|
cacheable: serial.identity,
|
contextDependencies: serial.pathSet,
|
exportsArgument: serial.identity,
|
fileDependencies: serial.pathSet,
|
harmonyModule: serial.identity,
|
jsonData: serial.identity,
|
strict: serial.identity,
|
}),
|
warnings: serial.moduleWarning,
|
errors: serial.moduleError,
|
_source: serial.source,
|
_buildHash: serial.identity,
|
hash: serial.identity,
|
_lastSuccessfulBuildMeta: serial.identity,
|
|
__hardSource_resolved: serialResolvedMap,
|
__hardSource_oldHashes: serial.pipe(
|
{
|
freeze(arg, module, extra) {
|
const obj = {};
|
const cachedMd5s = extra.compilation.__hardSourceFileMd5s;
|
|
for (const file of module.buildInfo.fileDependencies) {
|
obj[file] = cachedMd5s[file];
|
}
|
for (const dir of module.buildInfo.contextDependencies) {
|
obj[dir] = cachedMd5s[dir];
|
}
|
|
return obj;
|
},
|
thaw: serial.identity.thaw,
|
},
|
serialResourceHashMap,
|
),
|
});
|
|
const serialNormalError4 = {
|
freeze() {},
|
thaw(arg, module, extra) {
|
arg.error = arg.errors[0] || null;
|
return arg;
|
},
|
};
|
|
const serialNormalSourceExtra4 = {
|
freeze() {},
|
thaw(arg, module, extra) {
|
extra.source = arg._source;
|
return arg;
|
},
|
};
|
|
const serialNormalSource4 = serial.assigned({
|
_cachedSource: serial.source,
|
_cachedSourceHash: serial.identity,
|
renderedHash: serial.identity,
|
});
|
|
const serialNormalModule4PreBuild = serial.serial('NormalModule', {
|
constructor: serialNormalConstructor4,
|
setModuleExtra: serialNormalModuleExtra4,
|
identifier: serialNormalIdentifier4,
|
assigned: serialNormalAssigned4,
|
setOriginExtra: serialNormalOriginExtra4,
|
});
|
|
const serialNormalModule4PostBuild = serial.serial('NormalModule', {
|
build: serialNormalBuild4,
|
dependencyBlock: serial.dependencyBlock,
|
setError: serialNormalError4,
|
setSourceExtra: serialNormalSourceExtra4,
|
source: serialNormalSource4,
|
});
|
|
const serialNormalModule4 = serial.serial('NormalModule', {
|
constructor: serialNormalConstructor4,
|
setModuleExtra: serialNormalModuleExtra4,
|
identifier: serialNormalIdentifier4,
|
assigned: serialNormalAssigned4,
|
setOriginExtra: serialNormalOriginExtra4,
|
build: serialNormalBuild4,
|
dependencyBlock: serial.dependencyBlock,
|
setError: serialNormalError4,
|
setSourceExtra: serialNormalSourceExtra4,
|
source: serialNormalSource4,
|
});
|
|
const needRebuild4 = function() {
|
if (this.error) {
|
this.cacheItem.invalid = true;
|
this.cacheItem.invalidReason = 'error building';
|
return true;
|
}
|
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;
|
this.cacheItem.invalidReason = 'md5 mismatch';
|
return true;
|
}
|
}
|
for (const dir of this.buildInfo.contextDependencies) {
|
if (!cachedHashes[dir] || fileHashes[dir] !== cachedHashes[dir]) {
|
this.cacheItem.invalid = true;
|
this.cacheItem.invalidReason = 'md5 mismatch';
|
return true;
|
}
|
}
|
|
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', {
|
constructor: serial.constructed(NormalModule, {
|
request: serial.request,
|
userRequest: serial.request,
|
rawRequest: serial.request,
|
loaders: serial.loaders,
|
resource: serial.path,
|
parser: serial.parser,
|
}),
|
|
setModuleExtra: serialNormalModuleExtra4,
|
// Used internally by HardSource
|
identifier: serialNormalIdentifier4,
|
|
assigned: serial.assigned({
|
issuer: serial.pipe(
|
{
|
freeze(arg, { issuer }) {
|
return issuer && typeof issuer === 'object'
|
? issuer.identifier()
|
: issuer;
|
},
|
thaw(arg, frozen, extra) {
|
return arg;
|
},
|
},
|
serial.request,
|
{
|
freeze(arg) {
|
return arg;
|
},
|
thaw(arg, frozen, { compilation }) {
|
if (compilation.modules) {
|
for (const module of compilation.modules) {
|
if (
|
module &&
|
typeof module.identifier === 'function' &&
|
module.identifier() === arg
|
) {
|
return module;
|
}
|
}
|
for (const cacheId in compilation.cache) {
|
const module = compilation.cache[cacheId];
|
if (
|
module &&
|
typeof module.identifier === 'function' &&
|
module.identifier() === arg
|
) {
|
return module;
|
}
|
}
|
}
|
return arg;
|
},
|
},
|
),
|
useSourceMap: serial.identity,
|
lineToLine: serial.identity,
|
}),
|
|
setOriginExtra: {
|
freeze() {},
|
thaw(arg, frozen, extra) {
|
if (typeof arg.issuer === 'object') {
|
extra.origin = arg.issuer;
|
}
|
return arg;
|
},
|
},
|
|
build: serial.assigned({
|
built: serial.identity,
|
buildTimestamp: serial.identity,
|
cacheable: serial.identity,
|
meta: serial.identity,
|
assets: serial.moduleAssets,
|
fileDependencies: serial.pathArray,
|
contextDependencies: serial.pathArray,
|
harmonyModule: serial.identity,
|
strict: serial.identity,
|
exportsArgument: serial.identity,
|
warnings: serial.moduleWarning,
|
errors: serial.moduleError,
|
_source: serial.source,
|
|
__hardSource_resolved: serialResolvedMap,
|
__hardSource_oldHashes: serial.pipe(
|
{
|
freeze(arg, module, extra) {
|
const obj = {};
|
const cachedMd5s = extra.compilation.__hardSourceCachedMd5s;
|
|
for (const file of module.fileDependencies) {
|
obj[file] = cachedMd5s[file];
|
}
|
for (const dir of module.contextDependencies) {
|
obj[dir] = cachedMd5s[dir];
|
}
|
|
return obj;
|
},
|
thaw: serial.identity.thaw,
|
},
|
serialResourceHashMap,
|
),
|
}),
|
|
hash: {
|
freeze(arg, module, { compilation }, methods) {
|
return module.getHashDigest(compilation.dependencyTemplates);
|
},
|
thaw(arg) {
|
return arg;
|
},
|
},
|
|
dependencyBlock: serial.dependencyBlock,
|
|
setError: {
|
freeze() {},
|
thaw(arg, module, extra) {
|
arg.error = arg.errors[0] || null;
|
return arg;
|
},
|
},
|
|
setSourceExtra: {
|
freeze() {},
|
thaw(arg, module, extra) {
|
extra.source = arg._source;
|
return arg;
|
},
|
},
|
|
source: serial.assigned({
|
_cachedSource: serial.created({
|
source: serial.source,
|
hash: serial.identity,
|
}),
|
}),
|
});
|
|
const needRebuild3 = function() {
|
if (this.error) {
|
this.cacheItem.invalid = true;
|
this.cacheItem.invalidReason = 'error building';
|
return true;
|
}
|
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;
|
this.cacheItem.invalidReason = 'md5 mismatch';
|
return true;
|
}
|
}
|
for (const dir of this.contextDependencies) {
|
if (!cachedHashes[dir] || fileHashes[dir] !== cachedHashes[dir]) {
|
this.cacheItem.invalid = true;
|
this.cacheItem.invalidReason = 'md5 mismatch';
|
return true;
|
}
|
}
|
|
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 =>
|
module.buildInfo ? module.buildInfo.cacheable : module.cacheable;
|
|
class TransformNormalModulePlugin {
|
constructor(options) {
|
this.options = options || {};
|
}
|
|
apply(compiler) {
|
const schema = this.options.schema;
|
|
let serialNormalModule = serialNormalModule4;
|
let needRebuild = needRebuild4;
|
if (schema < 4) {
|
serialNormalModule = serialNormalModule3;
|
needRebuild = needRebuild3;
|
}
|
|
let createHash;
|
if (schema >= 4) {
|
createHash = require('webpack/lib/util/createHash');
|
}
|
|
let freeze;
|
let mapFreeze;
|
let _methods;
|
|
pluginCompat.tap(
|
compiler,
|
'_hardSourceMethods',
|
'TransformNormalModulePlugin',
|
methods => {
|
_methods = methods;
|
|
// store = methods.store;
|
// fetch = methods.fetch;
|
freeze = methods.freeze;
|
// thaw = methods.thaw;
|
mapFreeze = methods.mapFreeze;
|
// mapThaw = methods.mapThaw;
|
},
|
);
|
|
pluginCompat.tap(
|
compiler,
|
'compilation',
|
'TransformNormalModulePlugin',
|
compilation => {
|
pluginCompat.tap(
|
compilation,
|
'succeedModule',
|
'TransformNormalModulePlugin',
|
module => {
|
if (module instanceof NormalModule) {
|
try {
|
module._dependencyBlock = freeze(
|
'DependencyBlock',
|
null,
|
module,
|
{
|
module,
|
parent: module,
|
compilation,
|
},
|
);
|
} catch (e) {
|
logMessages.moduleFreezeError(compilation, module, e);
|
}
|
}
|
},
|
);
|
},
|
);
|
|
pluginCompat.tap(
|
compiler,
|
'_hardSourceFreezeModule',
|
'TransformNormalModulePlugin',
|
(frozen, module, extra) => {
|
// Set hash if it was not set.
|
if (
|
schema === 4 &&
|
module instanceof NormalModule &&
|
module.buildTimestamp &&
|
!module.hash
|
) {
|
const outputOptions = extra.compilation.outputOptions;
|
const hashFunction = outputOptions.hashFunction;
|
const hashDigest = outputOptions.hashDigest;
|
const hashDigestLength = outputOptions.hashDigestLength;
|
|
if (module.buildInfo && module._initBuildHash) {
|
module._initBuildHash(extra.compilation);
|
}
|
|
const moduleHash = createHash(hashFunction);
|
module.updateHash(moduleHash);
|
module.hash = moduleHash.digest(hashDigest);
|
module.renderedHash = module.hash.substr(0, hashDigestLength);
|
if (module._cachedSource) {
|
module._cachedSourceHash = module.getHashDigest(
|
extra.compilation.dependencyTemplates,
|
);
|
}
|
}
|
|
if (
|
module.request &&
|
(cacheable(module) || !module.built) &&
|
module instanceof NormalModule &&
|
(!frozen ||
|
(schema >= 4 && module.hash !== frozen.build.hash) ||
|
(schema < 4 &&
|
module.getHashDigest(extra.compilation.dependencyTemplates) !==
|
frozen.hash))
|
) {
|
const compilation = extra.compilation;
|
|
if (module.cacheItem) {
|
module.cacheItem.invalid = false;
|
module.cacheItem.invalidReason = null;
|
}
|
|
let serialModule = serialNormalModule;
|
if (!module.built) {
|
serialModule = serialNormalModule4PreBuild;
|
}
|
const f = serialModule.freeze(
|
null,
|
module,
|
{
|
module,
|
compilation,
|
},
|
_methods,
|
);
|
// The saved dependencies may not be the ones derived in the hash. This is
|
// alright, in such a case the dependencies were altered before the source
|
// was rendered. The dependencies should be modified a second time, if
|
// they are in the same way they'll match. If they are not modified in the
|
// same way, then it'll correctly rerender.
|
if (module._dependencyBlock) {
|
f.dependencyBlock = module._dependencyBlock;
|
}
|
return f;
|
}
|
|
return frozen;
|
},
|
);
|
|
pluginCompat.tap(
|
compiler,
|
'_hardSourceThawModule',
|
'TransformNormalModulePlugin thaw',
|
(module, frozen, { compilation, normalModuleFactory }) => {
|
if (frozen.type === 'NormalModule') {
|
let m;
|
if (module === null) {
|
let serialModule = serialNormalModule;
|
if (!frozen.build || !frozen.build.built) {
|
serialModule = serialNormalModule4PreBuild;
|
}
|
m = serialModule.thaw(
|
null,
|
frozen,
|
{
|
state: { imports: {} },
|
compilation: compilation,
|
normalModuleFactory: normalModuleFactory,
|
},
|
_methods,
|
);
|
} else {
|
m = serialNormalModule4PostBuild.thaw(
|
module,
|
frozen,
|
{
|
state: { imports: {} },
|
compilation: compilation,
|
normalModuleFactory: normalModuleFactory,
|
},
|
_methods,
|
);
|
}
|
|
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
|
// unbuilding will lead to double dependencies.
|
if (m.built && schema === 4 && !compilation.cache) {
|
m.unbuild();
|
}
|
// Side load into the cache if something for this identifier isn't already
|
// there.
|
else if (
|
m.built &&
|
compilation.cache &&
|
!compilation.cache[`m${m.identifier()}`]
|
) {
|
compilation.cache[`m${m.identifier()}`] = m;
|
}
|
|
return m;
|
}
|
return module;
|
},
|
);
|
}
|
}
|
|
module.exports = TransformNormalModulePlugin;
|