Skip to content

Commit

Permalink
Merge branch 'main' into feature/errors-warnings-space
Browse files Browse the repository at this point in the history
# Conflicts:
#	schemas/WebpackOptions.check.js
  • Loading branch information
vankop committed Mar 4, 2022
2 parents 38bffd1 + 5a26b7c commit 8f8ba91
Show file tree
Hide file tree
Showing 52 changed files with 1,153 additions and 265 deletions.
4 changes: 4 additions & 0 deletions declarations/WebpackOptions.d.ts
Expand Up @@ -2973,6 +2973,10 @@ export interface JavascriptParserOptions {
* Enable/disable evaluating import.meta.
*/
importMeta?: boolean;
/**
* Enable/disable evaluating import.meta.webpackContext.
*/
importMetaContext?: boolean;
/**
* Include polyfills or mocks for various node stuff.
*/
Expand Down
14 changes: 10 additions & 4 deletions lib/BannerPlugin.js
Expand Up @@ -77,6 +77,7 @@ class BannerPlugin {
undefined,
options
);
const cache = new WeakMap();

compiler.hooks.compilation.tap("BannerPlugin", compilation => {
compilation.hooks.processAssets.tap(
Expand All @@ -102,10 +103,15 @@ class BannerPlugin {

const comment = compilation.getPath(banner, data);

compilation.updateAsset(
file,
old => new ConcatSource(comment, "\n", old)
);
compilation.updateAsset(file, old => {
let cached = cache.get(old);
if (!cached || cached.comment !== comment) {
const source = new ConcatSource(comment, "\n", old);
cache.set(old, { source, comment });
return source;
}
return cached.source;
});
}
}
}
Expand Down
82 changes: 64 additions & 18 deletions lib/CleanPlugin.js
Expand Up @@ -19,6 +19,7 @@ const processAsyncTree = require("./util/processAsyncTree");
/** @typedef {import("./util/fs").StatsCallback} StatsCallback */

/** @typedef {(function(string):boolean)|RegExp} IgnoreItem */
/** @typedef {Map<string, number>} Assets */
/** @typedef {function(IgnoreItem): void} AddToIgnoreCallback */

/**
Expand All @@ -40,18 +41,32 @@ const validate = createSchemaValidation(
baseDataPath: "options"
}
);
const _10sec = 10 * 1000;

/**
* marge assets map 2 into map 1
* @param {Assets} as1 assets
* @param {Assets} as2 assets
* @returns {void}
*/
const mergeAssets = (as1, as2) => {
for (const [key, value1] of as2) {
const value2 = as1.get(key);
if (!value2 || value1 > value2) as1.set(key, value1);
}
};

/**
* @param {OutputFileSystem} fs filesystem
* @param {string} outputPath output path
* @param {Set<string>} currentAssets filename of the current assets (must not start with .. or ., must only use / as path separator)
* @param {Map<string, number>} currentAssets filename of the current assets (must not start with .. or ., must only use / as path separator)
* @param {function((Error | null)=, Set<string>=): void} callback returns the filenames of the assets that shouldn't be there
* @returns {void}
*/
const getDiffToFs = (fs, outputPath, currentAssets, callback) => {
const directories = new Set();
// get directories of assets
for (const asset of currentAssets) {
for (const [asset] of currentAssets) {
directories.add(asset.replace(/(^|\/)[^/]*$/, ""));
}
// and all parent directories
Expand Down Expand Up @@ -91,13 +106,15 @@ const getDiffToFs = (fs, outputPath, currentAssets, callback) => {
};

/**
* @param {Set<string>} currentAssets assets list
* @param {Set<string>} oldAssets old assets list
* @param {Assets} currentAssets assets list
* @param {Assets} oldAssets old assets list
* @returns {Set<string>} diff
*/
const getDiffToOldAssets = (currentAssets, oldAssets) => {
const diff = new Set();
for (const asset of oldAssets) {
const now = Date.now();
for (const [asset, ts] of oldAssets) {
if (ts >= now) continue;
if (!currentAssets.has(asset)) diff.add(asset);
}
return diff;
Expand All @@ -124,7 +141,7 @@ const doStat = (fs, filename, callback) => {
* @param {Logger} logger logger
* @param {Set<string>} diff filenames of the assets that shouldn't be there
* @param {function(string): boolean} isKept check if the entry is ignored
* @param {function(Error=): void} callback callback
* @param {function(Error=, Assets=): void} callback callback
* @returns {void}
*/
const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
Expand All @@ -137,11 +154,13 @@ const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
};
/** @typedef {{ type: "check" | "unlink" | "rmdir", filename: string, parent: { remaining: number, job: Job } | undefined }} Job */
/** @type {Job[]} */
const jobs = Array.from(diff, filename => ({
const jobs = Array.from(diff.keys(), filename => ({
type: "check",
filename,
parent: undefined
}));
/** @type {Assets} */
const keptAssets = new Map();
processAsyncTree(
jobs,
10,
Expand All @@ -161,6 +180,7 @@ const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
switch (type) {
case "check":
if (isKept(filename)) {
keptAssets.set(filename, 0);
// do not decrement parent entry as we don't want to delete the parent
log(`${filename} will be kept`);
return process.nextTick(callback);
Expand Down Expand Up @@ -247,7 +267,10 @@ const applyDiff = (fs, outputPath, dry, logger, diff, isKept, callback) => {
break;
}
},
callback
err => {
if (err) return callback(err);
callback(undefined, keptAssets);
}
);
};

Expand Down Expand Up @@ -302,6 +325,7 @@ class CleanPlugin {
// We assume that no external modification happens while the compiler is active
// So we can store the old assets and only diff to them to avoid fs access on
// incremental builds
/** @type {undefined|Assets} */
let oldAssets;

compiler.hooks.emit.tapAsync(
Expand All @@ -322,7 +346,9 @@ class CleanPlugin {
);
}

const currentAssets = new Set();
/** @type {Assets} */
const currentAssets = new Map();
const now = Date.now();
for (const asset of Object.keys(compilation.assets)) {
if (/^[A-Za-z]:\\|^\/|^\\\\/.test(asset)) continue;
let normalizedAsset;
Expand All @@ -335,7 +361,12 @@ class CleanPlugin {
);
} while (newNormalizedAsset !== normalizedAsset);
if (normalizedAsset.startsWith("../")) continue;
currentAssets.add(normalizedAsset);
const assetInfo = compilation.assetsInfo.get(asset);
if (assetInfo && assetInfo.hotModuleReplacement) {
currentAssets.set(normalizedAsset, now + _10sec);
} else {
currentAssets.set(normalizedAsset, 0);
}
}

const outputPath = compilation.getPath(compiler.outputPath, {});
Expand All @@ -346,19 +377,34 @@ class CleanPlugin {
return keepFn(path);
};

/**
* @param {Error=} err err
* @param {Set<string>=} diff diff
*/
const diffCallback = (err, diff) => {
if (err) {
oldAssets = undefined;
return callback(err);
callback(err);
return;
}
applyDiff(fs, outputPath, dry, logger, diff, isKept, err => {
if (err) {
oldAssets = undefined;
} else {
oldAssets = currentAssets;
applyDiff(
fs,
outputPath,
dry,
logger,
diff,
isKept,
(err, keptAssets) => {
if (err) {
oldAssets = undefined;
} else {
if (oldAssets) mergeAssets(currentAssets, oldAssets);
oldAssets = currentAssets;
if (keptAssets) mergeAssets(oldAssets, keptAssets);
}
callback(err);
}
callback(err);
});
);
};

if (oldAssets) {
Expand Down
116 changes: 90 additions & 26 deletions lib/ContextModule.js
Expand Up @@ -61,7 +61,7 @@ const makeSerializable = require("./util/makeSerializable");

/**
* @typedef {Object} ContextModuleOptionsExtras
* @property {string} resource
* @property {false|string|string[]} resource
* @property {string=} resourceQuery
* @property {string=} resourceFragment
* @property {TODO} resolveOptions
Expand Down Expand Up @@ -92,23 +92,36 @@ class ContextModule extends Module {
* @param {ContextModuleOptions} options options object
*/
constructor(resolveDependencies, options) {
const parsed = parseResource(options ? options.resource : "");
const resource = parsed.path;
const resourceQuery = (options && options.resourceQuery) || parsed.query;
const resourceFragment =
(options && options.resourceFragment) || parsed.fragment;

super("javascript/dynamic", resource);
if (!options || typeof options.resource === "string") {
const parsed = parseResource(
options ? /** @type {string} */ (options.resource) : ""
);
const resource = parsed.path;
const resourceQuery = (options && options.resourceQuery) || parsed.query;
const resourceFragment =
(options && options.resourceFragment) || parsed.fragment;

super("javascript/dynamic", resource);
/** @type {ContextModuleOptions} */
this.options = {
...options,
resource,
resourceQuery,
resourceFragment
};
} else {
super("javascript/dynamic");
/** @type {ContextModuleOptions} */
this.options = {
...options,
resource: options.resource,
resourceQuery: options.resourceQuery || "",
resourceFragment: options.resourceFragment || ""
};
}

// Info from Factory
this.resolveDependencies = resolveDependencies;
/** @type {ContextModuleOptions} */
this.options = {
...options,
resource,
resourceQuery,
resourceFragment
};
if (options && options.resolveOptions !== undefined) {
this.resolveOptions = options.resolveOptions;
}
Expand Down Expand Up @@ -155,7 +168,12 @@ class ContextModule extends Module {
}

_createIdentifier() {
let identifier = this.context;
let identifier =
this.context ||
(typeof this.options.resource === "string" ||
this.options.resource === false
? `${this.options.resource}`
: this.options.resource.join("|"));
if (this.options.resourceQuery) {
identifier += `|${this.options.resourceQuery}`;
}
Expand Down Expand Up @@ -220,7 +238,19 @@ class ContextModule extends Module {
* @returns {string} a user readable identifier of the module
*/
readableIdentifier(requestShortener) {
let identifier = requestShortener.shorten(this.context) + "/";
let identifier;
if (this.context) {
identifier = requestShortener.shorten(this.context) + "/";
} else if (
typeof this.options.resource === "string" ||
this.options.resource === false
) {
identifier = requestShortener.shorten(`${this.options.resource}`) + "/";
} else {
identifier = this.options.resource
.map(r => requestShortener.shorten(r) + "/")
.join(" ");
}
if (this.options.resourceQuery) {
identifier += ` ${this.options.resourceQuery}`;
}
Expand Down Expand Up @@ -270,11 +300,30 @@ class ContextModule extends Module {
* @returns {string | null} an identifier for library inclusion
*/
libIdent(options) {
let identifier = contextify(
options.context,
this.context,
options.associatedObjectForCache
);
let identifier;

if (this.context) {
identifier = contextify(
options.context,
this.context,
options.associatedObjectForCache
);
} else if (typeof this.options.resource === "string") {
identifier = contextify(
options.context,
this.options.resource,
options.associatedObjectForCache
);
} else if (this.options.resource === false) {
identifier = "false";
} else {
identifier = this.options.resource
.map(res =>
contextify(options.context, res, options.associatedObjectForCache)
)
.join(" ");
}

if (this.layer) identifier = `(${this.layer})/${identifier}`;
if (this.options.mode) {
identifier += ` ${this.options.mode}`;
Expand Down Expand Up @@ -323,8 +372,9 @@ class ContextModule extends Module {
// build if enforced
if (this._forceBuild) return callback(null, true);

// always build when we have no snapshot
if (!this.buildInfo.snapshot) return callback(null, true);
// always build when we have no snapshot and context
if (!this.buildInfo.snapshot)
return callback(null, Boolean(this.context || this.options.resource));

fileSystemInfo.checkSnapshotValid(this.buildInfo.snapshot, (err, valid) => {
callback(err, !valid);
Expand Down Expand Up @@ -439,10 +489,16 @@ class ContextModule extends Module {
);
return;
}
if (!this.context && !this.options.resource) return callback();

compilation.fileSystemInfo.createSnapshot(
startTime,
null,
[this.context],
this.context
? [this.context]
: typeof this.options.resource === "string"
? [this.options.resource]
: /** @type {string[]} */ (this.options.resource),
null,
SNAPSHOT_OPTIONS,
(err, snapshot) => {
Expand All @@ -466,7 +522,15 @@ class ContextModule extends Module {
missingDependencies,
buildDependencies
) {
contextDependencies.add(this.context);
if (this.context) {
contextDependencies.add(this.context);
} else if (typeof this.options.resource === "string") {
contextDependencies.add(this.options.resource);
} else if (this.options.resource === false) {
return;
} else {
for (const res of this.options.resource) contextDependencies.add(res);
}
}

/**
Expand Down

0 comments on commit 8f8ba91

Please sign in to comment.