@expo-harmony/cli 55.0.26-harmony.8 → 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.
Files changed (115) hide show
  1. package/README.md +74 -46
  2. package/build/bin/expo-harmony.js +1 -1
  3. package/build/buildHap/build.d.ts +3 -10
  4. package/build/buildHap/build.js +11 -69
  5. package/build/buildHap/index.d.ts +2 -0
  6. package/build/buildHap/index.js +19 -0
  7. package/build/cli.d.ts +3 -3
  8. package/build/cli.js +21 -187
  9. package/build/command.d.ts +9 -0
  10. package/build/command.js +20 -0
  11. package/build/doctor/doctor.d.ts +1 -1
  12. package/build/doctor/doctor.js +64 -56
  13. package/build/doctor/index.d.ts +2 -0
  14. package/build/doctor/index.js +16 -0
  15. package/build/exportEmbed/export.d.ts +2 -2
  16. package/build/exportEmbed/export.js +53 -49
  17. package/build/exportEmbed/index.d.ts +2 -0
  18. package/build/exportEmbed/index.js +19 -0
  19. package/build/file.d.ts +2 -1
  20. package/build/file.js +18 -8
  21. package/build/help.d.ts +1 -0
  22. package/build/help.js +36 -0
  23. package/build/index.d.ts +5 -3
  24. package/build/index.js +6 -5
  25. package/build/internal/prebuild.d.ts +3 -0
  26. package/build/internal/prebuild.js +9 -0
  27. package/build/log.d.ts +4 -0
  28. package/build/log.js +6 -0
  29. package/build/modules/index.d.ts +2 -0
  30. package/build/modules/index.js +16 -0
  31. package/build/native/bare.d.ts +4 -0
  32. package/build/native/bare.js +130 -0
  33. package/build/native/build.d.ts +15 -0
  34. package/build/native/build.js +67 -0
  35. package/build/{run → native}/cache.d.ts +2 -2
  36. package/build/{run → native}/cache.js +8 -8
  37. package/build/native/hvigor.d.ts +2 -0
  38. package/build/native/hvigor.js +77 -0
  39. package/build/native/install.d.ts +5 -0
  40. package/build/native/install.js +13 -0
  41. package/build/native/prepareProject.d.ts +11 -0
  42. package/build/native/prepareProject.js +68 -0
  43. package/build/native/project.d.ts +8 -0
  44. package/build/native/project.js +58 -0
  45. package/build/native/toolchain.d.ts +16 -0
  46. package/build/{tools.js → native/toolchain.js} +1 -57
  47. package/build/native/types.d.ts +47 -0
  48. package/build/native/types.js +2 -0
  49. package/build/prebuild/check.d.ts +7 -3
  50. package/build/prebuild/check.js +26 -20
  51. package/build/prebuild/clean.js +5 -5
  52. package/build/prebuild/index.d.ts +2 -0
  53. package/build/prebuild/index.js +51 -0
  54. package/build/prebuild/prebuild.js +11 -3
  55. package/build/prebuild/template.js +2 -2
  56. package/build/process.d.ts +11 -1
  57. package/build/process.js +16 -0
  58. package/build/profile.d.ts +1 -0
  59. package/build/profile.js +12 -0
  60. package/build/projectLock.js +7 -2
  61. package/build/run/devices.d.ts +1 -1
  62. package/build/run/devices.js +15 -10
  63. package/build/run/emulators.d.ts +1 -1
  64. package/build/run/emulators.js +2 -5
  65. package/build/run/index.d.ts +2 -0
  66. package/build/run/index.js +25 -0
  67. package/build/run/run.d.ts +4 -10
  68. package/build/run/run.js +45 -90
  69. package/build/start/index.d.ts +2 -0
  70. package/build/start/index.js +33 -0
  71. package/package.json +14 -4
  72. package/src/bin/expo-harmony.ts +2 -2
  73. package/src/buildHap/build.ts +13 -105
  74. package/src/buildHap/index.ts +19 -0
  75. package/src/cli.ts +24 -227
  76. package/src/command.ts +24 -0
  77. package/src/doctor/doctor.ts +63 -61
  78. package/src/doctor/index.ts +16 -0
  79. package/src/exportEmbed/export.ts +64 -55
  80. package/src/exportEmbed/index.ts +17 -0
  81. package/src/file.ts +20 -11
  82. package/src/help.ts +33 -0
  83. package/src/index.ts +5 -3
  84. package/src/internal/prebuild.ts +3 -0
  85. package/src/log.ts +5 -0
  86. package/src/modules/index.ts +16 -0
  87. package/src/native/bare.ts +145 -0
  88. package/src/native/build.ts +81 -0
  89. package/src/{run → native}/cache.ts +10 -9
  90. package/src/native/hvigor.ts +81 -0
  91. package/src/native/install.ts +17 -0
  92. package/src/native/prepareProject.ts +99 -0
  93. package/src/native/project.ts +88 -0
  94. package/src/{tools.ts → native/toolchain.ts} +11 -118
  95. package/src/native/types.ts +44 -0
  96. package/src/prebuild/check.ts +30 -19
  97. package/src/prebuild/clean.ts +3 -3
  98. package/src/prebuild/index.ts +56 -0
  99. package/src/prebuild/prebuild.ts +11 -1
  100. package/src/prebuild/template.ts +2 -2
  101. package/src/process.ts +38 -1
  102. package/src/profile.ts +13 -0
  103. package/src/projectLock.ts +9 -1
  104. package/src/run/devices.ts +16 -11
  105. package/src/run/emulators.ts +4 -6
  106. package/src/run/index.ts +25 -0
  107. package/src/run/run.ts +56 -129
  108. package/src/start/index.ts +31 -0
  109. package/build/buildHap/common.d.ts +0 -24
  110. package/build/buildHap/common.js +0 -92
  111. package/build/run/install.d.ts +0 -7
  112. package/build/run/install.js +0 -26
  113. package/build/tools.d.ts +0 -39
  114. package/src/buildHap/common.ts +0 -145
  115. package/src/run/install.ts +0 -41
