-
-
Notifications
You must be signed in to change notification settings - Fork 595
/
Copy pathresolve-require-sources.js
211 lines (202 loc) · 7.99 KB
/
resolve-require-sources.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import {
ES_IMPORT_SUFFIX,
EXTERNAL_SUFFIX,
IS_WRAPPED_COMMONJS,
isWrappedId,
PROXY_SUFFIX,
wrapId,
WRAPPED_SUFFIX
} from './helpers';
import { resolveExtensions } from './resolve-id';
export function getRequireResolver(extensions, detectCyclesAndConditional, currentlyResolving) {
const knownCjsModuleTypes = Object.create(null);
const requiredIds = Object.create(null);
const unconditionallyRequiredIds = Object.create(null);
const dependencies = Object.create(null);
const getDependencies = (id) => dependencies[id] || (dependencies[id] = new Set());
const isCyclic = (id) => {
const dependenciesToCheck = new Set(getDependencies(id));
for (const dependency of dependenciesToCheck) {
if (dependency === id) {
return true;
}
for (const childDependency of getDependencies(dependency)) {
dependenciesToCheck.add(childDependency);
}
}
return false;
};
// Once a module is listed here, its type (wrapped or not) is fixed and may
// not change for the rest of the current build, to not break already
// transformed modules.
const fullyAnalyzedModules = Object.create(null);
const getTypeForFullyAnalyzedModule = (id) => {
const knownType = knownCjsModuleTypes[id];
if (knownType !== true || !detectCyclesAndConditional || fullyAnalyzedModules[id]) {
return knownType;
}
if (isCyclic(id)) {
return (knownCjsModuleTypes[id] = IS_WRAPPED_COMMONJS);
}
return knownType;
};
const setInitialParentType = (id, initialCommonJSType) => {
// Fully analyzed modules may never change type
if (fullyAnalyzedModules[id]) {
return;
}
knownCjsModuleTypes[id] = initialCommonJSType;
if (
detectCyclesAndConditional &&
knownCjsModuleTypes[id] === true &&
requiredIds[id] &&
!unconditionallyRequiredIds[id]
) {
knownCjsModuleTypes[id] = IS_WRAPPED_COMMONJS;
}
};
const analyzeRequiredModule = async (parentId, resolved, isConditional, loadModule) => {
const childId = resolved.id;
requiredIds[childId] = true;
if (!(isConditional || knownCjsModuleTypes[parentId] === IS_WRAPPED_COMMONJS)) {
unconditionallyRequiredIds[childId] = true;
}
getDependencies(parentId).add(childId);
if (!isCyclic(childId)) {
// This makes sure the current transform handler waits for all direct
// dependencies to be loaded and transformed and therefore for all
// transitive CommonJS dependencies to be loaded as well so that all
// cycles have been found and knownCjsModuleTypes is reliable.
await loadModule(resolved);
}
};
const getTypeForImportedModule = async (resolved, loadModule) => {
if (resolved.id in knownCjsModuleTypes) {
// This handles cyclic ES dependencies
return knownCjsModuleTypes[resolved.id];
}
const {
meta: { commonjs }
} = await loadModule(resolved);
return (commonjs && commonjs.isCommonJS) || false;
};
return {
getWrappedIds: () =>
Object.keys(knownCjsModuleTypes).filter(
(id) => knownCjsModuleTypes[id] === IS_WRAPPED_COMMONJS
),
isRequiredId: (id) => requiredIds[id],
async shouldTransformCachedModule({
id: parentId,
resolvedSources,
meta: { commonjs: parentMeta }
}) {
// We explicitly track ES modules to handle circular imports
if (!(parentMeta && parentMeta.isCommonJS)) knownCjsModuleTypes[parentId] = false;
if (isWrappedId(parentId, ES_IMPORT_SUFFIX)) return false;
const parentRequires = parentMeta && parentMeta.requires;
if (parentRequires) {
setInitialParentType(parentId, parentMeta.initialCommonJSType);
await Promise.all(
parentRequires.map(({ resolved, isConditional }) =>
analyzeRequiredModule(parentId, resolved, isConditional, this.load)
)
);
if (getTypeForFullyAnalyzedModule(parentId) !== parentMeta.isCommonJS) {
return true;
}
for (const {
resolved: { id }
} of parentRequires) {
if (getTypeForFullyAnalyzedModule(id) !== parentMeta.isRequiredCommonJS[id]) {
return true;
}
}
// Now that we decided to go with the cached copy, neither the parent
// module nor any of its children may change types anymore
fullyAnalyzedModules[parentId] = true;
for (const {
resolved: { id }
} of parentRequires) {
fullyAnalyzedModules[id] = true;
}
}
const parentRequireSet = new Set((parentRequires || []).map(({ resolved: { id } }) => id));
return (
await Promise.all(
Object.keys(resolvedSources)
.map((source) => resolvedSources[source])
.filter(({ id, external }) => !(external || parentRequireSet.has(id)))
.map(async (resolved) => {
if (isWrappedId(resolved.id, ES_IMPORT_SUFFIX)) {
return (
(await getTypeForImportedModule(
(
await this.load({ id: resolved.id })
).meta.commonjs.resolved,
this.load
)) !== IS_WRAPPED_COMMONJS
);
}
return (await getTypeForImportedModule(resolved, this.load)) === IS_WRAPPED_COMMONJS;
})
)
).some((shouldTransform) => shouldTransform);
},
/* eslint-disable no-param-reassign */
resolveRequireSourcesAndUpdateMeta:
(rollupContext) => async (parentId, isParentCommonJS, parentMeta, sources) => {
parentMeta.initialCommonJSType = isParentCommonJS;
parentMeta.requires = [];
parentMeta.isRequiredCommonJS = Object.create(null);
setInitialParentType(parentId, isParentCommonJS);
const currentlyResolvingForParent = currentlyResolving.get(parentId) || new Set();
currentlyResolving.set(parentId, currentlyResolvingForParent);
const requireTargets = await Promise.all(
sources.map(async ({ source, isConditional }) => {
// Never analyze or proxy internal modules
if (source.startsWith('\0')) {
return { id: source, allowProxy: false };
}
currentlyResolvingForParent.add(source);
const resolved =
(await rollupContext.resolve(source, parentId, {
custom: { 'node-resolve': { isRequire: true } }
})) || resolveExtensions(source, parentId, extensions);
currentlyResolvingForParent.delete(source);
if (!resolved) {
return { id: wrapId(source, EXTERNAL_SUFFIX), allowProxy: false };
}
const childId = resolved.id;
if (resolved.external) {
return { id: wrapId(childId, EXTERNAL_SUFFIX), allowProxy: false };
}
parentMeta.requires.push({ resolved, isConditional });
await analyzeRequiredModule(parentId, resolved, isConditional, rollupContext.load);
return { id: childId, allowProxy: true };
})
);
parentMeta.isCommonJS = getTypeForFullyAnalyzedModule(parentId);
fullyAnalyzedModules[parentId] = true;
return requireTargets.map(({ id: dependencyId, allowProxy }, index) => {
// eslint-disable-next-line no-multi-assign
const isCommonJS = (parentMeta.isRequiredCommonJS[dependencyId] =
getTypeForFullyAnalyzedModule(dependencyId));
fullyAnalyzedModules[dependencyId] = true;
return {
source: sources[index].source,
id: allowProxy
? isCommonJS === IS_WRAPPED_COMMONJS
? wrapId(dependencyId, WRAPPED_SUFFIX)
: wrapId(dependencyId, PROXY_SUFFIX)
: dependencyId,
isCommonJS
};
});
},
isCurrentlyResolving(source, parentId) {
const currentlyResolvingForParent = currentlyResolving.get(parentId);
return currentlyResolvingForParent && currentlyResolvingForParent.has(source);
}
};
}