bare-stow 0.1.1 → 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 CHANGED
@@ -171,9 +171,9 @@ target = {
171
171
  format, // The bundle encoding: 'bundle', 'bundle.cjs', 'bundle.mjs', or 'bundle.json'.
172
172
  encoding, // The string encoding for encoded formats, or null for raw bytes.
173
173
  extension, // The bundle file extension, e.g. '.bundle'.
174
- module, // The harness module system: 'esm' or 'cjs'.
174
+ module, // Optional. Pins the harness module system to 'esm' or 'cjs'.
175
175
  hosts, // The host triples the target supports.
176
- generate({ bundleSpecifier, ipc, rpc, client }) {
176
+ generate({ bundleSpecifier, ipc, rpc, module, client }) {
177
177
  // Return an array of harness artifacts.
178
178
  }
179
179
  }
@@ -181,7 +181,9 @@ target = {
181
181
 
182
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.
183
183
 
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; 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.
185
187
 
186
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:
187
189
 
@@ -194,7 +196,7 @@ rpc = {
194
196
  }
195
197
  ```
196
198
 
197
- 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 target's `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.
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.
198
200
 
199
201
  ## CLI
200
202
 
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: 'esm' | 'cjs'
25
+ module?: 'esm' | 'cjs'
25
26
  hosts: string[]
26
27
  generate(context: TargetContext): Artifact[]
27
28
  }
package/index.js CHANGED
@@ -23,6 +23,9 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
23
23
  out = new URL(out)
24
24
  base = base ? new URL(base) : new URL('./', entry)
25
25
 
26
+ // The target may pin a module system; otherwise it follows the output path.
27
+ const module = t.module || (await resolveModule(out))
28
+
26
29
  if (hosts) {
27
30
  for (const host of hosts) {
28
31
  if (!t.hosts.includes(host)) {
@@ -93,8 +96,8 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
93
96
  const artifacts = rpc(client, resolveRPC).generate({
94
97
  ipc: 'ipc',
95
98
  rpc: 'rpc',
96
- module: t.module,
97
- role: 'client'
99
+ role: 'client',
100
+ module
98
101
  })
99
102
 
100
103
  clientSetup = {
@@ -107,6 +110,7 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
107
110
  bundleSpecifier,
108
111
  ipc: 'ipc',
109
112
  rpc: 'rpc',
113
+ module,
110
114
  client: clientSetup
111
115
  })
112
116
 
@@ -171,6 +175,39 @@ function collectOffloaded(base, out, sink) {
171
175
  }
172
176
  }
173
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
+
174
211
  function siblingURL(out, extension) {
175
212
  const dir = new URL('./', out)
176
213
  const base = path.basename(out.pathname, path.extname(out.pathname))
@@ -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
- return [
27
- {
28
- source: `\
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.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",