Implement JQHTML function cache ID system and fix bundle compilation Implement underscore prefix for system tables Fix JS syntax linter to support decorators and grant exception to Task system SPA: Update planning docs and wishlists with remaining features SPA: Document Navigation API abandonment and future enhancements Implement SPA browser integration with History API (Phase 1) Convert contacts view page to SPA action Convert clients pages to SPA actions and document conversion procedure SPA: Merge GET parameters and update documentation Implement SPA route URL generation in JavaScript and PHP Implement SPA bootstrap controller architecture Add SPA routing manual page (rsx:man spa) Add SPA routing documentation to CLAUDE.md Phase 4 Complete: Client-side SPA routing implementation Update get_routes() consumers for unified route structure Complete SPA Phase 3: PHP-side route type detection and is_spa flag Restore unified routes structure and Manifest_Query class Refactor route indexing and add SPA infrastructure Phase 3 Complete: SPA route registration in manifest Implement SPA Phase 2: Extract router code and test decorators Rename Jqhtml_Component to Component and complete SPA foundation setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
181 lines
5.3 KiB
JavaScript
181 lines
5.3 KiB
JavaScript
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
Author Tobias Koppers @sokra
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const {
|
|
DEFAULT_EXPORT,
|
|
NAMESPACE_OBJECT_EXPORT
|
|
} = require("./util/concatenate");
|
|
|
|
/** @typedef {import("./Chunk")} Chunk */
|
|
/** @typedef {import("./Module")} Module */
|
|
/** @typedef {import("./optimize/ConcatenatedModule").ConcatenatedModuleInfo} ConcatenatedModuleInfo */
|
|
/** @typedef {import("./optimize/ConcatenatedModule").ModuleInfo} ModuleInfo */
|
|
/** @typedef {import("./optimize/ConcatenatedModule").ExportName} Ids */
|
|
|
|
const MODULE_REFERENCE_REGEXP =
|
|
/^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?(_directImport)?(_deferredImport)?(?:_asiSafe(\d))?__$/;
|
|
|
|
/**
|
|
* @typedef {object} ModuleReferenceOptions
|
|
* @property {Ids} ids the properties/exports of the module
|
|
* @property {boolean} call true, when this referenced export is called
|
|
* @property {boolean} directImport true, when this referenced export is directly imported (not via property access)
|
|
* @property {boolean} deferredImport true, when this referenced export is deferred
|
|
* @property {boolean | undefined} asiSafe if the position is ASI safe or unknown
|
|
*/
|
|
|
|
class ConcatenationScope {
|
|
/**
|
|
* @param {ModuleInfo[] | Map<Module, ModuleInfo>} modulesMap all module info by module
|
|
* @param {ConcatenatedModuleInfo} currentModule the current module info
|
|
* @param {Set<string>} usedNames all used names
|
|
*/
|
|
constructor(modulesMap, currentModule, usedNames) {
|
|
this._currentModule = currentModule;
|
|
if (Array.isArray(modulesMap)) {
|
|
const map = new Map();
|
|
for (const info of modulesMap) {
|
|
map.set(info.module, info);
|
|
}
|
|
modulesMap = map;
|
|
}
|
|
this.usedNames = usedNames;
|
|
this._modulesMap = modulesMap;
|
|
}
|
|
|
|
/**
|
|
* @param {Module} module the referenced module
|
|
* @returns {boolean} true, when it's in the scope
|
|
*/
|
|
isModuleInScope(module) {
|
|
return this._modulesMap.has(module);
|
|
}
|
|
|
|
/**
|
|
* @param {string} exportName name of the export
|
|
* @param {string} symbol identifier of the export in source code
|
|
*/
|
|
registerExport(exportName, symbol) {
|
|
if (!this._currentModule.exportMap) {
|
|
this._currentModule.exportMap = new Map();
|
|
}
|
|
if (!this._currentModule.exportMap.has(exportName)) {
|
|
this._currentModule.exportMap.set(exportName, symbol);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} exportName name of the export
|
|
* @param {string} expression expression to be used
|
|
*/
|
|
registerRawExport(exportName, expression) {
|
|
if (!this._currentModule.rawExportMap) {
|
|
this._currentModule.rawExportMap = new Map();
|
|
}
|
|
if (!this._currentModule.rawExportMap.has(exportName)) {
|
|
this._currentModule.rawExportMap.set(exportName, expression);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} exportName name of the export
|
|
* @returns {string | undefined} the expression of the export
|
|
*/
|
|
getRawExport(exportName) {
|
|
if (!this._currentModule.rawExportMap) {
|
|
return undefined;
|
|
}
|
|
return this._currentModule.rawExportMap.get(exportName);
|
|
}
|
|
|
|
/**
|
|
* @param {string} exportName name of the export
|
|
* @param {string} expression expression to be used
|
|
*/
|
|
setRawExportMap(exportName, expression) {
|
|
if (!this._currentModule.rawExportMap) {
|
|
this._currentModule.rawExportMap = new Map();
|
|
}
|
|
if (this._currentModule.rawExportMap.has(exportName)) {
|
|
this._currentModule.rawExportMap.set(exportName, expression);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} symbol identifier of the export in source code
|
|
*/
|
|
registerNamespaceExport(symbol) {
|
|
this._currentModule.namespaceExportSymbol = symbol;
|
|
}
|
|
|
|
/**
|
|
* @param {Module} module the referenced module
|
|
* @param {Partial<ModuleReferenceOptions>} options options
|
|
* @returns {string} the reference as identifier
|
|
*/
|
|
createModuleReference(
|
|
module,
|
|
{
|
|
ids = undefined,
|
|
call = false,
|
|
directImport = false,
|
|
deferredImport = false,
|
|
asiSafe = false
|
|
}
|
|
) {
|
|
const info = /** @type {ModuleInfo} */ (this._modulesMap.get(module));
|
|
const callFlag = call ? "_call" : "";
|
|
const directImportFlag = directImport ? "_directImport" : "";
|
|
const deferredImportFlag = deferredImport ? "_deferredImport" : "";
|
|
const asiSafeFlag = asiSafe
|
|
? "_asiSafe1"
|
|
: asiSafe === false
|
|
? "_asiSafe0"
|
|
: "";
|
|
const exportData = ids
|
|
? Buffer.from(JSON.stringify(ids), "utf8").toString("hex")
|
|
: "ns";
|
|
// a "._" is appended to allow "delete ...", which would cause a SyntaxError in strict mode
|
|
return `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}${callFlag}${directImportFlag}${deferredImportFlag}${asiSafeFlag}__._`;
|
|
}
|
|
|
|
/**
|
|
* @param {string} name the identifier
|
|
* @returns {boolean} true, when it's an module reference
|
|
*/
|
|
static isModuleReference(name) {
|
|
return MODULE_REFERENCE_REGEXP.test(name);
|
|
}
|
|
|
|
/**
|
|
* @param {string} name the identifier
|
|
* @returns {ModuleReferenceOptions & { index: number } | null} parsed options and index
|
|
*/
|
|
static matchModuleReference(name) {
|
|
const match = MODULE_REFERENCE_REGEXP.exec(name);
|
|
if (!match) return null;
|
|
const index = Number(match[1]);
|
|
const asiSafe = match[6];
|
|
return {
|
|
index,
|
|
ids:
|
|
match[2] === "ns"
|
|
? []
|
|
: JSON.parse(Buffer.from(match[2], "hex").toString("utf8")),
|
|
call: Boolean(match[3]),
|
|
directImport: Boolean(match[4]),
|
|
deferredImport: Boolean(match[5]),
|
|
asiSafe: asiSafe ? asiSafe === "1" : undefined
|
|
};
|
|
}
|
|
}
|
|
|
|
ConcatenationScope.DEFAULT_EXPORT = DEFAULT_EXPORT;
|
|
ConcatenationScope.NAMESPACE_OBJECT_EXPORT = NAMESPACE_OBJECT_EXPORT;
|
|
|
|
module.exports = ConcatenationScope;
|