Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add comments abilities to require.context #13306

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
156 changes: 6 additions & 150 deletions lib/dependencies/ImportParserPlugin.js
Expand Up @@ -6,17 +6,14 @@
"use strict";

const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
const CommentCompilationWarning = require("../CommentCompilationWarning");
const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
const commentParser = require("../util/commentParser");
const ContextDependencyHelpers = require("./ContextDependencyHelpers");
const ImportContextDependency = require("./ImportContextDependency");
const ImportDependency = require("./ImportDependency");
const ImportEagerDependency = require("./ImportEagerDependency");
const ImportWeakDependency = require("./ImportWeakDependency");

/** @typedef {import("../ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */
/** @typedef {import("../ContextModule").ContextMode} ContextMode */

class ImportParserPlugin {
constructor(options) {
this.options = options;
Expand All @@ -26,154 +23,13 @@ class ImportParserPlugin {
parser.hooks.importCall.tap("ImportParserPlugin", expr => {
const param = parser.evaluateExpression(expr.source);

let chunkName = null;
/** @type {ContextMode} */
let mode = "lazy";
let include = null;
let exclude = null;
/** @type {string[][] | null} */
let exports = null;
/** @type {RawChunkGroupOptions} */
const groupOptions = {};

const { options: importOptions, errors: commentErrors } =
parser.parseCommentOptions(expr.range);
const parsed = commentParser(parser, expr, "lazy", true);

if (commentErrors) {
for (const e of commentErrors) {
const { comment } = e;
parser.state.module.addWarning(
new CommentCompilationWarning(
`Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
comment.loc
)
);
}
}

if (importOptions) {
if (importOptions.webpackIgnore !== undefined) {
if (typeof importOptions.webpackIgnore !== "boolean") {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackIgnore\` expected a boolean, but received: ${importOptions.webpackIgnore}.`,
expr.loc
)
);
} else {
// Do not instrument `import()` if `webpackIgnore` is `true`
if (importOptions.webpackIgnore) {
return false;
}
}
}
if (importOptions.webpackChunkName !== undefined) {
if (typeof importOptions.webpackChunkName !== "string") {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackChunkName\` expected a string, but received: ${importOptions.webpackChunkName}.`,
expr.loc
)
);
} else {
chunkName = importOptions.webpackChunkName;
}
}
if (importOptions.webpackMode !== undefined) {
if (typeof importOptions.webpackMode !== "string") {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackMode\` expected a string, but received: ${importOptions.webpackMode}.`,
expr.loc
)
);
} else {
mode = importOptions.webpackMode;
}
}
if (importOptions.webpackPrefetch !== undefined) {
if (importOptions.webpackPrefetch === true) {
groupOptions.prefetchOrder = 0;
} else if (typeof importOptions.webpackPrefetch === "number") {
groupOptions.prefetchOrder = importOptions.webpackPrefetch;
} else {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackPrefetch\` expected true or a number, but received: ${importOptions.webpackPrefetch}.`,
expr.loc
)
);
}
}
if (importOptions.webpackPreload !== undefined) {
if (importOptions.webpackPreload === true) {
groupOptions.preloadOrder = 0;
} else if (typeof importOptions.webpackPreload === "number") {
groupOptions.preloadOrder = importOptions.webpackPreload;
} else {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackPreload\` expected true or a number, but received: ${importOptions.webpackPreload}.`,
expr.loc
)
);
}
}
if (importOptions.webpackInclude !== undefined) {
if (
!importOptions.webpackInclude ||
importOptions.webpackInclude.constructor.name !== "RegExp"
) {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackInclude\` expected a regular expression, but received: ${importOptions.webpackInclude}.`,
expr.loc
)
);
} else {
include = new RegExp(importOptions.webpackInclude);
}
}
if (importOptions.webpackExclude !== undefined) {
if (
!importOptions.webpackExclude ||
importOptions.webpackExclude.constructor.name !== "RegExp"
) {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackExclude\` expected a regular expression, but received: ${importOptions.webpackExclude}.`,
expr.loc
)
);
} else {
exclude = new RegExp(importOptions.webpackExclude);
}
}
if (importOptions.webpackExports !== undefined) {
if (
!(
typeof importOptions.webpackExports === "string" ||
(Array.isArray(importOptions.webpackExports) &&
importOptions.webpackExports.every(
item => typeof item === "string"
))
)
) {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackExports\` expected a string or an array of strings, but received: ${importOptions.webpackExports}.`,
expr.loc
)
);
} else {
if (typeof importOptions.webpackExports === "string") {
exports = [[importOptions.webpackExports]];
} else {
exports = Array.from(importOptions.webpackExports, e => [e]);
}
}
}
if (!parsed) {
return false;
}
let mode = parsed.mode;
const { chunkName, exports, include, exclude, groupOptions } = parsed;