@@ -1,4 +1,5 @@
1
1
  import { type MetroSession } from './metro';
2
+ import { type NativeBuildResult } from '../native/build';
2
3
  export interface HarmonyRunOptions {
3
4
  appId?: string;
4
5
  device?: string;
@@ -10,18 +11,12 @@ export interface HarmonyRunOptions {
10
11
  sync?: boolean;
11
12
  variant?: 'debug' | 'release';
12
13
  }
13
- export interface HarmonyRunResult {
14
+ export interface HarmonyRunResult extends NativeBuildResult {
14
15
  bundleName: string;
15
16
  device: {
16
17
  id: string;
17
18
  transport: string;
18
19
  };
19
- export: null | {
20
- assetCount: number;
21
- bundleSha256: string;
22
- sourceMapSha256: string;
23
- };
24
- hapPath: string;
25
20
  installed: boolean;
26
21
  launched: true;
27
22
  metro: {
@@ -34,7 +29,6 @@ export interface HarmonyRunResult {
34
29
  variant: 'debug' | 'release';
35
30
  }
36
31
  interface HarmonyRunSessionOptions extends HarmonyRunOptions {
37
- /** Give a CLI-started Metro process ownership of the current terminal. */
38
32
  interactiveBundler?: boolean;
39
33
  }
40
34
  interface HarmonyRunSession {
@@ -46,6 +40,6 @@ interface HarmonyRunSession {
46
40
  };
47
41
  result: HarmonyRunResult;
48
42
  }
49
- declare function runHarmonySessionAsync(projectRoot: string, options?: HarmonyRunSessionOptions): Promise<HarmonyRunSession>;
50
- declare function runHarmonyAsync(projectRoot: string, options?: HarmonyRunOptions): Promise<HarmonyRunResult>;
43
+ declare function runHarmonySessionAsync(root: string, options?: HarmonyRunSessionOptions): Promise<HarmonyRunSession>;
44
+ declare function runHarmonyAsync(root: string, options?: HarmonyRunOptions): Promise<HarmonyRunResult>;
51
45
  export { runHarmonyAsync, runHarmonySessionAsync };
package/build/run/run.js CHANGED
@@ -8,14 +8,13 @@ exports.runHarmonySessionAsync = runHarmonySessionAsync;
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const devices_1 = require("./devices");
10
10
  const errors_1 = require("../errors");
11
- const export_1 = require("../exportEmbed/export");
12
- const tools_1 = require("../tools");
13
- const install_1 = require("./install");
11
+ const toolchain_1 = require("../native/toolchain");
14
12
  const metro_1 = require("./metro");
15
- const cache_1 = require("./cache");
16
- const path_1 = require("../path");
17
13
  const projectLock_1 = require("../projectLock");
18
- const common_1 = require("../buildHap/common");
14
+ const build_1 = require("../native/build");
15
+ const prepareProject_1 = require("../native/prepareProject");
16
+ const log_1 = require("../log");
17
+ const profile_1 = require("../profile");
19
18
  const BundleName = /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*){2,}$/u;
20
19
  function resolveRunIdentity(plan, options) {
21
20
  if (options.appId && !BundleName.test(options.appId)) {
@@ -29,8 +28,8 @@ function resolveRunIdentity(plan, options) {
29
28
  bundleName: options.appId || plan.bundleName,
30
29
  };
31
30
  }
32
- async function runHarmonyUnlockedAsync(projectRoot, options = {}) {
33
- const normalizedOptions = {
31
+ async function runHarmonyUnlockedAsync(root, options = {}) {
32
+ const settings = {
34
33
  appId: options.appId,
35
34
  device: options.device,
36
35
  interactiveBundler: Boolean(options.interactiveBundler),
@@ -43,109 +42,61 @@ async function runHarmonyUnlockedAsync(projectRoot, options = {}) {
43
42
  variant: options.variant || 'debug',
44
43
  };
45
44
  const steps = {};
46
- await (0, common_1.ensureGeneratedProjectAsync)(projectRoot, {
47
- ...normalizedOptions,
48
- skipGeneratedProjectCheck: true,
45
+ const plan = await (0, prepareProject_1.prepareNativeProjectAsync)(root, {
46
+ ...settings,
47
+ check: false,
49
48
  }, steps);
50
- const plan = await (0, common_1.timed)(steps, 'buildPlan', () => (0, tools_1.resolveHarmonyBuildPlanAsync)(projectRoot, { buildMode: normalizedOptions.variant }));
51
- const identity = resolveRunIdentity(plan, normalizedOptions);
52
- const toolchain = (0, tools_1.resolveHarmonyToolchain)();
53
- (0, common_1.progress)(normalizedOptions, 'Selecting a Harmony device or emulator');
54
- const device = await (0, common_1.timed)(steps, 'device', () => (0, devices_1.selectDeviceAsync)(toolchain.hdc, normalizedOptions.device, {
49
+ const identity = resolveRunIdentity(plan, settings);
50
+ const tools = (0, toolchain_1.resolveHarmonyToolchain)();
51
+ (0, log_1.progress)(settings, 'Selecting a Harmony device or emulator');
52
+ const device = await (0, profile_1.timedAsync)(steps, 'device', () => (0, devices_1.selectDeviceAsync)(tools.hdc, settings.device, {
55
53
  cwd: plan.harmonyRoot,
56
- emulator: (0, tools_1.resolveHarmonyEmulator)(toolchain),
57
- emulatorLogFile: node_path_1.default.join(projectRoot, '.expo', 'harmony', 'emulator.log'),
58
- onProgress: message => (0, common_1.progress)(normalizedOptions, message),
54
+ emulator: (0, toolchain_1.resolveHarmonyEmulator)(tools),
55
+ emulatorLogFile: node_path_1.default.join(root, '.expo', 'harmony', 'emulator.log'),
56
+ onProgress: message => (0, log_1.progress)(settings, message),
59
57
  }));
60
- let exportManifest = null;
61
- if (normalizedOptions.variant === 'release') {
62
- (0, common_1.progress)(normalizedOptions, 'Exporting the release Hermes bundle');
63
- exportManifest = await (0, common_1.timed)(steps, 'export', () => (0, export_1.exportEmbedAsync)(projectRoot, { resetCache: normalizedOptions.resetCache, skipDoctor: true }));
64
- }
65
- else {
66
- steps.export = 0;
67
- }
68
- (0, common_1.progress)(normalizedOptions, 'Installing Harmony project dependencies');
69
- await (0, common_1.timed)(steps, 'ohpm', () => (0, install_1.installHarmonyDependenciesAsync)(plan, toolchain));
58
+ const built = await (0, build_1.buildNativeAsync)(root, plan, tools, settings, steps);
70
59
  let metro = {
71
60
  owner: 'disabled',
72
- port: normalizedOptions.port,
61
+ port: settings.port,
73
62
  stop: async () => { },
74
63
  waitAsync: async () => { },
75
64
  };
76
65
  try {
77
- (0, common_1.progress)(normalizedOptions, 'Checking Harmony native dependency cache');
78
- const nativeBuildCache = await (0, common_1.timed)(steps, 'nativeCache', () => ((0, cache_1.prepareHarmonyNativeBuildCacheAsync)(projectRoot, plan)));
79
- if (nativeBuildCache.changed) {
80
- (0, common_1.progress)(normalizedOptions, 'Invalidated stale Harmony native build objects');
81
- }
82
- (0, common_1.progress)(normalizedOptions, `Building the ${normalizedOptions.variant} HAP`);
83
- const buildEnv = {
84
- ...process.env,
85
- EXPO_HARMONY_NODE: process.env.EXPO_HARMONY_NODE || process.execPath,
86
- EXPO_METRO_TARGET: 'harmony',
87
- HERMES_V1_ENABLED: 'true',
88
- ...(normalizedOptions.variant === 'release' ? { EXPO_HARMONY_BUNDLE_PREBUILT: '1' } : {}),
89
- ...(toolchain.sdkHome && !process.env.DEVECO_SDK_HOME
90
- ? { DEVECO_SDK_HOME: toolchain.sdkHome }
91
- : {}),
92
- };
93
- await (0, common_1.timed)(steps, 'build', () => (0, common_1.runCheckedAsync)(toolchain.hvigor.command, [
94
- ...toolchain.hvigor.args,
95
- ...plan.hvigorArgs,
96
- ], {
97
- code: 'ERR_HARMONY_BUILD_FAILED',
98
- cwd: plan.harmonyRoot,
99
- env: buildEnv,
100
- message: 'Hvigor build',
101
- operation: 'hvigor-build',
102
- timeoutMs: 15 * 60_000,
103
- }));
104
- if (!(0, common_1.isNonEmptyRegularFile)(plan.expectedHap)) {
105
- throw new errors_1.HarmonyCliError('ERR_HARMONY_HAP_MISSING', 'Hvigor completed without producing the expected non-empty regular HAP.', { operation: 'verify-hap' });
106
- }
107
- await (0, common_1.timed)(steps, 'nativeCacheCommit', () => (0, cache_1.commitHarmonyNativeBuildCacheAsync)(nativeBuildCache));
108
- if (normalizedOptions.variant === 'debug') {
109
- (0, common_1.progress)(normalizedOptions, normalizedOptions.noBundler
66
+ if (settings.variant === 'debug') {
67
+ (0, log_1.progress)(settings, settings.noBundler
110
68
  ? 'Connecting to the existing Expo Metro server'
111
69
  : 'Starting Expo Metro');
112
- metro = await (0, common_1.timed)(steps, 'metro', () => normalizedOptions.noBundler
113
- ? (0, metro_1.requireExistingMetroAsync)(normalizedOptions.port)
114
- : (0, metro_1.startExpoMetroAsync)(projectRoot, {
115
- interactive: normalizedOptions.interactiveBundler,
116
- port: normalizedOptions.port,
117
- resetCache: normalizedOptions.resetCache,
70
+ metro = await (0, profile_1.timedAsync)(steps, 'metro', () => settings.noBundler
71
+ ? (0, metro_1.requireExistingMetroAsync)(settings.port)
72
+ : (0, metro_1.startExpoMetroAsync)(root, {
73
+ interactive: settings.interactiveBundler,
74
+ port: settings.port,
75
+ resetCache: settings.resetCache,
118
76
  }));
119
- await (0, common_1.timed)(steps, 'metroPort', () => (0, devices_1.configureMetroPortAsync)(toolchain.hdc, device, normalizedOptions.port, { cwd: plan.harmonyRoot }));
77
+ await (0, profile_1.timedAsync)(steps, 'metroPort', () => (0, devices_1.configureMetroPortAsync)(tools.hdc, device, settings.port, { cwd: plan.harmonyRoot }));
120
78
  }
121
79
  else {
122
80
  steps.metro = 0;
123
81
  steps.metroPort = 0;
124
82
  }
125
- if (normalizedOptions.noInstall) {
83
+ if (settings.noInstall) {
126
84
  steps.install = 0;
127
85
  }
128
86
  else {
129
- (0, common_1.progress)(normalizedOptions, `Installing the HAP on ${device.id}`);
130
- await (0, common_1.timed)(steps, 'install', () => (0, devices_1.installHapAsync)(toolchain.hdc, device, plan.expectedHap, { cwd: plan.harmonyRoot }));
87
+ (0, log_1.progress)(settings, `Installing the HAP on ${device.id}`);
88
+ await (0, profile_1.timedAsync)(steps, 'install', () => (0, devices_1.installHapAsync)(tools.hdc, device, plan.expectedHap, { cwd: plan.harmonyRoot }));
131
89
  }
132
- (0, common_1.progress)(normalizedOptions, `Launching ${identity.bundleName}`);
133
- await (0, common_1.timed)(steps, 'launch', () => (0, devices_1.launchAppAsync)(toolchain.hdc, device, identity.bundleName, identity.abilityName, { cwd: plan.harmonyRoot }));
90
+ (0, log_1.progress)(settings, `Launching ${identity.bundleName}`);
91
+ await (0, profile_1.timedAsync)(steps, 'launch', () => (0, devices_1.launchAppAsync)(tools.hdc, device, identity.bundleName, identity.abilityName, { cwd: plan.harmonyRoot }));
134
92
  const result = {
135
93
  bundleName: identity.bundleName,
136
94
  device: {
137
95
  id: device.id,
138
96
  transport: device.transport,
139
97
  },
140
- export: exportManifest
141
- ? {
142
- assetCount: exportManifest.assets.length,
143
- bundleSha256: exportManifest.bundle.sha256,
144
- sourceMapSha256: exportManifest.sourceMap.sha256,
145
- }
146
- : null,
147
- hapPath: (0, path_1.toPosixPath)(node_path_1.default.relative(projectRoot, plan.expectedHap)),
148
- installed: !normalizedOptions.noInstall,
98
+ ...built,
99
+ installed: !settings.noInstall,
149
100
  launched: true,
150
101
  metro: {
151
102
  owner: metro.owner,
@@ -154,20 +105,24 @@ async function runHarmonyUnlockedAsync(projectRoot, options = {}) {
154
105
  ok: true,
155
106
  schemaVersion: 1,
156
107
  steps,
157
- variant: normalizedOptions.variant,
108
+ variant: settings.variant,
158
109
  };
159
110
  return { metro, result };
160
111
  }
161
112
  catch (error) {
162
113
  await metro.stop();
163
- throw error;
114
+ throw new errors_1.HarmonyCliError(error?.code || 'ERR_HARMONY_UNKNOWN', error?.message || 'Harmony run failed.', {
115
+ cause: error,
116
+ exitCode: error?.exitCode,
117
+ operation: error?.operation,
118
+ });
164
119
  }
165
120
  }
166
- async function runHarmonySessionAsync(projectRoot, options = {}) {
167
- return (0, projectLock_1.withHarmonyProjectLockAsync)(projectRoot, 'run', () => runHarmonyUnlockedAsync(projectRoot, options));
121
+ async function runHarmonySessionAsync(root, options = {}) {
122
+ return (0, projectLock_1.withHarmonyProjectLockAsync)(root, 'run', () => runHarmonyUnlockedAsync(root, options));
168
123
  }
169
- async function runHarmonyAsync(projectRoot, options = {}) {
170
- const session = await runHarmonySessionAsync(projectRoot, options);
124
+ async function runHarmonyAsync(root, options = {}) {
125
+ const session = await runHarmonySessionAsync(root, options);
171
126
  try {
172
127
  return session.result;
173
128
  }
@@ -0,0 +1,2 @@
1
+ import { type Command } from '../command';
2
+ export declare const command: Command;
@@ -0,0 +1,33 @@
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.parseStartArgs, argv, io);
8
+ if (!args)
9
+ return 0;
10
+ const { projectRoot: root, options } = args;
11
+ const { startExpoMetroAsync } = await import('../run/metro.js');
12
+ const metro = await startExpoMetroAsync(root, {
13
+ ...options,
14
+ interactive: true,
15
+ });
16
+ try {
17
+ if (metro.owner === 'existing') {
18
+ io.log(`Expo Metro is already running on port ${metro.port}.`);
19
+ if (options.resetCache) {
20
+ io.warn('Stop the existing Metro server and run this command again to reset its cache.');
21
+ }
22
+ }
23
+ else {
24
+ io.log('\n› Logs for your project will appear below. Press Ctrl+C to exit.');
25
+ await metro.waitAsync();
26
+ }
27
+ return 0;
28
+ }
29
+ finally {
30
+ await metro.stop();
31
+ }
32
+ };
33
+ exports.command = command;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo-harmony/cli",
3
- "version": "55.0.26-harmony.8",
3
+ "version": "55.0.26-harmony.9",
4
4
  "keywords": [
5
5
  "react-native",
6
6
  "expo",
@@ -30,15 +30,25 @@
30
30
  "require": "./build/index.js",
31
31
  "default": "./build/index.js"
32
32
  },
33
- "./bin/expo-harmony": "./build/bin/expo-harmony.js"
33
+ "./bin/expo-harmony": "./build/bin/expo-harmony.js",
34
+ "./native-build": {
35
+ "types": "./build/native/hvigor.d.ts",
36
+ "require": "./build/native/hvigor.js",
37
+ "default": "./build/native/hvigor.js"
38
+ },
39
+ "./internal/prebuild": {
40
+ "types": "./build/internal/prebuild.d.ts",
41
+ "require": "./build/internal/prebuild.js",
42
+ "default": "./build/internal/prebuild.js"
43
+ }
34
44
  },
35
45
  "bin": {
36
46
  "expo-harmony": "build/bin/expo-harmony.js"
37
47
  },
38
48
  "dependencies": {
39
- "@expo-harmony/config-plugins": "55.0.10-harmony.2",
49
+ "@expo-harmony/config-plugins": "55.0.10-harmony.3",
40
50
  "@expo-harmony/expo-modules-autolinking": "55.0.25-harmony.3",
41
- "@expo-harmony/prebuild-config": "55.0.0-harmony.6",
51
+ "@expo-harmony/prebuild-config": "55.0.0-harmony.7",
42
52
  "@expo/config": "55.0.17",
43
53
  "cross-spawn": "^7.0.6",
44
54
  "json5": "2.2.3"
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { main } from '../cli';
3
+ import { mainAsync } from '../cli';
4
4
 
5
- main();
5
+ mainAsync();
@@ -1,39 +1,16 @@
1
- import path from 'node:path';
2
-
3
- import { HarmonyCliError } from '../errors';
4
- import { exportEmbedAsync } from '../exportEmbed/export';
5
- import type { HarmonyExportManifest } from '../exportEmbed/manifest';
6
- import { toPosixPath } from '../path';
7
1
  import { withHarmonyProjectLockAsync } from '../projectLock';
8
- import {
9
- commitHarmonyNativeBuildCacheAsync,
10
- prepareHarmonyNativeBuildCacheAsync,
11
- } from '../run/cache';
12
- import { installHarmonyDependenciesAsync } from '../run/install';
13
- import {
14
- resolveHarmonyBuildPlanAsync,
15
- resolveHarmonyToolchain,
16
- } from '../tools';
17
- import {
18
- ensureGeneratedProjectAsync,
19
- isNonEmptyRegularFile,
20
- progress,
21
- runCheckedAsync,
22
- timed,
23
- } from './common';
2
+ import { buildNativeAsync, type NativeBuildResult } from '../native/build';
3
+ import { prepareNativeProjectAsync } from '../native/prepareProject';
4
+ import { resolveHarmonyToolchain } from '../native/toolchain';
24
5
 
25
6
  export interface HarmonyBuildOptions {
26
7
  io?: Pick<Console, 'error' | 'log' | 'warn'>;
27
- /** @internal For release verification after a successful isolated prebuild check. */
28
- skipGeneratedProjectCheck?: boolean;
29
8
  sync?: boolean;
30
9
  variant?: 'debug' | 'release';
31
10
  }
32
11
 
33
- export interface HarmonyBuildResult {
12
+ export interface HarmonyBuildResult extends NativeBuildResult {
34
13
  bundleName: string;
35
- export: null | { assetCount: number; bundleSha256: string; sourceMapSha256: string };
36
- hapPath: string;
37
14
  headless: true;
38
15
  installed: false;
39
16
  launched: false;
@@ -48,13 +25,12 @@ type NormalizedBuildOptions = Required<HarmonyBuildOptions> & {
48
25
  };
49
26
 
50
27
  async function buildHarmonyUnlockedAsync(
51
- projectRoot: string,
28
+ root: string,
52
29
  options: HarmonyBuildOptions = {}
53
30
  ): Promise<HarmonyBuildResult> {
54
- const normalizedOptions: NormalizedBuildOptions = {
31
+ const settings: NormalizedBuildOptions = {
55
32
  io: options.io || console,
56
33
  requireDeviceTools: false,
57
- skipGeneratedProjectCheck: Boolean(options.skipGeneratedProjectCheck),
58
34
  sync: Boolean(options.sync),
59
35
  variant: options.variant || 'debug',
60
36
  };
@@ -66,98 +42,30 @@ async function buildHarmonyUnlockedAsync(
66
42
  metroPort: 0,
67
43
  };
68
44
 
69
- await ensureGeneratedProjectAsync(projectRoot, normalizedOptions, steps);
70
-
71
- const plan = await timed(steps, 'buildPlan', () => resolveHarmonyBuildPlanAsync(
72
- projectRoot,
73
- { buildMode: normalizedOptions.variant }
74
- ));
75
- const toolchain = resolveHarmonyToolchain();
76
-
77
- let exportManifest: HarmonyExportManifest | null = null;
78
- if (normalizedOptions.variant === 'release') {
79
- progress(normalizedOptions, 'Exporting the release Hermes bundle');
80
- exportManifest = await timed(steps, 'export', () => exportEmbedAsync(
81
- projectRoot,
82
- { skipDoctor: true }
83
- ));
84
- } else {
85
- steps.export = 0;
86
- }
87
-
88
- progress(normalizedOptions, 'Installing Harmony project dependencies');
89
- await timed(steps, 'ohpm', () => installHarmonyDependenciesAsync(plan, toolchain));
90
-
91
- progress(normalizedOptions, 'Checking Harmony native dependency cache');
92
- const nativeBuildCache = await timed(steps, 'nativeCache', () => (
93
- prepareHarmonyNativeBuildCacheAsync(projectRoot, plan)
94
- ));
95
-
96
- if (nativeBuildCache.changed) {
97
- progress(normalizedOptions, 'Invalidated stale Harmony native build objects');
98
- }
99
-
100
- progress(normalizedOptions, `Building the ${normalizedOptions.variant} HAP without a device`);
101
- const buildEnv = {
102
- ...process.env,
103
- EXPO_HARMONY_NODE: process.env.EXPO_HARMONY_NODE || process.execPath,
104
- EXPO_METRO_TARGET: 'harmony',
105
- HERMES_V1_ENABLED: 'true',
106
- ...(normalizedOptions.variant === 'release' ? { EXPO_HARMONY_BUNDLE_PREBUILT: '1' } : {}),
107
- ...(toolchain.sdkHome && !process.env.DEVECO_SDK_HOME
108
- ? { DEVECO_SDK_HOME: toolchain.sdkHome }
109
- : {}),
110
- };
111
- await timed(steps, 'build', () => runCheckedAsync(toolchain.hvigor.command, [
112
- ...toolchain.hvigor.args,
113
- ...plan.hvigorArgs,
114
- ], {
115
- code: 'ERR_HARMONY_BUILD_FAILED',
116
- cwd: plan.harmonyRoot,
117
- env: buildEnv,
118
- message: 'Hvigor build',
119
- operation: 'hvigor-build',
120
- timeoutMs: 15 * 60_000,
121
- }));
122
-
123
- if (!isNonEmptyRegularFile(plan.expectedHap)) {
124
- throw new HarmonyCliError(
125
- 'ERR_HARMONY_HAP_MISSING',
126
- 'Hvigor completed without producing the expected non-empty regular HAP.',
127
- { operation: 'verify-hap' }
128
- );
129
- }
130
-
131
- await timed(steps, 'nativeCacheCommit', () => commitHarmonyNativeBuildCacheAsync(nativeBuildCache));
45
+ const plan = await prepareNativeProjectAsync(root, settings, steps);
46
+ const built = await buildNativeAsync(root, plan, resolveHarmonyToolchain(), settings, steps);
132
47
 
133
48
  return {
134
49
  bundleName: plan.bundleName,
135
- export: exportManifest
136
- ? {
137
- assetCount: exportManifest.assets.length,
138
- bundleSha256: exportManifest.bundle.sha256,
139
- sourceMapSha256: exportManifest.sourceMap.sha256,
140
- }
141
- : null,
142
- hapPath: toPosixPath(path.relative(projectRoot, plan.expectedHap)),
50
+ ...built,
143
51
  headless: true,
144
52
  installed: false,
145
53
  launched: false,
146
54
  ok: true,
147
55
  schemaVersion: 1,
148
56
  steps,
149
- variant: normalizedOptions.variant,
57
+ variant: settings.variant,
150
58
  };
151
59
  }
152
60
 
153
61
  async function buildHarmonyAsync(
154
- projectRoot: string,
62
+ root: string,
155
63
  options: HarmonyBuildOptions = {}
156
64
  ): Promise<HarmonyBuildResult> {
157
65
  return withHarmonyProjectLockAsync(
158
- projectRoot,
66
+ root,
159
67
  'build',
160
- () => buildHarmonyUnlockedAsync(projectRoot, options)
68
+ () => buildHarmonyUnlockedAsync(root, options)
161
69
  );
162
70
  }
163
71
 
@@ -0,0 +1,19 @@
1
+ import { parseCommandArgs, type Command } from '../command';
2
+ import { parseBuildArgs } from './options';
3
+
4
+ export const command: Command = async (argv, io) => {
5
+ const args = parseCommandArgs(parseBuildArgs, argv, io);
6
+ if (!args) return 0;
7
+
8
+ const { projectRoot: root, options } = args;
9
+ const { buildHarmonyAsync } = await import('./build.js');
10
+
11
+ const result = await buildHarmonyAsync(root, {
12
+ ...options,
13
+ io,
14
+ });
15
+
16
+ io.log(`Built ${result.variant} HAP at ${result.hapPath}; no device actions were performed.`);
17
+
18
+ return 0;
19
+ };