Add semantic token highlighting for 'that' variable and comment file references in VS Code extension Add Phone_Text_Input and Currency_Input components with formatting utilities Implement client widgets, form standardization, and soft delete functionality Add modal scroll lock and update documentation Implement comprehensive modal system with form integration and validation Fix modal component instantiation using jQuery plugin API Implement modal system with responsive sizing, queuing, and validation support Implement form submission with validation, error handling, and loading states Implement country/state selectors with dynamic data loading and Bootstrap styling Revert Rsx::Route() highlighting in Blade/PHP files Target specific PHP scopes for Rsx::Route() highlighting in Blade Expand injection selector for Rsx::Route() highlighting Add custom syntax highlighting for Rsx::Route() and Rsx.Route() calls Update jqhtml packages to v2.2.165 Add bundle path validation for common mistakes (development mode only) Create Ajax_Select_Input widget and Rsx_Reference_Data controller Create Country_Select_Input widget with default country support Initialize Tom Select on Select_Input widgets Add Tom Select bundle for enhanced select dropdowns Implement ISO 3166 geographic data system for country/region selection Implement widget-based form system with disabled state support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
812 lines
25 KiB
JavaScript
Executable File
812 lines
25 KiB
JavaScript
Executable File
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
Author Tobias Koppers @sokra
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const { fileURLToPath } = require("url");
|
|
const CommentCompilationWarning = require("../CommentCompilationWarning");
|
|
const RuntimeGlobals = require("../RuntimeGlobals");
|
|
const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
|
|
const WebpackError = require("../WebpackError");
|
|
const BasicEvaluatedExpression = require("../javascript/BasicEvaluatedExpression");
|
|
const { VariableInfo } = require("../javascript/JavascriptParser");
|
|
const {
|
|
evaluateToIdentifier,
|
|
evaluateToString,
|
|
expressionIsUnsupported,
|
|
toConstantDependency
|
|
} = require("../javascript/JavascriptParserHelpers");
|
|
const CommonJsFullRequireDependency = require("./CommonJsFullRequireDependency");
|
|
const CommonJsRequireContextDependency = require("./CommonJsRequireContextDependency");
|
|
const CommonJsRequireDependency = require("./CommonJsRequireDependency");
|
|
const ConstDependency = require("./ConstDependency");
|
|
const ContextDependencyHelpers = require("./ContextDependencyHelpers");
|
|
const LocalModuleDependency = require("./LocalModuleDependency");
|
|
const { getLocalModule } = require("./LocalModulesHelpers");
|
|
const RequireHeaderDependency = require("./RequireHeaderDependency");
|
|
const RequireResolveContextDependency = require("./RequireResolveContextDependency");
|
|
const RequireResolveDependency = require("./RequireResolveDependency");
|
|
const RequireResolveHeaderDependency = require("./RequireResolveHeaderDependency");
|
|
|
|
/** @typedef {import("estree").CallExpression} CallExpression */
|
|
/** @typedef {import("estree").Expression} Expression */
|
|
/** @typedef {import("estree").NewExpression} NewExpression */
|
|
/** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
|
|
/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
|
|
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
|
|
/** @typedef {import("../javascript/JavascriptParser").ImportSource} ImportSource */
|
|
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
|
/** @typedef {import("../javascript/JavascriptParser").Members} Members */
|
|
/** @typedef {import("../javascript/JavascriptParser").CalleeMembers} CalleeMembers */
|
|
|
|
/**
|
|
* @typedef {object} CommonJsImportSettings
|
|
* @property {string=} name
|
|
* @property {string} context
|
|
*/
|
|
|
|
const createRequireSpecifierTag = Symbol("createRequire");
|
|
const createdRequireIdentifierTag = Symbol("createRequire()");
|
|
|
|
const PLUGIN_NAME = "CommonJsImportsParserPlugin";
|
|
|
|
class CommonJsImportsParserPlugin {
|
|
/**
|
|
* @param {JavascriptParserOptions} options parser options
|
|
*/
|
|
constructor(options) {
|
|
this.options = options;
|
|
}
|
|
|
|
/**
|
|
* @param {JavascriptParser} parser the parser
|
|
* @returns {void}
|
|
*/
|
|
apply(parser) {
|
|
const options = this.options;
|
|
const getContext = () => {
|
|
if (parser.currentTagData) {
|
|
const { context } =
|
|
/** @type {CommonJsImportSettings} */
|
|
(parser.currentTagData);
|
|
return context;
|
|
}
|
|
};
|
|
|
|
// #region metadata
|
|
/**
|
|
* @param {string} expression expression
|
|
* @param {() => Members} getMembers get members
|
|
*/
|
|
const tapRequireExpression = (expression, getMembers) => {
|
|
parser.hooks.typeof
|
|
.for(expression)
|
|
.tap(
|
|
PLUGIN_NAME,
|
|
toConstantDependency(parser, JSON.stringify("function"))
|
|
);
|
|
parser.hooks.evaluateTypeof
|
|
.for(expression)
|
|
.tap(PLUGIN_NAME, evaluateToString("function"));
|
|
parser.hooks.evaluateIdentifier
|
|
.for(expression)
|
|
.tap(
|
|
PLUGIN_NAME,
|
|
evaluateToIdentifier(expression, "require", getMembers, true)
|
|
);
|
|
};
|
|
/**
|
|
* @param {string | symbol} tag tag
|
|
*/
|
|
const tapRequireExpressionTag = (tag) => {
|
|
parser.hooks.typeof
|
|
.for(tag)
|
|
.tap(
|
|
PLUGIN_NAME,
|
|
toConstantDependency(parser, JSON.stringify("function"))
|
|
);
|
|
parser.hooks.evaluateTypeof
|
|
.for(tag)
|
|
.tap(PLUGIN_NAME, evaluateToString("function"));
|
|
};
|
|
tapRequireExpression("require", () => []);
|
|
tapRequireExpression("require.resolve", () => ["resolve"]);
|
|
tapRequireExpression("require.resolveWeak", () => ["resolveWeak"]);
|
|
// #endregion
|
|
|
|
// Weird stuff //
|
|
parser.hooks.assign.for("require").tap(PLUGIN_NAME, (expr) => {
|
|
// to not leak to global "require", we need to define a local require here.
|
|
const dep = new ConstDependency("var require;", 0);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addPresentationalDependency(dep);
|
|
return true;
|
|
});
|
|
|
|
// #region Unsupported
|
|
parser.hooks.expression
|
|
.for("require.main")
|
|
.tap(
|
|
PLUGIN_NAME,
|
|
expressionIsUnsupported(
|
|
parser,
|
|
"require.main is not supported by webpack."
|
|
)
|
|
);
|
|
parser.hooks.call
|
|
.for("require.main.require")
|
|
.tap(
|
|
PLUGIN_NAME,
|
|
expressionIsUnsupported(
|
|
parser,
|
|
"require.main.require is not supported by webpack."
|
|
)
|
|
);
|
|
parser.hooks.expression
|
|
.for("module.parent.require")
|
|
.tap(
|
|
PLUGIN_NAME,
|
|
expressionIsUnsupported(
|
|
parser,
|
|
"module.parent.require is not supported by webpack."
|
|
)
|
|
);
|
|
parser.hooks.call
|
|
.for("module.parent.require")
|
|
.tap(
|
|
PLUGIN_NAME,
|
|
expressionIsUnsupported(
|
|
parser,
|
|
"module.parent.require is not supported by webpack."
|
|
)
|
|
);
|
|
// #endregion
|
|
|
|
// #region Renaming
|
|
/**
|
|
* @param {Expression} expr expression
|
|
* @returns {boolean} true when set undefined
|
|
*/
|
|
const defineUndefined = (expr) => {
|
|
// To avoid "not defined" error, replace the value with undefined
|
|
const dep = new ConstDependency(
|
|
"undefined",
|
|
/** @type {Range} */ (expr.range)
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addPresentationalDependency(dep);
|
|
return false;
|
|
};
|
|
parser.hooks.canRename.for("require").tap(PLUGIN_NAME, () => true);
|
|
parser.hooks.rename.for("require").tap(PLUGIN_NAME, defineUndefined);
|
|
// #endregion
|
|
|
|
// #region Inspection
|
|
const requireCache = toConstantDependency(
|
|
parser,
|
|
RuntimeGlobals.moduleCache,
|
|
[
|
|
RuntimeGlobals.moduleCache,
|
|
RuntimeGlobals.moduleId,
|
|
RuntimeGlobals.moduleLoaded
|
|
]
|
|
);
|
|
|
|
parser.hooks.expression.for("require.cache").tap(PLUGIN_NAME, requireCache);
|
|
// #endregion
|
|
|
|
// #region Require as expression
|
|
/**
|
|
* @param {Expression} expr expression
|
|
* @returns {boolean} true when handled
|
|
*/
|
|
const requireAsExpressionHandler = (expr) => {
|
|
const dep = new CommonJsRequireContextDependency(
|
|
{
|
|
request: /** @type {string} */ (options.unknownContextRequest),
|
|
recursive: /** @type {boolean} */ (options.unknownContextRecursive),
|
|
regExp: /** @type {RegExp} */ (options.unknownContextRegExp),
|
|
mode: "sync"
|
|
},
|
|
/** @type {Range} */ (expr.range),
|
|
undefined,
|
|
parser.scope.inShorthand,
|
|
getContext()
|
|
);
|
|
dep.critical =
|
|
options.unknownContextCritical &&
|
|
"require function is used in a way in which dependencies cannot be statically extracted";
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
dep.optional = Boolean(parser.scope.inTry);
|
|
parser.state.current.addDependency(dep);
|
|
return true;
|
|
};
|
|
parser.hooks.expression
|
|
.for("require")
|
|
.tap(PLUGIN_NAME, requireAsExpressionHandler);
|
|
// #endregion
|
|
|
|
// #region Require
|
|
/**
|
|
* @param {CallExpression | NewExpression} expr expression
|
|
* @param {BasicEvaluatedExpression} param param
|
|
* @returns {boolean | void} true when handled
|
|
*/
|
|
const processRequireItem = (expr, param) => {
|
|
if (param.isString()) {
|
|
const dep = new CommonJsRequireDependency(
|
|
/** @type {string} */ (param.string),
|
|
/** @type {Range} */ (param.range),
|
|
getContext()
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
dep.optional = Boolean(parser.scope.inTry);
|
|
parser.state.current.addDependency(dep);
|
|
return true;
|
|
}
|
|
};
|
|
/**
|
|
* @param {CallExpression | NewExpression} expr expression
|
|
* @param {BasicEvaluatedExpression} param param
|
|
* @returns {boolean | void} true when handled
|
|
*/
|
|
const processRequireContext = (expr, param) => {
|
|
const dep = ContextDependencyHelpers.create(
|
|
CommonJsRequireContextDependency,
|
|
/** @type {Range} */ (expr.range),
|
|
param,
|
|
expr,
|
|
options,
|
|
{
|
|
category: "commonjs"
|
|
},
|
|
parser,
|
|
undefined,
|
|
getContext()
|
|
);
|
|
if (!dep) return;
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
dep.optional = Boolean(parser.scope.inTry);
|
|
parser.state.current.addDependency(dep);
|
|
return true;
|
|
};
|
|
/**
|
|
* @param {boolean} callNew true, when require is called with new
|
|
* @returns {(expr: CallExpression | NewExpression) => (boolean | void)} handler
|
|
*/
|
|
const createRequireHandler = (callNew) => (expr) => {
|
|
if (options.commonjsMagicComments) {
|
|
const { options: requireOptions, errors: commentErrors } =
|
|
parser.parseCommentOptions(/** @type {Range} */ (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}`,
|
|
/** @type {DependencyLocation} */ (comment.loc)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
if (requireOptions && requireOptions.webpackIgnore !== undefined) {
|
|
if (typeof requireOptions.webpackIgnore !== "boolean") {
|
|
parser.state.module.addWarning(
|
|
new UnsupportedFeatureWarning(
|
|
`\`webpackIgnore\` expected a boolean, but received: ${requireOptions.webpackIgnore}.`,
|
|
/** @type {DependencyLocation} */ (expr.loc)
|
|
)
|
|
);
|
|
} else if (requireOptions.webpackIgnore) {
|
|
// Do not instrument `require()` if `webpackIgnore` is `true`
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (expr.arguments.length !== 1) return;
|
|
let localModule;
|
|
const param = parser.evaluateExpression(expr.arguments[0]);
|
|
if (param.isConditional()) {
|
|
let isExpression = false;
|
|
for (const p of /** @type {BasicEvaluatedExpression[]} */ (
|
|
param.options
|
|
)) {
|
|
const result = processRequireItem(expr, p);
|
|
if (result === undefined) {
|
|
isExpression = true;
|
|
}
|
|
}
|
|
if (!isExpression) {
|
|
const dep = new RequireHeaderDependency(
|
|
/** @type {Range} */ (expr.callee.range)
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addPresentationalDependency(dep);
|
|
return true;
|
|
}
|
|
}
|
|
if (
|
|
param.isString() &&
|
|
(localModule = getLocalModule(
|
|
parser.state,
|
|
/** @type {string} */ (param.string)
|
|
))
|
|
) {
|
|
localModule.flagUsed();
|
|
const dep = new LocalModuleDependency(
|
|
localModule,
|
|
/** @type {Range} */ (expr.range),
|
|
callNew
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addPresentationalDependency(dep);
|
|
} else {
|
|
const result = processRequireItem(expr, param);
|
|
if (result === undefined) {
|
|
processRequireContext(expr, param);
|
|
} else {
|
|
const dep = new RequireHeaderDependency(
|
|
/** @type {Range} */ (expr.callee.range)
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addPresentationalDependency(dep);
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
parser.hooks.call
|
|
.for("require")
|
|
.tap(PLUGIN_NAME, createRequireHandler(false));
|
|
parser.hooks.new
|
|
.for("require")
|
|
.tap(PLUGIN_NAME, createRequireHandler(true));
|
|
parser.hooks.call
|
|
.for("module.require")
|
|
.tap(PLUGIN_NAME, createRequireHandler(false));
|
|
parser.hooks.new
|
|
.for("module.require")
|
|
.tap(PLUGIN_NAME, createRequireHandler(true));
|
|
// #endregion
|
|
|
|
// #region Require with property access
|
|
/**
|
|
* @param {Expression} expr expression
|
|
* @param {CalleeMembers} calleeMembers callee members
|
|
* @param {CallExpression} callExpr call expression
|
|
* @param {Members} members members
|
|
* @param {Range[]} memberRanges member ranges
|
|
* @returns {boolean | void} true when handled
|
|
*/
|
|
const chainHandler = (
|
|
expr,
|
|
calleeMembers,
|
|
callExpr,
|
|
members,
|
|
memberRanges
|
|
) => {
|
|
if (callExpr.arguments.length !== 1) return;
|
|
const param = parser.evaluateExpression(callExpr.arguments[0]);
|
|
if (
|
|
param.isString() &&
|
|
!getLocalModule(parser.state, /** @type {string} */ (param.string))
|
|
) {
|
|
const dep = new CommonJsFullRequireDependency(
|
|
/** @type {string} */ (param.string),
|
|
/** @type {Range} */ (expr.range),
|
|
members,
|
|
/** @type {Range[]} */ memberRanges
|
|
);
|
|
dep.asiSafe = !parser.isAsiPosition(
|
|
/** @type {Range} */ (expr.range)[0]
|
|
);
|
|
dep.optional = Boolean(parser.scope.inTry);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.current.addDependency(dep);
|
|
return true;
|
|
}
|
|
};
|
|
/**
|
|
* @param {CallExpression} expr expression
|
|
* @param {CalleeMembers} calleeMembers callee members
|
|
* @param {CallExpression} callExpr call expression
|
|
* @param {Members} members members
|
|
* @param {Range[]} memberRanges member ranges
|
|
* @returns {boolean | void} true when handled
|
|
*/
|
|
const callChainHandler = (
|
|
expr,
|
|
calleeMembers,
|
|
callExpr,
|
|
members,
|
|
memberRanges
|
|
) => {
|
|
if (callExpr.arguments.length !== 1) return;
|
|
const param = parser.evaluateExpression(callExpr.arguments[0]);
|
|
if (
|
|
param.isString() &&
|
|
!getLocalModule(parser.state, /** @type {string} */ (param.string))
|
|
) {
|
|
const dep = new CommonJsFullRequireDependency(
|
|
/** @type {string} */ (param.string),
|
|
/** @type {Range} */ (expr.callee.range),
|
|
members,
|
|
/** @type {Range[]} */ memberRanges
|
|
);
|
|
dep.call = true;
|
|
dep.asiSafe = !parser.isAsiPosition(
|
|
/** @type {Range} */ (expr.range)[0]
|
|
);
|
|
dep.optional = Boolean(parser.scope.inTry);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.callee.loc);
|
|
parser.state.current.addDependency(dep);
|
|
parser.walkExpressions(expr.arguments);
|
|
return true;
|
|
}
|
|
};
|
|
parser.hooks.memberChainOfCallMemberChain
|
|
.for("require")
|
|
.tap(PLUGIN_NAME, chainHandler);
|
|
parser.hooks.memberChainOfCallMemberChain
|
|
.for("module.require")
|
|
.tap(PLUGIN_NAME, chainHandler);
|
|
parser.hooks.callMemberChainOfCallMemberChain
|
|
.for("require")
|
|
.tap(PLUGIN_NAME, callChainHandler);
|
|
parser.hooks.callMemberChainOfCallMemberChain
|
|
.for("module.require")
|
|
.tap(PLUGIN_NAME, callChainHandler);
|
|
// #endregion
|
|
|
|
// #region Require.resolve
|
|
/**
|
|
* @param {CallExpression} expr call expression
|
|
* @param {boolean} weak weak
|
|
* @returns {boolean | void} true when handled
|
|
*/
|
|
const processResolve = (expr, weak) => {
|
|
if (!weak && options.commonjsMagicComments) {
|
|
const { options: requireOptions, errors: commentErrors } =
|
|
parser.parseCommentOptions(/** @type {Range} */ (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}`,
|
|
/** @type {DependencyLocation} */ (comment.loc)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
if (requireOptions && requireOptions.webpackIgnore !== undefined) {
|
|
if (typeof requireOptions.webpackIgnore !== "boolean") {
|
|
parser.state.module.addWarning(
|
|
new UnsupportedFeatureWarning(
|
|
`\`webpackIgnore\` expected a boolean, but received: ${requireOptions.webpackIgnore}.`,
|
|
/** @type {DependencyLocation} */ (expr.loc)
|
|
)
|
|
);
|
|
} else if (requireOptions.webpackIgnore) {
|
|
// Do not instrument `require()` if `webpackIgnore` is `true`
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (expr.arguments.length !== 1) return;
|
|
const param = parser.evaluateExpression(expr.arguments[0]);
|
|
if (param.isConditional()) {
|
|
for (const option of /** @type {BasicEvaluatedExpression[]} */ (
|
|
param.options
|
|
)) {
|
|
const result = processResolveItem(expr, option, weak);
|
|
if (result === undefined) {
|
|
processResolveContext(expr, option, weak);
|
|
}
|
|
}
|
|
const dep = new RequireResolveHeaderDependency(
|
|
/** @type {Range} */ (expr.callee.range)
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addPresentationalDependency(dep);
|
|
return true;
|
|
}
|
|
const result = processResolveItem(expr, param, weak);
|
|
if (result === undefined) {
|
|
processResolveContext(expr, param, weak);
|
|
}
|
|
const dep = new RequireResolveHeaderDependency(
|
|
/** @type {Range} */ (expr.callee.range)
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addPresentationalDependency(dep);
|
|
return true;
|
|
};
|
|
/**
|
|
* @param {CallExpression} expr call expression
|
|
* @param {BasicEvaluatedExpression} param param
|
|
* @param {boolean} weak weak
|
|
* @returns {boolean | void} true when handled
|
|
*/
|
|
const processResolveItem = (expr, param, weak) => {
|
|
if (param.isString()) {
|
|
const dep = new RequireResolveDependency(
|
|
/** @type {string} */ (param.string),
|
|
/** @type {Range} */ (param.range),
|
|
getContext()
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
dep.optional = Boolean(parser.scope.inTry);
|
|
dep.weak = weak;
|
|
parser.state.current.addDependency(dep);
|
|
return true;
|
|
}
|
|
};
|
|
/**
|
|
* @param {CallExpression} expr call expression
|
|
* @param {BasicEvaluatedExpression} param param
|
|
* @param {boolean} weak weak
|
|
* @returns {boolean | void} true when handled
|
|
*/
|
|
const processResolveContext = (expr, param, weak) => {
|
|
const dep = ContextDependencyHelpers.create(
|
|
RequireResolveContextDependency,
|
|
/** @type {Range} */ (param.range),
|
|
param,
|
|
expr,
|
|
options,
|
|
{
|
|
category: "commonjs",
|
|
mode: weak ? "weak" : "sync"
|
|
},
|
|
parser,
|
|
getContext()
|
|
);
|
|
if (!dep) return;
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
dep.optional = Boolean(parser.scope.inTry);
|
|
parser.state.current.addDependency(dep);
|
|
return true;
|
|
};
|
|
|
|
parser.hooks.call
|
|
.for("require.resolve")
|
|
.tap(PLUGIN_NAME, (expr) => processResolve(expr, false));
|
|
parser.hooks.call
|
|
.for("require.resolveWeak")
|
|
.tap(PLUGIN_NAME, (expr) => processResolve(expr, true));
|
|
// #endregion
|
|
|
|
// #region Create require
|
|
|
|
if (!options.createRequire) return;
|
|
|
|
/** @type {ImportSource[]} */
|
|
let moduleName = [];
|
|
/** @type {string | undefined} */
|
|
let specifierName;
|
|
|
|
if (options.createRequire === true) {
|
|
moduleName = ["module", "node:module"];
|
|
specifierName = "createRequire";
|
|
} else {
|
|
let moduleName;
|
|
const match = /^(.*) from (.*)$/.exec(options.createRequire);
|
|
if (match) {
|
|
[, specifierName, moduleName] = match;
|
|
}
|
|
if (!specifierName || !moduleName) {
|
|
const err = new WebpackError(
|
|
`Parsing javascript parser option "createRequire" failed, got ${JSON.stringify(
|
|
options.createRequire
|
|
)}`
|
|
);
|
|
err.details =
|
|
'Expected string in format "createRequire from module", where "createRequire" is specifier name and "module" name of the module';
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
tapRequireExpressionTag(createdRequireIdentifierTag);
|
|
tapRequireExpressionTag(createRequireSpecifierTag);
|
|
parser.hooks.evaluateCallExpression
|
|
.for(createRequireSpecifierTag)
|
|
.tap(PLUGIN_NAME, (expr) => {
|
|
const context = parseCreateRequireArguments(expr);
|
|
if (context === undefined) return;
|
|
const ident = parser.evaluatedVariable({
|
|
tag: createdRequireIdentifierTag,
|
|
data: { context },
|
|
next: undefined
|
|
});
|
|
|
|
return new BasicEvaluatedExpression()
|
|
.setIdentifier(ident, ident, () => [])
|
|
.setSideEffects(false)
|
|
.setRange(/** @type {Range} */ (expr.range));
|
|
});
|
|
parser.hooks.unhandledExpressionMemberChain
|
|
.for(createdRequireIdentifierTag)
|
|
.tap(PLUGIN_NAME, (expr, members) =>
|
|
expressionIsUnsupported(
|
|
parser,
|
|
`createRequire().${members.join(".")} is not supported by webpack.`
|
|
)(expr)
|
|
);
|
|
parser.hooks.canRename
|
|
.for(createdRequireIdentifierTag)
|
|
.tap(PLUGIN_NAME, () => true);
|
|
parser.hooks.canRename
|
|
.for(createRequireSpecifierTag)
|
|
.tap(PLUGIN_NAME, () => true);
|
|
parser.hooks.rename
|
|
.for(createRequireSpecifierTag)
|
|
.tap(PLUGIN_NAME, defineUndefined);
|
|
parser.hooks.expression
|
|
.for(createdRequireIdentifierTag)
|
|
.tap(PLUGIN_NAME, requireAsExpressionHandler);
|
|
parser.hooks.call
|
|
.for(createdRequireIdentifierTag)
|
|
.tap(PLUGIN_NAME, createRequireHandler(false));
|
|
/**
|
|
* @param {CallExpression} expr call expression
|
|
* @returns {string | void} context
|
|
*/
|
|
const parseCreateRequireArguments = (expr) => {
|
|
const args = expr.arguments;
|
|
if (args.length !== 1) {
|
|
const err = new WebpackError(
|
|
"module.createRequire supports only one argument."
|
|
);
|
|
err.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addWarning(err);
|
|
return;
|
|
}
|
|
const arg = args[0];
|
|
const evaluated = parser.evaluateExpression(arg);
|
|
if (!evaluated.isString()) {
|
|
const err = new WebpackError(
|
|
"module.createRequire failed parsing argument."
|
|
);
|
|
err.loc = /** @type {DependencyLocation} */ (arg.loc);
|
|
parser.state.module.addWarning(err);
|
|
return;
|
|
}
|
|
const ctx = /** @type {string} */ (evaluated.string).startsWith("file://")
|
|
? fileURLToPath(/** @type {string} */ (evaluated.string))
|
|
: /** @type {string} */ (evaluated.string);
|
|
// argument always should be a filename
|
|
return ctx.slice(0, ctx.lastIndexOf(ctx.startsWith("/") ? "/" : "\\"));
|
|
};
|
|
|
|
parser.hooks.import.tap(
|
|
{
|
|
name: PLUGIN_NAME,
|
|
stage: -10
|
|
},
|
|
(statement, source) => {
|
|
if (
|
|
!moduleName.includes(source) ||
|
|
statement.specifiers.length !== 1 ||
|
|
statement.specifiers[0].type !== "ImportSpecifier" ||
|
|
statement.specifiers[0].imported.type !== "Identifier" ||
|
|
statement.specifiers[0].imported.name !== specifierName
|
|
) {
|
|
return;
|
|
}
|
|
// clear for 'import { createRequire as x } from "module"'
|
|
// if any other specifier was used import module
|
|
const clearDep = new ConstDependency(
|
|
parser.isAsiPosition(/** @type {Range} */ (statement.range)[0])
|
|
? ";"
|
|
: "",
|
|
/** @type {Range} */ (statement.range)
|
|
);
|
|
clearDep.loc = /** @type {DependencyLocation} */ (statement.loc);
|
|
parser.state.module.addPresentationalDependency(clearDep);
|
|
parser.unsetAsiPosition(/** @type {Range} */ (statement.range)[1]);
|
|
return true;
|
|
}
|
|
);
|
|
parser.hooks.importSpecifier.tap(
|
|
{
|
|
name: PLUGIN_NAME,
|
|
stage: -10
|
|
},
|
|
(statement, source, id, name) => {
|
|
if (!moduleName.includes(source) || id !== specifierName) return;
|
|
parser.tagVariable(name, createRequireSpecifierTag);
|
|
return true;
|
|
}
|
|
);
|
|
parser.hooks.preDeclarator.tap(PLUGIN_NAME, (declarator) => {
|
|
if (
|
|
declarator.id.type !== "Identifier" ||
|
|
!declarator.init ||
|
|
declarator.init.type !== "CallExpression" ||
|
|
declarator.init.callee.type !== "Identifier"
|
|
) {
|
|
return;
|
|
}
|
|
const variableInfo = parser.getVariableInfo(declarator.init.callee.name);
|
|
if (
|
|
variableInfo instanceof VariableInfo &&
|
|
variableInfo.tagInfo &&
|
|
variableInfo.tagInfo.tag === createRequireSpecifierTag
|
|
) {
|
|
const context = parseCreateRequireArguments(declarator.init);
|
|
if (context === undefined) return;
|
|
parser.tagVariable(declarator.id.name, createdRequireIdentifierTag, {
|
|
name: declarator.id.name,
|
|
context
|
|
});
|
|
return true;
|
|
}
|
|
});
|
|
|
|
parser.hooks.memberChainOfCallMemberChain
|
|
.for(createRequireSpecifierTag)
|
|
.tap(PLUGIN_NAME, (expr, calleeMembers, callExpr, members) => {
|
|
if (
|
|
calleeMembers.length !== 0 ||
|
|
members.length !== 1 ||
|
|
members[0] !== "cache"
|
|
) {
|
|
return;
|
|
}
|
|
// createRequire().cache
|
|
const context = parseCreateRequireArguments(callExpr);
|
|
if (context === undefined) return;
|
|
return requireCache(expr);
|
|
});
|
|
parser.hooks.callMemberChainOfCallMemberChain
|
|
.for(createRequireSpecifierTag)
|
|
.tap(PLUGIN_NAME, (expr, calleeMembers, innerCallExpression, members) => {
|
|
if (
|
|
calleeMembers.length !== 0 ||
|
|
members.length !== 1 ||
|
|
members[0] !== "resolve"
|
|
) {
|
|
return;
|
|
}
|
|
// createRequire().resolve()
|
|
return processResolve(expr, false);
|
|
});
|
|
parser.hooks.expressionMemberChain
|
|
.for(createdRequireIdentifierTag)
|
|
.tap(PLUGIN_NAME, (expr, members) => {
|
|
// require.cache
|
|
if (members.length === 1 && members[0] === "cache") {
|
|
return requireCache(expr);
|
|
}
|
|
});
|
|
parser.hooks.callMemberChain
|
|
.for(createdRequireIdentifierTag)
|
|
.tap(PLUGIN_NAME, (expr, members) => {
|
|
// require.resolve()
|
|
if (members.length === 1 && members[0] === "resolve") {
|
|
return processResolve(expr, false);
|
|
}
|
|
});
|
|
parser.hooks.call
|
|
.for(createRequireSpecifierTag)
|
|
.tap(PLUGIN_NAME, (expr) => {
|
|
const clearDep = new ConstDependency(
|
|
"/* createRequire() */ undefined",
|
|
/** @type {Range} */ (expr.range)
|
|
);
|
|
clearDep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addPresentationalDependency(clearDep);
|
|
return true;
|
|
});
|
|
// #endregion
|
|
}
|
|
}
|
|
|
|
module.exports = CommonJsImportsParserPlugin;
|