feat(webpack): support es module bundling (#10788)

This commit is contained in:
Nathan Walker
2025-09-18 13:03:17 -07:00
committed by GitHub
parent d6d3800884
commit 1e54baf198
20 changed files with 806 additions and 414 deletions

View File

@@ -0,0 +1,14 @@
export class CompatDefinePlugin {
private readonly definitions: Record<string, any>;
constructor(definitions: Record<string, any>) {
this.definitions = definitions || {};
}
apply(compiler: any) {
// Use the same webpack instance as the compiler to avoid version mismatches
const wp = compiler?.webpack || require('webpack');
const DefinePlugin = wp.DefinePlugin || require('webpack').DefinePlugin;
new DefinePlugin(this.definitions).apply(compiler);
}
}

View File

@@ -37,22 +37,28 @@ export class PlatformSuffixPlugin {
// require.context
compiler.hooks.contextModuleFactory.tap(id, (cmf) => {
// @ts-ignore
cmf.hooks.alternativeRequests.tap(id, (modules, options) => {
const additionalModules = [];
// we are looking for modules that are platform specific (something.<platform>.ext)
// and we are duplicating them without the platform suffix
// this allows using require.context with non-existent platformless filenames
// but mapped to the platform specific variant (done in the resolver hook below)
for (const module of modules) {
if (platformRE.test(module.request)) {
additionalModules.push({
...module,
request: module.request.replace(platformRE, '.'),
});
const altHook = (cmf as any).hooks?.alternativeRequests;
if (altHook && typeof altHook.tap === 'function') {
altHook.tap(id, (modules: any[], options: any) => {
const additionalModules = [];
// we are looking for modules that are platform specific (something.<platform>.ext)
// and we are duplicating them without the platform suffix
// this allows using require.context with non-existent platformless filenames
// but mapped to the platform specific variant (done in the resolver hook below)
for (const module of modules) {
if (platformRE.test(module.request)) {
additionalModules.push({
...module,
request: module.request.replace(platformRE, '.'),
});
}
}
}
modules.push(...additionalModules);
});
modules.push(...additionalModules);
});
} else {
// Hook may be absent on some webpack versions; skip gracefully
// console.log(`[${id}] alternativeRequests hook not available; skipping.`)
}
});
compiler.resolverFactory.hooks.resolver
@@ -73,57 +79,68 @@ export class PlatformSuffixPlugin {
// });
// })
resolver.hooks.normalResolve.tapAsync(
id,
(request_, resolveContext, callback) => {
for (const platform of this.extensions) {
const { path, request } = request_;
const ext = request && extname(request);
const platformExt = ext ? `.${platform}${ext}` : '';
const normalResolveHook = (resolver as any).hooks?.normalResolve;
const ensureHook = (name: string) => {
return typeof (resolver as any).ensureHook === 'function'
? (resolver as any).ensureHook(name)
: (resolver as any).hooks?.[name];
};
if (path && request && ext && !request.includes(platformExt)) {
const platformRequest = request.replace(ext, platformExt);
const extPath = resolve(path, platformRequest);
if (
!normalResolveHook ||
typeof normalResolveHook.tapAsync !== 'function'
) {
// Missing or incompatible hook; skip to avoid crashes
return;
}
// console.log({
// path,
// request,
// ext,
// extPath
// })
normalResolveHook.tapAsync(id, (request_, resolveContext, callback) => {
for (const platform of this.extensions) {
const { path, request } = request_;
const ext = request && extname(request);
const platformExt = ext ? `.${platform}${ext}` : '';
// if a file with the same + a platform suffix exists
// we want to resolve that file instead
if (existsSync(extPath)) {
const message = `resolving "${request}" to "${platformRequest}"`;
const hook = resolver.ensureHook('normalResolve');
console.log(message);
if (path && request && ext && !request.includes(platformExt)) {
const platformRequest = request.replace(ext, platformExt);
const extPath = resolve(path, platformRequest);
// here we are creating a new resolve object and replacing the path
// with the .<platform>.<ext> suffix
const obj = {
...request_,
path: resolver.join(path, platformRequest),
relativePath:
request_.relativePath &&
resolver.join(request_.relativePath, platformRequest),
request: undefined,
};
// console.log({
// path,
// request,
// ext,
// extPath
// })
// we call to the actual resolver to do the resolving of this new file
return resolver.doResolve(
hook,
obj,
message,
resolveContext,
callback
);
}
// if a file with the same + a platform suffix exists
// we want to resolve that file instead
if (existsSync(extPath)) {
const message = `resolving "${request}" to "${platformRequest}"`;
const hook = ensureHook('normalResolve');
// here we are creating a new resolve object and replacing the path
// with the .<platform>.<ext> suffix
const obj = {
...request_,
path: resolver.join(path, platformRequest),
relativePath:
request_.relativePath &&
resolver.join(request_.relativePath, platformRequest),
request: undefined,
};
// we call to the actual resolver to do the resolving of this new file
return (resolver as any).doResolve(
hook as any,
obj,
message,
resolveContext,
callback,
);
}
}
callback();
}
);
callback();
});
// resolver.hooks.rawFile.tap(id, (request, resolveContext, callback) => {
// if(request.path && !/\.ios\..+$/.test(request.path)) {
// const { ext } = parse(request.path)