hard-source-webpack-plugin
Version:
Hard cache the source of modules in webpack.
230 lines (201 loc) • 7.93 kB
JavaScript
const fs = require('graceful-fs');
const path = require('path');
const cachePrefix = require('./util').cachePrefix;
const pluginCompat = require('./util/plugin-compat');
const relateContext = require('./util/relate-context');
let NS;
NS = path.dirname(fs.realpathSync(__dirname));
class NormalModuleFactoryPlugin {
apply(compiler) {
const compilerHooks = pluginCompat.hooks(compiler);
let fetch;
compilerHooks._hardSourceMethods.tap(
'HardSource - TransformNormalModuleFactoryPlugin',
methods => {
fetch = methods.fetch;
},
);
compilerHooks.compilation.tap(
'HardSource - TransformNormalModuleFactoryPlugin',
(compilation, { normalModuleFactory, contextModuleFactory }) => {
// if (!active) {return;}
// compilation.fileTimestamps = fileTimestamps;
// compilation.contextTimestamps = contextTimestamps;
// compilation.__hardSourceFileMd5s = fileMd5s;
// compilation.__hardSourceCachedMd5s = cachedMd5s;
const compilationHooks = pluginCompat.hooks(compilation);
compilationHooks.buildModule.tap(
'HardSource - TransformNormalModuleFactoryPlugin',
module => {
if (module.constructor.name === 'NormalModule') {
module.__originalCreateLoaderContext =
module.__originalCreateLoaderContext ||
module.createLoaderContext;
module.__hardSource_resolved = {};
module.createLoaderContext = (...args) => {
const loaderContext = module.__originalCreateLoaderContext(
...args,
);
const _resolve = loaderContext.resolve;
loaderContext.resolve = (context, request, callback) => {
_resolve.call(
loaderContext,
context,
request,
(err, result) => {
if (err) {
callback(err, result);
} else {
module.__hardSource_resolved[
JSON.stringify({ context, request })
] = {
resource: result,
resolveOptions: module.resolveOptions,
};
callback(err, result);
}
},
);
};
return loaderContext;
};
}
},
);
const normalModuleFactoryHooks = pluginCompat.hooks(
normalModuleFactory,
);
// Webpack 2 can use different parsers based on config rule sets.
normalModuleFactoryHooks.parser
.for('javascript/auto')
.tap(
'HardSource - TransformNormalModuleFactoryPlugin',
(parser, options) => {
// Store the options somewhere that can not conflict with another plugin
// on the parser so we can look it up and store those options with a
// cached module resolution.
parser[`${NS}/parser-options`] = options;
},
);
normalModuleFactoryHooks.resolver.tap(
'HardSource - TransformNormalModuleFactoryPlugin',
fn => (request, cb) => {
const identifierPrefix = cachePrefix(compilation);
if (identifierPrefix === null) {
return fn.call(null, request, cb);
}
const cacheId = JSON.stringify([
identifierPrefix,
request.context,
request.request,
]);
const absCacheId = JSON.stringify([
identifierPrefix,
request.context,
relateContext.relateAbsoluteRequest(
request.context,
request.request,
),
]);
request.contextInfo.resolveOptions = request.resolveOptions;
const next = () => {
const originalRequest = request;
return fn.call(null, request, function(err, request) {
if (err) {
return cb(err);
}
if (!request.source) {
compilation.__hardSourceModuleResolveCacheChange.push(
cacheId,
);
compilation.__hardSourceModuleResolveCache[
cacheId
] = Object.assign({}, request, {
parser: null,
generator: null,
parserOptions: request.parser[`${NS}/parser-options`],
type: request.settings && request.settings.type,
settings: request.settings,
resourceResolveData: request.resourceResolveData,
dependencies: null,
});
}
cb(...arguments);
});
};
const fromCache = () => {
const result = Object.assign(
{},
compilation.__hardSourceModuleResolveCache[cacheId] ||
compilation.__hardSourceModuleResolveCache[absCacheId],
);
result.dependencies = request.dependencies;
if (!result.parser || !result.parser.parse) {
result.parser = result.settings
? normalModuleFactory.getParser(
result.type,
result.settings.parser,
)
: normalModuleFactory.getParser(result.parserOptions);
}
if (!result.generator && normalModuleFactory.getGenerator) {
result.generator = normalModuleFactory.getGenerator(
result.type,
result.settings.generator,
);
}
result.loaders = result.loaders.map(loader => {
if (typeof loader === 'object' && loader.ident) {
const ruleSet = normalModuleFactory.ruleSet;
return {
loader: loader.loader,
ident: loader.ident,
options: ruleSet.references[loader.ident],
};
}
return loader;
});
return cb(null, result);
};
if (
(compilation.__hardSourceModuleResolveCache[cacheId] &&
!compilation.__hardSourceModuleResolveCache[cacheId].invalid) ||
(compilation.__hardSourceModuleResolveCache[absCacheId] &&
!compilation.__hardSourceModuleResolveCache[absCacheId].invalid)
) {
return fromCache();
}
next();
},
);
normalModuleFactoryHooks.createModule.tap(
'HardSourceWebpackPlugin',
({ request }) => {
if (
compilation.cache &&
compilation.cache[`m${request}`] &&
compilation.cache[`m${request}`].cacheItem &&
!compilation.cache[`m${request}`].cacheItem.invalid
) {
return compilation.cache[`m${request}`];
}
const identifierPrefix = cachePrefix(compilation);
if (identifierPrefix === null) {
return;
}
const identifier = identifierPrefix + request;
const module = fetch('Module', identifier, {
compilation,
normalModuleFactory: normalModuleFactory,
contextModuleFactory: contextModuleFactory,
});
if (module) {
return module;
}
},
);
},
);
}
}
module.exports = NormalModuleFactoryPlugin;