@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,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HarmonyManifestPath = void 0;
4
+ exports.canonicalHarmonyManifestURL = canonicalHarmonyManifestURL;
5
+ exports.createHarmonyLaunchLink = createHarmonyLaunchLink;
6
+ const errors_1 = require("../errors");
7
+ exports.HarmonyManifestPath = '/manifest';
8
+ function canonicalHarmonyManifestURL(value) {
9
+ let url;
10
+ try {
11
+ url = new URL(value);
12
+ decodeURIComponent(url.search);
13
+ }
14
+ catch (cause) {
15
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_MANIFEST_URL', 'Expected an absolute HTTP(S) Harmony manifest URL.', { cause, operation: 'development-manifest' });
16
+ }
17
+ if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.hash
18
+ || !['/', exports.HarmonyManifestPath, '/index.exp'].includes(url.pathname)
19
+ || url.searchParams.getAll('platform').length !== 1 || url.searchParams.get('platform') !== 'harmony') {
20
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_MANIFEST_URL', 'Expected an HTTP(S) Expo manifest URL with platform=harmony and without credentials or a fragment.', { operation: 'development-manifest' });
21
+ }
22
+ url.pathname = exports.HarmonyManifestPath;
23
+ // Keep all other query values (including repetitions) in order. Platform has
24
+ // one canonical position, and URLSearchParams normalizes equivalent encoding.
25
+ url.searchParams.delete('platform');
26
+ url.searchParams.append('platform', 'harmony');
27
+ return url.toString();
28
+ }
29
+ function createHarmonyLaunchLink(manifest) {
30
+ const url = new URL('expo-harmony://open');
31
+ url.searchParams.set('url', canonicalHarmonyManifestURL(manifest));
32
+ return url.toString();
33
+ }
@@ -0,0 +1,5 @@
1
+ export declare function readDevelopmentSessionAsync(root: string, port: number, hostname?: string): Promise<{
2
+ manifestUrl: string;
3
+ launchLink: string;
4
+ bundleUrl: string;
5
+ }>;
@@ -0,0 +1,66 @@
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.readDevelopmentSessionAsync = readDevelopmentSessionAsync;
7
+ const node_http_1 = __importDefault(require("node:http"));
8
+ const node_os_1 = __importDefault(require("node:os"));
9
+ const errors_1 = require("../errors");
10
+ const config_1 = require("../runtime/config");
11
+ const protocol_1 = require("./protocol");
12
+ async function readDevelopmentSessionAsync(root, port, hostname) {
13
+ const project = await (0, config_1.readProjectRuntimeAsync)(root);
14
+ const { config } = project;
15
+ const host = hostname || process.env.REACT_NATIVE_PACKAGER_HOSTNAME
16
+ || Object.values(node_os_1.default.networkInterfaces()).flat().find(item => item?.family === 'IPv4' && !item.internal)?.address
17
+ || '127.0.0.1';
18
+ if (!/^[A-Za-z0-9.-]+$/.test(host)) {
19
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_MANIFEST_HOST', 'Harmony development requires an HTTP LAN hostname or IPv4 address.', { operation: 'development-manifest' });
20
+ }
21
+ const manifest = await new Promise((resolve, reject) => {
22
+ const request = node_http_1.default.get({
23
+ host: '127.0.0.1',
24
+ port,
25
+ path: `${protocol_1.HarmonyManifestPath}?platform=harmony`,
26
+ headers: { 'host': `${host}:${port}`, 'accept': 'application/json', 'expo-platform': 'harmony', 'expo-protocol-version': '0' },
27
+ }, (response) => {
28
+ let body = '';
29
+ response.setEncoding('utf8');
30
+ response.on('data', (chunk) => {
31
+ body += chunk;
32
+ if (body.length > 2 * 1024 * 1024)
33
+ request.destroy(new Error('Harmony manifest exceeded 2 MB.'));
34
+ });
35
+ response.on('error', reject);
36
+ response.on('end', () => {
37
+ try {
38
+ const value = JSON.parse(body);
39
+ if (response.statusCode !== 200 || response.headers['expo-protocol-version'] !== '0'
40
+ || !response.headers['content-type']?.startsWith('application/json')) {
41
+ reject(new Error(value.error?.message || 'Metro did not return an Expo development manifest with protocol version 0.'));
42
+ return;
43
+ }
44
+ resolve(value);
45
+ }
46
+ catch (cause) {
47
+ reject(new Error('Metro did not return a valid Harmony manifest.', { cause }));
48
+ }
49
+ });
50
+ });
51
+ request.setTimeout(60_000, () => request.destroy(new Error('Harmony manifest request timed out.')));
52
+ request.on('error', reject);
53
+ }).catch((cause) => {
54
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_METRO_MANIFEST', `Cannot use this Metro server: ${cause.message}`, { cause, operation: 'development-manifest' });
55
+ });
56
+ const identity = config.extra?.eas?.projectId || `@${config.owner || 'anonymous'}/${config.slug}/${project.bundleName}`;
57
+ if (manifest.extra?.harmony?.projectId !== identity
58
+ || manifest.runtimeVersion !== project.runtimeVersion) {
59
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_METRO_PROJECT', 'This Metro server belongs to another Harmony project.', { operation: 'development-manifest' });
60
+ }
61
+ return {
62
+ manifestUrl: manifest.extra.harmony.manifestUrl,
63
+ launchLink: (0, protocol_1.createHarmonyLaunchLink)(manifest.extra.harmony.manifestUrl),
64
+ bundleUrl: manifest.launchAsset.url,
65
+ };
66
+ }
@@ -0,0 +1,20 @@
1
+ export interface DoctorCheck {
2
+ details?: unknown;
3
+ id: string;
4
+ message: string;
5
+ status: 'error' | 'pass' | 'warn';
6
+ }
7
+ export interface DoctorResult {
8
+ checks: DoctorCheck[];
9
+ ok: boolean;
10
+ projectRoot: string;
11
+ }
12
+ interface DoctorOptions {
13
+ requireBuildTools?: boolean;
14
+ requireDeviceTools?: boolean;
15
+ validateGeneratedProject?: boolean;
16
+ validateModules?: boolean;
17
+ }
18
+ declare function doctorAsync(root: string, options?: DoctorOptions): Promise<DoctorResult>;
19
+ declare function formatDoctor(result: DoctorResult): string;
20
+ export { doctorAsync, formatDoctor };
@@ -0,0 +1,226 @@
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.doctorAsync = doctorAsync;
7
+ exports.formatDoctor = formatDoctor;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_module_1 = require("node:module");
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const config_1 = require("@expo/config");
12
+ const config_plugins_1 = require("@expo-harmony/config-plugins");
13
+ const expo_modules_autolinking_1 = require("@expo-harmony/expo-modules-autolinking");
14
+ const internal_1 = require("@expo-harmony/prebuild-config/internal");
15
+ const process_1 = require("../process");
16
+ const projectLock_1 = require("../projectLock");
17
+ const project_1 = require("../native/project");
18
+ const toolchain_1 = require("../native/toolchain");
19
+ const upstream_1 = require("../upstream");
20
+ const bare_1 = require("../native/bare");
21
+ function hasPlugin(plugins, name) {
22
+ return (plugins || []).some(plugin => (Array.isArray(plugin) ? plugin[0] : plugin) === name);
23
+ }
24
+ function check(id, status, message, details) {
25
+ return { id, status, message, ...(details ? { details } : {}) };
26
+ }
27
+ function canResolvePackage(root, name) {
28
+ const require = (0, node_module_1.createRequire)(node_path_1.default.join(root, 'package.json'));
29
+ try {
30
+ require.resolve(`${name}/package.json`);
31
+ return true;
32
+ }
33
+ catch {
34
+ try {
35
+ require.resolve(name);
36
+ return true;
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ }
43
+ async function validateMetroConfigAsync(root) {
44
+ const require = (0, node_module_1.createRequire)(node_path_1.default.join(root, 'package.json'));
45
+ let metro;
46
+ try {
47
+ metro = require('metro-config');
48
+ }
49
+ catch (cause) {
50
+ throw new Error('Cannot load the project-local metro-config package.', { cause });
51
+ }
52
+ if (typeof metro.resolveConfig !== 'function') {
53
+ const version = (() => {
54
+ try {
55
+ return require('metro-config/package.json').version;
56
+ }
57
+ catch {
58
+ return 'unknown';
59
+ }
60
+ })();
61
+ throw new Error(`metro-config ${version} does not expose resolveConfig().`);
62
+ }
63
+ const present = Object.hasOwn(process.env, 'EXPO_METRO_TARGET');
64
+ const previous = process.env.EXPO_METRO_TARGET;
65
+ let resolved;
66
+ try {
67
+ process.env.EXPO_METRO_TARGET = 'harmony';
68
+ resolved = await metro.resolveConfig(undefined, root);
69
+ }
70
+ finally {
71
+ if (present)
72
+ process.env.EXPO_METRO_TARGET = previous;
73
+ else
74
+ delete process.env.EXPO_METRO_TARGET;
75
+ }
76
+ const config = resolved?.config;
77
+ const platforms = config?.resolver?.platforms;
78
+ const conditions = config?.resolver?.unstable_conditionsByPlatform?.harmony;
79
+ if (!Array.isArray(platforms) || !platforms.includes('harmony')
80
+ || !Array.isArray(conditions) || !conditions.includes('harmony')
81
+ || typeof config?.resolver?.resolveRequest !== 'function') {
82
+ throw new Error('The resolved Metro config must register the harmony platform, Harmony conditions, and a resolver.');
83
+ }
84
+ }
85
+ async function doctorUnlockedAsync(root, options = {}) {
86
+ const checks = [];
87
+ const unavailable = options.requireBuildTools ? 'error' : 'warn';
88
+ let config;
89
+ const bare = (0, bare_1.isBareHarmonyProject)(root);
90
+ try {
91
+ if (bare) {
92
+ const plan = await (0, project_1.resolveHarmonyBuildPlanIfPresentAsync)(root);
93
+ checks.push(check('native-config', 'pass', `Bare Harmony project: ${plan.bundleName} / ${plan.moduleName}. Native files own the configuration.`));
94
+ }
95
+ else {
96
+ config = (0, config_1.getConfig)(root, {
97
+ isModdedConfig: true,
98
+ skipSDKVersionRequirement: true,
99
+ }).exp;
100
+ (0, config_plugins_1.normalizeHarmonyConfig)(config);
101
+ checks.push(check('app-config', 'pass', 'Harmony app config is valid.'));
102
+ }
103
+ }
104
+ catch (error) {
105
+ checks.push(check(bare ? 'native-config' : 'app-config', 'error', error.message, { code: error.code || 'ERR_HARMONY_CONFIG_INVALID' }));
106
+ }
107
+ if (config) {
108
+ checks.push(hasPlugin(config.plugins, '@expo-harmony/prebuild-config')
109
+ ? check('config-plugin', 'pass', '@expo-harmony/prebuild-config is registered.')
110
+ : check('config-plugin', 'error', 'Add @expo-harmony/prebuild-config to expo.plugins.'));
111
+ const harmony = config.harmony;
112
+ if (harmony?.signingConfigFile) {
113
+ try {
114
+ const signing = await (0, internal_1.validateHarmonySigningConfigFile)(root, harmony.signingConfigFile);
115
+ checks.push(check('signing', 'pass', `Harmony signing config ${signing.name} is valid.`));
116
+ }
117
+ catch (error) {
118
+ checks.push(check('signing', 'error', error.message, { code: error.code || 'ERR_HARMONY_SIGNING_INVALID' }));
119
+ }
120
+ }
121
+ else {
122
+ checks.push(check('signing', 'warn', 'No external signing config is set; unsigned generation remains available.'));
123
+ }
124
+ }
125
+ try {
126
+ await validateMetroConfigAsync(root);
127
+ checks.push(check('metro', 'pass', 'The resolved Metro config enables Harmony.'));
128
+ }
129
+ catch (error) {
130
+ checks.push(check('metro', 'error', `Cannot load a Harmony-enabled Metro config: ${error.message}`, { code: error.code || 'ERR_HARMONY_METRO_CONFIG' }));
131
+ }
132
+ for (const name of upstream_1.RequiredProjectPackages) {
133
+ if (canResolvePackage(root, name)) {
134
+ checks.push(check(`package:${name}`, 'pass', `${name} is resolvable.`));
135
+ }
136
+ else {
137
+ checks.push(check(`package:${name}`, 'error', `${name} is not resolvable from the app.`));
138
+ }
139
+ }
140
+ if (options.validateModules !== false) {
141
+ try {
142
+ const result = await (0, expo_modules_autolinking_1.verifyModulesAsync)({ platform: 'harmony', projectRoot: root });
143
+ const errors = result.diagnostics.filter(item => item.severity === 'error');
144
+ const warnings = result.diagnostics.filter(item => item.severity === 'warning');
145
+ if (errors.length > 0) {
146
+ checks.push(check('expo-modules', 'error', `Harmony Expo Modules verification found ${errors.length} error(s). Run 'expo-harmony modules verify' for details.`, { diagnostics: result.diagnostics }));
147
+ }
148
+ else if (warnings.length > 0) {
149
+ checks.push(check('expo-modules', 'warn', `Verified ${result.modules.length} Harmony native module(s) with ${warnings.length} warning(s).`, { diagnostics: warnings }));
150
+ }
151
+ else {
152
+ checks.push(check('expo-modules', 'pass', `Verified ${result.modules.length} Harmony native module(s).`));
153
+ }
154
+ }
155
+ catch (error) {
156
+ checks.push(check('expo-modules', 'error', `Harmony Expo Modules verification failed: ${error.message}`, { code: error.code || 'ERR_HARMONY_AUTOLINKING_FAILED' }));
157
+ }
158
+ }
159
+ const toolchain = (0, toolchain_1.resolveHarmonyToolchain)();
160
+ let sdk;
161
+ if (toolchain.sdkHome) {
162
+ sdk = check('harmony-sdk', 'pass', `Complete Harmony SDK root was resolved at ${toolchain.sdkHome}.`);
163
+ }
164
+ else {
165
+ sdk = check('harmony-sdk', unavailable, 'No complete Harmony SDK root with HMS and OpenHarmony components was found; generation works but HAP build cannot be verified.');
166
+ }
167
+ checks.push(sdk);
168
+ const tools = [
169
+ ['ohpm', toolchain.ohpm, ['--version'], unavailable],
170
+ ['hvigor-command', toolchain.hvigor, ['--version'], unavailable],
171
+ ];
172
+ if (options.requireDeviceTools !== false) {
173
+ tools.unshift(['hdc', toolchain.hdc, ['-v'], unavailable]);
174
+ }
175
+ for (const [id, tool, args, status] of tools) {
176
+ const command = [tool.command, ...tool.args].map(value => JSON.stringify(value)).join(' ');
177
+ try {
178
+ const result = await (0, process_1.spawnAsync)(tool.command, [...tool.args, ...args], {
179
+ capture: true,
180
+ cwd: root,
181
+ operation: `doctor-${id}`,
182
+ timeoutMs: 10_000,
183
+ });
184
+ checks.push(result.code === 0 && !result.timedOut
185
+ ? check(id, 'pass', `${command} is available through ${tool.source}.`)
186
+ : check(id, status, `${command} is unavailable or unhealthy; HAP build cannot be verified.`));
187
+ }
188
+ catch {
189
+ checks.push(check(id, status, `${command} is unavailable; generation remains available.`));
190
+ }
191
+ }
192
+ if (options.validateGeneratedProject !== false) {
193
+ try {
194
+ const plan = await (0, project_1.resolveHarmonyBuildPlanIfPresentAsync)(root);
195
+ if (plan && node_fs_1.default.existsSync(plan.harmonyRoot)) {
196
+ if (!node_fs_1.default.existsSync(plan.projectFiles.rootHvigor)
197
+ || !node_fs_1.default.existsSync(plan.projectFiles.moduleHvigor)) {
198
+ checks.push(check('hvigor', 'error', 'Root and module Hvigor files are required.'));
199
+ }
200
+ else {
201
+ const content = await node_fs_1.default.promises.readFile(plan.projectFiles.moduleHvigor, 'utf8');
202
+ checks.push((0, internal_1.isRnohAutolinkingDisabled)(content)
203
+ ? check('hvigor-autolinking', 'pass', 'RNOH duplicate autolinking is disabled.')
204
+ : check('hvigor-autolinking', 'error', 'The module Hvigor file must disable duplicate RNOH autolinking.'));
205
+ }
206
+ }
207
+ }
208
+ catch (error) {
209
+ checks.push(check('harmony-project', 'error', error.message, { code: error.code || 'ERR_HARMONY_TEMPLATE_INVALID' }));
210
+ }
211
+ }
212
+ return {
213
+ checks,
214
+ ok: checks.every(item => item.status !== 'error'),
215
+ projectRoot: root,
216
+ };
217
+ }
218
+ async function doctorAsync(root, options = {}) {
219
+ return (0, projectLock_1.withHarmonyProjectLockAsync)(root, 'doctor', () => doctorUnlockedAsync(root, options));
220
+ }
221
+ function formatDoctor(result) {
222
+ return result.checks.map((item) => {
223
+ const symbol = item.status === 'pass' ? '✓' : item.status === 'warn' ? '!' : '✗';
224
+ return `${symbol} [${item.status}] ${item.message}`;
225
+ }).join('\n');
226
+ }
@@ -0,0 +1,2 @@
1
+ import { type Command } from '../command';
2
+ export declare const command: Command;
@@ -0,0 +1,16 @@
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.parseDoctorArgs, argv, io);
8
+ if (!args)
9
+ return 0;
10
+ const { projectRoot: root } = args;
11
+ const { doctorAsync, formatDoctor } = await import('./doctor.js');
12
+ const result = await doctorAsync(root, { requireBuildTools: true });
13
+ io.log(formatDoctor(result));
14
+ return result.ok ? 0 : 1;
15
+ };
16
+ exports.command = command;
@@ -0,0 +1,5 @@
1
+ declare function parseDoctorArgs(argv: string[]): {
2
+ help: boolean;
3
+ project: string;
4
+ };
5
+ export { parseDoctorArgs };
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseDoctorArgs = parseDoctorArgs;
4
+ const args_1 = require("../args");
5
+ const errors_1 = require("../errors");
6
+ function parseDoctorArgs(argv) {
7
+ const { positionals, values } = (0, args_1.parseArgs)(args_1.CommonOptions, argv);
8
+ if (positionals.length > 1) {
9
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Unexpected doctor positional argument: ${positionals[1]}`, { operation: 'parse-arguments' });
10
+ }
11
+ return {
12
+ help: Boolean(values.help),
13
+ project: positionals[0],
14
+ };
15
+ }
@@ -0,0 +1,2 @@
1
+ declare function resolveHarmonyEntryPoint(projectRoot: any): string;
2
+ export { resolveHarmonyEntryPoint };
package/build/entry.js ADDED
@@ -0,0 +1,47 @@
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.resolveHarmonyEntryPoint = resolveHarmonyEntryPoint;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_module_1 = require("node:module");
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const paths_1 = require("@expo/config/paths");
11
+ const errors_1 = require("./errors");
12
+ function isFile(file) {
13
+ try {
14
+ return node_fs_1.default.statSync(file).isFile();
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ function resolveHarmonyEntryPoint(projectRoot) {
21
+ const packageJsonPath = node_path_1.default.join(projectRoot, 'package.json');
22
+ try {
23
+ const pkg = JSON.parse(node_fs_1.default.readFileSync(packageJsonPath, 'utf8'));
24
+ let entry = (0, paths_1.resolveEntryPoint)(projectRoot, { pkg, platform: 'harmony' });
25
+ if (!isFile(entry) && typeof pkg.main === 'string' && pkg.main) {
26
+ const logicalPackageRoot = entry;
27
+ const resolved = (0, node_module_1.createRequire)(packageJsonPath).resolve(pkg.main);
28
+ try {
29
+ const relative = node_path_1.default.relative(node_fs_1.default.realpathSync(logicalPackageRoot), node_fs_1.default.realpathSync(resolved));
30
+ entry = relative !== '..'
31
+ && !relative.startsWith(`..${node_path_1.default.sep}`)
32
+ && !node_path_1.default.isAbsolute(relative)
33
+ ? node_path_1.default.join(logicalPackageRoot, relative)
34
+ : resolved;
35
+ }
36
+ catch {
37
+ entry = resolved;
38
+ }
39
+ }
40
+ if (!isFile(entry))
41
+ throw new Error('Resolved entry is not a file.');
42
+ return entry;
43
+ }
44
+ catch (cause) {
45
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_ENTRY', 'Cannot resolve the Expo entry point for the Harmony bundle.', { cause, operation: 'resolve-entry-point' });
46
+ }
47
+ }
@@ -0,0 +1,12 @@
1
+ interface HarmonyCliErrorOptions {
2
+ cause?: unknown;
3
+ exitCode?: number;
4
+ operation?: string;
5
+ }
6
+ declare class HarmonyCliError extends Error {
7
+ code: string;
8
+ exitCode: number;
9
+ operation: string;
10
+ constructor(code: string, message: string, options?: HarmonyCliErrorOptions);
11
+ }
12
+ export { HarmonyCliError };
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HarmonyCliError = void 0;
4
+ class HarmonyCliError extends Error {
5
+ code;
6
+ exitCode;
7
+ operation;
8
+ constructor(code, message, options = {}) {
9
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
10
+ this.name = 'HarmonyCliError';
11
+ this.code = code;
12
+ this.exitCode = options.exitCode || 1;
13
+ this.operation = options.operation || 'cli';
14
+ }
15
+ }
16
+ exports.HarmonyCliError = HarmonyCliError;
@@ -0,0 +1,19 @@
1
+ interface ExpoHermesBundleOptions {
2
+ code: string;
3
+ filename: string;
4
+ map: string | null;
5
+ minify?: boolean;
6
+ projectRoot: string;
7
+ }
8
+ interface ExpoHermesBundleOutput {
9
+ hbc: Uint8Array;
10
+ sourcemap: string | null;
11
+ }
12
+ type ExpoHermesBuilder = (options: ExpoHermesBundleOptions) => Promise<ExpoHermesBundleOutput>;
13
+ declare function resolveExpoCli(projectRoot: any): {
14
+ cliPath: string;
15
+ packageJsonPath: any;
16
+ };
17
+ declare function resolveExpoHermesBuilder(projectRoot: string): ExpoHermesBuilder;
18
+ export { resolveExpoCli, resolveExpoHermesBuilder };
19
+ export type { ExpoHermesBuilder };
package/build/expo.js ADDED
@@ -0,0 +1,53 @@
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.resolveExpoCli = resolveExpoCli;
7
+ exports.resolveExpoHermesBuilder = resolveExpoHermesBuilder;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_module_1 = require("node:module");
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const errors_1 = require("./errors");
12
+ function resolveExpoCli(projectRoot) {
13
+ const projectRequire = (0, node_module_1.createRequire)(node_path_1.default.join(projectRoot, 'package.json'));
14
+ let packageJsonPath;
15
+ try {
16
+ packageJsonPath = projectRequire.resolve('expo/package.json');
17
+ }
18
+ catch (cause) {
19
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPO_CLI_NOT_FOUND', 'Cannot resolve the project-local expo package.', { cause, operation: 'resolve-expo-cli' });
20
+ }
21
+ const packageJson = JSON.parse(node_fs_1.default.readFileSync(packageJsonPath, 'utf8'));
22
+ const bin = typeof packageJson.bin === 'string' ? packageJson.bin : packageJson.bin?.expo;
23
+ if (typeof bin !== 'string' || !bin) {
24
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPO_CLI_NOT_FOUND', 'The project-local expo package has no expo binary.', { operation: 'resolve-expo-cli' });
25
+ }
26
+ const cliPath = node_path_1.default.resolve(node_path_1.default.dirname(packageJsonPath), bin);
27
+ if (!node_fs_1.default.existsSync(cliPath)) {
28
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPO_CLI_NOT_FOUND', `Expo CLI binary does not exist: ${cliPath}`, { operation: 'resolve-expo-cli' });
29
+ }
30
+ return { cliPath, packageJsonPath };
31
+ }
32
+ function resolveExpoHermesBuilder(projectRoot) {
33
+ const projectRequire = (0, node_module_1.createRequire)(node_path_1.default.join(projectRoot, 'package.json'));
34
+ let packageJsonPath;
35
+ try {
36
+ packageJsonPath = projectRequire.resolve('@expo/metro-config/package.json');
37
+ }
38
+ catch (cause) {
39
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_HERMES', 'Cannot resolve the project-local @expo/metro-config package required for Hermes export.', { cause, operation: 'resolve-expo-hermes' });
40
+ }
41
+ const modulePath = node_path_1.default.join(node_path_1.default.dirname(packageJsonPath), 'build', 'serializer', 'exportHermes.js');
42
+ let moduleExports;
43
+ try {
44
+ moduleExports = projectRequire(modulePath);
45
+ }
46
+ catch (cause) {
47
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_HERMES', `Cannot load the Expo Hermes exporter: ${modulePath}`, { cause, operation: 'resolve-expo-hermes' });
48
+ }
49
+ if (typeof moduleExports?.buildHermesBundleAsync !== 'function') {
50
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_HERMES', 'The project-local @expo/metro-config package does not expose buildHermesBundleAsync().', { operation: 'resolve-expo-hermes' });
51
+ }
52
+ return moduleExports.buildHermesBundleAsync;
53
+ }
@@ -0,0 +1,18 @@
1
+ import { type HarmonyExportManifest } from './manifest';
2
+ export interface ExportOptions {
3
+ check?: boolean;
4
+ outputDirectory?: string;
5
+ resetCache?: boolean;
6
+ skipDoctor?: boolean;
7
+ timeoutMs?: number;
8
+ }
9
+ export interface ExportTemporary {
10
+ assets: string;
11
+ bundle: string;
12
+ javascript: string;
13
+ metroSourceMap: string;
14
+ metadata: string;
15
+ sourceMap: string;
16
+ }
17
+ declare function exportEmbedAsync(root: string, options?: ExportOptions): Promise<HarmonyExportManifest>;
18
+ export { exportEmbedAsync };