bare-module-traverse 2.3.1 → 2.4.1
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 +84 -15
- package/index.d.ts +38 -9
- package/index.js +257 -161
- 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,59 @@ 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`. When `artifact` is `true`, the module is an addon or asset whose contents are loaded lazily and referenced by path. A driver that only needs to locate such artifacts, such as a module loader, may return `null` without reading, having already established existence by probing; a driver that embeds their contents, such as a bundler, reads them as normal.
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
next.value = {
|
|
172
|
+
module: URL,
|
|
173
|
+
artifact: boolean
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Probed module**
|
|
178
|
+
|
|
179
|
+
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.
|
|
180
|
+
|
|
168
181
|
```js
|
|
169
182
|
next.value = {
|
|
170
|
-
|
|
183
|
+
probe: URL
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Resolved module**
|
|
188
|
+
|
|
189
|
+
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.
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
next.value = {
|
|
193
|
+
resolution: URL
|
|
171
194
|
}
|
|
172
195
|
```
|
|
173
196
|
|
|
174
197
|
**File prefix**
|
|
175
198
|
|
|
199
|
+
A prefix to be listed. The driver returns the URLs that have it as a prefix, of which there may be none.
|
|
200
|
+
|
|
176
201
|
```js
|
|
177
202
|
next.value = {
|
|
178
203
|
prefix: URL
|
|
179
204
|
}
|
|
180
205
|
```
|
|
181
206
|
|
|
207
|
+
**Import set**
|
|
208
|
+
|
|
209
|
+
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.
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
next.value = {
|
|
213
|
+
links: [Generator]
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
182
217
|
**Dependency subgraph**
|
|
183
218
|
|
|
219
|
+
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.
|
|
220
|
+
|
|
184
221
|
```js
|
|
185
222
|
next.value = {
|
|
186
223
|
children: Generator,
|
|
@@ -190,6 +227,8 @@ next.value = {
|
|
|
190
227
|
|
|
191
228
|
**Dependency node**
|
|
192
229
|
|
|
230
|
+
A fully resolved node of the module graph and the traversal's output. This is what the iterable forms yield to the caller.
|
|
231
|
+
|
|
193
232
|
```js
|
|
194
233
|
next.value = {
|
|
195
234
|
dependency: {
|
|
@@ -211,40 +250,68 @@ next.value = {
|
|
|
211
250
|
}
|
|
212
251
|
```
|
|
213
252
|
|
|
214
|
-
To drive the generator functions, a
|
|
253
|
+
To drive the generator functions, a recursive routine like the following can be used:
|
|
215
254
|
|
|
216
255
|
```js
|
|
217
|
-
const
|
|
256
|
+
const artifacts = { addons: [], assets: [] }
|
|
257
|
+
const visited = new Set()
|
|
218
258
|
|
|
219
|
-
|
|
220
|
-
|
|
259
|
+
const queue = [traverse.module(url, null, {}, artifacts, visited)]
|
|
260
|
+
const deferred = []
|
|
221
261
|
|
|
262
|
+
function drive(generator) {
|
|
222
263
|
let next = generator.next()
|
|
223
264
|
|
|
224
265
|
while (next.done !== true) {
|
|
225
266
|
const value = next.value
|
|
226
267
|
|
|
227
268
|
if (value.module) {
|
|
228
|
-
// Read `value.module` if it exists, otherwise `null
|
|
269
|
+
// Read `value.module` if it exists, otherwise `null`. When
|
|
270
|
+
// `value.artifact` is `true`, the module is an addon or asset that may be
|
|
271
|
+
// left unread, returning `null`, unless its contents are needed
|
|
229
272
|
let source
|
|
230
273
|
|
|
231
274
|
next = generator.next(source)
|
|
275
|
+
} else if (value.probe) {
|
|
276
|
+
// Test whether `value.probe` exists, returning `true`, `false`, or
|
|
277
|
+
// `undefined` if probing isn't supported
|
|
278
|
+
let exists
|
|
279
|
+
|
|
280
|
+
next = generator.next(exists)
|
|
281
|
+
} else if (value.resolution) {
|
|
282
|
+
// Transform `value.resolution`, e.g. canonicalize it with `realpath`, or
|
|
283
|
+
// pass it through unchanged
|
|
284
|
+
let resolution = value.resolution
|
|
285
|
+
|
|
286
|
+
next = generator.next(resolution)
|
|
232
287
|
} else if (value.prefix) {
|
|
233
288
|
// List the modules that have `value.prefix` as a prefix
|
|
234
289
|
let modules
|
|
235
290
|
|
|
236
291
|
next = generator.next(modules)
|
|
292
|
+
} else if (value.links) {
|
|
293
|
+
// Drive each import to completion before resuming; their resolutions are
|
|
294
|
+
// independent, so a concurrent driver may instead drive them in parallel
|
|
295
|
+
for (const link of value.links) drive(link)
|
|
296
|
+
|
|
297
|
+
next = generator.next()
|
|
298
|
+
} else if (value.children) {
|
|
299
|
+
// Defer the subgraph if requested, otherwise traverse it next
|
|
300
|
+
if (value.deferred) deferred.push(value.children)
|
|
301
|
+
else queue.push(value.children)
|
|
302
|
+
|
|
303
|
+
next = generator.next()
|
|
237
304
|
} else {
|
|
238
|
-
|
|
239
|
-
queue.push(value.children)
|
|
240
|
-
} else {
|
|
241
|
-
const dependency = value.dependency
|
|
242
|
-
}
|
|
305
|
+
const dependency = value.dependency
|
|
243
306
|
|
|
244
307
|
next = generator.next()
|
|
245
308
|
}
|
|
246
309
|
}
|
|
247
310
|
}
|
|
311
|
+
|
|
312
|
+
while (queue.length > 0 || deferred.length > 0) {
|
|
313
|
+
drive(queue.length > 0 ? queue.pop() : deferred.shift())
|
|
314
|
+
}
|
|
248
315
|
```
|
|
249
316
|
|
|
250
317
|
Options are the same as `traverse()` for all functions.
|
|
@@ -258,7 +325,9 @@ Options are the same as `traverse()` for all functions.
|
|
|
258
325
|
|
|
259
326
|
#### `const generator = traverse.preresolved(url, source, resolutions, artifacts, visited[, options])`
|
|
260
327
|
|
|
261
|
-
#### `const generator = traverse.imports(parentURL, source, imports, artifacts, visited[, options])`
|
|
328
|
+
#### `const generator = traverse.imports(parentURL, source, imports, artifacts, lexer, visited[, options])`
|
|
329
|
+
|
|
330
|
+
#### `const generator = traverse.link(entry, specifier, condition, parentURL, imports, artifacts, visited[, options])`
|
|
262
331
|
|
|
263
332
|
#### `const generator = traverse.addons(parentURL, artifacts, visited[, options])`
|
|
264
333
|
|
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; artifact: boolean }
|
|
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 {
|
|
@@ -120,12 +134,27 @@ declare namespace traverse {
|
|
|
120
134
|
source: string | Buffer,
|
|
121
135
|
imports: ImportsMap,
|
|
122
136
|
artifacts: Artifacts,
|
|
137
|
+
lexer: {
|
|
138
|
+
imports: Import[]
|
|
139
|
+
exports: Export[]
|
|
140
|
+
},
|
|
123
141
|
visited: Set<string>,
|
|
124
142
|
opts?: TraverseOptions
|
|
125
143
|
): Traversal
|
|
126
144
|
|
|
127
|
-
export function
|
|
128
|
-
|
|
145
|
+
export function link(
|
|
146
|
+
entry: Import,
|
|
147
|
+
specifier: string,
|
|
148
|
+
condition: string,
|
|
149
|
+
parentURL: URL,
|
|
150
|
+
imports: ImportsMap,
|
|
151
|
+
artifacts: Artifacts,
|
|
152
|
+
visited: Set<string>,
|
|
153
|
+
opts?: TraverseOptions
|
|
154
|
+
): Traversal
|
|
155
|
+
|
|
156
|
+
export function addons(
|
|
157
|
+
parentURL: URL,
|
|
129
158
|
artifacts: Artifacts,
|
|
130
159
|
visited: Set<string>,
|
|
131
160
|
opts?: TraverseOptions
|
|
@@ -133,7 +162,7 @@ declare namespace traverse {
|
|
|
133
162
|
|
|
134
163
|
export function assets(
|
|
135
164
|
patterns: ConditionalSpecifier,
|
|
136
|
-
|
|
165
|
+
parentURL: URL,
|
|
137
166
|
artifacts: Artifacts,
|
|
138
167
|
visited: Set<string>,
|
|
139
168
|
opts?: TraverseOptions
|
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) {
|
|
157
|
-
|
|
199
|
+
const exists = probed !== undefined ? probed : artifact ? yield { probe: url } : undefined
|
|
158
200
|
|
|
159
|
-
if (
|
|
201
|
+
if (exists === false) {
|
|
202
|
+
throw errors.MODULE_NOT_FOUND(`Cannot find module '${url.href}'`, url.href)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
source = yield { module: url, artifact }
|
|
206
|
+
|
|
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,14 +215,12 @@ 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
|
|
175
221
|
|
|
176
222
|
for (const packageURL of lookupPackageScope(url, opts)) {
|
|
177
|
-
const source = yield { module: packageURL }
|
|
223
|
+
const source = yield { module: packageURL, artifact: false }
|
|
178
224
|
|
|
179
225
|
if (source !== null) {
|
|
180
226
|
info = JSON.parse(source)
|
|
@@ -193,7 +239,7 @@ exports.module = function* (url, source, attributes, artifacts, visited, opts =
|
|
|
193
239
|
if (typeof attributes.imports === 'string') {
|
|
194
240
|
const url = new URL(attributes.imports)
|
|
195
241
|
|
|
196
|
-
const source = yield { module: url }
|
|
242
|
+
const source = yield { module: url, artifact: false }
|
|
197
243
|
|
|
198
244
|
if (source !== null) {
|
|
199
245
|
opts = {
|
|
@@ -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)
|
|
@@ -238,7 +280,7 @@ exports.package = function* (url, source, artifacts, visited, opts = {}) {
|
|
|
238
280
|
visited.add(url.href)
|
|
239
281
|
|
|
240
282
|
if (source === null) {
|
|
241
|
-
source = yield { module: url }
|
|
283
|
+
source = yield { module: url, artifact: false }
|
|
242
284
|
|
|
243
285
|
if (source === null) return false
|
|
244
286
|
}
|
|
@@ -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,16 @@ 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
|
+
yield {
|
|
343
|
+
children: exports.module(url, null, {}, artifacts, visited, {
|
|
344
|
+
...opts,
|
|
345
|
+
asset: true
|
|
346
|
+
}),
|
|
347
|
+
deferred: true
|
|
348
|
+
}
|
|
297
349
|
} else if (
|
|
298
350
|
url.protocol !== builtinProtocol &&
|
|
299
351
|
url.protocol !== linkedProtocol &&
|
|
@@ -305,14 +357,13 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
|
|
|
305
357
|
}
|
|
306
358
|
}
|
|
307
359
|
} else {
|
|
308
|
-
|
|
360
|
+
for (const [condition, child] of Object.entries(entry)) {
|
|
361
|
+
stack.push({ entry: child, asset: asset || condition === 'asset' })
|
|
362
|
+
}
|
|
309
363
|
}
|
|
310
364
|
}
|
|
311
365
|
}
|
|
312
366
|
|
|
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
367
|
const type = moduleType(url, {}, null, opts)
|
|
317
368
|
|
|
318
369
|
const lexer = { imports: [], exports: [] }
|
|
@@ -335,22 +386,12 @@ exports.preresolved = function* (url, source, resolutions, artifacts, visited, o
|
|
|
335
386
|
}
|
|
336
387
|
|
|
337
388
|
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
389
|
const lexed = lex(source)
|
|
351
390
|
|
|
352
391
|
lexer.exports = lexed.exports
|
|
353
392
|
|
|
393
|
+
const links = []
|
|
394
|
+
|
|
354
395
|
for (const entry of lexed.imports) {
|
|
355
396
|
let specifier = entry.specifier
|
|
356
397
|
let condition = 'default'
|
|
@@ -366,129 +407,184 @@ exports.imports = function* (parentURL, source, imports, artifacts, lexer, visit
|
|
|
366
407
|
condition = 'import'
|
|
367
408
|
}
|
|
368
409
|
|
|
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
410
|
lexer.imports.push(entry)
|
|
386
411
|
|
|
387
|
-
|
|
412
|
+
links.push(
|
|
413
|
+
exports.link(entry, specifier, condition, parentURL, imports, artifacts, visited, opts)
|
|
414
|
+
)
|
|
388
415
|
}
|
|
389
416
|
|
|
390
|
-
|
|
391
|
-
|
|
417
|
+
yield { links }
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
exports.link = function* (
|
|
421
|
+
entry,
|
|
422
|
+
specifier,
|
|
423
|
+
condition,
|
|
424
|
+
parentURL,
|
|
425
|
+
imports,
|
|
426
|
+
artifacts,
|
|
427
|
+
visited,
|
|
428
|
+
opts = {}
|
|
429
|
+
) {
|
|
430
|
+
if (entry.attributes.imports) {
|
|
431
|
+
const specifier = entry.attributes.imports
|
|
432
|
+
|
|
433
|
+
yield* resolveImport(
|
|
434
|
+
{ type: 0, specifier, names: [], attributes: {}, position: [0, 0, 0] },
|
|
435
|
+
specifier,
|
|
436
|
+
'default',
|
|
437
|
+
parentURL,
|
|
438
|
+
imports,
|
|
439
|
+
artifacts,
|
|
440
|
+
visited,
|
|
441
|
+
opts
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
yield* resolveImport(entry, specifier, condition, parentURL, imports, artifacts, visited, opts)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function* resolveImport(entry, specifier, condition, parentURL, imports, artifacts, visited, opts) {
|
|
449
|
+
const {
|
|
450
|
+
resolve = exports.resolve.default,
|
|
451
|
+
builtinProtocol = 'builtin:',
|
|
452
|
+
linkedProtocol = 'linked:',
|
|
453
|
+
deferredProtocol = 'deferred:'
|
|
454
|
+
} = opts
|
|
392
455
|
|
|
393
|
-
|
|
456
|
+
const matchedConditions = []
|
|
394
457
|
|
|
395
|
-
|
|
396
|
-
const candidates = []
|
|
458
|
+
opts = { ...opts, matchedConditions }
|
|
397
459
|
|
|
398
|
-
|
|
399
|
-
let resolutions = 0
|
|
460
|
+
matchedConditions.push(condition)
|
|
400
461
|
|
|
401
|
-
|
|
402
|
-
|
|
462
|
+
const resolver = resolve(entry, parentURL, opts)
|
|
463
|
+
const candidates = []
|
|
403
464
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
} else {
|
|
407
|
-
const url = value.resolution
|
|
465
|
+
let next = resolver.next()
|
|
466
|
+
let resolutions = 0
|
|
408
467
|
|
|
409
|
-
|
|
468
|
+
while (next.done !== true) {
|
|
469
|
+
const value = next.value
|
|
410
470
|
|
|
411
|
-
|
|
471
|
+
if (value.package) {
|
|
472
|
+
next = resolver.next(JSON.parse(yield { module: value.package, artifact: false }))
|
|
473
|
+
} else {
|
|
474
|
+
const url = value.resolution
|
|
412
475
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
for (const url of yield { prefix }) {
|
|
425
|
-
yield {
|
|
426
|
-
children: exports.module(url, null, {}, artifacts, visited, {
|
|
427
|
-
...opts,
|
|
428
|
-
asset: true
|
|
429
|
-
}),
|
|
430
|
-
deferred: true
|
|
431
|
-
}
|
|
476
|
+
candidates.push(url)
|
|
477
|
+
|
|
478
|
+
let resolved = false
|
|
479
|
+
let resolution = url
|
|
480
|
+
|
|
481
|
+
if (
|
|
482
|
+
url.protocol === builtinProtocol ||
|
|
483
|
+
url.protocol === linkedProtocol ||
|
|
484
|
+
url.protocol === deferredProtocol
|
|
485
|
+
) {
|
|
486
|
+
addResolution(imports, specifier, matchedConditions, url)
|
|
432
487
|
|
|
433
|
-
|
|
488
|
+
resolved = true
|
|
489
|
+
} else if (condition === 'asset') {
|
|
490
|
+
const prefix = url
|
|
434
491
|
|
|
435
|
-
|
|
492
|
+
for (const url of yield { prefix }) {
|
|
493
|
+
const resolution = yield* postresolve(url)
|
|
494
|
+
|
|
495
|
+
yield {
|
|
496
|
+
children: exports.module(resolution, null, {}, artifacts, visited, {
|
|
497
|
+
...opts,
|
|
498
|
+
asset: true
|
|
499
|
+
}),
|
|
500
|
+
deferred: true
|
|
436
501
|
}
|
|
437
502
|
|
|
438
|
-
|
|
439
|
-
} else {
|
|
440
|
-
const source = yield { module: url }
|
|
503
|
+
addURL(artifacts.assets, resolution)
|
|
441
504
|
|
|
442
|
-
|
|
443
|
-
|
|
505
|
+
resolved = true
|
|
506
|
+
}
|
|
444
507
|
|
|
445
|
-
|
|
508
|
+
if (resolved) addResolution(imports, specifier, matchedConditions, url)
|
|
509
|
+
} else if (
|
|
510
|
+
condition === 'addon' ||
|
|
511
|
+
moduleType(url, entry.attributes, null, opts) === constants.ADDON
|
|
512
|
+
) {
|
|
513
|
+
let exists = yield { probe: url }
|
|
514
|
+
let source = null
|
|
446
515
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
516
|
+
if (exists === undefined) {
|
|
517
|
+
source = yield { module: url, artifact: false }
|
|
518
|
+
exists = source !== null
|
|
519
|
+
}
|
|
450
520
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
deferred: false
|
|
454
|
-
}
|
|
521
|
+
if (exists) {
|
|
522
|
+
resolution = yield* postresolve(url)
|
|
455
523
|
|
|
456
|
-
|
|
524
|
+
addResolution(imports, specifier, matchedConditions, exports.alias(resolution, opts))
|
|
525
|
+
|
|
526
|
+
yield {
|
|
527
|
+
children: exports.module(resolution, source, {}, artifacts, visited, {
|
|
528
|
+
...opts,
|
|
529
|
+
probed: true
|
|
530
|
+
}),
|
|
531
|
+
deferred: false
|
|
457
532
|
}
|
|
533
|
+
|
|
534
|
+
resolved = true
|
|
458
535
|
}
|
|
536
|
+
} else {
|
|
537
|
+
const source = yield { module: url, artifact: false }
|
|
459
538
|
|
|
460
|
-
if (
|
|
461
|
-
|
|
539
|
+
if (source !== null) {
|
|
540
|
+
resolution = yield* postresolve(url)
|
|
462
541
|
|
|
463
|
-
|
|
464
|
-
}
|
|
542
|
+
addResolution(imports, specifier, matchedConditions, exports.alias(resolution, opts))
|
|
465
543
|
|
|
466
|
-
|
|
467
|
-
}
|
|
468
|
-
}
|
|
544
|
+
let attributes = entry.attributes
|
|
469
545
|
|
|
470
|
-
|
|
546
|
+
if (attributes.imports) {
|
|
547
|
+
attributes = { ...attributes, imports: imports[attributes.imports].default }
|
|
548
|
+
}
|
|
471
549
|
|
|
472
|
-
|
|
473
|
-
|
|
550
|
+
yield {
|
|
551
|
+
children: exports.module(resolution, source, attributes, artifacts, visited, opts),
|
|
552
|
+
deferred: false
|
|
553
|
+
}
|
|
474
554
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
message += '\n' + candidates.map((url) => '- ' + url.href).join('\n')
|
|
555
|
+
resolved = true
|
|
556
|
+
}
|
|
478
557
|
}
|
|
479
558
|
|
|
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)
|
|
559
|
+
if (resolved) {
|
|
560
|
+
if (condition === 'addon') addURL(artifacts.addons, resolution)
|
|
561
|
+
|
|
562
|
+
resolutions++
|
|
487
563
|
}
|
|
564
|
+
|
|
565
|
+
next = resolver.next(resolved)
|
|
488
566
|
}
|
|
489
567
|
}
|
|
490
568
|
|
|
491
|
-
|
|
569
|
+
matchedConditions.pop()
|
|
570
|
+
|
|
571
|
+
if (resolutions === 0) {
|
|
572
|
+
let message = `Cannot find ${condition === 'addon' || condition === 'asset' ? condition : 'module'} '${specifier}' imported from '${parentURL.href}'`
|
|
573
|
+
|
|
574
|
+
if (candidates.length > 0) {
|
|
575
|
+
message += '\nCandidates:'
|
|
576
|
+
message += '\n' + candidates.map((url) => '- ' + url.href).join('\n')
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
switch (condition) {
|
|
580
|
+
case 'addon':
|
|
581
|
+
throw errors.ADDON_NOT_FOUND(message, specifier, parentURL, candidates)
|
|
582
|
+
case 'asset':
|
|
583
|
+
throw errors.ASSET_NOT_FOUND(message, specifier, parentURL, candidates)
|
|
584
|
+
default:
|
|
585
|
+
throw errors.MODULE_NOT_FOUND(message, specifier, parentURL, candidates)
|
|
586
|
+
}
|
|
587
|
+
}
|
|
492
588
|
}
|
|
493
589
|
|
|
494
590
|
const ADDON_EXTENSION = /\.(bare|node)$/
|
|
@@ -502,12 +598,14 @@ exports.addons = function* (parentURL, artifacts, visited, opts = {}) {
|
|
|
502
598
|
prefix.pathname = prefix.pathname.replace(ADDON_EXTENSION, '') + '/'
|
|
503
599
|
|
|
504
600
|
for (const url of yield { prefix }) {
|
|
601
|
+
const resolution = yield* postresolve(url)
|
|
602
|
+
|
|
505
603
|
yield {
|
|
506
|
-
children: exports.module(
|
|
604
|
+
children: exports.module(resolution, null, {}, artifacts, visited, opts),
|
|
507
605
|
deferred: false
|
|
508
606
|
}
|
|
509
607
|
|
|
510
|
-
addURL(artifacts.addons,
|
|
608
|
+
addURL(artifacts.addons, resolution)
|
|
511
609
|
|
|
512
610
|
yielded = true
|
|
513
611
|
}
|
|
@@ -522,21 +620,19 @@ exports.assets = function* (patterns, parentURL, artifacts, visited, opts = {})
|
|
|
522
620
|
let yielded = false
|
|
523
621
|
|
|
524
622
|
for (const url of matches) {
|
|
525
|
-
const
|
|
623
|
+
const resolution = yield* postresolve(url)
|
|
526
624
|
|
|
527
|
-
|
|
528
|
-
addURL(artifacts.assets, url)
|
|
529
|
-
|
|
530
|
-
yield {
|
|
531
|
-
children: exports.module(url, source, {}, artifacts, visited, {
|
|
532
|
-
...opts,
|
|
533
|
-
asset: true
|
|
534
|
-
}),
|
|
535
|
-
deferred: true
|
|
536
|
-
}
|
|
625
|
+
addURL(artifacts.assets, resolution)
|
|
537
626
|
|
|
538
|
-
|
|
627
|
+
yield {
|
|
628
|
+
children: exports.module(resolution, null, {}, artifacts, visited, {
|
|
629
|
+
...opts,
|
|
630
|
+
asset: true
|
|
631
|
+
}),
|
|
632
|
+
deferred: true
|
|
539
633
|
}
|
|
634
|
+
|
|
635
|
+
yielded = true
|
|
540
636
|
}
|
|
541
637
|
|
|
542
638
|
return yielded
|
|
@@ -605,10 +701,10 @@ exports.patternMatches = function* patternMatches(pattern, parentURL, matches, o
|
|
|
605
701
|
return matches
|
|
606
702
|
}
|
|
607
703
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
704
|
+
function* postresolve(url) {
|
|
705
|
+
return (yield { resolution: url }) || url
|
|
706
|
+
}
|
|
707
|
+
|
|
612
708
|
function moduleType(url, attributes, info, opts = {}) {
|
|
613
709
|
const { defaultType = constants.SCRIPT, aliases = null } = opts
|
|
614
710
|
|
|
@@ -635,7 +731,7 @@ function moduleType(url, attributes, info, opts = {}) {
|
|
|
635
731
|
|
|
636
732
|
const match = url.pathname.match(/\.[a-z]+$/)
|
|
637
733
|
|
|
638
|
-
if (match === null) return
|
|
734
|
+
if (match === null) return defaultType
|
|
639
735
|
|
|
640
736
|
let [extension] = match
|
|
641
737
|
|
|
@@ -666,7 +762,7 @@ function moduleType(url, attributes, info, opts = {}) {
|
|
|
666
762
|
return constants.TEXT
|
|
667
763
|
}
|
|
668
764
|
|
|
669
|
-
return
|
|
765
|
+
return defaultType
|
|
670
766
|
}
|
|
671
767
|
|
|
672
768
|
function addURL(collection, url) {
|