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>
426 lines
14 KiB
JavaScript
Executable File
426 lines
14 KiB
JavaScript
Executable File
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
Author Tobias Koppers @sokra
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const RuntimeGlobals = require("../RuntimeGlobals");
|
|
const formatLocation = require("../formatLocation");
|
|
const { evaluateToString } = require("../javascript/JavascriptParserHelpers");
|
|
const propertyAccess = require("../util/propertyAccess");
|
|
const CommonJsExportRequireDependency = require("./CommonJsExportRequireDependency");
|
|
const CommonJsExportsDependency = require("./CommonJsExportsDependency");
|
|
const CommonJsSelfReferenceDependency = require("./CommonJsSelfReferenceDependency");
|
|
const DynamicExports = require("./DynamicExports");
|
|
const HarmonyExports = require("./HarmonyExports");
|
|
const ModuleDecoratorDependency = require("./ModuleDecoratorDependency");
|
|
|
|
/** @typedef {import("estree").AssignmentExpression} AssignmentExpression */
|
|
/** @typedef {import("estree").CallExpression} CallExpression */
|
|
/** @typedef {import("estree").Expression} Expression */
|
|
/** @typedef {import("estree").Super} Super */
|
|
/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
|
|
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
|
/** @typedef {import("../ExportsInfo").ExportInfoName} ExportInfoName */
|
|
/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
|
|
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
|
|
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
|
/** @typedef {import("../javascript/JavascriptParser").Members} Members */
|
|
/** @typedef {import("../javascript/JavascriptParser").StatementPath} StatementPath */
|
|
/** @typedef {import("./CommonJsDependencyHelpers").CommonJSDependencyBaseKeywords} CommonJSDependencyBaseKeywords */
|
|
/** @typedef {import("../Module").BuildMeta} BuildMeta */
|
|
|
|
/**
|
|
* This function takes a generic expression and detects whether it is an ObjectExpression.
|
|
* This is used in the context of parsing CommonJS exports to get the value of the property descriptor
|
|
* when the `exports` object is assigned to `Object.defineProperty`.
|
|
*
|
|
* In CommonJS modules, the `exports` object can be assigned to `Object.defineProperty` and therefore
|
|
* webpack has to detect this case and get the value key of the property descriptor. See the following example
|
|
* for more information: https://astexplorer.net/#/gist/83ce51a4e96e59d777df315a6d111da6/8058ead48a1bb53c097738225db0967ef7f70e57
|
|
*
|
|
* This would be an example of a CommonJS module that exports an object with a property descriptor:
|
|
* ```js
|
|
* Object.defineProperty(exports, "__esModule", { value: true });
|
|
* exports.foo = void 0;
|
|
* exports.foo = "bar";
|
|
* ```
|
|
* @param {Expression} expr expression
|
|
* @returns {Expression | undefined} returns the value of property descriptor
|
|
*/
|
|
const getValueOfPropertyDescription = (expr) => {
|
|
if (expr.type !== "ObjectExpression") return;
|
|
for (const property of expr.properties) {
|
|
if (property.type === "SpreadElement" || property.computed) continue;
|
|
const key = property.key;
|
|
if (key.type !== "Identifier" || key.name !== "value") continue;
|
|
return /** @type {Expression} */ (property.value);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The purpose of this function is to check whether an expression is a truthy literal or not. This is
|
|
* useful when parsing CommonJS exports, because CommonJS modules can export any value, including falsy
|
|
* values like `null` and `false`. However, exports should only be created if the exported value is truthy.
|
|
* @param {Expression} expr expression being checked
|
|
* @returns {boolean} true, when the expression is a truthy literal
|
|
*/
|
|
const isTruthyLiteral = (expr) => {
|
|
switch (expr.type) {
|
|
case "Literal":
|
|
return Boolean(expr.value);
|
|
case "UnaryExpression":
|
|
if (expr.operator === "!") return isFalsyLiteral(expr.argument);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* The purpose of this function is to check whether an expression is a falsy literal or not. This is
|
|
* useful when parsing CommonJS exports, because CommonJS modules can export any value, including falsy
|
|
* values like `null` and `false`. However, exports should only be created if the exported value is truthy.
|
|
* @param {Expression} expr expression being checked
|
|
* @returns {boolean} true, when the expression is a falsy literal
|
|
*/
|
|
const isFalsyLiteral = (expr) => {
|
|
switch (expr.type) {
|
|
case "Literal":
|
|
return !expr.value;
|
|
case "UnaryExpression":
|
|
if (expr.operator === "!") return isTruthyLiteral(expr.argument);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* @param {JavascriptParser} parser the parser
|
|
* @param {Expression} expr expression
|
|
* @returns {{ argument: BasicEvaluatedExpression, ids: ExportInfoName[] } | undefined} parsed call
|
|
*/
|
|
const parseRequireCall = (parser, expr) => {
|
|
/** @type {ExportInfoName[]} */
|
|
const ids = [];
|
|
while (expr.type === "MemberExpression") {
|
|
if (expr.object.type === "Super") return;
|
|
if (!expr.property) return;
|
|
const prop = expr.property;
|
|
if (expr.computed) {
|
|
if (prop.type !== "Literal") return;
|
|
ids.push(`${prop.value}`);
|
|
} else {
|
|
if (prop.type !== "Identifier") return;
|
|
ids.push(prop.name);
|
|
}
|
|
expr = expr.object;
|
|
}
|
|
if (expr.type !== "CallExpression" || expr.arguments.length !== 1) return;
|
|
const callee = expr.callee;
|
|
if (
|
|
callee.type !== "Identifier" ||
|
|
parser.getVariableInfo(callee.name) !== "require"
|
|
) {
|
|
return;
|
|
}
|
|
const arg = expr.arguments[0];
|
|
if (arg.type === "SpreadElement") return;
|
|
const argValue = parser.evaluateExpression(arg);
|
|
return { argument: argValue, ids: ids.reverse() };
|
|
};
|
|
|
|
const PLUGIN_NAME = "CommonJsExportsParserPlugin";
|
|
|
|
class CommonJsExportsParserPlugin {
|
|
/**
|
|
* @param {ModuleGraph} moduleGraph module graph
|
|
*/
|
|
constructor(moduleGraph) {
|
|
this.moduleGraph = moduleGraph;
|
|
}
|
|
|
|
/**
|
|
* @param {JavascriptParser} parser the parser
|
|
* @returns {void}
|
|
*/
|
|
apply(parser) {
|
|
const enableStructuredExports = () => {
|
|
DynamicExports.enable(parser.state);
|
|
};
|
|
|
|
/**
|
|
* @param {boolean} topLevel true, when the export is on top level
|
|
* @param {Members} members members of the export
|
|
* @param {Expression | undefined} valueExpr expression for the value
|
|
* @returns {void}
|
|
*/
|
|
const checkNamespace = (topLevel, members, valueExpr) => {
|
|
if (!DynamicExports.isEnabled(parser.state)) return;
|
|
if (members.length > 0 && members[0] === "__esModule") {
|
|
if (valueExpr && isTruthyLiteral(valueExpr) && topLevel) {
|
|
DynamicExports.setFlagged(parser.state);
|
|
} else {
|
|
DynamicExports.setDynamic(parser.state);
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* @param {string=} reason reason
|
|
*/
|
|
const bailout = (reason) => {
|
|
DynamicExports.bailout(parser.state);
|
|
if (reason) bailoutHint(reason);
|
|
};
|
|
/**
|
|
* @param {string} reason reason
|
|
*/
|
|
const bailoutHint = (reason) => {
|
|
this.moduleGraph
|
|
.getOptimizationBailout(parser.state.module)
|
|
.push(`CommonJS bailout: ${reason}`);
|
|
};
|
|
|
|
// metadata //
|
|
parser.hooks.evaluateTypeof
|
|
.for("module")
|
|
.tap(PLUGIN_NAME, evaluateToString("object"));
|
|
parser.hooks.evaluateTypeof
|
|
.for("exports")
|
|
.tap(PLUGIN_NAME, evaluateToString("object"));
|
|
|
|
// exporting //
|
|
|
|
/**
|
|
* @param {AssignmentExpression} expr expression
|
|
* @param {CommonJSDependencyBaseKeywords} base commonjs base keywords
|
|
* @param {Members} members members of the export
|
|
* @returns {boolean | undefined} true, when the expression was handled
|
|
*/
|
|
const handleAssignExport = (expr, base, members) => {
|
|
if (HarmonyExports.isEnabled(parser.state)) return;
|
|
// Handle reexporting
|
|
const requireCall = parseRequireCall(parser, expr.right);
|
|
if (
|
|
requireCall &&
|
|
requireCall.argument.isString() &&
|
|
(members.length === 0 || members[0] !== "__esModule")
|
|
) {
|
|
enableStructuredExports();
|
|
// It's possible to reexport __esModule, so we must convert to a dynamic module
|
|
if (members.length === 0) DynamicExports.setDynamic(parser.state);
|
|
const dep = new CommonJsExportRequireDependency(
|
|
/** @type {Range} */ (expr.range),
|
|
null,
|
|
base,
|
|
members,
|
|
/** @type {string} */ (requireCall.argument.string),
|
|
requireCall.ids,
|
|
!parser.isStatementLevelExpression(expr)
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
dep.optional = Boolean(parser.scope.inTry);
|
|
parser.state.module.addDependency(dep);
|
|
/** @type {BuildMeta} */ (
|
|
parser.state.module.buildMeta
|
|
).treatAsCommonJs = true;
|
|
|
|
return true;
|
|
}
|
|
if (members.length === 0) return;
|
|
enableStructuredExports();
|
|
const remainingMembers = members;
|
|
checkNamespace(
|
|
/** @type {StatementPath} */
|
|
(parser.statementPath).length === 1 &&
|
|
parser.isStatementLevelExpression(expr),
|
|
remainingMembers,
|
|
expr.right
|
|
);
|
|
const dep = new CommonJsExportsDependency(
|
|
/** @type {Range} */ (expr.left.range),
|
|
null,
|
|
base,
|
|
remainingMembers
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addDependency(dep);
|
|
/** @type {BuildMeta} */ (parser.state.module.buildMeta).treatAsCommonJs =
|
|
true;
|
|
parser.walkExpression(expr.right);
|
|
return true;
|
|
};
|
|
parser.hooks.assignMemberChain
|
|
.for("exports")
|
|
.tap(PLUGIN_NAME, (expr, members) =>
|
|
handleAssignExport(expr, "exports", members)
|
|
);
|
|
parser.hooks.assignMemberChain
|
|
.for("this")
|
|
.tap(PLUGIN_NAME, (expr, members) => {
|
|
if (!parser.scope.topLevelScope) return;
|
|
return handleAssignExport(expr, "this", members);
|
|
});
|
|
parser.hooks.assignMemberChain
|
|
.for("module")
|
|
.tap(PLUGIN_NAME, (expr, members) => {
|
|
if (members[0] !== "exports") return;
|
|
return handleAssignExport(expr, "module.exports", members.slice(1));
|
|
});
|
|
parser.hooks.call
|
|
.for("Object.defineProperty")
|
|
.tap(PLUGIN_NAME, (expression) => {
|
|
const expr = /** @type {CallExpression} */ (expression);
|
|
if (!parser.isStatementLevelExpression(expr)) return;
|
|
if (expr.arguments.length !== 3) return;
|
|
if (expr.arguments[0].type === "SpreadElement") return;
|
|
if (expr.arguments[1].type === "SpreadElement") return;
|
|
if (expr.arguments[2].type === "SpreadElement") return;
|
|
const exportsArg = parser.evaluateExpression(expr.arguments[0]);
|
|
if (!exportsArg.isIdentifier()) return;
|
|
if (
|
|
exportsArg.identifier !== "exports" &&
|
|
exportsArg.identifier !== "module.exports" &&
|
|
(exportsArg.identifier !== "this" || !parser.scope.topLevelScope)
|
|
) {
|
|
return;
|
|
}
|
|
const propertyArg = parser.evaluateExpression(expr.arguments[1]);
|
|
const property = propertyArg.asString();
|
|
if (typeof property !== "string") return;
|
|
enableStructuredExports();
|
|
const descArg = expr.arguments[2];
|
|
checkNamespace(
|
|
/** @type {StatementPath} */
|
|
(parser.statementPath).length === 1,
|
|
[property],
|
|
getValueOfPropertyDescription(descArg)
|
|
);
|
|
const dep = new CommonJsExportsDependency(
|
|
/** @type {Range} */ (expr.range),
|
|
/** @type {Range} */ (expr.arguments[2].range),
|
|
`Object.defineProperty(${exportsArg.identifier})`,
|
|
[property]
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addDependency(dep);
|
|
/** @type {BuildMeta} */ (
|
|
parser.state.module.buildMeta
|
|
).treatAsCommonJs = true;
|
|
|
|
parser.walkExpression(expr.arguments[2]);
|
|
return true;
|
|
});
|
|
|
|
// Self reference //
|
|
|
|
/**
|
|
* @param {Expression | Super} expr expression
|
|
* @param {CommonJSDependencyBaseKeywords} base commonjs base keywords
|
|
* @param {Members} members members of the export
|
|
* @param {CallExpression=} call call expression
|
|
* @returns {boolean | void} true, when the expression was handled
|
|
*/
|
|
const handleAccessExport = (expr, base, members, call) => {
|
|
if (HarmonyExports.isEnabled(parser.state)) return;
|
|
if (members.length === 0) {
|
|
bailout(
|
|
`${base} is used directly at ${formatLocation(
|
|
/** @type {DependencyLocation} */ (expr.loc)
|
|
)}`
|
|
);
|
|
}
|
|
if (call && members.length === 1) {
|
|
bailoutHint(
|
|
`${base}${propertyAccess(
|
|
members
|
|
)}(...) prevents optimization as ${base} is passed as call context at ${formatLocation(
|
|
/** @type {DependencyLocation} */ (expr.loc)
|
|
)}`
|
|
);
|
|
}
|
|
const dep = new CommonJsSelfReferenceDependency(
|
|
/** @type {Range} */ (expr.range),
|
|
base,
|
|
members,
|
|
Boolean(call)
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addDependency(dep);
|
|
/** @type {BuildMeta} */ (parser.state.module.buildMeta).treatAsCommonJs =
|
|
true;
|
|
|
|
if (call) {
|
|
parser.walkExpressions(call.arguments);
|
|
}
|
|
return true;
|
|
};
|
|
parser.hooks.callMemberChain
|
|
.for("exports")
|
|
.tap(PLUGIN_NAME, (expr, members) =>
|
|
handleAccessExport(expr.callee, "exports", members, expr)
|
|
);
|
|
parser.hooks.expressionMemberChain
|
|
.for("exports")
|
|
.tap(PLUGIN_NAME, (expr, members) =>
|
|
handleAccessExport(expr, "exports", members)
|
|
);
|
|
parser.hooks.expression
|
|
.for("exports")
|
|
.tap(PLUGIN_NAME, (expr) => handleAccessExport(expr, "exports", []));
|
|
parser.hooks.callMemberChain
|
|
.for("module")
|
|
.tap(PLUGIN_NAME, (expr, members) => {
|
|
if (members[0] !== "exports") return;
|
|
return handleAccessExport(
|
|
expr.callee,
|
|
"module.exports",
|
|
members.slice(1),
|
|
expr
|
|
);
|
|
});
|
|
parser.hooks.expressionMemberChain
|
|
.for("module")
|
|
.tap(PLUGIN_NAME, (expr, members) => {
|
|
if (members[0] !== "exports") return;
|
|
return handleAccessExport(expr, "module.exports", members.slice(1));
|
|
});
|
|
parser.hooks.expression
|
|
.for("module.exports")
|
|
.tap(PLUGIN_NAME, (expr) =>
|
|
handleAccessExport(expr, "module.exports", [])
|
|
);
|
|
parser.hooks.callMemberChain
|
|
.for("this")
|
|
.tap(PLUGIN_NAME, (expr, members) => {
|
|
if (!parser.scope.topLevelScope) return;
|
|
return handleAccessExport(expr.callee, "this", members, expr);
|
|
});
|
|
parser.hooks.expressionMemberChain
|
|
.for("this")
|
|
.tap(PLUGIN_NAME, (expr, members) => {
|
|
if (!parser.scope.topLevelScope) return;
|
|
return handleAccessExport(expr, "this", members);
|
|
});
|
|
parser.hooks.expression.for("this").tap(PLUGIN_NAME, (expr) => {
|
|
if (!parser.scope.topLevelScope) return;
|
|
return handleAccessExport(expr, "this", []);
|
|
});
|
|
|
|
// Bailouts //
|
|
parser.hooks.expression.for("module").tap(PLUGIN_NAME, (expr) => {
|
|
bailout();
|
|
const isHarmony = HarmonyExports.isEnabled(parser.state);
|
|
const dep = new ModuleDecoratorDependency(
|
|
isHarmony
|
|
? RuntimeGlobals.harmonyModuleDecorator
|
|
: RuntimeGlobals.nodeModuleDecorator,
|
|
!isHarmony
|
|
);
|
|
dep.loc = /** @type {DependencyLocation} */ (expr.loc);
|
|
parser.state.module.addDependency(dep);
|
|
return true;
|
|
});
|
|
}
|
|
}
|
|
|
|
module.exports = CommonJsExportsParserPlugin;
|