bare-module-traverse 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -12
- package/index.d.ts +31 -6
- package/index.js +256 -145
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# bare-module-traverse
|
|
2
2
|
|
|
3
|
-
Low-level module graph traversal for Bare. The algorithm is implemented as a generator function that yields either modules to be read, prefixes to be listed, child dependencies to be traversed, or resolved dependencies of the module graph. As a convenience, the main export is a synchronous and asynchronous iterable that relies on modules being read and prefixes being listed by callbacks. For asynchronous iteration, the callbacks may return promises which will be awaited before being passed to the generator.
|
|
3
|
+
Low-level module graph traversal for Bare. The algorithm is implemented as a generator function that yields either modules to be read, modules to be probed for existence, resolutions to be transformed, prefixes to be listed, sets of imports to be resolved, child dependencies to be traversed, or resolved dependencies of the module graph. As a convenience, the main export is a synchronous and asynchronous iterable that relies on modules being read, modules being probed, resolutions being transformed, and prefixes being listed by callbacks. For asynchronous iteration, the callbacks may return promises which will be awaited before being passed to the generator.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
npm i bare-module-traverse
|
|
@@ -50,9 +50,9 @@ for await (const dependency of traverse(
|
|
|
50
50
|
|
|
51
51
|
## API
|
|
52
52
|
|
|
53
|
-
#### `const dependencies = traverse(url[, options], readModule[, listPrefix])`
|
|
53
|
+
#### `const dependencies = traverse(url[, options], readModule[, listPrefix[, probeModule[, resolveModule]]])`
|
|
54
54
|
|
|
55
|
-
Traverse the module graph rooted at `url`, which must be a WHATWG `URL` instance. `readModule` is called with a `URL` instance for every module to be read and must either return the module source, if it exists, or `null`. `listPrefix` is called with a `URL` instance of every prefix to be listed and must yield `URL` instances that have the specified `URL` as a prefix. If not provided, prefixes won't be traversed. If `readModule` returns a promise or `listPrefix` returns a promise generator, synchronous iteration is not supported.
|
|
55
|
+
Traverse the module graph rooted at `url`, which must be a WHATWG `URL` instance. `readModule` is called with a `URL` instance for every module to be read and must either return the module source, if it exists, or `null`. `listPrefix` is called with a `URL` instance of every prefix to be listed and must yield `URL` instances that have the specified `URL` as a prefix. If not provided, prefixes won't be traversed. `probeModule` is called with a `URL` instance to test whether a module exists and must return `true` if it does, `false` if it doesn't, or `undefined` if probing isn't supported; this lets existence be checked without reading the full module source, such as when locating an addon or asset. When it returns `undefined`, existence is instead determined by reading the module, so no source is read twice. If not provided, `probeModule` returns `undefined` and no probing is performed. `resolveModule` is called with a `URL` instance for every resolved, existing module and must return the `URL` to use in its place, applying any post-resolution transform; a file system implementation would canonicalize symlinks here with `realpath` so a module reached through different symlinks dedupes against its real location. If not provided, resolutions are used unchanged. If `readModule`, `probeModule`, or `resolveModule` returns a promise, or `listPrefix` returns a promise generator, synchronous iteration is not supported.
|
|
56
56
|
|
|
57
57
|
Options include:
|
|
58
58
|
|
|
@@ -165,22 +165,58 @@ The following generator functions implement the traversal algorithm. The yielded
|
|
|
165
165
|
|
|
166
166
|
**Source module**
|
|
167
167
|
|
|
168
|
+
A module to be read. The driver returns its source if it exists, otherwise `null`.
|
|
169
|
+
|
|
168
170
|
```js
|
|
169
171
|
next.value = {
|
|
170
172
|
module: URL
|
|
171
173
|
}
|
|
172
174
|
```
|
|
173
175
|
|
|
176
|
+
**Probed module**
|
|
177
|
+
|
|
178
|
+
A module whose existence is to be tested without reading its full source, such as when locating an addon or asset. The driver returns `true` if it exists, `false` if it doesn't, or `undefined` if probing isn't supported, in which case existence is instead determined by reading the module.
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
next.value = {
|
|
182
|
+
probe: URL
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Resolved module**
|
|
187
|
+
|
|
188
|
+
A resolved, existing module whose URL is to be transformed. The driver returns the URL to use in its place, applying any post-resolution transform, such as canonicalizing symlinks with `realpath`, or the URL unchanged.
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
next.value = {
|
|
192
|
+
resolution: URL
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
174
196
|
**File prefix**
|
|
175
197
|
|
|
198
|
+
A prefix to be listed. The driver returns the URLs that have it as a prefix, of which there may be none.
|
|
199
|
+
|
|
176
200
|
```js
|
|
177
201
|
next.value = {
|
|
178
202
|
prefix: URL
|
|
179
203
|
}
|
|
180
204
|
```
|
|
181
205
|
|
|
206
|
+
**Import set**
|
|
207
|
+
|
|
208
|
+
A set of independent imports to resolve. Each generator must be driven to completion before the parent generator is resumed, since the parent's resolved imports aren't complete until they are. The generators yield the same values as any other and may themselves yield dependency subgraphs to be traversed. A driver may drive them one at a time or, as their resolutions are independent, concurrently.
|
|
209
|
+
|
|
210
|
+
```js
|
|
211
|
+
next.value = {
|
|
212
|
+
links: [Generator]
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
182
216
|
**Dependency subgraph**
|
|
183
217
|
|
|
218
|
+
A child subgraph to be traversed by driving its generator as the parent is driven. If `deferred` is `true`, it must be traversed only once all non-deferred subgraphs have been, ensuring, for example, that a module reached both as an import and as an asset is claimed by the import traversal first.
|
|
219
|
+
|
|
184
220
|
```js
|
|
185
221
|
next.value = {
|
|
186
222
|
children: Generator,
|
|
@@ -190,6 +226,8 @@ next.value = {
|
|
|
190
226
|
|
|
191
227
|
**Dependency node**
|
|
192
228
|
|
|
229
|
+
A fully resolved node of the module graph and the traversal's output. This is what the iterable forms yield to the caller.
|
|
230
|
+
|
|
193
231
|
```js
|
|
194
232
|
next.value = {
|
|
195
233
|
dependency: {
|
|
@@ -211,14 +249,16 @@ next.value = {
|
|
|
211
249
|
}
|
|
212
250
|
```
|
|
213
251
|
|
|
214
|
-
To drive the generator functions, a
|
|
252
|
+
To drive the generator functions, a recursive routine like the following can be used:
|
|
215
253
|
|
|
216
254
|
```js
|
|
217
|
-
const
|
|
255
|
+
const artifacts = { addons: [], assets: [] }
|
|
256
|
+
const visited = new Set()
|
|
218
257
|
|
|
219
|
-
|
|
220
|
-
|
|
258
|
+
const queue = [traverse.module(url, null, {}, artifacts, visited)]
|
|
259
|
+
const deferred = []
|
|
221
260
|
|
|
261
|
+
function drive(generator) {
|
|
222
262
|
let next = generator.next()
|
|
223
263
|
|
|
224
264
|
while (next.done !== true) {
|
|
@@ -229,22 +269,46 @@ while (queue.length > 0) {
|
|
|
229
269
|
let source
|
|
230
270
|
|
|
231
271
|
next = generator.next(source)
|
|
272
|
+
} else if (value.probe) {
|
|
273
|
+
// Test whether `value.probe` exists, returning `true`, `false`, or
|
|
274
|
+
// `undefined` if probing isn't supported
|
|
275
|
+
let exists
|
|
276
|
+
|
|
277
|
+
next = generator.next(exists)
|
|
278
|
+
} else if (value.resolution) {
|
|
279
|
+
// Transform `value.resolution`, e.g. canonicalize it with `realpath`, or
|
|
280
|
+
// pass it through unchanged
|
|
281
|
+
let resolution = value.resolution
|
|
282
|
+
|
|
283
|
+
next = generator.next(resolution)
|
|
232
284
|
} else if (value.prefix) {
|
|
233
285
|
// List the modules that have `value.prefix` as a prefix
|
|
234
286
|
let modules
|
|
235
287
|
|
|
236
288
|
next = generator.next(modules)
|
|
289
|
+
} else if (value.links) {
|
|
290
|
+
// Drive each import to completion before resuming; their resolutions are
|
|
291
|
+
// independent, so a concurrent driver may instead drive them in parallel
|
|
292
|
+
for (const link of value.links) drive(link)
|
|
293
|
+
|
|
294
|
+
next = generator.next()
|
|
295
|
+
} else if (value.children) {
|
|
296
|
+
// Defer the subgraph if requested, otherwise traverse it next
|
|
297
|
+
if (value.deferred) deferred.push(value.children)
|
|
298
|
+
else queue.push(value.children)
|
|
299
|
+
|
|
300
|
+
next = generator.next()
|
|
237
301
|
} else {
|
|
238
|
-
|
|
239
|
-
queue.push(value.children)
|
|
240
|
-
} else {
|
|
241
|
-
const dependency = value.dependency
|
|
242
|
-
}
|
|
302
|
+
const dependency = value.dependency
|
|
243
303
|
|
|
244
304
|
next = generator.next()
|
|
245
305
|
}
|
|
246
306
|
}
|
|
247
307
|
}
|
|
308
|
+
|
|
309
|
+
while (queue.length > 0 || deferred.length > 0) {
|
|
310
|
+
drive(queue.length > 0 ? queue.pop() : deferred.shift())
|
|
311
|
+
}
|
|
248
312
|
```
|
|
249
313
|
|
|
250
314
|
Options are the same as `traverse()` for all functions.
|
|
@@ -260,6 +324,8 @@ Options are the same as `traverse()` for all functions.
|
|
|
260
324
|
|
|
261
325
|
#### `const generator = traverse.imports(parentURL, source, imports, artifacts, visited[, options])`
|
|
262
326
|
|
|
327
|
+
#### `const generator = traverse.link(entry, specifier, condition, parentURL, imports, artifacts, visited[, options])`
|
|
328
|
+
|
|
263
329
|
#### `const generator = traverse.addons(parentURL, artifacts, visited[, options])`
|
|
264
330
|
|
|
265
331
|
#### `const generator = traverse.assets(patterns, parentURL, artifacts, visited[, options])`
|
package/index.d.ts
CHANGED
|
@@ -42,36 +42,50 @@ interface TraverseOptions extends ResolveOptions {
|
|
|
42
42
|
declare function traverse(
|
|
43
43
|
entry: URL,
|
|
44
44
|
readModule: (url: URL) => Buffer | string | null,
|
|
45
|
-
listPrefix?: (url: URL) => Iterable<URL
|
|
45
|
+
listPrefix?: (url: URL) => Iterable<URL>,
|
|
46
|
+
probeModule?: (url: URL) => boolean | undefined,
|
|
47
|
+
resolveModule?: (url: URL) => URL
|
|
46
48
|
): Iterable<Dependency>
|
|
47
49
|
|
|
48
50
|
declare function traverse(
|
|
49
51
|
entry: URL,
|
|
50
52
|
readModule: (url: URL) => Promise<Buffer | string | null>,
|
|
51
|
-
listPrefix?: (url: URL) => AsyncIterable<URL
|
|
53
|
+
listPrefix?: (url: URL) => AsyncIterable<URL>,
|
|
54
|
+
probeModule?: (url: URL) => Promise<boolean | undefined>,
|
|
55
|
+
resolveModule?: (url: URL) => Promise<URL>
|
|
52
56
|
): AsyncIterable<Dependency>
|
|
53
57
|
|
|
54
58
|
declare function traverse(
|
|
55
59
|
entry: URL,
|
|
56
60
|
opts: TraverseOptions,
|
|
57
61
|
readModule: (url: URL) => Buffer | string | null,
|
|
58
|
-
listPrefix?: (url: URL) => Iterable<URL
|
|
62
|
+
listPrefix?: (url: URL) => Iterable<URL>,
|
|
63
|
+
probeModule?: (url: URL) => boolean | undefined,
|
|
64
|
+
resolveModule?: (url: URL) => URL
|
|
59
65
|
): Iterable<Dependency>
|
|
60
66
|
|
|
61
67
|
declare function traverse(
|
|
62
68
|
entry: URL,
|
|
63
69
|
opts: TraverseOptions,
|
|
64
70
|
readModule: (url: URL) => Promise<Buffer | string | null>,
|
|
65
|
-
listPrefix?: (url: URL) => AsyncIterable<URL
|
|
71
|
+
listPrefix?: (url: URL) => AsyncIterable<URL>,
|
|
72
|
+
probeModule?: (url: URL) => Promise<boolean | undefined>,
|
|
73
|
+
resolveModule?: (url: URL) => Promise<URL>
|
|
66
74
|
): AsyncIterable<Dependency>
|
|
67
75
|
|
|
68
76
|
declare namespace traverse {
|
|
69
77
|
export { type TraverseOptions }
|
|
70
78
|
|
|
71
79
|
export type Traversal = Generator<
|
|
72
|
-
|
|
80
|
+
| { module: URL }
|
|
81
|
+
| { probe: URL }
|
|
82
|
+
| { resolution: URL }
|
|
83
|
+
| { prefix: URL }
|
|
84
|
+
| { links: Traversal[] }
|
|
85
|
+
| { children: Traversal; deferred: boolean }
|
|
86
|
+
| { dependency: Dependency },
|
|
73
87
|
boolean,
|
|
74
|
-
void | URL[] | Buffer | string | null
|
|
88
|
+
void | URL | URL[] | Buffer | string | boolean | null
|
|
75
89
|
>
|
|
76
90
|
|
|
77
91
|
export interface Artifacts {
|
|
@@ -124,6 +138,17 @@ declare namespace traverse {
|
|
|
124
138
|
opts?: TraverseOptions
|
|
125
139
|
): Traversal
|
|
126
140
|
|
|
141
|
+
export function link(
|
|
142
|
+
entry: Import,
|
|
143
|
+
specifier: string,
|
|
144
|
+
condition: string,
|
|
145
|
+
parentURL: URL,
|
|
146
|
+
imports: ImportsMap,
|
|
147
|
+
artifacts: Artifacts,
|
|
148
|
+
visited: Set<string>,
|
|
149
|
+
opts?: TraverseOptions
|
|
150
|
+
): Traversal
|
|
151
|
+
|
|
127
152
|
export function prebuilds(
|
|
128
153
|
packageURL: URL,
|
|
129
154
|
artifacts: Artifacts,
|
package/index.js
CHANGED
|
@@ -13,13 +13,33 @@ const constants = {
|
|
|
13
13
|
TEXT: 7
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
function defaultResolveModule(url) {
|
|
17
|
+
return url
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function defaultProbeModule() {
|
|
21
|
+
return undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = exports = function traverse(
|
|
25
|
+
entry,
|
|
26
|
+
opts,
|
|
27
|
+
readModule,
|
|
28
|
+
listPrefix,
|
|
29
|
+
probeModule,
|
|
30
|
+
resolveModule
|
|
31
|
+
) {
|
|
17
32
|
if (typeof opts === 'function') {
|
|
33
|
+
resolveModule = probeModule
|
|
34
|
+
probeModule = listPrefix
|
|
18
35
|
listPrefix = readModule
|
|
19
36
|
readModule = opts
|
|
20
37
|
opts = {}
|
|
21
38
|
}
|
|
22
39
|
|
|
40
|
+
if (typeof resolveModule !== 'function') resolveModule = defaultResolveModule
|
|
41
|
+
if (typeof probeModule !== 'function') probeModule = defaultProbeModule
|
|
42
|
+
|
|
23
43
|
return {
|
|
24
44
|
*[Symbol.iterator]() {
|
|
25
45
|
const artifacts = { addons: [], assets: [] }
|
|
@@ -27,15 +47,9 @@ module.exports = exports = function traverse(entry, opts, readModule, listPrefix
|
|
|
27
47
|
const visited = opts.visited || new Set()
|
|
28
48
|
|
|
29
49
|
const queue = [exports.module(entry, null, {}, artifacts, visited, opts)]
|
|
30
|
-
|
|
31
|
-
// Asset traversal is deferred until the module graph has been fully
|
|
32
|
-
// drained so that a module imported both as an asset and as a regular
|
|
33
|
-
// module is typed by the regular import
|
|
34
50
|
const deferred = []
|
|
35
51
|
|
|
36
|
-
|
|
37
|
-
const generator = queue.length > 0 ? queue.pop() : deferred.shift()
|
|
38
|
-
|
|
52
|
+
function* drive(generator) {
|
|
39
53
|
let next = generator.next()
|
|
40
54
|
|
|
41
55
|
while (next.done !== true) {
|
|
@@ -43,6 +57,10 @@ module.exports = exports = function traverse(entry, opts, readModule, listPrefix
|
|
|
43
57
|
|
|
44
58
|
if (value.module) {
|
|
45
59
|
next = generator.next(readModule(value.module))
|
|
60
|
+
} else if (value.probe) {
|
|
61
|
+
next = generator.next(probeModule(value.probe))
|
|
62
|
+
} else if (value.resolution) {
|
|
63
|
+
next = generator.next(resolveModule(value.resolution))
|
|
46
64
|
} else if (value.prefix) {
|
|
47
65
|
const result = []
|
|
48
66
|
|
|
@@ -57,17 +75,27 @@ module.exports = exports = function traverse(entry, opts, readModule, listPrefix
|
|
|
57
75
|
}
|
|
58
76
|
|
|
59
77
|
next = generator.next(result)
|
|
78
|
+
} else if (value.links) {
|
|
79
|
+
for (const link of value.links) yield* drive(link)
|
|
80
|
+
|
|
81
|
+
next = generator.next()
|
|
82
|
+
} else if (value.children) {
|
|
83
|
+
if (value.deferred) deferred.push(value.children)
|
|
84
|
+
else queue.push(value.children)
|
|
85
|
+
|
|
86
|
+
next = generator.next()
|
|
60
87
|
} else {
|
|
61
|
-
|
|
62
|
-
if (value.deferred) deferred.push(value.children)
|
|
63
|
-
else queue.push(value.children)
|
|
64
|
-
} else yield value.dependency
|
|
88
|
+
yield value.dependency
|
|
65
89
|
|
|
66
90
|
next = generator.next()
|
|
67
91
|
}
|
|
68
92
|
}
|
|
69
93
|
}
|
|
70
94
|
|
|
95
|
+
while (queue.length > 0 || deferred.length > 0) {
|
|
96
|
+
yield* drive(queue.length > 0 ? queue.pop() : deferred.shift())
|
|
97
|
+
}
|
|
98
|
+
|
|
71
99
|
return artifacts
|
|
72
100
|
},
|
|
73
101
|
|
|
@@ -77,15 +105,9 @@ module.exports = exports = function traverse(entry, opts, readModule, listPrefix
|
|
|
77
105
|
const visited = opts.visited || new Set()
|
|
78
106
|
|
|
79
107
|
const queue = [exports.module(entry, null, {}, artifacts, visited, opts)]
|
|
80
|
-
|
|
81
|
-
// Asset traversal is deferred until the module graph has been fully
|
|
82
|
-
// drained so that a module imported both as an asset and as a regular
|
|
83
|
-
// module is typed by the regular import.
|
|
84
108
|
const deferred = []
|
|
85
109
|
|
|
86
|
-
|
|
87
|
-
const generator = queue.length > 0 ? queue.pop() : deferred.shift()
|
|
88
|
-
|
|
110
|
+
async function* drive(generator) {
|
|
89
111
|
let next = generator.next()
|
|
90
112
|
|
|
91
113
|
while (next.done !== true) {
|
|
@@ -93,6 +115,10 @@ module.exports = exports = function traverse(entry, opts, readModule, listPrefix
|
|
|
93
115
|
|
|
94
116
|
if (value.module) {
|
|
95
117
|
next = generator.next(await readModule(value.module))
|
|
118
|
+
} else if (value.probe) {
|
|
119
|
+
next = generator.next(await probeModule(value.probe))
|
|
120
|
+
} else if (value.resolution) {
|
|
121
|
+
next = generator.next(await resolveModule(value.resolution))
|
|
96
122
|
} else if (value.prefix) {
|
|
97
123
|
const result = []
|
|
98
124
|
|
|
@@ -107,17 +133,27 @@ module.exports = exports = function traverse(entry, opts, readModule, listPrefix
|
|
|
107
133
|
}
|
|
108
134
|
|
|
109
135
|
next = generator.next(result)
|
|
136
|
+
} else if (value.links) {
|
|
137
|
+
for (const link of value.links) yield* drive(link)
|
|
138
|
+
|
|
139
|
+
next = generator.next()
|
|
140
|
+
} else if (value.children) {
|
|
141
|
+
if (value.deferred) deferred.push(value.children)
|
|
142
|
+
else queue.push(value.children)
|
|
143
|
+
|
|
144
|
+
next = generator.next()
|
|
110
145
|
} else {
|
|
111
|
-
|
|
112
|
-
if (value.deferred) deferred.push(value.children)
|
|
113
|
-
else queue.push(value.children)
|
|
114
|
-
} else yield value.dependency
|
|
146
|
+
yield value.dependency
|
|
115
147
|
|
|
116
148
|
next = generator.next()
|
|
117
149
|
}
|
|
118
150
|
}
|
|
119
151
|
}
|
|
120
152
|
|
|
153
|
+
while (queue.length > 0 || deferred.length > 0) {
|
|
154
|
+
yield* drive(queue.length > 0 ? queue.pop() : deferred.shift())
|
|
155
|
+
}
|
|
156
|
+
|
|
121
157
|
return artifacts
|
|
122
158
|
}
|
|
123
159
|
}
|
|
@@ -147,16 +183,28 @@ exports.alias = function alias(url, opts = {}) {
|
|
|
147
183
|
}
|
|
148
184
|
|
|
149
185
|
exports.module = function* (url, source, attributes, artifacts, visited, opts = {}) {
|
|
150
|
-
const { resolutions = null, asset = false } = opts
|
|
186
|
+
const { resolutions = null, asset = false, probed } = opts
|
|
151
187
|
|
|
152
188
|
if (visited.has(url.href)) return false
|
|
153
189
|
|
|
154
190
|
visited.add(url.href)
|
|
155
191
|
|
|
192
|
+
if (probed !== undefined) opts = { ...opts, probed: undefined }
|
|
193
|
+
|
|
194
|
+
attributes = attributes || {}
|
|
195
|
+
|
|
196
|
+
const artifact = asset === true || moduleType(url, attributes, null, opts) === constants.ADDON
|
|
197
|
+
|
|
156
198
|
if (source === null) {
|
|
199
|
+
const exists = probed !== undefined ? probed : artifact ? yield { probe: url } : undefined
|
|
200
|
+
|
|
201
|
+
if (exists === false) {
|
|
202
|
+
throw errors.MODULE_NOT_FOUND(`Cannot find module '${url.href}'`, url.href)
|
|
203
|
+
}
|
|
204
|
+
|
|
157
205
|
source = yield { module: url }
|
|
158
206
|
|
|
159
|
-
if (source === null) {
|
|
207
|
+
if (exists !== true && source === null) {
|
|
160
208
|
throw errors.MODULE_NOT_FOUND(`Cannot find module '${url.href}'`, url.href)
|
|
161
209
|
}
|
|
162
210
|
}
|
|
@@ -167,8 +215,6 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
|
|
|
167
215
|
}
|
|
168
216
|
}
|
|
169
217
|
|
|
170
|
-
attributes = attributes || {}
|
|
171
|
-
|
|
172
218
|
const imports = {}
|
|
173
219
|
|
|
174
220
|
let info = null
|
|
@@ -207,10 +253,6 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
|
|
|
207
253
|
|
|
208
254
|
const lexer = { imports: [], exports: [] }
|
|
209
255
|
|
|
210
|
-
// Assets are opaque; their contents are never interpreted, so we only recurse
|
|
211
|
-
// into modules that are actually imported as modules. An asset that happens
|
|
212
|
-
// to be a script, module, or addon is not lexed and its imports and addons
|
|
213
|
-
// are not followed.
|
|
214
256
|
if (asset === false) {
|
|
215
257
|
if (type === constants.SCRIPT || type === constants.MODULE) {
|
|
216
258
|
yield* exports.imports(url, source, imports, artifacts, lexer, visited, opts)
|
|
@@ -281,10 +323,10 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
|
|
|
281
323
|
if (typeof imports !== 'object' || imports === null) return false
|
|
282
324
|
|
|
283
325
|
for (const [specifier, entry] of Object.entries(imports)) {
|
|
284
|
-
const stack = [entry]
|
|
326
|
+
const stack = [{ entry, asset: false }]
|
|
285
327
|
|
|
286
328
|
while (stack.length > 0) {
|
|
287
|
-
const entry = stack.pop()
|
|
329
|
+
const { entry, asset } = stack.pop()
|
|
288
330
|
|
|
289
331
|
if (typeof entry === 'string') {
|
|
290
332
|
const url = new URL(entry)
|
|
@@ -294,6 +336,20 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
|
|
|
294
336
|
children: exports.package(url, null, artifacts, visited, opts),
|
|
295
337
|
deferred: false
|
|
296
338
|
}
|
|
339
|
+
} else if (asset) {
|
|
340
|
+
addURL(artifacts.assets, url)
|
|
341
|
+
|
|
342
|
+
const source = yield { module: url }
|
|
343
|
+
|
|
344
|
+
if (source !== null) {
|
|
345
|
+
yield {
|
|
346
|
+
children: exports.module(url, source, {}, artifacts, visited, {
|
|
347
|
+
...opts,
|
|
348
|
+
asset: true
|
|
349
|
+
}),
|
|
350
|
+
deferred: true
|
|
351
|
+
}
|
|
352
|
+
}
|
|
297
353
|
} else if (
|
|
298
354
|
url.protocol !== builtinProtocol &&
|
|
299
355
|
url.protocol !== linkedProtocol &&
|
|
@@ -305,14 +361,13 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
|
|
|
305
361
|
}
|
|
306
362
|
}
|
|
307
363
|
} else {
|
|
308
|
-
|
|
364
|
+
for (const [condition, child] of Object.entries(entry)) {
|
|
365
|
+
stack.push({ entry: child, asset: asset || condition === 'asset' })
|
|
366
|
+
}
|
|
309
367
|
}
|
|
310
368
|
}
|
|
311
369
|
}
|
|
312
370
|
|
|
313
|
-
// The type is derived from the extension and default type alone; unlike
|
|
314
|
-
// `module`, the preresolved path does not read the package scope, so any
|
|
315
|
-
// `package.json` type declaration is not consulted here.
|
|
316
371
|
const type = moduleType(url, {}, null, opts)
|
|
317
372
|
|
|
318
373
|
const lexer = { imports: [], exports: [] }
|
|
@@ -335,22 +390,12 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
|
|
|
335
390
|
}
|
|
336
391
|
|
|
337
392
|
exports.imports = function* (parentURL, source, imports, artifacts, lexer, visited, opts = {}) {
|
|
338
|
-
const {
|
|
339
|
-
resolve = exports.resolve.default,
|
|
340
|
-
builtinProtocol = 'builtin:',
|
|
341
|
-
linkedProtocol = 'linked:',
|
|
342
|
-
deferredProtocol = 'deferred:',
|
|
343
|
-
matchedConditions = []
|
|
344
|
-
} = opts
|
|
345
|
-
|
|
346
|
-
let yielded = false
|
|
347
|
-
|
|
348
|
-
const queue = []
|
|
349
|
-
|
|
350
393
|
const lexed = lex(source)
|
|
351
394
|
|
|
352
395
|
lexer.exports = lexed.exports
|
|
353
396
|
|
|
397
|
+
const links = []
|
|
398
|
+
|
|
354
399
|
for (const entry of lexed.imports) {
|
|
355
400
|
let specifier = entry.specifier
|
|
356
401
|
let condition = 'default'
|
|
@@ -360,131 +405,187 @@ exports.imports = function* (parentURL, source, imports, artifacts, lexer, visit
|
|
|
360
405
|
condition = 'addon'
|
|
361
406
|
} else if (entry.type & lex.constants.ASSET) {
|
|
362
407
|
condition = 'asset'
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (entry.
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
queue.push({
|
|
369
|
-
entry: {
|
|
370
|
-
type: 0,
|
|
371
|
-
specifier,
|
|
372
|
-
names: [],
|
|
373
|
-
attributes: {},
|
|
374
|
-
position: [0, 0, 0]
|
|
375
|
-
},
|
|
376
|
-
specifier,
|
|
377
|
-
condition: 'default'
|
|
378
|
-
})
|
|
408
|
+
} else if (entry.type & lex.constants.REQUIRE) {
|
|
409
|
+
condition = 'require'
|
|
410
|
+
} else if (entry.type & lex.constants.IMPORT) {
|
|
411
|
+
condition = 'import'
|
|
379
412
|
}
|
|
380
413
|
|
|
381
414
|
lexer.imports.push(entry)
|
|
382
415
|
|
|
383
|
-
|
|
416
|
+
links.push(
|
|
417
|
+
exports.link(entry, specifier, condition, parentURL, imports, artifacts, visited, opts)
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
yield { links }
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
exports.link = function* (
|
|
425
|
+
entry,
|
|
426
|
+
specifier,
|
|
427
|
+
condition,
|
|
428
|
+
parentURL,
|
|
429
|
+
imports,
|
|
430
|
+
artifacts,
|
|
431
|
+
visited,
|
|
432
|
+
opts = {}
|
|
433
|
+
) {
|
|
434
|
+
if (entry.attributes.imports) {
|
|
435
|
+
const specifier = entry.attributes.imports
|
|
436
|
+
|
|
437
|
+
yield* resolveImport(
|
|
438
|
+
{ type: 0, specifier, names: [], attributes: {}, position: [0, 0, 0] },
|
|
439
|
+
specifier,
|
|
440
|
+
'default',
|
|
441
|
+
parentURL,
|
|
442
|
+
imports,
|
|
443
|
+
artifacts,
|
|
444
|
+
visited,
|
|
445
|
+
opts
|
|
446
|
+
)
|
|
384
447
|
}
|
|
385
448
|
|
|
386
|
-
|
|
387
|
-
|
|
449
|
+
yield* resolveImport(entry, specifier, condition, parentURL, imports, artifacts, visited, opts)
|
|
450
|
+
}
|
|
388
451
|
|
|
389
|
-
|
|
452
|
+
function* resolveImport(entry, specifier, condition, parentURL, imports, artifacts, visited, opts) {
|
|
453
|
+
const {
|
|
454
|
+
resolve = exports.resolve.default,
|
|
455
|
+
builtinProtocol = 'builtin:',
|
|
456
|
+
linkedProtocol = 'linked:',
|
|
457
|
+
deferredProtocol = 'deferred:'
|
|
458
|
+
} = opts
|
|
390
459
|
|
|
391
|
-
|
|
392
|
-
const candidates = []
|
|
460
|
+
const matchedConditions = []
|
|
393
461
|
|
|
394
|
-
|
|
395
|
-
let resolutions = 0
|
|
462
|
+
opts = { ...opts, matchedConditions }
|
|
396
463
|
|
|
397
|
-
|
|
398
|
-
const value = next.value
|
|
464
|
+
matchedConditions.push(condition)
|
|
399
465
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
} else {
|
|
403
|
-
const url = value.resolution
|
|
466
|
+
const resolver = resolve(entry, parentURL, opts)
|
|
467
|
+
const candidates = []
|
|
404
468
|
|
|
405
|
-
|
|
469
|
+
let next = resolver.next()
|
|
470
|
+
let resolutions = 0
|
|
406
471
|
|
|
407
|
-
|
|
472
|
+
while (next.done !== true) {
|
|
473
|
+
const value = next.value
|
|
408
474
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
) {
|
|
414
|
-
addResolution(imports, specifier, matchedConditions, url)
|
|
475
|
+
if (value.package) {
|
|
476
|
+
next = resolver.next(JSON.parse(yield { module: value.package }))
|
|
477
|
+
} else {
|
|
478
|
+
const url = value.resolution
|
|
415
479
|
|
|
416
|
-
|
|
417
|
-
} else if (condition === 'asset') {
|
|
418
|
-
const prefix = url
|
|
480
|
+
candidates.push(url)
|
|
419
481
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
482
|
+
let resolved = false
|
|
483
|
+
let resolution = url
|
|
484
|
+
|
|
485
|
+
if (
|
|
486
|
+
url.protocol === builtinProtocol ||
|
|
487
|
+
url.protocol === linkedProtocol ||
|
|
488
|
+
url.protocol === deferredProtocol
|
|
489
|
+
) {
|
|
490
|
+
addResolution(imports, specifier, matchedConditions, url)
|
|
428
491
|
|
|
429
|
-
|
|
492
|
+
resolved = true
|
|
493
|
+
} else if (condition === 'asset') {
|
|
494
|
+
const prefix = url
|
|
430
495
|
|
|
431
|
-
|
|
496
|
+
for (const url of yield { prefix }) {
|
|
497
|
+
const resolution = yield* postresolve(url)
|
|
498
|
+
|
|
499
|
+
yield {
|
|
500
|
+
children: exports.module(resolution, null, {}, artifacts, visited, {
|
|
501
|
+
...opts,
|
|
502
|
+
asset: true
|
|
503
|
+
}),
|
|
504
|
+
deferred: true
|
|
432
505
|
}
|
|
433
506
|
|
|
434
|
-
|
|
435
|
-
} else {
|
|
436
|
-
const source = yield { module: url }
|
|
507
|
+
addURL(artifacts.assets, resolution)
|
|
437
508
|
|
|
438
|
-
|
|
439
|
-
|
|
509
|
+
resolved = true
|
|
510
|
+
}
|
|
440
511
|
|
|
441
|
-
|
|
512
|
+
if (resolved) addResolution(imports, specifier, matchedConditions, url)
|
|
513
|
+
} else if (condition === 'addon') {
|
|
514
|
+
let exists = yield { probe: url }
|
|
515
|
+
let source = null
|
|
442
516
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
517
|
+
if (exists === undefined) {
|
|
518
|
+
source = yield { module: url }
|
|
519
|
+
exists = source !== null
|
|
520
|
+
}
|
|
446
521
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
deferred: false
|
|
450
|
-
}
|
|
522
|
+
if (exists) {
|
|
523
|
+
resolution = yield* postresolve(url)
|
|
451
524
|
|
|
452
|
-
|
|
525
|
+
addResolution(imports, specifier, matchedConditions, resolution)
|
|
526
|
+
|
|
527
|
+
yield {
|
|
528
|
+
children: exports.module(resolution, source, {}, artifacts, visited, {
|
|
529
|
+
...opts,
|
|
530
|
+
probed: true
|
|
531
|
+
}),
|
|
532
|
+
deferred: false
|
|
453
533
|
}
|
|
534
|
+
|
|
535
|
+
resolved = true
|
|
454
536
|
}
|
|
537
|
+
} else {
|
|
538
|
+
const source = yield { module: url }
|
|
455
539
|
|
|
456
|
-
if (
|
|
457
|
-
|
|
540
|
+
if (source !== null) {
|
|
541
|
+
resolution = yield* postresolve(url)
|
|
458
542
|
|
|
459
|
-
|
|
460
|
-
}
|
|
543
|
+
addResolution(imports, specifier, matchedConditions, exports.alias(resolution, opts))
|
|
461
544
|
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
}
|
|
545
|
+
let attributes = entry.attributes
|
|
465
546
|
|
|
466
|
-
|
|
547
|
+
if (attributes.imports) {
|
|
548
|
+
attributes = { ...attributes, imports: imports[attributes.imports].default }
|
|
549
|
+
}
|
|
467
550
|
|
|
468
|
-
|
|
469
|
-
|
|
551
|
+
yield {
|
|
552
|
+
children: exports.module(resolution, source, attributes, artifacts, visited, opts),
|
|
553
|
+
deferred: false
|
|
554
|
+
}
|
|
470
555
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
message += '\n' + candidates.map((url) => '- ' + url.href).join('\n')
|
|
556
|
+
resolved = true
|
|
557
|
+
}
|
|
474
558
|
}
|
|
475
559
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
throw errors.ASSET_NOT_FOUND(message, specifier, parentURL, candidates)
|
|
481
|
-
default:
|
|
482
|
-
throw errors.MODULE_NOT_FOUND(message, specifier, parentURL, candidates)
|
|
560
|
+
if (resolved) {
|
|
561
|
+
if (condition === 'addon') addURL(artifacts.addons, resolution)
|
|
562
|
+
|
|
563
|
+
resolutions++
|
|
483
564
|
}
|
|
565
|
+
|
|
566
|
+
next = resolver.next(resolved)
|
|
484
567
|
}
|
|
485
568
|
}
|
|
486
569
|
|
|
487
|
-
|
|
570
|
+
matchedConditions.pop()
|
|
571
|
+
|
|
572
|
+
if (resolutions === 0) {
|
|
573
|
+
let message = `Cannot find ${condition === 'addon' || condition === 'asset' ? condition : 'module'} '${specifier}' imported from '${parentURL.href}'`
|
|
574
|
+
|
|
575
|
+
if (candidates.length > 0) {
|
|
576
|
+
message += '\nCandidates:'
|
|
577
|
+
message += '\n' + candidates.map((url) => '- ' + url.href).join('\n')
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
switch (condition) {
|
|
581
|
+
case 'addon':
|
|
582
|
+
throw errors.ADDON_NOT_FOUND(message, specifier, parentURL, candidates)
|
|
583
|
+
case 'asset':
|
|
584
|
+
throw errors.ASSET_NOT_FOUND(message, specifier, parentURL, candidates)
|
|
585
|
+
default:
|
|
586
|
+
throw errors.MODULE_NOT_FOUND(message, specifier, parentURL, candidates)
|
|
587
|
+
}
|
|
588
|
+
}
|
|
488
589
|
}
|
|
489
590
|
|
|
490
591
|
const ADDON_EXTENSION = /\.(bare|node)$/
|
|
@@ -498,12 +599,14 @@ exports.addons = function* (parentURL, artifacts, visited, opts = {}) {
|
|
|
498
599
|
prefix.pathname = prefix.pathname.replace(ADDON_EXTENSION, '') + '/'
|
|
499
600
|
|
|
500
601
|
for (const url of yield { prefix }) {
|
|
602
|
+
const resolution = yield* postresolve(url)
|
|
603
|
+
|
|
501
604
|
yield {
|
|
502
|
-
children: exports.module(
|
|
605
|
+
children: exports.module(resolution, null, {}, artifacts, visited, opts),
|
|
503
606
|
deferred: false
|
|
504
607
|
}
|
|
505
608
|
|
|
506
|
-
addURL(artifacts.addons,
|
|
609
|
+
addURL(artifacts.addons, resolution)
|
|
507
610
|
|
|
508
611
|
yielded = true
|
|
509
612
|
}
|
|
@@ -521,10 +624,12 @@ exports.assets = function* (patterns, parentURL, artifacts, visited, opts = {})
|
|
|
521
624
|
const source = yield { module: url }
|
|
522
625
|
|
|
523
626
|
if (source !== null) {
|
|
524
|
-
|
|
627
|
+
const resolution = yield* postresolve(url)
|
|
628
|
+
|
|
629
|
+
addURL(artifacts.assets, resolution)
|
|
525
630
|
|
|
526
631
|
yield {
|
|
527
|
-
children: exports.module(
|
|
632
|
+
children: exports.module(resolution, source, {}, artifacts, visited, {
|
|
528
633
|
...opts,
|
|
529
634
|
asset: true
|
|
530
635
|
}),
|
|
@@ -601,10 +706,10 @@ exports.patternMatches = function* patternMatches(pattern, parentURL, matches, o
|
|
|
601
706
|
return matches
|
|
602
707
|
}
|
|
603
708
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
709
|
+
function* postresolve(url) {
|
|
710
|
+
return (yield { resolution: url }) || url
|
|
711
|
+
}
|
|
712
|
+
|
|
608
713
|
function moduleType(url, attributes, info, opts = {}) {
|
|
609
714
|
const { defaultType = constants.SCRIPT, aliases = null } = opts
|
|
610
715
|
|
|
@@ -631,7 +736,7 @@ function moduleType(url, attributes, info, opts = {}) {
|
|
|
631
736
|
|
|
632
737
|
const match = url.pathname.match(/\.[a-z]+$/)
|
|
633
738
|
|
|
634
|
-
if (match === null) return
|
|
739
|
+
if (match === null) return defaultType
|
|
635
740
|
|
|
636
741
|
let [extension] = match
|
|
637
742
|
|
|
@@ -662,7 +767,7 @@ function moduleType(url, attributes, info, opts = {}) {
|
|
|
662
767
|
return constants.TEXT
|
|
663
768
|
}
|
|
664
769
|
|
|
665
|
-
return
|
|
770
|
+
return defaultType
|
|
666
771
|
}
|
|
667
772
|
|
|
668
773
|
function addURL(collection, url) {
|
|
@@ -765,6 +870,12 @@ function compressImportsMapEntry(resolved) {
|
|
|
765
870
|
if (entry[0] === 'default') primary = entry[1]
|
|
766
871
|
}
|
|
767
872
|
|
|
873
|
+
if (entries.length === 0) return resolved
|
|
874
|
+
|
|
875
|
+
const [, first] = entries[0]
|
|
876
|
+
|
|
877
|
+
if (entries.every(([, resolved]) => resolved === first)) return first
|
|
878
|
+
|
|
768
879
|
entries = entries.filter(
|
|
769
880
|
([condition, resolved]) => condition === 'default' || resolved !== primary
|
|
770
881
|
)
|