bare-stow 0.1.9 → 0.3.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 +92 -111
- package/bin.js +142 -53
- package/index.d.ts +318 -82
- package/index.js +163 -207
- package/lib/assets.js +54 -0
- package/lib/fs.js +2 -4
- package/lib/graph.js +94 -0
- package/lib/language/javascript.js +74 -0
- package/lib/language/kotlin.js +139 -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 +149 -0
- package/lib/provider.js +33 -0
- package/lib/rpc/bare-rpc.js +147 -15
- package/lib/rpc.js +6 -18
- package/lib/shim.js +24 -31
- package/lib/target/bare-kit.js +238 -0
- package/lib/target/bare-sidecar.js +86 -53
- package/lib/target/bare-worker.js +18 -72
- package/lib/target/javascript.js +63 -0
- package/lib/target/swift.js +57 -0
- package/lib/target.js +43 -12
- package/package.json +20 -36
- package/lib/host.d.ts +0 -64
- package/lib/host.js +0 -114
- 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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
|
|
3
|
+
exports.name = 'kotlin'
|
|
4
|
+
|
|
5
|
+
exports.options = function options(given = {}) {
|
|
6
|
+
const { package: name, platform = 'android', ...unknown } = given
|
|
7
|
+
|
|
8
|
+
for (const option of Object.keys(unknown)) {
|
|
9
|
+
throw new Error(`Unknown option '${option}' for language 'kotlin'`)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (name === undefined) throw new Error("Option 'package' is required for language 'kotlin'")
|
|
13
|
+
|
|
14
|
+
if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/.test(name)) {
|
|
15
|
+
throw new Error(`'${name}' is not a valid Kotlin package name`)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (platform !== 'android' && platform !== 'jvm') {
|
|
19
|
+
throw new Error(`Option 'platform' must be 'android' or 'jvm', got '${platform}'`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { package: name, platform }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// The bundle is an asset or resource of the module, under the directory of its
|
|
26
|
+
// package as those of every module of the application share one namespace. A
|
|
27
|
+
// JVM module can't read its resources as files, so it lists the files beside
|
|
28
|
+
// the bundle for the harness to extract.
|
|
29
|
+
exports.layout = function layout(out, options) {
|
|
30
|
+
const dir = options.package.replace(/\./g, '/')
|
|
31
|
+
|
|
32
|
+
const module = {
|
|
33
|
+
harness: new URL(`src/main/kotlin/${dir}/Harness.kt`, out),
|
|
34
|
+
name: options.package,
|
|
35
|
+
manifest: new URL('build.gradle', out)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (options.platform === 'jvm') {
|
|
39
|
+
return { ...module, artifacts: new URL(`src/main/resources/${dir}/`, out), listing: true }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
...module,
|
|
44
|
+
artifacts: new URL(`src/main/assets/${dir}/`, out),
|
|
45
|
+
addons: new URL('src/main/addons/', out),
|
|
46
|
+
resources: new URL('src/main/assets/', out)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
exports.sibling = function sibling(url, name) {
|
|
51
|
+
return new URL(capitalize(name) + '.kt', url)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Assets and resources are referenced by their path within those of the
|
|
55
|
+
// application.
|
|
56
|
+
exports.reference = function reference(from, to) {
|
|
57
|
+
for (const dir of ['/src/main/assets/', '/src/main/resources/']) {
|
|
58
|
+
const index = to.pathname.lastIndexOf(dir)
|
|
59
|
+
|
|
60
|
+
if (index !== -1) return to.pathname.slice(index + dir.length)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return path.posix.relative(path.posix.dirname(from.pathname), to.pathname)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The dependencies of Kotlin generators map Maven coordinates without a version
|
|
67
|
+
// to the version that the module uses. They are part of the API of the module,
|
|
68
|
+
// as the harness exposes their types.
|
|
69
|
+
exports.manifest = function manifest({ name, dependencies, options = {}, addons = [] }) {
|
|
70
|
+
const apis = Object.entries(dependencies).map(
|
|
71
|
+
([coordinates, version]) => ` api ${JSON.stringify(coordinates + ':' + version)}`
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if (options.platform === 'jvm') {
|
|
75
|
+
return `\
|
|
76
|
+
plugins {
|
|
77
|
+
id "java-library"
|
|
78
|
+
id "org.jetbrains.kotlin.jvm"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
dependencies {
|
|
82
|
+
${apis.join('\n')}
|
|
83
|
+
}
|
|
84
|
+
`
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Linked addons are native libraries in a directory per ABI, along with the
|
|
88
|
+
// archives of any Java classes they use.
|
|
89
|
+
const libraries = [
|
|
90
|
+
...new Set(
|
|
91
|
+
addons
|
|
92
|
+
.filter((addon) => addon.endsWith('.so'))
|
|
93
|
+
.map((addon) => path.posix.dirname(path.posix.dirname(addon)))
|
|
94
|
+
)
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
for (const addon of addons) {
|
|
98
|
+
if (addon.endsWith('.jar')) apis.push(` api files(${JSON.stringify(addon)})`)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const sourceSets = libraries.length
|
|
102
|
+
? `
|
|
103
|
+
|
|
104
|
+
sourceSets {
|
|
105
|
+
main {
|
|
106
|
+
${libraries.map((dir) => ` jniLibs.directories.add(${JSON.stringify(dir)})`).join('\n')}
|
|
107
|
+
}
|
|
108
|
+
}`
|
|
109
|
+
: ''
|
|
110
|
+
|
|
111
|
+
// Projects that opt out of the Kotlin support built into AGP 9, such as those
|
|
112
|
+
// of Expo, need the Kotlin plugin to compile the harness.
|
|
113
|
+
return `\
|
|
114
|
+
plugins {
|
|
115
|
+
id "com.android.library"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (findProperty("android.builtInKotlin") == "false") {
|
|
119
|
+
apply plugin: "org.jetbrains.kotlin.android"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
android {
|
|
123
|
+
namespace = ${JSON.stringify(name)}
|
|
124
|
+
compileSdk = 36
|
|
125
|
+
|
|
126
|
+
defaultConfig {
|
|
127
|
+
minSdk = 29
|
|
128
|
+
}${sourceSets}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
dependencies {
|
|
132
|
+
${apis.join('\n')}
|
|
133
|
+
}
|
|
134
|
+
`
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function capitalize(name) {
|
|
138
|
+
return name.charAt(0).toUpperCase() + name.slice(1)
|
|
139
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
|
|
3
|
+
exports.name = 'python'
|
|
4
|
+
|
|
5
|
+
exports.options = function options(given = {}) {
|
|
6
|
+
for (const name of Object.keys(given)) {
|
|
7
|
+
throw new Error(`Unknown option '${name}' for language 'python'`)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return {}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
exports.layout = function layout(out) {
|
|
14
|
+
const name = path.posix.basename(out.pathname).replace(/-/g, '_')
|
|
15
|
+
|
|
16
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
17
|
+
throw new Error(`'${name}' is not a valid Python package name`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
harness: new URL(`${name}/__init__.py`, out),
|
|
22
|
+
name,
|
|
23
|
+
manifest: new URL('pyproject.toml', out)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
exports.sibling = function sibling(url, name) {
|
|
28
|
+
return new URL(`${name}.py`, url)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
exports.reference = function reference(from, to) {
|
|
32
|
+
const relative = path.posix.relative(path.posix.dirname(from.pathname), to.pathname)
|
|
33
|
+
|
|
34
|
+
if (relative.endsWith('.py') && !relative.includes('/')) {
|
|
35
|
+
return '.' + path.posix.basename(relative, '.py')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return relative
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
exports.manifest = function manifest({ name, dependencies, artifacts }) {
|
|
42
|
+
const requirements = Object.entries(dependencies).map(
|
|
43
|
+
([dependency, version]) => ` ${JSON.stringify(dependency + version)},`
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
// The bundle is listed as an artifact so that it is included in the wheel
|
|
47
|
+
// even when ignored by version control.
|
|
48
|
+
return `\
|
|
49
|
+
[build-system]
|
|
50
|
+
requires = ["hatchling"]
|
|
51
|
+
build-backend = "hatchling.build"
|
|
52
|
+
|
|
53
|
+
[project]
|
|
54
|
+
name = ${JSON.stringify(name)}
|
|
55
|
+
version = "0.0.0"
|
|
56
|
+
requires-python = ">=3.10"
|
|
57
|
+
dependencies = [
|
|
58
|
+
${requirements.join('\n')}
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.hatch.build.targets.wheel]
|
|
62
|
+
packages = [${JSON.stringify(name)}]
|
|
63
|
+
artifacts = [${artifacts.map((artifact) => JSON.stringify(artifact)).join(', ')}]
|
|
64
|
+
`
|
|
65
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
|
|
3
|
+
exports.name = 'swift'
|
|
4
|
+
|
|
5
|
+
exports.options = function options(given = {}) {
|
|
6
|
+
for (const name of Object.keys(given)) {
|
|
7
|
+
throw new Error(`Unknown option '${name}' for language 'swift'`)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return {}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
exports.layout = function layout(out) {
|
|
14
|
+
const name = path.posix.basename(out.pathname).split(/[-_.]/).map(capitalize).join('')
|
|
15
|
+
|
|
16
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
17
|
+
throw new Error(`'${name}' is not a valid Swift module name`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
harness: new URL(`Sources/${name}/Harness.swift`, out),
|
|
22
|
+
name,
|
|
23
|
+
manifest: new URL('Package.swift', out),
|
|
24
|
+
addons: new URL('Frameworks/', out),
|
|
25
|
+
resources: out
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
exports.sibling = function sibling(url, name) {
|
|
30
|
+
return new URL(capitalize(name) + '.swift', url)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
exports.reference = function reference(from, to) {
|
|
34
|
+
return path.posix.relative(path.posix.dirname(from.pathname), to.pathname)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// The oldest versions of the Apple platforms that the Swift runtimes support.
|
|
38
|
+
const platforms = {
|
|
39
|
+
darwin: '.macOS(.v13)',
|
|
40
|
+
ios: '.iOS(.v15)'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// The dependencies of Swift generators map package identities to the URL, the
|
|
44
|
+
// minimum version, and the products of the package that the module uses. Like
|
|
45
|
+
// npm, a minor release of a package before 1.0.0 may break compatibility.
|
|
46
|
+
exports.manifest = function manifest({ name, dependencies, hosts, artifacts, addons = [] }) {
|
|
47
|
+
const supported = Object.entries(platforms)
|
|
48
|
+
.filter(([platform]) => hosts.some((host) => host.startsWith(platform + '-')))
|
|
49
|
+
.map(([, minimum]) => minimum)
|
|
50
|
+
|
|
51
|
+
const packages = Object.values(dependencies).map(({ url, from }) => {
|
|
52
|
+
const version = from.startsWith('0.')
|
|
53
|
+
? `.upToNextMinor(from: ${JSON.stringify(from)})`
|
|
54
|
+
: `from: ${JSON.stringify(from)}`
|
|
55
|
+
|
|
56
|
+
return ` .package(url: ${JSON.stringify(url)}, ${version}),`
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const products = Object.entries(dependencies).flatMap(([identity, { products }]) =>
|
|
60
|
+
products.map(
|
|
61
|
+
(product) =>
|
|
62
|
+
` .product(name: ${JSON.stringify(product)}, package: ${JSON.stringify(identity)}),`
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
// Linked addons are binary targets that the module depends on.
|
|
67
|
+
const frameworks = addons
|
|
68
|
+
.filter((addon) => addon.endsWith('.xcframework'))
|
|
69
|
+
.map((addon) => ({ name: path.posix.basename(addon, '.xcframework'), path: addon }))
|
|
70
|
+
|
|
71
|
+
for (const framework of frameworks) products.push(` ${JSON.stringify(framework.name)},`)
|
|
72
|
+
|
|
73
|
+
const binaries = frameworks.map(
|
|
74
|
+
(framework) =>
|
|
75
|
+
`,\n .binaryTarget(name: ${JSON.stringify(framework.name)}, path: ${JSON.stringify(framework.path)})`
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
// Resources are named relative to the directory of the module.
|
|
79
|
+
const resources = artifacts.map(
|
|
80
|
+
(artifact) => `.copy(${JSON.stringify(path.posix.relative(`Sources/${name}`, artifact))})`
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return `\
|
|
84
|
+
// swift-tools-version: 5.10
|
|
85
|
+
import PackageDescription
|
|
86
|
+
|
|
87
|
+
let package = Package(
|
|
88
|
+
name: ${JSON.stringify(name)},
|
|
89
|
+
platforms: [${supported.join(', ')}],
|
|
90
|
+
products: [
|
|
91
|
+
.library(name: ${JSON.stringify(name)}, targets: [${JSON.stringify(name)}])
|
|
92
|
+
],
|
|
93
|
+
dependencies: [
|
|
94
|
+
${packages.join('\n')}
|
|
95
|
+
],
|
|
96
|
+
targets: [
|
|
97
|
+
.target(
|
|
98
|
+
name: ${JSON.stringify(name)},
|
|
99
|
+
dependencies: [
|
|
100
|
+
${products.join('\n')}
|
|
101
|
+
],
|
|
102
|
+
resources: [${resources.join(', ')}]
|
|
103
|
+
)${binaries.join('')}
|
|
104
|
+
]
|
|
105
|
+
)
|
|
106
|
+
`
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function capitalize(name) {
|
|
110
|
+
return name.charAt(0).toUpperCase() + name.slice(1)
|
|
111
|
+
}
|
package/lib/language.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { registry } = require('./provider')
|
|
2
|
+
|
|
3
|
+
const providers = {
|
|
4
|
+
javascript: () => require('./language/javascript'),
|
|
5
|
+
kotlin: () => require('./language/kotlin'),
|
|
6
|
+
python: () => require('./language/python'),
|
|
7
|
+
swift: () => require('./language/swift')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = registry('language', providers, function validate(language) {
|
|
11
|
+
for (const method of ['options', 'layout', 'sibling', 'reference', 'manifest']) {
|
|
12
|
+
if (typeof language?.[method] !== 'function') {
|
|
13
|
+
throw new Error(`Language must provide a ${method}() function`)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return language
|
|
18
|
+
})
|
package/lib/link.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const pack = require('bare-pack')
|
|
2
|
+
const fs = require('./fs')
|
|
3
|
+
const assets = require('./assets')
|
|
4
|
+
const shim = require('./shim')
|
|
5
|
+
const graph = require('./graph')
|
|
6
|
+
const rpcs = require('./rpc')
|
|
7
|
+
|
|
8
|
+
module.exports = async function* link(entries, target, out, opts = {}) {
|
|
9
|
+
if (!target) throw new Error("'target' is required")
|
|
10
|
+
if (!out) throw new Error("'out' is required")
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
language,
|
|
14
|
+
languageOptions,
|
|
15
|
+
targetOptions,
|
|
16
|
+
server,
|
|
17
|
+
hosts,
|
|
18
|
+
resolveTarget,
|
|
19
|
+
resolveRPC,
|
|
20
|
+
resolveLanguage,
|
|
21
|
+
readModule = fs.readModule,
|
|
22
|
+
listPrefix = fs.listPrefix,
|
|
23
|
+
resolveModule = fs.resolveModule,
|
|
24
|
+
...packOpts
|
|
25
|
+
} = opts
|
|
26
|
+
|
|
27
|
+
entries = await Promise.all([].concat(entries).map((entry) => resolveModule(new URL(entry))))
|
|
28
|
+
|
|
29
|
+
if (entries.length === 0) throw new Error("'entries' must not be empty")
|
|
30
|
+
out = new URL(out)
|
|
31
|
+
|
|
32
|
+
if (!out.pathname.endsWith('/')) out.pathname += '/'
|
|
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 (!target.linked) throw new Error(`Target '${target.name}' does not link addons`)
|
|
50
|
+
|
|
51
|
+
if (typeof harness.link !== 'function') {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Target '${target.name}' does not support linking for language '${language.name}'`
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (server) server = rpcs(server, resolveRPC)
|
|
58
|
+
|
|
59
|
+
const layout = language.layout(out, options)
|
|
60
|
+
|
|
61
|
+
if (!layout.addons) throw new Error(`Language '${language.name}' does not support linked addons`)
|
|
62
|
+
|
|
63
|
+
// Every shim imports the same runtime and RPC server, so one covers them all.
|
|
64
|
+
const main = shim(entries[0], target.isolation, server)
|
|
65
|
+
|
|
66
|
+
const traversal = graph.traversal(packOpts, hosts)
|
|
67
|
+
|
|
68
|
+
const hooks = {
|
|
69
|
+
readModule: graph.readModule(main.modules, readModule),
|
|
70
|
+
listPrefix,
|
|
71
|
+
resolveModule
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const addons = []
|
|
75
|
+
|
|
76
|
+
for await (const url of graph.link(
|
|
77
|
+
[main.url, ...entries.slice(1)],
|
|
78
|
+
layout.addons,
|
|
79
|
+
traversal,
|
|
80
|
+
hooks
|
|
81
|
+
)) {
|
|
82
|
+
addons.push(url)
|
|
83
|
+
yield { url }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const linked = await collectAssets(entries, traversal, hooks)
|
|
87
|
+
|
|
88
|
+
if (linked.length > 0) {
|
|
89
|
+
if (!layout.resources) {
|
|
90
|
+
throw new Error(`Language '${language.name}' does not support linked assets`)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for await (const url of assets.write(layout.resources, assets.app, linked)) yield { url }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const generated = harness.link({ options })
|
|
97
|
+
|
|
98
|
+
const files = []
|
|
99
|
+
|
|
100
|
+
if (generated.source) files.push({ url: layout.harness, source: generated.source })
|
|
101
|
+
|
|
102
|
+
files.push({
|
|
103
|
+
url: layout.manifest,
|
|
104
|
+
source: language.manifest({
|
|
105
|
+
name: layout.name,
|
|
106
|
+
dependencies: generated.dependencies || {},
|
|
107
|
+
options,
|
|
108
|
+
hosts,
|
|
109
|
+
artifacts: [],
|
|
110
|
+
addons: addons.map((url) => language.reference(layout.manifest, url))
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
for (const file of files) {
|
|
115
|
+
await fs.writeFile(file.url, file.source)
|
|
116
|
+
yield { url: file.url }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function collectAssets(entries, traversal, hooks) {
|
|
121
|
+
const collected = new Map()
|
|
122
|
+
|
|
123
|
+
for (const entry of entries) {
|
|
124
|
+
const bundle = await pack(
|
|
125
|
+
entry,
|
|
126
|
+
{ ...traversal, linked: true },
|
|
127
|
+
hooks.readModule,
|
|
128
|
+
hooks.listPrefix,
|
|
129
|
+
hooks.resolveModule
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
for (const key of bundle.assets) {
|
|
133
|
+
const url = new URL(key)
|
|
134
|
+
const relative = await assets.path(url, hooks.readModule)
|
|
135
|
+
const source = bundle.read(key)
|
|
136
|
+
const existing = collected.get(relative)
|
|
137
|
+
|
|
138
|
+
if (existing && !existing.source.equals(source)) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Assets '${existing.url.href}' and '${url.href}' both link as '${relative}'`
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
collected.set(relative, { path: relative, url, source })
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return [...collected.values()]
|
|
149
|
+
}
|
package/lib/provider.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
exports.registry = function registry(kind, providers, validate) {
|
|
2
|
+
return function provider(name, resolve) {
|
|
3
|
+
if (typeof name !== 'string') return validate(name)
|
|
4
|
+
|
|
5
|
+
const load = providers[name]
|
|
6
|
+
|
|
7
|
+
if (load) return load()
|
|
8
|
+
|
|
9
|
+
if (resolve) return validate(resolve(name))
|
|
10
|
+
|
|
11
|
+
throw new Error(`Unknown ${kind} '${name}'`)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
exports.validateLanguages = function validateLanguages(kind, languages) {
|
|
16
|
+
if (typeof languages !== 'object' || languages === null) {
|
|
17
|
+
throw new Error(`${kind} must provide a languages object`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for (const [name, entry] of Object.entries(languages)) {
|
|
21
|
+
if (!entry || typeof entry.generate !== 'function') {
|
|
22
|
+
throw new Error(`${kind} must provide a generate() function for language '${name}'`)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
exports.select = function select(kind, provider, language) {
|
|
28
|
+
if (!Object.hasOwn(provider.languages, language)) {
|
|
29
|
+
throw new Error(`${kind} '${provider.name}' does not support language '${language}'`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return provider.languages[language]
|
|
33
|
+
}
|