@expo-harmony/cli 55.0.26-harmony.1 → 55.0.26-harmony.11
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 +87 -38
- package/build/args.d.ts +19 -0
- package/build/args.js +27 -0
- package/build/bin/expo-harmony.d.ts +2 -0
- package/build/bin/expo-harmony.js +1 -1
- package/build/buildHap/build.d.ts +18 -0
- package/build/buildHap/build.js +38 -0
- package/build/buildHap/index.d.ts +2 -0
- package/build/buildHap/index.js +19 -0
- package/build/buildHap/options.d.ts +7 -0
- package/build/buildHap/options.js +30 -0
- package/build/cli.d.ts +3 -0
- package/build/cli.js +40 -0
- package/build/command.d.ts +9 -0
- package/build/command.js +20 -0
- package/build/development/manifest.d.ts +1 -0
- package/build/development/manifest.js +121 -0
- package/build/development/protocol.d.ts +3 -0
- package/build/development/protocol.js +33 -0
- package/build/development/session.d.ts +5 -0
- package/build/development/session.js +66 -0
- package/build/doctor/doctor.d.ts +20 -0
- package/build/doctor/doctor.js +226 -0
- package/build/doctor/index.d.ts +2 -0
- package/build/doctor/index.js +16 -0
- package/build/doctor/options.d.ts +5 -0
- package/build/doctor/options.js +15 -0
- package/build/entry.d.ts +2 -0
- package/build/entry.js +47 -0
- package/build/errors.d.ts +12 -0
- package/build/errors.js +16 -0
- package/build/expo.d.ts +19 -0
- package/build/expo.js +53 -0
- package/build/exportEmbed/export.d.ts +18 -0
- package/build/exportEmbed/export.js +124 -0
- package/build/exportEmbed/index.d.ts +2 -0
- package/build/exportEmbed/index.js +19 -0
- package/build/exportEmbed/manifest.d.ts +44 -0
- package/build/exportEmbed/manifest.js +171 -0
- package/build/exportEmbed/options.d.ts +7 -0
- package/build/exportEmbed/options.js +22 -0
- package/build/file.d.ts +10 -0
- package/build/file.js +110 -0
- package/build/help.d.ts +1 -0
- package/build/help.js +44 -0
- package/build/index.d.ts +14 -0
- package/build/index.js +6 -5
- package/build/internal/development.d.ts +1 -0
- package/build/internal/development.js +8 -0
- package/build/internal/export.d.ts +1 -0
- package/build/internal/export.js +80 -0
- 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/modules/modules.d.ts +42 -0
- package/build/modules/modules.js +84 -0
- package/build/modules/options.d.ts +10 -0
- package/build/modules/options.js +45 -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 +69 -0
- package/build/native/cache.d.ts +11 -0
- package/build/native/cache.js +73 -0
- package/build/native/hvigor.d.ts +3 -0
- package/build/native/hvigor.js +83 -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/native/toolchain.js +172 -0
- package/build/native/types.d.ts +47 -0
- package/build/native/types.js +2 -0
- package/build/path.d.ts +4 -0
- package/build/path.js +27 -0
- package/build/prebuild/check.d.ts +10 -0
- package/build/prebuild/check.js +151 -0
- package/build/prebuild/clean.d.ts +2 -0
- package/build/prebuild/clean.js +50 -0
- package/build/prebuild/index.d.ts +2 -0
- package/build/prebuild/index.js +51 -0
- package/build/prebuild/options.d.ts +10 -0
- package/build/prebuild/options.js +50 -0
- package/build/prebuild/prebuild.d.ts +6 -0
- package/build/prebuild/prebuild.js +70 -0
- package/build/prebuild/template.d.ts +8 -0
- package/build/prebuild/template.js +86 -0
- package/build/process.d.ts +41 -0
- package/build/process.js +324 -0
- package/build/profile.d.ts +1 -0
- package/build/profile.js +12 -0
- package/build/project.d.ts +2 -0
- package/build/project.js +23 -0
- package/build/projectLock.d.ts +2 -0
- package/build/projectLock.js +185 -0
- package/build/run/devices.d.ts +29 -0
- package/build/run/devices.js +223 -0
- package/build/run/emulators.d.ts +13 -0
- package/build/run/emulators.js +82 -0
- package/build/run/index.d.ts +2 -0
- package/build/run/index.js +25 -0
- package/build/run/metro.d.ts +21 -0
- package/build/run/metro.js +162 -0
- package/build/run/options.d.ts +14 -0
- package/build/run/options.js +62 -0
- package/build/run/run.d.ts +46 -0
- package/build/run/run.js +135 -0
- package/build/runtime/config.d.ts +37 -0
- package/build/runtime/config.js +169 -0
- package/build/runtime/contract.d.ts +5 -0
- package/build/runtime/contract.js +109 -0
- package/build/runtime/fingerprint.d.ts +1 -0
- package/build/runtime/fingerprint.js +49 -0
- package/build/runtime/index.d.ts +2 -0
- package/build/runtime/index.js +50 -0
- package/build/start/index.d.ts +2 -0
- package/build/start/index.js +38 -0
- package/build/start/options.d.ts +9 -0
- package/build/start/options.js +38 -0
- package/build/updates/export.d.ts +14 -0
- package/build/updates/export.js +46 -0
- package/build/updates/index.d.ts +2 -0
- package/build/updates/index.js +96 -0
- package/build/upstream.d.ts +9 -0
- package/build/upstream.js +17 -0
- package/package.json +21 -5
- 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 +27 -225
- package/src/command.ts +24 -0
- package/src/development/manifest.ts +127 -0
- package/src/development/protocol.ts +42 -0
- package/src/development/session.ts +83 -0
- package/src/doctor/doctor.ts +67 -62
- package/src/doctor/index.ts +16 -0
- package/src/exportEmbed/export.ts +73 -72
- package/src/exportEmbed/index.ts +17 -0
- package/src/file.ts +20 -11
- package/src/help.ts +41 -0
- package/src/index.ts +5 -3
- package/src/internal/development.ts +8 -0
- package/src/internal/export.ts +82 -0
- 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 +83 -0
- package/src/{run → native}/cache.ts +10 -9
- package/src/native/hvigor.ts +88 -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} +38 -140
- package/src/native/types.ts +44 -0
- package/src/prebuild/check.ts +56 -31
- 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 +175 -64
- package/src/profile.ts +13 -0
- package/src/projectLock.ts +9 -1
- package/src/run/devices.ts +24 -11
- package/src/run/emulators.ts +4 -6
- package/src/run/index.ts +25 -0
- package/src/run/metro.ts +10 -1
- package/src/run/options.ts +2 -0
- package/src/run/run.ts +63 -129
- package/src/runtime/config.ts +207 -0
- package/src/runtime/contract.ts +167 -0
- package/src/runtime/fingerprint.ts +49 -0
- package/src/runtime/index.ts +54 -0
- package/src/start/index.ts +37 -0
- package/src/start/options.ts +12 -0
- package/src/updates/export.ts +60 -0
- package/src/updates/index.ts +89 -0
- package/src/buildHap/common.ts +0 -145
- package/src/run/install.ts +0 -41
package/src/run/options.ts
CHANGED
|
@@ -8,6 +8,7 @@ const RunOptions = {
|
|
|
8
8
|
'no-bundler': { type: 'boolean' },
|
|
9
9
|
'no-install': { type: 'boolean' },
|
|
10
10
|
'port': { type: 'string' },
|
|
11
|
+
'private-key-path': { type: 'string' },
|
|
11
12
|
'reset-cache': { type: 'boolean' },
|
|
12
13
|
'sync': { type: 'boolean' },
|
|
13
14
|
'variant': { type: 'string' },
|
|
@@ -61,6 +62,7 @@ function parseRunArgs(argv: string[]) {
|
|
|
61
62
|
noBundler: Boolean(values['no-bundler']),
|
|
62
63
|
noInstall: Boolean(values['no-install']),
|
|
63
64
|
port,
|
|
65
|
+
privateKeyPath: values['private-key-path'],
|
|
64
66
|
project: positionals[0],
|
|
65
67
|
resetCache: Boolean(values['reset-cache']),
|
|
66
68
|
sync: Boolean(values.sync),
|
package/src/run/run.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { getConfig } from '@expo/config';
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
configureMetroPortAsync,
|
|
@@ -7,33 +8,18 @@ import {
|
|
|
7
8
|
selectDeviceAsync,
|
|
8
9
|
} from './devices';
|
|
9
10
|
import { HarmonyCliError } from '../errors';
|
|
10
|
-
import {
|
|
11
|
-
import type
|
|
12
|
-
import {
|
|
13
|
-
resolveHarmonyBuildPlanAsync,
|
|
14
|
-
resolveHarmonyEmulator,
|
|
15
|
-
resolveHarmonyToolchain,
|
|
16
|
-
type HarmonyBuildPlan,
|
|
17
|
-
} from '../tools';
|
|
18
|
-
import { installHarmonyDependenciesAsync } from './install';
|
|
11
|
+
import { resolveHarmonyEmulator, resolveHarmonyToolchain } from '../native/toolchain';
|
|
12
|
+
import { type HarmonyBuildPlan } from '../native/types';
|
|
19
13
|
import {
|
|
20
14
|
requireExistingMetroAsync,
|
|
21
15
|
startExpoMetroAsync,
|
|
22
16
|
type MetroSession,
|
|
23
17
|
} from './metro';
|
|
24
|
-
import {
|
|
25
|
-
commitHarmonyNativeBuildCacheAsync,
|
|
26
|
-
prepareHarmonyNativeBuildCacheAsync,
|
|
27
|
-
} from './cache';
|
|
28
|
-
import { toPosixPath } from '../path';
|
|
29
18
|
import { withHarmonyProjectLockAsync } from '../projectLock';
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
runCheckedAsync,
|
|
35
|
-
timed,
|
|
36
|
-
} from '../buildHap/common';
|
|
19
|
+
import { buildNativeAsync, type NativeBuildResult } from '../native/build';
|
|
20
|
+
import { prepareNativeProjectAsync } from '../native/prepareProject';
|
|
21
|
+
import { progress } from '../log';
|
|
22
|
+
import { timedAsync } from '../profile';
|
|
37
23
|
|
|
38
24
|
export interface HarmonyRunOptions {
|
|
39
25
|
appId?: string;
|
|
@@ -42,16 +28,15 @@ export interface HarmonyRunOptions {
|
|
|
42
28
|
noBundler?: boolean;
|
|
43
29
|
noInstall?: boolean;
|
|
44
30
|
port?: number;
|
|
31
|
+
privateKeyPath?: string;
|
|
45
32
|
resetCache?: boolean;
|
|
46
33
|
sync?: boolean;
|
|
47
34
|
variant?: 'debug' | 'release';
|
|
48
35
|
}
|
|
49
36
|
|
|
50
|
-
export interface HarmonyRunResult {
|
|
37
|
+
export interface HarmonyRunResult extends NativeBuildResult {
|
|
51
38
|
bundleName: string;
|
|
52
39
|
device: { id: string; transport: string };
|
|
53
|
-
export: null | { assetCount: number; bundleSha256: string; sourceMapSha256: string };
|
|
54
|
-
hapPath: string;
|
|
55
40
|
installed: boolean;
|
|
56
41
|
launched: true;
|
|
57
42
|
metro: { owner: 'disabled' | 'existing' | 'started'; port: number };
|
|
@@ -62,7 +47,6 @@ export interface HarmonyRunResult {
|
|
|
62
47
|
}
|
|
63
48
|
|
|
64
49
|
interface HarmonyRunSessionOptions extends HarmonyRunOptions {
|
|
65
|
-
/** Give a CLI-started Metro process ownership of the current terminal. */
|
|
66
50
|
interactiveBundler?: boolean;
|
|
67
51
|
}
|
|
68
52
|
|
|
@@ -76,8 +60,8 @@ interface HarmonyRunSession {
|
|
|
76
60
|
result: HarmonyRunResult;
|
|
77
61
|
}
|
|
78
62
|
|
|
79
|
-
type NormalizedRunOptions = Required<Omit<HarmonyRunSessionOptions, 'appId' | 'device'>>
|
|
80
|
-
& Pick<HarmonyRunSessionOptions, 'appId' | 'device'>;
|
|
63
|
+
type NormalizedRunOptions = Required<Omit<HarmonyRunSessionOptions, 'appId' | 'device' | 'privateKeyPath'>>
|
|
64
|
+
& Pick<HarmonyRunSessionOptions, 'appId' | 'device' | 'privateKeyPath'>;
|
|
81
65
|
|
|
82
66
|
const BundleName = /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*){2,}$/u;
|
|
83
67
|
|
|
@@ -101,10 +85,10 @@ function resolveRunIdentity(plan: HarmonyBuildPlan, options: NormalizedRunOption
|
|
|
101
85
|
}
|
|
102
86
|
|
|
103
87
|
async function runHarmonyUnlockedAsync(
|
|
104
|
-
|
|
88
|
+
root: string,
|
|
105
89
|
options: HarmonyRunSessionOptions = {}
|
|
106
90
|
): Promise<HarmonyRunSession> {
|
|
107
|
-
const
|
|
91
|
+
const settings: NormalizedRunOptions = {
|
|
108
92
|
appId: options.appId,
|
|
109
93
|
device: options.device,
|
|
110
94
|
interactiveBundler: Boolean(options.interactiveBundler),
|
|
@@ -112,108 +96,60 @@ async function runHarmonyUnlockedAsync(
|
|
|
112
96
|
noBundler: Boolean(options.noBundler),
|
|
113
97
|
noInstall: Boolean(options.noInstall),
|
|
114
98
|
port: options.port || 8081,
|
|
99
|
+
privateKeyPath: options.privateKeyPath,
|
|
115
100
|
resetCache: Boolean(options.resetCache),
|
|
116
101
|
sync: Boolean(options.sync),
|
|
117
102
|
variant: options.variant || 'debug',
|
|
118
103
|
};
|
|
119
104
|
const steps: Record<string, number> = {};
|
|
120
105
|
|
|
121
|
-
await
|
|
106
|
+
const plan = await prepareNativeProjectAsync(root, {
|
|
107
|
+
...settings,
|
|
108
|
+
check: false,
|
|
109
|
+
}, steps);
|
|
122
110
|
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
{ buildMode: normalizedOptions.variant }
|
|
126
|
-
));
|
|
127
|
-
const identity = resolveRunIdentity(plan, normalizedOptions);
|
|
128
|
-
const toolchain = resolveHarmonyToolchain();
|
|
111
|
+
const identity = resolveRunIdentity(plan, settings);
|
|
112
|
+
const tools = resolveHarmonyToolchain();
|
|
129
113
|
|
|
130
|
-
progress(
|
|
131
|
-
const device = await
|
|
132
|
-
|
|
133
|
-
|
|
114
|
+
progress(settings, 'Selecting a Harmony device or emulator');
|
|
115
|
+
const device = await timedAsync(steps, 'device', () => selectDeviceAsync(
|
|
116
|
+
tools.hdc,
|
|
117
|
+
settings.device,
|
|
134
118
|
{
|
|
135
119
|
cwd: plan.harmonyRoot,
|
|
136
|
-
emulator: resolveHarmonyEmulator(
|
|
137
|
-
emulatorLogFile: path.join(
|
|
138
|
-
onProgress: message => progress(
|
|
120
|
+
emulator: resolveHarmonyEmulator(tools),
|
|
121
|
+
emulatorLogFile: path.join(root, '.expo', 'harmony', 'emulator.log'),
|
|
122
|
+
onProgress: message => progress(settings, message),
|
|
139
123
|
}
|
|
140
124
|
));
|
|
141
125
|
|
|
142
|
-
|
|
143
|
-
if (normalizedOptions.variant === 'release') {
|
|
144
|
-
progress(normalizedOptions, 'Exporting the release Hermes bundle');
|
|
145
|
-
exportManifest = await timed(steps, 'export', () => exportEmbedAsync(
|
|
146
|
-
projectRoot,
|
|
147
|
-
{ resetCache: normalizedOptions.resetCache, skipDoctor: true }
|
|
148
|
-
));
|
|
149
|
-
} else {
|
|
150
|
-
steps.export = 0;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
progress(normalizedOptions, 'Installing Harmony project dependencies');
|
|
154
|
-
await timed(steps, 'ohpm', () => installHarmonyDependenciesAsync(plan, toolchain));
|
|
126
|
+
const built = await buildNativeAsync(root, plan, tools, settings, steps);
|
|
155
127
|
|
|
156
128
|
let metro: HarmonyRunSession['metro'] = {
|
|
157
129
|
owner: 'disabled',
|
|
158
|
-
port:
|
|
130
|
+
port: settings.port,
|
|
159
131
|
stop: async () => {},
|
|
160
132
|
waitAsync: async () => {},
|
|
161
133
|
};
|
|
162
|
-
try {
|
|
163
|
-
progress(normalizedOptions, 'Checking Harmony native dependency cache');
|
|
164
|
-
const nativeBuildCache = await timed(steps, 'nativeCache', () => (
|
|
165
|
-
prepareHarmonyNativeBuildCacheAsync(projectRoot, plan)
|
|
166
|
-
));
|
|
167
134
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
progress(normalizedOptions, `Building the ${normalizedOptions.variant} HAP`);
|
|
173
|
-
const buildEnv = {
|
|
174
|
-
...process.env,
|
|
175
|
-
EXPO_HARMONY_NODE: process.env.EXPO_HARMONY_NODE || process.execPath,
|
|
176
|
-
EXPO_METRO_TARGET: 'harmony',
|
|
177
|
-
HERMES_V1_ENABLED: 'true',
|
|
178
|
-
...(normalizedOptions.variant === 'release' ? { EXPO_HARMONY_BUNDLE_PREBUILT: '1' } : {}),
|
|
179
|
-
...(toolchain.sdkHome && !process.env.DEVECO_SDK_HOME
|
|
180
|
-
? { DEVECO_SDK_HOME: toolchain.sdkHome }
|
|
181
|
-
: {}),
|
|
182
|
-
};
|
|
183
|
-
await timed(steps, 'build', () => runCheckedAsync(toolchain.hvigor.command, [
|
|
184
|
-
...toolchain.hvigor.args,
|
|
185
|
-
...plan.hvigorArgs,
|
|
186
|
-
], {
|
|
187
|
-
code: 'ERR_HARMONY_BUILD_FAILED',
|
|
188
|
-
cwd: plan.harmonyRoot,
|
|
189
|
-
env: buildEnv,
|
|
190
|
-
message: 'Hvigor build',
|
|
191
|
-
operation: 'hvigor-build',
|
|
192
|
-
timeoutMs: 15 * 60_000,
|
|
193
|
-
}));
|
|
194
|
-
|
|
195
|
-
if (!isNonEmptyRegularFile(plan.expectedHap)) {
|
|
196
|
-
throw new HarmonyCliError('ERR_HARMONY_HAP_MISSING', 'Hvigor completed without producing the expected non-empty regular HAP.', { operation: 'verify-hap' });
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
await timed(steps, 'nativeCacheCommit', () => commitHarmonyNativeBuildCacheAsync(nativeBuildCache));
|
|
200
|
-
|
|
201
|
-
if (normalizedOptions.variant === 'debug') {
|
|
202
|
-
progress(normalizedOptions, normalizedOptions.noBundler
|
|
135
|
+
try {
|
|
136
|
+
if (settings.variant === 'debug' && getConfig(root).exp.updates?.useNativeDebug !== true) {
|
|
137
|
+
progress(settings, settings.noBundler
|
|
203
138
|
? 'Connecting to the existing Expo Metro server'
|
|
204
139
|
: 'Starting Expo Metro');
|
|
205
|
-
metro = await
|
|
206
|
-
? requireExistingMetroAsync(
|
|
207
|
-
: startExpoMetroAsync(
|
|
208
|
-
interactive:
|
|
209
|
-
port:
|
|
210
|
-
|
|
140
|
+
metro = await timedAsync(steps, 'metro', () => settings.noBundler
|
|
141
|
+
? requireExistingMetroAsync(settings.port)
|
|
142
|
+
: startExpoMetroAsync(root, {
|
|
143
|
+
interactive: settings.interactiveBundler,
|
|
144
|
+
port: settings.port,
|
|
145
|
+
privateKeyPath: settings.privateKeyPath,
|
|
146
|
+
resetCache: settings.resetCache,
|
|
211
147
|
}));
|
|
212
148
|
|
|
213
|
-
await
|
|
214
|
-
|
|
149
|
+
await timedAsync(steps, 'metroPort', () => configureMetroPortAsync(
|
|
150
|
+
tools.hdc,
|
|
215
151
|
device,
|
|
216
|
-
|
|
152
|
+
settings.port,
|
|
217
153
|
{ cwd: plan.harmonyRoot }
|
|
218
154
|
));
|
|
219
155
|
} else {
|
|
@@ -221,21 +157,21 @@ async function runHarmonyUnlockedAsync(
|
|
|
221
157
|
steps.metroPort = 0;
|
|
222
158
|
}
|
|
223
159
|
|
|
224
|
-
if (
|
|
160
|
+
if (settings.noInstall) {
|
|
225
161
|
steps.install = 0;
|
|
226
162
|
} else {
|
|
227
|
-
progress(
|
|
228
|
-
await
|
|
229
|
-
|
|
163
|
+
progress(settings, `Installing the HAP on ${device.id}`);
|
|
164
|
+
await timedAsync(steps, 'install', () => installHapAsync(
|
|
165
|
+
tools.hdc,
|
|
230
166
|
device,
|
|
231
167
|
plan.expectedHap,
|
|
232
168
|
{ cwd: plan.harmonyRoot }
|
|
233
169
|
));
|
|
234
170
|
}
|
|
235
171
|
|
|
236
|
-
progress(
|
|
237
|
-
await
|
|
238
|
-
|
|
172
|
+
progress(settings, `Launching ${identity.bundleName}`);
|
|
173
|
+
await timedAsync(steps, 'launch', () => launchAppAsync(
|
|
174
|
+
tools.hdc,
|
|
239
175
|
device,
|
|
240
176
|
identity.bundleName,
|
|
241
177
|
identity.abilityName,
|
|
@@ -248,15 +184,8 @@ async function runHarmonyUnlockedAsync(
|
|
|
248
184
|
id: device.id,
|
|
249
185
|
transport: device.transport,
|
|
250
186
|
},
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
assetCount: exportManifest.assets.length,
|
|
254
|
-
bundleSha256: exportManifest.bundle.sha256,
|
|
255
|
-
sourceMapSha256: exportManifest.sourceMap.sha256,
|
|
256
|
-
}
|
|
257
|
-
: null,
|
|
258
|
-
hapPath: toPosixPath(path.relative(projectRoot, plan.expectedHap)),
|
|
259
|
-
installed: !normalizedOptions.noInstall,
|
|
187
|
+
...built,
|
|
188
|
+
installed: !settings.noInstall,
|
|
260
189
|
launched: true,
|
|
261
190
|
metro: {
|
|
262
191
|
owner: metro.owner,
|
|
@@ -265,32 +194,37 @@ async function runHarmonyUnlockedAsync(
|
|
|
265
194
|
ok: true,
|
|
266
195
|
schemaVersion: 1,
|
|
267
196
|
steps,
|
|
268
|
-
variant:
|
|
197
|
+
variant: settings.variant,
|
|
269
198
|
};
|
|
270
199
|
|
|
271
200
|
return { metro, result };
|
|
272
201
|
} catch (error) {
|
|
273
202
|
await metro.stop();
|
|
274
|
-
|
|
203
|
+
|
|
204
|
+
throw new HarmonyCliError(error?.code || 'ERR_HARMONY_UNKNOWN', error?.message || 'Harmony run failed.', {
|
|
205
|
+
cause: error,
|
|
206
|
+
exitCode: error?.exitCode,
|
|
207
|
+
operation: error?.operation,
|
|
208
|
+
});
|
|
275
209
|
}
|
|
276
210
|
}
|
|
277
211
|
|
|
278
212
|
async function runHarmonySessionAsync(
|
|
279
|
-
|
|
213
|
+
root: string,
|
|
280
214
|
options: HarmonyRunSessionOptions = {}
|
|
281
215
|
): Promise<HarmonyRunSession> {
|
|
282
216
|
return withHarmonyProjectLockAsync(
|
|
283
|
-
|
|
217
|
+
root,
|
|
284
218
|
'run',
|
|
285
|
-
() => runHarmonyUnlockedAsync(
|
|
219
|
+
() => runHarmonyUnlockedAsync(root, options)
|
|
286
220
|
);
|
|
287
221
|
}
|
|
288
222
|
|
|
289
223
|
async function runHarmonyAsync(
|
|
290
|
-
|
|
224
|
+
root: string,
|
|
291
225
|
options: HarmonyRunOptions = {}
|
|
292
226
|
): Promise<HarmonyRunResult> {
|
|
293
|
-
const session = await runHarmonySessionAsync(
|
|
227
|
+
const session = await runHarmonySessionAsync(root, options);
|
|
294
228
|
|
|
295
229
|
try {
|
|
296
230
|
return session.result;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { getConfig } from '@expo/config';
|
|
7
|
+
import { compileHarmonyModsAsync, normalizeHarmonyConfig, parseHarmonySdkVersion, type ExpoConfigWithHarmony } from '@expo-harmony/config-plugins';
|
|
8
|
+
import type { HarmonyRuntimeContract } from '@expo-harmony/expo-modules-autolinking/runtime';
|
|
9
|
+
import { withHarmonyPrebuildConfig } from '@expo-harmony/prebuild-config';
|
|
10
|
+
import JSON5 from 'json5';
|
|
11
|
+
|
|
12
|
+
import { fingerprintHarmonyAsync } from './fingerprint';
|
|
13
|
+
import { HarmonyCliError } from '../errors';
|
|
14
|
+
import { resolveHarmonyBuildPlanAsync } from '../native/project';
|
|
15
|
+
import type { HarmonyBuildPlan } from '../native/types';
|
|
16
|
+
|
|
17
|
+
export interface HarmonyMetroRuntime {
|
|
18
|
+
reactPackage: string;
|
|
19
|
+
harmonyPackage: string;
|
|
20
|
+
fixedRuntime: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function resolveRuntimeVersionAsync(root: string, config: ExpoConfigWithHarmony, app): Promise<string> {
|
|
24
|
+
if (typeof config.runtimeVersion === 'object' && config.runtimeVersion?.policy === 'nativeVersion') {
|
|
25
|
+
if (typeof app?.versionName !== 'string' || !app.versionName
|
|
26
|
+
|| !Number.isSafeInteger(app.versionCode) || app.versionCode < 1) {
|
|
27
|
+
throw new HarmonyCliError(
|
|
28
|
+
'ERR_HARMONY_RUNTIME_CONFIG',
|
|
29
|
+
'The nativeVersion policy requires a Harmony versionName and a positive versionCode.',
|
|
30
|
+
{ operation: 'runtime-contract' }
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return `${app.versionName}(${app.versionCode})`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof config.runtimeVersion === 'object' && config.runtimeVersion?.policy === 'fingerprint') {
|
|
38
|
+
return (await fingerprintHarmonyAsync(root)).hash;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const require = createRequire(path.join(root, 'package.json'));
|
|
42
|
+
const expo = createRequire(require.resolve('expo/package.json'));
|
|
43
|
+
const { getRuntimeVersionAsync } = expo('@expo/config-plugins/build/utils/Updates');
|
|
44
|
+
const version = await getRuntimeVersionAsync(root, {
|
|
45
|
+
...config,
|
|
46
|
+
runtimeVersion: config.runtimeVersion ?? { policy: 'sdkVersion' },
|
|
47
|
+
}, 'harmony');
|
|
48
|
+
|
|
49
|
+
if (typeof version !== 'string' || !version) {
|
|
50
|
+
throw new HarmonyCliError(
|
|
51
|
+
'ERR_HARMONY_RUNTIME_CONFIG',
|
|
52
|
+
'Cannot resolve expo.runtimeVersion for Harmony.',
|
|
53
|
+
{ operation: 'runtime-contract' }
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return version;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function readMetroRuntimeAsync(root: string): Promise<HarmonyMetroRuntime | undefined> {
|
|
61
|
+
const require = createRequire(path.join(root, 'package.json'));
|
|
62
|
+
const previous = process.env.EXPO_METRO_TARGET;
|
|
63
|
+
let config;
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
process.env.EXPO_METRO_TARGET = 'harmony';
|
|
67
|
+
config = (await require('metro-config').resolveConfig(undefined, root)).config;
|
|
68
|
+
} finally {
|
|
69
|
+
if (previous === undefined) delete process.env.EXPO_METRO_TARGET;
|
|
70
|
+
else process.env.EXPO_METRO_TARGET = previous;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return config?.resolver?.resolveRequest?.harmonyRuntime;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function onlyKeys(value, keys: string[]): boolean {
|
|
77
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
78
|
+
&& Object.keys(value).every(key => keys.includes(key));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function optionalStrings(value): boolean {
|
|
82
|
+
return value === undefined || (Array.isArray(value) && value.every(item => typeof item === 'string' && item.length > 0));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function supportedReleaseOptions(modes): boolean {
|
|
86
|
+
if (modes === undefined) return true;
|
|
87
|
+
if (!Array.isArray(modes) || modes.length > 1) return false;
|
|
88
|
+
|
|
89
|
+
return modes.every((mode) => {
|
|
90
|
+
if (!onlyKeys(mode, ['name', 'arkOptions', 'nativeLib']) || mode.name !== 'release') return false;
|
|
91
|
+
|
|
92
|
+
if (mode.nativeLib !== undefined) {
|
|
93
|
+
const symbol = mode.nativeLib?.debugSymbol;
|
|
94
|
+
|
|
95
|
+
if (!onlyKeys(mode.nativeLib, ['debugSymbol']) || !onlyKeys(symbol, ['strip', 'exclude'])
|
|
96
|
+
|| typeof symbol.strip !== 'boolean' || !optionalStrings(symbol.exclude)) return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (mode.arkOptions !== undefined) {
|
|
100
|
+
const obfuscation = mode.arkOptions?.obfuscation;
|
|
101
|
+
const rules = obfuscation?.ruleOptions;
|
|
102
|
+
|
|
103
|
+
if (!onlyKeys(mode.arkOptions, ['obfuscation']) || !onlyKeys(obfuscation, ['ruleOptions'])
|
|
104
|
+
|| !onlyKeys(rules, ['enable', 'files']) || typeof rules.enable !== 'boolean'
|
|
105
|
+
|| !optionalStrings(rules.files)) return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return true;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function nativeLibFilterHash(filter): string | undefined {
|
|
113
|
+
if (filter === undefined) return undefined;
|
|
114
|
+
if (!onlyKeys(filter, ['excludes', 'pickFirsts', 'pickLasts', 'enableOverride'])
|
|
115
|
+
|| !['excludes', 'pickFirsts', 'pickLasts'].every(key => optionalStrings(filter[key]))
|
|
116
|
+
|| (filter.enableOverride !== undefined && typeof filter.enableOverride !== 'boolean')) {
|
|
117
|
+
throw new HarmonyCliError('ERR_HARMONY_RUNTIME_CONFIG', 'Unsupported nativeLib.filter configuration.', { operation: 'runtime-contract' });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const values = {};
|
|
121
|
+
|
|
122
|
+
for (const key of ['excludes', 'pickFirsts', 'pickLasts']) {
|
|
123
|
+
if (filter[key]?.length) values[key] = [...new Set(filter[key])].sort();
|
|
124
|
+
}
|
|
125
|
+
if (filter.enableOverride) values['enableOverride'] = true;
|
|
126
|
+
|
|
127
|
+
return Object.keys(values).length ? createHash('sha256').update(JSON.stringify(values)).digest('hex') : undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function nativeConfig(profile, module, build, product: string, ability: string): HarmonyRuntimeContract['config'] {
|
|
131
|
+
const selected = profile?.app?.products?.find(item => item.name === product);
|
|
132
|
+
const entry = module?.abilities?.find(item => item.name === ability);
|
|
133
|
+
|
|
134
|
+
if (!selected || !entry || !build
|
|
135
|
+
|| /(?:USE_HERMES|HERMES_V1_ENABLED)(?::BOOL)?=(?:OFF|FALSE|0)/i.test(build.buildOption?.externalNativeOptions?.arguments || '')
|
|
136
|
+
|| Object.keys(selected.buildOption || {}).some(key => key !== 'nativeCompiler')
|
|
137
|
+
|| profile.app.buildModeSet?.some(mode => mode.buildOption)
|
|
138
|
+
|| !supportedReleaseOptions(build.buildOptionSet) || build.targets?.some(target => target.buildOption)) {
|
|
139
|
+
throw new HarmonyCliError(
|
|
140
|
+
'ERR_HARMONY_RUNTIME_CONFIG',
|
|
141
|
+
'Runtime contracts require an existing Harmony product and ability, Hermes v1, and no per-target or per-mode overrides except release debugSymbol and ArkTS obfuscation rules.',
|
|
142
|
+
{ operation: 'runtime-contract' }
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const hash = nativeLibFilterHash(build.buildOption?.nativeLib?.filter);
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
nativeCompiler: selected.buildOption?.nativeCompiler,
|
|
150
|
+
targetApiVersion: parseHarmonySdkVersion(selected.targetSdkVersion, 'targetSdkVersion').api,
|
|
151
|
+
compatibleApiVersion: parseHarmonySdkVersion(selected.compatibleSdkVersion, 'compatibleSdkVersion').api,
|
|
152
|
+
...(hash ? { nativeLibFilterHash: hash } : {}),
|
|
153
|
+
permissions: [...new Set<string>((module.requestPermissions || []).map(item => item.name))].sort(),
|
|
154
|
+
querySchemes: [...new Set<string>(module.querySchemes || [])].sort(),
|
|
155
|
+
backgroundModes: [...new Set<string>(entry.backgroundModes || [])].sort(),
|
|
156
|
+
abiFilters: [...(build.buildOption?.externalNativeOptions?.abiFilters || [])].sort(),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function readNativeRuntime(plan: HarmonyBuildPlan) {
|
|
161
|
+
const read = (file: string) => JSON5.parse(fs.readFileSync(file, 'utf8'));
|
|
162
|
+
const app = read(path.join(plan.harmonyRoot, 'AppScope/app.json5')).app;
|
|
163
|
+
const profile = read(plan.projectFiles.projectBuildProfile);
|
|
164
|
+
const module = read(plan.projectFiles.moduleJson).module;
|
|
165
|
+
const build = read(path.join(plan.moduleRoot, 'build-profile.json5'));
|
|
166
|
+
|
|
167
|
+
return { app, config: nativeConfig(profile, module, build, plan.productName, plan.abilityName) };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function readProjectRuntimeAsync(root: string) {
|
|
171
|
+
const { exp } = getConfig(root, { skipSDKVersionRequirement: true, isModdedConfig: true });
|
|
172
|
+
const config = exp as ExpoConfigWithHarmony;
|
|
173
|
+
let native: ReturnType<typeof readNativeRuntime>;
|
|
174
|
+
let bundle: string;
|
|
175
|
+
|
|
176
|
+
if (config.harmony) {
|
|
177
|
+
const settings = normalizeHarmonyConfig(config);
|
|
178
|
+
const evaluated = await compileHarmonyModsAsync(withHarmonyPrebuildConfig(config), {
|
|
179
|
+
projectRoot: root,
|
|
180
|
+
introspect: true,
|
|
181
|
+
ignoreExistingNativeFiles: true,
|
|
182
|
+
});
|
|
183
|
+
const results = evaluated._internal?.modResults?.harmony;
|
|
184
|
+
native = {
|
|
185
|
+
app: results?.appJson?.app,
|
|
186
|
+
config: nativeConfig(
|
|
187
|
+
results?.projectBuildProfile,
|
|
188
|
+
results?.moduleJson?.module,
|
|
189
|
+
results?.entryBuildProfile,
|
|
190
|
+
settings.productName,
|
|
191
|
+
settings.abilityName
|
|
192
|
+
),
|
|
193
|
+
};
|
|
194
|
+
bundle = settings.bundleName;
|
|
195
|
+
} else {
|
|
196
|
+
const plan = await resolveHarmonyBuildPlanAsync(root);
|
|
197
|
+
native = readNativeRuntime(plan);
|
|
198
|
+
bundle = plan.bundleName;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
config,
|
|
203
|
+
bundleName: bundle,
|
|
204
|
+
native: native.config,
|
|
205
|
+
runtimeVersion: await resolveRuntimeVersionAsync(root, config, native.app),
|
|
206
|
+
};
|
|
207
|
+
}
|