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/index.js
CHANGED
|
@@ -1,86 +1,101 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
-
const { fileURLToPath } = require('url')
|
|
3
2
|
const pack = require('bare-pack')
|
|
4
3
|
const id = require('bare-bundle-id')
|
|
5
|
-
const strip = require('bare-type-stripper')
|
|
6
|
-
const { resolve } = require('bare-module-traverse')
|
|
7
|
-
const lex = require('bare-module-lexer')
|
|
8
4
|
const fs = require('./lib/fs')
|
|
5
|
+
const assets = require('./lib/assets')
|
|
9
6
|
const shim = require('./lib/shim')
|
|
10
|
-
const
|
|
11
|
-
const
|
|
7
|
+
const graph = require('./lib/graph')
|
|
8
|
+
const rpcs = require('./lib/rpc')
|
|
9
|
+
const { select } = require('./lib/provider')
|
|
12
10
|
|
|
13
|
-
module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
11
|
+
module.exports = exports = async function* stow(entry, target, out, opts = {}) {
|
|
14
12
|
if (!target) throw new Error("'target' is required")
|
|
15
13
|
if (!out) throw new Error("'out' is required")
|
|
16
14
|
|
|
17
|
-
let {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
let {
|
|
16
|
+
language,
|
|
17
|
+
languageOptions,
|
|
18
|
+
targetOptions,
|
|
19
|
+
client,
|
|
20
|
+
server,
|
|
21
|
+
base,
|
|
22
|
+
hosts,
|
|
23
|
+
resolveTarget,
|
|
24
|
+
resolveRPC,
|
|
25
|
+
resolveLanguage,
|
|
26
|
+
...packOpts
|
|
27
|
+
} = opts
|
|
22
28
|
|
|
23
29
|
entry = await fs.resolveModule(new URL(entry))
|
|
24
30
|
out = new URL(out)
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
const module = t.module || (await resolveModule(out))
|
|
32
|
+
if (!out.pathname.endsWith('/')) out.pathname += '/'
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
const resolved = graph.providers(target, {
|
|
35
|
+
language,
|
|
36
|
+
languageOptions,
|
|
37
|
+
targetOptions,
|
|
38
|
+
hosts,
|
|
39
|
+
resolveTarget,
|
|
40
|
+
resolveLanguage
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const { harness, options } = resolved
|
|
44
|
+
|
|
45
|
+
target = resolved.target
|
|
46
|
+
language = resolved.language
|
|
47
|
+
hosts = resolved.hosts
|
|
48
|
+
|
|
49
|
+
if (client) client = select('RPC library', rpcs(client, resolveRPC), language.name)
|
|
50
|
+
if (server) server = rpcs(server, resolveRPC)
|
|
51
|
+
|
|
52
|
+
const layout = language.layout(out, options)
|
|
53
|
+
|
|
54
|
+
const bundleURL = new URL('index' + target.extension, layout.artifacts ?? layout.harness)
|
|
55
|
+
|
|
56
|
+
// Linked bundles leave their assets out, as files that the runtime loads from
|
|
57
|
+
// next to the bundle or from the application.
|
|
58
|
+
const resources = target.linked && target.linking === 'app' ? assets.app : null
|
|
59
|
+
const name = resources ?? 'index'
|
|
60
|
+
|
|
61
|
+
if (client) {
|
|
62
|
+
client = {
|
|
63
|
+
url: language.sibling(layout.harness, 'client'),
|
|
64
|
+
...client.generate({ role: 'client', options })
|
|
34
65
|
}
|
|
35
|
-
} else {
|
|
36
|
-
hosts = t.hosts
|
|
37
66
|
}
|
|
38
67
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}),
|
|
51
|
-
null
|
|
52
|
-
)
|
|
53
|
-
: null
|
|
68
|
+
const files = [
|
|
69
|
+
{
|
|
70
|
+
url: layout.harness,
|
|
71
|
+
...harness.generate({
|
|
72
|
+
bundle: language.reference(layout.harness, bundleURL),
|
|
73
|
+
client: client && language.reference(layout.harness, client.url),
|
|
74
|
+
resources,
|
|
75
|
+
options
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
]
|
|
54
79
|
|
|
55
|
-
|
|
80
|
+
if (client) files.push(client)
|
|
56
81
|
|
|
57
|
-
const
|
|
82
|
+
const main = shim(entry, target.isolation, server)
|
|
58
83
|
|
|
59
|
-
const
|
|
60
|
-
const bundleSpecifier = relativeSpecifier(out, bundleURL)
|
|
84
|
+
const readModule = graph.readModule(main.modules)
|
|
61
85
|
|
|
62
86
|
const offloaded = []
|
|
63
87
|
|
|
64
88
|
let writeFile
|
|
65
89
|
|
|
66
|
-
if (isOffloadEnabled(
|
|
90
|
+
if (isOffloadEnabled(target.offload)) writeFile = collectOffloaded(offloaded)
|
|
91
|
+
|
|
92
|
+
const traversal = graph.traversal(packOpts, hosts)
|
|
67
93
|
|
|
68
94
|
// The base of the bundle depends on where its modules live, so the graph is
|
|
69
95
|
// packed unbased and unmounted once that is known.
|
|
70
96
|
let bundle = await pack(
|
|
71
|
-
|
|
72
|
-
{
|
|
73
|
-
...packOpts,
|
|
74
|
-
resolve: resolve.bare,
|
|
75
|
-
aliases: {
|
|
76
|
-
'.ts': '.js',
|
|
77
|
-
'.mts': '.mjs',
|
|
78
|
-
'.cts': '.cjs'
|
|
79
|
-
},
|
|
80
|
-
hosts,
|
|
81
|
-
linked: t.linked,
|
|
82
|
-
offload: t.offload
|
|
83
|
-
},
|
|
97
|
+
main.url,
|
|
98
|
+
{ ...traversal, linked: target.linked, offload: target.offload },
|
|
84
99
|
readModule,
|
|
85
100
|
fs.listPrefix,
|
|
86
101
|
fs.resolveModule,
|
|
@@ -91,50 +106,70 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
91
106
|
|
|
92
107
|
bundle = rerootOffloaded(bundle.unmount(base), base, bundleURL, offloaded)
|
|
93
108
|
|
|
109
|
+
let unpacked = []
|
|
110
|
+
|
|
111
|
+
if (target.linked) {
|
|
112
|
+
const result = await assets.unpack(bundle, base, name)
|
|
113
|
+
|
|
114
|
+
bundle = result.bundle
|
|
115
|
+
|
|
116
|
+
// Assets left to the application are written by `link()` instead.
|
|
117
|
+
if (resources === null) unpacked = result.assets
|
|
118
|
+
}
|
|
119
|
+
|
|
94
120
|
bundle.id = id(bundle).toString('hex')
|
|
95
121
|
|
|
96
|
-
// The
|
|
97
|
-
|
|
98
|
-
// into both channels before handing it to the target.
|
|
99
|
-
let clientSetup = null
|
|
122
|
+
// The manifest lists the offloaded files only once it's known there are any.
|
|
123
|
+
const artifacts = offloaded.length > 0 ? [bundleURL, offloadURL(bundleURL)] : [bundleURL]
|
|
100
124
|
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
125
|
+
if (unpacked.length > 0) {
|
|
126
|
+
artifacts.push(new URL(name + '/', bundleURL), new URL(name + '.assets', bundleURL))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const addons = []
|
|
130
|
+
|
|
131
|
+
// Targets that leave linking to the application use `link()` instead.
|
|
132
|
+
if (target.linked && target.linking === 'package') {
|
|
133
|
+
if (!layout.addons) {
|
|
134
|
+
throw new Error(`Language '${language.name}' does not support linked addons`)
|
|
135
|
+
}
|
|
108
136
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
137
|
+
for await (const url of graph.link([main.url], layout.addons, traversal, readModule)) {
|
|
138
|
+
addons.push(url)
|
|
139
|
+
yield { url }
|
|
112
140
|
}
|
|
113
141
|
}
|
|
114
142
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
143
|
+
files.push({
|
|
144
|
+
url: layout.manifest,
|
|
145
|
+
source: language.manifest({
|
|
146
|
+
name: layout.name,
|
|
147
|
+
dependencies: Object.assign({}, ...files.map((file) => file.dependencies)),
|
|
148
|
+
options,
|
|
149
|
+
hosts,
|
|
150
|
+
artifacts: artifacts.map((url) => language.reference(layout.manifest, url)),
|
|
151
|
+
addons: addons.map((url) => language.reference(layout.manifest, url))
|
|
152
|
+
})
|
|
121
153
|
})
|
|
122
154
|
|
|
123
|
-
for (const
|
|
124
|
-
|
|
125
|
-
|
|
155
|
+
for (const file of files) {
|
|
156
|
+
await fs.writeFile(file.url, file.source)
|
|
157
|
+
yield { url: file.url }
|
|
126
158
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
for (const artifact of artifacts) {
|
|
130
|
-
const url = artifact.extension ? siblingURL(out, artifact.extension) : out
|
|
159
|
+
if (file.types) {
|
|
160
|
+
const typesURL = language.types(file.url)
|
|
131
161
|
|
|
132
|
-
|
|
133
|
-
|
|
162
|
+
await fs.writeFile(typesURL, file.types)
|
|
163
|
+
yield { url: typesURL }
|
|
164
|
+
}
|
|
134
165
|
}
|
|
135
166
|
|
|
136
|
-
await fs.writeFile(bundleURL, encodeBundle(bundle,
|
|
137
|
-
yield { url: bundleURL }
|
|
167
|
+
await fs.writeFile(bundleURL, encodeBundle(bundle, target.format, target.encoding ?? 'utf8'))
|
|
168
|
+
yield { url: bundleURL, base }
|
|
169
|
+
|
|
170
|
+
if (unpacked.length > 0) {
|
|
171
|
+
for await (const url of assets.write(new URL('./', bundleURL), name, unpacked)) yield { url }
|
|
172
|
+
}
|
|
138
173
|
|
|
139
174
|
// Offloaded files are collected in the order their reads complete, so they
|
|
140
175
|
// are sorted to keep the output stable.
|
|
@@ -146,8 +181,8 @@ module.exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
146
181
|
}
|
|
147
182
|
}
|
|
148
183
|
|
|
149
|
-
function resolveBase(base,
|
|
150
|
-
const modules = [...
|
|
184
|
+
function resolveBase(base, bundle) {
|
|
185
|
+
const modules = [...bundle.keys()].map((href) => new URL(href))
|
|
151
186
|
|
|
152
187
|
if (base) {
|
|
153
188
|
base = new URL(base)
|
|
@@ -191,20 +226,6 @@ function isOffloadEnabled(offload) {
|
|
|
191
226
|
return false
|
|
192
227
|
}
|
|
193
228
|
|
|
194
|
-
function wrapReadModule(readModule, shimURL, shimSource) {
|
|
195
|
-
return async function (url) {
|
|
196
|
-
if (url.href === shimURL.href) return shimSource
|
|
197
|
-
|
|
198
|
-
const source = await readModule(url)
|
|
199
|
-
|
|
200
|
-
if (source === null) return null
|
|
201
|
-
|
|
202
|
-
if (/\.(c|m)?ts$/.test(url.pathname)) return strip(source)
|
|
203
|
-
|
|
204
|
-
return source
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
229
|
function collectOffloaded(sink) {
|
|
209
230
|
return function writeFile(url, source) {
|
|
210
231
|
sink.push({ url, source })
|
|
@@ -217,11 +238,15 @@ function collectOffloaded(sink) {
|
|
|
217
238
|
// next to the harness, as a mirrored `node_modules` beside the harness would
|
|
218
239
|
// shadow the packages it imports. Their resolutions are rewritten to point into
|
|
219
240
|
// that directory, along with the directories that hold them.
|
|
241
|
+
function offloadURL(bundleURL) {
|
|
242
|
+
return new URL(path.basename(bundleURL.pathname) + '.d/', bundleURL)
|
|
243
|
+
}
|
|
244
|
+
|
|
220
245
|
function rerootOffloaded(bundle, base, bundleURL, offloaded) {
|
|
221
246
|
if (offloaded.length === 0) return bundle
|
|
222
247
|
|
|
223
|
-
const
|
|
224
|
-
const
|
|
248
|
+
const dir = offloadURL(bundleURL)
|
|
249
|
+
const name = path.basename(dir.pathname)
|
|
225
250
|
const rewrites = new Map()
|
|
226
251
|
|
|
227
252
|
for (const file of offloaded) {
|
|
@@ -288,107 +313,6 @@ function isTerminator(input) {
|
|
|
288
313
|
return input === '' || input.endsWith('/') || input.endsWith(':')
|
|
289
314
|
}
|
|
290
315
|
|
|
291
|
-
async function assertImportsResolve(source, out) {
|
|
292
|
-
for (const entry of lex(source).imports) {
|
|
293
|
-
const { specifier } = entry
|
|
294
|
-
|
|
295
|
-
if (specifier.startsWith('.') || specifier.startsWith('/')) continue
|
|
296
|
-
|
|
297
|
-
if (await resolves(entry, out)) continue
|
|
298
|
-
|
|
299
|
-
throw new Error(
|
|
300
|
-
`Harness '${fileURLToPath(out)}' imports '${specifier}', which the package containing it does not depend on`
|
|
301
|
-
)
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
async function resolves(entry, parentURL) {
|
|
306
|
-
const resolver = resolve.bare(entry, parentURL)
|
|
307
|
-
|
|
308
|
-
let next = resolver.next()
|
|
309
|
-
|
|
310
|
-
while (next.done !== true) {
|
|
311
|
-
const value = next.value
|
|
312
|
-
|
|
313
|
-
if (value.package) {
|
|
314
|
-
const source = await fs.readModule(value.package)
|
|
315
|
-
|
|
316
|
-
next = resolver.next(source === null ? null : JSON.parse(source))
|
|
317
|
-
} else {
|
|
318
|
-
const exists = await fs.isFile(value.resolution)
|
|
319
|
-
|
|
320
|
-
if (exists) return true
|
|
321
|
-
|
|
322
|
-
next = resolver.next(false)
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
return false
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
async function resolveModule(out) {
|
|
330
|
-
switch (path.extname(out.pathname)) {
|
|
331
|
-
case '.mjs':
|
|
332
|
-
return 'esm'
|
|
333
|
-
case '.cjs':
|
|
334
|
-
return 'cjs'
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// For an ambiguous extension (e.g. '.js') the module system follows the
|
|
338
|
-
// `type` of the closest enclosing `package.json`, defaulting to CommonJS.
|
|
339
|
-
return (await packageType(new URL('./', out))) === 'module' ? 'esm' : 'cjs'
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
async function packageType(dir) {
|
|
343
|
-
while (true) {
|
|
344
|
-
let source = null
|
|
345
|
-
|
|
346
|
-
try {
|
|
347
|
-
source = await fs.readFile(new URL('package.json', dir))
|
|
348
|
-
} catch {
|
|
349
|
-
// No `package.json` in this directory; keep searching upwards.
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (source !== null) return JSON.parse(source).type ?? null
|
|
353
|
-
|
|
354
|
-
const parent = new URL('../', dir)
|
|
355
|
-
|
|
356
|
-
if (parent.pathname === dir.pathname) return null
|
|
357
|
-
|
|
358
|
-
dir = parent
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function siblingURL(out, extension) {
|
|
363
|
-
const dir = new URL('./', out)
|
|
364
|
-
const base = path.basename(out.pathname, path.extname(out.pathname))
|
|
365
|
-
|
|
366
|
-
return new URL(base + extension, dir)
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function fragment(artifacts, extension) {
|
|
370
|
-
for (const artifact of artifacts) {
|
|
371
|
-
if ((artifact.extension ?? null) === extension) return artifact.source
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
return null
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
function relativeSpecifier(from, to) {
|
|
378
|
-
if (from.protocol === 'file:' && to.protocol === 'file:') {
|
|
379
|
-
const fromDir = path.dirname(fileURLToPath(from))
|
|
380
|
-
const toPath = fileURLToPath(to)
|
|
381
|
-
|
|
382
|
-
let relative = path.relative(fromDir, toPath).split(path.sep).join('/')
|
|
383
|
-
|
|
384
|
-
if (!relative.startsWith('.')) relative = './' + relative
|
|
385
|
-
|
|
386
|
-
return relative
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
return to.href
|
|
390
|
-
}
|
|
391
|
-
|
|
392
316
|
function encodeBundle(bundle, format, encoding) {
|
|
393
317
|
const data = bundle.toBuffer()
|
|
394
318
|
|
|
@@ -405,3 +329,7 @@ function encodeBundle(bundle, format, encoding) {
|
|
|
405
329
|
throw new Error(`Unknown format '${format}'`)
|
|
406
330
|
}
|
|
407
331
|
}
|
|
332
|
+
|
|
333
|
+
exports.link = require('./lib/link')
|
|
334
|
+
|
|
335
|
+
exports.resources = assets.app
|
package/lib/assets.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const unpack = require('bare-unpack')
|
|
2
|
+
const fs = require('./fs')
|
|
3
|
+
|
|
4
|
+
// The assets of bundles linked by the application share one directory, named
|
|
5
|
+
// after the bundle that Bare Kit starts them as.
|
|
6
|
+
exports.app = 'bare-stow'
|
|
7
|
+
|
|
8
|
+
// Assets are laid out the way Bare Kit expects: the assets of `<name>.bundle`
|
|
9
|
+
// live in `<name>/`, listed in `<name>.assets` for platforms that extract them.
|
|
10
|
+
exports.unpack = async function (bundle, base, name) {
|
|
11
|
+
const assets = []
|
|
12
|
+
|
|
13
|
+
bundle = await unpack(bundle, { files: false, assets: true }, async (key) => {
|
|
14
|
+
const relative = await exports.path(new URL('.' + key, base))
|
|
15
|
+
|
|
16
|
+
assets.push({ path: relative, source: bundle.read(key) })
|
|
17
|
+
|
|
18
|
+
return '/../' + name + '/' + relative
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
return { bundle, assets }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Assets of dependencies keep their path from `node_modules`, and others their
|
|
25
|
+
// path within their package, so that every bundle places an asset alike.
|
|
26
|
+
exports.path = async function (url) {
|
|
27
|
+
const nm = url.pathname.indexOf('/node_modules/')
|
|
28
|
+
|
|
29
|
+
if (nm >= 0) return url.pathname.slice(nm + 1)
|
|
30
|
+
|
|
31
|
+
for (let dir = new URL('./', url); ; dir = new URL('../', dir)) {
|
|
32
|
+
if (await fs.isFile(new URL('package.json', dir))) {
|
|
33
|
+
return url.pathname.slice(dir.pathname.length)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (dir.pathname === '/') throw new Error(`Asset '${url.href}' is not part of a package`)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
exports.write = async function* (dir, name, assets) {
|
|
41
|
+
assets = [...assets].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0))
|
|
42
|
+
|
|
43
|
+
for (const asset of assets) {
|
|
44
|
+
const url = new URL(name + '/' + asset.path, dir)
|
|
45
|
+
|
|
46
|
+
await fs.writeFile(url, asset.source)
|
|
47
|
+
yield url
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const listing = new URL(name + '.assets', dir)
|
|
51
|
+
|
|
52
|
+
await fs.writeFile(listing, assets.map((asset) => '/' + asset.path + '\n').join(''))
|
|
53
|
+
yield listing
|
|
54
|
+
}
|
package/lib/fs.js
CHANGED
|
@@ -43,7 +43,7 @@ exports.readModule = async function readModule(url) {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
function isFile(url) {
|
|
46
|
+
exports.isFile = function isFile(url) {
|
|
47
47
|
return new Promise((resolve) => {
|
|
48
48
|
fs.stat(fileURLToPath(url), (err, stat) => {
|
|
49
49
|
resolve(err === null && stat.isFile())
|
|
@@ -60,7 +60,7 @@ function isDir(url) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
exports.listPrefix = async function* listPrefix(url) {
|
|
63
|
-
if (await isFile(url)) return yield url
|
|
63
|
+
if (await exports.isFile(url)) return yield url
|
|
64
64
|
|
|
65
65
|
if (url.pathname[url.pathname.length - 1] !== '/') {
|
|
66
66
|
url.pathname += '/'
|
|
@@ -77,6 +77,4 @@ exports.listPrefix = async function* listPrefix(url) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
exports.isFile = isFile
|
|
81
|
-
|
|
82
80
|
exports.resolveModule = require('bare-pack/fs').resolveModule
|
package/lib/graph.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const { fileURLToPath, pathToFileURL } = require('url')
|
|
2
|
+
const bareLink = require('bare-link')
|
|
3
|
+
const strip = require('bare-type-stripper')
|
|
4
|
+
const { resolve } = require('bare-module-traverse')
|
|
5
|
+
const fs = require('./fs')
|
|
6
|
+
const targets = require('./target')
|
|
7
|
+
const languages = require('./language')
|
|
8
|
+
const { select } = require('./provider')
|
|
9
|
+
|
|
10
|
+
exports.providers = function providers(target, opts = {}) {
|
|
11
|
+
let {
|
|
12
|
+
language = 'javascript',
|
|
13
|
+
languageOptions,
|
|
14
|
+
targetOptions,
|
|
15
|
+
hosts,
|
|
16
|
+
resolveTarget,
|
|
17
|
+
resolveLanguage
|
|
18
|
+
} = opts
|
|
19
|
+
|
|
20
|
+
target = targets(target, resolveTarget)
|
|
21
|
+
language = languages(language, resolveLanguage)
|
|
22
|
+
|
|
23
|
+
const harness = select('Target', target, language.name)
|
|
24
|
+
|
|
25
|
+
// A target may pin options that it can't do without, overriding those given.
|
|
26
|
+
const options = { ...language.options(languageOptions), ...harness.options }
|
|
27
|
+
|
|
28
|
+
targetOptions = targets.options(target, targetOptions)
|
|
29
|
+
|
|
30
|
+
target = targets.resolve(target, { language: language.name, options, targetOptions })
|
|
31
|
+
|
|
32
|
+
hosts = resolveHosts(hosts, target.hosts, target.name)
|
|
33
|
+
|
|
34
|
+
return { target, language, harness, options, targetOptions, hosts }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveHosts(hosts, supported, name) {
|
|
38
|
+
if (!hosts) return supported
|
|
39
|
+
|
|
40
|
+
for (const host of hosts) {
|
|
41
|
+
if (!supported.includes(host)) {
|
|
42
|
+
throw new Error(`Host '${host}' is not supported by target '${name}'`)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return hosts
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// The graph is traversed the same way when packing it as when linking its
|
|
50
|
+
// addons.
|
|
51
|
+
exports.traversal = function traversal(opts, hosts) {
|
|
52
|
+
return {
|
|
53
|
+
...opts,
|
|
54
|
+
resolve: resolve.bare,
|
|
55
|
+
aliases: {
|
|
56
|
+
'.ts': '.js',
|
|
57
|
+
'.mts': '.mjs',
|
|
58
|
+
'.cts': '.cjs'
|
|
59
|
+
},
|
|
60
|
+
hosts
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
exports.readModule = function readModule(modules) {
|
|
65
|
+
return async function (url) {
|
|
66
|
+
if (modules.has(url.href)) return modules.get(url.href)
|
|
67
|
+
|
|
68
|
+
const source = await fs.readModule(url)
|
|
69
|
+
|
|
70
|
+
if (source === null) return null
|
|
71
|
+
|
|
72
|
+
if (/\.(c|m)?ts$/.test(url.pathname)) return strip(source)
|
|
73
|
+
|
|
74
|
+
return source
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// The layout must not depend on the hosts, as Swift packages only accept
|
|
79
|
+
// XCFrameworks. Resources linked outside of the directory are intermediate.
|
|
80
|
+
exports.link = async function* link(entries, dir, traversal, readModule) {
|
|
81
|
+
for await (const resource of bareLink(
|
|
82
|
+
entries.map((url) => fileURLToPath(url)),
|
|
83
|
+
{
|
|
84
|
+
...traversal,
|
|
85
|
+
out: fileURLToPath(dir),
|
|
86
|
+
alwaysMultiHost: true,
|
|
87
|
+
readModule,
|
|
88
|
+
listPrefix: fs.listPrefix,
|
|
89
|
+
resolveModule: fs.resolveModule
|
|
90
|
+
}
|
|
91
|
+
)) {
|
|
92
|
+
const url = pathToFileURL(resource)
|
|
93
|
+
|
|
94
|
+
if (url.href.startsWith(dir.href)) yield url
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const { fileURLToPath } = require('url')
|
|
3
|
+
|
|
4
|
+
exports.name = 'javascript'
|
|
5
|
+
|
|
6
|
+
const MODULES = ['esm', 'cjs']
|
|
7
|
+
|
|
8
|
+
exports.options = function options(given = {}) {
|
|
9
|
+
const { module = 'esm', ...unknown } = given
|
|
10
|
+
|
|
11
|
+
for (const name of Object.keys(unknown)) {
|
|
12
|
+
throw new Error(`Unknown option '${name}' for language 'javascript'`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!MODULES.includes(module)) {
|
|
16
|
+
throw new Error(`Option 'module' must be one of ${MODULES.join(', ')}, got '${module}'`)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return { module }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
exports.layout = function layout(out) {
|
|
23
|
+
const name = path.posix.basename(out.pathname)
|
|
24
|
+
|
|
25
|
+
if (!/^[a-z0-9~-][a-z0-9._~-]*$/.test(name)) {
|
|
26
|
+
throw new Error(`'${name}' is not a valid package name`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
harness: new URL('index.js', out),
|
|
31
|
+
name,
|
|
32
|
+
manifest: new URL('package.json', out)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
exports.sibling = function sibling(url, name) {
|
|
37
|
+
return new URL(`${name}.js`, url)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
exports.types = function types(url) {
|
|
41
|
+
return new URL(path.posix.basename(url.pathname, '.js') + '.d.ts', url)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
exports.reference = function reference(from, to) {
|
|
45
|
+
if (from.protocol === 'file:' && to.protocol === 'file:') {
|
|
46
|
+
const fromDir = path.dirname(fileURLToPath(from))
|
|
47
|
+
const toPath = fileURLToPath(to)
|
|
48
|
+
|
|
49
|
+
let relative = path.relative(fromDir, toPath).split(path.sep).join('/')
|
|
50
|
+
|
|
51
|
+
if (!relative.startsWith('.')) relative = './' + relative
|
|
52
|
+
|
|
53
|
+
return relative
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return to.href
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
exports.manifest = function manifest({ name, dependencies, options }) {
|
|
60
|
+
const pkg = {
|
|
61
|
+
name,
|
|
62
|
+
private: true,
|
|
63
|
+
type: options.module === 'esm' ? 'module' : 'commonjs',
|
|
64
|
+
exports: {
|
|
65
|
+
'.': {
|
|
66
|
+
types: './index.d.ts',
|
|
67
|
+
default: './index.js'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
dependencies
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return JSON.stringify(pkg, null, 2) + '\n'
|
|
74
|
+
}
|