hard-source-webpack-plugin-fixed-hashbug
Version:
Hard cache the source of modules in webpack. Patched version fixing the hash bug
390 lines (364 loc) • 10.5 kB
JavaScript
const DependenciesBlockVariable = require('webpack/lib/DependenciesBlockVariable');
const pluginCompat = require('./util/plugin-compat');
const BlockSchemas3 = [
[
'AMDRequireDependenciesBlock',
'expr',
'arrayRange',
'functionRange',
'errorCallbackRange',
'module',
'loc',
],
['ImportDependenciesBlock', 'request', 'range', 'chunkName', 'module', 'loc'],
[
'RequireEnsureDependenciesBlock',
'expr',
'successExpression',
'errorExpression',
'chunkName',
'chunkNameRange',
'module',
'loc',
],
['AsyncDependenciesBlock', 'name', 'module'],
];
const BlockSchemas4 = [
[
'AMDRequireDependenciesBlock',
'expr',
'arrayRange',
'functionRange',
'errorCallbackRange',
'module',
'loc',
'request',
],
[
'ImportDependenciesBlock',
'request',
'range',
'groupOptions',
'module',
'loc',
'originModule',
],
[
'RequireEnsureDependenciesBlock',
'expr',
'successExpression',
'errorExpression',
'chunkName',
'chunkNameRange',
'module',
'loc',
],
['AsyncDependenciesBlock', 'groupOptions', 'module', 'loc', 'request'],
];
try {
BlockSchemas3[0].DependencyBlock = require('webpack/lib/dependencies/AMDRequireDependenciesBlock');
BlockSchemas4[0].DependencyBlock = require('webpack/lib/dependencies/AMDRequireDependenciesBlock');
} catch (_) {}
try {
BlockSchemas3[1].DependencyBlock = require('webpack/lib/dependencies/ImportDependenciesBlock');
BlockSchemas4[1].DependencyBlock = require('webpack/lib/dependencies/ImportDependenciesBlock');
} catch (_) {}
try {
BlockSchemas3[2].DependencyBlock = require('webpack/lib/dependencies/RequireEnsureDependenciesBlock');
BlockSchemas4[2].DependencyBlock = require('webpack/lib/dependencies/RequireEnsureDependenciesBlock');
} catch (_) {}
try {
BlockSchemas3[3].DependencyBlock = require('webpack/lib/AsyncDependenciesBlock');
BlockSchemas4[3].DependencyBlock = require('webpack/lib/AsyncDependenciesBlock');
} catch (_) {}
const freezeArgument = {
chunkName(arg, { chunkName }, extra, methods) {
return chunkName;
},
name(arg, { name }, extra, methods) {
return name;
},
groupOptions(arg, { groupOptions }) {
return groupOptions;
},
module(arg, block, extra, methods) {},
originModule(arg, block, extra, methods) {},
};
const thawArgument = {
module(arg, frozen, extra, methods) {
return extra.module;
},
originModule(arg, block, extra, methods) {
return extra.module;
},
};
function freezeDependencyBlock(dependencyBlock, extra, methods) {
const schemas = extra.schemas;
for (let i = 0; i < schemas.length; i++) {
if (dependencyBlock.constructor === schemas[i].DependencyBlock) {
const frozen = {
type: schemas[i][0],
};
for (let j = 1; j < schemas[i].length; j++) {
let arg = dependencyBlock[schemas[i][j]];
if (freezeArgument[schemas[i][j]]) {
arg = freezeArgument[schemas[i][j]](
arg,
dependencyBlock,
extra,
methods,
);
}
frozen[schemas[i][j]] = arg;
}
return frozen;
}
}
}
function thawDependencyBlock(frozen, extra, methods) {
const schemas = extra.schemas;
for (let i = 0; i < schemas.length; i++) {
if (frozen.type === schemas[i][0]) {
const DependencyBlock = schemas[i].DependencyBlock;
const args = [];
for (let j = 1; j < schemas[i].length; j++) {
let arg = frozen[schemas[i][j]];
if (thawArgument[schemas[i][j]]) {
arg = thawArgument[schemas[i][j]](arg, frozen, extra, methods);
}
args.push(arg);
}
try {
return new DependencyBlock(...args);
} catch (_) {
return new (Function.prototype.bind.apply(
DependencyBlock,
[null].concat(args),
))();
}
}
}
}
function assertFrozen({ length }, original, typeName, freeze) {
if (length !== original.length) {
const didNotFreeze = original.filter(item => !freeze(item));
if (didNotFreeze.length > 0) {
throw new Error(
`Unfrozen ${typeName} (${didNotFreeze.length} / ${
original.length
}): ${didNotFreeze
.map(({ constructor }) => constructor.name)
.filter((name, i, names) => !names.slice(0, i).includes(name))
.join(', ')}`,
);
}
}
}
class TransformDependencyBlockPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
let schemas = BlockSchemas4;
if (this.options.schema < 4) {
schemas = BlockSchemas3;
}
let methods;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'TransformDependencyBlockPlugin',
_methods => {
methods = _methods;
},
);
let freeze;
let mapFreeze;
let mapThaw;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'TransformDependencyBlockPlugin',
methods => {
// store = methods.store;
// fetch = methods.fetch;
freeze = methods.freeze;
// thaw = methods.thaw;
mapFreeze = methods.mapFreeze;
mapThaw = methods.mapThaw;
},
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeDependencyVariable',
'TransformDependencyBlockPlugin',
(frozen, { name, expression, dependencies }, extra) => ({
type: 'DependenciesBlockVariable',
name: name,
expression: expression,
dependencies: mapFreeze('Dependency', null, dependencies, extra),
}),
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeDependencyBlock',
'TransformDependencyBlockPlugin',
(frozen, block, extra) => {
extra.schemas = schemas;
const _frozen = freezeDependencyBlock(block, extra, methods);
if (_frozen) {
if (
(block.dependencies && block.dependencies.length > 0) ||
(block.variables && block.variables.length > 0) ||
(block.blocks && block.blocks.length > 0)
) {
_frozen.dependencies = mapFreeze(
'Dependency',
null,
block.dependencies,
extra,
);
assertFrozen(
_frozen.dependencies,
block.dependencies,
'dependencies',
item => freeze('Dependency', null, item, extra),
);
_frozen.variables = mapFreeze(
'DependencyVariable',
null,
block.variables,
extra,
);
assertFrozen(
_frozen.variables,
block.variables,
'dependency variables',
item => freeze('DependencyVariable', null, item, extra),
);
_frozen.blocks = mapFreeze(
'DependencyBlock',
null,
block.blocks,
extra,
);
assertFrozen(_frozen.blocks, block.blocks, 'blocks', item =>
freeze('DependencyBlock', null, item, extra),
);
}
if (block.parent) {
_frozen.parent = true;
}
return _frozen;
}
const _frozenBlock = {
type: 'DependenciesBlock',
dependencies: mapFreeze(
'Dependency',
null,
block.dependencies,
extra,
),
variables: mapFreeze(
'DependencyVariable',
null,
block.variables,
extra,
),
blocks: mapFreeze('DependencyBlock', null, block.blocks, extra),
};
assertFrozen(
_frozenBlock.dependencies,
block.dependencies,
'dependencies',
item => freeze('Dependency', null, item, extra),
);
assertFrozen(
_frozenBlock.variables,
block.variables,
'dependency variables',
item => freeze('DependencyVariable', null, item, extra),
);
assertFrozen(_frozenBlock.blocks, block.blocks, 'blocks', item =>
freeze('DependencyBlock', null, item, extra),
);
return _frozenBlock;
},
);
pluginCompat.tap(
compiler,
'_hardSourceThawDependencyVariable',
'TransformDependencyBlockPlugin',
(variable, { name, expression, dependencies }, extra) =>
new DependenciesBlockVariable(
name,
expression,
mapThaw('Dependency', null, dependencies, extra),
),
);
pluginCompat.tap(
compiler,
'_hardSourceThawDependencyBlock',
'TransformDependencyBlockPlugin',
(block, frozen, extra) => {
extra.schemas = schemas;
const _thawed = thawDependencyBlock(frozen, extra, methods);
if (_thawed) {
if (_thawed.dependencies) {
var blockExtra = {
state: extra.state,
module: extra.module,
parent: _thawed,
compilation: extra.compilation,
};
_thawed.dependencies = mapThaw(
'Dependency',
null,
frozen.dependencies,
blockExtra,
);
_thawed.variables = mapThaw(
'DependencyVariable',
null,
frozen.variables,
blockExtra,
);
mapThaw('DependencyBlock', null, frozen.blocks, blockExtra);
}
if (frozen.parent) {
extra.parent.addBlock(_thawed);
}
return _thawed;
}
if (block) {
var blockExtra = {
state: extra.state,
module: extra.module,
parent: block,
compilation: extra.compilation,
};
block.dependencies = mapThaw(
'Dependency',
null,
frozen.dependencies,
blockExtra,
);
block.variables = mapThaw(
'DependencyVariable',
null,
frozen.variables,
blockExtra,
);
block.blocks = mapThaw(
'DependencyBlock',
null,
frozen.blocks,
blockExtra,
);
}
return block;
},
);
}
}
module.exports = TransformDependencyBlockPlugin;