isolated-externals-plugin
Advanced tools
Comparing version 2.4.0-preload-non-esm-e559461.11 to 2.4.0-preload-non-esm-e559461.12
import { Compiler } from 'webpack'; | ||
import { ExternalInfo } from './util/externalsClasses'; | ||
interface UnpromisedEntries { | ||
[key: string]: string[]; | ||
} | ||
export interface IsolatedExternalsElement { | ||
@@ -14,4 +17,6 @@ [key: string]: ExternalInfo; | ||
readonly moduleDir: string; | ||
readonly unpromisedEntries: UnpromisedEntries; | ||
constructor(config?: IsolatedExternals, externalsModuleLocation?: string, unpromisedEntryModuleLocation?: string); | ||
apply(compiler: Compiler): void; | ||
} | ||
export {}; |
@@ -81,2 +81,3 @@ "use strict"; | ||
this.moduleDir = path_1.default.dirname(this.externalsModuleLocation); | ||
this.unpromisedEntries = {}; | ||
} | ||
@@ -165,8 +166,9 @@ apply(compiler) { | ||
}); | ||
compiler.hooks.thisCompilation.tap('IsolatedExternalsPlugin', (compilation, compilationParams) => { | ||
const isolateCompilationEntries = (compilation, compilationParams) => { | ||
const logger = compilation.getLogger('IsolatedExternalsPlugin'); | ||
const { normalModuleFactory } = compilationParams; | ||
const unpromisedEntries = {}; | ||
const getTargetEntry = (dep) => { | ||
var _a; | ||
const unpromisedEntries = this.unpromisedEntries; | ||
const getParentModule = (result) => compilation.moduleGraph.getParentModule(result.dependencies[0]); | ||
const getTargetEntry = (dependency, parents) => { | ||
const dep = dependency; | ||
if (!dep) { | ||
@@ -177,3 +179,4 @@ return; | ||
if (rawRequest) { | ||
const entryName = (0, getRequestParam_1.default)(rawRequest, 'isolatedExternalsEntry'); | ||
const entryName = (0, getRequestParam_1.default)(rawRequest, 'isolatedExternalsEntry') || | ||
(0, getRequestParam_1.default)(rawRequest, 'unpromised-entry'); | ||
if (entryName) { | ||
@@ -183,8 +186,8 @@ return dep; | ||
} | ||
const connections = Array.from(compilation.moduleGraph.getIncomingConnections(dep)); | ||
return getTargetEntry((_a = connections.find((conn) => conn.originModule !== dep && | ||
getTargetEntry(conn.originModule))) === null || _a === void 0 ? void 0 : _a.originModule); | ||
}; | ||
const getTargetEntryFromDeps = (deps) => { | ||
const targetEntry = deps === null || deps === void 0 ? void 0 : deps.map((dep) => getTargetEntry(compilation.moduleGraph.getParentModule(dep))).find(Boolean); | ||
const connections = parents || | ||
Array.from(compilation.moduleGraph.getIncomingConnections(dep)).map((conn) => conn === null || conn === void 0 ? void 0 : conn.originModule); | ||
let targetEntry; | ||
connections | ||
.filter((conn) => conn && conn.identifier() !== dep.identifier()) | ||
.find((conn) => (targetEntry = getTargetEntry(conn))); | ||
return targetEntry; | ||
@@ -199,6 +202,6 @@ }; | ||
} | ||
function getEntryDepsRequest(entryName, request, replacedRequest, replacedContext) { | ||
function getEntryDepsRequest(entryName, request, replacedRequest, replacedContext, existingEntries) { | ||
if (!entryName) | ||
return request; | ||
const unpromiseDeps = unpromisedEntries[entryName] || []; | ||
const unpromiseDeps = existingEntries[entryName] || []; | ||
if (!unpromiseDeps) | ||
@@ -222,101 +225,135 @@ return request; | ||
return; | ||
if (!unpromisedEntries[entryName]) { | ||
return; | ||
} | ||
const newEntryDep = webpack_1.EntryPlugin.createDependency(request, entry.options || entryName); | ||
return newEntryDep; | ||
} | ||
const rebuildEntryModule = async (entryName) => { | ||
const rebuilding = new Promise((resolve, reject) => { | ||
const entry = compilation.entries.get(entryName); | ||
if (!entry) { | ||
logger.debug('no entry', entryName); | ||
return resolve(); | ||
} | ||
const entryDep = entry.dependencies.find((dep) => ((0, getRequestParam_1.default)(dep.request, 'isolatedExternalsEntry') || | ||
(0, getRequestParam_1.default)(dep.request, 'unpromised-entry')) === | ||
entryName); | ||
if (!entryDep) { | ||
logger.debug('no entry dependency', entryName); | ||
return resolve(); | ||
} | ||
const newEntryRequest = getEntryDepsRequest(entryName, this.unpromisedEntryModuleLocation, entryDep.request, /^\./.test(entryDep.request) | ||
? entryDep.getContext() || process.cwd() | ||
: ''); | ||
if (entryDep.request === newEntryRequest) { | ||
logger.debug('no need to rebuild entry module', entryDep.request); | ||
return resolve(); | ||
} | ||
const newEntryDep = getEntryDep(entryName, newEntryRequest); | ||
if (!newEntryDep) { | ||
logger.debug('no new entry module', entryName, entryDep.request); | ||
return resolve(); | ||
} | ||
logger.debug('replacing entry module', { entryName, newEntryDep }); | ||
entry.dependencies = entry.dependencies.filter((dep) => dep !== entryDep); | ||
compilation.addEntry('', newEntryDep, entryName, (err) => { | ||
if (err) | ||
return reject(err); | ||
resolve(); | ||
}); | ||
}); | ||
await rebuilding; | ||
}; | ||
function getTargetEntryNameFromResult(result, existingTargetEntry) { | ||
const targetEntry = existingTargetEntry !== null && existingTargetEntry !== void 0 ? existingTargetEntry : getTargetEntryFromDeps(result.dependencies); | ||
const targetEntry = existingTargetEntry !== null && existingTargetEntry !== void 0 ? existingTargetEntry : getTargetEntry(result.type | ||
? result | ||
: getParentModule(result)); | ||
if (!targetEntry) | ||
return ''; | ||
const entryName = (0, getRequestParam_1.default)(targetEntry.userRequest, 'isolatedExternalsEntry') || ''; | ||
const entryName = (0, getRequestParam_1.default)(targetEntry.userRequest, 'isolatedExternalsEntry') || | ||
''; | ||
return entryName; | ||
} | ||
const getExternalsBlockForResult = (result, knownEntryName) => { | ||
if (knownEntryName) | ||
return finalIsolatedExternals[knownEntryName]; | ||
const targetEntry = getTargetEntryFromDeps(result.dependencies); | ||
if (!targetEntry) | ||
return; | ||
const entryName = getTargetEntryNameFromResult(result, targetEntry); | ||
const externalsBlock = finalIsolatedExternals[entryName]; | ||
return externalsBlock; | ||
}; | ||
const getParents = (dep, parents = []) => { | ||
let parent = dep; | ||
if (dep.constructor.name.includes('Dependency')) { | ||
parent = compilation.moduleGraph.getParentModule(dep); | ||
if ((parent === null || parent === void 0 ? void 0 : parent.request) === dep.request) | ||
parent = undefined; | ||
const setsEqual = (setA, setB) => setA.size === setB.size && [...setA].every((value) => setB.has(value)); | ||
const knownParents = {}; | ||
const addKnownParent = (req, info) => (knownParents[req] = info); | ||
const moduleHasNonEsmDeps = (module) => module.dependencies.some((dep) => !['unknown', 'esm', 'self'].includes(dep.category) || | ||
dep.constructor.name.includes('CommonJs')); | ||
const parentsHaveNonEsmDep = (parent, parents) => { | ||
if (!parents.length) { | ||
logger.debug(`top level parent: \n`, parent.identifier()); | ||
return moduleHasNonEsmDeps(parent); | ||
} | ||
if (!parent) { | ||
return parents; | ||
function* hasNonEsmDepGen() { | ||
let parentInd = 0; | ||
let isNonEsm = false; | ||
while (parentInd < parents.length && !isNonEsm) { | ||
let targetParent = parents[parentInd]; | ||
while (!targetParent && parentInd < parents.length) | ||
targetParent = parents[++parentInd]; | ||
if (!targetParent) | ||
return isNonEsm; | ||
isNonEsm = isNonEsmParent(targetParent); | ||
parentInd++; | ||
yield isNonEsm; | ||
} | ||
return isNonEsm; | ||
} | ||
const moduleParents = [ | ||
...compilation.moduleGraph.getIncomingConnections(parent), | ||
] | ||
.filter((mod) => mod.originModule !== parent) | ||
.map((mod) => mod.originModule) | ||
.filter(Boolean); | ||
const newParents = [ | ||
parent, | ||
...moduleParents.flatMap((mod) => getParents(mod, parents)), | ||
]; | ||
return newParents; | ||
const hasNonEsmDep = hasNonEsmDepGen(); | ||
for (const isNonEsm of hasNonEsmDep) { | ||
if (isNonEsm) | ||
return true; | ||
} | ||
return false; | ||
}; | ||
const parentsHaveNonEsmDep = (parents, result) => { | ||
let previousChildReq = result.request; | ||
const hasNonEsmParent = result.dependencyType !== 'esm' || | ||
parents.some((parent) => { | ||
const targetChild = parent.dependencies.find((dep) => dep.request === previousChildReq); | ||
const { category } = (targetChild || {}); | ||
previousChildReq = parent.request; | ||
return !['esm', 'self'].includes(category || ''); | ||
const createModuleSet = (arr) => new Set(arr.filter((m) => Boolean(m)).map((m) => m.identifier())); | ||
const isNonEsmParent = (parent, existingParents) => { | ||
const knownResult = knownParents[parent.identifier()]; | ||
const parents = existingParents || | ||
[...compilation.moduleGraph.getIncomingConnections(parent)] | ||
.filter((mod) => mod.originModule !== parent) | ||
.map((mod) => mod.originModule) | ||
.filter((m) => Boolean(m)) | ||
.filter((m) => m.identifier() !== parent.identifier()); | ||
const parentSet = createModuleSet(parents); | ||
try { | ||
if (knownResult && setsEqual(knownResult.connections, parentSet)) { | ||
return knownResult.isNonEsm; | ||
} | ||
const hasNonEsmDeps = moduleHasNonEsmDeps(parent); | ||
const isNonEsm = hasNonEsmDeps || parentsHaveNonEsmDep(parent, parents); | ||
if (parent.identifier().includes('react-query/devtools')) { | ||
logger.debug('react-query/devtools', { | ||
req: parent.identifier(), | ||
hasNonEsmDeps, | ||
isNonEsm, | ||
deps: parent.dependencies.map((dep) => ({ | ||
category: dep.category, | ||
id: dep.request, | ||
className: dep.constructor.name, | ||
})), | ||
}); | ||
} | ||
addKnownParent(parent.identifier(), { | ||
isNonEsm, | ||
connections: parentSet, | ||
}); | ||
return hasNonEsmParent; | ||
if (hasNonEsmDeps) { | ||
logger.debug('found non-esm parent', { | ||
req: parent.identifier(), | ||
deps: parent.dependencies.map((dep) => { | ||
const { category, constructor: { name: constructorName }, } = dep; | ||
return { | ||
category, | ||
id: dep.request, | ||
name: dep.name, | ||
constructorName, | ||
}; | ||
}), | ||
}); | ||
} | ||
return isNonEsm; | ||
} | ||
catch (err) { | ||
console.warn('Error in isNonEsmParent: ', { | ||
req: parent.identifier(), | ||
parentSet, | ||
}); | ||
console.error(err); | ||
logger.error(err); | ||
throw err; | ||
} | ||
}; | ||
normalModuleFactory.hooks.beforeResolve.tapPromise('IsolatedExternalsPlugin', async (result) => { | ||
const isNonEsmResult = (result, parent, parents) => { | ||
const connections = parents || | ||
[...compilation.moduleGraph.getIncomingConnections(parent)].map((c) => c.originModule); | ||
const knownParent = knownParents[parent.identifier()]; | ||
if (knownParent && | ||
setsEqual(knownParent.connections, createModuleSet(connections))) { | ||
logger.debug('known parent: \n', parent.identifier(), '\n', knownParent); | ||
return knownParent.isNonEsm; | ||
} | ||
const isNonEsm = (result.dependencyType !== 'esm' && | ||
(logger.debug(`Found dependency type for ${result.request}:`, result.dependencyType), | ||
true)) || | ||
isNonEsmParent(parent, connections); | ||
return isNonEsm; | ||
}; | ||
normalModuleFactory.hooks.beforeResolve.tap('IsolatedExternalsPlugin', (result) => { | ||
try { | ||
const targetEntry = getTargetEntryFromDeps(result.dependencies); | ||
const parentModule = getParentModule(result); | ||
const parents = [ | ||
...compilation.moduleGraph.getIncomingConnections(parentModule), | ||
] | ||
.map((c) => c.originModule) | ||
.filter((m) => (m === null || m === void 0 ? void 0 : m.identifier()) !== parentModule.identifier()); | ||
if (!parents.length) | ||
return; | ||
const targetEntry = getTargetEntry(parentModule, parents); | ||
if (!targetEntry) | ||
return; | ||
const entryName = getTargetEntryNameFromResult(result, targetEntry); | ||
const externalsBlock = getExternalsBlockForResult(result, entryName); | ||
const externalsBlock = finalIsolatedExternals[entryName]; | ||
const externalName = result.request; | ||
@@ -326,7 +363,7 @@ const targetExternal = externalsBlock === null || externalsBlock === void 0 ? void 0 : externalsBlock[externalName]; | ||
return; | ||
const parents = getParents(result.dependencies[0]); | ||
const hasNonEsmParent = parentsHaveNonEsmDep(parents, result); | ||
if (!hasNonEsmParent) | ||
logger.debug(`Checking dep "${result.request} for entry "${entryName}"`); | ||
const nonEsmInChain = isNonEsmResult(result, parentModule, parents); | ||
if (!nonEsmInChain) | ||
return; | ||
logger.debug('unpromising entry', entryName, 'for external', result.request); | ||
logger.debug(`unpromising entry "${entryName}" for external "${result.request}".`); | ||
const req = result.request.endsWith('/') | ||
@@ -346,3 +383,2 @@ ? result.request + 'index' | ||
]; | ||
await rebuildEntryModule(entryName); | ||
} | ||
@@ -352,5 +388,56 @@ catch (err) { | ||
logger.error(err); | ||
console.error(err); | ||
throw err; | ||
} | ||
}); | ||
const updateEntries = async () => { | ||
const existingEntries = Object.assign({}, unpromisedEntries); | ||
for (const entryName of Object.keys(existingEntries)) { | ||
const entry = compilation.entries.get(entryName); | ||
if (!entry) { | ||
logger.debug('no entry', entryName); | ||
break; | ||
} | ||
const entryDep = entry.dependencies.find((dep) => ((0, getRequestParam_1.default)(dep.request, 'isolatedExternalsEntry') || | ||
(0, getRequestParam_1.default)(dep.request, 'unpromised-entry')) === entryName); | ||
if (!entryDep) { | ||
logger.debug('no entry dependency', entryName); | ||
break; | ||
} | ||
const newEntryRequest = getEntryDepsRequest(entryName, this.unpromisedEntryModuleLocation, entryDep.request, /^\./.test(entryDep.request) | ||
? entryDep.getContext() || process.cwd() | ||
: '', existingEntries); | ||
if (entryDep.request === newEntryRequest) { | ||
logger.debug('no need to rebuild entry module', { | ||
entryName, | ||
request: entryDep.request, | ||
}); | ||
break; | ||
} | ||
logger.debug('Rebuilding entry:', { | ||
entryName, | ||
newEntryRequest, | ||
}); | ||
const newEntryDep = getEntryDep(entryName, newEntryRequest); | ||
if (!newEntryDep) { | ||
logger.debug('no new entry module', entryName, entryDep.request); | ||
break; | ||
} | ||
logger.debug('replacing entry module', { | ||
entryName, | ||
newEntryDep, | ||
}); | ||
entry.dependencies = entry.dependencies.filter((dep) => dep !== entryDep); | ||
const rebuilding = new Promise((resolve, reject) => { | ||
compilation.addEntry('', newEntryDep, entryName, (err) => { | ||
if (err) | ||
return reject(err); | ||
resolve(); | ||
}); | ||
}); | ||
await rebuilding; | ||
} | ||
}; | ||
compilation.hooks.buildModule.tap('IsolatedExternalsPlugin', () => void updateEntries()); | ||
compilation.hooks.rebuildModule.tap('IsolatedExternalsPlugin', () => void updateEntries()); | ||
normalModuleFactory.hooks.factorize.tapAsync('IsolatedExternalsPlugin', (data, cb) => { | ||
@@ -363,3 +450,4 @@ var _a; | ||
} | ||
const targetEntry = getTargetEntryFromDeps(data.dependencies); | ||
const parent = getParentModule(data); | ||
const targetEntry = getTargetEntry(parent); | ||
if (!targetEntry) { | ||
@@ -377,2 +465,6 @@ callOriginalExternalsPlugin(); | ||
}); | ||
}; | ||
compiler.hooks.thisCompilation.tap('IsolatedExternalsPlugin', isolateCompilationEntries); | ||
compiler.hooks.watchRun.tap('IsolatedExternalsPlugin', (newCompiler) => { | ||
newCompiler.hooks.thisCompilation.tap('IsolatedExternalsPlugin', isolateCompilationEntries); | ||
}); | ||
@@ -379,0 +471,0 @@ compiler.hooks.compilation.tap('IsolatedExternalsPlugin', (compilation) => { |
{ | ||
"name": "isolated-externals-plugin", | ||
"version": "2.4.0-preload-non-esm-e559461.11", | ||
"version": "2.4.0-preload-non-esm-e559461.12", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
80210
1535