if (param.isString()) {
if (mode !== "lazy" && mode !== "eager" && mode !== "weak") {
Expand Down
17 changes: 16 additions & 1 deletion lib/dependencies/RequireContextDependencyParserPlugin.js
Expand Up @@ -5,6 +5,7 @@

"use strict";

const commentParser = require("../util/commentParser");
const RequireContextDependency = require("./RequireContextDependency");

module.exports = class RequireContextDependencyParserPlugin {
Expand All @@ -14,9 +15,18 @@ module.exports = class RequireContextDependencyParserPlugin {
.tap("RequireContextDependencyParserPlugin", expr => {
let regExp = /^\.\/.*$/;
let recursive = true;
let mode = "sync";

const parsed = commentParser(parser, expr, "sync", false);

if (!parsed) {
return false;
}
let mode = parsed.mode;
const { chunkName, exports, include, exclude, groupOptions } = parsed;

switch (expr.arguments.length) {
case 4: {
// TODO Deprecate this argument in favor of comment
const modeExpr = parser.evaluateExpression(expr.arguments[3]);
if (!modeExpr.isString()) return;
mode = modeExpr.string;
Expand All @@ -43,6 +53,11 @@ module.exports = class RequireContextDependencyParserPlugin {
recursive,
regExp,
mode,
chunkName,
groupOptions,
include,
exclude,
referencedExports: exports,
category: "commonjs"
},
expr.range
Expand Down
176 changes: 176 additions & 0 deletions lib/util/commentParser.js
@@ -0,0 +1,176 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/

"use strict";

const CommentCompilationWarning = require("../CommentCompilationWarning");
const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");

/** @typedef {import("estree").Expression} ExpressionNode */
/** @typedef {import("../ChunkGroup").RawChunkGroupOptions} RawChunkGroupOptions */
/** @typedef {import("../ContextModule").ContextMode} ContextMode */
/** @typedef {import("../javascript/JavascriptParser")} Parser */

/**
* @param {Parser} parser The parser
* @param {ExpressionNode} expr The expression
* @param {ContextMode} mode The default context mode
* @param {boolean} allowIgnore Should we allow 'webpackIgnore'
*
* @returns {{chunkName: null | string, mode: ContextMode, include: null | RegExp, exports: string[][] | null, exclude: null | RegExp, groupOptions: RawChunkGroupOptions}|false} All the parsed properties
*/
module.exports = (parser, expr, mode = "lazy", allowIgnore = false) => {
let chunkName = null;
let include = null;
let exclude = null;

/** @type {string[][] | null} */
let exports = null;

/** @type {RawChunkGroupOptions} */
const groupOptions = {};

const { options: importOptions, errors: commentErrors } =
parser.parseCommentOptions(expr.range);

if (commentErrors) {
for (const e of commentErrors) {
const { comment } = e;
parser.state.module.addWarning(
new CommentCompilationWarning(
`Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
comment.loc
)
);
}
}

if (importOptions) {
if (allowIgnore) {
if (importOptions.webpackIgnore !== undefined) {
if (typeof importOptions.webpackIgnore !== "boolean") {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackIgnore\` expected a boolean, but received: ${importOptions.webpackIgnore}.`,
expr.loc
)
);
} else {
// Do not instrument `import()` if `webpackIgnore` is `true`
if (importOptions.webpackIgnore) {
return false;
}
}
}
}
if (importOptions.webpackChunkName !== undefined) {
if (typeof importOptions.webpackChunkName !== "string") {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackChunkName\` expected a string, but received: ${importOptions.webpackChunkName}.`,
expr.loc
)
);
} else {
chunkName = importOptions.webpackChunkName;
}
}
if (importOptions.webpackMode !== undefined) {
if (typeof importOptions.webpackMode !== "string") {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackMode\` expected a string, but received: ${importOptions.webpackMode}.`,
expr.loc
)
);
} else {
mode = importOptions.webpackMode;
}
}
if (importOptions.webpackPrefetch !== undefined) {
if (importOptions.webpackPrefetch === true) {
groupOptions.prefetchOrder = 0;
} else if (typeof importOptions.webpackPrefetch === "number") {
groupOptions.prefetchOrder = importOptions.webpackPrefetch;
} else {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackPrefetch\` expected true or a number, but received: ${importOptions.webpackPrefetch}.`,
expr.loc
)
);
}
}
if (importOptions.webpackPreload !== undefined) {
if (importOptions.webpackPreload === true) {
groupOptions.preloadOrder = 0;
} else if (typeof importOptions.webpackPreload === "number") {
groupOptions.preloadOrder = importOptions.webpackPreload;
} else {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackPreload\` expected true or a number, but received: ${importOptions.webpackPreload}.`,
expr.loc
)
);
}
}
if (importOptions.webpackInclude !== undefined) {
if (
!importOptions.webpackInclude ||
importOptions.webpackInclude.constructor.name !== "RegExp"
) {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackInclude\` expected a regular expression, but received: ${importOptions.webpackInclude}.`,
expr.loc
)
);
} else {
include = new RegExp(importOptions.webpackInclude);
}
}
if (importOptions.webpackExclude !== undefined) {
if (
!importOptions.webpackExclude ||
importOptions.webpackExclude.constructor.name !== "RegExp"
) {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackExclude\` expected a regular expression, but received: ${importOptions.webpackExclude}.`,
expr.loc
)
);
} else {
exclude = new RegExp(importOptions.webpackExclude);
}
}
if (importOptions.webpackExports !== undefined) {
if (
!(
typeof importOptions.webpackExports === "string" ||
(Array.isArray(importOptions.webpackExports) &&
importOptions.webpackExports.every(
item => typeof item === "string"
))
)
) {
parser.state.module.addWarning(
new UnsupportedFeatureWarning(
`\`webpackExports\` expected a string or an array of strings, but received: ${importOptions.webpackExports}.`,
expr.loc
)
);
} else {
if (typeof importOptions.webpackExports === "string") {
exports = [[importOptions.webpackExports]];
} else {
exports = Array.from(importOptions.webpackExports, e => [e]);
}
}
}
}
return { chunkName, mode, exports, include, exclude, groupOptions };
};