@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.
Files changed (185) hide show
  1. package/README.md +87 -38
  2. package/build/args.d.ts +19 -0
  3. package/build/args.js +27 -0
  4. package/build/bin/expo-harmony.d.ts +2 -0
  5. package/build/bin/expo-harmony.js +1 -1
  6. package/build/buildHap/build.d.ts +18 -0
  7. package/build/buildHap/build.js +38 -0
  8. package/build/buildHap/index.d.ts +2 -0
  9. package/build/buildHap/index.js +19 -0
  10. package/build/buildHap/options.d.ts +7 -0
  11. package/build/buildHap/options.js +30 -0
  12. package/build/cli.d.ts +3 -0
  13. package/build/cli.js +40 -0
  14. package/build/command.d.ts +9 -0
  15. package/build/command.js +20 -0
  16. package/build/development/manifest.d.ts +1 -0
  17. package/build/development/manifest.js +121 -0
  18. package/build/development/protocol.d.ts +3 -0
  19. package/build/development/protocol.js +33 -0
  20. package/build/development/session.d.ts +5 -0
  21. package/build/development/session.js +66 -0
  22. package/build/doctor/doctor.d.ts +20 -0
  23. package/build/doctor/doctor.js +226 -0
  24. package/build/doctor/index.d.ts +2 -0
  25. package/build/doctor/index.js +16 -0
  26. package/build/doctor/options.d.ts +5 -0
  27. package/build/doctor/options.js +15 -0
  28. package/build/entry.d.ts +2 -0
  29. package/build/entry.js +47 -0
  30. package/build/errors.d.ts +12 -0
  31. package/build/errors.js +16 -0
  32. package/build/expo.d.ts +19 -0
  33. package/build/expo.js +53 -0
  34. package/build/exportEmbed/export.d.ts +18 -0
  35. package/build/exportEmbed/export.js +124 -0
  36. package/build/exportEmbed/index.d.ts +2 -0
  37. package/build/exportEmbed/index.js +19 -0
  38. package/build/exportEmbed/manifest.d.ts +44 -0
  39. package/build/exportEmbed/manifest.js +171 -0
  40. package/build/exportEmbed/options.d.ts +7 -0
  41. package/build/exportEmbed/options.js +22 -0
  42. package/build/file.d.ts +10 -0
  43. package/build/file.js +110 -0
  44. package/build/help.d.ts +1 -0
  45. package/build/help.js +44 -0
  46. package/build/index.d.ts +14 -0
  47. package/build/index.js +6 -5
  48. package/build/internal/development.d.ts +1 -0
  49. package/build/internal/development.js +8 -0
  50. package/build/internal/export.d.ts +1 -0
  51. package/build/internal/export.js +80 -0
  52. package/build/internal/prebuild.d.ts +3 -0
  53. package/build/internal/prebuild.js +9 -0
  54. package/build/log.d.ts +4 -0
  55. package/build/log.js +6 -0
  56. package/build/modules/index.d.ts +2 -0
  57. package/build/modules/index.js +16 -0
  58. package/build/modules/modules.d.ts +42 -0
  59. package/build/modules/modules.js +84 -0
  60. package/build/modules/options.d.ts +10 -0
  61. package/build/modules/options.js +45 -0
  62. package/build/native/bare.d.ts +4 -0
  63. package/build/native/bare.js +130 -0
  64. package/build/native/build.d.ts +15 -0
  65. package/build/native/build.js +69 -0
  66. package/build/native/cache.d.ts +11 -0
  67. package/build/native/cache.js +73 -0
  68. package/build/native/hvigor.d.ts +3 -0
  69. package/build/native/hvigor.js +83 -0
  70. package/build/native/install.d.ts +5 -0
  71. package/build/native/install.js +13 -0
  72. package/build/native/prepareProject.d.ts +11 -0
  73. package/build/native/prepareProject.js +68 -0
  74. package/build/native/project.d.ts +8 -0
  75. package/build/native/project.js +58 -0
  76. package/build/native/toolchain.d.ts +16 -0
  77. package/build/native/toolchain.js +172 -0
  78. package/build/native/types.d.ts +47 -0
  79. package/build/native/types.js +2 -0
  80. package/build/path.d.ts +4 -0
  81. package/build/path.js +27 -0
  82. package/build/prebuild/check.d.ts +10 -0
  83. package/build/prebuild/check.js +151 -0
  84. package/build/prebuild/clean.d.ts +2 -0
  85. package/build/prebuild/clean.js +50 -0
  86. package/build/prebuild/index.d.ts +2 -0
  87. package/build/prebuild/index.js +51 -0
  88. package/build/prebuild/options.d.ts +10 -0
  89. package/build/prebuild/options.js +50 -0
  90. package/build/prebuild/prebuild.d.ts +6 -0
  91. package/build/prebuild/prebuild.js +70 -0
  92. package/build/prebuild/template.d.ts +8 -0
  93. package/build/prebuild/template.js +86 -0
  94. package/build/process.d.ts +41 -0
  95. package/build/process.js +324 -0
  96. package/build/profile.d.ts +1 -0
  97. package/build/profile.js +12 -0
  98. package/build/project.d.ts +2 -0
  99. package/build/project.js +23 -0
  100. package/build/projectLock.d.ts +2 -0
  101. package/build/projectLock.js +185 -0
  102. package/build/run/devices.d.ts +29 -0
  103. package/build/run/devices.js +223 -0
  104. package/build/run/emulators.d.ts +13 -0
  105. package/build/run/emulators.js +82 -0
  106. package/build/run/index.d.ts +2 -0
  107. package/build/run/index.js +25 -0
  108. package/build/run/metro.d.ts +21 -0
  109. package/build/run/metro.js +162 -0
  110. package/build/run/options.d.ts +14 -0
  111. package/build/run/options.js +62 -0
  112. package/build/run/run.d.ts +46 -0
  113. package/build/run/run.js +135 -0
  114. package/build/runtime/config.d.ts +37 -0
  115. package/build/runtime/config.js +169 -0
  116. package/build/runtime/contract.d.ts +5 -0
  117. package/build/runtime/contract.js +109 -0
  118. package/build/runtime/fingerprint.d.ts +1 -0
  119. package/build/runtime/fingerprint.js +49 -0
  120. package/build/runtime/index.d.ts +2 -0
  121. package/build/runtime/index.js +50 -0
  122. package/build/start/index.d.ts +2 -0
  123. package/build/start/index.js +38 -0
  124. package/build/start/options.d.ts +9 -0
  125. package/build/start/options.js +38 -0
  126. package/build/updates/export.d.ts +14 -0
  127. package/build/updates/export.js +46 -0
  128. package/build/updates/index.d.ts +2 -0
  129. package/build/updates/index.js +96 -0
  130. package/build/upstream.d.ts +9 -0
  131. package/build/upstream.js +17 -0
  132. package/package.json +21 -5
  133. package/src/bin/expo-harmony.ts +2 -2
  134. package/src/buildHap/build.ts +13 -105
  135. package/src/buildHap/index.ts +19 -0
  136. package/src/cli.ts +27 -225
  137. package/src/command.ts +24 -0
  138. package/src/development/manifest.ts +127 -0
  139. package/src/development/protocol.ts +42 -0
  140. package/src/development/session.ts +83 -0
  141. package/src/doctor/doctor.ts +67 -62
  142. package/src/doctor/index.ts +16 -0
  143. package/src/exportEmbed/export.ts +73 -72
  144. package/src/exportEmbed/index.ts +17 -0
  145. package/src/file.ts +20 -11
  146. package/src/help.ts +41 -0
  147. package/src/index.ts +5 -3
  148. package/src/internal/development.ts +8 -0
  149. package/src/internal/export.ts +82 -0
  150. package/src/internal/prebuild.ts +3 -0
  151. package/src/log.ts +5 -0
  152. package/src/modules/index.ts +16 -0
  153. package/src/native/bare.ts +145 -0
  154. package/src/native/build.ts +83 -0
  155. package/src/{run → native}/cache.ts +10 -9
  156. package/src/native/hvigor.ts +88 -0
  157. package/src/native/install.ts +17 -0
  158. package/src/native/prepareProject.ts +99 -0
  159. package/src/native/project.ts +88 -0
  160. package/src/{tools.ts → native/toolchain.ts} +38 -140
  161. package/src/native/types.ts +44 -0
  162. package/src/prebuild/check.ts +56 -31
  163. package/src/prebuild/clean.ts +3 -3
  164. package/src/prebuild/index.ts +56 -0
  165. package/src/prebuild/prebuild.ts +11 -1
  166. package/src/prebuild/template.ts +2 -2
  167. package/src/process.ts +175 -64
  168. package/src/profile.ts +13 -0
  169. package/src/projectLock.ts +9 -1
  170. package/src/run/devices.ts +24 -11
  171. package/src/run/emulators.ts +4 -6
  172. package/src/run/index.ts +25 -0
  173. package/src/run/metro.ts +10 -1
  174. package/src/run/options.ts +2 -0
  175. package/src/run/run.ts +63 -129
  176. package/src/runtime/config.ts +207 -0
  177. package/src/runtime/contract.ts +167 -0
  178. package/src/runtime/fingerprint.ts +49 -0
  179. package/src/runtime/index.ts +54 -0
  180. package/src/start/index.ts +37 -0
  181. package/src/start/options.ts +12 -0
  182. package/src/updates/export.ts +60 -0
  183. package/src/updates/index.ts +89 -0
  184. package/src/buildHap/common.ts +0 -145
  185. package/src/run/install.ts +0 -41
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseStartArgs = parseStartArgs;
4
+ const args_1 = require("../args");
5
+ const errors_1 = require("../errors");
6
+ const StartOptions = {
7
+ ...args_1.CommonOptions,
8
+ 'clear': { short: 'c', type: 'boolean' },
9
+ 'host': { type: 'string' },
10
+ 'port': { type: 'string' },
11
+ 'private-key-path': { type: 'string' },
12
+ 'reset-cache': { type: 'boolean' },
13
+ };
14
+ function parseStartArgs(argv) {
15
+ const { positionals, values } = (0, args_1.parseArgs)(StartOptions, argv);
16
+ if (positionals.length > 1) {
17
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Unexpected start positional argument: ${positionals[1]}`, {
18
+ operation: 'parse-arguments',
19
+ });
20
+ }
21
+ const port = values.port === undefined ? 8081 : Number(values.port);
22
+ if (!Number.isInteger(port) || port < 1 || port > 65_535) {
23
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--port must be an integer between 1 and 65535.', {
24
+ operation: 'parse-arguments',
25
+ });
26
+ }
27
+ if (values.host !== undefined && !/^[A-Za-z0-9.-]+$/.test(values.host)) {
28
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--host must be a hostname or IPv4 address without a port or scheme.', { operation: 'parse-arguments' });
29
+ }
30
+ return {
31
+ help: Boolean(values.help),
32
+ host: values.host,
33
+ port,
34
+ privateKeyPath: values['private-key-path'],
35
+ project: positionals[0],
36
+ resetCache: Boolean(values['reset-cache'] || values.clear),
37
+ };
38
+ }
@@ -0,0 +1,14 @@
1
+ import type { ExpoConfigWithHarmony } from '@expo-harmony/config-plugins';
2
+ export interface UpdateAsset {
3
+ key: string;
4
+ hash: string;
5
+ contentType: string;
6
+ fileExtension: string;
7
+ embeddedAssetFilename: string;
8
+ }
9
+ export declare function publishUpdatesConfigurationAsync(root: string, directory: string, config: ExpoConfigWithHarmony, runtime: string): Promise<void>;
10
+ export declare function publishEmbeddedUpdateAsync(root: string, paths: {
11
+ rawfileRoot: string;
12
+ bundle: string;
13
+ metadataRoot: string;
14
+ }, assets: UpdateAsset[]): Promise<void>;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.publishUpdatesConfigurationAsync = publishUpdatesConfigurationAsync;
7
+ exports.publishEmbeddedUpdateAsync = publishEmbeddedUpdateAsync;
8
+ const node_crypto_1 = __importDefault(require("node:crypto"));
9
+ const promises_1 = __importDefault(require("node:fs/promises"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const file_1 = require("../file");
12
+ const errors_1 = require("../errors");
13
+ const config_1 = require("../runtime/config");
14
+ async function publishUpdatesConfigurationAsync(root, directory, config, runtime) {
15
+ const updates = { ...config.updates, runtimeVersion: runtime };
16
+ if (updates.url) {
17
+ const url = new URL(updates.url);
18
+ if (!['http:', 'https:'].includes(url.protocol))
19
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_UPDATES_CONFIG', 'Updates URL must use HTTP or HTTPS.', { operation: 'updates-config' });
20
+ }
21
+ if (updates.codeSigningCertificate) {
22
+ const certificate = await promises_1.default.readFile(node_path_1.default.resolve(root, updates.codeSigningCertificate), 'utf8');
23
+ new node_crypto_1.default.X509Certificate(certificate);
24
+ updates.codeSigningCertificate = certificate;
25
+ }
26
+ await (0, file_1.atomicWriteJson)(updates, node_path_1.default.join(directory, 'expo-updates/config.json'), directory);
27
+ }
28
+ async function publishEmbeddedUpdateAsync(root, paths, assets) {
29
+ const project = await (0, config_1.readProjectRuntimeAsync)(root);
30
+ const bundle = await promises_1.default.readFile(paths.bundle);
31
+ const hash = node_crypto_1.default.createHash('sha256').update(Uint8Array.from(bundle)).digest('base64url');
32
+ const directory = node_path_1.default.join(paths.rawfileRoot, 'expo-updates');
33
+ const id = node_crypto_1.default.randomUUID();
34
+ const manifest = {
35
+ id, commitTime: Date.now(),
36
+ launchAsset: { packagerHash: `bundle-${id}`, hash, type: 'hbc',
37
+ embeddedAssetFilename: node_path_1.default.relative(paths.rawfileRoot, paths.bundle).split(node_path_1.default.sep).join('/') },
38
+ assets: [...new Map(assets.map(asset => [asset.key, asset])).values()].map(asset => ({
39
+ packagerHash: asset.key, hash: asset.hash, type: asset.fileExtension.replace(/^\./, ''),
40
+ embeddedAssetFilename: asset.embeddedAssetFilename,
41
+ })),
42
+ };
43
+ await publishUpdatesConfigurationAsync(root, paths.rawfileRoot, project.config, project.runtimeVersion);
44
+ await (0, file_1.atomicWriteJson)(manifest, node_path_1.default.join(directory, 'manifest.json'), paths.rawfileRoot);
45
+ await (0, file_1.atomicWriteJson)(manifest, node_path_1.default.join(paths.metadataRoot, 'update.json'), paths.metadataRoot);
46
+ }
@@ -0,0 +1,2 @@
1
+ import { type Command } from '../command';
2
+ export declare const command: Command;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.command = void 0;
7
+ const node_crypto_1 = __importDefault(require("node:crypto"));
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const node_os_1 = __importDefault(require("node:os"));
10
+ const node_module_1 = require("node:module");
11
+ const config_1 = require("@expo/config");
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const args_1 = require("../args");
14
+ const command_1 = require("../command");
15
+ const export_1 = require("../exportEmbed/export");
16
+ const errors_1 = require("../errors");
17
+ const file_1 = require("../file");
18
+ const path_1 = require("../path");
19
+ const expo_1 = require("../expo");
20
+ const config_2 = require("../runtime/config");
21
+ const command = async (argv, io) => {
22
+ const args = (0, command_1.parseCommandArgs)((values) => {
23
+ const { values: options, positionals } = (0, args_1.parseArgs)({ ...args_1.CommonOptions,
24
+ 'output-dir': { type: 'string' }, 'asset-url': { type: 'string' },
25
+ 'private-key-path': { type: 'string' }, 'reset-cache': { type: 'boolean' },
26
+ }, values);
27
+ if (positionals.length > 1)
28
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_ARGUMENTS', 'Expected at most one project directory.', { operation: 'export' });
29
+ return { ...options, project: positionals[0], help: Boolean(options.help) };
30
+ }, argv, io);
31
+ if (!args)
32
+ return 0;
33
+ const { projectRoot: root, options } = args;
34
+ const base = options['asset-url'];
35
+ if (!base || !['http:', 'https:'].includes(new URL(base).protocol)) {
36
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_ARGUMENTS', 'OTA export requires --asset-url with the HTTP(S) directory that will host its assets.', { operation: 'export' });
37
+ }
38
+ const output = node_path_1.default.resolve(root, options['output-dir'] ?? 'dist');
39
+ const temporary = await promises_1.default.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'expo-harmony-update-'));
40
+ try {
41
+ await (0, export_1.exportEmbedAsync)(root, { outputDirectory: temporary, resetCache: options['reset-cache'] });
42
+ const embedded = JSON.parse(await promises_1.default.readFile(node_path_1.default.join(temporary, 'update.json'), 'utf8'));
43
+ const project = await (0, config_2.readProjectRuntimeAsync)(root);
44
+ const { exp } = (0, config_1.getConfig)(root, { isPublicConfig: true });
45
+ const require = (0, node_module_1.createRequire)((0, expo_1.resolveExpoCli)(root).cliPath);
46
+ const upstream = (0, node_module_1.createRequire)(require.resolve('@expo/cli/package.json'));
47
+ const mime = upstream('mime-types');
48
+ const assets = [embedded.launchAsset, ...embedded.assets].map(asset => ({
49
+ key: asset.packagerHash, hash: asset.hash,
50
+ contentType: asset.type === 'hbc' ? 'application/javascript' : mime.lookup(asset.type) || 'application/octet-stream',
51
+ fileExtension: asset.type ? '.' + asset.type : '', embeddedAssetFilename: asset.embeddedAssetFilename,
52
+ }));
53
+ const manifest = {
54
+ id: embedded.id, createdAt: new Date(embedded.commitTime).toISOString(), runtimeVersion: project.runtimeVersion,
55
+ launchAsset: assets[0], assets: assets.slice(1), metadata: {},
56
+ extra: { expoClient: exp, eas: { projectId: exp.extra?.eas?.projectId },
57
+ scopeKey: exp.originalFullName ?? `@${exp.owner ?? 'anonymous'}/${exp.slug}` },
58
+ };
59
+ const metadata = [];
60
+ for (const asset of assets) {
61
+ const relative = `assets/${asset.hash}${asset.fileExtension}`;
62
+ await (0, file_1.atomicCopy)(node_path_1.default.join(temporary, 'embedded', (0, path_1.assertSafeRelative)(asset.embeddedAssetFilename, 'Embedded update asset')), node_path_1.default.join(output, relative), output);
63
+ Object.assign(asset, { url: new URL(relative, base.endsWith('/') ? base : base + '/').href });
64
+ delete asset.embeddedAssetFilename;
65
+ if (asset !== manifest.launchAsset)
66
+ metadata.push({ path: relative, ext: asset.fileExtension.replace(/^\./, '') });
67
+ }
68
+ await (0, file_1.atomicCopy)(node_path_1.default.join(temporary, 'hermes_bundle.hbc.map'), node_path_1.default.join(output, 'bundle.hbc.map'), output);
69
+ await (0, file_1.atomicWriteJson)({ version: 0, bundler: 'metro', fileMetadata: { harmony: {
70
+ bundle: `assets/${manifest.launchAsset.hash}.hbc`, assets: metadata,
71
+ } } }, node_path_1.default.join(output, 'metadata.json'), output);
72
+ await (0, file_1.atomicWriteJson)(manifest, node_path_1.default.join(output, 'manifest.json'), output);
73
+ if (options['private-key-path']) {
74
+ const key = await promises_1.default.readFile(node_path_1.default.resolve(root, options['private-key-path']), 'utf8');
75
+ if (node_crypto_1.default.createPrivateKey(key).asymmetricKeyType !== 'rsa')
76
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_UPDATES_SIGNING', 'Expo Updates signing requires an RSA private key.', { operation: 'export' });
77
+ const body = await promises_1.default.readFile(node_path_1.default.join(output, 'manifest.json'));
78
+ const signature = node_crypto_1.default.sign('RSA-SHA256', Uint8Array.from(body), key).toString('base64');
79
+ const id = exp.updates?.codeSigningMetadata?.keyid ?? 'root';
80
+ const { serializeDictionary } = upstream('structured-headers');
81
+ await promises_1.default.writeFile(node_path_1.default.join(temporary, 'signature.txt'), serializeDictionary(new Map([
82
+ ['sig', [signature, new Map()]], ['keyid', [id, new Map()]], ['alg', ['rsa-v1_5-sha256', new Map()]],
83
+ ])));
84
+ await (0, file_1.atomicCopy)(node_path_1.default.join(temporary, 'signature.txt'), node_path_1.default.join(output, 'signature.txt'), output);
85
+ }
86
+ else {
87
+ await promises_1.default.rm(node_path_1.default.join(output, 'signature.txt'), { force: true });
88
+ }
89
+ io.log(`Exported Harmony update ${manifest.id} for runtime ${manifest.runtimeVersion} to ${output}.`);
90
+ }
91
+ finally {
92
+ await promises_1.default.rm(temporary, { recursive: true, force: true });
93
+ }
94
+ return 0;
95
+ };
96
+ exports.command = command;
@@ -0,0 +1,9 @@
1
+ declare const ProjectPackages: Readonly<{
2
+ expo: "expo";
3
+ expoAutolinking: "@expo-harmony/expo-modules-autolinking";
4
+ reactNative: "react-native";
5
+ rnohCli: "@react-native-oh/react-native-harmony-cli";
6
+ rnohRuntime: "@react-native-oh/react-native-harmony";
7
+ }>;
8
+ declare const RequiredProjectPackages: readonly ("@react-native-oh/react-native-harmony" | "@expo-harmony/expo-modules-autolinking" | "@react-native-oh/react-native-harmony-cli")[];
9
+ export { ProjectPackages, RequiredProjectPackages, };
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RequiredProjectPackages = exports.ProjectPackages = void 0;
4
+ const ProjectPackages = Object.freeze({
5
+ expo: 'expo',
6
+ expoAutolinking: '@expo-harmony/expo-modules-autolinking',
7
+ reactNative: 'react-native',
8
+ rnohCli: '@react-native-oh/react-native-harmony-cli',
9
+ rnohRuntime: '@react-native-oh/react-native-harmony',
10
+ });
11
+ exports.ProjectPackages = ProjectPackages;
12
+ const RequiredProjectPackages = Object.freeze([
13
+ ProjectPackages.rnohRuntime,
14
+ ProjectPackages.rnohCli,
15
+ ProjectPackages.expoAutolinking,
16
+ ]);
17
+ exports.RequiredProjectPackages = RequiredProjectPackages;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo-harmony/cli",
3
- "version": "55.0.26-harmony.1",
3
+ "version": "55.0.26-harmony.11",
4
4
  "keywords": [
5
5
  "react-native",
6
6
  "expo",
@@ -30,16 +30,32 @@
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
+ },
44
+ "./internal/development": {
45
+ "types": "./build/internal/development.d.ts",
46
+ "require": "./build/internal/development.js",
47
+ "default": "./build/internal/development.js"
48
+ }
34
49
  },
35
50
  "bin": {
36
51
  "expo-harmony": "build/bin/expo-harmony.js"
37
52
  },
38
53
  "dependencies": {
39
- "@expo-harmony/config-plugins": "55.0.10-harmony.1",
40
- "@expo-harmony/expo-modules-autolinking": "55.0.25-harmony.1",
41
- "@expo-harmony/prebuild-config": "55.0.0-harmony.1",
54
+ "@expo-harmony/config-plugins": "55.0.10-harmony.5",
55
+ "@expo-harmony/expo-modules-autolinking": "55.0.25-harmony.5",
56
+ "@expo-harmony/prebuild-config": "55.0.0-harmony.9",
42
57
  "@expo/config": "55.0.17",
58
+ "@expo/fingerprint": "0.16.7",
43
59
  "cross-spawn": "^7.0.6",
44
60
  "json5": "2.2.3"
45
61
  },
@@ -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
+ };