@expo-harmony/cli 55.0.26-harmony.7 → 55.0.26-harmony.9
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 +74 -46
- package/build/bin/expo-harmony.js +1 -1
- package/build/buildHap/build.d.ts +3 -10
- package/build/buildHap/build.js +11 -69
- package/build/buildHap/index.d.ts +2 -0
- package/build/buildHap/index.js +19 -0
- package/build/cli.d.ts +3 -3
- package/build/cli.js +21 -187
- package/build/command.d.ts +9 -0
- package/build/command.js +20 -0
- package/build/doctor/doctor.d.ts +1 -1
- package/build/doctor/doctor.js +64 -56
- package/build/doctor/index.d.ts +2 -0
- package/build/doctor/index.js +16 -0
- package/build/exportEmbed/export.d.ts +2 -2
- package/build/exportEmbed/export.js +53 -49
- package/build/exportEmbed/index.d.ts +2 -0
- package/build/exportEmbed/index.js +19 -0
- package/build/file.d.ts +2 -1
- package/build/file.js +18 -8
- package/build/help.d.ts +1 -0
- package/build/help.js +36 -0
- package/build/index.d.ts +5 -3
- package/build/index.js +6 -5
- package/build/internal/prebuild.d.ts +3 -0
- package/build/internal/prebuild.js +9 -0
- package/build/log.d.ts +4 -0
- package/build/log.js +6 -0
- package/build/modules/index.d.ts +2 -0
- package/build/modules/index.js +16 -0
- package/build/native/bare.d.ts +4 -0
- package/build/native/bare.js +130 -0
- package/build/native/build.d.ts +15 -0
- package/build/native/build.js +67 -0
- package/build/{run → native}/cache.d.ts +2 -2
- package/build/{run → native}/cache.js +8 -8
- package/build/native/hvigor.d.ts +2 -0
- package/build/native/hvigor.js +77 -0
- package/build/native/install.d.ts +5 -0
- package/build/native/install.js +13 -0
- package/build/native/prepareProject.d.ts +11 -0
- package/build/native/prepareProject.js +68 -0
- package/build/native/project.d.ts +8 -0
- package/build/native/project.js +58 -0
- package/build/native/toolchain.d.ts +16 -0
- package/build/{tools.js → native/toolchain.js} +1 -57
- package/build/native/types.d.ts +47 -0
- package/build/native/types.js +2 -0
- package/build/prebuild/check.d.ts +7 -3
- package/build/prebuild/check.js +26 -20
- package/build/prebuild/clean.js +5 -5
- package/build/prebuild/index.d.ts +2 -0
- package/build/prebuild/index.js +51 -0
- package/build/prebuild/prebuild.js +11 -3
- package/build/prebuild/template.js +2 -2
- package/build/process.d.ts +11 -1
- package/build/process.js +16 -0
- package/build/profile.d.ts +1 -0
- package/build/profile.js +12 -0
- package/build/projectLock.js +7 -2
- package/build/run/devices.d.ts +1 -1
- package/build/run/devices.js +15 -10
- package/build/run/emulators.d.ts +1 -1
- package/build/run/emulators.js +2 -5
- package/build/run/index.d.ts +2 -0
- package/build/run/index.js +25 -0
- package/build/run/metro.js +1 -1
- package/build/run/run.d.ts +4 -10
- package/build/run/run.js +45 -90
- package/build/start/index.d.ts +2 -0
- package/build/start/index.js +33 -0
- package/package.json +14 -4
- package/src/bin/expo-harmony.ts +2 -2
- package/src/buildHap/build.ts +13 -105
- package/src/buildHap/index.ts +19 -0
- package/src/cli.ts +24 -227
- package/src/command.ts +24 -0
- package/src/doctor/doctor.ts +63 -61
- package/src/doctor/index.ts +16 -0
- package/src/exportEmbed/export.ts +64 -55
- package/src/exportEmbed/index.ts +17 -0
- package/src/file.ts +20 -11
- package/src/help.ts +33 -0
- package/src/index.ts +5 -3
- package/src/internal/prebuild.ts +3 -0
- package/src/log.ts +5 -0
- package/src/modules/index.ts +16 -0
- package/src/native/bare.ts +145 -0
- package/src/native/build.ts +81 -0
- package/src/{run → native}/cache.ts +10 -9
- package/src/native/hvigor.ts +81 -0
- package/src/native/install.ts +17 -0
- package/src/native/prepareProject.ts +99 -0
- package/src/native/project.ts +88 -0
- package/src/{tools.ts → native/toolchain.ts} +11 -118
- package/src/native/types.ts +44 -0
- package/src/prebuild/check.ts +30 -19
- package/src/prebuild/clean.ts +3 -3
- package/src/prebuild/index.ts +56 -0
- package/src/prebuild/prebuild.ts +11 -1
- package/src/prebuild/template.ts +2 -2
- package/src/process.ts +38 -1
- package/src/profile.ts +13 -0
- package/src/projectLock.ts +9 -1
- package/src/run/devices.ts +16 -11
- package/src/run/emulators.ts +4 -6
- package/src/run/index.ts +25 -0
- package/src/run/metro.ts +1 -1
- package/src/run/run.ts +56 -129
- package/src/start/index.ts +31 -0
- package/build/buildHap/common.d.ts +0 -24
- package/build/buildHap/common.js +0 -92
- package/build/run/install.d.ts +0 -7
- package/build/run/install.js +0 -26
- package/build/tools.d.ts +0 -39
- package/src/buildHap/common.ts +0 -145
- package/src/run/install.ts +0 -41
|
@@ -4,16 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createHarmonyToolchainEnv = createHarmonyToolchainEnv;
|
|
7
|
-
exports.resolveHarmonyBuildPlanAsync = resolveHarmonyBuildPlanAsync;
|
|
8
|
-
exports.resolveHarmonyBuildPlanIfPresentAsync = resolveHarmonyBuildPlanIfPresentAsync;
|
|
9
7
|
exports.resolveHarmonyEmulator = resolveHarmonyEmulator;
|
|
10
8
|
exports.resolveHarmonyToolchain = resolveHarmonyToolchain;
|
|
11
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
11
|
const tool_command_1 = require("@expo-harmony/expo-modules-autolinking/tool-command");
|
|
14
|
-
const build_descriptor_1 = require("@expo-harmony/prebuild-config/build-descriptor");
|
|
15
|
-
const check_1 = require("@expo-harmony/prebuild-config/check");
|
|
16
|
-
const errors_1 = require("./errors");
|
|
17
12
|
function existingFile(candidates) {
|
|
18
13
|
return candidates.find(candidate => node_fs_1.default.existsSync(candidate)) || null;
|
|
19
14
|
}
|
|
@@ -93,9 +88,7 @@ function devEcoNode(layout, platform, env) {
|
|
|
93
88
|
function resolveHarmonyToolchain() {
|
|
94
89
|
const env = process.env;
|
|
95
90
|
const platform = process.platform;
|
|
96
|
-
// Hvigor
|
|
97
|
-
// Reject metadata-only candidates so doctor cannot claim an SDK is
|
|
98
|
-
// buildable without its HMS, OpenHarmony, native, ETS, and toolchain parts.
|
|
91
|
+
// Hvigor needs the complete SDK root containing default/, not default/ itself.
|
|
99
92
|
const sdkHome = resolveHarmonySdkRoot(env, platform);
|
|
100
93
|
const layouts = sdkHome ? devEcoLayouts(sdkHome) : [];
|
|
101
94
|
let ohpm;
|
|
@@ -177,52 +170,3 @@ function resolveHarmonyEmulator(toolchain) {
|
|
|
177
170
|
? { args: [], command, source: 'deveco' }
|
|
178
171
|
: { args: [], command: executable, source: 'path' };
|
|
179
172
|
}
|
|
180
|
-
function createHarmonyBuildPlan(projectRoot, build, mode) {
|
|
181
|
-
const resolve = relative => (0, build_descriptor_1.resolveHarmonyBuildPath)(projectRoot, relative);
|
|
182
|
-
const variant = build.variants[mode];
|
|
183
|
-
return {
|
|
184
|
-
abilityName: build.identity.abilityName,
|
|
185
|
-
buildMode: mode,
|
|
186
|
-
bundleName: build.identity.bundleName,
|
|
187
|
-
expectedHap: resolve(variant.expectedHap),
|
|
188
|
-
exportPaths: Object.fromEntries(Object.entries(build.export).map(([name, relative]) => [name, resolve(relative)])),
|
|
189
|
-
harmonyRoot: resolve(build.harmonyRoot),
|
|
190
|
-
hvigorArgs: [...variant.hvigorArgs],
|
|
191
|
-
moduleName: build.identity.moduleName,
|
|
192
|
-
moduleRoot: resolve(build.moduleRoot),
|
|
193
|
-
nativeCache: {
|
|
194
|
-
invalidationRoots: build.nativeCache.invalidationRoots.map(resolve),
|
|
195
|
-
stateFile: resolve(build.nativeCache.stateFile),
|
|
196
|
-
},
|
|
197
|
-
nativeInputs: {
|
|
198
|
-
lockfile: resolve(build.nativeInputs.lockfile),
|
|
199
|
-
manifest: resolve(build.nativeInputs.manifest),
|
|
200
|
-
},
|
|
201
|
-
productName: build.identity.productName,
|
|
202
|
-
projectFiles: Object.fromEntries(Object.entries(build.projectFiles).map(([name, relative]) => [name, resolve(relative)])),
|
|
203
|
-
targetName: build.identity.targetName,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
async function resolveHarmonyBuildPlanIfPresentAsync(projectRoot, options = {}) {
|
|
207
|
-
const mode = options.buildMode || 'debug';
|
|
208
|
-
if (!['debug', 'release'].includes(mode)) {
|
|
209
|
-
throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Harmony buildMode must be debug or release, received: ${mode}`, { operation: 'resolve-build' });
|
|
210
|
-
}
|
|
211
|
-
let manifest;
|
|
212
|
-
try {
|
|
213
|
-
manifest = await (0, check_1.readManifestIfPresentAsync)(projectRoot);
|
|
214
|
-
}
|
|
215
|
-
catch (cause) {
|
|
216
|
-
throw new errors_1.HarmonyCliError(cause.code || 'ERR_HARMONY_TEMPLATE_INVALID', `Cannot read the generated Harmony build descriptor: ${cause.message}`, { cause, operation: 'resolve-build' });
|
|
217
|
-
}
|
|
218
|
-
if (!manifest)
|
|
219
|
-
return null;
|
|
220
|
-
return createHarmonyBuildPlan(projectRoot, manifest.build, mode);
|
|
221
|
-
}
|
|
222
|
-
async function resolveHarmonyBuildPlanAsync(projectRoot, options = {}) {
|
|
223
|
-
const plan = await resolveHarmonyBuildPlanIfPresentAsync(projectRoot, options);
|
|
224
|
-
if (!plan) {
|
|
225
|
-
throw new errors_1.HarmonyCliError('ERR_HARMONY_MANIFEST_DRIFT', 'Cannot read the generated Harmony build descriptor because the CNG manifest is missing.', { operation: 'resolve-build' });
|
|
226
|
-
}
|
|
227
|
-
return plan;
|
|
228
|
-
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type HarmonyBuildMode = 'debug' | 'release';
|
|
2
|
+
interface NativeBuildPlan {
|
|
3
|
+
abilityName: string;
|
|
4
|
+
buildMode: HarmonyBuildMode;
|
|
5
|
+
bundleName: string;
|
|
6
|
+
expectedHap: string;
|
|
7
|
+
exportPaths: {
|
|
8
|
+
bundle: string;
|
|
9
|
+
manifest: string;
|
|
10
|
+
metadataRoot: string;
|
|
11
|
+
rawfileRoot: string;
|
|
12
|
+
sourceMap: string;
|
|
13
|
+
};
|
|
14
|
+
harmonyRoot: string;
|
|
15
|
+
hvigorArgs: string[];
|
|
16
|
+
moduleName: string;
|
|
17
|
+
moduleRoot: string;
|
|
18
|
+
nativeCache: {
|
|
19
|
+
invalidationRoots: string[];
|
|
20
|
+
stateFile: string;
|
|
21
|
+
};
|
|
22
|
+
nativeInputs: {
|
|
23
|
+
lockfile: string;
|
|
24
|
+
manifest: string;
|
|
25
|
+
};
|
|
26
|
+
projectFiles: {
|
|
27
|
+
hvigorConfig: string;
|
|
28
|
+
moduleHvigor: string;
|
|
29
|
+
moduleJson: string;
|
|
30
|
+
projectBuildProfile: string;
|
|
31
|
+
rootHvigor: string;
|
|
32
|
+
};
|
|
33
|
+
productName: string;
|
|
34
|
+
targetName: string;
|
|
35
|
+
}
|
|
36
|
+
export interface BareHarmonyBuildPlan extends NativeBuildPlan {
|
|
37
|
+
workflow: 'bare';
|
|
38
|
+
}
|
|
39
|
+
export interface CngHarmonyBuildPlan extends NativeBuildPlan {
|
|
40
|
+
workflow: 'cng';
|
|
41
|
+
projectFiles: NativeBuildPlan['projectFiles'] & {
|
|
42
|
+
nativeInputsStamp: string;
|
|
43
|
+
templateMarker: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export type HarmonyBuildPlan = BareHarmonyBuildPlan | CngHarmonyBuildPlan;
|
|
47
|
+
export {};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
export type { Change as CheckChange } from '@expo-harmony/prebuild-config/
|
|
1
|
+
export type { Change as CheckChange } from '@expo-harmony/prebuild-config/internal';
|
|
2
2
|
interface CheckOptions {
|
|
3
3
|
buildType?: 'debug' | 'release';
|
|
4
4
|
}
|
|
5
|
-
declare function
|
|
6
|
-
|
|
5
|
+
declare function withGeneratedProjectAsync<T>(project: string, options: CheckOptions & {
|
|
6
|
+
clean?: boolean;
|
|
7
|
+
skipPatches?: boolean;
|
|
8
|
+
}, action: (expected: string, checksum: string) => Promise<T>): Promise<T>;
|
|
9
|
+
declare function checkAsync(project: any, options?: CheckOptions): Promise<import("@expo-harmony/prebuild-config/internal").Result>;
|
|
10
|
+
export { checkAsync, withGeneratedProjectAsync };
|
package/build/prebuild/check.js
CHANGED
|
@@ -4,19 +4,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.checkAsync = checkAsync;
|
|
7
|
+
exports.withGeneratedProjectAsync = withGeneratedProjectAsync;
|
|
8
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
10
|
const node_os_1 = __importDefault(require("node:os"));
|
|
9
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
-
const
|
|
12
|
+
const internal_1 = require("@expo-harmony/prebuild-config/internal");
|
|
11
13
|
const errors_1 = require("../errors");
|
|
12
14
|
const path_1 = require("../path");
|
|
13
15
|
const process_1 = require("../process");
|
|
14
16
|
const projectLock_1 = require("../projectLock");
|
|
15
|
-
const
|
|
17
|
+
const toolchain_1 = require("../native/toolchain");
|
|
18
|
+
const project_1 = require("../native/project");
|
|
16
19
|
const expo_1 = require("../expo");
|
|
17
20
|
const template_1 = require("./template");
|
|
18
|
-
const
|
|
19
|
-
const
|
|
21
|
+
const IgnoredDirectories = new Set(['.expo', '.git', '.hvigor', '.yarn', 'node_modules']);
|
|
22
|
+
const GeneratedDirectories = new Set([
|
|
20
23
|
'.cxx',
|
|
21
24
|
'.git',
|
|
22
25
|
'.hvigor',
|
|
@@ -68,16 +71,18 @@ function shouldCopyPrebuildCheckPath(project, source, plan) {
|
|
|
68
71
|
if (!relative)
|
|
69
72
|
return true;
|
|
70
73
|
const segments = relative.split(node_path_1.default.sep);
|
|
71
|
-
if (
|
|
74
|
+
if (IgnoredDirectories.has(segments[0]))
|
|
72
75
|
return false;
|
|
73
|
-
const
|
|
76
|
+
const root = (0, path_1.isInside)(plan.harmonyRoot, source)
|
|
74
77
|
? plan.harmonyRoot
|
|
75
78
|
: isAppLocalHarmonyPath(project, source)
|
|
76
79
|
? node_path_1.default.join(project, ...segments.slice(0, 3))
|
|
77
80
|
: null;
|
|
78
|
-
if (
|
|
79
|
-
const native = node_path_1.default.relative(
|
|
80
|
-
if (
|
|
81
|
+
if (root) {
|
|
82
|
+
const native = node_path_1.default.relative(root, source).split(node_path_1.default.sep);
|
|
83
|
+
if (GeneratedDirectories.has(native[0])
|
|
84
|
+
|| (GeneratedDirectories.has(native[1])
|
|
85
|
+
&& node_fs_1.default.existsSync(node_path_1.default.join(root, native[0], 'src/main/module.json5'))))
|
|
81
86
|
return false;
|
|
82
87
|
}
|
|
83
88
|
return source !== plan.exportPaths.bundle;
|
|
@@ -85,6 +90,7 @@ function shouldCopyPrebuildCheckPath(project, source, plan) {
|
|
|
85
90
|
async function copyAsync(project, target, plan, temp = node_path_1.default.dirname(target)) {
|
|
86
91
|
await node_fs_1.default.promises.cp(project, target, {
|
|
87
92
|
recursive: true,
|
|
93
|
+
verbatimSymlinks: true,
|
|
88
94
|
filter: source => shouldCopyPrebuildCheckPath(project, source, plan),
|
|
89
95
|
});
|
|
90
96
|
const modules = node_path_1.default.join(project, 'node_modules');
|
|
@@ -93,22 +99,20 @@ async function copyAsync(project, target, plan, temp = node_path_1.default.dirna
|
|
|
93
99
|
operation: 'check',
|
|
94
100
|
});
|
|
95
101
|
}
|
|
96
|
-
//
|
|
97
|
-
// entries. Dependency scanners then retain the isolated lexical package path
|
|
98
|
-
// (including scoped packages) instead of collapsing the entire node_modules
|
|
99
|
-
// root to the source project's realpath. Package directories stay linked;
|
|
100
|
-
// Windows copies loose files to avoid requiring file-symlink privileges.
|
|
102
|
+
// Link individual packages so dependency scanners retain paths inside the mirror.
|
|
101
103
|
await linkModulesAsync(modules, node_path_1.default.join(target, 'node_modules'));
|
|
102
|
-
await (0,
|
|
104
|
+
await (0, internal_1.stageAsync)(project, target, temp);
|
|
103
105
|
}
|
|
104
|
-
async function
|
|
106
|
+
async function withGeneratedProjectAsync(project, options, action) {
|
|
105
107
|
project = node_path_1.default.resolve(project);
|
|
106
|
-
const plan = await (0,
|
|
108
|
+
const plan = await (0, project_1.resolveHarmonyBuildPlanAsync)(project);
|
|
107
109
|
const temp = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'expo-harmony-check-'));
|
|
108
110
|
const expected = mirrorRoot(temp, project);
|
|
109
111
|
let packed;
|
|
110
112
|
try {
|
|
111
113
|
await copyAsync(project, expected, plan, temp);
|
|
114
|
+
if (options.clean)
|
|
115
|
+
await node_fs_1.default.promises.rm(node_path_1.default.join(expected, 'harmony'), { recursive: true, force: true });
|
|
112
116
|
packed = await (0, template_1.packAsync)(project);
|
|
113
117
|
const expo = (0, expo_1.resolveExpoCli)(project);
|
|
114
118
|
const result = await (0, process_1.spawnAsync)(process.execPath, [
|
|
@@ -122,17 +126,19 @@ async function checkUnlockedAsync(project, options) {
|
|
|
122
126
|
capture: true,
|
|
123
127
|
cwd: expected,
|
|
124
128
|
env: {
|
|
125
|
-
...(0,
|
|
129
|
+
...(0, toolchain_1.createHarmonyToolchainEnv)(),
|
|
126
130
|
...packed.env,
|
|
127
131
|
...(options.buildType ? { EXPO_HARMONY_BUILD_TYPE: options.buildType } : {}),
|
|
128
132
|
EXPO_HARMONY_CHECK_MIRROR_ROOT: temp,
|
|
133
|
+
...(options.skipPatches ? { EXPO_HARMONY_SKIP_PATCHES: '1' } : {}),
|
|
129
134
|
},
|
|
130
135
|
operation: 'check-prebuild',
|
|
131
136
|
});
|
|
132
137
|
if (result.code !== 0) {
|
|
133
138
|
throw new errors_1.HarmonyCliError('ERR_HARMONY_MANIFEST_DRIFT', `Isolated Expo prebuild failed:\n${result.stderr || result.stdout}`, { exitCode: result.code, operation: 'check-prebuild' });
|
|
134
139
|
}
|
|
135
|
-
|
|
140
|
+
const checksum = (0, node_crypto_1.createHash)('md5').update(Uint8Array.from(await node_fs_1.default.promises.readFile(packed.tarball))).digest('hex');
|
|
141
|
+
return await action(expected, checksum);
|
|
136
142
|
}
|
|
137
143
|
finally {
|
|
138
144
|
if (packed)
|
|
@@ -141,5 +147,5 @@ async function checkUnlockedAsync(project, options) {
|
|
|
141
147
|
}
|
|
142
148
|
}
|
|
143
149
|
async function checkAsync(project, options = {}) {
|
|
144
|
-
return (0, projectLock_1.withHarmonyProjectLockAsync)(project, 'prebuild-check', () =>
|
|
150
|
+
return (0, projectLock_1.withHarmonyProjectLockAsync)(project, 'prebuild-check', () => withGeneratedProjectAsync(project, options, expected => (0, internal_1.compareAsync)(project, expected)));
|
|
145
151
|
}
|
package/build/prebuild/clean.js
CHANGED
|
@@ -6,14 +6,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.assertSafeCleanTarget = assertSafeCleanTarget;
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const
|
|
9
|
+
const internal_1 = require("@expo-harmony/prebuild-config/internal");
|
|
10
10
|
const errors_1 = require("../errors");
|
|
11
11
|
const path_1 = require("../path");
|
|
12
|
-
const
|
|
12
|
+
const project_1 = require("../native/project");
|
|
13
13
|
async function assertSafeCleanTarget(projectRoot) {
|
|
14
14
|
const root = await node_fs_1.default.promises.realpath(projectRoot);
|
|
15
|
-
const plan = await (0,
|
|
16
|
-
const target = plan?.harmonyRoot ?? node_path_1.default.join(root,
|
|
15
|
+
const plan = await (0, project_1.resolveHarmonyBuildPlanIfPresentAsync)(root);
|
|
16
|
+
const target = plan?.harmonyRoot ?? node_path_1.default.join(root, internal_1.HarmonyPlatformDirectory);
|
|
17
17
|
let stat;
|
|
18
18
|
try {
|
|
19
19
|
stat = await node_fs_1.default.promises.lstat(target);
|
|
@@ -23,7 +23,7 @@ async function assertSafeCleanTarget(projectRoot) {
|
|
|
23
23
|
return target;
|
|
24
24
|
throw new errors_1.HarmonyCliError(error.code || 'ERR_HARMONY_CLEAN_TARGET', error.message || `Cannot inspect the Harmony clean target: ${target}. Please delete it manually.`, { cause: error, exitCode: error.exitCode, operation: error.operation });
|
|
25
25
|
}
|
|
26
|
-
if (!plan) {
|
|
26
|
+
if (!plan || plan.workflow !== 'cng') {
|
|
27
27
|
throw new errors_1.HarmonyCliError('ERR_HARMONY_CLEAN_TARGET', `Refusing to clean ${target} because its CNG manifest is missing. Please delete it manually or run prebuild without --clean.`, { operation: 'clean' });
|
|
28
28
|
}
|
|
29
29
|
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.command = void 0;
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const errors_1 = require("../errors");
|
|
6
|
+
const projectLock_1 = require("../projectLock");
|
|
7
|
+
const options_1 = require("./options");
|
|
8
|
+
const command = async (argv, io) => {
|
|
9
|
+
const args = (0, command_1.parseCommandArgs)(args => (0, options_1.parsePrebuildArgs)(args, { allowProject: true }), argv, io);
|
|
10
|
+
if (!args)
|
|
11
|
+
return 0;
|
|
12
|
+
const { projectRoot: root, options } = args;
|
|
13
|
+
const { check, clean, passthrough } = options;
|
|
14
|
+
if (check && passthrough.length) {
|
|
15
|
+
throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--check cannot be combined with mutating prebuild options.');
|
|
16
|
+
}
|
|
17
|
+
return (0, projectLock_1.withHarmonyProjectLockAsync)(root, check ? 'prebuild-check' : 'prebuild', async () => {
|
|
18
|
+
if (clean) {
|
|
19
|
+
const { assertSafeCleanTarget } = await import('./clean.js');
|
|
20
|
+
await assertSafeCleanTarget(root);
|
|
21
|
+
}
|
|
22
|
+
const { doctorAsync, formatDoctor } = await import('../doctor/doctor.js');
|
|
23
|
+
const doctor = await doctorAsync(root, {
|
|
24
|
+
requireBuildTools: false,
|
|
25
|
+
validateGeneratedProject: !clean,
|
|
26
|
+
validateModules: false,
|
|
27
|
+
});
|
|
28
|
+
if (!doctor.ok) {
|
|
29
|
+
io.error(formatDoctor(doctor));
|
|
30
|
+
throw new errors_1.HarmonyCliError('ERR_HARMONY_DOCTOR_FAILED', 'Harmony doctor found blocking errors.', {
|
|
31
|
+
operation: 'doctor',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
for (const item of doctor.checks.filter(item => item.status === 'warn'))
|
|
35
|
+
io.warn(`! ${item.message}`);
|
|
36
|
+
if (check) {
|
|
37
|
+
const { checkAsync } = await import('./check.js');
|
|
38
|
+
const result = await checkAsync(root);
|
|
39
|
+
if (result.clean)
|
|
40
|
+
io.log('Harmony CNG output is up to date.');
|
|
41
|
+
else
|
|
42
|
+
for (const change of result.changes)
|
|
43
|
+
io.log(`${change.type}: ${change.path}`);
|
|
44
|
+
return result.clean ? 0 : 2;
|
|
45
|
+
}
|
|
46
|
+
const { prebuildParsedAsync } = await import('./prebuild.js');
|
|
47
|
+
await prebuildParsedAsync(root, passthrough);
|
|
48
|
+
return 0;
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
exports.command = command;
|
|
@@ -6,13 +6,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.prebuildParsedAsync = prebuildParsedAsync;
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const errors_1 = require("../errors");
|
|
9
|
+
const bare_1 = require("../native/bare");
|
|
9
10
|
const process_1 = require("../process");
|
|
10
11
|
const projectLock_1 = require("../projectLock");
|
|
11
12
|
const expo_1 = require("../expo");
|
|
12
|
-
const
|
|
13
|
+
const toolchain_1 = require("../native/toolchain");
|
|
14
|
+
const project_1 = require("../native/project");
|
|
13
15
|
const clean_1 = require("./clean");
|
|
14
16
|
const template_1 = require("./template");
|
|
15
17
|
async function prebuildParsedUnlockedAsync(projectRoot, passthrough, options = {}) {
|
|
18
|
+
if ((0, bare_1.isBareHarmonyProject)(projectRoot)) {
|
|
19
|
+
throw new errors_1.HarmonyCliError('ERR_HARMONY_BARE_PREBUILD', 'This Harmony project is manually maintained. Build it directly; prebuild cannot overwrite a bare project.', { operation: 'prebuild' });
|
|
20
|
+
}
|
|
16
21
|
if (passthrough.includes('--clean'))
|
|
17
22
|
await (0, clean_1.assertSafeCleanTarget)(projectRoot);
|
|
18
23
|
const expo = (0, expo_1.resolveExpoCli)(projectRoot);
|
|
@@ -28,7 +33,7 @@ async function prebuildParsedUnlockedAsync(projectRoot, passthrough, options = {
|
|
|
28
33
|
capture: Boolean(options.capture),
|
|
29
34
|
cwd: projectRoot,
|
|
30
35
|
env: {
|
|
31
|
-
...(0,
|
|
36
|
+
...(0, toolchain_1.createHarmonyToolchainEnv)(),
|
|
32
37
|
...packed.env,
|
|
33
38
|
...(options.buildType ? { EXPO_HARMONY_BUILD_TYPE: options.buildType } : {}),
|
|
34
39
|
},
|
|
@@ -43,9 +48,12 @@ async function prebuildParsedUnlockedAsync(projectRoot, passthrough, options = {
|
|
|
43
48
|
});
|
|
44
49
|
}
|
|
45
50
|
try {
|
|
46
|
-
const plan = await (0,
|
|
51
|
+
const plan = await (0, project_1.resolveHarmonyBuildPlanAsync)(projectRoot, {
|
|
47
52
|
buildMode: options.buildType,
|
|
48
53
|
});
|
|
54
|
+
if (plan.workflow !== 'cng') {
|
|
55
|
+
throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'Prebuild did not produce a CNG project.', { operation: 'prebuild' });
|
|
56
|
+
}
|
|
49
57
|
await node_fs_1.default.promises.access(plan.projectFiles.templateMarker, node_fs_1.default.constants.R_OK);
|
|
50
58
|
}
|
|
51
59
|
catch (cause) {
|
|
@@ -14,10 +14,10 @@ function resolve(project) {
|
|
|
14
14
|
const load = (0, node_module_1.createRequire)(node_path_1.default.join(project, 'package.json'));
|
|
15
15
|
let api;
|
|
16
16
|
try {
|
|
17
|
-
api = load('@expo-harmony/prebuild-config/template');
|
|
17
|
+
api = load('@expo-harmony/prebuild-config/internal/template');
|
|
18
18
|
}
|
|
19
19
|
catch (cause) {
|
|
20
|
-
throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'Cannot load @expo-harmony/prebuild-config/template from the project. Update the project-local prebuild config to a compatible version.', { cause, operation: 'resolve-template' });
|
|
20
|
+
throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'Cannot load @expo-harmony/prebuild-config/internal/template from the project. Update the project-local prebuild config to a compatible version.', { cause, operation: 'resolve-template' });
|
|
21
21
|
}
|
|
22
22
|
if (typeof api?.ROOT_ENV !== 'string' || typeof api.resolveBundled !== 'function') {
|
|
23
23
|
throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'The project-local @expo-harmony/prebuild-config template API is invalid.', { operation: 'resolve-template' });
|
package/build/process.d.ts
CHANGED
|
@@ -18,6 +18,15 @@ export interface ProcessResult {
|
|
|
18
18
|
stdout: string;
|
|
19
19
|
timedOut: boolean;
|
|
20
20
|
}
|
|
21
|
+
interface CheckedProcessOptions {
|
|
22
|
+
code: string;
|
|
23
|
+
cwd: string;
|
|
24
|
+
env?: NodeJS.ProcessEnv;
|
|
25
|
+
message: string;
|
|
26
|
+
operation: string;
|
|
27
|
+
outputLimit?: number;
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
}
|
|
21
30
|
declare function formatDiagnostics(result: Pick<ProcessResult, 'stderr' | 'stdout'>, limit?: number): string;
|
|
22
31
|
declare function spawnAsync(command: string, args: string[], options?: ProcessOptions): Promise<ProcessResult>;
|
|
23
32
|
declare function startManagedProcess(command: string, args: string[], options?: ProcessOptions): {
|
|
@@ -28,4 +37,5 @@ declare function startManagedProcess(command: string, args: string[], options?:
|
|
|
28
37
|
stop: (signal?: NodeJS.Signals, graceMs?: number) => Promise<ProcessResult>;
|
|
29
38
|
wasStopped: () => boolean;
|
|
30
39
|
};
|
|
31
|
-
|
|
40
|
+
declare function runCheckedAsync(command: string, args: string[], options: CheckedProcessOptions): Promise<ProcessResult>;
|
|
41
|
+
export { formatDiagnostics, runCheckedAsync, spawnAsync, startManagedProcess };
|
package/build/process.js
CHANGED
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.formatDiagnostics = formatDiagnostics;
|
|
7
|
+
exports.runCheckedAsync = runCheckedAsync;
|
|
7
8
|
exports.spawnAsync = spawnAsync;
|
|
8
9
|
exports.startManagedProcess = startManagedProcess;
|
|
9
10
|
const cross_spawn_1 = __importDefault(require("cross-spawn"));
|
|
@@ -306,3 +307,18 @@ function startManagedProcess(command, args, options = {}) {
|
|
|
306
307
|
wasStopped: () => stopped,
|
|
307
308
|
};
|
|
308
309
|
}
|
|
310
|
+
async function runCheckedAsync(command, args, options) {
|
|
311
|
+
const result = await spawnAsync(command, args, {
|
|
312
|
+
capture: true,
|
|
313
|
+
cwd: options.cwd,
|
|
314
|
+
env: options.env,
|
|
315
|
+
operation: options.operation,
|
|
316
|
+
outputLimit: options.outputLimit || 2 * 1024 * 1024,
|
|
317
|
+
timeoutMs: options.timeoutMs,
|
|
318
|
+
});
|
|
319
|
+
if (result.code !== 0 || result.timedOut) {
|
|
320
|
+
const diagnostics = formatDiagnostics(result);
|
|
321
|
+
throw new errors_1.HarmonyCliError(options.code, `${options.message} exited with code ${result.code}${result.timedOut ? ' after timing out' : ''}.${diagnostics ? `\n${diagnostics}` : ''}`, { exitCode: result.code || 1, operation: options.operation });
|
|
322
|
+
}
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function timedAsync<T>(steps: Record<string, number>, name: string, operation: () => Promise<T> | T): Promise<T>;
|
package/build/profile.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.timedAsync = timedAsync;
|
|
4
|
+
async function timedAsync(steps, name, operation) {
|
|
5
|
+
const started = performance.now();
|
|
6
|
+
try {
|
|
7
|
+
return await operation();
|
|
8
|
+
}
|
|
9
|
+
finally {
|
|
10
|
+
steps[name] = Math.max(0, Math.round(performance.now() - started));
|
|
11
|
+
}
|
|
12
|
+
}
|
package/build/projectLock.js
CHANGED
|
@@ -147,8 +147,13 @@ async function acquireHarmonyProjectLockAsync(projectRoot, operation) {
|
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
149
|
catch (cause) {
|
|
150
|
-
if (cause instanceof errors_1.HarmonyCliError)
|
|
151
|
-
throw cause
|
|
150
|
+
if (cause instanceof errors_1.HarmonyCliError) {
|
|
151
|
+
throw new errors_1.HarmonyCliError(cause.code, cause.message, {
|
|
152
|
+
cause,
|
|
153
|
+
exitCode: cause.exitCode,
|
|
154
|
+
operation: cause.operation,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
152
157
|
if (cause?.code !== 'EEXIST') {
|
|
153
158
|
throw new errors_1.HarmonyCliError('ERR_HARMONY_PROJECT_LOCK', 'Cannot acquire the Harmony native operation lock.', { cause, operation: 'project-lock' });
|
|
154
159
|
}
|
package/build/run/devices.d.ts
CHANGED
package/build/run/devices.js
CHANGED
|
@@ -18,8 +18,6 @@ function parseHdcTargets(output) {
|
|
|
18
18
|
throw new errors_1.HarmonyCliError('ERR_HARMONY_DEVICE_OUTPUT', 'HDC returned an unsupported target-list format.', { operation: 'list-devices' });
|
|
19
19
|
}
|
|
20
20
|
return {
|
|
21
|
-
// Only the first HDC column is a selectable target name. The remaining
|
|
22
|
-
// verbose columns describe transport, state, location and connect tool.
|
|
23
21
|
aliases: [fields[0]],
|
|
24
22
|
connectTool: fields[4] || null,
|
|
25
23
|
id: fields[0],
|
|
@@ -32,10 +30,7 @@ function parseHdcTargets(output) {
|
|
|
32
30
|
function hasCommandFailure(result) {
|
|
33
31
|
const output = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
34
32
|
return result.code !== 0 || result.timedOut
|
|
35
|
-
// HDC
|
|
36
|
-
// stdout while still returning exit code 0 (for example,
|
|
37
|
-
// "Connect server failed"). Treat its documented failure vocabulary as
|
|
38
|
-
// authoritative regardless of where it appears on the line.
|
|
33
|
+
// HDC can print failures to stdout while returning exit code 0.
|
|
39
34
|
|| /\[(?:Fail|Error)\]|\b(?:Failure|failed)\b|(?:失败|错误)/iu.test(output);
|
|
40
35
|
}
|
|
41
36
|
async function runHdcAsync(hdc, args, options = {}) {
|
|
@@ -93,7 +88,6 @@ async function selectDeviceAsync(hdc, requested, options = {}) {
|
|
|
93
88
|
throw new errors_1.HarmonyCliError('ERR_HARMONY_DEVICE_NOT_FOUND', 'No connected Harmony device was reported by HDC.', { operation: 'select-device' });
|
|
94
89
|
}
|
|
95
90
|
const instances = await (0, emulators_1.listEmulatorsAsync)(options.emulator, { cwd: options.cwd });
|
|
96
|
-
// Prefer an instance that is already booting when HDC is not connected yet.
|
|
97
91
|
const running = instances.filter(instance => instance.running);
|
|
98
92
|
const candidates = requested
|
|
99
93
|
? instances.filter(instance => instance.name === requested)
|
|
@@ -167,12 +161,23 @@ async function installHapAsync(hdc, device, hap, options = {}) {
|
|
|
167
161
|
async function configureMetroPortAsync(hdc, device, port, options = {}) {
|
|
168
162
|
const deviceEndpoint = `tcp:${options.devicePort || 8081}`;
|
|
169
163
|
const hostEndpoint = `tcp:${port}`;
|
|
170
|
-
await runHdcAsync(hdc, ['-t', device.id, 'fport', '
|
|
171
|
-
allowFailure: true,
|
|
164
|
+
const forwards = await runHdcAsync(hdc, ['-t', device.id, 'fport', 'ls'], {
|
|
172
165
|
cwd: options.cwd,
|
|
173
|
-
operation: '
|
|
166
|
+
operation: 'list-metro-ports',
|
|
174
167
|
timeoutMs: 15_000,
|
|
175
168
|
});
|
|
169
|
+
for (const line of forwards.stdout.split(/\r?\n/u)) {
|
|
170
|
+
const [target, remote, local, direction] = line.trim().split(/\s+/u);
|
|
171
|
+
if (target !== device.id || remote !== deviceEndpoint || direction !== '[Reverse]')
|
|
172
|
+
continue;
|
|
173
|
+
if (local === hostEndpoint)
|
|
174
|
+
return;
|
|
175
|
+
await runHdcAsync(hdc, ['-t', device.id, 'fport', 'rm', remote, local], {
|
|
176
|
+
cwd: options.cwd,
|
|
177
|
+
operation: 'remove-metro-port',
|
|
178
|
+
timeoutMs: 15_000,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
176
181
|
await runHdcAsync(hdc, ['-t', device.id, 'rport', deviceEndpoint, hostEndpoint], {
|
|
177
182
|
code: 'ERR_HARMONY_METRO_FORWARD',
|
|
178
183
|
cwd: options.cwd,
|
package/build/run/emulators.d.ts
CHANGED
package/build/run/emulators.js
CHANGED
|
@@ -51,9 +51,7 @@ function startEmulator(tool, name, logFile, cwd) {
|
|
|
51
51
|
const log = node_fs_1.default.openSync(logFile, 'w', 0o600);
|
|
52
52
|
let failure = null;
|
|
53
53
|
try {
|
|
54
|
-
//
|
|
55
|
-
// process group and file-backed output so it survives the CLI/Metro exit.
|
|
56
|
-
// Default instance/image paths match the ones used by -list -details.
|
|
54
|
+
// A detached process with file output lets the emulator survive CLI exit.
|
|
57
55
|
const child = (0, cross_spawn_1.default)(tool.command, [...tool.args, '-start', name], {
|
|
58
56
|
cwd,
|
|
59
57
|
detached: true,
|
|
@@ -65,8 +63,7 @@ function startEmulator(tool, name, logFile, cwd) {
|
|
|
65
63
|
failure = cause.message;
|
|
66
64
|
});
|
|
67
65
|
child.once('exit', (code, signal) => {
|
|
68
|
-
// Some
|
|
69
|
-
// prove readiness; the caller still checks the guest's name and boot state.
|
|
66
|
+
// Some launchers exit before the guest boots; HDC still has to confirm readiness.
|
|
70
67
|
if (code !== 0)
|
|
71
68
|
failure = signal ? `terminated by ${signal}` : `exited with code ${code}`;
|
|
72
69
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.command = void 0;
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const options_1 = require("./options");
|
|
6
|
+
const command = async (argv, io) => {
|
|
7
|
+
const args = (0, command_1.parseCommandArgs)(options_1.parseRunArgs, argv, io);
|
|
8
|
+
if (!args)
|
|
9
|
+
return 0;
|
|
10
|
+
const { projectRoot: root, options } = args;
|
|
11
|
+
const { runHarmonySessionAsync } = await import('./run.js');
|
|
12
|
+
const session = await runHarmonySessionAsync(root, {
|
|
13
|
+
...options,
|
|
14
|
+
interactiveBundler: true,
|
|
15
|
+
io,
|
|
16
|
+
});
|
|
17
|
+
const { result } = session;
|
|
18
|
+
io.log(`Launched ${result.bundleName} on ${result.device.id} (${result.variant}).`);
|
|
19
|
+
if (session.metro.owner === 'started') {
|
|
20
|
+
io.log('\n› Logs for your project will appear below. Press Ctrl+C to exit.');
|
|
21
|
+
await session.metro.waitAsync();
|
|
22
|
+
}
|
|
23
|
+
return 0;
|
|
24
|
+
};
|
|
25
|
+
exports.command = command;
|
package/build/run/metro.js
CHANGED
|
@@ -102,7 +102,7 @@ async function startExpoMetroAsync(projectRoot, options) {
|
|
|
102
102
|
exitError = error;
|
|
103
103
|
});
|
|
104
104
|
const startedAt = Date.now();
|
|
105
|
-
const timeoutMs = options.readyTimeoutMs ||
|
|
105
|
+
const timeoutMs = options.readyTimeoutMs || 1800000;
|
|
106
106
|
try {
|
|
107
107
|
while (Date.now() - startedAt < timeoutMs) {
|
|
108
108
|
if (exitError instanceof errors_1.HarmonyCliError) {
|