bare-stow 0.1.0 → 0.1.2
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 +10 -4
- package/bin.js +0 -2
- package/index.d.ts +2 -1
- package/index.js +57 -8
- package/lib/resolve.js +40 -0
- package/lib/target/bare-sidecar.js +25 -6
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -144,6 +144,10 @@ opts = {
|
|
|
144
144
|
|
|
145
145
|
Any additional options are forwarded to `bare-pack`. See <https://github.com/holepunchto/bare-pack> for the full set, including `builtins`, `imports`, `defer`, and `resolve`.
|
|
146
146
|
|
|
147
|
+
#### TypeScript
|
|
148
|
+
|
|
149
|
+
TypeScript modules are supported out of the box: `.ts`, `.mts`, and `.cts` sources are stripped of their type syntax with [`bare-type-stripper`](https://github.com/holepunchto/bare-type-stripper) as they are read, and their extensions are aliased to `.js`, `.mjs`, and `.cjs` respectively for module-type detection. The stripper only erases types, so the module system follows the aliased JavaScript extension. Because stripping is purely lexical, non-erasable constructs (`enum`, `namespace` with a body, parameter properties) throw, and JSX (`.tsx`) is not supported.
|
|
150
|
+
|
|
147
151
|
### Targets and RPC providers
|
|
148
152
|
|
|
149
153
|
The harness and RPC code generators are decoupled from the bundler: `target`, `client`, and `server` each accept a provider object directly, so generators can live outside `bare-stow`. Passing a name instead resolves it through `resolveTarget` / `resolveRPC`, which default to the built-in registries.
|
|
@@ -167,9 +171,9 @@ target = {
|
|
|
167
171
|
format, // The bundle encoding: 'bundle', 'bundle.cjs', 'bundle.mjs', or 'bundle.json'.
|
|
168
172
|
encoding, // The string encoding for encoded formats, or null for raw bytes.
|
|
169
173
|
extension, // The bundle file extension, e.g. '.bundle'.
|
|
170
|
-
module, //
|
|
174
|
+
module, // Optional. Pins the harness module system to 'esm' or 'cjs'.
|
|
171
175
|
hosts, // The host triples the target supports.
|
|
172
|
-
generate({ bundleSpecifier, ipc, rpc, client }) {
|
|
176
|
+
generate({ bundleSpecifier, ipc, rpc, module, client }) {
|
|
173
177
|
// Return an array of harness artifacts.
|
|
174
178
|
}
|
|
175
179
|
}
|
|
@@ -177,7 +181,9 @@ target = {
|
|
|
177
181
|
|
|
178
182
|
For a target, each artifact is a file. The first has no `extension` and is written to `out`; each remaining artifact is written next to `out` with its own `extension` substituted for the harness extension (e.g. `{ extension: '.d.ts' }` becomes `index.d.ts`). The built-in targets emit two artifacts: The harness and its TypeScript declaration.
|
|
179
183
|
|
|
180
|
-
`generate()` receives `bundleSpecifier`, the relative specifier from the harness to the bundle artifact; `ipc`, the identifier the harness should bind the IPC stream to; `rpc`, the identifier the RPC client setup binds its instance to; and `client`, the resolved RPC client (or `null` when no client is requested). When present, `client` is `{ source, type }`: `source` is the runtime setup to splice into the harness (referencing `ipc` and binding `rpc`), and `type` is the TypeScript type of the bound instance to splice into the declaration. The target owns `linked`, `offload`, `format`, `encoding`, and `extension`; they are not overridable through `opts`.
|
|
184
|
+
`generate()` receives `bundleSpecifier`, the relative specifier from the harness to the bundle artifact; `ipc`, the identifier the harness should bind the IPC stream to; `rpc`, the identifier the RPC client setup binds its instance to; `module`, the harness module system (`'esm'` or `'cjs'`) so the harness can emit the matching module and import forms; and `client`, the resolved RPC client (or `null` when no client is requested). When present, `client` is `{ source, type }`: `source` is the runtime setup to splice into the harness (referencing `ipc` and binding `rpc`), and `type` is the TypeScript type of the bound instance to splice into the declaration. The target owns `linked`, `offload`, `format`, `encoding`, and `extension`; they are not overridable through `opts`.
|
|
185
|
+
|
|
186
|
+
The harness `module` system is derived from the output path: `out` ending in `.mjs` is ESM and `.cjs` is CommonJS, while an ambiguous `.js` (or other) extension follows the `type` of the closest enclosing `package.json` (`"module"` is ESM, otherwise CommonJS). A target may set its own `module` to pin the harness to one system regardless of the output path.
|
|
181
187
|
|
|
182
188
|
An RPC provider generates the wiring that binds an RPC instance to the IPC stream, used on both the client (harness) and server (shim) side:
|
|
183
189
|
|
|
@@ -190,7 +196,7 @@ rpc = {
|
|
|
190
196
|
}
|
|
191
197
|
```
|
|
192
198
|
|
|
193
|
-
For an RPC provider, each artifact is a fragment contributed to the harness file with the matching extension. The artifact with no `extension` is the runtime setup (reading `ipc`, binding `rpc`); the `{ extension: '.d.ts' }` artifact is the TypeScript type of the bound instance. `generate()` receives `ipc`, the identifier of the IPC stream in scope; `rpc`, the identifier to bind the RPC instance to; `module`, the surrounding module system (`'esm'` or `'cjs'`) so the runtime fragment can emit the matching import form; and `role`, either `'client'` or `'server'`. The bundler owns the `ipc` and `rpc` binding names and threads them to both the target and the RPC provider so neither has to assume them. It renders the client in the
|
|
199
|
+
For an RPC provider, each artifact is a fragment contributed to the harness file with the matching extension. The artifact with no `extension` is the runtime setup (reading `ipc`, binding `rpc`); the `{ extension: '.d.ts' }` artifact is the TypeScript type of the bound instance. `generate()` receives `ipc`, the identifier of the IPC stream in scope; `rpc`, the identifier to bind the RPC instance to; `module`, the surrounding module system (`'esm'` or `'cjs'`) so the runtime fragment can emit the matching import form; and `role`, either `'client'` or `'server'`. The bundler owns the `ipc` and `rpc` binding names and threads them to both the target and the RPC provider so neither has to assume them. It renders the client in the harness `module` and the server as `'esm'` (the shim is always an ES module), splicing the runtime fragment and, for the client, the type fragment into the target's artifacts. The shim is not a typed, host-facing module, so the server's type fragment is unused.
|
|
194
200
|
|
|
195
201
|
## CLI
|
|
196
202
|
|
package/bin.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const { pathToFileURL } = require('url')
|
|
4
4
|
const { command, flag, arg, summary } = require('paparam')
|
|
5
|
-
const { resolve } = require('bare-module-traverse')
|
|
6
5
|
const pkg = require('./package')
|
|
7
6
|
const stow = require('.')
|
|
8
7
|
|
|
@@ -72,7 +71,6 @@ const cmd = command(
|
|
|
72
71
|
hosts,
|
|
73
72
|
resolveTarget,
|
|
74
73
|
resolveRPC,
|
|
75
|
-
resolve: resolve.bare,
|
|
76
74
|
builtins,
|
|
77
75
|
imports,
|
|
78
76
|
defer
|
package/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ interface TargetContext {
|
|
|
11
11
|
bundleSpecifier: string
|
|
12
12
|
ipc: string
|
|
13
13
|
rpc: string
|
|
14
|
+
module: 'esm' | 'cjs'
|
|
14
15
|
client: RPCClient | null
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -21,7 +22,7 @@ interface Target {
|
|
|
21
22
|
format: 'bundle' | 'bundle.cjs' | 'bundle.mjs' | 'bundle.json'
|
|
22
23
|
encoding: string | null
|
|
23
24
|
extension: string
|
|
24
|
-
module
|
|
25
|
+
module?: 'esm' | 'cjs'
|
|
25
26
|
hosts: string[]
|
|
26
27
|
generate(context: TargetContext): Artifact[]
|
|
27
28
|
}
|
package/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const { fileURLToPath } = require('url')
|
|
3
3
|
const pack = require('bare-pack')
|
|
4
|
-
const { resolve } = require('bare-module-traverse')
|
|
5
4
|
const id = require('bare-bundle-id')
|
|
5
|
+
const strip = require('bare-type-stripper')
|
|
6
6
|
const fs = require('./lib/fs')
|
|
7
7
|
const shim = require('./lib/shim')
|
|
8
8
|
const harness = require('./lib/target')
|
|
9
9
|
const rpc = require('./lib/rpc')
|
|
10
|
+
const resolve = require('./lib/resolve')
|
|
10
11
|
|
|
11
12
|
module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
12
13
|
if (!target) throw new Error("'target' is required")
|
|
@@ -22,6 +23,9 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
22
23
|
out = new URL(out)
|
|
23
24
|
base = base ? new URL(base) : new URL('./', entry)
|
|
24
25
|
|
|
26
|
+
// The target may pin a module system; otherwise it follows the output path.
|
|
27
|
+
const module = t.module || (await resolveModule(out))
|
|
28
|
+
|
|
25
29
|
if (hosts) {
|
|
26
30
|
for (const host of hosts) {
|
|
27
31
|
if (!t.hosts.includes(host)) {
|
|
@@ -64,12 +68,17 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
64
68
|
const bundle = await pack(
|
|
65
69
|
shimURL,
|
|
66
70
|
{
|
|
67
|
-
resolve: resolve.bare,
|
|
68
71
|
...packOpts,
|
|
72
|
+
resolve,
|
|
73
|
+
aliases: {
|
|
74
|
+
'.ts': '.js',
|
|
75
|
+
'.mts': '.mjs',
|
|
76
|
+
'.cts': '.cjs'
|
|
77
|
+
},
|
|
78
|
+
base,
|
|
69
79
|
hosts,
|
|
70
80
|
linked: t.linked,
|
|
71
|
-
offload: t.offload
|
|
72
|
-
base: base
|
|
81
|
+
offload: t.offload
|
|
73
82
|
},
|
|
74
83
|
readModule,
|
|
75
84
|
fs.listPrefix,
|
|
@@ -87,8 +96,8 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
87
96
|
const artifacts = rpc(client, resolveRPC).generate({
|
|
88
97
|
ipc: 'ipc',
|
|
89
98
|
rpc: 'rpc',
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
role: 'client',
|
|
100
|
+
module
|
|
92
101
|
})
|
|
93
102
|
|
|
94
103
|
clientSetup = {
|
|
@@ -101,6 +110,7 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
101
110
|
bundleSpecifier,
|
|
102
111
|
ipc: 'ipc',
|
|
103
112
|
rpc: 'rpc',
|
|
113
|
+
module,
|
|
104
114
|
client: clientSetup
|
|
105
115
|
})
|
|
106
116
|
|
|
@@ -130,10 +140,16 @@ function isOffloadEnabled(offload) {
|
|
|
130
140
|
}
|
|
131
141
|
|
|
132
142
|
function wrapReadModule(readModule, shimURL, shimSource) {
|
|
133
|
-
return function (url) {
|
|
143
|
+
return async function (url) {
|
|
134
144
|
if (url.href === shimURL.href) return shimSource
|
|
135
145
|
|
|
136
|
-
|
|
146
|
+
const source = await readModule(url)
|
|
147
|
+
|
|
148
|
+
if (source === null) return null
|
|
149
|
+
|
|
150
|
+
if (/\.(c|m)?ts$/.test(url.pathname)) return strip(source)
|
|
151
|
+
|
|
152
|
+
return source
|
|
137
153
|
}
|
|
138
154
|
}
|
|
139
155
|
|
|
@@ -159,6 +175,39 @@ function collectOffloaded(base, out, sink) {
|
|
|
159
175
|
}
|
|
160
176
|
}
|
|
161
177
|
|
|
178
|
+
async function resolveModule(out) {
|
|
179
|
+
switch (path.extname(out.pathname)) {
|
|
180
|
+
case '.mjs':
|
|
181
|
+
return 'esm'
|
|
182
|
+
case '.cjs':
|
|
183
|
+
return 'cjs'
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// For an ambiguous extension (e.g. '.js') the module system follows the
|
|
187
|
+
// `type` of the closest enclosing `package.json`, defaulting to CommonJS.
|
|
188
|
+
return (await packageType(new URL('./', out))) === 'module' ? 'esm' : 'cjs'
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function packageType(dir) {
|
|
192
|
+
while (true) {
|
|
193
|
+
let source = null
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
source = await fs.readFile(new URL('package.json', dir))
|
|
197
|
+
} catch {
|
|
198
|
+
// No `package.json` in this directory; keep searching upwards.
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (source !== null) return JSON.parse(source).type ?? null
|
|
202
|
+
|
|
203
|
+
const parent = new URL('../', dir)
|
|
204
|
+
|
|
205
|
+
if (parent.pathname === dir.pathname) return null
|
|
206
|
+
|
|
207
|
+
dir = parent
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
162
211
|
function siblingURL(out, extension) {
|
|
163
212
|
const dir = new URL('./', out)
|
|
164
213
|
const base = path.basename(out.pathname, path.extname(out.pathname))
|
package/lib/resolve.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const lex = require('bare-module-lexer')
|
|
2
|
+
const { resolve } = require('bare-module-traverse')
|
|
3
|
+
|
|
4
|
+
module.exports = function bare(entry, parentURL, opts = {}) {
|
|
5
|
+
const { linked = false, hosts } = opts
|
|
6
|
+
|
|
7
|
+
let extensions
|
|
8
|
+
let conditions = hosts.map((host) => ['bare', 'node', ...host.split('-')])
|
|
9
|
+
|
|
10
|
+
if (entry.type & lex.constants.ADDON) {
|
|
11
|
+
extensions = linked ? [] : ['.bare', '.node']
|
|
12
|
+
conditions = conditions.map((conditions) => ['addon', ...conditions])
|
|
13
|
+
|
|
14
|
+
return resolve.addon(entry.specifier || '.', parentURL, {
|
|
15
|
+
...opts,
|
|
16
|
+
extensions,
|
|
17
|
+
conditions,
|
|
18
|
+
hosts,
|
|
19
|
+
linked
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (entry.type & lex.constants.ASSET) {
|
|
24
|
+
conditions = conditions.map((conditions) => ['asset', ...conditions])
|
|
25
|
+
} else {
|
|
26
|
+
extensions = ['.js', '.cjs', '.mjs', '.ts', '.cts', '.mts', '.json', '.bare', '.node']
|
|
27
|
+
|
|
28
|
+
if (entry.type & lex.constants.REQUIRE) {
|
|
29
|
+
conditions = conditions.map((conditions) => ['require', ...conditions])
|
|
30
|
+
} else if (entry.type & lex.constants.IMPORT) {
|
|
31
|
+
conditions = conditions.map((conditions) => ['import', ...conditions])
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return resolve.module(entry.specifier, parentURL, {
|
|
36
|
+
...opts,
|
|
37
|
+
extensions,
|
|
38
|
+
conditions
|
|
39
|
+
})
|
|
40
|
+
}
|
|
@@ -4,7 +4,6 @@ exports.offload = true
|
|
|
4
4
|
exports.format = 'bundle'
|
|
5
5
|
exports.encoding = null
|
|
6
6
|
exports.extension = '.bundle'
|
|
7
|
-
exports.module = 'cjs'
|
|
8
7
|
exports.hosts = [
|
|
9
8
|
'darwin-arm64',
|
|
10
9
|
'darwin-x64',
|
|
@@ -15,7 +14,9 @@ exports.hosts = [
|
|
|
15
14
|
]
|
|
16
15
|
|
|
17
16
|
exports.generate = function generate(opts) {
|
|
18
|
-
const { bundleSpecifier, ipc, rpc, client = null } = opts
|
|
17
|
+
const { bundleSpecifier, ipc, rpc, module = 'cjs', client = null } = opts
|
|
18
|
+
|
|
19
|
+
const esm = module === 'esm'
|
|
19
20
|
|
|
20
21
|
const returned = client ? `{ ${ipc}, ${rpc} }` : `{ ${ipc} }`
|
|
21
22
|
|
|
@@ -23,9 +24,25 @@ exports.generate = function generate(opts) {
|
|
|
23
24
|
? `{ ${ipc}: import('bare-stow/host').IPC; ${rpc}: ${client.type} }`
|
|
24
25
|
: `{ ${ipc}: import('bare-stow/host').IPC }`
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
const source = esm
|
|
28
|
+
? `\
|
|
29
|
+
import Sidecar from 'bare-sidecar'
|
|
30
|
+
import stow from 'bare-stow/host'
|
|
31
|
+
import { fileURLToPath } from 'url'
|
|
32
|
+
|
|
33
|
+
const bundle = fileURLToPath(new URL(${JSON.stringify(bundleSpecifier)}, import.meta.url))
|
|
34
|
+
|
|
35
|
+
export async function start(opts = {}) {
|
|
36
|
+
const sidecar = new Sidecar(bundle, opts)
|
|
37
|
+
|
|
38
|
+
const ${ipc} = stow.wrap(sidecar)
|
|
39
|
+
${client ? '\n' + indent(client.source, 2) : ''}
|
|
40
|
+
await ${ipc}.ready
|
|
41
|
+
|
|
42
|
+
return ${returned}
|
|
43
|
+
}
|
|
44
|
+
`
|
|
45
|
+
: `\
|
|
29
46
|
const path = require('path')
|
|
30
47
|
const Sidecar = require('bare-sidecar')
|
|
31
48
|
const stow = require('bare-stow/host')
|
|
@@ -44,7 +61,9 @@ ${client ? '\n' + indent(client.source, 4) : ''}
|
|
|
44
61
|
}
|
|
45
62
|
}
|
|
46
63
|
`
|
|
47
|
-
|
|
64
|
+
|
|
65
|
+
return [
|
|
66
|
+
{ source },
|
|
48
67
|
{
|
|
49
68
|
extension: '.d.ts',
|
|
50
69
|
source: `\
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-stow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Module bundling and harness generation for Bare",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./package": "./package.json",
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
"bin.js",
|
|
41
41
|
"lib"
|
|
42
42
|
],
|
|
43
|
+
"workspaces": [
|
|
44
|
+
"test/fixtures/*",
|
|
45
|
+
"test/fixtures/*/*"
|
|
46
|
+
],
|
|
43
47
|
"scripts": {
|
|
44
48
|
"format": "prettier --write . && lunte --fix",
|
|
45
49
|
"lint": "prettier --check . && lunte",
|
|
@@ -61,10 +65,12 @@
|
|
|
61
65
|
"bare-bundle": "^1.8.3",
|
|
62
66
|
"bare-bundle-id": "^1.0.0",
|
|
63
67
|
"bare-fs": "^4.2.1",
|
|
68
|
+
"bare-module-lexer": "^1.5.2",
|
|
64
69
|
"bare-module-traverse": "^2.1.1",
|
|
65
70
|
"bare-pack": "^2.1.0",
|
|
66
71
|
"bare-path": "^3.0.0",
|
|
67
72
|
"bare-stream": "^2.13.1",
|
|
73
|
+
"bare-type-stripper": "^0.1.2",
|
|
68
74
|
"paparam": "^1.5.0"
|
|
69
75
|
},
|
|
70
76
|
"devDependencies": {
|