Files
formipay/node_modules/webpack/lib/schemes/VirtualUrlPlugin.js
dwindown e8fbfb14c1 fix: prevent asset conflicts between React and Grid.js versions
Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 17:02:14 +07:00

301 lines
8.7 KiB
JavaScript

/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Natsu @xiaoxiaojx
*/
"use strict";
const { getContext } = require("loader-runner");
const ModuleNotFoundError = require("../ModuleNotFoundError");
const NormalModule = require("../NormalModule");
const { isAbsolute, join } = require("../util/fs");
const { parseResourceWithoutFragment } = require("../util/identifier");
const DEFAULT_SCHEME = "virtual";
const PLUGIN_NAME = "VirtualUrlPlugin";
/**
* Defines the compiler type used by this module.
* @typedef {import("../Compiler")} Compiler
* @typedef {import("../../declarations/plugins/schemes/VirtualUrlPlugin").VirtualModule} VirtualModuleConfig
* @typedef {import("../../declarations/plugins/schemes/VirtualUrlPlugin").VirtualModuleContent} VirtualModuleInput
* @typedef {import("../../declarations/plugins/schemes/VirtualUrlPlugin").VirtualUrlOptions} VirtualUrlOptions
*/
/** @typedef {(loaderContext: LoaderContext<EXPECTED_ANY>) => Promise<string | Buffer> | string | Buffer} SourceFn */
/** @typedef {() => string} VersionFn */
/** @typedef {{ [key: string]: VirtualModuleInput }} VirtualModules */
/**
* Defines the loader context type used by this module.
* @template T
* @typedef {import("../../declarations/LoaderContext").LoaderContext<T>} LoaderContext
*/
/**
* Normalizes a virtual module definition into a standard format
* @param {VirtualModuleInput} virtualConfig The virtual module to normalize
* @returns {VirtualModuleConfig} The normalized virtual module
*/
function normalizeModule(virtualConfig) {
if (typeof virtualConfig === "string") {
return {
type: "",
source() {
return virtualConfig;
}
};
} else if (typeof virtualConfig === "function") {
return {
type: "",
source: virtualConfig
};
}
return virtualConfig;
}
/** @typedef {{ [key: string]: VirtualModuleConfig }} NormalizedModules */
/**
* Normalizes all virtual modules with the given scheme
* @param {VirtualModules} virtualConfigs The virtual modules to normalize
* @param {string} scheme The URL scheme to use
* @returns {NormalizedModules} The normalized virtual modules
*/
function normalizeModules(virtualConfigs, scheme) {
return Object.keys(virtualConfigs).reduce((pre, id) => {
pre[toVid(id, scheme)] = normalizeModule(virtualConfigs[id]);
return pre;
}, /** @type {NormalizedModules} */ ({}));
}
/**
* Converts a module id and scheme to a virtual module id
* @param {string} id The module id
* @param {string} scheme The URL scheme
* @returns {string} The virtual module id
*/
function toVid(id, scheme) {
return `${scheme}:${id}`;
}
/**
* Converts a virtual module id to a module id
* @param {string} vid The virtual module id
* @param {string} scheme The URL scheme
* @returns {string} The module id
*/
function fromVid(vid, scheme) {
return vid.replace(`${scheme}:`, "");
}
const VALUE_DEP_VERSION = `webpack/${PLUGIN_NAME}/version`;
/**
* Converts a module id and scheme to a cache key
* @param {string} id The module id
* @param {string} scheme The URL scheme
* @returns {string} The cache key
*/
function toCacheKey(id, scheme) {
return `${VALUE_DEP_VERSION}/${toVid(id, scheme)}`;
}
class VirtualUrlPlugin {
/**
* Creates an instance of VirtualUrlPlugin.
* @param {VirtualModules} modules The virtual modules
* @param {Omit<VirtualUrlOptions, "modules"> | string=} schemeOrOptions The URL scheme to use
*/
constructor(modules, schemeOrOptions) {
/** @type {VirtualUrlOptions} */
this.options = {
modules,
...(typeof schemeOrOptions === "string"
? { scheme: schemeOrOptions }
: schemeOrOptions || {})
};
/** @type {string} */
this.scheme = this.options.scheme || DEFAULT_SCHEME;
/** @type {VirtualUrlOptions["context"]} */
this.context = this.options.context || "auto";
/** @type {NormalizedModules} */
this.modules = normalizeModules(this.options.modules, this.scheme);
}
/**
* Applies the plugin by registering its hooks on the compiler.
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.validate.tap(PLUGIN_NAME, () => {
compiler.validate(
() => require("../../schemas/plugins/schemes/VirtualUrlPlugin.json"),
this.options,
{
name: "Virtual Url Plugin",
baseDataPath: "options"
},
(options) =>
require("../../schemas/plugins/schemes/VirtualUrlPlugin.check")(
options
)
);
});
const scheme = this.scheme;
const cachedParseResourceWithoutFragment =
parseResourceWithoutFragment.bindCache(compiler.root);
compiler.hooks.compilation.tap(
PLUGIN_NAME,
(compilation, { normalModuleFactory }) => {
compilation.hooks.assetPath.tap(
{ name: PLUGIN_NAME, before: "TemplatedPathPlugin" },
(path, data) => {
if (data.filename && this.modules[data.filename]) {
/**
* Returns safe path.
* @param {string} str path
* @returns {string} safe path
*/
const toSafePath = (str) =>
`__${str
.replace(/:/g, "__")
.replace(/^[^a-z0-9]+|[^a-z0-9]+$/gi, "")
.replace(/[^a-z0-9._-]+/gi, "_")}`;
// filename: virtual:logo.svg -> __virtual__logo.svg
data.filename = toSafePath(data.filename);
}
return path;
}
);
normalModuleFactory.hooks.resolveForScheme
.for(scheme)
.tap(PLUGIN_NAME, (resourceData) => {
const virtualConfig = this.findVirtualModuleConfigById(
resourceData.resource
);
const url = cachedParseResourceWithoutFragment(
resourceData.resource
);
const path = url.path;
const type = virtualConfig.type || "";
const context = virtualConfig.context || this.context;
resourceData.path = path + type;
resourceData.resource = path;
if (context === "auto") {
const context = getContext(path);
if (context === path) {
resourceData.context = compiler.context;
} else {
const resolvedContext = fromVid(context, scheme);
resourceData.context = isAbsolute(resolvedContext)
? resolvedContext
: join(
/** @type {import("..").InputFileSystem} */
(compiler.inputFileSystem),
compiler.context,
resolvedContext
);
}
} else if (context && typeof context === "string") {
resourceData.context = context;
} else {
resourceData.context = compiler.context;
}
if (virtualConfig.version) {
const cacheKey = toCacheKey(resourceData.resource, scheme);
const cacheVersion = this.getCacheVersion(virtualConfig.version);
compilation.valueCacheVersions.set(
cacheKey,
/** @type {string} */ (cacheVersion)
);
}
return true;
});
const hooks = NormalModule.getCompilationHooks(compilation);
hooks.readResource
.for(scheme)
.tapAsync(PLUGIN_NAME, async (loaderContext, callback) => {
const { resourcePath } = loaderContext;
const module = /** @type {NormalModule} */ (loaderContext._module);
const cacheKey = toCacheKey(resourcePath, scheme);
const addVersionValueDependency = () => {
if (!module || !module.buildInfo) return;
const buildInfo = module.buildInfo;
if (!buildInfo.valueDependencies) {
buildInfo.valueDependencies = new Map();
}
const cacheVersion = compilation.valueCacheVersions.get(cacheKey);
if (compilation.valueCacheVersions.has(cacheKey)) {
buildInfo.valueDependencies.set(
cacheKey,
/** @type {string} */ (cacheVersion)
);
}
};
try {
const virtualConfig =
this.findVirtualModuleConfigById(resourcePath);
const content = await virtualConfig.source(loaderContext);
addVersionValueDependency();
callback(null, content);
} catch (err) {
callback(/** @type {Error} */ (err));
}
});
}
);
}
/**
* Finds virtual module config by id.
* @param {string} id The module id
* @returns {VirtualModuleConfig} The virtual module config
*/
findVirtualModuleConfigById(id) {
const config = this.modules[id];
if (!config) {
throw new ModuleNotFoundError(
null,
new Error(`Can't resolve virtual module ${id}`),
{
name: `virtual module ${id}`
}
);
}
return config;
}
/**
* Get the cache version for a given version value
* @param {VersionFn | true | string} version The version value or function
* @returns {string | undefined} The cache version
*/
getCacheVersion(version) {
return version === true
? undefined
: (typeof version === "function" ? version() : version) || "unset";
}
}
VirtualUrlPlugin.DEFAULT_SCHEME = DEFAULT_SCHEME;
module.exports = VirtualUrlPlugin;