bare-module-traverse 2.3.1 → 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 +246 -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'
|
|
@@ -366,129 +411,181 @@ exports.imports = function* (parentURL, source, imports, artifacts, lexer, visit
|
|
|
366
411
|
condition = 'import'
|
|
367
412
|
}
|
|
368
413
|
|
|
369
|
-
if (entry.attributes.imports) {
|
|
370
|
-
const specifier = entry.attributes.imports
|
|
371
|
-
|
|
372
|
-
queue.push({
|
|
373
|
-
entry: {
|
|
374
|
-
type: 0,
|
|
375
|
-
specifier,
|
|
376
|
-
names: [],
|
|
377
|
-
attributes: {},
|
|
378
|
-
position: [0, 0, 0]
|
|
379
|
-
},
|
|
380
|
-
specifier,
|
|
381
|
-
condition: 'default'
|
|
382
|
-
})
|
|
383
|
-
}
|
|
384
|
-
|
|
385
414
|
lexer.imports.push(entry)
|
|
386
415
|
|
|
387
|
-
|
|
416
|
+
links.push(
|
|
417
|
+
exports.link(entry, specifier, condition, parentURL, imports, artifacts, visited, opts)
|
|
418
|
+
)
|
|
388
419
|
}
|
|
389
420
|
|
|
390
|
-
|
|
391
|
-
|
|
421
|
+
yield { links }
|
|
422
|
+
}
|
|
392
423
|
|
|
393
|
-
|
|
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
|
+
)
|
|
447
|
+
}
|
|
394
448
|
|
|
395
|
-
|
|
396
|
-
|
|
449
|
+
yield* resolveImport(entry, specifier, condition, parentURL, imports, artifacts, visited, opts)
|
|
450
|
+
}
|
|
397
451
|
|
|
398
|
-
|
|
399
|
-
|
|
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
|
|
400
459
|
|
|
401
|
-
|
|
402
|
-
const value = next.value
|
|
460
|
+
const matchedConditions = []
|
|
403
461
|
|
|
404
|
-
|
|
405
|
-
next = resolver.next(JSON.parse(yield { module: value.package }))
|
|
406
|
-
} else {
|
|
407
|
-
const url = value.resolution
|
|
462
|
+
opts = { ...opts, matchedConditions }
|
|
408
463
|
|
|
409
|
-
|
|
464
|
+
matchedConditions.push(condition)
|
|
410
465
|
|
|
411
|
-
|
|
466
|
+
const resolver = resolve(entry, parentURL, opts)
|
|
467
|
+
const candidates = []
|
|
412
468
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
url.protocol === linkedProtocol ||
|
|
416
|
-
url.protocol === deferredProtocol
|
|
417
|
-
) {
|
|
418
|
-
addResolution(imports, specifier, matchedConditions, url)
|
|
469
|
+
let next = resolver.next()
|
|
470
|
+
let resolutions = 0
|
|
419
471
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const prefix = url
|
|
472
|
+
while (next.done !== true) {
|
|
473
|
+
const value = next.value
|
|
423
474
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
475
|
+
if (value.package) {
|
|
476
|
+
next = resolver.next(JSON.parse(yield { module: value.package }))
|
|
477
|
+
} else {
|
|
478
|
+
const url = value.resolution
|
|
479
|
+
|
|
480
|
+
candidates.push(url)
|
|
481
|
+
|
|
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)
|
|
432
491
|
|
|
433
|
-
|
|
492
|
+
resolved = true
|
|
493
|
+
} else if (condition === 'asset') {
|
|
494
|
+
const prefix = url
|
|
434
495
|
|
|
435
|
-
|
|
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
|
|
436
505
|
}
|
|
437
506
|
|
|
438
|
-
|
|
439
|
-
} else {
|
|
440
|
-
const source = yield { module: url }
|
|
507
|
+
addURL(artifacts.assets, resolution)
|
|
441
508
|
|
|
442
|
-
|
|
443
|
-
|
|
509
|
+
resolved = true
|
|
510
|
+
}
|
|
444
511
|
|
|
445
|
-
|
|
512
|
+
if (resolved) addResolution(imports, specifier, matchedConditions, url)
|
|
513
|
+
} else if (condition === 'addon') {
|
|
514
|
+
let exists = yield { probe: url }
|
|
515
|
+
let source = null
|
|
446
516
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
517
|
+
if (exists === undefined) {
|
|
518
|
+
source = yield { module: url }
|
|
519
|
+
exists = source !== null
|
|
520
|
+
}
|
|
450
521
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
deferred: false
|
|
454
|
-
}
|
|
522
|
+
if (exists) {
|
|
523
|
+
resolution = yield* postresolve(url)
|
|
455
524
|
|
|
456
|
-
|
|
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
|
|
457
533
|
}
|
|
534
|
+
|
|
535
|
+
resolved = true
|
|
458
536
|
}
|
|
537
|
+
} else {
|
|
538
|
+
const source = yield { module: url }
|
|
459
539
|
|
|
460
|
-
if (
|
|
461
|
-
|
|
540
|
+
if (source !== null) {
|
|
541
|
+
resolution = yield* postresolve(url)
|
|
462
542
|
|
|
463
|
-
|
|
464
|
-
}
|
|
543
|
+
addResolution(imports, specifier, matchedConditions, exports.alias(resolution, opts))
|
|
465
544
|
|
|
466
|
-
|
|
467
|
-
}
|
|
468
|
-
}
|
|
545
|
+
let attributes = entry.attributes
|
|
469
546
|
|
|
470
|
-
|
|
547
|
+
if (attributes.imports) {
|
|
548
|
+
attributes = { ...attributes, imports: imports[attributes.imports].default }
|
|
549
|
+
}
|
|
471
550
|
|
|
472
|
-
|
|
473
|
-
|
|
551
|
+
yield {
|
|
552
|
+
children: exports.module(resolution, source, attributes, artifacts, visited, opts),
|
|
553
|
+
deferred: false
|
|
554
|
+
}
|
|
474
555
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
message += '\n' + candidates.map((url) => '- ' + url.href).join('\n')
|
|
556
|
+
resolved = true
|
|
557
|
+
}
|
|
478
558
|
}
|
|
479
559
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
throw errors.ASSET_NOT_FOUND(message, specifier, parentURL, candidates)
|
|
485
|
-
default:
|
|
486
|
-
throw errors.MODULE_NOT_FOUND(message, specifier, parentURL, candidates)
|
|
560
|
+
if (resolved) {
|
|
561
|
+
if (condition === 'addon') addURL(artifacts.addons, resolution)
|
|
562
|
+
|
|
563
|
+
resolutions++
|
|
487
564
|
}
|
|
565
|
+
|
|
566
|
+
next = resolver.next(resolved)
|
|
488
567
|
}
|
|
489
568
|
}
|
|
490
569
|
|
|
491
|
-
|
|
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
|
+
}
|
|
492
589
|
}
|
|
493
590
|
|
|
494
591
|
const ADDON_EXTENSION = /\.(bare|node)$/
|
|
@@ -502,12 +599,14 @@ exports.addons = function* (parentURL, artifacts, visited, opts = {}) {
|
|
|
502
599
|
prefix.pathname = prefix.pathname.replace(ADDON_EXTENSION, '') + '/'
|
|
503
600
|
|
|
504
601
|
for (const url of yield { prefix }) {
|
|
602
|
+
const resolution = yield* postresolve(url)
|
|
603
|
+
|
|
505
604
|
yield {
|
|
506
|
-
children: exports.module(
|
|
605
|
+
children: exports.module(resolution, null, {}, artifacts, visited, opts),
|
|
507
606
|
deferred: false
|
|
508
607
|
}
|
|
509
608
|
|
|
510
|
-
addURL(artifacts.addons,
|
|
609
|
+
addURL(artifacts.addons, resolution)
|
|
511
610
|
|
|
512
611
|
yielded = true
|
|
513
612
|
}
|
|
@@ -525,10 +624,12 @@ exports.assets = function* (patterns, parentURL, artifacts, visited, opts = {})
|
|
|
525
624
|
const source = yield { module: url }
|
|
526
625
|
|
|
527
626
|
if (source !== null) {
|
|
528
|
-
|
|
627
|
+
const resolution = yield* postresolve(url)
|
|
628
|
+
|
|
629
|
+
addURL(artifacts.assets, resolution)
|
|
529
630
|
|
|
530
631
|
yield {
|
|
531
|
-
children: exports.module(
|
|
632
|
+
children: exports.module(resolution, source, {}, artifacts, visited, {
|
|
532
633
|
...opts,
|
|
533
634
|
asset: true
|
|
534
635
|
}),
|
|
@@ -605,10 +706,10 @@ exports.patternMatches = function* patternMatches(pattern, parentURL, matches, o
|
|
|
605
706
|
return matches
|
|
606
707
|
}
|
|
607
708
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
709
|
+
function* postresolve(url) {
|
|
710
|
+
return (yield { resolution: url }) || url
|
|
711
|
+
}
|
|
712
|
+
|
|
612
713
|
function moduleType(url, attributes, info, opts = {}) {
|
|
613
714
|
const { defaultType = constants.SCRIPT, aliases = null } = opts
|
|
614
715
|
|
|
@@ -635,7 +736,7 @@ function moduleType(url, attributes, info, opts = {}) {
|
|
|
635
736
|
|
|
636
737
|
const match = url.pathname.match(/\.[a-z]+$/)
|
|
637
738
|
|
|
638
|
-
if (match === null) return
|
|
739
|
+
if (match === null) return defaultType
|
|
639
740
|
|
|
640
741
|
let [extension] = match
|
|
641
742
|
|
|
@@ -666,7 +767,7 @@ function moduleType(url, attributes, info, opts = {}) {
|
|
|
666
767
|
return constants.TEXT
|
|
667
768
|
}
|
|
668
769
|
|
|
669
|
-
return
|
|
770
|
+
return defaultType
|
|
670
771
|
}
|
|
671
772
|
|
|
672
773
|
function addURL(collection, url) {
|