bare-stow 0.1.8 → 0.2.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 +90 -111
- package/bin.js +142 -53
- package/index.d.ts +301 -81
- package/index.js +132 -204
- package/lib/assets.js +54 -0
- package/lib/fs.js +2 -4
- package/lib/graph.js +96 -0
- package/lib/language/javascript.js +74 -0
- package/lib/language/kotlin.js +109 -0
- package/lib/language/python.js +65 -0
- package/lib/language/swift.js +111 -0
- package/lib/language.js +18 -0
- package/lib/link.js +141 -0
- package/lib/provider.js +33 -0
- package/lib/rpc/bare-rpc.js +220 -15
- package/lib/rpc.js +6 -18
- package/lib/shim.js +24 -31
- package/lib/target/bare-kit.js +144 -0
- package/lib/target/bare-sidecar.js +88 -51
- package/lib/target/bare-worker.js +18 -68
- package/lib/target/javascript.js +63 -0
- package/lib/target/swift.js +57 -0
- package/lib/target.js +43 -12
- package/package.json +7 -22
- package/lib/host.d.ts +0 -56
- package/lib/host.js +0 -95
- package/lib/protocol.d.ts +0 -46
- package/lib/protocol.js +0 -139
- package/lib/runtime.d.ts +0 -29
- package/lib/runtime.js +0 -92
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# bare-stow
|
|
2
2
|
|
|
3
|
-
Module bundling and harness generation for Bare.
|
|
4
|
-
|
|
5
|
-
A [CLI](#cli) is also included and provides support for stowing a module graph from the command line.
|
|
3
|
+
Module bundling and harness generation for Bare. `bare-stow` packs a module graph into a bundle with <https://github.com/holepunchto/bare-pack> and generates a harness package that boots the bundle from a host application written in JavaScript, Kotlin, Python, or Swift.
|
|
6
4
|
|
|
7
5
|
```
|
|
8
6
|
npm i [-g] bare-stow
|
|
@@ -10,7 +8,7 @@ npm i [-g] bare-stow
|
|
|
10
8
|
|
|
11
9
|
## Usage
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
The entry module exports a `start()` function that receives the IPC stream and an optional `ready` callback. It may return a function to run before the bundle exits:
|
|
14
12
|
|
|
15
13
|
```js
|
|
16
14
|
module.exports = async function start(ipc, ready) {
|
|
@@ -18,8 +16,7 @@ module.exports = async function start(ipc, ready) {
|
|
|
18
16
|
// Handle messages from the host.
|
|
19
17
|
})
|
|
20
18
|
|
|
21
|
-
//
|
|
22
|
-
// signaled automatically when `start()` resolves.
|
|
19
|
+
// Signal readiness early. Otherwise, it's signaled when `start()` resolves.
|
|
23
20
|
ready()
|
|
24
21
|
|
|
25
22
|
return async function stop() {
|
|
@@ -28,155 +25,135 @@ module.exports = async function start(ipc, ready) {
|
|
|
28
25
|
}
|
|
29
26
|
```
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
If a `server` is requested, `start()` instead receives `{ rpc, ipc }`, with `rpc` bound to the IPC stream. Its handlers are registered in `start()`, before the bundle signals ready:
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
module.exports = function start({ rpc }) {
|
|
32
|
+
rpc.respond(1, (req, data) => data)
|
|
33
|
+
}
|
|
34
|
+
```
|
|
32
35
|
|
|
33
|
-
Stow it for
|
|
36
|
+
Stow it for a target:
|
|
34
37
|
|
|
35
38
|
```js
|
|
36
39
|
const stow = require('bare-stow')
|
|
37
40
|
|
|
38
|
-
const entry =
|
|
39
|
-
const out =
|
|
41
|
+
const entry = 'file:///app/core.js'
|
|
42
|
+
const out = 'file:///app/core-harness/'
|
|
40
43
|
|
|
41
44
|
for await (const artifact of stow(entry, 'bare-sidecar', out)) {
|
|
42
45
|
console.log(artifact.url.href)
|
|
43
46
|
}
|
|
44
47
|
```
|
|
45
48
|
|
|
46
|
-
Or
|
|
49
|
+
Or from the command line:
|
|
47
50
|
|
|
48
51
|
```console
|
|
49
|
-
$ bare-stow --target bare-sidecar --out ./
|
|
52
|
+
$ bare-stow pack --target bare-sidecar --out ./core-harness ./core.js
|
|
50
53
|
```
|
|
51
54
|
|
|
52
|
-
This writes
|
|
55
|
+
This writes a package to `out` with the harness, the bundle, and a manifest declaring the dependencies of the harness. `stow()` yields each file once written, and the bundle along with its `base`, the directory that its modules are named relative to. It's the `base` option if given, and otherwise the closest directory containing every module. Add the package as a dependency of the host and boot the bundle with `start()`, which resolves once the bundle is ready:
|
|
53
56
|
|
|
54
57
|
```js
|
|
55
|
-
|
|
58
|
+
import * as harness from 'core-harness'
|
|
56
59
|
|
|
57
60
|
const { ipc } = await harness.start()
|
|
58
61
|
|
|
59
62
|
ipc.write(Buffer.from('hello from the host'))
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
// Request a graceful shutdown.
|
|
66
|
-
ipc.destroy()
|
|
64
|
+
// Request a graceful shutdown, which resolves with the exit code.
|
|
65
|
+
const code = await ipc.terminate()
|
|
67
66
|
```
|
|
68
67
|
|
|
69
|
-
The
|
|
68
|
+
The bundle imports `bare-stow-runtime`, and the RPC library if a `server` is requested, so the package containing the entry must depend on them.
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
`start()` accepts `timeout`, how long to wait in milliseconds for the bundle to signal ready, and `grace`, how long a terminating bundle gets to run `stop()` and exit before it's killed. `timeout` defaults to waiting indefinitely and `grace` to 5 seconds. On Windows, a sidecar is killed along with its host, so `stop()` only runs when the host terminates it.
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
## Targets
|
|
74
73
|
|
|
75
|
-
|
|
74
|
+
- `bare-sidecar` runs the bundle in a separate process using <https://github.com/holepunchto/bare-sidecar>, with the `javascript`, `python`, and `swift` languages on desktop. In JavaScript and Python, `start()` also accepts the options of `bare-sidecar`.
|
|
75
|
+
- `bare-worker` runs the bundle in a thread of a Bare host using <https://github.com/holepunchto/bare-worker>, with the `javascript` language.
|
|
76
|
+
- `bare-kit` runs the bundle in a worklet using <https://github.com/holepunchto/bare-kit>, with the `swift` language on macOS and iOS and the `kotlin` language on Android. `start()` also accepts the options of the worklet and returns it alongside `ipc`, and in Kotlin takes the Android `Context` first. Native addons are linked with <https://github.com/holepunchto/bare-link> and declared in the manifest, and assets are written next to the bundle. With the target option `linking` set to `'app'`, both are left to `link()` instead.
|
|
76
77
|
|
|
77
|
-
|
|
78
|
+
## Linking
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
A target may leave addons and assets to the application, so that bundles sharing them don't each bring a copy. For `bare-kit`, stow each bundle with the target option `linking` set to `'app'`:
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
| Worker -> Host | `exit` | `{ code }` |
|
|
85
|
-
| Worker -> Host | `error` | `{ message, stack }` |
|
|
86
|
-
| Host -> Worker | `terminate` | - |
|
|
82
|
+
```console
|
|
83
|
+
$ bare-stow pack --target bare-kit --target-option linking=app --language swift --out ./ios/Core ./core.js
|
|
84
|
+
```
|
|
87
85
|
|
|
88
|
-
|
|
86
|
+
`link()` then links the addons and assets of all bundles once, into a package the application depends on:
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
const entries = [new URL('file:///app/core.js'), new URL('file:///app/push.js')]
|
|
90
|
+
const out = new URL('file:///app/ios/BareKitAddons/')
|
|
89
91
|
|
|
90
|
-
|
|
92
|
+
for await (const artifact of stow.link(entries, 'bare-kit', out, { language: 'swift' })) {
|
|
93
|
+
console.log(artifact.url.href)
|
|
94
|
+
}
|
|
95
|
+
```
|
|
91
96
|
|
|
92
|
-
- `
|
|
93
|
-
- `ipc.on('exit', code)`: Emitted when the worker signals exit. `code` carries the exit code.
|
|
94
|
-
- `ipc.on('error', err)`: Emitted when the worker reports a fault, or when the underlying transport errors. Either way the `ipc` is destroyed.
|
|
95
|
-
- `ipc.on('close')`: Emitted when the underlying transport closes.
|
|
96
|
-
- `ipc.destroy()`: Standard stream destroy. Sends a `terminate` control frame to the worker and waits for the underlying transport to close.
|
|
97
|
-
- `ipc.terminate()`: Like `destroy()` but resolves with the worker's exit code.
|
|
97
|
+
The language lays out the package and the target adds what loads the addons. For `bare-kit`, that's `bare-kit-swift` or `bare-kit-kotlin`. The assets go in a folder named by `stow.resources`, listed in a file of the same name with the `.assets` extension, which a Swift application copies into its bundle. `link()` takes the same options as `stow()`, except `client` and `base`.
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
## Languages
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
const protocol = require('bare-stow/protocol')
|
|
103
|
-
|
|
104
|
-
const ipc = protocol.attach(stream) // Any duplex byte stream
|
|
105
|
-
ipc.write(Buffer.from('payload')) // User data
|
|
106
|
-
ipc.send('ready') // Control frame
|
|
107
|
-
ipc.on('exit', () => {
|
|
108
|
-
/* ... */
|
|
109
|
-
})
|
|
110
|
-
```
|
|
101
|
+
The harness is generated in one of these languages, selected with `language` and configured with `languageOptions`:
|
|
111
102
|
|
|
112
|
-
|
|
103
|
+
- `javascript` writes an npm package. The `module` option is either `'esm'`, the default, or `'cjs'`.
|
|
104
|
+
- `kotlin` writes an Android library module. The `package` option names the package of the generated code and is required. The dependencies are published to JitPack (<https://jitpack.io>), so add it to the repositories of the application.
|
|
105
|
+
- `python` writes a Python package.
|
|
106
|
+
- `swift` writes a Swift package.
|
|
113
107
|
|
|
114
|
-
|
|
115
|
-
const { wrap } = require('bare-stow/host')
|
|
108
|
+
For example, with an RPC client and server:
|
|
116
109
|
|
|
117
|
-
|
|
118
|
-
|
|
110
|
+
```console
|
|
111
|
+
$ bare-stow pack --target bare-sidecar --language python --client bare-rpc --server bare-rpc --out ./my-core ./core.js
|
|
119
112
|
```
|
|
120
113
|
|
|
121
|
-
|
|
114
|
+
```python
|
|
115
|
+
import asyncio
|
|
122
116
|
|
|
123
|
-
|
|
117
|
+
import my_core
|
|
124
118
|
|
|
125
|
-
Both kinds of provider return an ordered array of artifacts. An artifact is `{ extension?, source }`:
|
|
126
119
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
]
|
|
132
|
-
```
|
|
120
|
+
async def main():
|
|
121
|
+
ipc, rpc = await my_core.start(
|
|
122
|
+
handlers=lambda router: router.on_request(2, lambda req: b"hello")
|
|
123
|
+
)
|
|
133
124
|
|
|
134
|
-
|
|
125
|
+
await rpc.request(1, b"ping")
|
|
126
|
+
await ipc.terminate()
|
|
135
127
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
name, // The target's name, used in diagnostics.
|
|
139
|
-
linked, // Whether assets and addons are linked into the bundle.
|
|
140
|
-
offload, // Whether (and which) assets and addons are offloaded to a `<bundle>.d` directory next to the bundle.
|
|
141
|
-
format, // The bundle encoding: 'bundle', 'bundle.cjs', 'bundle.mjs', or 'bundle.json'.
|
|
142
|
-
encoding, // The string encoding for encoded formats, or null for raw bytes.
|
|
143
|
-
extension, // The bundle file extension, e.g. '.bundle'.
|
|
144
|
-
module, // Optional. Pins the harness module system to 'esm' or 'cjs'.
|
|
145
|
-
hosts, // The host triples the target supports.
|
|
146
|
-
generate({ bundleSpecifier, ipc, rpc, module, client }) {
|
|
147
|
-
// Return an array of harness artifacts.
|
|
148
|
-
}
|
|
149
|
-
}
|
|
128
|
+
|
|
129
|
+
asyncio.run(main())
|
|
150
130
|
```
|
|
151
131
|
|
|
152
|
-
|
|
132
|
+
With an RPC client, `start()` takes the handlers for the requests and events of the bundle, so that they are registered before the bundle can send any.
|
|
153
133
|
|
|
154
|
-
|
|
134
|
+
## Protocol
|
|
155
135
|
|
|
156
|
-
The harness
|
|
136
|
+
The harness and the bundle multiplex a control channel and the user data over a single duplex stream. The `ipc` returned from `start()` is a duplex stream of the user data, so RPC libraries such as `bare-rpc` bind to it like any other stream. Embedders bringing their own transport can use the protocol directly:
|
|
157
137
|
|
|
158
|
-
|
|
138
|
+
- <https://github.com/holepunchto/bare-stow-protocol> implements the framing.
|
|
139
|
+
- <https://github.com/holepunchto/bare-stow-host> implements the host side and documents `ipc`, with <https://github.com/holepunchto/bare-stow-host-kotlin>, <https://github.com/holepunchto/bare-stow-host-python>, and <https://github.com/holepunchto/bare-stow-host-swift> for the other languages.
|
|
140
|
+
- <https://github.com/holepunchto/bare-stow-runtime> implements the bundle side.
|
|
159
141
|
|
|
160
|
-
|
|
161
|
-
rpc = {
|
|
162
|
-
name, // The library's name, used in diagnostics.
|
|
163
|
-
generate({ ipc, rpc, module, role }) {
|
|
164
|
-
// Return an array of RPC artifacts.
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
```
|
|
142
|
+
## Providers
|
|
168
143
|
|
|
169
|
-
|
|
144
|
+
Languages, targets, and RPC libraries are providers that may live outside `bare-stow`. `stow()` and `link()` accept a provider object in place of a name, and names other than the built-in ones are resolved through `resolveLanguage`, `resolveTarget`, and `resolveRPC`. For example, <https://github.com/holepunchto/bare-stow-target-react-native> is a target for React Native. Targets and RPC libraries generate code for each language they list under `languages`, and providers without an entry for the language of the harness are rejected. Targets may also take options, given as `targetOptions`. The server side of an RPC library is always generated as JavaScript, as the bundle runs in Bare. See `index.d.ts` for the interfaces.
|
|
170
145
|
|
|
171
146
|
## CLI
|
|
172
147
|
|
|
173
|
-
#### `bare-stow [flags] <entry>`
|
|
148
|
+
#### `bare-stow pack [flags] <entry>`
|
|
174
149
|
|
|
175
|
-
Stow the module graph rooted at `<entry
|
|
150
|
+
Stow the module graph rooted at `<entry>` into the directory given by `--out`.
|
|
176
151
|
|
|
177
152
|
```console
|
|
178
|
-
--version|-v
|
|
179
153
|
--target <name>
|
|
154
|
+
--target-option <key=value>
|
|
155
|
+
--language <name>
|
|
156
|
+
--language-option <key=value>
|
|
180
157
|
--client <name>
|
|
181
158
|
--server <name>
|
|
182
159
|
--base <path>
|
|
@@ -188,25 +165,27 @@ Stow the module graph rooted at `<entry>`, writing the harness and bundle to the
|
|
|
188
165
|
--help|-h
|
|
189
166
|
```
|
|
190
167
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
The target runtime. Required. The built-in `bare-sidecar` and `bare-worker` are resolved directly; any other `<name>` is loaded from its `bare-stow-target-<name>` package, or from `<name>` itself as a full module specifier, resolved from the working directory.
|
|
194
|
-
|
|
195
|
-
##### `--client <name>` and `--server <name>`
|
|
196
|
-
|
|
197
|
-
The RPC library to wire into the harness (`--client`) or the bundle entry shim (`--server`). The built-in `bare-rpc` is resolved directly; any other `<name>` is loaded from its `bare-stow-rpc-<name>` package, or from `<name>` itself as a full module specifier, resolved from the working directory.
|
|
198
|
-
|
|
199
|
-
##### `--builtins <path>` and `--imports <path>`
|
|
168
|
+
#### `bare-stow link [flags] <entry...>`
|
|
200
169
|
|
|
201
|
-
|
|
170
|
+
Link the addons and assets of the module graphs rooted at each `<entry>` into the package given by `--out`.
|
|
202
171
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
172
|
+
```console
|
|
173
|
+
--target <name>
|
|
174
|
+
--target-option <key=value>
|
|
175
|
+
--language <name>
|
|
176
|
+
--language-option <key=value>
|
|
177
|
+
--server <name>
|
|
178
|
+
--out|-o <path>
|
|
179
|
+
--builtins <path>
|
|
180
|
+
--imports <path>
|
|
181
|
+
--defer <specifier>
|
|
182
|
+
--host <name>
|
|
183
|
+
--help|-h
|
|
184
|
+
```
|
|
206
185
|
|
|
207
|
-
|
|
186
|
+
`--target`, `--language`, `--client`, and `--server` take a built-in name, the name of a `bare-stow-target-<name>`, `bare-stow-language-<name>`, or `bare-stow-rpc-<name>` package, or a module specifier, resolved from the working directory. `--target-option`, `--language-option`, `--defer`, and `--host` may be passed multiple times. `--host` must name hosts the target supports, and defaults to all of them.
|
|
208
187
|
|
|
209
|
-
|
|
188
|
+
`--builtins` and `--imports` are paths to JavaScript or JSON files exporting a list of builtin modules and a map of import overrides, which are forwarded to `bare-pack`.
|
|
210
189
|
|
|
211
190
|
## License
|
|
212
191
|
|
package/bin.js
CHANGED
|
@@ -1,79 +1,116 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const { pathToFileURL } = require('url')
|
|
4
|
-
const { command, flag, arg, summary } = require('paparam')
|
|
4
|
+
const { command, flag, arg, rest, summary } = require('paparam')
|
|
5
5
|
const pkg = require('./package')
|
|
6
6
|
const stow = require('.')
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const flags = {
|
|
9
|
+
target: () =>
|
|
10
|
+
flag(
|
|
11
|
+
'--target <name>',
|
|
12
|
+
'The target runtime (bare-kit, bare-sidecar, bare-worker, or a bare-stow-target-<name> package)'
|
|
13
|
+
),
|
|
14
|
+
targetOption: () => flag('--target-option <key=value>', 'An option for the target').multiple(),
|
|
15
|
+
language: () =>
|
|
16
|
+
flag(
|
|
17
|
+
'--language <name>',
|
|
18
|
+
'The language of the harness (javascript, kotlin, python, swift, or a bare-stow-language-<name> package; defaults to javascript)'
|
|
19
|
+
),
|
|
20
|
+
languageOption: () =>
|
|
21
|
+
flag(
|
|
22
|
+
'--language-option <key=value>',
|
|
23
|
+
'An option for the language, such as module=cjs for javascript'
|
|
24
|
+
).multiple(),
|
|
25
|
+
server: () =>
|
|
26
|
+
flag(
|
|
27
|
+
'--server <name>',
|
|
28
|
+
'The RPC server to include (bare-rpc, or a bare-stow-rpc-<name> package)'
|
|
29
|
+
),
|
|
30
|
+
builtins: () => flag('--builtins <path>', 'A list of builtin modules'),
|
|
31
|
+
imports: () => flag('--imports <path>', 'A map of global import overrides'),
|
|
32
|
+
defer: () => flag('--defer <specifier>', 'A module specifier to defer resolution of').multiple(),
|
|
33
|
+
host: () =>
|
|
34
|
+
flag('--host <name>', 'A host triple to include (must be a subset of the target)').multiple()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const link = command(
|
|
38
|
+
'link',
|
|
39
|
+
summary('Link the addons and assets of the module graphs of an application'),
|
|
40
|
+
rest('<entry...>', 'The entry points of the module graphs'),
|
|
41
|
+
flags.target(),
|
|
42
|
+
flags.targetOption(),
|
|
43
|
+
flags.language(),
|
|
44
|
+
flags.languageOption(),
|
|
45
|
+
flags.server(),
|
|
46
|
+
flag('--out|-o <path>', 'The directory to write the package of linked addons and assets to'),
|
|
47
|
+
flags.builtins(),
|
|
48
|
+
flags.imports(),
|
|
49
|
+
flags.defer(),
|
|
50
|
+
flags.host(),
|
|
51
|
+
async (cmd) => {
|
|
52
|
+
const entries = cmd.rest || []
|
|
53
|
+
const { target, language, server, out, defer, host: hosts } = cmd.flags
|
|
54
|
+
|
|
55
|
+
if (!target) throw new Error('--target is required')
|
|
56
|
+
if (!out) throw new Error('--out is required')
|
|
57
|
+
if (entries.length === 0) throw new Error('<entry...> is required')
|
|
58
|
+
|
|
59
|
+
for await (const _ of stow.link(
|
|
60
|
+
entries.map((entry) => pathToFileURL(entry)),
|
|
61
|
+
target,
|
|
62
|
+
pathToFileURL(out),
|
|
63
|
+
{
|
|
64
|
+
...common(cmd.flags),
|
|
65
|
+
language,
|
|
66
|
+
server,
|
|
67
|
+
hosts,
|
|
68
|
+
defer
|
|
69
|
+
}
|
|
70
|
+
)) {
|
|
71
|
+
//
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const pack = command(
|
|
77
|
+
'pack',
|
|
78
|
+
summary('Stow a module graph as a bundle and the harness that boots it'),
|
|
11
79
|
arg('<entry>', 'The entry point of the module graph'),
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
),
|
|
80
|
+
flags.target(),
|
|
81
|
+
flags.targetOption(),
|
|
82
|
+
flags.language(),
|
|
83
|
+
flags.languageOption(),
|
|
17
84
|
flag(
|
|
18
85
|
'--client <name>',
|
|
19
86
|
'The RPC client to include (bare-rpc, or a bare-stow-rpc-<name> package)'
|
|
20
87
|
),
|
|
21
|
-
|
|
22
|
-
'--server <name>',
|
|
23
|
-
'The RPC server to include (bare-rpc, or a bare-stow-rpc-<name> package)'
|
|
24
|
-
),
|
|
88
|
+
flags.server(),
|
|
25
89
|
flag(
|
|
26
90
|
'--base <path>',
|
|
27
91
|
'The base path of the bundle (defaults to the closest directory containing every bundled module)'
|
|
28
92
|
),
|
|
29
|
-
flag('--out|-o <path>', 'The
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
93
|
+
flag('--out|-o <path>', 'The directory to write the harness package to'),
|
|
94
|
+
flags.builtins(),
|
|
95
|
+
flags.imports(),
|
|
96
|
+
flags.defer(),
|
|
97
|
+
flags.host(),
|
|
34
98
|
async (cmd) => {
|
|
35
99
|
const { entry } = cmd.args
|
|
36
|
-
let {
|
|
37
|
-
version,
|
|
38
|
-
target,
|
|
39
|
-
client,
|
|
40
|
-
server,
|
|
41
|
-
base,
|
|
42
|
-
out,
|
|
43
|
-
builtins,
|
|
44
|
-
imports,
|
|
45
|
-
defer,
|
|
46
|
-
host: hosts
|
|
47
|
-
} = cmd.flags
|
|
48
|
-
|
|
49
|
-
if (version) return console.log(`v${pkg.version}`)
|
|
100
|
+
let { target, language, client, server, base, out, defer, host: hosts } = cmd.flags
|
|
50
101
|
|
|
51
102
|
if (!target) throw new Error('--target is required')
|
|
52
103
|
if (!out) throw new Error('--out is required')
|
|
53
104
|
|
|
54
|
-
if (builtins) {
|
|
55
|
-
builtins = require(path.resolve(builtins))
|
|
56
|
-
|
|
57
|
-
if ('default' in builtins) builtins = builtins.default
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (imports) {
|
|
61
|
-
imports = require(path.resolve(imports))
|
|
62
|
-
|
|
63
|
-
if ('default' in imports) imports = imports.default
|
|
64
|
-
}
|
|
65
|
-
|
|
66
105
|
if (base) base = pathToFileURL(path.resolve(base) + '/')
|
|
67
106
|
|
|
68
107
|
for await (const _ of stow(pathToFileURL(entry), target, pathToFileURL(out), {
|
|
108
|
+
...common(cmd.flags),
|
|
109
|
+
language,
|
|
69
110
|
client,
|
|
70
111
|
server,
|
|
71
112
|
base,
|
|
72
113
|
hosts,
|
|
73
|
-
resolveTarget,
|
|
74
|
-
resolveRPC,
|
|
75
|
-
builtins,
|
|
76
|
-
imports,
|
|
77
114
|
defer
|
|
78
115
|
})) {
|
|
79
116
|
//
|
|
@@ -81,19 +118,59 @@ const cmd = command(
|
|
|
81
118
|
}
|
|
82
119
|
)
|
|
83
120
|
|
|
121
|
+
// The options shared by pack and link.
|
|
122
|
+
function common(flags) {
|
|
123
|
+
return {
|
|
124
|
+
languageOptions: keyValues('--language-option', flags.languageOption),
|
|
125
|
+
targetOptions: keyValues('--target-option', flags.targetOption),
|
|
126
|
+
builtins: load(flags.builtins),
|
|
127
|
+
imports: load(flags.imports),
|
|
128
|
+
resolveTarget,
|
|
129
|
+
resolveRPC,
|
|
130
|
+
resolveLanguage
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function keyValues(name, options = []) {
|
|
135
|
+
const result = {}
|
|
136
|
+
|
|
137
|
+
for (const option of options) {
|
|
138
|
+
const i = option.indexOf('=')
|
|
139
|
+
|
|
140
|
+
if (i === -1) throw new Error(`${name} must be <key>=<value>, got '${option}'`)
|
|
141
|
+
|
|
142
|
+
result[option.slice(0, i)] = option.slice(i + 1)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return result
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function load(file) {
|
|
149
|
+
if (!file) return undefined
|
|
150
|
+
|
|
151
|
+
const value = require(path.resolve(file))
|
|
152
|
+
|
|
153
|
+
return 'default' in value ? value.default : value
|
|
154
|
+
}
|
|
155
|
+
|
|
84
156
|
// Built-in names are resolved by bare-stow itself; any other name is loaded
|
|
85
|
-
// from its `bare-stow-target-<name
|
|
86
|
-
// full module specifier, resolved
|
|
157
|
+
// from its `bare-stow-target-<name>`, `bare-stow-rpc-<name>`, or
|
|
158
|
+
// `bare-stow-language-<name>` package, or as a full module specifier, resolved
|
|
159
|
+
// from the working directory.
|
|
87
160
|
|
|
88
161
|
function resolveTarget(name) {
|
|
89
|
-
return
|
|
162
|
+
return resolveProvider('bare-stow-target', name)
|
|
90
163
|
}
|
|
91
164
|
|
|
92
165
|
function resolveRPC(name) {
|
|
93
|
-
return
|
|
166
|
+
return resolveProvider('bare-stow-rpc', name)
|
|
94
167
|
}
|
|
95
168
|
|
|
96
|
-
function
|
|
169
|
+
function resolveLanguage(name) {
|
|
170
|
+
return resolveProvider('bare-stow-language', name)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function resolveProvider(prefix, name) {
|
|
97
174
|
for (const specifier of [`${prefix}-${name}`, name]) {
|
|
98
175
|
try {
|
|
99
176
|
return require(require.resolve(specifier, { paths: [process.cwd()] }))
|
|
@@ -105,4 +182,16 @@ function load(prefix, name) {
|
|
|
105
182
|
throw new Error(`Cannot resolve '${name}'`)
|
|
106
183
|
}
|
|
107
184
|
|
|
185
|
+
const cmd = command(
|
|
186
|
+
pkg.name,
|
|
187
|
+
summary(pkg.description),
|
|
188
|
+
flag('--version|-v', 'Print the current version'),
|
|
189
|
+
pack,
|
|
190
|
+
link,
|
|
191
|
+
({ flags }) => {
|
|
192
|
+
if (flags.version) console.log(`v${pkg.version}`)
|
|
193
|
+
else console.log(cmd.help())
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
|
|
108
197
|
cmd.parse()
|