This commit is contained in:
darenhsu
2022-07-17 13:16:16 +08:00
parent 84759556ff
commit befd344ab0
28070 changed files with 4008428 additions and 1 deletions
+127
View File
@@ -0,0 +1,127 @@
const crypto = require('crypto');
const pluginCompat = require('./util/plugin-compat');
const relateContext = require('./util/relate-context');
const { parityCacheFromCache, pushParityWriteOps } = require('./util/parity');
function requestHash(request) {
return crypto
.createHash('sha1')
.update(request)
.digest()
.hexSlice();
}
const relateNormalRequest = relateContext.relateNormalRequest;
class AssetCache {
apply(compiler) {
const compilerHooks = pluginCompat.hooks(compiler);
let assetCache = {};
let parityCache = {};
const assetArchetypeCache = {
_ops: [],
get(id) {
const hashId = requestHash(relateNormalRequest(compiler, id));
if (assetCache[hashId]) {
if (typeof assetCache[hashId] === 'string') {
assetCache[hashId] = JSON.parse(assetCache[hashId]);
}
return assetCache[hashId];
}
},
set(id, item) {
const hashId = requestHash(relateNormalRequest(compiler, id));
if (item) {
assetCache[hashId] = item;
this._ops.push({
key: hashId,
value: item,
});
} else {
assetCache[hashId] = null;
this._ops.push({
key: hashId,
value: null,
});
}
},
operations() {
const ops = this._ops.slice();
this._ops.length = 0;
return ops;
},
};
compilerHooks._hardSourceArchetypeRegister.call(
'Asset',
assetArchetypeCache,
);
let assetCacheSerializer;
let assetParityCacheSerializer;
compilerHooks._hardSourceCreateSerializer.tap(
'HardSource - AssetCache',
(cacheSerializerFactory, cacheDirPath) => {
assetCacheSerializer = cacheSerializerFactory.create({
name: 'assets',
type: 'file',
cacheDirPath,
});
assetParityCacheSerializer = cacheSerializerFactory.create({
name: 'assets-parity',
type: 'data',
cacheDirPath,
});
},
);
compilerHooks._hardSourceResetCache.tap('HardSource - AssetCache', () => {
assetCache = {};
parityCache = {};
});
compilerHooks._hardSourceReadCache.tapPromise(
'HardSource - AssetCache',
() =>
Promise.all([
assetCacheSerializer.read().then(_assetCache => {
assetCache = _assetCache;
}),
assetParityCacheSerializer.read().then(_parityCache => {
parityCache = _parityCache;
}),
]),
);
compilerHooks._hardSourceParityCache.tap(
'HardSource - AssetCache',
parityRoot => {
parityCacheFromCache('Asset', parityRoot, parityCache);
},
);
compilerHooks._hardSourceWriteCache.tapPromise(
'HardSource - AssetCache',
compilation => {
const assetOps = assetArchetypeCache.operations();
const parityOps = [];
pushParityWriteOps(compilation, parityOps);
return Promise.all([
assetCacheSerializer.write(assetOps),
assetParityCacheSerializer.write(parityOps),
]);
},
);
}
}
module.exports = AssetCache;
+517
View File
@@ -0,0 +1,517 @@
const path = require('path');
const lodash = require('lodash');
const nodeObjectHash = require('node-object-hash');
const parseJson = require('parse-json');
const pluginCompat = require('./util/plugin-compat');
const promisify = require('./util/promisify');
const relateContext = require('./util/relate-context');
const serial = require('./util/serial');
const values = require('./util/Object.values');
const bulkFsTask = require('./util/bulk-fs-task');
const { parityCacheFromCache, pushParityWriteOps } = require('./util/parity');
const serialNormalResolved = serial.created({
result: serial.path,
resourceResolveData: serial.objectAssign({
context: serial.created({
issuer: serial.request,
resolveOptions: serial.identity,
}),
path: serial.path,
descriptionFilePath: serial.path,
descriptionFileRoot: serial.path,
}),
});
class EnhancedResolveCache {
apply(compiler) {
let missingCacheSerializer;
let resolverCacheSerializer;
let missingCache = { normal: {}, loader: {}, context: {} };
let resolverCache = { normal: {}, loader: {}, context: {} };
let parityCache = {};
const compilerHooks = pluginCompat.hooks(compiler);
compilerHooks._hardSourceCreateSerializer.tap(
'HardSource - EnhancedResolveCache',
(cacheSerializerFactory, cacheDirPath) => {
missingCacheSerializer = cacheSerializerFactory.create({
name: 'missing-resolve',
type: 'data',
autoParse: true,
cacheDirPath,
});
resolverCacheSerializer = cacheSerializerFactory.create({
name: 'resolver',
type: 'data',
autoParse: true,
cacheDirPath,
});
},
);
compilerHooks._hardSourceResetCache.tap(
'HardSource - EnhancedResolveCache',
() => {
missingCache = { normal: {}, loader: {}, context: {} };
resolverCache = { normal: {}, loader: {}, context: {} };
parityCache = {};
compiler.__hardSource_missingCache = missingCache;
},
);
compilerHooks._hardSourceReadCache.tapPromise(
'HardSource - EnhancedResolveCache',
({ contextNormalPath, contextNormalRequest }) => {
return Promise.all([
missingCacheSerializer.read().then(_missingCache => {
missingCache = { normal: {}, loader: {}, context: {} };
compiler.__hardSource_missingCache = missingCache;
function contextNormalMissingKey(compiler, key) {
const parsed = parseJson(key);
return JSON.stringify([
contextNormalPath(compiler, parsed[0]),
contextNormalPath(compiler, parsed[1]),
]);
}
function contextNormalMissing(compiler, missing) {
return missing.map(missed =>
contextNormalRequest(compiler, missed),
);
}
Object.keys(_missingCache).forEach(key => {
let item = _missingCache[key];
if (typeof item === 'string') {
item = parseJson(item);
}
const splitIndex = key.indexOf('/');
const group = key.substring(0, splitIndex);
const keyName = contextNormalMissingKey(
compiler,
key.substring(splitIndex + 1),
);
missingCache[group] = missingCache[group] || {};
missingCache[group][keyName] = contextNormalMissing(
compiler,
item,
);
});
}),
resolverCacheSerializer.read().then(_resolverCache => {
resolverCache = { normal: {}, loader: {}, context: {} };
parityCache = {};
function contextNormalResolvedKey(compiler, key) {
const parsed = parseJson(key);
return JSON.stringify([
contextNormalPath(compiler, parsed[0]),
parsed[1],
]);
}
function contextNormalResolved(compiler, resolved) {
return serialNormalResolved.thaw(resolved, resolved, {
compiler,
});
}
Object.keys(_resolverCache).forEach(key => {
let item = _resolverCache[key];
if (typeof item === 'string') {
item = parseJson(item);
}
if (key.startsWith('__hardSource_parityToken')) {
parityCache[key] = item;
return;
}
const splitIndex = key.indexOf('/');
const group = key.substring(0, splitIndex);
const keyName = contextNormalResolvedKey(
compiler,
key.substring(splitIndex + 1),
);
resolverCache[group] = resolverCache[group] || {};
resolverCache[group][keyName] = contextNormalResolved(
compiler,
item,
);
});
}),
]);
},
);
compilerHooks._hardSourceParityCache.tap(
'HardSource - EnhancedResolveCache',
parityRoot => {
parityCacheFromCache('EnhancedResolve', parityRoot, parityCache);
},
);
let missingVerifyResolve;
compiler.__hardSource_missingVerify = new Promise(resolve => {
missingVerifyResolve = resolve;
});
compilerHooks._hardSourceVerifyCache.tapPromise(
'HardSource - EnhancedResolveCache',
() =>
(() => {
compiler.__hardSource_missingVerify = new Promise(resolve => {
missingVerifyResolve = resolve;
});
const bulk = lodash.flatten(
Object.keys(missingCache).map(group =>
lodash.flatten(
Object.keys(missingCache[group])
.map(key => {
const missingItem = missingCache[group][key];
if (!missingItem) {
return;
}
return missingItem.map((missed, index) => [
group,
key,
missed,
index,
]);
})
.filter(Boolean),
),
),
);
return bulkFsTask(bulk, (item, task) => {
const group = item[0];
const key = item[1];
const missingItem = missingCache[group][key];
const missed = item[2];
const missedPath = missed.split('?')[0];
const missedIndex = item[3];
// The missed index is the resolved item. Invalidate if it does not
// exist.
if (missedIndex === missingItem.length - 1) {
compiler.inputFileSystem.stat(
missed,
task((err, stat) => {
if (err) {
missingItem.invalid = true;
missingItem.invalidReason = 'resolved now missing';
}
}),
);
} else {
compiler.inputFileSystem.stat(
missed,
task((err, stat) => {
if (err) {
return;
}
if (stat.isDirectory()) {
if (group === 'context') {
missingItem.invalid = true;
}
}
if (stat.isFile()) {
if (group === 'loader' || group.startsWith('normal')) {
missingItem.invalid = true;
missingItem.invalidReason = 'missing now found';
}
}
}),
);
}
});
})().then(missingVerifyResolve),
);
function bindResolvers() {
function configureMissing(key, resolver) {
// missingCache[key] = missingCache[key] || {};
// resolverCache[key] = resolverCache[key] || {};
const _resolve = resolver.resolve;
resolver.resolve = function(info, context, request, cb, cb2) {
let numArgs = 4;
if (!cb) {
numArgs = 3;
cb = request;
request = context;
context = info;
}
let resolveContext;
if (cb2) {
numArgs = 5;
resolveContext = cb;
cb = cb2;
}
if (info && info.resolveOptions) {
key = `normal-${new nodeObjectHash({ sort: false }).hash(
info.resolveOptions,
)}`;
resolverCache[key] = resolverCache[key] || {};
missingCache[key] = missingCache[key] || {};
}
const resolveId = JSON.stringify([context, request]);
const absResolveId = JSON.stringify([
context,
relateContext.relateAbsolutePath(context, request),
]);
const resolve =
resolverCache[key][resolveId] || resolverCache[key][absResolveId];
if (resolve && !resolve.invalid) {
const missingId = JSON.stringify([context, resolve.result]);
const missing = missingCache[key][missingId];
if (missing && !missing.invalid) {
return cb(
null,
[resolve.result].concat(request.split('?').slice(1)).join('?'),
resolve.resourceResolveData,
);
} else {
resolve.invalid = true;
resolve.invalidReason = 'out of date';
}
}
let localMissing = [];
const callback = (err, result, result2) => {
if (result) {
const inverseId = JSON.stringify([context, result.split('?')[0]]);
const resolveId = JSON.stringify([context, request]);
// Skip recording missing for any dependency in node_modules.
// Changes to them will be handled by the environment hash. If we
// tracked the stuff in node_modules too, we'd be adding a whole
// bunch of reduntant work.
if (result.includes('node_modules')) {
localMissing = localMissing.filter(
missed => !missed.includes('node_modules'),
);
}
// In case of other cache layers, if we already have missing
// recorded and we get a new empty array of missing, keep the old
// value.
if (localMissing.length === 0 && missingCache[key][inverseId]) {
return cb(err, result, result2);
}
missingCache[key][inverseId] = localMissing
.filter((missed, missedIndex) => {
const index = localMissing.indexOf(missed);
if (index === -1 || index < missedIndex) {
return false;
}
if (missed === result) {
return false;
}
return true;
})
.concat(result.split('?')[0]);
missingCache[key][inverseId].new = true;
resolverCache[key][resolveId] = {
result: result.split('?')[0],
resourceResolveData: result2,
new: true,
};
}
cb(err, result, result2);
};
const _missing =
cb.missing || (resolveContext && resolveContext.missing);
if (_missing) {
callback.missing = {
push(path) {
localMissing.push(path);
_missing.push(path);
},
add(path) {
localMissing.push(path);
_missing.add(path);
},
};
if (resolveContext) {
resolveContext.missing = callback.missing;
}
} else {
callback.missing = Object.assign(localMissing, {
add(path) {
localMissing.push(path);
},
});
if (resolveContext) {
resolveContext.missing = callback.missing;
}
}
if (numArgs === 3) {
_resolve.call(this, context, request, callback);
} else if (numArgs === 5) {
_resolve.call(
this,
info,
context,
request,
resolveContext,
callback,
);
} else {
_resolve.call(this, info, context, request, callback);
}
};
}
if (compiler.resolverFactory) {
compiler.resolverFactory.hooks.resolver
.for('normal')
.tap('HardSource resolve cache', (resolver, options) => {
const normalCacheId = `normal-${new nodeObjectHash({
sort: false,
}).hash(Object.assign({}, options, { fileSystem: null }))}`;
resolverCache[normalCacheId] = resolverCache[normalCacheId] || {};
missingCache[normalCacheId] = missingCache[normalCacheId] || {};
configureMissing(normalCacheId, resolver);
return resolver;
});
compiler.resolverFactory.hooks.resolver
.for('loader')
.tap('HardSource resolve cache', resolver => {
configureMissing('loader', resolver);
return resolver;
});
compiler.resolverFactory.hooks.resolver
.for('context')
.tap('HardSource resolve cache', resolver => {
configureMissing('context', resolver);
return resolver;
});
} else {
configureMissing('normal', compiler.resolvers.normal);
configureMissing('loader', compiler.resolvers.loader);
configureMissing('context', compiler.resolvers.context);
}
}
compilerHooks.afterPlugins.tap('HardSource - EnhancedResolveCache', () => {
if (compiler.resolvers.normal) {
bindResolvers();
} else {
compilerHooks.afterResolvers.tap(
'HardSource - EnhancedResolveCache',
bindResolvers,
);
}
});
compilerHooks._hardSourceWriteCache.tapPromise(
'HardSource - EnhancedResolveCache',
(compilation, { relateNormalPath, relateNormalRequest }) => {
if (compilation.compiler.parentCompilation) {
const resolverOps = [];
pushParityWriteOps(compilation, resolverOps);
return resolverCacheSerializer.write(resolverOps);
}
const missingOps = [];
const resolverOps = [];
function relateNormalMissingKey(compiler, key) {
const parsed = parseJson(key);
return JSON.stringify([
relateNormalPath(compiler, parsed[0]),
relateNormalPath(compiler, parsed[1]),
]);
}
function relateNormalMissing(compiler, missing) {
return missing.map(missed => relateNormalRequest(compiler, missed));
}
Object.keys(missingCache).forEach(group => {
Object.keys(missingCache[group]).forEach(key => {
if (!missingCache[group][key]) {
return;
}
if (missingCache[group][key].new) {
missingCache[group][key].new = false;
missingOps.push({
key: `${group}/${relateNormalMissingKey(compiler, key)}`,
value: JSON.stringify(
relateNormalMissing(compiler, missingCache[group][key]),
),
});
} else if (missingCache[group][key].invalid) {
missingCache[group][key] = null;
missingOps.push({
key: `${group}/${relateNormalMissingKey(compiler, key)}`,
value: null,
});
}
});
});
function relateNormalResolvedKey(compiler, key) {
const parsed = parseJson(key);
return JSON.stringify([
relateNormalPath(compiler, parsed[0]),
relateContext.relateAbsolutePath(parsed[0], parsed[1]),
]);
}
function relateNormalResolved(compiler, resolved) {
return serialNormalResolved.freeze(resolved, resolved, {
compiler,
});
}
Object.keys(resolverCache).forEach(group => {
Object.keys(resolverCache[group]).forEach(key => {
if (!resolverCache[group][key]) {
return;
}
if (resolverCache[group][key].new) {
resolverCache[group][key].new = false;
resolverOps.push({
key: `${group}/${relateNormalResolvedKey(compiler, key)}`,
value: JSON.stringify(
relateNormalResolved(compiler, resolverCache[group][key]),
),
});
} else if (resolverCache[group][key].invalid) {
resolverCache[group][key] = null;
resolverOps.push({
key: `${group}/${relateNormalResolvedKey(compiler, key)}`,
value: null,
});
}
});
});
pushParityWriteOps(compilation, resolverOps);
return Promise.all([
missingCacheSerializer.write(missingOps),
resolverCacheSerializer.write(resolverOps),
]);
},
);
}
}
module.exports = EnhancedResolveCache;
+481
View File
@@ -0,0 +1,481 @@
const crypto = require('crypto');
const path = require('path');
const lodash = require('lodash');
const bulkFsTask = require('./util/bulk-fs-task');
const pluginCompat = require('./util/plugin-compat');
const promisify = require('./util/promisify');
const values = require('./util/Object.values');
const { parityCacheFromCache, pushParityWriteOps } = require('./util/parity');
class Md5Cache {
apply(compiler) {
let md5Cache = {};
let parityCache = {};
const fileMd5s = {};
const cachedMd5s = {};
let fileTimestamps = {};
const contextMd5s = {};
let contextTimestamps = {};
let md5CacheSerializer;
let latestStats = {};
let latestMd5s = {};
let unbuildMd5s = {};
let fileDependencies = [];
let contextDependencies = [];
let stat;
let readdir;
let readFile;
let mtime;
let md5;
let fileStamp;
let contextStamp;
let contextStamps;
function bindFS() {
stat = promisify(compiler.inputFileSystem.stat, {
context: compiler.inputFileSystem,
});
// stat = promisify(fs.stat, {context: fs});
readdir = promisify(compiler.inputFileSystem.readdir, {
context: compiler.inputFileSystem,
});
readFile = promisify(compiler.inputFileSystem.readFile, {
context: compiler.inputFileSystem,
});
mtime = file =>
stat(file)
.then(stat => +stat.mtime)
.catch(() => 0);
md5 = file =>
readFile(file)
.then(contents =>
crypto
.createHash('md5')
.update(contents, 'utf8')
.digest('hex'),
)
.catch(() => '');
fileStamp = (file, stats) => {
if (compiler.__hardSource_fileTimestamps[file]) {
return compiler.__hardSource_fileTimestamps[file];
} else {
if (!stats[file]) {
stats[file] = stat(file);
}
return stats[file].then(stat => {
const mtime = +stat.mtime;
compiler.__hardSource_fileTimestamps[file] = mtime;
return mtime;
});
}
};
contextStamp = (dir, stats) => {
const context = {};
let selfTime = 0;
function walk(dir) {
return readdir(dir)
.then(items =>
Promise.all(
items.map(item => {
const file = path.join(dir, item);
if (!stats[file]) {
stats[file] = stat(file);
}
return stats[file].then(
stat => {
if (stat.isDirectory()) {
return walk(path.join(dir, item)).then(items2 =>
items2.map(item2 => path.join(item, item2)),
);
}
if (+stat.mtime > selfTime) {
selfTime = +stat.mtime;
}
return item;
},
() => {
return;
},
);
}),
),
)
.catch(() => [])
.then(items =>
items
.reduce((carry, item) => carry.concat(item), [])
.filter(Boolean),
);
}
return walk(dir).then(items => {
items.sort();
const selfHash = crypto.createHash('md5');
items.forEach(item => {
selfHash.update(item);
});
context.mtime = selfTime;
context.hash = selfHash.digest('hex');
return context;
});
};
contextStamps = (contextDependencies, stats) => {
stats = stats || {};
const contexts = {};
contextDependencies.forEach(context => {
contexts[context] = { files: [], mtime: 0, hash: '' };
});
const compilerContextTs = compiler.contextTimestamps;
contextDependencies.forEach(contextPath => {
const _context = contextStamp(contextPath, stats);
if (!_context.then) {
contexts[contextPath] = _context;
} else {
contexts[contextPath] = _context.then(context => {
contexts[contextPath] = context;
return context;
});
}
});
return contexts;
};
}
if (compiler.inputFileSystem) {
bindFS();
} else {
pluginCompat.tap(
compiler,
'afterEnvironment',
'HardSource - Md5Cache',
bindFS,
);
}
pluginCompat.tap(
compiler,
'_hardSourceCreateSerializer',
'HardSource - Md5Cache',
(cacheSerializerFactory, cacheDirPath) => {
md5CacheSerializer = cacheSerializerFactory.create({
name: 'md5',
type: 'data',
autoParse: true,
cacheDirPath,
});
},
);
pluginCompat.tap(
compiler,
'_hardSourceResetCache',
'HardSource - Md5Cache',
() => {
md5Cache = {};
parityCache = {};
fileTimestamps = {};
contextTimestamps = {};
},
);
pluginCompat.tapPromise(
compiler,
'_hardSourceReadCache',
'HardSource - Md5Cache',
({ contextKeys, contextNormalPath }) =>
md5CacheSerializer
.read()
.then(_md5Cache => {
Object.keys(_md5Cache).forEach(key => {
if (key.startsWith('__hardSource_parityToken')) {
parityCache[key] = _md5Cache[key];
delete _md5Cache[key];
}
});
return _md5Cache;
})
.then(contextKeys(compiler, contextNormalPath))
.then(_md5Cache => {
Object.keys(_md5Cache).forEach(key => {
if (typeof _md5Cache[key] === 'string') {
_md5Cache[key] = JSON.parse(_md5Cache[key]);
}
if (_md5Cache[key] && _md5Cache[key].hash) {
cachedMd5s[key] = _md5Cache[key].hash;
}
});
md5Cache = _md5Cache;
})
.then(() => {
const dependencies = Object.keys(md5Cache);
fileDependencies = dependencies.filter(
file => md5Cache[file].isFile,
);
contextDependencies = dependencies.filter(
file => md5Cache[file].isDirectory,
);
}),
);
pluginCompat.tap(
compiler,
'_hardSourceParityCache',
'HardSource - Md5Cache',
parityRoot => {
parityCacheFromCache('Md5', parityRoot, parityCache);
},
);
pluginCompat.tapPromise(
compiler,
'_hardSourceVerifyCache',
'HardSource - Md5Cache',
() => {
latestStats = {};
latestMd5s = {};
unbuildMd5s = {};
const stats = {};
// var md5s = latestMd5s;
// Prepare objects to mark md5s to delete if they are not used.
for (const key in cachedMd5s) {
unbuildMd5s[key] = null;
}
return Promise.all([
(() => {
const compilerFileTs = (compiler.__hardSource_fileTimestamps = {});
const fileTs = (fileTimestamps = {});
return bulkFsTask(fileDependencies, (file, task) => {
if (compiler.__hardSource_fileTimestamps[file]) {
return compiler.__hardSource_fileTimestamps[file];
} else {
compiler.inputFileSystem.stat(
file,
task((err, value) => {
if (err) {
return 0;
}
const mtime = +value.mtime;
compiler.__hardSource_fileTimestamps[file] = mtime;
return mtime;
}),
);
}
}).then(mtimes => {
const bulk = lodash.zip(fileDependencies, mtimes);
return bulkFsTask(bulk, (item, task) => {
const file = item[0];
const mtime = item[1];
fileTs[file] = mtime || 0;
if (!compiler.__hardSource_fileTimestamps[file]) {
compiler.__hardSource_fileTimestamps[file] = mtime;
}
compiler.inputFileSystem.readFile(
file,
task(function(err, body) {
if (err) {
fileMd5s[file] = '';
return;
}
const hash = crypto
.createHash('md5')
.update(body, 'utf8')
.digest('hex');
fileMd5s[file] = hash;
}),
);
});
});
})(),
(() => {
compiler.contextTimestamps = compiler.contextTimestamps || {};
const contextTs = (contextTimestamps = {});
const contexts = contextStamps(contextDependencies, stats);
return Promise.all(values(contexts)).then(function() {
for (var contextPath in contexts) {
var context = contexts[contextPath];
if (!compiler.contextTimestamps[contextPath]) {
compiler.contextTimestamps[contextPath] = context.mtime;
}
contextTimestamps[contextPath] = context.mtime;
fileMd5s[contextPath] = context.hash;
}
});
})(),
]);
},
);
pluginCompat.tap(
compiler,
'compilation',
'HardSource - Md5Cache',
compilation => {
compilation.__hardSourceFileMd5s = fileMd5s;
compilation.__hardSourceCachedMd5s = cachedMd5s;
compilation.__hardSourceFileTimestamps = fileTimestamps;
},
);
pluginCompat.tapPromise(
compiler,
'_hardSourceWriteCache',
'HardSource - Md5Cache',
(compilation, { relateNormalPath, contextNormalPath }) => {
const moduleOps = [];
const dataOps = [];
const md5Ops = [];
const assetOps = [];
const moduleResolveOps = [];
const missingOps = [];
const resolverOps = [];
let buildingMd5s = {};
function buildMd5Ops(dependencies) {
dependencies.forEach(file => {
function updateMd5CacheItem(value) {
if (
!md5Cache[file] ||
(md5Cache[file] && md5Cache[file].hash !== value.hash)
) {
md5Cache[file] = value;
cachedMd5s[file] = value.hash;
md5Ops.push({
key: relateNormalPath(compiler, file),
value: value,
});
} else if (
!value.mtime &&
md5Cache[file] &&
md5Cache[file].mtime !== value.mtime
) {
md5Cache[file] = value;
cachedMd5s[file] = value.hash;
}
}
const building = buildingMd5s[file];
if (!building.then) {
updateMd5CacheItem(building);
} else {
buildingMd5s[file] = building.then(updateMd5CacheItem);
}
});
}
const fileDependencies = Array.from(compilation.fileDependencies).map(
file => contextNormalPath(compiler, file),
);
const MD5_TIME_PRECISION_BUFFER = 2000;
fileDependencies.forEach(file => {
if (buildingMd5s[file]) {
return;
}
delete unbuildMd5s[file];
if (fileMd5s[file]) {
buildingMd5s[file] = {
// Subtract a small buffer from now for file systems that record
// lower precision mtimes.
mtime: Date.now() - MD5_TIME_PRECISION_BUFFER,
hash: fileMd5s[file],
isFile: true,
isDirectory: false,
};
} else {
buildingMd5s[file] = md5(file).then(hash => ({
mtime: Date.now() - MD5_TIME_PRECISION_BUFFER,
hash,
isFile: true,
isDirectory: false,
}));
}
});
buildMd5Ops(fileDependencies);
const contextDependencies = Array.from(
compilation.contextDependencies,
).map(file => contextNormalPath(compiler, file));
const contexts = contextStamps(contextDependencies);
contextDependencies.forEach(file => {
if (buildingMd5s[file]) {
return;
}
delete unbuildMd5s[file];
let context = contexts[file];
if (!context.then) {
// Subtract a small buffer from now for file systems that record lower
// precision mtimes.
context.mtime = Date.now() - MD5_TIME_PRECISION_BUFFER;
context.isFile = false;
context.isDirectory = true;
} else {
context = context.then(context => {
context.mtime = Date.now() - MD5_TIME_PRECISION_BUFFER;
context.isFile = false;
context.isDirectory = true;
return context;
});
}
buildingMd5s[file] = context;
});
buildMd5Ops(contextDependencies);
const writeMd5Ops = Promise.all(
Object.keys(buildingMd5s).map(key => buildingMd5s[key]),
).then(() => {
if (!compilation.compiler.parentCompilation) {
for (const key in unbuildMd5s) {
md5Ops.push({
key: relateNormalPath(compiler, key),
value: unbuildMd5s[key],
});
}
}
pushParityWriteOps(compilation, md5Ops);
});
return writeMd5Ops.then(() => md5CacheSerializer.write(md5Ops));
},
);
}
}
module.exports = Md5Cache;
+145
View File
@@ -0,0 +1,145 @@
const pluginCompat = require('./util/plugin-compat');
const relateContext = require('./util/relate-context');
const { parityCacheFromCache, pushParityWriteOps } = require('./util/parity');
const relateNormalPath = relateContext.relateNormalPath;
function relateNormalRequest(compiler, key) {
return key
.split('!')
.map(subkey => relateNormalPath(compiler, subkey))
.join('!');
}
function relateNormalModuleId(compiler, id) {
return id.substring(0, 24) + relateNormalRequest(compiler, id.substring(24));
}
class ModuleCache {
apply(compiler) {
const compilerHooks = pluginCompat.hooks(compiler);
let moduleCache = {};
let parityCache = {};
const moduleArchetypeCache = {
_ops: [],
get(id) {
if (moduleCache[id] && !moduleCache[id].invalid) {
if (typeof moduleCache[id] === 'string') {
moduleCache[id] = JSON.parse(moduleCache[id]);
}
return moduleCache[id];
}
},
set(id, item) {
moduleCache[id] = item;
if (item) {
this._ops.push(id);
} else if (moduleCache[id]) {
if (typeof moduleCache[id] === 'string') {
moduleCache[id] = JSON.parse(moduleCache[id]);
}
moduleCache[id].invalid = true;
moduleCache[id].invalidReason = 'overwritten';
this._ops.push(id);
}
},
operations() {
const _this = this;
const ops = this._ops.map(id => ({
key: relateNormalModuleId(compiler, id),
value: _this.get(id) || null,
}));
this._ops.length = 0;
return ops;
},
};
compilerHooks._hardSourceArchetypeRegister.call(
'Module',
moduleArchetypeCache,
);
let moduleCacheSerializer;
compilerHooks._hardSourceCreateSerializer.tap(
'HardSource - ModuleCache',
(cacheSerializerFactory, cacheDirPath) => {
moduleCacheSerializer = cacheSerializerFactory.create({
name: 'module',
type: 'data',
cacheDirPath,
autoParse: true,
});
},
);
compilerHooks._hardSourceResetCache.tap('HardSource - ModuleCache', () => {
moduleCache = {};
});
compilerHooks._hardSourceReadCache.tapPromise(
'HardSource - ModuleCache',
({ contextKeys, contextNormalModuleId, copyWithDeser }) =>
moduleCacheSerializer
.read()
.then(_moduleCache => {
Object.keys(_moduleCache).forEach(key => {
if (key.startsWith('__hardSource_parityToken')) {
parityCache[key] = _moduleCache[key];
delete _moduleCache[key];
}
});
return _moduleCache;
})
.then(contextKeys(compiler, contextNormalModuleId))
.then(copyWithDeser.bind(null, moduleCache)),
);
compilerHooks._hardSourceParityCache.tap(
'HardSource - ModuleCache',
parityRoot => {
parityCacheFromCache('Module', parityRoot, parityCache);
},
);
compilerHooks.compilation.tap('HardSource - ModuleCache', compilation => {
compilation.__hardSourceModuleCache = moduleCache;
});
compilerHooks._hardSourceWriteCache.tapPromise(
'HardSource - ModuleCache',
compilation => {
const moduleOps = moduleArchetypeCache.operations();
if (!compilation.compiler.parentCompilation) {
// Add ops to remove no longer valid modules. If they were replaced with a
// up to date module, they will already have replaced this item so we
// won't accidentally delete up to date modules.
Object.keys(moduleCache).forEach(key => {
const cacheItem = moduleCache[key];
if (cacheItem && cacheItem.invalid) {
// console.log('invalid', cacheItem.invalidReason);
moduleCache[key] = null;
moduleOps.push({
key,
value: null,
});
}
});
}
pushParityWriteOps(compilation, moduleOps);
return moduleCacheSerializer.write(moduleOps);
},
);
}
}
module.exports = ModuleCache;
+408
View File
@@ -0,0 +1,408 @@
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;
+157
View File
@@ -0,0 +1,157 @@
/**
* A factory wrapper around a webpack compiler plugin to create a serializer
* object that caches a various data hard-source turns into json data without
* circular references.
*
* The wrapper uses a plugin hook on the webpack Compiler called
* `'hard-source-cache-factory'`. It is a waterfall plugin, the returned value
* of one plugin handle is passed to the next as the first argument. This
* plugin is expected to return a factory function that takes one argument. The
* argument passed to the factory function is the info about what kind of cache
* serializer hard-source wants.
*
* The info object contains three fields, `name`, `type`, and `cacheDirPath`.
*
* One example of info might be
*
* ```js
* {
* name: 'asset',
* type: 'file',
* cacheDirPath: '/absolute/path/to/my-project/path/configured/in/hard-source'
* }
* ```
*
* - `name` is the general name of the cache in hard-source.
* - `type` is the type of data contained. The `file` type means it'll be file
* data like large buffers and strings. The `data` type means its generally
* smaller info and serializable with JSON.stringify.
* - `cacheDirPath` is the root of the hard-source disk cache. A serializer
* should add some further element to the path for where it will store its
* info.
*
* So an example plugin handle should take the `factory` argument and return
* its own wrapping factory function. That function will take the `info` data
* and if it wants to returns a serializer. Otherwise its best to call the
* factory passed into the plugin handle.
*
* ```js
* compiler.plugin('hard-source-cache-factory', function(factory) {
* return function(info) {
* if (info.type === 'data') {
* return new MySerializer({
* cacheDirPath: join(info.cacheDirPath, info.name)
* });
* }
* return factory(info);
* };
* });
* ```
*
* @module hard-source-webpack-plugin/cache-serializer-factory
* @author Michael "Z" Goddard <mzgoddard@gmail.com>
*/
/**
* @constructor Serializer
* @memberof module:hard-source-webpack-plugin/cache-serializer-factory
*/
/**
* @method read
* @memberof module:hard-source-webpack-plugin/cache-serializer-factory~Serializer#
* @returns {Promise} promise that resolves the disk cache's contents
* @resolves {Object} a map of keys to current values stored on disk that has
* previously been cached
*/
/**
* @method write
* @memberof module:hard-source-webpack-plugin/cache-serializer-factory~Serializer#
* @param {Array.Object} ops difference of values to be stored in the disk cache
* @param {string} ops.key
* @param ops.value
* @returns {Promise} promise that resolves when writing completes
*/
const FileSerializerPlugin = require('./SerializerFilePlugin');
const Append2SerializerPlugin = require('./SerializerAppend2Plugin');
const pluginCompat = require('./util/plugin-compat');
/**
* @constructor CacheSerializerFactory
* @memberof module:hard-source-webpack-plugin/cache-serializer-factory
*/
class CacheSerializerFactory {
constructor(compiler) {
this.compiler = compiler;
pluginCompat.register(compiler, 'hardSourceCacheFactory', 'syncWaterfall', [
'factory',
]);
pluginCompat.tap(
compiler,
'hardSourceCacheFactory',
'default factory',
factory => info => {
// It's best to have plugins to hard-source listed in the config after it
// but to make hard-source easier to use we can call the factory of a
// plugin passed into this default factory.
if (factory) {
serializer = factory(info);
if (serializer) {
return serializer;
}
}
// Otherwise lets return the default serializers.
switch (info.type) {
case 'data':
return CacheSerializerFactory.dataSerializer.createSerializer(info);
break;
case 'file':
return CacheSerializerFactory.fileSerializer.createSerializer(info);
break;
default:
throw new Error(
`Unknown hard-source cache serializer type: ${info.type}`,
);
break;
}
},
);
}
/**
* @method create
* @memberof module:hard-source-webpack-plugin/cache-serializer-factory~CacheSerializerFactory#
* @param {Object} info
* @param {String} info.name
* @param {String} info.type
* @param {String} info.cacheDirPath
* @returns {Serializer}
*/
create(info) {
const factory = pluginCompat.call(this.compiler, 'hardSourceCacheFactory', [
null,
]);
const serializer = factory(info);
return serializer;
}
}
/**
* The default data serializer factory.
*/
CacheSerializerFactory.dataSerializer = Append2SerializerPlugin;
/**
* The default file serializer factory.
*/
CacheSerializerFactory.fileSerializer = FileSerializerPlugin;
module.exports = CacheSerializerFactory;
+133
View File
@@ -0,0 +1,133 @@
const chalk = require('chalk');
const pluginCompat = require('./util/plugin-compat');
const LOGGER_SEPARATOR = ':';
const DEFAULT_LOGGER_PREFIX = 'hardsource';
const messages = {
'serialization--error-freezing-module': {
short: value =>
`Could not freeze ${value.data.moduleReadable}: ${
value.data.errorMessage
}`,
},
'serialzation--cache-incomplete': {
short: value =>
`Last compilation did not finish saving. Building new cache.`,
},
'confighash--directory-no-confighash': {
short: value => `Config hash skipped in cache directory.`,
},
'confighash--new': {
short: value =>
`Writing new cache ${value.data.configHash.substring(0, 8)}...`,
},
'confighash--reused': {
short: value =>
`Reading from cache ${value.data.configHash.substring(0, 8)}...`,
},
'caches--delete-old': {
short: value =>
`Deleted ${value.data.deletedSizeMB} MB. Using ${
value.data.sizeMB
} MB of disk space.`,
},
'caches--keep': {
short: value => `Using ${value.data.sizeMB} MB of disk space.`,
},
'environment--inputs': {
short: value =>
`Tracking node dependencies with: ${value.data.inputs.join(', ')}.`,
},
'environment--config-changed': {
short: value => 'Configuration changed. Building new cache.',
},
'environment--changed': {
short: value => `Node dependencies changed. Building new cache.`,
},
'environment--hardsource-changed': {
short: value => `hard-source version changed. Building new cache.`,
},
'childcompiler--no-cache': {
once: value =>
`A child compiler has its cache disabled. Skipping child in hard-source.`,
},
'childcompiler--unnamed-cache': {
once: value =>
`A child compiler has unnamed cache. Skipping child in hard-source.`,
},
unrecognized: {
short: value => value.message,
},
};
const logLevels = ['error', 'warn', 'info', 'log', 'debug'];
const levelId = level => logLevels.indexOf(level.toLowerCase());
const compareLevel = (a, b) => levelId(a) - levelId(b);
class ChalkLoggerPlugin {
constructor(options = {}) {
this.options = options;
this.once = {};
// mode: 'test' or 'none'
this.options.mode =
this.options.mode || (process.env.NODE_ENV === 'test' ? 'test' : 'none');
// level: 'error', 'warn', 'info', 'log', 'debug'
this.options.level =
this.options.level || (this.options.mode === 'test' ? 'warn' : 'debug');
}
apply(compiler) {
const compilerHooks = pluginCompat.hooks(compiler);
compilerHooks.hardSourceLog.tap('HardSource - ChalkLoggerPlugin', value => {
if (compareLevel(this.options.level, value.level) < 0) {
return;
}
let headerColor = chalk.white;
let color = chalk.white;
if (value.level === 'error') {
headerColor = chalk.red;
} else if (value.level === 'warn') {
headerColor = chalk.yellow;
} else if (value.level === 'info') {
headerColor = chalk.white;
} else {
headerColor = color = chalk.gray;
}
const header = headerColor(
`[${DEFAULT_LOGGER_PREFIX}${LOGGER_SEPARATOR}${compiler.__hardSource_shortConfigHash ||
value.from}]`,
);
// Always use warn or error so that output goes to stderr.
const consoleFn = value.level === 'error' ? console.error : console.warn;
let handle = messages[value.data.id];
if (!handle) {
handle = messages.unrecognized;
}
if (handle) {
if (handle.once) {
if (!this.once[value.data.id]) {
this.once[value.data.id] = true;
consoleFn.call(console, header, color(handle.once(value)));
}
} else if (handle.short) {
consoleFn.call(console, header, color(handle.short(value)));
}
} else {
consoleFn.call(console, header, color(value.message));
}
});
}
}
module.exports = ChalkLoggerPlugin;
+53
View File
@@ -0,0 +1,53 @@
const pluginCompat = require('./util/plugin-compat');
const matchTest = (test, source) => {
if (Array.isArray(test)) {
return test.some(subtest => matchTest(subtest, source));
} else if (test instanceof RegExp) {
return test.test(source);
} else if (typeof test === 'string') {
return source.startsWith(test);
} else if (typeof test === 'function') {
return test(source);
}
return false;
};
const matchOne = ({ test, include, exclude }, source) => {
return (
(test ? matchTest(test, source) : true) &&
(include ? matchTest(include, source) : true) &&
(exclude ? !matchTest(exclude, source) : true)
);
};
const matchAny = (test, source) => {
if (Array.isArray(test)) {
return test.some(subtest => matchOne(subtest, source));
}
return matchOne(test, source);
};
class ExcludeModulePlugin {
constructor(match) {
this.match = match;
}
apply(compiler) {
const compilerHooks = pluginCompat.hooks(compiler);
compilerHooks.afterPlugins.tap('HardSource - ExcludeModulePlugin', () => {
compilerHooks._hardSourceAfterFreezeModule.tap(
'HardSource - ExcludeModulePlugin',
(frozen, module, extra) => {
if (matchAny(this.match, module.identifier())) {
return null;
}
return frozen;
},
);
});
}
}
module.exports = ExcludeModulePlugin;
+306
View File
@@ -0,0 +1,306 @@
const { fork: cpFork } = require('child_process');
const { cpus } = require('os');
const { resolve } = require('path');
const logMessages = require('./util/log-messages');
const pluginCompat = require('./util/plugin-compat');
const webpackBin = () => {
try {
return require.resolve('webpack-cli');
} catch (e) {}
try {
return require.resolve('webpack-command');
} catch (e) {}
throw new Error('webpack cli tool not installed or discoverable');
};
const configPath = compiler => {
try {
return require.resolve(
resolve(compiler.options.context || process.cwd(), 'webpack.config'),
);
} catch (e) {}
try {
return require.resolve(resolve(process.cwd(), 'webpack.config'));
} catch (e) {}
throw new Error('config not in obvious location');
};
class ParallelModulePlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
try {
require('webpack/lib/JavascriptGenerator');
} catch (e) {
logMessages.parallelRequireWebpack4(compiler);
return;
}
const options = this.options || {};
const fork =
options.fork ||
((fork, compiler, webpackBin) =>
fork(webpackBin(compiler), ['--config', configPath(compiler)], {
silent: true,
}));
const numWorkers = options.numWorkers
? typeof options.numWorkers === 'function'
? options.numWorkers
: () => options.numWorkers
: () => cpus().length;
const minModules =
typeof options.minModules === 'number' ? options.minModules : 10;
const compilerHooks = pluginCompat.hooks(compiler);
let freeze, thaw;
compilerHooks._hardSourceMethods.tap('ParallelModulePlugin', methods => {
freeze = methods.freeze;
thaw = methods.thaw;
});
compilerHooks.thisCompilation.tap(
'ParallelModulePlugin',
(compilation, params) => {
const compilationHooks = pluginCompat.hooks(compilation);
const nmfHooks = pluginCompat.hooks(params.normalModuleFactory);
const doMaster = () => {
const jobs = {};
const readyJobs = {};
const workers = [];
let nextWorkerIndex = 0;
let start = 0;
let started = false;
let configMismatch = false;
let modules = 0;
const startWorkers = () => {
const _numWorkers = numWorkers();
logMessages.parallelStartWorkers(compiler, {
numWorkers: _numWorkers,
});
for (let i = 0; i < _numWorkers; i++) {
const worker = fork(cpFork, compiler, webpackBin);
workers.push(worker);
worker.on('message', _result => {
if (configMismatch) {
return;
}
if (_result.startsWith('ready:')) {
const configHash = _result.split(':')[1];
if (configHash !== compiler.__hardSource_configHash) {
logMessages.parallelConfigMismatch(compiler, {
outHash: compiler.__hardSource_configHash,
theirHash: configHash,
});
configMismatch = true;
killWorkers();
for (const id in jobs) {
jobs[id].cb({ error: true });
delete readyJobs[id];
delete jobs[id];
}
return;
}
}
if (Object.values(readyJobs).length) {
const id = Object.keys(readyJobs)[0];
worker.send(
JSON.stringify({
id,
data: readyJobs[id].data,
}),
);
delete readyJobs[id];
} else {
worker.ready = true;
}
if (_result.startsWith('ready:')) {
start = Date.now();
return;
}
const result = JSON.parse(_result);
jobs[result.id].cb(result);
delete [result.id];
});
}
};
const killWorkers = () => {
Object.values(workers).forEach(worker => worker.kill());
};
const doJob = (module, cb) => {
if (configMismatch) {
cb({ error: new Error('config mismatch') });
return;
}
const id = 'xxxxxxxx-xxxxxxxx'.replace(/x/g, () =>
Math.random()
.toString(16)
.substring(2, 3),
);
jobs[id] = {
id,
data: freeze('Module', null, module, {
id: module.identifier(),
compilation,
}),
cb,
};
const worker = Object.values(workers).find(worker => worker.ready);
if (worker) {
worker.ready = false;
worker.send(
JSON.stringify({
id,
data: jobs[id].data,
}),
);
} else {
readyJobs[id] = jobs[id];
}
if (!started) {
started = true;
startWorkers();
}
};
const _create = params.normalModuleFactory.create;
params.normalModuleFactory.create = (data, cb) => {
_create.call(params.normalModuleFactory, data, (err, module) => {
if (err) {
return cb(err);
}
if (module.constructor.name === 'NormalModule') {
const build = module.build;
module.build = (
options,
compilation,
resolver,
fs,
callback,
) => {
if (modules < minModules) {
build.call(
module,
options,
compilation,
resolver,
fs,
callback,
);
modules += 1;
return;
}
try {
doJob(module, result => {
if (result.error) {
build.call(
module,
options,
compilation,
resolver,
fs,
callback,
);
} else {
thaw('Module', module, result.module, {
compilation,
normalModuleFactory: params.normalModuleFactory,
contextModuleFactory: params.contextModuleFactory,
});
callback();
}
});
} catch (e) {
logMessages.parallelErrorSendingJob(compiler, e);
build.call(
module,
options,
compilation,
resolver,
fs,
callback,
);
}
};
cb(null, module);
} else {
cb(err, module);
}
});
};
compilationHooks.seal.tap('ParallelModulePlugin', () => {
killWorkers();
});
};
const doChild = () => {
const _create = params.normalModuleFactory.create;
params.normalModuleFactory.create = (data, cb) => {};
process.send('ready:' + compiler.__hardSource_configHash);
process.on('message', _job => {
const job = JSON.parse(_job);
const module = thaw('Module', null, job.data, {
compilation,
normalModuleFactory: params.normalModuleFactory,
contextModuleFactory: params.contextModuleFactory,
});
module.build(
compilation.options,
compilation,
compilation.resolverFactory.get('normal', module.resolveOptions),
compilation.inputFileSystem,
error => {
process.send(
JSON.stringify({
id: job.id,
error: error,
module:
module &&
freeze('Module', null, module, {
id: module.identifier(),
compilation,
}),
}),
);
},
);
});
};
if (!process.send) {
doMaster();
} else {
doChild();
}
},
);
}
}
module.exports = ParallelModulePlugin;
+602
View File
@@ -0,0 +1,602 @@
const fs = require('graceful-fs');
const join = require('path').join;
const Readable = require('stream').Readable;
const _mkdirp = require('mkdirp');
const _rimraf = require('rimraf');
const writeJsonFile = require('write-json-file');
const entries = require('./util/Object.entries');
const values = require('./util/Object.values');
const promisify = require('./util/promisify');
const rimraf = promisify(_rimraf);
const open = promisify(fs.open);
const close = promisify(fs.close);
const read = promisify(fs.read);
const readFile = promisify(fs.readFile);
const write = promisify(fs.write);
const rename = promisify(fs.rename);
const unlink = promisify(fs.unlink);
const stat = promisify(fs.stat);
const mkdirp = promisify(_mkdirp);
const APPEND_VERSION = 1;
const _blockSize = 4 * 1024;
const _logSize = 2 * 1024 * 1024;
const _minCompactSize = 512 * 1024;
const _compactMultiplierThreshold = 1.5;
const value = (key, size, start) => ({
key,
size: size || 0,
start: start || 0,
});
const objFrom = map => {
if (map instanceof Map) {
const obj = {};
map.forEach((value, key) => {
obj[key] = value;
});
return obj;
}
return map;
};
const table = ({ nextByte, blockSize, logSize, map }) => ({
version: APPEND_VERSION,
nextByte: nextByte,
blockSize: blockSize,
logSize: logSize,
map: objFrom(map),
});
const modTable = ({ nextByte, blockSize, logSize, map }) => ({
version: APPEND_VERSION,
nextByte: nextByte,
blockSize: blockSize,
logSize: logSize,
map: new Map(entries(map)),
});
function putKey(_table, key, size) {
// _table.map[key] = value(key, size, _table.nextByte, Math.ceil(size / _table.blockSize));
_table.map.set(key, value(key, size, _table.nextByte));
_table.nextByte = _table.nextByte + size;
return _table;
}
function delKey(_table, key) {
// if (_table.map[key]) {
// delete _table.map[key];
if (_table.map.get(key)) {
_table.map.delete(key);
}
return _table;
}
const _tablepath = ({ path }) => join(path, 'table.json');
const _defaultTable = ({ blockSize, logSize }) =>
table({
nextByte: 0,
blockSize: blockSize || _blockSize,
logSize: logSize || _logSize,
map: {},
});
const timeout100 = () => new Promise(resolve => setTimeout(resolve, 100));
const _retry = (fn, n) => {
n = n || 5;
const _retryFn = value => {
if (n) {
n--;
return fn(value).catch(_retryFn);
}
return fn(value);
};
return _retryFn;
};
const _readTable = _this =>
readFile(_tablepath(_this), 'utf8')
.catch(e => JSON.stringify(_defaultTable(_this)))
.then(JSON.parse)
.then(_table => {
if (_table.version !== APPEND_VERSION) {
return _defaultTable(_this);
}
return _table;
});
const _writeTable = (_this, _table) => writeJsonFile(_tablepath(_this), _table);
const _logFilepath = ({ path }, { logSize }, index) => {
let logId = ((index / logSize) | 0).toString();
while (logId.length < 4) {
logId = `0${logId}`;
}
return join(path, `log${logId}`);
};
const _openLog = (_this, mode, _table, index) => {
if (_this._fd !== null) {
return Promise.resolve();
} else {
// If mode is 'a', stat the log to write to, if it should be empty and
// isn't, unlink before opening.
return Promise.resolve()
.then(() => {
if (mode === 'a' && index % _table.logSize === 0) {
return stat(_logFilepath(_this, _table, index))
.then(({ size }) => {
if (size > 0) {
return unlink(_logFilepath(_this, _table, index)).then(
timeout100,
);
}
})
.catch(() => {});
}
})
.then(() => open(_logFilepath(_this, _table, index), mode))
.then(fd => {
_this._fd = fd;
if (mode === 'a') {
_this._writeBuffer = new Buffer(_table.logSize);
_this._writeOffset = 0;
}
})
.catch(e => {
throw e;
});
}
};
const _closeLog = _this => {
if (_this._fd === null) {
return Promise.resolve();
} else {
return Promise.resolve()
.then(() => {
if (_this._writeBuffer) {
return write(_this._fd, _this._writeBuffer, 0, _this._writeOffset);
}
})
.then(() => close(_this._fd))
.then(() => {
_this._fd = null;
_this._writeBuffer = null;
_this._writeOffset = 0;
});
}
};
const _readBufferSize = (_this, { blockSize, logSize }) =>
Math.min(32 * blockSize, logSize);
const _readLog = (_this, _table) => {
let index = 0;
const out = new Readable({
read() {},
});
const rbSize = _table.logSize;
const _readBuffer = new Buffer(rbSize);
function _log() {
if (index >= _table.nextByte) {
out.push(null);
return _closeLog(_this);
}
const offset = 0;
function step() {
if (!_this._fd) {
index = _table.nextByte;
return _log();
}
return read(_this._fd, _readBuffer, 0, rbSize, 0).then(read => {
index += _table.logSize;
out.push(_readBuffer);
return _log();
});
}
return _closeLog(_this)
.then(() => _openLog(_this, 'r', _table, index))
.then(step);
}
Promise.resolve().then(_log);
return out;
};
const _appendBlock = (_this, _table, blockContent, index, next) => {
let prep;
if (_this._fd !== null && index % _table.logSize === 0) {
prep = _closeLog(_this).then(() => _openLog(_this, 'a', _table, index));
} else if (_this._fd === null) {
prep = _openLog(_this, 'a', _table, index);
}
function work() {
if (!_this._fd) {
return next(new Error());
}
if (blockContent.length > _table.logSize) {
return next(new Error('block longer than max size'));
}
const writeSlice = _this._writeBuffer.slice(
_this._writeOffset,
_this._writeOffset + blockContent.length,
);
// if (blockContent.length < _table.blockSize) {
// writeSlice.fill(0);
// }
blockContent.copy(writeSlice);
_this._writeOffset += blockContent.length;
if (_this._writeOffset > _this._writeBuffer.length) {
return next(
new Error(
`writeOffset ${_this._writeOffset} past writeBuffer length ${
_this._writeBuffer.length
}`,
),
);
}
if (_this._writeOffset > _table.logSize) {
return next(
new Error(
`writeOffset ${_this._writeOffset} past logSize ${_table.logSize}`,
),
);
}
next();
// return fs.write(_this._fd, blockContent, 0, _table.blockSize, next);
}
if (prep) {
prep.then(work);
} else {
work();
}
// return Promise.resolve()
// .then(function() {
// if (index % (_table.logSize / _table.blockSize) === 0) {
// return _closeLog(_this);
// }
// })
// .then(function() {
// return _openLog(_this, 'a', _table, index);
// })
// .then(function() {
// if (!_this._fd) {
// throw new Error();
// }
// if (blockContent.length > _table.blockSize) {
// throw new Error('block longer than max size');
// }
// if (blockContent.length < _table.blockSize) {
// var _blockContent = new Buffer(_table.blockSize);
// blockContent.copy(_blockContent);
// blockContent = _blockContent;
// }
// return write(_this._fd, blockContent, 0, _table.blockSize);
// });
};
const _sizeNeeded = (_this, { map }) =>
values(map).reduce((carry, { size }) => carry + size, 0);
const _sizeUsed = (_this, { nextByte }) => nextByte;
const _compactSize = (_this, _table) =>
Math.max(
_this.compactSizeThreshold,
_sizeNeeded(_this, _table) * _this.compactMultiplierThreshold,
);
const _lock = (_this, mustLock, promiseFn) => {
if (mustLock !== false) {
return (_this.lock = promiseFn(_this.lock));
}
return promiseFn(Promise.resolve());
};
const serialFsTask = (array, each) =>
new Promise((resolve, reject) => {
let queue = 0;
let index = 0;
let inNext = false;
function next(err) {
if (err) {
return reject(err);
}
if (index === array.length) {
return resolve();
}
queue++;
if (inNext) {
return;
}
inNext = true;
while (queue > index && index < array.length) {
try {
each(array[index++], next);
} catch (e) {
return next(e);
}
}
inNext = false;
}
next();
});
class AppendSerializer {
constructor(options) {
this.path = options.cacheDirPath;
this.autoParse = options.autoParse;
this.blockSize = options.blockSize || _blockSize;
this.logSize = options.logSize || _logSize;
this.compactSizeThreshold = options.compactSizeThreshold || _minCompactSize;
this.compactMultiplierThreshold =
options.compactMultiplierThreshold || _compactMultiplierThreshold;
this.lock = Promise.resolve();
this._fd = null;
}
read(mustLock) {
const start = Date.now();
const _this = this;
function _read() {
let activeTable;
return Promise.resolve()
.then(_retry(() => _readTable(_this)))
.then(_table => {
activeTable = _table;
})
.then(() => {
const map = new Map();
const valueStarts = [];
values(activeTable.map).forEach(value => {
valueStarts.push({
start: value.start,
end: value.start + value.size,
value,
});
});
valueStarts.sort((a, b) => a.start - b.start);
return new Promise((resolve, reject) => {
let valueIndex = 0;
let destBuffer = new Buffer(2 * 1024 * 1024);
let offset = 0;
let logOffset = 0;
const log = _readLog(_this, activeTable);
log.on('data', data => {
if (valueIndex >= valueStarts.length) {
return;
}
for (let bufferIndex = 0; bufferIndex < data.length; ) {
if (bufferIndex + logOffset >= valueStarts[valueIndex].end) {
valueIndex++;
}
if (valueIndex >= valueStarts.length) {
return;
}
const value = valueStarts[valueIndex].value;
if (bufferIndex + logOffset >= value.start) {
if (value.size > destBuffer.length) {
const newLength = Math.pow(
2,
Math.ceil(Math.log(value.size) / Math.log(2)),
);
destBuffer = new Buffer(newLength);
}
const readAmount = Math.min(
value.start + value.size - logOffset - bufferIndex,
activeTable.logSize - bufferIndex,
);
data
.slice(bufferIndex, bufferIndex + readAmount)
.copy(destBuffer.slice(offset, offset + readAmount));
bufferIndex += readAmount;
offset += readAmount;
if (offset >= value.size) {
offset = 0;
if (_this.autoParse) {
// console.log(value.size, destBuffer.utf8Slice(0, value.size))
map.set(
value.key,
JSON.parse(destBuffer.utf8Slice(0, value.size)),
);
} else {
map.set(value.key, destBuffer.utf8Slice(0, value.size));
}
}
} else if (bufferIndex + logOffset < value.start) {
bufferIndex += value.start - (bufferIndex + logOffset);
}
}
logOffset += activeTable.logSize;
});
log.on('end', resolve);
log.on('error', reject);
}).then(() => objFrom(map));
});
}
return _lock(_this, mustLock, promise =>
promise
.then(() => _read())
.catch(e =>
_closeLog(_this).then(() => {
throw e;
}),
),
);
}
write(ops, mustLock) {
if (ops.length === 0) {
return Promise.resolve();
}
const steps = 0;
const _this = this;
let activeTable;
let contentBuffer;
let contentLength;
function _write() {
return Promise.resolve()
.then(_retry(() => mkdirp(_this.path)))
.then(_retry(() => _readTable(_this)))
.then(_table => {
activeTable = modTable(_table);
const _ops = ops.slice();
function step(op, next) {
// steps++;
// var op = _ops.shift();
// if (!op) {
// return;
// }
let content = op.value;
if (content !== null) {
if (typeof content !== 'string') {
content = JSON.stringify(content);
}
if (
Buffer.byteLength &&
contentBuffer &&
Buffer.byteLength(content) <= contentBuffer.length
) {
contentLength = contentBuffer.utf8Write(content);
} else {
contentBuffer = new Buffer(content);
contentLength = contentBuffer.length;
}
const blockCount = Math.ceil(
((activeTable.nextByte % activeTable.logSize) + contentLength) /
activeTable.logSize,
);
let nextByte = activeTable.nextByte;
activeTable = putKey(activeTable, op.key, contentLength);
let bufferIndex = 0;
const bulk = Array.from(new Array(blockCount)).map((_, i) => i);
return serialFsTask(bulk, (_, next) => {
const blockSlice = contentBuffer.slice(
bufferIndex,
Math.min(
bufferIndex +
(activeTable.logSize - (nextByte % activeTable.logSize)),
contentLength,
),
);
_appendBlock(_this, activeTable, blockSlice, nextByte, next);
bufferIndex += blockSlice.length;
nextByte += blockSlice.length;
}).then(next);
// function append() {
// if (bufferIndex < contentBuffer.length) {
// var blockSlice = contentBuffer.slice(bufferIndex, bufferIndex + activeTable.blockSize);
// bufferIndex += activeTable.blockSize;
// return _appendBlock(_this, activeTable, blockSlice, nextByte++)
// .then(append);
// }
// }
// return append()
// .then(step);
} else {
activeTable = delKey(activeTable, op.key);
next();
}
}
return serialFsTask(_ops, step);
// return step();
})
.then(() => _closeLog(_this))
.then(
_retry(() => {
activeTable = table(activeTable);
return _writeTable(_this, activeTable);
}),
);
}
return _lock(_this, mustLock, promise =>
promise
.then(() => _write())
.catch(e =>
_closeLog(_this).then(() => {
throw e;
}),
)
.then(() => {
if (
_sizeUsed(_this, activeTable) > _compactSize(_this, activeTable)
) {
return _this.compact(false);
}
}),
);
}
compact(mustLock) {
const _this = this;
return _this
.read(mustLock)
.then(map => {
const ops = [];
Object.keys(map).forEach(key => {
ops.push({
key,
value: map[key],
});
});
return ops;
})
.then(ops =>
rimraf(`${_this.path}~`)
.then(timeout100)
.then(() => ops),
)
.then(ops => {
const copy = new AppendSerializer({
cacheDirPath: `${_this.path}~`,
blockSize: _this.blockSize,
logSize: _this.logSize,
compactSizeThreshold: _this.compactSizeThreshold,
compactMultiplierThreshold: _this.compactMultiplierThreshold,
});
return _lock(_this, mustLock, promise =>
promise
.then(() => copy.write(ops))
.then(() => rimraf(_this.path))
.then(timeout100)
.then(_retry(() => rename(copy.path, _this.path), 10)),
);
});
}
}
module.exports = AppendSerializer;
+425
View File
@@ -0,0 +1,425 @@
const fs = require('graceful-fs');
const { join, resolve } = require('path');
const _mkdirp = require('mkdirp');
const parseJson = require('parse-json');
const _rimraf = require('rimraf');
const promisify = require('./util/promisify');
const close = promisify(fs.close);
const mkdirp = promisify(_mkdirp);
const open = promisify(fs.open);
const read = promisify(fs.readFile);
const readdir = promisify(fs.readdir);
const readfd = promisify(fs.read);
const rename = promisify(fs.rename);
const rimraf = promisify(_rimraf);
const write = promisify(fs.writeFile);
const nextPow2 = n => {
const exponent = Math.log(n) / Math.log(2);
const nextExponent = Math.floor(exponent) + 1;
return Math.pow(2, nextExponent);
};
const resizePow2 = (buffer, n) => {
const tmpBuffer = Buffer.allocUnsafe(nextPow2(n));
buffer.copy(tmpBuffer.slice(0, buffer.length));
return tmpBuffer;
};
const MAX_CHUNK = 2 * 1024 * 1024;
const TMP_CHUNK = 0.5 * 1024 * 1024;
const MAX_CHUNK_PLUS = 2.5 * 1024 * 1024;
const LARGE_CONTENT = 64 * 1024;
let tmpBuffer = Buffer.allocUnsafe(TMP_CHUNK);
let outBuffer = Buffer.allocUnsafe(MAX_CHUNK_PLUS);
const _buffers = [];
const alloc = size => {
const buffer = _buffers.pop();
if (buffer && buffer.length >= size) {
return buffer;
}
return Buffer.allocUnsafe(size);
};
const drop = buffer => _buffers.push(buffer);
class WriteOutput {
constructor(length = 0, table = [], buffer = alloc(MAX_CHUNK_PLUS)) {
this.length = length;
this.table = table;
this.buffer = buffer;
}
static clone(other) {
return new WriteOutput(other.length, other.table, other.buffer);
}
take() {
const output = WriteOutput.clone(this);
this.length = 0;
this.table = [];
this.buffer = alloc(MAX_CHUNK_PLUS);
return output;
}
add(key, content) {
if (content !== null) {
// Write content to a temporary buffer
let length = tmpBuffer.utf8Write(content);
while (length === tmpBuffer.length) {
tmpBuffer = Buffer.allocUnsafe(tmpBuffer.length * 2);
length = tmpBuffer.utf8Write(content);
}
const start = this.length;
const end = start + length;
// Ensure output buffer is long enough to add the new content
if (end > this.buffer.length) {
this.buffer = resizePow2(this.buffer, end);
}
// Copy temporary buffer to the end of the current output buffer
tmpBuffer.copy(this.buffer.slice(start, end));
this.table.push({
name: key,
start,
end,
});
this.length = end;
} else {
this.table.push({
name: key,
start: -1,
end: -1,
});
}
}
}
class Semaphore {
constructor(max) {
this.max = max;
this.count = 0;
this.next = [];
}
async guard() {
if (this.count < this.max) {
this.count++;
return new SemaphoreGuard(this);
} else {
return new Promise(resolve => {
this.next.push(resolve);
}).then(() => new SemaphoreGuard(this));
}
}
}
class SemaphoreGuard {
constructor(parent) {
this.parent = parent;
}
done() {
const next = this.parent.next.shift();
if (next) {
next();
} else {
this.parent.count--;
}
}
}
class Append2 {
constructor({ cacheDirPath: path, autoParse }) {
this.path = path;
this.autoParse = autoParse;
this.inBuffer = Buffer.alloc(0);
this._buffers = [];
this.outBuffer = Buffer.alloc(0);
}
async _readFile(file) {
const fd = await open(file, 'r+');
let body = alloc(MAX_CHUNK_PLUS);
await readfd(fd, body, 0, 4, null);
const fullLength = body.readUInt32LE(0);
if (fullLength > body.length) {
drop(body);
body = alloc(nextPow2(fullLength));
}
await readfd(fd, body, 0, fullLength, null);
close(fd);
const tableLength = body.readUInt32LE(0);
const tableBody = body.utf8Slice(4, 4 + tableLength);
const table = parseJson(tableBody);
const content = body.slice(4 + tableLength);
return [table, content, body];
}
async read() {
const out = {};
const size = { used: 0, total: 0 };
const table = {};
const order = {};
await mkdirp(this.path);
const items = await readdir(this.path);
const logs = items.filter(item => /^log\d+$/.test(item));
logs.sort();
const reverseLogs = logs.reverse();
const sema = new Semaphore(8);
return Promise.all(
reverseLogs.map(async (_file, index) => {
const file = join(this.path, _file);
const guard = await sema.guard();
const [table, content, body] = await this._readFile(file);
const keys = Object.keys(table);
if (keys.length > 0) {
size.total += table[keys.length - 1].end;
}
for (const entry of table) {
if (
typeof order[entry.name] === 'undefined' ||
order[entry.name] > index
) {
if (typeof order[entry.name] !== 'undefined') {
size.used -= table[entry.name];
}
table[entry.name] = entry.end - entry.start;
size.used += entry.end - entry.start;
order[entry.name] = index;
// Negative start positions are not set on the output. They are
// treated as if they were deleted in a prior write. A future
// compact will remove all instances of any old entries.
if (entry.start >= 0) {
await new Promise(process.nextTick);
const data = content.utf8Slice(entry.start, entry.end);
if (this.autoParse) {
out[entry.name] = parseJson(data);
} else {
out[entry.name] = data;
}
} else {
delete out[entry.name];
}
}
}
drop(body);
guard.done();
}),
)
.then(async () => {
if (size.used / size.total < 0.6) {
await this.compact(out);
}
})
.then(() => out);
}
async _markLog() {
const count = (await readdir(this.path)).filter(item =>
/log\d+$/.test(item),
).length;
const marker = Math.random()
.toString(16)
.substring(2)
.padStart(13, '0');
const logName = `log${count.toString().padStart(4, '0')}`;
const file = resolve(this.path, logName);
await write(file, marker);
const writtenMarker = await read(file, 'utf8');
if (marker === writtenMarker) {
return file;
}
return null;
}
async _write(file, output) {
// 4 bytes - full length
// 4 bytes - length of table
// x bytes - table
// y bytes - content
// Write table into a temporary buffer at position 8
const content = JSON.stringify(output.table);
let length = tmpBuffer.utf8Write(content, 8);
// Make the temporary buffer longer if the space used is the same as the
// length
while (8 + length === tmpBuffer.length) {
tmpBuffer = Buffer.allocUnsafe(nextPow2(8 + length));
// Write again to see if the length is more due to the last buffer being
// too short.
length = tmpBuffer.utf8Write(content, 8);
}
// Ensure the buffer is long enough to fit the table and content.
const end = 8 + length + output.length;
if (end > tmpBuffer.length) {
tmpBuffer = resizePow2(tmpBuffer, end);
}
// Copy the output after the table.
output.buffer.copy(tmpBuffer.slice(8 + length, end));
// Full length after this uint.
tmpBuffer.writeUInt32LE(end - 4, 0);
// Length of table after this uint.
tmpBuffer.writeUInt32LE(length, 4);
if (end > output.buffer.length) {
output.buffer = alloc(nextPow2(end));
}
tmpBuffer.copy(output.buffer.slice(0, end));
await write(file, output.buffer.slice(0, end));
drop(output.buffer);
}
async _markAndWrite(output) {
const file = await this._markLog();
if (file !== null) {
await this._write(file, output.take());
}
}
// Write out a log chunk once the file reaches the maximum chunk size.
async _writeAtMax(output) {
while (output.length >= MAX_CHUNK) {
await this._markAndWrite(output);
}
}
// Write out a log chunk if their is any entries in the table.
async _writeAtAny(output) {
while (output.table.length > 0) {
await this._markAndWrite(output);
}
}
async write(ops) {
let smallOutput = new WriteOutput();
let largeOutput = new WriteOutput();
const outputPromises = [];
await mkdirp(this.path);
for (const op of ops) {
if (op.value !== null) {
let content = op.value;
if (typeof content !== 'string') {
content = JSON.stringify(content);
}
if (content.length < LARGE_CONTENT) {
smallOutput.add(op.key, content);
await this._writeAtMax(smallOutput);
} else {
largeOutput.add(op.key, content);
await this._writeAtMax(largeOutput);
}
} else {
smallOutput.add(op.key, null);
await this._writeAtMax(smallOutput);
}
}
await this._writeAtAny(smallOutput);
await this._writeAtAny(largeOutput);
await Promise.all(outputPromises);
}
async sizes() {
const size = {
used: 0,
total: 0,
};
const table = {};
const order = {};
await mkdirp(this.path);
const items = await readdir(this.path);
const logs = items.filter(item => /^log\d+$/.test(item));
logs.sort();
const reverseLogs = logs.reverse();
const sema = new Semaphore(8);
return Promise.all(
reverseLogs.map(async (_file, index) => {
const file = join(this.path, _file);
const guard = await sema.guard();
const [table, content, body] = await this._readFile(file);
size.total += content.length;
for (const entry of table) {
if (
typeof order[entry.name] === 'undefined' ||
order[entry.name] > index
) {
if (typeof order[entry.name] !== 'undefined') {
size.used -= table[entry.name];
}
table[entry.name] = entry.end - entry.start;
size.used += entry.end - entry.start;
order[entry.name] = index;
}
}
drop(body);
guard.done();
}),
).then(() => size);
}
async compact(_obj = this.read()) {
const obj = await _obj;
const ops = [];
for (const key in obj) {
ops.push({
key,
value: obj[key],
});
}
const truePath = this.path;
this.path += '~';
await this.write(ops);
this.path = truePath;
await rimraf(this.path);
await rename(`${this.path}~`, this.path);
}
}
module.exports = Append2;
+48
View File
@@ -0,0 +1,48 @@
const join = require('path').join;
const pluginCompat = require('./util/plugin-compat');
let Append2Serializer;
const _blockSizeByName = {
data: 4 * 1024,
md5: 128,
'missing-resolve': 256,
module: 4 * 1024,
'module-resolve': 1024,
resolver: 256,
};
class SerializerAppend2Plugin {
apply(compiler) {
pluginCompat.tap(
compiler,
'hardSourceCacheFactory',
'Append2Serializer',
factory => info => {
if (info.type === 'data') {
return SerializerAppend2Plugin.createSerializer(info);
}
return factory(info);
},
);
}
}
SerializerAppend2Plugin.createSerializer = ({
cacheDirPath,
name,
autoParse,
}) => {
if (!Append2Serializer) {
Append2Serializer = require('./SerializerAppend2');
}
return new Append2Serializer({
cacheDirPath: join(cacheDirPath, name),
blockSize: _blockSizeByName[name],
autoParse: autoParse,
});
};
module.exports = SerializerAppend2Plugin;
+48
View File
@@ -0,0 +1,48 @@
const join = require('path').join;
const pluginCompat = require('./util/plugin-compat');
let AppendSerializer;
const _blockSizeByName = {
data: 4 * 1024,
md5: 128,
'missing-resolve': 256,
module: 4 * 1024,
'module-resolve': 1024,
resolver: 256,
};
class SerializerAppendPlugin {
apply(compiler) {
pluginCompat.tap(
compiler,
'hardSourceCacheFactory',
'AppendSerializer',
factory => info => {
if (info.type === 'data') {
return SerializerAppendPlugin.createSerializer(info);
}
return factory(info);
},
);
}
}
SerializerAppendPlugin.createSerializer = ({
cacheDirPath,
name,
autoParse,
}) => {
if (!AppendSerializer) {
AppendSerializer = require('./SerializerAppend');
}
return new AppendSerializer({
cacheDirPath: join(cacheDirPath, name),
blockSize: _blockSizeByName[name],
autoParse: autoParse,
});
};
module.exports = SerializerAppendPlugin;
+43
View File
@@ -0,0 +1,43 @@
const cacache = require('cacache');
class CacacheSerializer {
constructor({ cacheDirPath }) {
this.path = cacheDirPath;
}
read() {
const cache = {};
const promises = [];
return new Promise((resolve, reject) => {
cacache.ls
.stream(this.path)
.on('data', ({ key }) => {
promises.push(
cacache.get(this.path, key).then(({ data }) => {
cache[key] = JSON.parse(data);
}),
);
})
.on('error', reject)
.on('end', () => {
resolve();
});
})
.then(() => Promise.all(promises))
.then(() => cache);
}
write(ops) {
return Promise.all(
ops.map(op => {
if (op.value) {
return cacache.put(this.path, op.key, JSON.stringify(op.value));
} else {
return cacache.rm.entry(this.path, op.key);
}
}),
);
}
}
module.exports = CacacheSerializer;
+33
View File
@@ -0,0 +1,33 @@
const join = require('path').join;
const pluginCompat = require('./util/plugin-compat');
let CacacheSerializer;
class SerializerCacachePlugin {
apply(compiler) {
pluginCompat.tap(
compiler,
'hardSourceCacheFactory',
'CacacheSerializer',
factory => info => {
if (info.type === 'data') {
return SerializerCacachePlugin.createSerializer(info);
}
return factory(info);
},
);
}
}
SerializerCacachePlugin.createSerializer = ({ cacheDirPath, name }) => {
if (!CacacheSerializer) {
CacacheSerializer = require('./SerializerCacache');
}
return new CacacheSerializer({
cacheDirPath: join(cacheDirPath, name),
});
};
module.exports = SerializerCacachePlugin;
+50
View File
@@ -0,0 +1,50 @@
const fs = require('graceful-fs');
const join = require('path').join;
const _mkdirp = require('mkdirp');
const promisify = require('./util/promisify');
const mkdirp = promisify(_mkdirp);
const fsReadFile = promisify(fs.readFile, { context: fs });
const fsReaddir = promisify(fs.readdir, { context: fs });
const fsWriteFile = promisify(fs.writeFile, { context: fs });
class FileSerializer {
constructor({ cacheDirPath }) {
this.path = cacheDirPath;
}
read() {
const assets = {};
const cacheAssetDirPath = this.path;
return mkdirp(cacheAssetDirPath)
.then(() => fsReaddir(cacheAssetDirPath))
.then(dir =>
dir.map(name =>
Promise.all([name, fsReadFile(join(cacheAssetDirPath, name))]),
),
)
.then(a => Promise.all(a))
.then(_assets => {
for (let i = 0; i < _assets.length; i++) {
assets[_assets[i][0]] = _assets[i][1];
}
})
.then(() => assets);
}
write(assetOps) {
const cacheAssetDirPath = this.path;
return mkdirp(cacheAssetDirPath)
.then(() =>
assetOps.map(({ key, value }) => {
const assetPath = join(cacheAssetDirPath, key);
return fsWriteFile(assetPath, value);
}),
)
.then(a => Promise.all(a));
}
}
module.exports = FileSerializer;
+33
View File
@@ -0,0 +1,33 @@
const join = require('path').join;
const pluginCompat = require('./util/plugin-compat');
let FileSerializer;
class SerializerFilePlugin {
apply(compiler) {
pluginCompat.tap(
compiler,
'hardSourceCacheFactory',
'FileSerializer',
factory => info => {
if (info.type === 'file') {
return SerializerFilePlugin.createSerializer(info);
}
return factory(info);
},
);
}
}
SerializerFilePlugin.createSerializer = ({ cacheDirPath, name }) => {
if (!FileSerializer) {
FileSerializer = require('./SerializerFile');
}
return new FileSerializer({
cacheDirPath: join(cacheDirPath, name),
});
};
module.exports = SerializerFilePlugin;
+38
View File
@@ -0,0 +1,38 @@
const fs = require('graceful-fs');
const promisify = require('./util/promisify');
const fsReadFile = promisify(fs.readFile, { context: fs });
const fsWriteFile = promisify(fs.writeFile, { context: fs });
class JsonSerializer {
constructor({ cacheDirPath }) {
this.path = cacheDirPath;
if (!/\.json$/.test(this.path)) {
this.path += '.json';
}
}
read() {
const cacheDirPath = this.path;
return fsReadFile(cacheDirPath, 'utf8')
.catch(() => '{}')
.then(JSON.parse);
}
write(moduleOps) {
const cacheDirPath = this.path;
return this.read()
.then(cache => {
for (let i = 0; i < moduleOps.length; i++) {
const op = moduleOps[i];
cache[op.key] = op.value;
}
return cache;
})
.then(JSON.stringify)
.then(cache => fsWriteFile(cacheDirPath, cache));
}
}
module.exports = JsonSerializer;
+33
View File
@@ -0,0 +1,33 @@
const join = require('path').join;
const pluginCompat = require('./util/plugin-compat');
let JsonSerializer;
class SerializerJsonPlugin {
apply(compiler) {
pluginCompat.tap(
compiler,
'hardSourceCacheFactory',
'JsonSerializer',
factory => info => {
if (info.type === 'data') {
return SerializerJsonPlugin.createSerializer(info);
}
return factory(info);
},
);
}
}
SerializerJsonPlugin.createSerializer = ({ cacheDirPath, name }) => {
if (!JsonSerializer) {
JsonSerializer = require('./SerializerJson');
}
return new JsonSerializer({
cacheDirPath: join(cacheDirPath, name),
});
};
module.exports = SerializerJsonPlugin;
+63
View File
@@ -0,0 +1,63 @@
const _level = require('level');
const promisify = require('./util/promisify');
const level = promisify(_level);
class LevelDbSerializer {
constructor({ cacheDirPath }) {
this.path = cacheDirPath;
this.leveldbLock = Promise.resolve();
}
read() {
const start = Date.now();
const moduleCache = {};
return level(this.path)
.then(
db =>
new Promise((resolve, reject) => {
const dbClose = promisify(db.close, { context: db });
db.createReadStream()
.on('data', data => {
const value = data.value;
if (!moduleCache[data.key]) {
moduleCache[data.key] = value;
}
})
.on('end', () => {
dbClose().then(resolve, reject);
});
}),
)
.then(() => moduleCache);
}
write(moduleOps) {
const ops = moduleOps;
if (ops.length === 0) {
return Promise.resolve();
}
for (let i = 0; i < ops.length; i++) {
if (ops[i].value === null) {
ops[i].type = 'delete';
} else {
if (typeof ops[i].value !== 'string') {
ops[i].value = JSON.stringify(ops[i].value);
}
ops[i].type = 'put';
}
}
const cachePath = this.path;
return (this.leveldbLock = this.leveldbLock
.then(() => level(cachePath))
.then(db => promisify(db.batch, { context: db })(ops).then(() => db))
.then(db => promisify(db.close, { context: db })()));
}
}
module.exports = LevelDbSerializer;
+44
View File
@@ -0,0 +1,44 @@
const join = require('path').join;
const pluginCompat = require('./util/plugin-compat');
let LevelDbSerializer;
let AppendSerializerPlugin;
class HardSourceLevelDbSerializerPlugin {
apply(compiler) {
pluginCompat.tap(
compiler,
'hardSourceCacheFactory',
'LevelDbSerializer',
factory => info => {
if (info.type === 'data') {
return HardSourceLevelDbSerializerPlugin.createSerializer(info);
}
return factory(info);
},
);
}
}
HardSourceLevelDbSerializerPlugin.createSerializer = info => {
if (!LevelDbSerializer) {
try {
LevelDbSerializer = require('./SerializerLeveldb');
} catch (e) {}
}
if (LevelDbSerializer) {
return new LevelDbSerializer({
cacheDirPath: join(info.cacheDirPath, info.name),
});
} else {
if (!AppendSerializerPlugin) {
AppendSerializerPlugin = require('./SerializerAppendPlugin');
}
return AppendSerializerPlugin.createSerializer(info);
}
};
module.exports = HardSourceLevelDbSerializerPlugin;
@@ -0,0 +1,34 @@
const path = require('path');
let extractTextNS;
let extractTextNS2;
try {
extractTextNS = path.dirname(require.resolve('extract-text-webpack-plugin'));
} catch (_) {}
const pluginCompat = require('./util/plugin-compat');
class SupportExtractTextPlugin {
apply(compiler) {
pluginCompat.tap(
compiler,
'_hardSourceAfterFreezeModule',
'SupportExtractTextPlugin',
(frozen, module, extra) => {
// Ignore the modules that kick off child compilers in extract text.
// These modules must always be built so the child compilers run so
// that assets get built.
if (
module[extractTextNS] ||
(!module.factoryMeta && module.meta && module.meta[extractTextNS])
) {
return null;
}
return frozen;
},
);
}
}
module.exports = SupportExtractTextPlugin;
@@ -0,0 +1,67 @@
const pluginCompat = require('./util/plugin-compat');
const mixinCssDependency = function(CssDependency) {
const Dependency = Object.getPrototypeOf(CssDependency.prototype).constructor;
CssDependency.prototype.updateHash = function(hash) {
Dependency.prototype.updateHash.call(this, hash);
hash.update(this.content);
};
};
class SupportMiniCssExtractPlugin {
apply(compiler) {
let CssDependency;
pluginCompat.tap(
compiler,
'make',
'SupportMiniCssExtractPlugin',
({ dependencyFactories }) => {
const Dependencies = dependencyFactories.keys();
for (const Dep of Dependencies) {
if (Dep.name === 'CssDependency') {
CssDependency = Dep;
mixinCssDependency(CssDependency);
break;
}
}
},
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeDependency',
'HardMiniCssExtractPlugin freeze',
(frozen, dependency, extra) => {
if (dependency.constructor === CssDependency) {
return {
type: 'CssDependency',
line: {
identifier: dependency.identifier,
content: dependency.content,
media: dependency.media,
sourceMap: dependency.sourceMap,
},
context: dependency.context,
identifierIndex: dependency.identifierIndex,
};
}
return frozen;
},
);
pluginCompat.tap(
compiler,
'_hardSourceThawDependency',
'HardMiniCssExtractPlugin',
(dependency, { type, line, context, identifierIndex }, extra) => {
if (type === 'CssDependency') {
return new CssDependency(line, context, identifierIndex);
}
return dependency;
},
);
}
}
module.exports = SupportMiniCssExtractPlugin;
+206
View File
@@ -0,0 +1,206 @@
const pluginCompat = require('./util/plugin-compat');
class ArchetypeSystem {
apply(compiler) {
const compilerHooks = pluginCompat.hooks(compiler);
const archetypeCaches = {
// asset: assetArchetypeCache,
// Asset: assetArchetypeCache,
// module: moduleArchetypeCache,
// Module: moduleArchetypeCache,
};
pluginCompat.register(compiler, '_hardSourceArchetypeRegister', 'sync', [
'name',
'archetypeCache',
]);
compilerHooks._hardSourceArchetypeRegister.tap(
'HardSource - ArchetypeSystem',
(name, cache) => {
archetypeCaches[name] = cache;
archetypeCaches[name.toLowerCase()] = cache;
},
);
let freeze;
let thaw;
let mapMap;
let mapFreeze;
let mapThaw;
let store;
let fetch;
pluginCompat.register(compiler, '_hardSourceMethods', 'sync', ['methods']);
[
'Asset',
'Compilation',
'Dependency',
'DependencyBlock',
'DependencyVariable',
'Module',
'ModuleAssets',
'ModuleError',
'ModuleWarning',
'Source',
].forEach(archetype => {
pluginCompat.register(
compiler,
`_hardSourceBeforeFreeze${archetype}`,
'syncWaterfall',
['frozen', 'item', 'extra'],
);
pluginCompat.register(
compiler,
`_hardSourceFreeze${archetype}`,
'syncWaterfall',
['frozen', 'item', 'extra'],
);
pluginCompat.register(
compiler,
`_hardSourceAfterFreeze${archetype}`,
'syncWaterfall',
['frozen', 'item', 'extra'],
);
pluginCompat.register(
compiler,
`_hardSourceBeforeThaw${archetype}`,
'syncWaterfall',
['item', 'frozen', 'extra'],
);
pluginCompat.register(
compiler,
`_hardSourceThaw${archetype}`,
'syncWaterfall',
['item', 'frozen', 'extra'],
);
pluginCompat.register(
compiler,
`_hardSourceAfterThaw${archetype}`,
'syncWaterfall',
['item', 'frozen', 'extra'],
);
});
function run(_compiler) {
let compiler = _compiler;
if (_compiler.compiler) {
compiler = _compiler.compiler;
}
freeze = (archetype, frozen, item, extra) => {
if (!item) {
return item;
}
frozen = pluginCompat.call(
compiler,
`_hardSourceBeforeFreeze${archetype}`,
[frozen, item, extra],
);
frozen = pluginCompat.call(compiler, `_hardSourceFreeze${archetype}`, [
frozen,
item,
extra,
]);
frozen = pluginCompat.call(
compiler,
`_hardSourceAfterFreeze${archetype}`,
[frozen, item, extra],
);
return frozen;
};
thaw = (archetype, item, frozen, extra) => {
if (!frozen) {
return frozen;
}
item = pluginCompat.call(
compiler,
`_hardSourceBeforeThaw${archetype}`,
[item, frozen, extra],
);
item = pluginCompat.call(compiler, `_hardSourceThaw${archetype}`, [
item,
frozen,
extra,
]);
item = pluginCompat.call(compiler, `_hardSourceAfterThaw${archetype}`, [
item,
frozen,
extra,
]);
return item;
};
mapMap = (fn, name, output, input, extra) => {
if (output) {
return input
.map((item, index) => fn(name, output[index], item, extra))
.filter(Boolean);
} else {
return input.map(item => fn(name, null, item, extra)).filter(Boolean);
}
};
mapFreeze = (name, frozen, items, extra) =>
mapMap(freeze, name, frozen, items, extra);
mapThaw = (name, items, frozen, extra) =>
mapMap(thaw, name, items, frozen, extra);
store = (archetype, id, item, extra) => {
const cache = archetypeCaches[archetype];
if (item) {
const frozen = cache.get(id);
const newFrozen = freeze(archetype, frozen, item, extra);
if (
(frozen && newFrozen && newFrozen !== frozen) ||
(!frozen && newFrozen)
) {
cache.set(id, newFrozen);
return newFrozen;
} else if (frozen) {
return frozen;
}
} else {
cache.set(id, null);
}
};
fetch = (archetype, id, extra) => {
const cache = archetypeCaches[archetype];
const frozen = cache.get(id);
return thaw(archetype, null, frozen, extra);
};
const methods = {
freeze,
thaw,
mapFreeze,
mapThaw,
store,
fetch,
};
pluginCompat.call(compiler, '_hardSourceMethods', [methods]);
}
compilerHooks.watchRun.tap('HardSource - ArchetypeSystem', run);
compilerHooks.run.tap('HardSource - ArchetypeSystem', run);
compilerHooks.compilation.tap(
'HardSource - ArchetypeSystem',
compilation => {
compilation.__hardSourceMethods = {
freeze,
thaw,
mapFreeze,
mapThaw,
store,
fetch,
};
},
);
}
}
module.exports = ArchetypeSystem;
+38
View File
@@ -0,0 +1,38 @@
const pluginCompat = require('./util/plugin-compat');
const logMessages = require('./util/log-messages');
const { ParityRoot } = require('./util/parity');
class ParitySystem {
apply(compiler) {
pluginCompat.register(compiler, '_hardSourceParityCache', 'sync', [
'parityRoot',
]);
const compilerHooks = pluginCompat.hooks(compiler);
function runParityOrReset(_compiler) {
const parityRoot = new ParityRoot();
compilerHooks._hardSourceParityCache.call(parityRoot);
if (!parityRoot.verify()) {
logMessages.cacheNoParity(compiler, { parityRoot });
// Reset the cache, some part of it is incomplete and using it will lead
// to errors.
compilerHooks._hardSourceResetCache.call();
}
return Promise.resolve();
}
compilerHooks.watchRun.tapPromise(
'HardSource - index - parityOrReset',
runParityOrReset,
);
compilerHooks.run.tapPromise(
'HardSource - index - parityOrReset',
runParityOrReset,
);
}
}
module.exports = ParitySystem;
+163
View File
@@ -0,0 +1,163 @@
const { readdir: _readdir, stat: _stat } = require('graceful-fs');
const { basename, join } = require('path');
const _rimraf = require('rimraf');
const logMessages = require('./util/log-messages');
const pluginCompat = require('./util/plugin-compat');
const promisify = require('./util/promisify');
const readdir = promisify(_readdir);
const rimraf = promisify(_rimraf);
const stat = promisify(_stat);
const directorySize = async dir => {
const _stat = await stat(dir);
if (_stat.isFile()) {
return _stat.size;
}
if (_stat.isDirectory()) {
const names = await readdir(dir);
let size = 0;
for (const name of names) {
size += await directorySize(join(dir, name));
}
return size;
}
return 0;
};
class CacheInfo {
constructor(id = '') {
this.id = id;
this.lastModified = 0;
this.size = 0;
}
static async fromDirectory(dir) {
const info = new CacheInfo(basename(dir));
info.lastModified = new Date(
(await stat(join(dir, 'stamp'))).mtime,
).getTime();
info.size = await directorySize(dir);
return info;
}
static async fromDirectoryChildren(dir) {
const children = [];
const names = await readdir(dir);
for (const name of names) {
children.push(await CacheInfo.fromDirectory(join(dir, name)));
}
return children;
}
}
// Compilers for webpack with multiple parallel configurations might try to
// delete caches at the same time. Mutex lock the process of pruning to keep
// from multiple pruning runs from colliding with each other.
let deleteLock = null;
class PruneCachesSystem {
constructor(cacheRoot, options = {}) {
this.cacheRoot = cacheRoot;
this.options = Object.assign(
{
// Caches younger than `maxAge` are not considered for deletion. They
// must be at least this (default: 2 days) old in milliseconds.
maxAge: 2 * 24 * 60 * 60 * 1000,
// All caches together must be larger than `sizeThreshold` before any
// caches will be deleted. Together they must be at least this
// (default: 50 MB) big in bytes.
sizeThreshold: 50 * 1024 * 1024,
},
options,
);
}
apply(compiler) {
const compilerHooks = pluginCompat.hooks(compiler);
const deleteOldCaches = async () => {
while (deleteLock !== null) {
await deleteLock;
}
let resolveLock;
let infos;
try {
deleteLock = new Promise(resolve => {
resolveLock = resolve;
});
infos = await CacheInfo.fromDirectoryChildren(this.cacheRoot);
// Sort lastModified in descending order. More recently modified at the
// beginning of the array.
infos.sort((a, b) => b.lastModified - a.lastModified);
const totalSize = infos.reduce((carry, info) => carry + info.size, 0);
const oldInfos = infos.filter(
info => info.lastModified < Date.now() - this.options.maxAge,
);
const oldTotalSize = oldInfos.reduce(
(carry, info) => carry + info.size,
0,
);
if (oldInfos.length > 0 && totalSize > this.options.sizeThreshold) {
const newInfos = infos.filter(
info => info.lastModified >= Date.now() - this.options.maxAge,
);
for (const info of oldInfos) {
rimraf(join(this.cacheRoot, info.id));
}
const newTotalSize = newInfos.reduce(
(carry, info) => carry + info.size,
0,
);
logMessages.deleteOldCaches(compiler, {
infos,
totalSize,
newInfos,
newTotalSize,
oldInfos,
oldTotalSize,
});
} else {
logMessages.keepCaches(compiler, {
infos,
totalSize,
});
}
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
} finally {
if (typeof resolveLock === 'function') {
deleteLock = null;
resolveLock();
}
}
};
compilerHooks.watchRun.tapPromise(
'HardSource - PruneCachesSystem',
deleteOldCaches,
);
compilerHooks.run.tapPromise(
'HardSource - PruneCachesSystem',
deleteOldCaches,
);
}
}
module.exports = PruneCachesSystem;
+35
View File
@@ -0,0 +1,35 @@
const RawSource = require('webpack-sources').RawSource;
const pluginCompat = require('./util/plugin-compat');
class TransformAssetPlugin {
apply(compiler) {
pluginCompat.tap(
compiler,
'_hardSourceFreezeAsset',
'TransformAssetPlugin freeze',
(frozen, asset, extra) => asset.source(),
);
pluginCompat.tap(
compiler,
'_hardSourceThawAsset',
'TransformAssetPlugin thaw',
(thawed, asset, extra) => {
if (!thawed) {
thawed = asset;
if (thawed.type === 'buffer') {
thawed = new Buffer(thawed);
}
if (!(thawed instanceof RawSource)) {
thawed = new RawSource(thawed);
}
}
return thawed;
},
);
}
}
module.exports = TransformAssetPlugin;
@@ -0,0 +1,79 @@
const pluginCompat = require('./util/plugin-compat');
function freezeDependency(dependency, extra, methods) {
if (extra.schemas.map.has(dependency.constructor)) {
return extra.schemas.map
.get(dependency.constructor)
.freeze(dependency, dependency, extra, methods);
}
if (extra.schemas[dependency.constructor.name]) {
return extra.schemas[dependency.constructor.name].freeze(
dependency,
dependency,
extra,
methods,
);
}
}
function thawDependency(frozen, extra, methods) {
if (extra.schemas[frozen.type]) {
return extra.schemas[frozen.type].thaw(null, frozen, extra, methods);
}
}
class TransformBasicDependencyPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
if (this.options.schema < 4) {
const TransformBasicDependencyPluginLegacy = require('./TransformBasicDependencyPluginLegacy');
new TransformBasicDependencyPluginLegacy(this.options).apply(compiler);
} else {
const schemas = require('./schema-4');
let methods;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'TransformBasicDependencyPlugin methods',
_methods => {
methods = _methods;
},
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeDependency',
'TransformBasicDependencyPlugin freeze',
(frozen, dependency, extra) => {
extra.schemas = schemas;
const _frozen = freezeDependency(dependency, extra, methods);
if (_frozen) {
return _frozen;
}
return frozen;
},
);
pluginCompat.tap(
compiler,
'_hardSourceThawDependency',
'TransformBasicDependencyPlugin',
(dependency, frozen, extra) => {
extra.schemas = schemas;
const _thawed = thawDependency(frozen, extra, methods);
if (_thawed) {
return _thawed;
}
return dependency;
},
);
}
}
}
module.exports = TransformBasicDependencyPlugin;
@@ -0,0 +1,822 @@
const cachePrefix = require('./util').cachePrefix;
const LoggerFactory = require('./loggerFactory');
const pluginCompat = require('./util/plugin-compat');
const relateContext = require('./util/relate-context');
let LocalModule;
try {
LocalModule = require('webpack/lib/dependencies/LocalModule');
} catch (_) {}
function flattenPrototype(obj) {
if (typeof obj === 'string') {
return obj;
}
const copy = {};
for (const key in obj) {
copy[key] = obj[key];
}
return copy;
}
let AMDDefineDependency;
let AMDRequireArrayDependency;
let AMDRequireContextDependency;
let AMDRequireDependency;
let AMDRequireItemDependency;
let CommonJsRequireContextDependency;
let CommonJsRequireDependency;
let ConstDependency;
let ContextDependency;
let ContextElementDependency;
let CriticalDependencyWarning;
let DelegatedExportsDependency;
let DelegatedSourceDependency;
let DllEntryDependency;
let HarmonyAcceptDependency;
let HarmonyAcceptImportDependency;
let HarmonyCompatibilityDependency;
let HarmonyExportExpressionDependency;
let HarmonyExportHeaderDependency;
let HarmonyExportImportedSpecifierDependency;
let HarmonyExportSpecifierDependency;
let HarmonyImportDependency;
let HarmonyImportSpecifierDependency;
let ImportContextDependency;
let ImportDependency;
let ImportEagerContextDependency;
let ImportEagerDependency;
let ImportLazyContextDependency;
let ImportLazyOnceContextDependency;
let ImportWeakContextDependency;
let ImportWeakDependency;
let LoaderDependency;
let LocalModuleDependency;
let ModuleDependency;
let ModuleHotAcceptDependency;
let ModuleHotDeclineDependency;
let MultiEntryDependency;
let NullDependency;
let PrefetchDependency;
let RequireContextDependency;
let RequireEnsureDependency;
let RequireEnsureItemDependency;
let RequireHeaderDependency;
let RequireIncludeDependency;
let RequireResolveContextDependency;
let RequireResolveDependency;
let RequireResolveHeaderDependency;
let SingleEntryDependency;
let UnsupportedDependency;
const DependencySchemas2 = [
[
'AMDDefineDependency',
'range',
'arrayRange',
'functionRange',
'objectRange',
'namedModule',
],
['AMDRequireArrayDependency', 'depsArray', 'range'],
[
'AMDRequireContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
],
['AMDRequireDependency', 'block'],
['AMDRequireItemDependency', 'request', 'range'],
[
'CommonJsRequireContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
],
['CommonJsRequireDependency', 'request', 'range'],
['ConstDependency', 'expression', 'range'],
['ContextDependency', 'request', 'recursive', 'regExp'],
['ContextElementDependency', 'request', 'userRequest'],
['DelegatedSourceDependency', 'request'],
['DllEntryDependency', 'dependencies', 'name'],
['HarmonyAcceptDependency', 'range', 'dependencies', 'hasCallback'],
['HarmonyAcceptImportDependency', 'request', 'importedVar', 'range'],
['HarmonyCompatibilityDependency', 'originModule'],
[
'HarmonyExportExpressionDependency',
'originModule',
'range',
'rangeStatement',
'prefix',
],
['HarmonyExportHeaderDependency', 'range', 'rangeStatement'],
[
'HarmonyExportImportedSpecifierDependency',
'originModule',
'importDependency',
'importedVar',
'id',
'name',
],
[
'HarmonyExportSpecifierDependency',
'originModule',
'id',
'name',
'position',
'immutable',
],
['HarmonyImportDependency', 'request', 'importedVar', 'range'],
[
'HarmonyImportSpecifierDependency',
'importDependency',
'importedVar',
'id',
'name',
'range',
'strictExportPresence',
],
[
'ImportContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
'chunkName',
],
['ImportDependency', 'request', 'block'],
[
'ImportEagerContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
'chunkName',
],
['ImportEagerDependency', 'request', 'range'],
[
'ImportLazyContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
'chunkName',
],
[
'ImportLazyOnceContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
'chunkName',
],
['LoaderDependency', 'request'],
['LocalModuleDependency', 'localModule', 'range'],
['ModuleDependency', 'request'],
['ModuleHotAcceptDependency', 'request', 'range'],
['ModuleHotDeclineDependency', 'request', 'range'],
['MultiEntryDependency', 'dependencies', 'name'],
['NullDependency'],
['PrefetchDependency', 'request'],
['RequireContextDependency', 'request', 'recursive', 'regExp', 'range'],
['RequireEnsureDependency', 'block'],
['RequireEnsureItemDependency', 'request'],
['RequireHeaderDependency', 'range'],
['RequireIncludeDependency', 'request', 'range'],
[
'RequireResolveContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
],
['RequireResolveDependency', 'request', 'range'],
['RequireResolveHeaderDependency', 'range'],
['SingleEntryDependency', 'request'],
['UnsupportedDependency', 'request', 'range'],
];
const DependencySchemas3 = [
[
'AMDDefineDependency',
'range',
'arrayRange',
'functionRange',
'objectRange',
'namedModule',
],
['AMDRequireArrayDependency', 'depsArray', 'range'],
[
'AMDRequireContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
],
['AMDRequireDependency', 'block'],
['AMDRequireItemDependency', 'request', 'range'],
[
'CommonJsRequireContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
],
['CommonJsRequireDependency', 'request', 'range'],
['ConstDependency', 'expression', 'range'],
['ContextDependency', 'request', 'recursive', 'regExp'],
['ContextElementDependency', 'request', 'userRequest'],
['CriticalDependencyWarning', 'message'],
['DelegatedExportsDependency', 'originModule', 'exports'],
['DelegatedSourceDependency', 'request'],
['DllEntryDependency', 'dependencies', 'name'],
['HarmonyAcceptDependency', 'range', 'dependencies', 'hasCallback'],
['HarmonyAcceptImportDependency', 'request', 'importedVar', 'range'],
['HarmonyCompatibilityDependency', 'originModule'],
[
'HarmonyExportExpressionDependency',
'originModule',
'range',
'rangeStatement',
'prefix',
],
['HarmonyExportHeaderDependency', 'range', 'rangeStatement'],
[
'HarmonyExportImportedSpecifierDependency',
'originModule',
'importDependency',
'importedVar',
'id',
'name',
'activeExports',
'otherStarExports',
],
[
'HarmonyExportSpecifierDependency',
'originModule',
'id',
'name',
'position',
'immutable',
],
['HarmonyImportDependency', 'request', 'importedVar', 'range'],
[
'HarmonyImportSpecifierDependency',
'importDependency',
'importedVar',
'id',
'name',
'range',
'strictExportPresence',
],
[
'ImportContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
'chunkName',
],
['ImportDependency', 'request', 'block'],
[
'ImportEagerContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
'chunkName',
],
['ImportEagerDependency', 'request', 'range'],
[
'ImportLazyContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
'chunkName',
],
[
'ImportLazyOnceContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
'chunkName',
],
[
'ImportWeakContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
'chunkName',
],
['ImportWeakDependency', 'request', 'range'],
['LoaderDependency', 'request'],
['LocalModuleDependency', 'localModule', 'range'],
['ModuleDependency', 'request'],
['ModuleHotAcceptDependency', 'request', 'range'],
['ModuleHotDeclineDependency', 'request', 'range'],
['MultiEntryDependency', 'dependencies', 'name'],
['NullDependency'],
['PrefetchDependency', 'request'],
[
'RequireContextDependency',
'request',
'recursive',
'regExp',
'asyncMode',
'range',
],
['RequireEnsureDependency', 'block'],
['RequireEnsureItemDependency', 'request'],
['RequireHeaderDependency', 'range'],
['RequireIncludeDependency', 'request', 'range'],
[
'RequireResolveContextDependency',
'request',
'recursive',
'regExp',
'range',
'valueRange',
],
['RequireResolveDependency', 'request', 'range'],
['RequireResolveHeaderDependency', 'range'],
['SingleEntryDependency', 'request'],
['UnsupportedDependency', 'request', 'range'],
];
const freezeArgument = {
dependencies(arg, dependency, extra, methods) {
return methods.mapFreeze('Dependency', null, arg, extra);
},
depsArray(arg, dependency, extra, methods) {
return methods.mapFreeze('Dependency', null, arg, extra);
},
localModule({ name, idx }, dependency, extra, methods) {
return {
name: name,
idx: idx,
};
},
regExp(arg, dependency, extra, methods) {
return arg ? arg.source : false;
},
request(arg, dependency, extra, methods) {
return relateContext.relateAbsoluteRequest(extra.module.context, arg);
},
userRequest(arg, dependency, extra, methods) {
return relateContext.relateAbsoluteRequest(extra.module.context, arg);
},
block(arg, dependency, extra, methods) {
// Dependency nested in a parent. Freezing the block is a loop.
if (arg.dependencies.includes(dependency)) {
return;
}
return methods.freeze('DependencyBlock', null, arg, extra);
},
importDependency(arg, dependency, extra, methods) {
return methods.freeze('Dependency', null, arg, extra);
},
originModule(arg, dependency, extra, methods) {
// This will be in extra, generated or found during the process of thawing.
},
activeExports(arg, dependency, extra, methods) {
return null;
},
otherStarExports(arg, dependency, extra, methods) {
if (arg) {
// This will be in extra, generated during the process of thawing.
return 'star';
}
return null;
},
options(arg, dependency, extra, methods) {
if (arg.regExp) {
return Object.assign({}, arg, {
regExp: arg.regExp.source,
});
}
return arg;
},
parserScope(arg, dependencies, extra, methods) {
return;
},
};
const thawArgument = {
dependencies(arg, frozen, extra, methods) {
return methods.mapThaw('Dependency', null, arg, extra);
},
depsArray(arg, frozen, extra, methods) {
return methods.mapThaw('Dependency', null, arg, extra);
},
localModule({ idx, name, used }, frozen, extra, methods) {
const state = extra.state;
if (!state.localModules) {
state.localModules = [];
}
if (!state.localModules[idx]) {
state.localModules[idx] = new LocalModule(extra.module, name, idx);
state.localModules[idx].used = used;
}
return state.localModules[idx];
},
regExp(arg, frozen, extra, methods) {
return arg ? new RegExp(arg) : arg;
},
// request: function(arg, dependency, extra, methods) {
// return relateContext.contextNormalRequest(extra.compilation.compiler, arg);
// },
block(arg, frozen, extra, methods) {
// Not having a block, means it needs to create a cycle and refer to its
// parent.
if (!arg) {
return extra.parent;
}
return methods.thaw('DependencyBlock', null, arg, extra);
},
importDependency(arg, frozen, extra, methods) {
return methods.thaw('Dependency', null, arg, extra);
},
originModule(arg, frozen, extra, methods) {
return extra.module;
},
activeExports(arg, { name }, { state }, methods) {
state.activeExports = state.activeExports || new Set();
if (name) {
state.activeExports.add(name);
}
return state.activeExports;
},
otherStarExports(arg, frozen, { state }, methods) {
if (arg === 'star') {
return state.otherStarExports || [];
}
return null;
},
options(arg, frozen, extra, methods) {
if (arg.regExp) {
return Object.assign({}, arg, {
regExp: new RegExp(arg.regExp),
});
}
return arg;
},
parserScope(arg, frozen, { state }, methods) {
state.harmonyParserScope = state.harmonyParserScope || {};
return state.harmonyParserScope;
},
};
function freezeDependency(dependency, extra, methods) {
const schemas = extra.schemas;
for (let i = 0; i < schemas.length; i++) {
if (dependency.constructor === schemas[i].Dependency) {
const frozen = {
type: schemas[i][0],
};
for (let j = 1; j < schemas[i].length; j++) {
let arg = dependency[schemas[i][j]];
if (freezeArgument[schemas[i][j]]) {
arg = freezeArgument[schemas[i][j]](arg, dependency, extra, methods);
}
frozen[schemas[i][j]] = arg;
}
return frozen;
}
}
}
function thawDependency(frozen, extra, methods) {
const schemas = extra.schemas;
schemas.map = schemas.map || {};
if (schemas.map[frozen.type]) {
const depSchema = schemas.map[frozen.type];
const Dependency = depSchema.Dependency;
try {
return new Dependency(...depSchema.args(frozen, extra, methods));
} catch (_) {
return new (Function.prototype.bind.apply(
Dependency,
[null].concat(depSchema.args(frozen, extra, methods)),
))();
}
}
for (const depSchema of schemas) {
if (frozen.type === depSchema[0]) {
schemas.map[frozen.type] = depSchema;
const Dependency = depSchema.Dependency;
const lines = [];
for (let j = 1; j < depSchema.length; j++) {
const argName = depSchema[j];
if (thawArgument[argName]) {
lines.push(
` thawArgument.${argName}(frozen.${argName}, frozen, extra, methods)`,
);
} else {
lines.push(` frozen.${argName}`);
}
}
depSchema.args = new Function(
'thawArgument',
`
return function(frozen, extra, methods) {
return [
${lines.join(',\n')}
];
};
`,
)(thawArgument);
try {
return new Dependency(...depSchema.args(frozen, extra, methods));
} catch (_) {
return new (Function.prototype.bind.apply(
Dependency,
[null].concat(depSchema.args(frozen, extra, methods)),
))();
}
}
}
}
class TransformBasicDependencyPluginLegacy {
constructor(options) {
this.options = options;
}
apply(compiler) {
let schemas = DependencySchemas3;
if (this.options.schema < 3) {
schemas = DependencySchemas2;
}
pluginCompat.tap(
compiler,
'afterPlugins',
'TransformBasicDependencyPlugin scan Dependency types',
() => {
pluginCompat.tap(
compiler,
'compilation',
'TransformBasicDependencyPlugin scan Dependencies types',
({ dependencyFactories }) => {
const Dependencies = dependencyFactories.keys();
for (const Dep of Dependencies) {
for (let i = 0; i < schemas.length; i++) {
if (Dep.name === schemas[i][0]) {
schemas[i].Dependency = Dep;
}
}
}
for (let i = 0; i < schemas.length; i++) {
if (!schemas[i].Dependency) {
if (this.options.schema < 4) {
} else {
if (schemas[i][0] === 'JsonExportsDependency') {
try {
schemas[
i
].Dependency = require('webpack/lib/dependencies/JsonExportsDependency');
} catch (_) {}
} else if (schemas[i][0] === 'DelegatedExportsDependency') {
try {
schemas[
i
].Dependency = require('webpack/lib/dependencies/DelegatedExportsDependency');
} catch (_) {}
} else if (schemas[i][0] === 'DelegatedSourceDependency') {
try {
schemas[
i
].Dependency = require('webpack/lib/dependencies/DelegatedSourceDependency');
} catch (_) {}
}
}
}
}
},
);
},
);
let methods;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'TransformBasicDependencyPlugin methods',
_methods => {
methods = _methods;
},
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeDependency',
'TransformBasicDependencyPlugin freeze',
(frozen, dependency, extra) => {
extra.schemas = schemas;
const _frozen = freezeDependency(dependency, extra, methods);
if (_frozen) {
if (dependency.prepend) {
_frozen.prepend = dependency.prepend;
}
if (dependency.replaces) {
_frozen.replaces = dependency.replaces;
}
if (dependency.critical) {
_frozen.critical = dependency.critical;
}
if (typeof dependency.namespaceObjectAsContext !== 'undefined') {
_frozen.namespaceObjectAsContext =
dependency.namespaceObjectAsContext;
}
if (typeof dependency.callArgs !== 'undefined') {
_frozen.callArgs = dependency.callArgs;
}
if (typeof dependency.call !== 'undefined') {
_frozen.call = dependency.call;
}
if (typeof dependency.directImport !== 'undefined') {
_frozen.directImport = dependency.directImport;
}
if (typeof dependency.shorthand !== 'undefined') {
_frozen.shorthand = dependency.shorthand;
}
if (
typeof dependency.localModule === 'object' &&
dependency.localModule !== null
) {
_frozen.localModule = {
name: dependency.localModule.name,
idx: dependency.localModule.idx,
used: dependency.localModule.used,
};
}
return _frozen;
}
return frozen;
},
);
pluginCompat.tap(
compiler,
'_hardSourceAfterFreezeDependency',
'TransformBasicDependencyPlugin after freeze',
(frozen, dependency, extra) => {
if (frozen && dependency.loc) {
frozen.loc = flattenPrototype(dependency.loc);
}
if (frozen && dependency.optional) {
frozen.optional = dependency.optional;
}
if (frozen && dependency.getWarnings) {
const warnings = dependency.getWarnings();
if (warnings && warnings.length) {
frozen.warnings = warnings.map(({ stack }) =>
stack.includes('\n at pluginCompat.tap')
? stack.split('\n at pluginCompat.tap')[0]
: stack.split('\n at Compiler.pluginCompat.tap')[0],
);
}
}
return frozen;
},
);
pluginCompat.tap(
compiler,
'_hardSourceThawDependency',
'TransformBasicDependencyPlugin',
(dependency, frozen, extra) => {
extra.schemas = schemas;
const _thawed = thawDependency(frozen, extra, methods);
if (_thawed) {
const state = extra.state;
// console.log('Thawed', frozen.type);
if (frozen.prepend) {
_thawed.prepend = frozen.prepend;
}
if (frozen.replaces) {
_thawed.replaces = frozen.replaced;
}
if (frozen.critical) {
_thawed.critical = frozen.critical;
}
if (typeof frozen.namespaceObjectAsContext !== 'undefined') {
_thawed.namespaceObjectAsContext = frozen.namespaceObjectAsContext;
}
if (typeof frozen.callArgs !== 'undefined') {
_thawed.callArgs = frozen.callArgs;
}
if (typeof frozen.call !== 'undefined') {
_thawed.call = frozen.call;
}
if (typeof frozen.directImport !== 'undefined') {
_thawed.directImport = frozen.directImport;
}
if (typeof frozen.shorthand !== 'undefined') {
_thawed.shorthand = frozen.shorthand;
}
if (
typeof frozen.localModule === 'object' &&
frozen.localModule !== null
) {
if (!state.localModules) {
state.localModules = [];
}
if (!state.localModules[frozen.localModule.idx]) {
state.localModules[frozen.localModule.idx] = new LocalModule(
extra.module,
frozen.localModule.name,
frozen.localModule.idx,
);
state.localModules[frozen.localModule.idx].used =
frozen.localModule.used;
}
_thawed.localModule = state.localModules[frozen.localModule.idx];
}
if (frozen.type === 'HarmonyImportDependency') {
const ref = frozen.range.toString();
if (state.imports[ref]) {
return state.imports[ref];
}
state.imports[ref] = _thawed;
} else if (
frozen.type === 'HarmonyExportImportedSpecifierDependency'
) {
if (_thawed.otherStarExports) {
extra.state.otherStarExports = (
extra.state.otherStarExports || []
).concat(_thawed);
}
}
return _thawed;
}
return dependency;
},
);
pluginCompat.tap(
compiler,
'_hardSourceAfterThawDependency',
'TransformBasicDependencyPlugin',
(dependency, { loc, optional, warnings }, extra) => {
if (dependency && loc) {
dependency.loc = loc;
}
if (dependency && optional) {
dependency.optional = true;
}
if (dependency && warnings && dependency.getWarnings) {
const frozenWarnings = warnings;
const _getWarnings = dependency.getWarnings;
dependency.getWarnings = function() {
const warnings = _getWarnings.call(this);
if (warnings && warnings.length) {
return warnings.map((warning, i) => {
const stack = warning.stack.split(
'\n at Compilation.reportDependencyErrorsAndWarnings',
)[1];
warning.stack = `${
frozenWarnings[i]
}\n at Compilation.reportDependencyErrorsAndWarnings${stack}`;
return warning;
});
}
return warnings;
};
}
return dependency;
},
);
}
}
module.exports = TransformBasicDependencyPluginLegacy;
@@ -0,0 +1,47 @@
const cachePrefix = require('./util').cachePrefix;
const logMessages = require('./util/log-messages');
const pluginCompat = require('./util/plugin-compat');
class TransformCompilationPlugin {
apply(compiler) {
let store;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'TransformCompilationPlugin copy methods',
methods => {
store = methods.store;
// fetch = methods.fetch;
// freeze = methods.freeze;
// thaw = methods.thaw;
},
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeCompilation',
'TransformCompilationPlugin freeze',
(_, compilation) => {
compilation.modules.forEach(module => {
const identifierPrefix = cachePrefix(compilation);
if (identifierPrefix === null) {
return;
}
const identifier = identifierPrefix + module.identifier();
try {
store('Module', identifier, module, {
id: identifier,
compilation,
});
} catch (e) {
logMessages.moduleFreezeError(compilation, module, e);
}
});
},
);
}
}
module.exports = TransformCompilationPlugin;
@@ -0,0 +1,107 @@
const NormalModule = require('webpack/lib/NormalModule');
const cachePrefix = require('./util').cachePrefix;
const pluginCompat = require('./util/plugin-compat');
function wrapSource(source, methods) {
Object.keys(methods).forEach(key => {
const _method = source[key];
source[key] = function(...args) {
methods[key].apply(this, args);
_method && _method.apply(this, args);
};
});
return source;
}
function spyMethod(name, mods) {
return function(...args) {
mods.push([name].concat([].slice.call(args)));
};
}
function isEqual(a, b) {
if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {
return a.reduce(
(carry, value, index) => carry && isEqual(value, b[index]),
true,
);
} else if (a === b) {
return true;
}
return false;
}
class HardModuleConcatenationPlugin {
apply(compiler) {
let store;
let freeze;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'HardModuleConcatenationPlugin',
methods => {
store = methods.store;
// fetch = methods.fetch;
freeze = methods.freeze;
// thaw = methods.thaw;
// mapFreeze = methods.mapFreeze;
// mapThaw = methods.mapThaw;
},
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeModule',
'HardModuleConcatenationPlugin',
(frozen, { modules }, extra) => {
if (modules) {
const compilation = extra.compilation;
modules.forEach(module => {
if (
(module.cacheable ||
(module.buildInfo && module.buildInfo.cacheable)) &&
module instanceof NormalModule
) {
const identifierPrefix = cachePrefix(compilation);
if (identifierPrefix === null) {
return;
}
const identifier = identifierPrefix + module.identifier();
store('Module', identifier, module, {
id: identifier,
compilation,
});
}
});
}
return frozen;
},
);
pluginCompat.tap(
compiler,
'_hardSourceAfterFreezeModule',
'HardModuleConcatenationPlugin',
(frozen, module, { compilation }) => {
return frozen;
if (frozen && module.__hardSource_concatedSource) {
const source = module.__hardSource_concatedSource;
frozen.source = source.source();
frozen.sourceMap = freeze('SourceMap', null, source, {
module,
compilation: compilation,
});
frozen.concatenatedSourceMods = module.__hardSource_sourceMods;
}
return frozen;
},
);
}
}
module.exports = HardModuleConcatenationPlugin;
@@ -0,0 +1,389 @@
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;
+169
View File
@@ -0,0 +1,169 @@
const relateContext = require('./util/relate-context');
const pluginCompat = require('./util/plugin-compat');
const GeneratorSchemas4 = [
['ByTypeGenerator', 'map'],
['JavascriptGenerator'],
['JsonGenerator'],
['WebAssemblyGenerator'],
['WebAssemblyJavascriptGenerator'],
];
try {
try {
GeneratorSchemas4[0].Generator = require('webpack/lib/Generator').byType(
{},
).constructor;
} catch (_) {}
GeneratorSchemas4[1].Generator = require('webpack/lib/JavascriptGenerator');
GeneratorSchemas4[2].Generator = require('webpack/lib/JsonGenerator');
try {
GeneratorSchemas4[3].Generator = require('webpack/lib/WebAssemblyGenerator');
} catch (_) {
GeneratorSchemas4[3].Generator = require('webpack/lib/wasm/WebAssemblyGenerator');
}
try {
GeneratorSchemas4[4].Generator = require('webpack/lib/wasm/WebAssemblyJavascriptGenerator');
} catch (_) {}
} catch (_) {}
const freezeArgument = {
map(arg, generator, extra, methods) {
const map = {};
for (const key in arg) {
map[key] = methods.freeze('Generator', null, arg[key], extra);
}
return map;
},
};
const thawArgument = {
map(arg, generator, extra, methods) {
const map = {};
for (const key in arg) {
map[key] = methods.thaw('Generator', null, arg[key], extra);
}
return map;
},
};
function freezeGenerator(generator, extra, methods) {
const schemas = extra.schemas;
for (let i = 0; i < schemas.length; i++) {
if (generator.constructor === schemas[i].Generator) {
const frozen = {
type: schemas[i][0],
};
for (let j = 1; j < schemas[i].length; j++) {
let arg = generator[schemas[i][j]];
if (freezeArgument[schemas[i][j]]) {
arg = freezeArgument[schemas[i][j]](arg, generator, extra, methods);
}
frozen[schemas[i][j]] = arg;
}
return frozen;
}
}
}
function thawGenerator(frozen, extra, methods) {
const schemas = extra.schemas;
for (let i = 0; i < schemas.length; i++) {
if (frozen.type === schemas[i][0]) {
const Generator = schemas[i].Generator;
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 Generator(...args);
} catch (_) {
return new (Function.prototype.bind.apply(
Generator,
[null].concat(args),
))();
}
}
}
}
class TransformGeneratorPlugin {
apply(compiler) {
pluginCompat.register(
compiler,
'_hardSourceBeforeFreezeGenerator',
'syncWaterfall',
['frozen', 'item', 'extra'],
);
pluginCompat.register(
compiler,
'_hardSourceFreezeGenerator',
'syncWaterfall',
['frozen', 'item', 'extra'],
);
pluginCompat.register(
compiler,
'_hardSourceAfterFreezeGenerator',
'syncWaterfall',
['frozen', 'item', 'extra'],
);
pluginCompat.register(
compiler,
'_hardSourceBeforeThawGenerator',
'syncWaterfall',
['item', 'frozen', 'extra'],
);
pluginCompat.register(
compiler,
'_hardSourceThawGenerator',
'syncWaterfall',
['item', 'frozen', 'extra'],
);
pluginCompat.register(
compiler,
'_hardSourceAfterThawGenerator',
'syncWaterfall',
['item', 'frozen', 'extra'],
);
let methods;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'TransformGeneratorPlugin',
_methods => {
methods = _methods;
},
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeGenerator',
'TransformGeneratorPlugin freeze',
(frozen, generator, extra) => {
extra.schemas = GeneratorSchemas4;
frozen = freezeGenerator(generator, extra, methods);
frozen.moduleType = extra.module.type;
frozen.options = {};
return frozen;
},
);
pluginCompat.tap(
compiler,
'_hardSourceThawGenerator',
'TransformGeneratorPlugin thaw',
(generator, { moduleType, options }, { normalModuleFactory }) => {
return normalModuleFactory.getGenerator(moduleType, options);
},
);
}
}
module.exports = TransformGeneratorPlugin;
@@ -0,0 +1,56 @@
const pluginCompat = require('./util/plugin-compat');
class TransformModuleAssetsPlugin {
apply(compiler) {
let store;
let fetch;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'TransformModuleAssetsPlugin copy methods',
methods => {
store = methods.store;
fetch = methods.fetch;
// freeze = methods.freeze;
// thaw = methods.thaw;
// mapFreeze = methods.mapFreeze;
// mapThaw = methods.mapThaw;
},
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeModuleAssets',
'TransformModuleAssetsPlugin freeze',
(frozen, assets, extra) => {
if (!frozen && assets) {
Object.keys(assets).forEach(key => {
store('Asset', key, assets[key], extra);
});
frozen = Object.keys(assets);
}
return frozen;
},
);
pluginCompat.tap(
compiler,
'_hardSourceThawModuleAssets',
'TransformModuleAssetsPlugin thaw',
(assets, frozen, extra) => {
if (!assets && frozen) {
assets = {};
frozen.forEach(key => {
assets[key] = fetch('Asset', key, extra);
});
}
return assets;
},
);
}
}
module.exports = TransformModuleAssetsPlugin;
@@ -0,0 +1,109 @@
const WebpackError = require('webpack/lib/WebpackError');
const ModuleError = require('webpack/lib/ModuleError');
const ModuleWarning = require('webpack/lib/ModuleWarning');
const pluginCompat = require('./util/plugin-compat');
class TransformModuleErrorsPlugin {
apply(compiler) {
let freeze;
let thaw;
let mapFreeze;
let mapThaw;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'TransformModuleErrorsPlugin',
methods => {
// store = methods.store;
// fetch = methods.fetch;
freeze = methods.freeze;
thaw = methods.thaw;
mapFreeze = methods.mapFreeze;
mapThaw = methods.mapThaw;
},
);
function freezeErrorWarning(
frozen,
{ message, details, originLoc, dependencies, name, loc, constructor },
extra,
) {
return {
constructor: constructor.name,
message: message,
details: details,
originLoc: originLoc,
dependencies:
dependencies && mapFreeze('Dependency', null, dependencies, extra),
name: name,
loc: loc,
};
}
pluginCompat.tap(
compiler,
'_hardSourceFreezeModuleError',
'TransformModuleErrorsPlugin',
freezeErrorWarning,
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeModuleWarning',
'TransformModuleErrorsPlugin',
freezeErrorWarning,
);
function thawError(
ErrorClass,
error,
{ constructor, message, details, originLoc, dependencies, name, loc },
extra,
) {
const module = extra.module;
error = new ErrorClass(module, message);
if (constructor === 'SystemImportDeprecationWarning') {
error = new WebpackError(message);
error.module = module;
}
if (details) {
error.details = details;
}
if (extra.origin) {
error.origin = extra.origin;
}
if (originLoc) {
error.originLoc = originLoc;
}
if (dependencies) {
error.dependencies = mapThaw('Dependency', null, dependencies, extra);
}
if (name) {
error.name = name;
}
if (loc) {
error.loc = loc;
}
return error;
}
pluginCompat.tap(
compiler,
'_hardSourceThawModuleError',
'TransformModuleErrorsPlugin',
(error, frozen, extra) => thawError(ModuleError, error, frozen, extra),
);
pluginCompat.tap(
compiler,
'_hardSourceThawModuleWarning',
'TransformModuleErrorsPlugin',
(warning, frozen, extra) =>
thawError(ModuleWarning, warning, frozen, extra),
);
}
}
module.exports = TransformModuleErrorsPlugin;
@@ -0,0 +1,229 @@
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;
@@ -0,0 +1,706 @@
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;
+162
View File
@@ -0,0 +1,162 @@
const relateContext = require('./util/relate-context');
const pluginCompat = require('./util/plugin-compat');
const ParserSchemas3 = [['Parser', 'options']];
const ParserSchemas4 = [
['JsonParser', 'options'],
['Parser', 'options', 'sourceType'],
['WebAssemblyParser', 'options'],
];
try {
ParserSchemas3[0].Parser = require('webpack/lib/Parser');
} catch (_) {}
try {
ParserSchemas4[0].Parser = require('webpack/lib/JsonParser');
ParserSchemas4[1].Parser = require('webpack/lib/Parser');
try {
ParserSchemas4[2].Parser = require('webpack/lib/WebAssemblyParser');
} catch (_) {
ParserSchemas4[2].Parser = require('webpack/lib/wasm/WebAssemblyParser');
}
} catch (_) {}
const freezeArgument = {};
const thawArgument = {};
function freezeParser(parser, extra, methods) {
const schemas = extra.schemas;
for (let i = 0; i < schemas.length; i++) {
if (parser.constructor === schemas[i].Parser) {
const frozen = {
type: schemas[i][0],
};
for (let j = 1; j < schemas[i].length; j++) {
let arg = parser[schemas[i][j]];
if (freezeArgument[schemas[i][j]]) {
arg = freezeArgument[schemas[i][j]](arg, parser, extra, methods);
}
frozen[schemas[i][j]] = arg;
}
return frozen;
}
}
}
function thawParser(frozen, extra, methods) {
const schemas = extra.schemas;
for (let i = 0; i < schemas.length; i++) {
if (frozen.type === schemas[i][0]) {
const Parser = schemas[i].Parser;
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 Parser(...args);
} catch (_) {
return new (Function.prototype.bind.apply(
Parser,
[null].concat(args),
))();
}
}
}
}
class TransformParserPlugin {
constructor(options) {
this.options = options || {};
}
apply(compiler) {
const schema = this.options.schema;
let schemas = ParserSchemas4;
if (schema < 4) {
schemas = ParserSchemas3;
}
pluginCompat.register(
compiler,
'_hardSourceBeforeFreezeParser',
'syncWaterfall',
['frozen', 'item', 'extra'],
);
pluginCompat.register(
compiler,
'_hardSourceFreezeParser',
'syncWaterfall',
['frozen', 'item', 'extra'],
);
pluginCompat.register(
compiler,
'_hardSourceAfterFreezeParser',
'syncWaterfall',
['frozen', 'item', 'extra'],
);
pluginCompat.register(
compiler,
'_hardSourceBeforeThawParser',
'syncWaterfall',
['item', 'frozen', 'extra'],
);
pluginCompat.register(compiler, '_hardSourceThawParser', 'syncWaterfall', [
'item',
'frozen',
'extra',
]);
pluginCompat.register(
compiler,
'_hardSourceAfterThawParser',
'syncWaterfall',
['item', 'frozen', 'extra'],
);
let methods;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'TransformParserPlugin',
_methods => {
methods = _methods;
},
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeParser',
'TransformParserPlugin freeze',
(frozen, parser, extra) => {
extra.schemas = schemas;
frozen = freezeParser(parser, extra, methods);
if (schema === 4) {
frozen.moduleType = extra.module.type;
}
return frozen;
},
);
pluginCompat.tap(
compiler,
'_hardSourceThawParser',
'TransformParserPlugin thaw',
(parser, { options, moduleType }, { normalModuleFactory }) => {
if (schema < 4) {
return normalModuleFactory.getParser(options);
} else {
return normalModuleFactory.getParser(moduleType, options);
}
},
);
}
}
module.exports = TransformParserPlugin;
+225
View File
@@ -0,0 +1,225 @@
const relateContext = require('./util/relate-context');
const pluginCompat = require('./util/plugin-compat');
const SourceSchemas3 = [
['CachedSource', 'source'],
['ConcatSource'],
['LineToLineMappedSource', 'value', 'name', 'originalSource'],
['OriginalSource', 'value', 'name'],
['RawSource', 'value'],
['ReplaceSource', 'source', 'name'],
[
'SourceMapSource',
'value',
'name',
'sourceMap',
'originalSource',
'innerSourceMap',
],
];
try {
SourceSchemas3[0].Source = require('webpack-sources/lib/CachedSource');
} catch (_) {}
try {
SourceSchemas3[1].Source = require('webpack-sources/lib/ConcatSource');
} catch (_) {}
try {
SourceSchemas3[2].Source = require('webpack-sources/lib/LineToLineMappedSource');
} catch (_) {}
try {
SourceSchemas3[3].Source = require('webpack-sources/lib/OriginalSource');
} catch (_) {}
try {
SourceSchemas3[4].Source = require('webpack-sources/lib/RawSource');
} catch (_) {}
try {
SourceSchemas3[5].Source = require('webpack-sources/lib/ReplaceSource');
} catch (_) {}
try {
SourceSchemas3[6].Source = require('webpack-sources/lib/SourceMapSource');
} catch (_) {}
const freezeArgument = {
value(arg, { _value }, extra, methods) {
return _value;
},
name(arg, { _name }, { compilation }, methods) {
try {
return relateContext.relateNormalPath(compilation.compiler, _name);
} catch (e) {
console.error(e.stack);
process.exit();
}
},
sourceMap(arg, { _sourceMap }, extra, methods) {
return _sourceMap;
},
originalSource(arg, { _originalSource }, extra, methods) {
return _originalSource;
},
innerSourceMap(arg, { _innerSourceMap }, extra, methods) {
return _innerSourceMap;
},
source(arg, { constructor, _source }, extra, methods) {
if (constructor.name === 'ReplaceSource') {
return;
}
return methods.freeze('Source', null, _source, extra);
},
};
const thawArgument = {
name(arg, source, { compilation }, methods) {
try {
return relateContext.contextNormalPath(compilation.compiler, arg);
} catch (e) {
console.error(e.stack);
process.exit();
}
},
source(arg, { type }, extra, methods) {
if (type === 'ReplaceSource') {
return extra.source;
}
return methods.thaw('Source', null, arg, extra);
},
value(arg, frozen, extra, methods) {
if (arg && arg.type === 'Buffer') {
return new Buffer(arg.data);
}
return arg;
},
};
function freezeSource(source, extra, methods) {
const schemas = extra.schemas;
for (let i = 0; i < schemas.length; i++) {
if (source.constructor.name === schemas[i].Source.name) {
const frozen = {
type: schemas[i][0],
};
for (let j = 1; j < schemas[i].length; j++) {
let arg = source[schemas[i][j]];
if (freezeArgument[schemas[i][j]]) {
arg = freezeArgument[schemas[i][j]](arg, source, extra, methods);
}
frozen[schemas[i][j]] = arg;
}
return frozen;
}
}
throw new Error(`Unfrozen ${source.constructor.name}.`);
}
function thawSource(frozen, extra, methods) {
const schemas = extra.schemas;
for (let i = 0; i < schemas.length; i++) {
if (frozen.type === schemas[i][0]) {
const Source = schemas[i].Source;
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 Source(...args);
} catch (_) {
return new (Function.prototype.bind.apply(
Source,
[null].concat(args),
))();
}
}
}
}
class TransformSourcePlugin {
apply(compiler) {
let methods;
pluginCompat.tap(
compiler,
'_hardSourceMethods',
'TransformSourcePlugin',
_methods => {
methods = _methods;
},
);
pluginCompat.tap(
compiler,
'_hardSourceFreezeSource',
'TransformSourcePlugin freeze',
(frozen, source, extra) => {
if (typeof source === 'string') {
return {
type: 'String',
value: source,
};
} else if (Buffer.isBuffer && Buffer.isBuffer(source)) {
// Serialization layer might transform it into JSON or handle it as binary
// data
return source;
}
extra.schemas = SourceSchemas3;
frozen = freezeSource(source, extra, methods);
if (frozen.type === 'ReplaceSource') {
frozen.replacements = source.replacements;
} else if (frozen.type === 'CachedSource') {
frozen.cachedSource = source._cachedSource;
frozen.cachedSize = source._cachedSize;
frozen.cachedMaps = source._cachedMaps;
} else if (frozen.type === 'ConcatSource') {
frozen.children = methods.mapFreeze(
'Source',
null,
source.children,
extra,
);
}
return frozen;
},
);
pluginCompat.tap(
compiler,
'_hardSourceThawSource',
'TransformSourcePlugin thaw',
(source, frozen, extra) => {
if (frozen.type === 'String') {
return frozen.value;
} else if (frozen.type === 'Buffer') {
return new Buffer(frozen.data);
} else if (Buffer.isBuffer && Buffer.isBuffer(frozen)) {
return frozen;
}
extra.schemas = SourceSchemas3;
source = thawSource(frozen, extra, methods);
if (frozen.type === 'ReplaceSource') {
source.replacements = frozen.replacements;
} else if (frozen.type === 'CachedSource') {
source._cachedSource = frozen.cachedSource;
source._cachedSize = frozen.cachedSize;
source._cachedMaps = frozen.cachedMaps;
} else if (frozen.type === 'ConcatSource') {
source.children = methods.mapThaw(
'Source',
null,
frozen.children,
extra,
);
}
return source;
},
);
}
}
module.exports = TransformSourcePlugin;
+30
View File
@@ -0,0 +1,30 @@
const crypto = require('crypto');
const path = require('path');
const nodeObjectHash = require('node-object-hash');
const sort = nodeObjectHash({
sort: false,
}).sort;
function relateContextToCacheDir(config) {
const hardSourcePlugin = config.plugins.find(
({ constructor }) => constructor.name === 'HardSourceWebpackPlugin',
);
const cacheDir = hardSourcePlugin.getCachePath();
const context = path.resolve(process.cwd(), config.context);
const clone = Object.assign({}, config, {
context: path.relative(cacheDir, context),
});
const sorted = sort(clone)
.replace(new RegExp(`${context}[^,\\]}]*`, 'g'), match =>
path.relative(cacheDir, match),
)
.replace(/\\/g, '/');
return crypto
.createHash('sha256')
.update(sorted)
.digest('hex');
}
module.exports = relateContextToCacheDir;
+149
View File
@@ -0,0 +1,149 @@
// - scan dirs
// - stat items
// - hash files
// - stat dir items under
// - hash files
// - hash files
const crypto = require('crypto');
const fs = require('graceful-fs');
const path = require('path');
const pkgDir = require('pkg-dir');
const promisify = require('./util/promisify');
const readFile = promisify(fs.readFile);
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
function hashFile(file) {
return readFile(file)
.then(src => [
file,
crypto
.createHash('md5')
.update(src)
.digest('hex'),
])
.catch(() => {});
}
function hashObject(obj) {
const hash = crypto.createHash('md5');
obj.forEach(item => {
hash.update(item[0]);
hash.update(item[1]);
});
return hash.digest('hex');
}
function hashFiles(root, files) {
return Promise.all(files.map(file => hashFile(path.join(root, file)))).then(
hashes => hashes.filter(Boolean),
);
}
function flatten(items) {
return (items || []).reduce(
(carry, item) => (item ? carry.concat(item) : carry),
[],
);
}
const inputs = async ({ files, directories, root = process.cwd() } = {}) => {
let defaults;
if (!files && !directories) {
const lockFiles = (await Promise.all(
['package-lock.json', 'yarn.lock'].map(f =>
stat(path.join(root, f)).then(() => f, () => null),
),
)).filter(Boolean);
if (lockFiles.length) {
return lockFiles;
}
}
if (!files) {
files = ['package.json'];
}
if (!directories) {
directories = ['node_modules'];
}
directories = directories.map(d => `${d}/*`);
return flatten([files, directories]);
};
module.exports = options => {
options = options || {};
const root = options.root || pkgDir.sync(process.cwd());
let files = options.files;
let directories = options.directories;
let hashDefaults = Promise.resolve();
if (!files && !directories) {
hashDefaults = hashFiles(root, ['package-lock.json', 'yarn.lock']);
}
return hashDefaults
.then(_defaults => {
if (_defaults && _defaults.length > 0) {
return [_defaults];
} else {
if (!files) {
files = ['package.json'];
}
if (!directories) {
directories = ['node_modules'];
}
}
return Promise.all([
hashFiles(root, files),
Promise.all(
directories.map(dir =>
readdir(path.join(root, dir))
.then(items =>
Promise.all(
items.map(item =>
stat(path.join(root, dir, item))
.then(stat => {
if (stat.isDirectory()) {
return hashFiles(path.join(root, dir, item), files);
}
if (stat.isFile()) {
return hashFile(path.join(root, dir, item)).then(
hash => (hash ? [hash] : hash),
);
}
})
.catch(function(...args) {
console.error(args);
}),
),
).then(hashes => hashes.filter(Boolean)),
)
.catch(() => {})
.then(flatten),
),
).then(flatten),
]);
})
.then(flatten)
.then(items => {
items.forEach(item => {
item[0] = path.relative(root, item[0]);
});
// console.log(items);
items.sort((a, b) => {
if (a[0] < b[0]) {
return -1;
} else if (a[0] > b[0]) {
return 1;
}
return 0;
});
return hashObject(items);
});
};
module.exports.inputs = inputs;
+219
View File
@@ -0,0 +1,219 @@
/**
* The LoggerFactory wraps a hard source plugin exposed on the webpack Compiler.
*
* The plugin handle, `'hard-source-log'` takes one object as input and should
* log it to the console, disk, or somewhere. Or not if it the message should
* be ignored. The object has a few arguments that generally follows this
* structure. The `data` key will generally have an `id` value.
*
* ```js
* {
* from: 'core',
* level: 'error',
* message: 'HardSourceWebpackPlugin requires a cacheDirectory setting.',
* data: {
* id: 'need-cache-directory-option'
* }
* }
* ```
*
* So a simple plugin handle may be
*
* ```js
* compiler.plugin('hard-source-log', function(message) {
* console[message.level].call(
* console,
* 'hard-source:' + message.from, message.message
* );
* });
* ```
*
* @module hard-source-webpack-plugin/logger-factory
* @author Michael "Z" Goddard <mzgoddard@gmail.com>
*/
const pluginCompat = require('./util/plugin-compat');
const LOGGER_SEPARATOR = ':';
const DEFAULT_LOGGER_PREFIX = 'hard-source';
const LOGGER_FACTORY_COMPILER_KEY = `${__dirname}/hard-source-logger-factory-compiler-key`;
/**
* @constructor Logger
* @memberof module:hard-source-webpack-plugin/logger-factory
*/
class Logger {
constructor(compiler) {
this.compiler = compiler;
this._lock = null;
}
/**
* @method lock
* @memberof module:hard-source-webpack-plugin/logger-factory~Logger#
*/
lock() {
this._lock = [];
}
/**
* @method unlock
* @memberof module:hard-source-webpack-plugin/logger-factory~Logger#
*/
unlock() {
const _this = this;
if (_this._lock) {
const lock = _this._lock;
_this._lock = null;
lock.forEach(value => {
_this.write(value);
});
}
}
/**
* @method write
* @memberof module:hard-source-webpack-plugin/logger-factory~Logger#
*/
write(value) {
if (this._lock) {
return this._lock.push(value);
}
if (this.compiler.hooks && this.compiler.hooks.hardSourceLog.taps.length) {
this.compiler.hooks.hardSourceLog.call(value);
} else if (
this.compiler._plugins &&
this.compiler._plugins['hard-source-log'] &&
this.compiler._plugins['hard-source-log'].length
) {
(this.compiler.applyPlugins1 || this.compiler.applyPlugins).call(
this.compiler,
'hard-source-log',
value,
);
} else {
console.error(
`[${DEFAULT_LOGGER_PREFIX}${LOGGER_SEPARATOR}${value.from}]`,
value.message,
);
}
}
/**
* @method from
* @memberof module:hard-source-webpack-plugin/logger-factory~Logger#
*/
from(name) {
return new LoggerFrom(this, name);
}
}
/**
* @constructor LoggerFrom
* @memberof module:hard-source-webpack-plugin/logger-factory
*/
class LoggerFrom {
constructor(logger, from) {
this._logger = logger;
this._from = from;
}
/**
* @method from
* @memberof module:hard-source-webpack-plugin/logger-factory~LoggerFrom#
*/
from(name) {
return new LoggerFrom(this._logger, this._from + LOGGER_SEPARATOR + name);
}
/**
* @method _write
* @memberof module:hard-source-webpack-plugin/logger-factory~LoggerFrom#
*/
_write(level, data, message) {
this._logger.write({
from: this._from,
level,
message,
data,
});
}
/**
* @method error
* @memberof module:hard-source-webpack-plugin/logger-factory~LoggerFrom#
*/
error(data, message) {
this._write('error', data, message);
}
/**
* @method warn
* @memberof module:hard-source-webpack-plugin/logger-factory~LoggerFrom#
*/
warn(data, message) {
this._write('warn', data, message);
}
/**
* @method info
* @memberof module:hard-source-webpack-plugin/logger-factory~LoggerFrom#
*/
info(data, message) {
this._write('info', data, message);
}
/**
* @method log
* @memberof module:hard-source-webpack-plugin/logger-factory~LoggerFrom#
*/
log(data, message) {
this._write('log', data, message);
}
/**
* @method debug
* @memberof module:hard-source-webpack-plugin/logger-factory~LoggerFrom#
*/
debug(data, message) {
this._write('debug', data, message);
}
}
/**
* @constructor LoggerFactory
* @memberof module:hard-source-webpack-plugin/logger-factory
*/
class LoggerFactory {
constructor(compiler) {
this.compiler = compiler;
pluginCompat.register(compiler, 'hardSourceLog', 'sync', ['data']);
}
/**
* @method create
* @memberof module:hard-source-webpack-plugin/logger-factory~LoggerFactory#
*/
create() {
const compiler = this.compiler;
if (!compiler[LOGGER_FACTORY_COMPILER_KEY]) {
compiler[LOGGER_FACTORY_COMPILER_KEY] = new Logger(this.compiler);
}
return compiler[LOGGER_FACTORY_COMPILER_KEY];
}
}
/**
* @function getLogger
* @memberof module:hard-source-webpack-plugin/logger-factory~LoggerFactory.
*/
LoggerFactory.getLogger = compilation => {
while (compilation.compiler.parentCompilation) {
compilation = compilation.compiler.parentCompilation;
}
return new LoggerFactory(compilation.compiler).create();
};
module.exports = LoggerFactory;
+561
View File
@@ -0,0 +1,561 @@
/** prelude
const serial = require('../util/serial');
const relateContext = require('../util/relate-context');
const LocalModule = require('webpack/lib/dependencies/LocalModule');
function flattenPrototype(obj) {
if (typeof obj === 'string') {
return obj;
}
const copy = {};
for (const key in obj) {
copy[key] = obj[key];
}
return copy;
}
const assignTruthful = {
freeze(arg, dependency) {
return arg;
},
thaw(arg, frozen) {
return arg;
},
};
const assignDefined = {
freeze(arg, dependency) {
if (typeof arg !== 'undefined') {
return arg;
}
},
thaw(arg, frozen) {
if (typeof arg !== 'undefined') {
return arg;
}
},
}
const optional = serial.assigned({
prepend: assignTruthful,
replaces: assignTruthful,
critical: assignTruthful,
namespaceObjectAsContext: assignDefined,
callArgs: assignDefined,
call: assignDefined,
directImport: assignDefined,
shorthand: assignDefined,
optional: assignTruthful,
loc: {
freeze(arg, dependency) {
return flattenPrototype(dependency.loc);
},
thaw(arg, frozen) {
return arg;
},
},
});
const localModuleAssigned = {
freeze(_, dependency) {
if (
typeof dependency.localModule === 'object' &&
dependency.localModule !== null
) {
return {
name: dependency.localModule.name,
idx: dependency.localModule.idx,
used: dependency.localModule.used,
};
}
},
thaw(thawed, localModule, extra) {
const state = extra.state;
if (
typeof localModule === 'object' &&
localModule !== null
) {
if (!state.localModules) {
state.localModules = [];
}
if (!state.localModules[localModule.idx]) {
state.localModules[localModule.idx] = new LocalModule(
extra.module,
localModule.name,
localModule.idx,
);
state.localModules[localModule.idx].used =
localModule.used;
}
thawed.localModule = state.localModules[localModule.idx];
}
return thawed;
},
};
const warnings = {
freeze(frozen, dependency) {
if (frozen && dependency.getWarnings) {
const warnings = dependency.getWarnings();
if (warnings && warnings.length) {
return warnings.map(
({ stack }) =>
stack.includes('\n at Object.freeze')
? stack.split('\n at Object.freeze')[0]
: stack.includes('\n at pluginCompat.tap')
? stack.split('\n at pluginCompat.tap')[0]
: stack.split('\n at Compiler.pluginCompat.tap')[0],
);
}
}
},
thaw(dependency, warnings) {
if (dependency && warnings && dependency.getWarnings) {
const frozenWarnings = warnings;
const _getWarnings = dependency.getWarnings;
dependency.getWarnings = function() {
const warnings = _getWarnings.call(this);
if (warnings && warnings.length) {
return warnings.map((warning, i) => {
const stack = warning.stack.split(
'\n at Compilation.reportDependencyErrorsAndWarnings',
)[1];
warning.stack = `${
frozenWarnings[i]
}\n at Compilation.reportDependencyErrorsAndWarnings${stack}`;
return warning;
});
}
return warnings;
};
}
return dependency;
},
};
**/
const constructorArguments = {
/** dependencies
dependencies: {
freeze(arg, dependency, extra, methods) {
return methods.mapFreeze('Dependency', null, arg, extra);
},
thaw(arg, frozen, extra, methods) {
return methods.mapThaw('Dependency', null, arg, extra);
},
},
**/
/** freeze dependencies
dependencies: methods.mapFreeze('Dependency', null, dependency.dependencies, extra),
**/
/** thaw dependencies
methods.mapThaw('Dependency', null, frozen.dependencies, extra),
**/
/** depsArray
depsArray: {
freeze(arg, dependency, extra, methods) {
return methods.mapFreeze('Dependency', null, arg, extra);
},
thaw(arg, frozen, extra, methods) {
return methods.mapThaw('Dependency', null, arg, extra);
},
},
**/
/** freeze depsArray
depsArray: methods.mapFreeze('Dependency', null, dependency.depsArray, extra),
**/
/** thaw depsArray
methods.mapThaw('Dependency', null, frozen.depsArray, extra),
**/
/** localModule
localModule: {
freeze({ name, idx }, dependency, extra, methods) {
return {
name: name,
idx: idx,
};
},
thaw({ idx, name, used }, frozen, extra, methods) {
const state = extra.state;
if (!state.localModules) {
state.localModules = [];
}
if (!state.localModules[idx]) {
state.localModules[idx] = new LocalModule(extra.module, name, idx);
state.localModules[idx].used = used;
}
return state.localModules[idx];
},
},
**/
/** freeze localModule
localModule: {
name: dependency.localModule.name,
name: dependency.localModule.idx,
},
**/
/** thaw prep localModule
if (!extra.state.localModules) {
extra.state.localModules = [];
}
if (!extra.state.localModules[frozen.localModule.idx]) {
extra.state.localModules[frozen.localModule.idx] = new LocalModule(extra.module, frozen.localModule.name, frozen.localModule.idx);
extra.state.localModules[frozen.localModule.idx].used = frozen.localModule.used;
}
**/
/** thaw localModule
extra.state.localModules[frozen.localModule.idx],
**/
/** regExp
regExp: {
freeze(arg, dependency, extra, methods) {
return arg ? arg.source : false;
},
thaw(arg, frozen, extra, methods) {
return arg ? new RegExp(arg) : arg;
},
},
**/
/** freeze regExp
regExp: dependency.regExp ? dependency.regExp.source : false,
**/
/** thaw regExp
frozen.regExp ? new RegExp(frozen.regExp) : frozen.regExp,
**/
/** request
request: {
freeze(arg, dependency, extra, methods) {
return relateContext.relateAbsoluteRequest(extra.module.context, arg);
},
thaw(arg, dependency, extra, methods) {
return arg;
// return relateContext.contextNormalRequest(extra.compilation.compiler, arg);
},
},
**/
/** freeze request
request: relateContext.relateAbsoluteRequest(extra.module.context, dependency.request),
**/
/** thaw request
frozen.request,
**/
/** userRequest
userRequest: {
freeze(arg, dependency, extra, methods) {
return relateContext.relateAbsoluteRequest(extra.module.context, arg);
},
thaw(arg, dependency, extra, methods) {
return arg;
// return relateContext.contextNormalRequest(extra.compilation.compiler, arg);
},
},
**/
/** freeze userRequest
userRequest: relateContext.relateAbsoluteRequest(extra.module.context, dependency.userRequest),
**/
/** thaw userRequest
frozen.userRequest,
**/
/** block
block: {
freeze(arg, dependency, extra, methods) {
// Dependency nested in a parent. Freezing the block is a loop.
if (arg.dependencies.includes(dependency)) {
return;
}
return methods.freeze('DependencyBlock', null, arg, extra);
},
thaw(arg, frozen, extra, methods) {
// Not having a block, means it needs to create a cycle and refer to its
// parent.
if (!arg) {
return extra.parent;
}
return methods.thaw('DependencyBlock', null, arg, extra);
},
},
**/
/** freeze block
block: !dependency.block.dependencies.includes(dependency) ?
methods.freeze('DependencyBlock', null, dependency.block, extra) :
undefined,
**/
/** thaw block
!frozen.block ? extra.parent : methods.thaw('DependencyBlock', null, frozen.block, extra),
**/
/** importDependency
importDependency: {
freeze(arg, dependency, extra, methods) {
return methods.freeze('Dependency', null, arg, extra);
},
thaw(arg, frozen, extra, methods) {
return methods.thaw('Dependency', null, arg, extra);
},
},
**/
/** freeze importDependency
importDependency: methods.freeze('Dependency', null, dependency.importDependency, extra),
**/
/** thaw importDependency
methods.thaw('Dependency', null, frozen.importDependency, extra),
**/
/** originModule
originModule: {
freeze(arg, dependency, extra, methods) {
// This will be in extra, generated or found during the process of thawing.
},
thaw(arg, frozen, extra, methods) {
return extra.module;
},
},
**/
/** freeze originModule
originModule: null,
**/
/** thaw originModule
extra.module,
**/
/** activeExports
activeExports: {
freeze(arg, dependency, extra, methods) {
return null;
},
thaw(arg, { name }, { state }, methods) {
state.activeExports = state.activeExports || new Set();
if (name) {
state.activeExports.add(name);
}
return state.activeExports;
},
},
**/
/** freeze activeExports
activeExports: null,
**/
/** thaw prep activeExports
extra.state.activeExports = extra.state.activeExports || new Set();
if (frozen.name) {
extra.state.activeExports.add(frozen.name);
}
**/
/** thaw activeExports
extra.state.activeExports,
**/
/** otherStarExports
otherStarExports: {
freeze(arg, dependency, extra, methods) {
if (arg) {
// This will be in extra, generated during the process of thawing.
return 'star';
}
return null;
},
thaw(arg, frozen, { state }, methods) {
if (arg === 'star') {
return state.otherStarExports || [];
}
return null;
},
},
**/
/** freeze otherStarExports
otherStarExports: dependency.otherStarExports ? 'star' : null,
**/
/** thaw otherStarExports
frozen.otherStarExports === 'star' ?
(extra.state.otherStarExports || []) :
null,
**/
/** options
options: {
freeze(arg, dependency, extra, methods) {
if (arg.regExp) {
return Object.assign({}, arg, {
regExp: arg.regExp.source,
});
}
return arg;
},
thaw(arg, frozen, extra, methods) {
if (arg.regExp) {
return Object.assign({}, arg, {
regExp: new RegExp(arg.regExp),
});
}
return arg;
},
},
**/
/** freeze options
options: dependency.options.regExp ?
Object.assign({}, dependency.options, {
regExp: dependency.options.regExp.source,
}) :
dependency.options,
**/
/** thaw options
frozen.options.regExp ?
Object.assign({}, frozen.options, {
regExp: new RegExp(frozen.options.regExp),
}) :
frozen.options,
**/
/** parserScope
parserScope: {
freeze(arg, dependencies, extra, methods) {
return;
},
thaw(arg, frozen, { state }, methods) {
state.harmonyParserScope = state.harmonyParserScope || {};
return state.harmonyParserScope;
},
},
**/
/** freeze parserScope
parserScope: null,
**/
/** thaw prep parserScope
extra.state.harmonyParserScope = extra.state.harmonyParserScope || {};
**/
/** thaw parserScope
extra.state.harmonyParserScope,
**/
};
/** importDependencyState
importDependency: {
freeze(frozen) {
return frozen;
},
thaw(thawed, frozen, extra) {
const state = extra.state;
const ref = frozen.range.toString();
if (state.imports[ref]) {
return state.imports[ref];
}
state.imports[ref] = thawed;
return thawed;
},
},
**/
/** exportImportedDependencyState
exportImportedDependency: {
freeze(frozen) {},
thaw(thawed, frozen, extra) {
if (thawed.otherStarExports) {
extra.state.otherStarExports = (
extra.state.otherStarExports || []
).concat(thawed);
}
return thawed;
},
},
**/
const fs = require('graceful-fs');
const path = require('path');
const generateRaw = fs.readFileSync(path.join(__dirname, '_generate.js'), 'utf8');
const generateBlocks = generateRaw
.split(/((?:\/\*\*)((?!\*\*\/)[^\r\n]*\r?\n)+)/g)
.filter(Boolean)
.filter(str => !str.startsWith('**/'))
.reduce((carry, item, index) => index % 2 === 0 ? [...carry, item] : carry, []);
const getBlock = name => {
let lines = generateBlocks
.find(block => block.startsWith(`/** ${name}`));
if (lines) {
lines = lines.split('\n');
lines = lines.slice(1, lines.length - 1);
}
return lines || [];
};
const dependencyInfo = require('./basic-dependency.json');
let output = getBlock('prelude');
for (const dependency of dependencyInfo) {
const DepName = dependency[0];
const depName = DepName[0].toLowerCase() + DepName.slice(1);
const DepNameSerial = `${DepName}Serial`;
output.push(`const ${dependency[0]} = require('webpack/lib/dependencies/${dependency[0]}');`);
output.push(`const ${DepNameSerial} = serial.serial('${DepName}', {`);
// output.push(` constructor: serial.constructed(${DepName}, {`);
// for (const argument of dependency.slice(1)) {
// let block = getBlock(argument);
// if (!block.length) {
// block = [` ${argument}: serial.identity,`];
// }
// output.push(...block);
// }
// output.push(` }),`);
output.push(` constructor: {`);
output.push(` freeze(_, dependency, extra, methods) {`);
output.push(` return {`);
for (const argument of dependency.slice(1)) {
let block = getBlock(`freeze ${argument}`);
if (!block.length) {
block = [` ${argument}: dependency.${argument},`];
}
output.push(...block);
}
output.push(` };`);
output.push(` },`);
output.push(` thaw(thawed, frozen, extra, methods) {`);
for (const argument of dependency.slice(1)) {
let block = getBlock(`thaw prep ${argument}`);
output.push(...block);
}
output.push(` return new ${DepName}(`);
for (const argument of dependency.slice(1)) {
let block = getBlock(`thaw ${argument}`);
if (!block.length) {
block = [` frozen.${argument},`];
}
output.push(...block);
}
output.push(` );`);
output.push(` },`);
output.push(` },`);
output.push(``);
output.push(` optional,`);
if (DepName === 'AMDDefineDependency' || DepName === 'LocalModuleDependency') {
output.push(``);
output.push(` localModuleAssigned,`);
}
output.push(``);
output.push(` warnings,`);
if (DepName === 'HarmonyImportDependency') {
output.push(``);
output.push(...getBlock('importDependencyState'));
}
if (DepName === 'HarmonyExportImportedSpecifierDependency') {
output.push(``);
output.push(...getBlock('exportImportedDependencyState'));
}
output.push(`});`);
output.push(``);
}
output.push(`exports.map = new Map();`);
for (const dependency of dependencyInfo) {
const DepName = dependency[0];
const DepNameSerial = `${DepName}Serial`;
output.push(`exports.${DepName} = ${DepNameSerial};`);
output.push(`exports.map.set(${DepName}, ${DepNameSerial});`);
}
output.push(``);
require('graceful-fs').writeFileSync(require('path').join(__dirname, 'index.js'), output.join('\n'), 'utf8');
@@ -0,0 +1,103 @@
[
[
"AMDDefineDependency",
"range",
"arrayRange",
"functionRange",
"objectRange",
"namedModule"
],
["AMDRequireArrayDependency", "depsArray", "range"],
["AMDRequireContextDependency", "options", "range", "valueRange"],
["AMDRequireDependency", "block"],
["AMDRequireItemDependency", "request", "range"],
["CommonJsRequireContextDependency", "options", "range", "valueRange"],
["CommonJsRequireDependency", "request", "range"],
["ConstDependency", "expression", "range", "requireWebpackRequire"],
["ContextDependency", "options"],
["ContextElementDependency", "request", "userRequest"],
["CriticalDependencyWarning", "message"],
["DelegatedExportsDependency", "originModule", "exports"],
["DelegatedSourceDependency", "request"],
["DllEntryDependency", "dependencies", "name"],
["HarmonyAcceptDependency", "range", "dependencies", "hasCallback"],
["HarmonyAcceptImportDependency", "request", "originModule", "parserScope"],
["HarmonyCompatibilityDependency", "originModule"],
[
"HarmonyExportExpressionDependency",
"originModule",
"range",
"rangeStatement",
"prefix"
],
["HarmonyExportHeaderDependency", "range", "rangeStatement"],
[
"HarmonyExportImportedSpecifierDependency",
"request",
"originModule",
"sourceOrder",
"parserScope",
"id",
"name",
"activeExports",
"otherStarExports",
"strictExportPresence"
],
["HarmonyExportSpecifierDependency", "originModule", "id", "name"],
[
"HarmonyImportDependency",
"request",
"originModule",
"sourceOrder",
"parserScope"
],
[
"HarmonyImportSideEffectDependency",
"request",
"originModule",
"sourceOrder",
"parserScope"
],
[
"HarmonyImportSpecifierDependency",
"request",
"originModule",
"sourceOrder",
"parserScope",
"id",
"name",
"range",
"strictExportPresence"
],
["HarmonyInitDependency", "originModule"],
["ImportContextDependency", "options", "range", "valueRange"],
["ImportDependency", "request", "originModule", "block"],
["ImportEagerDependency", "request", "originModule", "range"],
["ImportWeakDependency", "request", "originModule", "range"],
["JsonExportsDependency", "exports"],
["LoaderDependency", "request"],
["LocalModuleDependency", "localModule", "range", "callNew"],
["ModuleDependency", "request"],
["ModuleHotAcceptDependency", "request", "range"],
["ModuleHotDeclineDependency", "request", "range"],
["MultiEntryDependency", "dependencies", "name"],
["NullDependency"],
["PrefetchDependency", "request"],
["RequireContextDependency", "options", "range"],
["RequireEnsureDependency", "block"],
["RequireEnsureItemDependency", "request"],
["RequireHeaderDependency", "range"],
["RequireIncludeDependency", "request", "range"],
["RequireResolveContextDependency", "options", "range", "valueRange"],
["RequireResolveDependency", "request", "range"],
["RequireResolveHeaderDependency", "range"],
["SingleEntryDependency", "request"],
["UnsupportedDependency", "request", "range"],
[
"WebAssemblyImportDependency",
"request",
"name",
"description",
"onlyDirectImport"
]
]
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
module.exports = function Object_values(obj) {
return Object.keys(obj).map(key => [key, obj[key]]);
};
+3
View File
@@ -0,0 +1,3 @@
module.exports = function Object_values(obj) {
return Object.keys(obj).map(key => obj[key]);
};
+29
View File
@@ -0,0 +1,29 @@
// Perform a set of tasks in array quickly. Sometimes testing much better for
// large sets of work instead of individual promises.
const bulkFsTask = (array, each) =>
new Promise((resolve, reject) => {
let ops = 0;
const out = [];
array.forEach((item, i) => {
out[i] = each(item, (back, callback) => {
ops++;
return (err, value) => {
try {
out[i] = back(err, value, out[i]);
} catch (e) {
return reject(e);
}
ops--;
if (ops === 0) {
resolve(out);
}
};
});
});
if (ops === 0) {
resolve(out);
}
});
module.exports = bulkFsTask;
+89
View File
@@ -0,0 +1,89 @@
const fs = require('graceful-fs');
const path = require('path');
const logMessages = require('./log-messages');
exports.cachePrefix = cachePrefix;
const NS = fs.realpathSync(path.dirname(__dirname));
const cachePrefixNS = `${NS}/cachePrefix`;
function cachePrefix(compilation) {
if (typeof compilation[cachePrefixNS] === 'undefined') {
let prefix = '';
let nextCompilation = compilation;
while (nextCompilation.compiler.parentCompilation) {
const parentCompilation = nextCompilation.compiler.parentCompilation;
if (!nextCompilation.cache) {
logMessages.childCompilerWithoutCache(compilation);
prefix = null;
break;
}
const cache = nextCompilation.cache;
let parentCache = parentCompilation.cache;
if (cache === parentCache) {
nextCompilation = parentCompilation;
continue;
}
let cacheKey;
for (var key in parentCache) {
if (key && parentCache[key] === cache) {
cacheKey = key;
break;
}
}
// webpack 3 adds the children member containing compiler names paired
// with arrays of compilation caches, one for each compilation sharing the
// same name.
if (!cacheKey && parentCache.children) {
parentCache = parentCache.children;
for (var key in parentCache) {
if (key && parentCache[key]) {
for (const index in parentCache[key]) {
if (parentCache[key][index] === cache) {
cacheKey = `${key}.${index}`;
break;
}
if (
parentCache[key][index] &&
typeof parentCache[key][index] === 'object'
) {
for (const subkey in parentCache[key][index]) {
if (parentCache[key][index][subkey] === cache) {
cacheKey = `${key}.${index}.${subkey}`;
break;
}
}
}
}
}
}
}
if (!cacheKey) {
logMessages.childCompilerUnnamedCache(compilation);
prefix = null;
break;
} else {
prefix = cacheKey + prefix;
}
nextCompilation = parentCompilation;
}
compilation[cachePrefixNS] =
prefix !== null
? require('crypto')
.createHash('md5')
.update(prefix)
.digest('base64')
: null;
}
return compilation[cachePrefixNS];
}
+248
View File
@@ -0,0 +1,248 @@
const cachePrefix = require('.').cachePrefix;
const LoggerFactory = require('../loggerFactory');
exports.moduleFreezeError = (compilation, module, e) => {
const loggerSerial = LoggerFactory.getLogger(compilation).from('serial');
const compilerName = compilation.compiler.name;
const compilerContext = compilation.compiler.options.context;
const identifierPrefix = cachePrefix(compilation);
const moduleIdentifier = module.identifier();
const shortener = new (require('webpack/lib/RequestShortener'))(
compilerContext,
);
const moduleReadable = module.readableIdentifier(shortener);
loggerSerial.error(
{
id: 'serialization--error-freezing-module',
identifierPrefix,
compilerName,
moduleIdentifier,
moduleReadable,
error: e,
errorMessage: e.message,
errorStack: e.stack,
},
`Unable to freeze module "${moduleReadable}${
compilerName ? `" in compilation "${compilerName}` : ''
}". An error occured serializing it into a string: ${e.message}`,
);
};
exports.cacheNoParity = (compiler, { parityRoot }) => {
const loggerSerial = new LoggerFactory(compiler).create().from('serial');
loggerSerial.error(
{
id: 'serialzation--cache-incomplete',
parityRoot,
},
[
`Previous cache did not complete parity check. ${parityRoot.reason}`,
'Resetting cache.',
].join('\n'),
);
};
exports.serialBadCache = (compiler, error) => {
const loggerSerial = new LoggerFactory(compiler).create().from('serial');
loggerSerial.error(
{
id: 'serialzation--bad-cache',
},
['Cache is corrupted.', error.stack || error.message || error].join('\n'),
);
};
const logCore = compiler => new LoggerFactory(compiler).create().from('core');
exports.configHashSetButNotUsed = (compiler, { cacheDirectory }) => {
const loggerCore = logCore(compiler);
loggerCore.error(
{
id: 'confighash--directory-no-confighash',
cacheDirectory: cacheDirectory,
},
'HardSourceWebpackPlugin cannot use [confighash] in cacheDirectory ' +
'without configHash option being set and returning a non-falsy value.',
);
};
exports.configHashFirstBuild = (compiler, { cacheDirPath, configHash }) => {
const loggerCore = logCore(compiler);
loggerCore.log(
{
id: 'confighash--new',
cacheDirPath,
configHash,
},
`HardSourceWebpackPlugin is writing to a new confighash path for the first time: ${cacheDirPath}`,
);
};
exports.configHashBuildWith = (compiler, { cacheDirPath, configHash }) => {
const loggerCore = logCore(compiler);
loggerCore.log(
{
id: 'confighash--reused',
cacheDirPath,
configHash,
},
`HardSourceWebpackPlugin is reading from and writing to a confighash path: ${cacheDirPath}`,
);
};
exports.deleteOldCaches = (compiler, { newTotalSize, oldTotalSize }) => {
const loggerCore = logCore(compiler);
const sizeMB = Math.ceil(newTotalSize / 1024 / 1024);
const deletedSizeMB = Math.ceil(oldTotalSize / 1024 / 1024);
loggerCore.log(
{
id: 'caches--delete-old',
size: newTotalSize,
sizeMB,
deletedSize: oldTotalSize,
deletedSizeMB,
},
`HardSourceWebpackPlugin is using ${sizeMB} MB of disk space after deleting ${deletedSizeMB} MB.`,
);
};
exports.keepCaches = (compiler, { totalSize }) => {
const loggerCore = logCore(compiler);
const sizeMB = Math.ceil(totalSize / 1024 / 1024);
loggerCore.log(
{
id: 'caches--keep',
size: totalSize,
sizeMB,
},
`HardSourceWebpackPlugin is using ${sizeMB} MB of disk space.`,
);
};
exports.environmentInputs = (compiler, { inputs }) => {
const loggerCore = logCore(compiler);
loggerCore.log(
{
id: 'environment--inputs',
inputs,
},
`Tracking environment changes with ${inputs.join(', ')}.`,
);
};
exports.configHashChanged = compiler => {
const loggerCore = logCore(compiler);
loggerCore.warn(
{
id: 'environment--config-changed',
},
'Environment has changed (configuration was changed).\n' +
'HardSourceWebpackPlugin will reset the cache and store a fresh one.',
);
};
exports.environmentHashChanged = compiler => {
const loggerCore = logCore(compiler);
loggerCore.warn(
{
id: 'environment--changed',
},
'Environment has changed (node_modules was updated).\n' +
'HardSourceWebpackPlugin will reset the cache and store a fresh one.',
);
};
exports.hardSourceVersionChanged = compiler => {
const loggerCore = logCore(compiler);
loggerCore.warn(
{
id: 'environment--hardsource-changed',
},
'Installed HardSource version does not match the saved ' +
'cache.\nHardSourceWebpackPlugin will reset the cache and store ' +
'a fresh one.',
);
};
exports.childCompilerWithoutCache = compilation => {
var loggerUtil = LoggerFactory.getLogger(compilation).from('util');
loggerUtil.error(
{
id: 'childcompiler--no-cache',
compilerName: compilation.compiler.name,
},
[
`A child compiler (${compilation.compiler.name}) does not`,
"have a memory cache. Enable a memory cache with webpack's",
'`cache` configuration option. HardSourceWebpackPlugin will be',
'disabled for this child compiler until then.',
].join('\n'),
);
};
exports.childCompilerUnnamedCache = compilation => {
var loggerUtil = LoggerFactory.getLogger(compilation).from('util');
loggerUtil.error(
{
id: 'childcompiler--unnamed-cache',
compilerName: compilation.compiler.name,
},
[
`A child compiler (${compilation.compiler.name}) has a`,
'memory cache but its cache name is unknown.',
'HardSourceWebpackPlugin will be disabled for this child',
'compiler.',
].join('\n'),
);
};
const logParallel = compiler =>
new LoggerFactory(compiler).create().from('parallel');
exports.parallelStartWorkers = (compiler, options) => {
const loggerParallel = logParallel(compiler);
loggerParallel.log(
{
id: 'parallel--start-workers',
numWorkers: options.numWorkers,
},
[`Start ${options.numWorkers} module workers.`].join('\n'),
);
};
exports.parallelConfigMismatch = (compiler, options) => {
const loggerParallel = logParallel(compiler);
loggerParallel.error(
{
id: 'parallel--config-mismatch',
ourHash: options.ourHash,
theirHash: options.theirHash,
},
[
`Child process's configuration does not match parent `,
`configuration. Unable to parallelize webpack.`,
].join('\n'),
);
};
exports.parallelErrorSendingJob = (compiler, error) => {
const loggerParallel = logParallel(compiler);
loggerParallel.error(
{
id: 'parallel--error-sending-job',
error,
},
`Failed to send parallel module work. ${error.stack}`,
);
};
exports.parallelRequireWebpack4 = compiler => {
const loggerParallel = logParallel(compiler);
loggerParallel.error(
{
id: 'parallel--webpack-4',
},
`Parallel Module Plugin requires webpack 4.`,
);
};
+194
View File
@@ -0,0 +1,194 @@
const parseJson = require('parse-json');
const { cachePrefix } = require('.');
class ParityRoot {
constructor() {
this.children = [];
}
add(name) {
const bits = new ParityCache(name);
this.children.push(bits);
return bits;
}
verify() {
const firstChild = this.children[0];
if (!this.children.some(child => child.root)) {
return true;
}
for (const child of this.children) {
if (!child.verify()) {
this.reason = {
cache: child,
cacheName: child.name,
cacheReason: child.reason,
message: `Cache ${child.name} is not complete. ${
child.reason.message
}`,
};
return false;
}
if (child !== firstChild && child.root.token !== firstChild.root.token) {
this.reason = {
firstCache: firstChild,
firstCacheName: firstChild.name,
firstCacheReason: firstChild.reason,
secondCache: child,
secondCacheName: child.name,
secondCacheReason: child.reason,
message: `Cache ${firstChild.name} and ${child.name} disagree.`,
};
return false;
}
}
return true;
}
}
class ParityCache {
constructor(name) {
this.name = name;
this.root = null;
this.bits = {};
this.reason = null;
}
add(_token) {
const token = ParityToken.fromJson(_token);
if (token.isRoot) {
this.root = token;
}
this.bits[token.id] = token;
}
verify() {
if (this.root === null) {
this.reason = {
message: 'Root compilation not found.',
};
return false;
}
for (const id of this.root.ids) {
if (typeof this.bits[id] === 'undefined') {
this.reason = {
message: `Compilation '${id}' not found.`,
};
return false;
} else if (this.root.token !== this.bits[id].token) {
this.reason = {
message: `Root and '${id}' compilation disagree.`,
};
return false;
}
}
return true;
}
}
const createParityToken = (id, ids = null) => {
const token = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c =>
c === 'x'
? ((Math.random() * 16) | 0).toString(16)
: (((Math.random() * 4) | 0) + 8).toString(16),
);
return new ParityToken(id, token, ids);
};
class ParityToken {
constructor(id, token, ids = null) {
this.id = id;
this.token = token;
this.isRoot = ids !== null;
this.ids = ids;
}
static fromCompilation(compilation) {
let parentCompilation = compilation;
while (parentCompilation.compiler.parentCompilation) {
parentCompilation = parentCompilation.compiler.parentCompilation;
}
if (!parentCompilation.__hardSource_parityToken) {
parentCompilation.__hardSource_parityToken = createParityToken(
cachePrefix(parentCompilation),
[],
);
}
if (compilation !== parentCompilation) {
return parentCompilation.__hardSource_parityToken.createChild(
cachePrefix(compilation),
);
}
return parentCompilation.__hardSource_parityToken;
}
static fromJson(json) {
return new ParityToken(json.id, json.token, json.ids);
}
createChild(id) {
this.ids.push(id);
return new ParityToken(id, this.token);
}
toJSON() {
return {
type: 'CacheParityToken',
id: this.id,
ids: this.ids,
token: this.token,
};
}
}
const parseIfString = item => {
if (typeof item === 'string') {
return parseJson(item);
}
return item;
};
const parityCacheFromCache = (name, parityRoot, cache) => {
const parityCache = parityRoot.add(name);
if (cache.__hardSource_parityToken_root) {
const rootCompilation = parseIfString(cache.__hardSource_parityToken_root);
parityCache.add(rootCompilation);
rootCompilation.ids.forEach(id => {
if (cache[`__hardSource_parityToken_${id}`]) {
parityCache.add(parseIfString(cache[`__hardSource_parityToken_${id}`]));
}
});
}
};
const pushParityWriteOps = (compilation, ops) => {
if (compilation.compiler.parentCompilation) {
ops.push({
key: `__hardSource_parityToken_${cachePrefix(compilation)}`,
value: JSON.stringify(ParityToken.fromCompilation(compilation)),
});
} else {
ops.push({
key: `__hardSource_parityToken_root`,
value: JSON.stringify(ParityToken.fromCompilation(compilation)),
});
}
};
module.exports = {
ParityRoot,
ParityCache,
ParityToken,
parityCacheFromCache,
pushParityWriteOps,
};
+247
View File
@@ -0,0 +1,247 @@
let hookTypes;
const callStyles = {
sync: 'applyPlugins',
syncWaterfall: 'applyPluginsWaterfall',
syncBail: 'applyPluginsBailResult',
sync_map: 'applyPlugins',
asyncWaterfall: 'applyPluginsAsyncWaterfall',
asyncParallel: 'applyPluginsParallel',
asyncSerial: 'applyPluginsAsync',
};
const camelToDash = camel =>
camel.replace(/_/g, '--').replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
const knownPluginRegistrations = {
Compilation: {
needAdditionalPass: ['sync', []],
succeedModule: ['sync', ['module']],
buildModule: ['sync', ['module']],
seal: ['sync', []],
},
Compiler: {
afterCompile: ['asyncSerial', ['compilation']],
afterEnvironment: ['sync', []],
afterPlugins: ['sync', []],
afterResolvers: ['sync', []],
compilation: ['sync', ['compilation', 'params']],
emit: ['asyncSerial', ['compilation']],
make: ['asyncParallel', ['compilation']],
watchRun: ['asyncSerial', ['watcher']],
run: ['asyncSerial', ['compiler']],
},
NormalModuleFactory: {
createModule: ['syncBail', ['data']],
parser: ['sync_map', ['parser', 'parserOptions']],
resolver: ['syncWaterfall', ['nextResolver']],
},
ContextModuleFactory: {
afterResolve: ['asyncWaterfall', ['data']],
},
};
exports.register = (tapable, name, style, args) => {
if (tapable.hooks) {
if (!hookTypes) {
const Tapable = require('tapable');
hookTypes = {
sync: Tapable.SyncHook,
syncWaterfall: Tapable.SyncWaterfallHook,
syncBail: Tapable.SyncBailHook,
asyncWaterfall: Tapable.AsyncWaterfallHook,
asyncParallel: Tapable.AsyncParallelHook,
asyncSerial: Tapable.AsyncSeriesHook,
asyncSeries: Tapable.AsyncSeriesHook,
};
}
if (!tapable.hooks[name]) {
tapable.hooks[name] = new hookTypes[style](args);
}
} else {
if (!tapable.__hardSource_hooks) {
tapable.__hardSource_hooks = {};
}
if (!tapable.__hardSource_hooks[name]) {
tapable.__hardSource_hooks[name] = {
name,
dashName: camelToDash(name),
style,
args,
async: style.startsWith('async'),
map: style.endsWith('_map'),
};
}
if (!tapable.__hardSource_proxy) {
tapable.__hardSource_proxy = {};
}
if (!tapable.__hardSource_proxy[name]) {
if (tapable.__hardSource_hooks[name].map) {
const _forCache = {};
tapable.__hardSource_proxy[name] = {
_forCache,
for: key => {
let hook = _forCache[key];
if (hook) {
return hook;
}
_forCache[key] = {
tap: (...args) => exports.tapFor(tapable, name, key, ...args),
tapPromise: (...args) =>
exports.tapPromiseFor(tapable, name, key, ...args),
call: (...args) => exports.callFor(tapable, name, key, ...args),
promise: (...args) =>
exports.promiseFor(tapable, name, key, ...args),
};
return _forCache[key];
},
tap: (...args) => exports.tapFor(tapable, name, ...args),
tapPromise: (...args) =>
exports.tapPromiseFor(tapable, name, ...args),
call: (...args) => exports.callFor(tapable, name, ...args),
promise: (...args) => exports.promiseFor(tapable, name, ...args),
};
} else {
tapable.__hardSource_proxy[name] = {
tap: (...args) => exports.tap(tapable, name, ...args),
tapPromise: (...args) => exports.tapPromise(tapable, name, ...args),
call: (...args) => exports.call(tapable, name, args),
promise: (...args) => exports.promise(tapable, name, args),
};
}
}
}
};
exports.tap = (tapable, name, reason, callback) => {
if (tapable.hooks) {
tapable.hooks[name].tap(reason, callback);
} else {
if (!tapable.__hardSource_hooks || !tapable.__hardSource_hooks[name]) {
const registration =
knownPluginRegistrations[tapable.constructor.name][name];
exports.register(tapable, name, registration[0], registration[1]);
}
const dashName = tapable.__hardSource_hooks[name].dashName;
if (tapable.__hardSource_hooks[name].async) {
tapable.plugin(dashName, (...args) => {
const cb = args.pop();
cb(null, callback(...args));
});
} else {
tapable.plugin(dashName, callback);
}
}
};
exports.tapPromise = (tapable, name, reason, callback) => {
if (tapable.hooks) {
tapable.hooks[name].tapPromise(reason, callback);
} else {
if (!tapable.__hardSource_hooks || !tapable.__hardSource_hooks[name]) {
const registration =
knownPluginRegistrations[tapable.constructor.name][name];
exports.register(tapable, name, registration[0], registration[1]);
}
const dashName = tapable.__hardSource_hooks[name].dashName;
tapable.plugin(dashName, (...args) => {
const cb = args.pop();
return callback(...args).then(value => cb(null, value), cb);
});
}
};
exports.tapAsync = (tapable, name, reason, callback) => {
if (tapable.hooks) {
tapable.hooks[name].tapAsync(reason, callback);
} else {
if (!tapable.__hardSource_hooks || !tapable.__hardSource_hooks[name]) {
const registration =
knownPluginRegistrations[tapable.constructor.name][name];
exports.register(tapable, name, registration[0], registration[1]);
}
const dashName = tapable.__hardSource_hooks[name].dashName;
tapable.plugin(dashName, callback);
}
};
exports.call = (tapable, name, args) => {
if (tapable.hooks) {
const hook = tapable.hooks[name];
return hook.call(...args);
} else {
const dashName = tapable.__hardSource_hooks[name].dashName;
const style = tapable.__hardSource_hooks[name].style;
return tapable[callStyles[style]](...[dashName].concat(args));
}
};
exports.promise = (tapable, name, args) => {
if (tapable.hooks) {
const hook = tapable.hooks[name];
return hook.promise(...args);
} else {
const dashName = tapable.__hardSource_hooks[name].dashName;
const style = tapable.__hardSource_hooks[name].style;
return new Promise((resolve, reject) => {
tapable[callStyles[style]](
...[dashName].concat(args, (err, value) => {
if (err) {
reject(err);
} else {
resolve(value);
}
}),
);
});
}
};
exports.tapFor = (tapable, name, key, reason, callback) => {
if (tapable.hooks) {
tapable.hooks[name].for(key).tap(reason, callback);
} else {
exports.tap(tapable, name, reason, callback);
}
};
exports.tapPromiseFor = (tapable, name, key, reason, callback) => {
if (tapable.hooks) {
tapable.hooks[name].for(key).tapPromise(reason, callback);
} else {
exports.tapPromise(tapable, name, reason, callback);
}
};
exports.callFor = (tapable, name, key, args) => {
if (tapable.hooks) {
tapable.hooks[name].for(key).call(...args);
} else {
exports.call(tapable, name, args);
}
};
exports.promiseFor = (tapable, name, key, args) => {
if (tapable.hooks) {
tapable.hooks[name].for(key).promise(...args);
} else {
exports.promise(tapable, name, args);
}
};
exports.hooks = tapable => {
if (tapable.hooks) {
return tapable.hooks;
}
if (!tapable.__hardSource_proxy) {
tapable.__hardSource_proxy = {};
}
const registrations = knownPluginRegistrations[tapable.constructor.name];
if (registrations) {
for (const name in registrations) {
const registration = registrations[name];
exports.register(tapable, name, registration[0], registration[1]);
}
}
return tapable.__hardSource_proxy;
};
+15
View File
@@ -0,0 +1,15 @@
module.exports = function promisify(f, o) {
const ctx = (o && o.context) || null;
return function promisify_wrap() {
const args = Array.from(arguments);
return new Promise(function promisify_resolver(resolve, reject) {
args.push(function promisify_callback(err, value) {
if (err) {
return reject(err);
}
return resolve(value);
});
f.apply(ctx, args);
});
};
};
+195
View File
@@ -0,0 +1,195 @@
const path = require('path');
const rootCompiler = compiler => {
while (compiler.parentCompilation) {
compiler = compiler.parentCompilation.compiler;
}
return compiler;
};
const compilerContext = (exports.compilerContext = function compilerContext(
compiler,
) {
return rootCompiler(compiler.compiler ? compiler.compiler : compiler).context;
});
const relateNormalPath = (exports.relateNormalPath = function relateNormalPath(
compiler,
key,
) {
if (typeof key !== 'string') {
return key;
}
if (compilerContext(compiler) === key) {
return '.';
}
if (key === '') {
return key;
}
const rel = path.relative(compilerContext(compiler), key.split('?')[0]);
return [rel.replace(/\\/g, '/')].concat(key.split('?').slice(1)).join('?');
});
const relateNormalRequest = (exports.relateNormalRequest = function relateNormalRequest(
compiler,
key,
) {
return key
.split('!')
.map(subkey => relateNormalPath(compiler, subkey))
.join('!');
});
const relateNormalModuleId = (exports.relateNormalModuleId = function relateNormalModuleId(
compiler,
id,
) {
return id.substring(0, 24) + relateNormalRequest(compiler, id.substring(24));
});
const relateNormalLoaders = (exports.relateNormalLoaders = function relateNormalLoaders(
compiler,
loaders,
) {
return loaders.map(loader =>
Object.assign({}, loader, {
loader: relateNormalPath(compiler, loader.loader),
}),
);
});
const relateNormalPathArray = (exports.relateNormalPathArray = function relateNormalPathArray(
compiler,
paths,
) {
return paths.map(subpath => relateNormalPath(compiler, subpath));
});
const relateNormalPathSet = (exports.relateNormalPathSet = function relateNormalPathSet(
compiler,
paths,
) {
return relateNormalPathArray(compiler, Array.from(paths));
});
/**
*
*/
// Cache whether we need to replace path.sep because contextNormalPath is called _very_ frequently
const resolveRelativeCompilerContext =
'/' === path.sep
? function(context, key) {
return path.resolve(context, key);
}
: function(context, key) {
return path.resolve(context, key).replace(/\//g, path.sep);
};
const contextNormalPath = (exports.contextNormalPath = function contextNormalPath(
compiler,
key,
) {
if (typeof key !== 'string' || key === '') {
return key;
}
const context = compilerContext(compiler);
if (key === '.') {
return context;
}
const markIndex = key.indexOf('?');
if (markIndex === -1) {
return resolveRelativeCompilerContext(context, key);
}
const abs = resolveRelativeCompilerContext(
context,
key.substring(0, markIndex),
);
return abs + '?' + key.substring(markIndex + 1);
});
const contextNormalRequest = (exports.contextNormalRequest = function contextNormalRequest(
compiler,
key,
) {
// return key
// .split('!')
// .map(subkey => contextNormalPath(compiler, subkey))
// .join('!');
let i = -1;
let j = -1;
let _newkey = '';
while ((i = key.indexOf('!', i + 1)) !== -1) {
_newkey += contextNormalPath(compiler, key.substring(j + 1, i));
_newkey += '!';
j = i;
}
_newkey += contextNormalPath(compiler, key.substring(j + 1));
return _newkey;
});
const contextNormalModuleId = (exports.contextNormalModuleId = function contextNormalModuleId(
compiler,
id,
) {
return id.substring(0, 24) + contextNormalRequest(compiler, id.substring(24));
});
const contextNormalLoaders = (exports.contextNormalLoaders = function contextNormalLoaders(
compiler,
loaders,
) {
return loaders.map(loader =>
Object.assign({}, loader, {
loader: contextNormalPath(compiler, loader.loader),
}),
);
});
const contextNormalPathArray = (exports.contextNormalPathArray = function contextNormalPathArray(
compiler,
paths,
) {
return paths.map(subpath => contextNormalPath(compiler, subpath));
});
const contextNormalPathSet = (exports.contextNormalPathSet = function contextNormalPathSet(
compiler,
paths,
) {
return new Set(contextNormalPathArray(compiler, paths));
});
/**
*
*/
const maybeAbsolutePath = (exports.maybeAbsolutePath = function maybeAbsolutePath(
path,
) {
return /^([a-zA-Z]:\\\\|\/)/.test(path);
});
const relateAbsolutePath = (exports.relateAbsolutePath = function relateAbsolutePath(
context,
absPath,
) {
if (maybeAbsolutePath(absPath)) {
return path.relative(context, absPath);
}
return absPath;
});
const relateAbsoluteRequest = (exports.relateAbsoluteRequest = function relateAbsoluteRequest(
context,
absReq,
) {
return absReq
.split(/!/g)
.map(path => relateAbsolutePath(context, path))
.join('!');
});
+220
View File
@@ -0,0 +1,220 @@
const relateContext = require('./relate-context');
const pipe = (exports.pipe = (...fns) => ({
freeze(arg, module, extra, methods) {
for (const fn of fns) {
arg = fn.freeze(arg, module, extra, methods);
}
return arg;
},
thaw(arg, frozen, extra, methods) {
for (const fn of fns) {
arg = fn.thaw(arg, frozen, extra, methods);
}
return arg;
},
}));
const serialMap = (exports.map = (keyOp, valueOp) => ({
freeze(arg, module, extra) {
const resolved = [];
for (const key in arg) {
resolved.push([
keyOp.freeze(key, key, extra),
valueOp.freeze(arg[key], arg[key], extra),
]);
}
return resolved;
},
thaw(arg, frozen, extra) {
const resolved = {};
for (const item of arg) {
const key = keyOp.thaw(item[0], item[0], extra);
const value = valueOp.thaw(item[1], item[1], extra);
resolved[key] = value;
}
return resolved;
},
}));
const contextual = (exports.contextual = fnname => {
const relate = relateContext[`relateNormal${fnname}`];
const context = relateContext[`contextNormal${fnname}`];
return {
freeze: (arg, module, { compiler, compilation }, methods) =>
arg ? relate(compiler || compilation.compiler, arg) : arg,
thaw: (arg, module, { compiler, compilation }, methods) =>
arg ? context(compiler || compilation.compiler, arg) : arg,
};
});
const archetype = (exports.archetype = type => ({
freeze: (arg, module, extra, { freeze }) => freeze(type, null, arg, extra),
thaw: (arg, module, extra, { thaw }) => thaw(type, null, arg, extra),
}));
const mapArchetype = (exports.mapArchetype = type => ({
freeze: (arg, module, extra, { mapFreeze }) =>
mapFreeze(type, null, arg, extra),
thaw: (arg, module, extra, { mapThaw }) => mapThaw(type, null, arg, extra),
}));
const path = (exports.path = contextual('Path'));
const pathArray = (exports.pathArray = contextual('PathArray'));
const pathSet = (exports.pathSet = contextual('PathSet'));
const request = (exports.request = contextual('Request'));
const _loaders = (exports._loaders = contextual('Loaders'));
const loaders = (exports.loaders = {
freeze: _loaders.freeze,
thaw(arg, frozen, extra, methods) {
return _loaders.thaw(arg, frozen, extra, methods).map(loader => {
if (loader.ident) {
let ruleSet =
extra.normalModuleFactory && extra.normalModuleFactory.ruleSet;
if (!ruleSet) {
ruleSet = extra.compiler.__hardSource_ruleSet;
if (!ruleSet) {
const RuleSet = require('webpack/lib/RuleSet');
if (extra.compiler.options.module.defaultRules) {
// webpack 4
ruleSet = extra.compiler.__hardSource_ruleSet = new RuleSet(
extra.compiler.options.module.defaultRules.concat(
extra.compiler.options.module.rules,
),
);
} else {
// webpack <4
ruleSet = extra.compiler.__hardSource_ruleSet = new RuleSet(
extra.compiler.options.module.rules ||
extra.compiler.options.module.loaders,
);
}
}
}
return {
loader: loader.loader,
ident: loader.ident,
options: ruleSet.findOptionsByIdent(loader.ident),
};
}
return loader;
});
},
});
const regExp = (exports.regExp = {
freeze: arg => (arg ? arg.source : false),
thaw: arg => (arg ? new RegExp(arg) : false),
});
const parser = (exports.parser = archetype('Parser'));
const generator = (exports.generator = archetype('Generator'));
const source = (exports.source = archetype('Source'));
const moduleAssets = (exports.moduleAssets = archetype('ModuleAssets'));
const moduleError = (exports.moduleError = mapArchetype('ModuleError'));
const moduleWarning = (exports.moduleWarning = mapArchetype('ModuleWarning'));
const dependencyBlock = (exports.dependencyBlock = {
freeze: (arg, module, extra, { freeze }) =>
freeze('DependencyBlock', null, arg, extra),
thaw: (arg, module, extra, { thaw }) =>
thaw('DependencyBlock', arg, module, extra),
});
const _null = (exports.null = {
freeze: () => null,
thaw: () => null,
});
const identity = (exports.identity = {
freeze: (arg, module, extra, methods) => arg,
thaw: (arg, module, extra, methods) => arg,
});
const assigned = (exports.assigned = members => ({
freeze(arg, module, extra, methods) {
const out = {};
for (const key in members) {
out[key] = members[key].freeze(arg[key], module, extra, methods);
}
return out;
},
thaw(arg, frozen, extra, methods) {
for (const key in members) {
arg[key] = members[key].thaw(frozen[key], frozen, extra, methods);
}
return arg;
},
}));
const created = (exports.created = members => ({
freeze(arg, module, extra, methods) {
if (!arg) {
return null;
}
const out = {};
for (const key in members) {
out[key] = members[key].freeze(arg[key], module, extra, methods);
}
return out;
},
thaw(arg, frozen, extra, methods) {
if (!arg) {
return null;
}
const out = {};
for (const key in members) {
out[key] = members[key].thaw(arg[key], frozen, extra, methods);
}
return out;
},
}));
const objectAssign = (exports.objectAssign = opts => ({
freeze(arg, module, extra) {
const out = Object.assign({}, arg);
for (const key in opts) {
out[key] = opts[key].freeze(arg[key], arg, extra);
}
return out;
},
thaw(arg, frozen, extra) {
const out = Object.assign({}, arg);
for (const key in opts) {
out[key] = opts[key].thaw(arg[key], arg, extra);
}
return out;
},
}));
const constructed = (exports.constructed = (Type, args) => ({
freeze(arg, module, extra, methods) {
const out = {};
for (const key in args) {
out[key] = args[key].freeze(arg[key], module, extra, methods);
}
return out;
},
thaw(arg, frozen, extra, methods) {
const newArgs = [];
for (const key in args) {
newArgs.push(args[key].thaw(frozen[key], frozen, extra, methods));
}
return new Type(...newArgs);
},
}));
const serial = (exports.serial = (name, stages) => ({
freeze(arg, module, extra, methods) {
const out = {
type: name,
};
for (const key in stages) {
out[key] = stages[key].freeze(module, module, extra, methods);
}
return out;
},
thaw(arg, frozen, extra, methods) {
let out = arg;
for (const key in stages) {
out = stages[key].thaw(out, frozen[key], extra, methods);
}
return out;
},
}));