bare-stow 0.1.5 → 0.1.7
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 +3 -1
- package/bin.js +6 -5
- package/index.js +116 -17
- package/lib/host.js +15 -3
- package/lib/target/bare-sidecar.js +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -66,6 +66,8 @@ ipc.on('exit', (code) => {
|
|
|
66
66
|
ipc.destroy()
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
For the `bare-sidecar` target, `start()` accepts the options of `bare-sidecar`. The sidecar inherits the standard streams of the host unless `stdio` says otherwise.
|
|
70
|
+
|
|
69
71
|
## Protocol
|
|
70
72
|
|
|
71
73
|
The harness multiplexes a control channel and a user data channel over the underlying binary duplex stream. The `ipc` returned from `start()` is itself a duplex stream carrying the user data; control frames ride alongside it on the same handle. RPC libraries (such as `bare-rpc`) bind to `ipc` just like a raw stream.
|
|
@@ -137,7 +139,7 @@ opts = {
|
|
|
137
139
|
|
|
138
140
|
- `client`: The RPC library to wire into the harness as a client, given either as a built-in name (`'bare-rpc'`) or as an [RPC provider](#targets-and-rpc-providers) object.
|
|
139
141
|
- `server`: The RPC library to wire into the bundle entry shim as a server, given either as a built-in name (`'bare-rpc'`) or as an [RPC provider](#targets-and-rpc-providers) object.
|
|
140
|
-
- `base`: The base `URL` of the module graph. Defaults to the directory
|
|
142
|
+
- `base`: The base `URL` of the module graph, which must contain every module in the bundle. Defaults to the closest directory that does.
|
|
141
143
|
- `hosts`: An array of host triples to build for. Must be a subset of the host triples supported by the target; passing a host the target does not support throws. Defaults to all host triples supported by the target.
|
|
142
144
|
- `resolveTarget`: A function mapping a target name to a [target provider](#targets-and-rpc-providers) object, called when `target` is a name not in the built-in registry (which knows `'bare-sidecar'` and `'bare-worker'`).
|
|
143
145
|
- `resolveRPC`: A function mapping an RPC library name to an [RPC provider](#targets-and-rpc-providers) object, called when `client` or `server` is a name not in the built-in registry (which only knows `'bare-rpc'`).
|
package/bin.js
CHANGED
|
@@ -22,7 +22,10 @@ const cmd = command(
|
|
|
22
22
|
'--server <name>',
|
|
23
23
|
'The RPC server to include (bare-rpc, or a bare-stow-rpc-<name> package)'
|
|
24
24
|
),
|
|
25
|
-
flag(
|
|
25
|
+
flag(
|
|
26
|
+
'--base <path>',
|
|
27
|
+
'The base path of the bundle (defaults to the closest directory containing every bundled module)'
|
|
28
|
+
),
|
|
26
29
|
flag('--out|-o <path>', 'The output path of the harness'),
|
|
27
30
|
flag('--builtins <path>', 'A list of builtin modules'),
|
|
28
31
|
flag('--imports <path>', 'A map of global import overrides'),
|
|
@@ -35,7 +38,7 @@ const cmd = command(
|
|
|
35
38
|
target,
|
|
36
39
|
client,
|
|
37
40
|
server,
|
|
38
|
-
base
|
|
41
|
+
base,
|
|
39
42
|
out,
|
|
40
43
|
builtins,
|
|
41
44
|
imports,
|
|
@@ -60,9 +63,7 @@ const cmd = command(
|
|
|
60
63
|
if ('default' in imports) imports = imports.default
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
base = pathToFileURL(base)
|
|
64
|
-
|
|
65
|
-
if (!base.pathname.endsWith('/')) base.pathname += '/'
|
|
66
|
+
if (base) base = pathToFileURL(path.resolve(base) + '/')
|
|
66
67
|
|
|
67
68
|
for await (const _ of stow(pathToFileURL(entry), target, pathToFileURL(out), {
|
|
68
69
|
client,
|
package/index.js
CHANGED
|
@@ -21,7 +21,6 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
21
21
|
|
|
22
22
|
entry = new URL(entry)
|
|
23
23
|
out = new URL(out)
|
|
24
|
-
base = base ? new URL(base) : new URL('./', entry)
|
|
25
24
|
|
|
26
25
|
// The target may pin a module system; otherwise it follows the output path.
|
|
27
26
|
const module = t.module || (await resolveModule(out))
|
|
@@ -36,7 +35,7 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
36
35
|
hosts = t.hosts
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
const shimURL = shim.url(
|
|
38
|
+
const shimURL = shim.url(new URL('./', entry))
|
|
40
39
|
|
|
41
40
|
// The server only contributes runtime wiring to the shim; the shim is not a
|
|
42
41
|
// typed, host-facing module so it has no declaration artifact.
|
|
@@ -63,9 +62,11 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
63
62
|
|
|
64
63
|
let writeFile
|
|
65
64
|
|
|
66
|
-
if (isOffloadEnabled(t.offload)) writeFile = collectOffloaded(
|
|
65
|
+
if (isOffloadEnabled(t.offload)) writeFile = collectOffloaded(offloaded)
|
|
67
66
|
|
|
68
|
-
|
|
67
|
+
// The base of the bundle depends on where its modules live, so the graph is
|
|
68
|
+
// packed unbased and unmounted once that is known.
|
|
69
|
+
let bundle = await pack(
|
|
69
70
|
shimURL,
|
|
70
71
|
{
|
|
71
72
|
...packOpts,
|
|
@@ -75,7 +76,6 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
75
76
|
'.mts': '.mjs',
|
|
76
77
|
'.cts': '.cjs'
|
|
77
78
|
},
|
|
78
|
-
base,
|
|
79
79
|
hosts,
|
|
80
80
|
linked: t.linked,
|
|
81
81
|
offload: t.offload
|
|
@@ -85,6 +85,10 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
85
85
|
writeFile
|
|
86
86
|
)
|
|
87
87
|
|
|
88
|
+
base = resolveBase(base, bundle)
|
|
89
|
+
|
|
90
|
+
bundle = rerootOffloaded(bundle.unmount(base), base, out, offloaded)
|
|
91
|
+
|
|
88
92
|
bundle.id = id(bundle).toString('hex')
|
|
89
93
|
|
|
90
94
|
// The client contributes both runtime wiring (spliced into the harness) and a
|
|
@@ -132,6 +136,44 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
132
136
|
}
|
|
133
137
|
}
|
|
134
138
|
|
|
139
|
+
function resolveBase(base, graph) {
|
|
140
|
+
const modules = [...graph.keys()].map((href) => new URL(href))
|
|
141
|
+
|
|
142
|
+
if (base) {
|
|
143
|
+
base = new URL(base)
|
|
144
|
+
|
|
145
|
+
if (!base.pathname.endsWith('/')) base.pathname += '/'
|
|
146
|
+
|
|
147
|
+
for (const url of modules) {
|
|
148
|
+
if (url.protocol === base.protocol && !url.href.startsWith(base.href)) {
|
|
149
|
+
throw new Error(`Module '${url.href}' is outside base '${base.href}'`)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return base
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let common = null
|
|
157
|
+
|
|
158
|
+
for (const url of modules) {
|
|
159
|
+
if (url.protocol !== 'file:') continue
|
|
160
|
+
|
|
161
|
+
const dir = new URL('./', url)
|
|
162
|
+
|
|
163
|
+
if (common === null) common = dir
|
|
164
|
+
|
|
165
|
+
while (!dir.href.startsWith(common.href)) {
|
|
166
|
+
const parent = new URL('../', common)
|
|
167
|
+
|
|
168
|
+
if (parent.href === common.href) throw new Error('Modules share no common base')
|
|
169
|
+
|
|
170
|
+
common = parent
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return common
|
|
175
|
+
}
|
|
176
|
+
|
|
135
177
|
function isOffloadEnabled(offload) {
|
|
136
178
|
if (offload === true) return true
|
|
137
179
|
if (offload && (offload.addons || offload.assets)) return true
|
|
@@ -153,26 +195,83 @@ function wrapReadModule(readModule, shimURL, shimSource) {
|
|
|
153
195
|
}
|
|
154
196
|
}
|
|
155
197
|
|
|
156
|
-
function collectOffloaded(
|
|
198
|
+
function collectOffloaded(sink) {
|
|
199
|
+
return function writeFile(url, source) {
|
|
200
|
+
sink.push({ url, source })
|
|
201
|
+
|
|
202
|
+
return null
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Offloaded files are written next to the harness, so their resolutions are
|
|
207
|
+
// rewritten to point beside the bundle, along with the directories that hold
|
|
208
|
+
// them, as `bare-pack` does for a bundle packed with a base.
|
|
209
|
+
function rerootOffloaded(bundle, base, out, offloaded) {
|
|
210
|
+
if (offloaded.length === 0) return bundle
|
|
211
|
+
|
|
157
212
|
const dir = new URL('./', out)
|
|
213
|
+
const rewrites = new Map()
|
|
158
214
|
|
|
159
|
-
|
|
160
|
-
|
|
215
|
+
for (const file of offloaded) {
|
|
216
|
+
const relative = offloadedPath(file.url, base)
|
|
217
|
+
|
|
218
|
+
let key = '/' + path.posix.relative(base.pathname, file.url.pathname)
|
|
219
|
+
let value = '/../' + relative
|
|
220
|
+
|
|
221
|
+
rewrites.set(key, value)
|
|
222
|
+
|
|
223
|
+
for (;;) {
|
|
224
|
+
key = key.substring(0, key.lastIndexOf('/'))
|
|
225
|
+
|
|
226
|
+
if (isTerminator(key)) break
|
|
227
|
+
|
|
228
|
+
value = value.substring(0, value.lastIndexOf('/'))
|
|
161
229
|
|
|
162
|
-
|
|
230
|
+
if (isTerminator(value)) break
|
|
163
231
|
|
|
164
|
-
|
|
165
|
-
relative = url.pathname.slice(nm + 1)
|
|
166
|
-
} else if (url.pathname.startsWith(base.pathname)) {
|
|
167
|
-
relative = url.pathname.slice(base.pathname.length)
|
|
168
|
-
} else {
|
|
169
|
-
relative = url.pathname.replace(/^\//, '')
|
|
232
|
+
rewrites.set(key, value)
|
|
170
233
|
}
|
|
171
234
|
|
|
172
|
-
|
|
235
|
+
file.url = new URL(relative, dir)
|
|
236
|
+
}
|
|
173
237
|
|
|
174
|
-
|
|
238
|
+
const resolutions = {}
|
|
239
|
+
|
|
240
|
+
for (const [href, imports] of Object.entries(bundle.resolutions)) {
|
|
241
|
+
resolutions[href] = rewriteImports(imports, rewrites)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
bundle.resolutions = resolutions
|
|
245
|
+
|
|
246
|
+
return bundle
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function offloadedPath(url, base) {
|
|
250
|
+
const nm = url.pathname.indexOf('/node_modules/')
|
|
251
|
+
|
|
252
|
+
if (nm >= 0) return url.pathname.slice(nm + 1)
|
|
253
|
+
|
|
254
|
+
if (url.pathname.startsWith(base.pathname)) return url.pathname.slice(base.pathname.length)
|
|
255
|
+
|
|
256
|
+
return url.pathname.replace(/^\//, '')
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function rewriteImports(imports, rewrites) {
|
|
260
|
+
if (typeof imports === 'string') return rewrites.get(imports) || imports
|
|
261
|
+
|
|
262
|
+
if (typeof imports !== 'object' || imports === null) return imports
|
|
263
|
+
|
|
264
|
+
const rewritten = {}
|
|
265
|
+
|
|
266
|
+
for (const [condition, value] of Object.entries(imports)) {
|
|
267
|
+
rewritten[condition] = rewriteImports(value, rewrites)
|
|
175
268
|
}
|
|
269
|
+
|
|
270
|
+
return rewritten
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function isTerminator(input) {
|
|
274
|
+
return input === '' || input.endsWith('/') || input.endsWith(':')
|
|
176
275
|
}
|
|
177
276
|
|
|
178
277
|
async function resolveModule(out) {
|
package/lib/host.js
CHANGED
|
@@ -5,17 +5,29 @@ class IPC extends Protocol {
|
|
|
5
5
|
super(stream)
|
|
6
6
|
|
|
7
7
|
this._ready = new Promise((resolve, reject) => {
|
|
8
|
+
const cleanup = () => {
|
|
9
|
+
this.off('ready', onready).off('error', onerror).off('exit', onexit).off('close', onclose)
|
|
10
|
+
}
|
|
11
|
+
|
|
8
12
|
const onready = () => {
|
|
9
|
-
|
|
13
|
+
cleanup()
|
|
10
14
|
resolve()
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
const onerror = (err) => {
|
|
14
|
-
|
|
18
|
+
cleanup()
|
|
15
19
|
reject(err)
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
const onexit = (code) => {
|
|
23
|
+
// An error frame precedes the exit frame and is emitted on destroy
|
|
24
|
+
if (this.destroying) return
|
|
25
|
+
onerror(new Error(`Worker exited with code ${code} before ready`))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const onclose = () => onerror(new Error('Worker closed before ready'))
|
|
29
|
+
|
|
30
|
+
this.on('ready', onready).on('error', onerror).on('exit', onexit).on('close', onclose)
|
|
19
31
|
})
|
|
20
32
|
|
|
21
33
|
this._ready.catch(() => {})
|
|
@@ -33,7 +33,7 @@ import { fileURLToPath } from 'url'
|
|
|
33
33
|
const bundle = fileURLToPath(new URL(${JSON.stringify(bundleSpecifier)}, import.meta.url))
|
|
34
34
|
|
|
35
35
|
export async function start(opts = {}) {
|
|
36
|
-
const sidecar = new Sidecar(bundle, opts)
|
|
36
|
+
const sidecar = new Sidecar(bundle, { stdio: 'inherit', ...opts })
|
|
37
37
|
|
|
38
38
|
const ${ipc} = stow.wrap(sidecar)
|
|
39
39
|
${client ? '\n' + indent(client.source, 2) : ''}
|
|
@@ -51,7 +51,7 @@ const bundle = path.join(__dirname, ${JSON.stringify(bundleSpecifier)})
|
|
|
51
51
|
|
|
52
52
|
module.exports = {
|
|
53
53
|
async start(opts = {}) {
|
|
54
|
-
const sidecar = new Sidecar(bundle, opts)
|
|
54
|
+
const sidecar = new Sidecar(bundle, { stdio: 'inherit', ...opts })
|
|
55
55
|
|
|
56
56
|
const ${ipc} = stow.wrap(sidecar)
|
|
57
57
|
${client ? '\n' + indent(client.source, 4) : ''}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-stow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Module bundling and harness generation for Bare",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./package": "./package.json",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"bare-rpc": "^1.3.2",
|
|
79
|
-
"bare-sidecar": "^0.5.
|
|
79
|
+
"bare-sidecar": "^0.5.8",
|
|
80
80
|
"bare-worker": "^4.3.0",
|
|
81
81
|
"brittle": "^4.0.2",
|
|
82
82
|
"lunte": "^1.7.0",
|