crossbind 2.0.0-beta.64 → 2.0.0-beta.65
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/package.json +1 -1
- package/src/actions/androidDevice.js +77 -0
- package/src/actions/androidGradle.js +121 -0
- package/src/actions/buildApp.js +434 -0
- package/src/actions/buildDependencies.js +4 -3
- package/src/actions/buildExternal.js +3 -3
- package/src/actions/buildLib.js +1 -1
- package/src/actions/buildNode.js +15 -10
- package/src/actions/createInterface.js +1 -2
- package/src/actions/createLib.js +4 -1
- package/src/actions/getCmakeParameters.js +2 -1
- package/src/actions/getDependLibs.js +8 -2
- package/src/actions/getLinkInputs.js +10 -7
- package/src/actions/gradleProject.js +110 -0
- package/src/actions/iosSimulator.js +63 -0
- package/src/actions/licenses.js +160 -14
- package/src/actions/nativeBridges.js +35 -0
- package/src/actions/run.js +3 -3
- package/src/actions/target.js +2 -1
- package/src/actions/xcodeProject.js +152 -0
- package/src/assets/js-runtime/adapters/worker-comlink.js +8 -1
- package/src/assets/toolchain-digests.json +24 -18
- package/src/bin.js +75 -32
- package/src/index.js +6 -0
- package/src/integration/getCrossbindScript.js +15 -1
- package/src/state/index.js +5 -1
- package/src/state/loadConfig.js +2 -0
- package/src/utils/appRustCrates.js +3 -1
- package/src/utils/downloadAndExtractFile.js +42 -14
- package/src/utils/embindRsFingerprint.js +10 -3
- package/src/utils/hermes.js +40 -0
- package/src/utils/plist.js +42 -0
- package/src/utils/pullDockerImage.js +9 -3
- package/src/utils/replaceBasePathForDocker.js +8 -0
- package/src/utils/resolveAppHost.js +21 -0
- package/src/utils/selectRunner.js +1 -1
- package/src/utils/systemKeys.js +4 -0
- package/src/utils/targets.js +19 -3
- package/vitest.config.js +5 -0
|
@@ -14,19 +14,45 @@ const RETRYABLE_STATUS = new Set([408, 429]);
|
|
|
14
14
|
const sleep = (ms) => new Promise((resolve) => { setTimeout(resolve, ms); });
|
|
15
15
|
const retryable = (error) => Object.assign(error, { retryable: true });
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// A recipe names its source with getURL and may name mirrors that serve the same archive under the same pin.
|
|
18
|
+
export function sourcesOf({ getURL, getMirrors }, version) {
|
|
19
|
+
return [getURL(version), ...(getMirrors?.(version) ?? [])];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// `urls` is one URL or a list of them: the first names the archive, the others are mirrors of it.
|
|
23
|
+
export default async function downloadAndExtractFile(urls, output, sha256, options = {}) {
|
|
24
|
+
const sources = [urls].flat();
|
|
25
|
+
const archive = path.basename(sources[0]);
|
|
19
26
|
if (fs.existsSync(`${output}/source`) && sourceCameFrom(output, archive)) {
|
|
20
27
|
return false;
|
|
21
28
|
}
|
|
29
|
+
sources.forEach((url) => assertHttps(url));
|
|
22
30
|
fs.rmSync(`${output}/source`, { recursive: true, force: true });
|
|
23
|
-
const filePath = await
|
|
24
|
-
verifyIntegrity(filePath, url, sha256);
|
|
31
|
+
const { filePath, url } = await downloadFromAny(sources, output, sha256, { ...options, name: archive });
|
|
25
32
|
extractArchive(filePath, url, output);
|
|
26
33
|
fs.writeFileSync(`${output}/source.archive`, archive);
|
|
27
34
|
return true;
|
|
28
35
|
}
|
|
29
36
|
|
|
37
|
+
// A source that cannot serve the archive, or serves bytes the pin does not match, hands over to the next one.
|
|
38
|
+
async function downloadFromAny(sources, output, sha256, options) {
|
|
39
|
+
const failures = [];
|
|
40
|
+
for (const url of sources) {
|
|
41
|
+
try {
|
|
42
|
+
const filePath = await downloadFile(url, output, options);
|
|
43
|
+
verifyIntegrity(filePath, url, sha256);
|
|
44
|
+
return { filePath, url };
|
|
45
|
+
} catch (err) {
|
|
46
|
+
failures.push(err);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (failures.length === 1) throw failures[0];
|
|
50
|
+
throw new Error(
|
|
51
|
+
`crossbind: no source of ${options.name} could be used:\n${failures.map((err) => ` - ${err.message}`).join('\n')}`,
|
|
52
|
+
{ cause: failures.at(-1) },
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
30
56
|
// A cached source tree only counts when it came from the archive the recipe names now, or a
|
|
31
57
|
// nativeVersion bump would keep compiling the old tree. Trees from before the marker are judged
|
|
32
58
|
// by the archive that was downloaded next to them.
|
|
@@ -37,20 +63,22 @@ function sourceCameFrom(output, archive) {
|
|
|
37
63
|
}
|
|
38
64
|
|
|
39
65
|
// Extraction runs through the system tar (GNU tar and bsdtar both detect gzip/bzip2/xz, and
|
|
40
|
-
// every platform the engine builds on ships one)
|
|
41
|
-
//
|
|
42
|
-
// recipes address as build/source - one
|
|
66
|
+
// every platform the engine builds on ships one), and a zip archive, which GNU tar cannot read,
|
|
67
|
+
// through unzip. Extracting into a scratch directory keeps the archive from choosing where its
|
|
68
|
+
// files land, and gives the upstream root folder - which recipes address as build/source - one
|
|
69
|
+
// place to be renamed from.
|
|
43
70
|
function extractArchive(filePath, url, output) {
|
|
44
71
|
const work = `${output}/.extract`;
|
|
45
72
|
fs.rmSync(work, { recursive: true, force: true });
|
|
46
73
|
mkdirSync(work, { recursive: true });
|
|
47
74
|
try {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
75
|
+
const [tool, args] = /\.(zip|aar)$/.test(filePath) ? ['unzip', ['-q', filePath, '-d', work]] : ['tar', ['-xf', filePath, '-C', work]];
|
|
76
|
+
const extracted = spawnSync(tool, args, { encoding: 'utf8' });
|
|
77
|
+
if (extracted.error) {
|
|
78
|
+
throw new Error(`crossbind: cannot run ${tool} to extract ${filePath}: ${extracted.error.message}`);
|
|
51
79
|
}
|
|
52
|
-
if (
|
|
53
|
-
throw new Error(`crossbind: extracting ${filePath} (from ${url}) failed: ${(
|
|
80
|
+
if (extracted.status !== 0) {
|
|
81
|
+
throw new Error(`crossbind: extracting ${filePath} (from ${url}) failed: ${(extracted.stderr || '').trim() || `${tool} exited ${extracted.status}`}`);
|
|
54
82
|
}
|
|
55
83
|
const entries = fs.readdirSync(work, { withFileTypes: true });
|
|
56
84
|
if (entries.length === 0) {
|
|
@@ -95,9 +123,9 @@ export function assertHttps(target, context = target) {
|
|
|
95
123
|
|
|
96
124
|
// fetch follows redirects itself, which release downloads rely on (github and the osgeo
|
|
97
125
|
// mirrors both bounce), so the download needs no redirect library of its own.
|
|
98
|
-
export async function downloadFile(url, folder, { retryDelaysMs = RETRY_DELAYS_MS } = {}) {
|
|
126
|
+
export async function downloadFile(url, folder, { retryDelaysMs = RETRY_DELAYS_MS, name = path.basename(url) } = {}) {
|
|
99
127
|
mkdirSync(folder, { recursive: true });
|
|
100
|
-
const dest = `${folder}/${
|
|
128
|
+
const dest = `${folder}/${name}`;
|
|
101
129
|
if (fs.existsSync(dest)) return dest;
|
|
102
130
|
|
|
103
131
|
assertHttps(url);
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { getContentHash, getFileHash } from './hash.js';
|
|
4
5
|
import { collectRustSources } from './inputStamp.js';
|
|
5
6
|
import resolveEmbindRustRoot from './resolveEmbindRust.js';
|
|
7
|
+
import { HOST_BUILT_PLATFORMS } from './targets.js';
|
|
8
|
+
|
|
9
|
+
const { toolchains } = createRequire(import.meta.url)('../assets/toolchain-digests.json');
|
|
6
10
|
|
|
7
11
|
// Every cargo staticlib bundles the embind-rs it was built from. An archive from another one links stale glue, and next
|
|
8
12
|
// to an archive built after it every embind_rs symbol is defined twice, so a prebuilt records the one it came from.
|
|
9
13
|
export const EMBIND_RS_FINGERPRINT_FILE = 'crossbind-embind-rs.fingerprint';
|
|
10
14
|
|
|
11
|
-
// Paths count relative to the crate, so an archive built from the same embind-rs elsewhere matches.
|
|
12
|
-
|
|
15
|
+
// Paths count relative to the crate, so an archive built from the same embind-rs elsewhere matches. An archive an image
|
|
16
|
+
// built carries that image's Rust std too, whose second copy from another rustc lld refuses, so the image's rustc
|
|
17
|
+
// counts as well; Apple's linker takes the mix, and the host's own rustc builds those.
|
|
18
|
+
export function getEmbindRsFingerprint(target) {
|
|
13
19
|
const crate = `${resolveEmbindRustRoot()}/crate`;
|
|
14
20
|
const files = [...collectRustSources([crate]), `${crate}/Cargo.toml`];
|
|
15
|
-
|
|
21
|
+
const embindRs = getContentHash(files.map((file) => `${path.relative(crate, file)}:${getFileHash(file)}`).sort().join('\n'));
|
|
22
|
+
return !target || HOST_BUILT_PLATFORMS.includes(target.platform) ? embindRs : getContentHash(`${embindRs}\nrustc ${toolchains.rust}`);
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
export function isEmbindRsFingerprintStale(prebuiltDir, fingerprint) {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import withDirLock from './dirLock.js';
|
|
5
|
+
import downloadAndExtractFile from './downloadAndExtractFile.js';
|
|
6
|
+
|
|
7
|
+
// The Hermes React Native publishes on Maven Central for Apple platforms: the macOS framework, the iOS xcframework,
|
|
8
|
+
// hermesc and the headers. A new version also moves third_party/jsi of @crossbind/core-app-host to its tag, and the
|
|
9
|
+
// Hermes of crossbind's android-app, linux and windows images.
|
|
10
|
+
export const HERMES = {
|
|
11
|
+
version: '250829098.0.17',
|
|
12
|
+
sha256: '2967ee51b41bd433b3027dda875e96194354e2b4d715f9f186fd0c04ccef309d',
|
|
13
|
+
license: 'MIT',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const hermesUrl = (version) => `https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-ios-release.tar.gz`;
|
|
17
|
+
|
|
18
|
+
export const hermesDir = () => path.join(os.homedir(), '.crossbind', 'hermes', HERMES.version);
|
|
19
|
+
|
|
20
|
+
// The release's destroot, downloaded once and checked against the pinned sha256.
|
|
21
|
+
export default async function resolveHermes() {
|
|
22
|
+
const dir = hermesDir();
|
|
23
|
+
fs.mkdirSync(path.dirname(dir), { recursive: true });
|
|
24
|
+
await withDirLock(`${dir}.lock`, () => downloadAndExtractFile(hermesUrl(HERMES.version), dir, HERMES.sha256));
|
|
25
|
+
return `${dir}/source/destroot`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Linux and Windows have no published Hermes, so crossbind's linux and windows images build the release's source, with
|
|
29
|
+
// their static libc++: JSI hands std types across, and the host and the project's archives build there too.
|
|
30
|
+
export const HERMES_SOURCE = {
|
|
31
|
+
url: `https://github.com/facebook/hermes/archive/refs/tags/hermes-v${HERMES.version}.tar.gz`,
|
|
32
|
+
sha256: '37e91915c67ce0c457f69b2de00fcaf28fcf755ab7de6e4f33f1d6ee9efcffed',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Android's Hermes is React Native's AAR, which an app's Gradle build takes for the libraries it packages, and which
|
|
36
|
+
// crossbind's android-app image holds, with its headers and hermesc, for the app's host.
|
|
37
|
+
export const HERMES_ANDROID = {
|
|
38
|
+
url: `https://repo1.maven.org/maven2/com/facebook/hermes/hermes-android/${HERMES.version}/hermes-android-${HERMES.version}-release.aar`,
|
|
39
|
+
sha256: '6fb440b29aadb5925bed1109338da61623c54c3508020bb2cecebcaaa80e53b8',
|
|
40
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Property lists as Apple's tools read them: XML for an Info.plist, the older text form for an Xcode project.
|
|
2
|
+
|
|
3
|
+
const XML_INDENT = ' ';
|
|
4
|
+
const escapeXml = (text) => String(text).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
5
|
+
|
|
6
|
+
function xmlLines(value, indent) {
|
|
7
|
+
if (typeof value === 'boolean') return [`${indent}<${value}/>`];
|
|
8
|
+
const inner = indent + XML_INDENT;
|
|
9
|
+
if (Array.isArray(value)) return [`${indent}<array>`, ...value.flatMap((item) => xmlLines(item, inner)), `${indent}</array>`];
|
|
10
|
+
if (typeof value === 'object') {
|
|
11
|
+
const entries = Object.entries(value);
|
|
12
|
+
if (entries.length === 0) return [`${indent}<dict/>`];
|
|
13
|
+
return [
|
|
14
|
+
`${indent}<dict>`,
|
|
15
|
+
...entries.flatMap(([key, item]) => [`${inner}<key>${escapeXml(key)}</key>`, ...xmlLines(item, inner)]),
|
|
16
|
+
`${indent}</dict>`,
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
return [`${indent}<string>${escapeXml(value)}</string>`];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function xmlPlist(dict) {
|
|
23
|
+
return '<?xml version="1.0" encoding="UTF-8"?>\n'
|
|
24
|
+
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n'
|
|
25
|
+
+ `<plist version="1.0">\n${xmlLines(dict, '').join('\n')}\n</plist>\n`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Every value of the text form is a string, quoted here whatever it holds.
|
|
29
|
+
const quote = (text) => `"${String(text).replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
30
|
+
|
|
31
|
+
function textValue(value, indent) {
|
|
32
|
+
const inner = `${indent}\t`;
|
|
33
|
+
if (Array.isArray(value)) return `(\n${value.map((item) => `${inner}${textValue(item, inner)},\n`).join('')}${indent})`;
|
|
34
|
+
if (typeof value === 'object') {
|
|
35
|
+
return `{\n${Object.entries(value).map(([key, item]) => `${inner}${quote(key)} = ${textValue(item, inner)};\n`).join('')}${indent}}`;
|
|
36
|
+
}
|
|
37
|
+
return quote(value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function textPlist(root) {
|
|
41
|
+
return `// !$*UTF8*$!\n${textValue(root, '')}\n`;
|
|
42
|
+
}
|
|
@@ -8,7 +8,11 @@ const table = createRequire(import.meta.url)('../assets/toolchain-digests.json')
|
|
|
8
8
|
const REGISTRY = table.registry;
|
|
9
9
|
const IMAGE_VERSION = table.version;
|
|
10
10
|
const OVERRIDE_KEYS = {
|
|
11
|
-
web: 'CROSSBIND_IMAGE_WEB',
|
|
11
|
+
web: 'CROSSBIND_IMAGE_WEB',
|
|
12
|
+
android: 'CROSSBIND_IMAGE_ANDROID',
|
|
13
|
+
'android-app': 'CROSSBIND_IMAGE_ANDROID_APP',
|
|
14
|
+
linux: 'CROSSBIND_IMAGE_LINUX',
|
|
15
|
+
windows: 'CROSSBIND_IMAGE_WINDOWS',
|
|
12
16
|
};
|
|
13
17
|
const IMAGES = Object.fromEntries(
|
|
14
18
|
Object.keys(OVERRIDE_KEYS).map((role) => [
|
|
@@ -21,9 +25,11 @@ const IMAGES = Object.fromEntries(
|
|
|
21
25
|
);
|
|
22
26
|
|
|
23
27
|
// Which image a target compiles in: wasm and wasi share the web image, linux and linuxmusl the linux
|
|
24
|
-
// image, ios and darwin never reach docker.
|
|
28
|
+
// image, ios and darwin never reach docker. An Android app builds in android-app, the android image with
|
|
29
|
+
// what packages the app.
|
|
25
30
|
export function imageRoleFor(target) {
|
|
26
|
-
if (target?.platform === 'android'
|
|
31
|
+
if (target?.platform === 'android') return target.runtimeEnv === 'app' ? 'android-app' : 'android';
|
|
32
|
+
if (target?.platform === 'linux') return 'linux';
|
|
27
33
|
if (target?.platform === 'linuxmusl') return 'linux';
|
|
28
34
|
if (target?.platform === 'win32') return 'windows';
|
|
29
35
|
return 'web';
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
1
3
|
export const DOCKER_BASE = '/tmp/crossbind/live';
|
|
2
4
|
|
|
5
|
+
// Whether a container, which sees the project's base alone, sees dir.
|
|
6
|
+
export function isUnderBase(dir, basePath) {
|
|
7
|
+
const relative = path.relative(basePath, path.resolve(dir));
|
|
8
|
+
return !/^\.\.(?:[\\/]|$)/.test(relative) && !path.isAbsolute(relative);
|
|
9
|
+
}
|
|
10
|
+
|
|
3
11
|
export default function replaceBasePathForDocker(data, basePath) {
|
|
4
12
|
if (typeof data === 'string' || data instanceof String) {
|
|
5
13
|
return data.replaceAll(basePath, DOCKER_BASE);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import createCompanionResolver from './resolveCompanionPackage.js';
|
|
5
|
+
|
|
6
|
+
const PKG = '@crossbind/core-app-host';
|
|
7
|
+
|
|
8
|
+
const resolveAppHostRoot = createCompanionResolver({
|
|
9
|
+
pkg: PKG,
|
|
10
|
+
siblingDir: 'app-host',
|
|
11
|
+
siblingMarker: 'cpp/src/main.cpp',
|
|
12
|
+
missingMessage: `crossbind: apps need ${PKG} - add it to your devDependencies.`,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export default resolveAppHostRoot;
|
|
16
|
+
|
|
17
|
+
// The embind runtime ships in @crossbind/core-embind-jsi, a dependency of the app host package.
|
|
18
|
+
export function resolveAppEmbindJsiRoot() {
|
|
19
|
+
const req = createRequire(path.join(resolveAppHostRoot(), 'package.json'));
|
|
20
|
+
return path.dirname(fs.realpathSync(req.resolve('@crossbind/core-embind-jsi/package.json')));
|
|
21
|
+
}
|
|
@@ -7,7 +7,7 @@ const SYSTEM_CONFIG = '~/.crossbind.json';
|
|
|
7
7
|
const SHARED = { key: 'REMOTE_URL', url: 'CROSSBIND_REMOTE_URL', token: 'CROSSBIND_TOKEN' };
|
|
8
8
|
|
|
9
9
|
export const remoteVariables = (role) => {
|
|
10
|
-
const suffix = `_${role.toUpperCase()}`;
|
|
10
|
+
const suffix = `_${role.toUpperCase().replaceAll('-', '_')}`;
|
|
11
11
|
return { key: `${SHARED.key}${suffix}`, url: `${SHARED.url}${suffix}`, token: `${SHARED.token}${suffix}` };
|
|
12
12
|
};
|
|
13
13
|
|
package/src/utils/systemKeys.js
CHANGED
|
@@ -48,6 +48,10 @@ const systemKeys = {
|
|
|
48
48
|
description: 'Image reference used for android builds instead of the pinned one. A reference without the release digest disables the reproducibility guarantee. The CROSSBIND_IMAGE_ANDROID environment variable overrides it.',
|
|
49
49
|
default: '',
|
|
50
50
|
},
|
|
51
|
+
DOCKER_IMAGE_ANDROID_APP: {
|
|
52
|
+
description: 'Image reference used for Android apps instead of the pinned one. A reference without the release digest disables the reproducibility guarantee. The CROSSBIND_IMAGE_ANDROID_APP environment variable overrides it.',
|
|
53
|
+
default: '',
|
|
54
|
+
},
|
|
51
55
|
DOCKER_IMAGE_LINUX: {
|
|
52
56
|
description: 'Image reference used for linux (Node.js addon) builds instead of the pinned one. A reference without the release digest disables the reproducibility guarantee. The CROSSBIND_IMAGE_LINUX environment variable overrides it.',
|
|
53
57
|
default: '',
|
package/src/utils/targets.js
CHANGED
|
@@ -148,12 +148,17 @@ const BASE_TARGETS = [
|
|
|
148
148
|
},
|
|
149
149
|
];
|
|
150
150
|
|
|
151
|
-
// A native executable links the same archives as the Node-API addon of its platform and arch
|
|
151
|
+
// A native executable links the same archives as the Node-API addon of its platform and arch, and so does an app's
|
|
152
|
+
// host on macOS, glibc Linux and Windows. An iOS or Android app links the archives React Native does, one target per
|
|
153
|
+
// SDK or ABI.
|
|
152
154
|
export const TARGETS = [
|
|
153
155
|
...BASE_TARGETS,
|
|
154
156
|
...BASE_TARGETS
|
|
155
157
|
.filter((target) => target.runtimeEnv === 'node' && target.platform !== 'wasm')
|
|
156
158
|
.map((target) => ({ ...target, runtimeEnv: 'native' })),
|
|
159
|
+
...BASE_TARGETS
|
|
160
|
+
.filter((target) => (target.runtimeEnv === 'node' && ['darwin', 'linux', 'win32'].includes(target.platform)) || ['ios', 'android'].includes(target.platform))
|
|
161
|
+
.map((target) => ({ ...target, runtimeEnv: 'app' })),
|
|
157
162
|
];
|
|
158
163
|
|
|
159
164
|
// Built only when named with -p: a plain `crossbind build` keeps producing what it did before
|
|
@@ -163,8 +168,8 @@ export const OPT_IN_PLATFORMS = ['darwin', 'linux', 'linuxmusl', 'win32'];
|
|
|
163
168
|
// Built on the host itself: Apple's linker and SDKs exist in no toolchain image.
|
|
164
169
|
export const HOST_BUILT_PLATFORMS = ['ios', 'darwin'];
|
|
165
170
|
|
|
166
|
-
// The runtime environment names the binary a build makes; android and ios
|
|
167
|
-
//
|
|
171
|
+
// The runtime environment names the binary a build makes; on android and ios React Native links
|
|
172
|
+
// the archives, and the app is the one binary.
|
|
168
173
|
export const runtimeEnvsOf = (platform) => [
|
|
169
174
|
...new Set(TARGETS.filter((target) => target.platform === platform && target.runtimeEnv).map((target) => target.runtimeEnv)),
|
|
170
175
|
];
|
|
@@ -203,6 +208,17 @@ export const wasiNamesOf = (target, projectName) => ({
|
|
|
203
208
|
wasmName: `${projectName}-${targetPathOf(target)}${target.runtimeEnv === 'http' ? '.http' : ''}.wasm`,
|
|
204
209
|
});
|
|
205
210
|
|
|
211
|
+
// One app per platform, arch and build type, and one page runtime per build type for all of them. A macOS app is a
|
|
212
|
+
// bundle; a Linux app a directory; an iOS or Android one what its SDK or ABI brings to the app's Xcode or Gradle
|
|
213
|
+
// project.
|
|
214
|
+
export function appNamesOf(target, projectName) {
|
|
215
|
+
const suffix = target.buildType === 'debug' ? '.debug' : '';
|
|
216
|
+
return {
|
|
217
|
+
appName: `${projectName}.${target.platform}-${target.arch}${suffix}${target.platform === 'darwin' ? '.app' : ''}`,
|
|
218
|
+
appJsName: `${projectName}.app${suffix}.js`,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
206
222
|
// One executable per platform, arch and build type, named like the addons it replaces.
|
|
207
223
|
export function nativeCommandNamesOf(target, projectName) {
|
|
208
224
|
const suffix = target.buildType === 'debug' ? '.debug' : '';
|
package/vitest.config.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
1
2
|
import { defineConfig } from 'vitest/config';
|
|
2
3
|
|
|
3
4
|
export default defineConfig({
|
|
5
|
+
// The app host package's scripts import crossbind's runtime modules the way buildApp bundles them.
|
|
6
|
+
resolve: {
|
|
7
|
+
alias: [{ find: /^crossbind\/runtime\//, replacement: fileURLToPath(new URL('./src/assets/js-runtime/', import.meta.url)) }],
|
|
8
|
+
},
|
|
4
9
|
// loadJs/loadConfig dynamically import config files written to os.tmpdir()
|
|
5
10
|
// during tests. Vite 6 tightened server.fs.allow and rejects those out-of-root
|
|
6
11
|
// paths ("Does the file exist?"); relax the fs check for the test runner.
|