@expo-harmony/cli 55.0.26-harmony.1 → 55.0.26-harmony.2

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 (73) hide show
  1. package/build/args.d.ts +19 -0
  2. package/build/args.js +27 -0
  3. package/build/bin/expo-harmony.d.ts +2 -0
  4. package/build/buildHap/build.d.ts +25 -0
  5. package/build/buildHap/build.js +96 -0
  6. package/build/buildHap/common.d.ts +24 -0
  7. package/build/buildHap/common.js +92 -0
  8. package/build/buildHap/options.d.ts +7 -0
  9. package/build/buildHap/options.js +30 -0
  10. package/build/cli.d.ts +3 -0
  11. package/build/cli.js +202 -0
  12. package/build/doctor/doctor.d.ts +20 -0
  13. package/build/doctor/doctor.js +217 -0
  14. package/build/doctor/options.d.ts +5 -0
  15. package/build/doctor/options.js +15 -0
  16. package/build/entry.d.ts +2 -0
  17. package/build/entry.js +47 -0
  18. package/build/errors.d.ts +12 -0
  19. package/build/errors.js +16 -0
  20. package/build/expo.d.ts +19 -0
  21. package/build/expo.js +53 -0
  22. package/build/exportEmbed/export.d.ts +16 -0
  23. package/build/exportEmbed/export.js +125 -0
  24. package/build/exportEmbed/manifest.d.ts +44 -0
  25. package/build/exportEmbed/manifest.js +171 -0
  26. package/build/exportEmbed/options.d.ts +7 -0
  27. package/build/exportEmbed/options.js +22 -0
  28. package/build/file.d.ts +9 -0
  29. package/build/file.js +100 -0
  30. package/build/index.d.ts +12 -0
  31. package/build/modules/modules.d.ts +42 -0
  32. package/build/modules/modules.js +84 -0
  33. package/build/modules/options.d.ts +10 -0
  34. package/build/modules/options.js +45 -0
  35. package/build/path.d.ts +4 -0
  36. package/build/path.js +27 -0
  37. package/build/prebuild/check.d.ts +6 -0
  38. package/build/prebuild/check.js +139 -0
  39. package/build/prebuild/clean.d.ts +2 -0
  40. package/build/prebuild/clean.js +50 -0
  41. package/build/prebuild/options.d.ts +10 -0
  42. package/build/prebuild/options.js +50 -0
  43. package/build/prebuild/prebuild.d.ts +6 -0
  44. package/build/prebuild/prebuild.js +62 -0
  45. package/build/prebuild/template.d.ts +8 -0
  46. package/build/prebuild/template.js +86 -0
  47. package/build/process.d.ts +31 -0
  48. package/build/process.js +236 -0
  49. package/build/project.d.ts +2 -0
  50. package/build/project.js +23 -0
  51. package/build/projectLock.d.ts +2 -0
  52. package/build/projectLock.js +180 -0
  53. package/build/run/cache.d.ts +11 -0
  54. package/build/run/cache.js +73 -0
  55. package/build/run/devices.d.ts +29 -0
  56. package/build/run/devices.js +211 -0
  57. package/build/run/emulators.d.ts +13 -0
  58. package/build/run/emulators.js +85 -0
  59. package/build/run/install.d.ts +7 -0
  60. package/build/run/install.js +26 -0
  61. package/build/run/metro.d.ts +17 -0
  62. package/build/run/metro.js +156 -0
  63. package/build/run/options.d.ts +13 -0
  64. package/build/run/options.js +60 -0
  65. package/build/run/run.d.ts +51 -0
  66. package/build/run/run.js +174 -0
  67. package/build/start/options.d.ts +7 -0
  68. package/build/start/options.js +31 -0
  69. package/build/tools.d.ts +39 -0
  70. package/build/tools.js +226 -0
  71. package/build/upstream.d.ts +9 -0
  72. package/build/upstream.js +17 -0
  73. package/package.json +1 -1
@@ -0,0 +1,171 @@
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.HermesMagic = void 0;
7
+ exports.assertHermesBundle = assertHermesBundle;
8
+ exports.assertSourceMap = assertSourceMap;
9
+ exports.exportPaths = exportPaths;
10
+ exports.publishExportAsync = publishExportAsync;
11
+ exports.validatePublishedExportAsync = validatePublishedExportAsync;
12
+ const node_fs_1 = __importDefault(require("node:fs"));
13
+ const node_os_1 = __importDefault(require("node:os"));
14
+ const node_path_1 = __importDefault(require("node:path"));
15
+ const errors_1 = require("../errors");
16
+ const file_1 = require("../file");
17
+ const path_1 = require("../path");
18
+ const HermesMagic = Buffer.from('c61fbc03c103191f', 'hex');
19
+ exports.HermesMagic = HermesMagic;
20
+ const ManifestSchemaVersion = 1;
21
+ function exportPaths(plan) {
22
+ return plan.exportPaths;
23
+ }
24
+ async function assertHermesBundle(file) {
25
+ let handle;
26
+ try {
27
+ handle = await node_fs_1.default.promises.open(file, 'r');
28
+ const header = Buffer.alloc(12);
29
+ const { bytesRead } = await handle.read(header, 0, header.length, 0);
30
+ const stat = await handle.stat();
31
+ if (bytesRead < header.length || stat.size <= header.length
32
+ || header.subarray(0, HermesMagic.length).toString('hex') !== HermesMagic.toString('hex')) {
33
+ throw new Error('Expo export did not produce non-empty Hermes bytecode.');
34
+ }
35
+ return { bytecodeVersion: header.readUInt32LE(8), size: stat.size };
36
+ }
37
+ catch (cause) {
38
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_INVALID_BUNDLE', 'The Harmony export output is not a valid Hermes bytecode bundle.', { cause, operation: 'validate-export' });
39
+ }
40
+ finally {
41
+ await handle?.close();
42
+ }
43
+ }
44
+ async function assertSourceMap(file) {
45
+ try {
46
+ const contents = await node_fs_1.default.promises.readFile(file, 'utf8');
47
+ const sourceMap = JSON.parse(contents);
48
+ const paths = JSON.stringify({ sourceRoot: sourceMap?.sourceRoot, sources: sourceMap?.sources });
49
+ const sourcePaths = [sourceMap?.sourceRoot, ...(sourceMap?.sources || [])]
50
+ .filter(value => typeof value === 'string');
51
+ const hasAbsoluteSource = sourcePaths.some(value => (node_path_1.default.posix.isAbsolute(value) || node_path_1.default.win32.isAbsolute(value) || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//u.test(value)));
52
+ if (sourceMap?.version !== 3 || !Array.isArray(sourceMap.sources)
53
+ || hasAbsoluteSource || paths.includes(node_os_1.default.homedir())) {
54
+ throw new Error('Invalid or host-specific source map.');
55
+ }
56
+ return await (0, file_1.describeFile)(file);
57
+ }
58
+ catch (cause) {
59
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_INVALID_SOURCEMAP', 'Expo export did not produce a portable source map.', { cause, operation: 'validate-export' });
60
+ }
61
+ }
62
+ async function readPreviousManifest(file) {
63
+ try {
64
+ const manifest = JSON.parse(await node_fs_1.default.promises.readFile(file, 'utf8'));
65
+ if (manifest?.schemaVersion !== ManifestSchemaVersion || !Array.isArray(manifest.assets)) {
66
+ throw new Error('Unsupported export manifest schema.');
67
+ }
68
+ for (const asset of manifest.assets) {
69
+ (0, path_1.assertSafeRelative)(asset?.path, 'Previous export manifest');
70
+ }
71
+ return manifest;
72
+ }
73
+ catch (cause) {
74
+ if (cause.code === 'ENOENT')
75
+ return null;
76
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_MANIFEST', 'Cannot read the previous Harmony export manifest.', { cause, operation: 'publish-export' });
77
+ }
78
+ }
79
+ async function publishExportAsync(projectRoot, paths, temporary, entryFile, bytecode) {
80
+ const previous = await readPreviousManifest(paths.manifest);
81
+ const previouslyOwned = new Set((previous?.assets || []).map(asset => asset.path));
82
+ const assetFiles = await (0, file_1.listFiles)(temporary.assets);
83
+ const assetOutputs = [];
84
+ for (const source of assetFiles) {
85
+ const relative = (0, path_1.toPosixPath)(node_path_1.default.relative(temporary.assets, source));
86
+ const nativeRelative = (0, path_1.assertSafeRelative)(relative, 'Expo asset output');
87
+ const destination = node_path_1.default.join(paths.rawfileRoot, nativeRelative);
88
+ if (await node_fs_1.default.promises.lstat(destination).catch(error => error.code === 'ENOENT' ? null : Promise.reject(error))) {
89
+ if (!previouslyOwned.has(relative)) {
90
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_COLLISION', `Expo asset output collides with an unmanaged raw resource: ${relative}.`, { operation: 'publish-export' });
91
+ }
92
+ }
93
+ assetOutputs.push({ descriptor: { path: relative, ...(await (0, file_1.describeFile)(source)) }, source });
94
+ }
95
+ assetOutputs.sort((left, right) => left.descriptor.path.localeCompare(right.descriptor.path, 'en'));
96
+ const assets = assetOutputs.map(output => output.descriptor);
97
+ const bundle = await (0, file_1.describeFile)(temporary.bundle);
98
+ const sourceMap = await (0, file_1.describeFile)(temporary.sourceMap);
99
+ const manifest = {
100
+ assets,
101
+ bundle: {
102
+ bytecodeVersion: bytecode.bytecodeVersion,
103
+ path: (0, path_1.toPosixPath)(node_path_1.default.relative(projectRoot, paths.bundle)),
104
+ ...bundle,
105
+ },
106
+ entryFile: (0, path_1.toPosixPath)(node_path_1.default.relative(projectRoot, entryFile)),
107
+ platform: 'harmony',
108
+ schemaVersion: ManifestSchemaVersion,
109
+ sourceMap: {
110
+ path: (0, path_1.toPosixPath)(node_path_1.default.relative(projectRoot, paths.sourceMap)),
111
+ ...sourceMap,
112
+ },
113
+ transformProfile: 'hermes-stable',
114
+ };
115
+ try {
116
+ await (0, file_1.atomicCopy)(temporary.bundle, paths.bundle, paths.rawfileRoot);
117
+ await (0, file_1.atomicCopy)(temporary.sourceMap, paths.sourceMap, paths.metadataRoot);
118
+ for (const output of assetOutputs) {
119
+ const relative = output.descriptor.path;
120
+ await (0, file_1.atomicCopy)(output.source, node_path_1.default.join(paths.rawfileRoot, (0, path_1.assertSafeRelative)(relative, 'Expo asset output')), paths.rawfileRoot);
121
+ }
122
+ await (0, file_1.atomicWriteJson)(manifest, paths.manifest, node_path_1.default.dirname(paths.manifest));
123
+ const nextOwned = new Set(assets.map(asset => asset.path));
124
+ for (const stale of previouslyOwned) {
125
+ if (nextOwned.has(stale))
126
+ continue;
127
+ const destination = node_path_1.default.join(paths.rawfileRoot, (0, path_1.assertSafeRelative)(stale, 'Previous export manifest'));
128
+ if (!(0, path_1.isInside)(paths.rawfileRoot, destination))
129
+ continue;
130
+ await node_fs_1.default.promises.rm(destination, { force: true });
131
+ await (0, file_1.removeEmptyParents)(destination, paths.rawfileRoot);
132
+ }
133
+ }
134
+ catch (cause) {
135
+ if (cause instanceof errors_1.HarmonyCliError) {
136
+ throw new errors_1.HarmonyCliError(cause.code, cause.message, {
137
+ cause,
138
+ exitCode: cause.exitCode,
139
+ operation: cause.operation,
140
+ });
141
+ }
142
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_PUBLISH', 'Cannot publish the validated Harmony bundle and assets.', { cause, operation: 'publish-export' });
143
+ }
144
+ return manifest;
145
+ }
146
+ async function validatePublishedExportAsync(paths) {
147
+ const manifest = await readPreviousManifest(paths.manifest);
148
+ if (!manifest) {
149
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_MANIFEST', 'Harmony export manifest does not exist. Run expo-harmony export:embed first.', { operation: 'check-export' });
150
+ }
151
+ const bytecode = await assertHermesBundle(paths.bundle);
152
+ const sourceMap = await assertSourceMap(paths.sourceMap);
153
+ const expectedBundle = await (0, file_1.describeFile)(paths.bundle);
154
+ if (manifest.bundle?.sha256 !== expectedBundle.sha256
155
+ || manifest.bundle?.size !== expectedBundle.size
156
+ || manifest.bundle?.bytecodeVersion !== bytecode.bytecodeVersion
157
+ || manifest.sourceMap?.sha256 !== sourceMap.sha256
158
+ || manifest.sourceMap?.size !== sourceMap.size) {
159
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_MANIFEST', 'Harmony bundle or source map differs from the export manifest.', { operation: 'check-export' });
160
+ }
161
+ for (const asset of manifest.assets) {
162
+ const file = node_path_1.default.join(paths.rawfileRoot, (0, path_1.assertSafeRelative)(asset.path, 'Export manifest'));
163
+ const actual = await (0, file_1.describeFile)(file).catch((cause) => {
164
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_MANIFEST', `Harmony export asset is missing or invalid: ${asset.path}.`, { cause, operation: 'check-export' });
165
+ });
166
+ if (actual.sha256 !== asset.sha256 || actual.size !== asset.size) {
167
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_EXPORT_MANIFEST', `Harmony export asset differs from the manifest: ${asset.path}.`, { operation: 'check-export' });
168
+ }
169
+ }
170
+ return manifest;
171
+ }
@@ -0,0 +1,7 @@
1
+ declare function parseExportEmbedArgs(argv: string[]): {
2
+ check: boolean;
3
+ help: boolean;
4
+ project: string;
5
+ resetCache: boolean;
6
+ };
7
+ export { parseExportEmbedArgs };
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseExportEmbedArgs = parseExportEmbedArgs;
4
+ const errors_1 = require("../errors");
5
+ const args_1 = require("../args");
6
+ const ExportEmbedOptions = {
7
+ ...args_1.CommonOptions,
8
+ 'check': { type: 'boolean' },
9
+ 'reset-cache': { type: 'boolean' },
10
+ };
11
+ function parseExportEmbedArgs(argv) {
12
+ const { positionals, values } = (0, args_1.parseArgs)(ExportEmbedOptions, argv);
13
+ if (positionals.length > 1) {
14
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Unexpected export:embed positional argument: ${positionals[1]}`, { operation: 'parse-arguments' });
15
+ }
16
+ return {
17
+ check: Boolean(values.check),
18
+ help: Boolean(values.help),
19
+ project: positionals[0],
20
+ resetCache: Boolean(values['reset-cache']),
21
+ };
22
+ }
@@ -0,0 +1,9 @@
1
+ declare function describeFile(file: string): Promise<{
2
+ sha256: string;
3
+ size: number;
4
+ }>;
5
+ declare function listFiles(root: string): Promise<string[]>;
6
+ declare function atomicCopy(source: string, destination: string, root: string): Promise<void>;
7
+ declare function atomicWriteJson(value: unknown, destination: string, root: string): Promise<void>;
8
+ declare function removeEmptyParents(file: string, stop: string): Promise<void>;
9
+ export { atomicCopy, atomicWriteJson, describeFile, listFiles, removeEmptyParents };
package/build/file.js ADDED
@@ -0,0 +1,100 @@
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.atomicCopy = atomicCopy;
7
+ exports.atomicWriteJson = atomicWriteJson;
8
+ exports.describeFile = describeFile;
9
+ exports.listFiles = listFiles;
10
+ exports.removeEmptyParents = removeEmptyParents;
11
+ const node_crypto_1 = __importDefault(require("node:crypto"));
12
+ const node_fs_1 = __importDefault(require("node:fs"));
13
+ const node_path_1 = __importDefault(require("node:path"));
14
+ const path_1 = require("./path");
15
+ async function sha256File(file) {
16
+ return await new Promise((resolve, reject) => {
17
+ const hash = node_crypto_1.default.createHash('sha256');
18
+ const input = node_fs_1.default.createReadStream(file);
19
+ input.on('error', reject);
20
+ input.on('data', (chunk) => {
21
+ const bytes = typeof chunk === 'string' ? new TextEncoder().encode(chunk) : Uint8Array.from(chunk);
22
+ hash.update(bytes);
23
+ });
24
+ input.on('end', () => resolve(hash.digest('hex')));
25
+ });
26
+ }
27
+ async function describeFile(file) {
28
+ const stat = await node_fs_1.default.promises.stat(file);
29
+ if (!stat.isFile())
30
+ throw new Error(`Expected a regular file: ${file}`);
31
+ return { sha256: await sha256File(file), size: stat.size };
32
+ }
33
+ async function listFiles(root) {
34
+ const files = [];
35
+ async function visit(directory) {
36
+ const entries = await node_fs_1.default.promises.readdir(directory, { withFileTypes: true });
37
+ entries.sort((left, right) => left.name.localeCompare(right.name, 'en'));
38
+ for (const entry of entries) {
39
+ const file = node_path_1.default.join(directory, entry.name);
40
+ if (entry.isSymbolicLink())
41
+ throw new Error(`Asset output contains a symbolic link: ${entry.name}`);
42
+ if (entry.isDirectory())
43
+ await visit(file);
44
+ else if (entry.isFile())
45
+ files.push(file);
46
+ else
47
+ throw new Error(`Asset output contains an unsupported file: ${entry.name}`);
48
+ }
49
+ }
50
+ await visit(root);
51
+ return files;
52
+ }
53
+ async function ensureSafeParent(root, destination) {
54
+ if (!(0, path_1.isInside)(root, destination) || destination === root) {
55
+ throw new Error('Export destination escapes its owned root.');
56
+ }
57
+ await node_fs_1.default.promises.mkdir(root, { recursive: true });
58
+ await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(destination), { recursive: true });
59
+ const [realRoot, realParent] = await Promise.all([
60
+ node_fs_1.default.promises.realpath(root),
61
+ node_fs_1.default.promises.realpath(node_path_1.default.dirname(destination)),
62
+ ]);
63
+ if (!(0, path_1.isInside)(realRoot, realParent)) {
64
+ throw new Error('Export destination follows a link outside its owned root.');
65
+ }
66
+ }
67
+ async function atomicCopy(source, destination, root) {
68
+ await ensureSafeParent(root, destination);
69
+ const temporary = node_path_1.default.join(node_path_1.default.dirname(destination), `.${node_path_1.default.basename(destination)}.expo-${node_crypto_1.default.randomUUID()}.tmp`);
70
+ try {
71
+ await node_fs_1.default.promises.copyFile(source, temporary, node_fs_1.default.constants.COPYFILE_EXCL);
72
+ await node_fs_1.default.promises.rename(temporary, destination);
73
+ }
74
+ finally {
75
+ await node_fs_1.default.promises.rm(temporary, { force: true });
76
+ }
77
+ }
78
+ async function atomicWriteJson(value, destination, root) {
79
+ await ensureSafeParent(root, destination);
80
+ const temporary = node_path_1.default.join(node_path_1.default.dirname(destination), `.${node_path_1.default.basename(destination)}.expo-${node_crypto_1.default.randomUUID()}.tmp`);
81
+ try {
82
+ await node_fs_1.default.promises.writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { flag: 'wx' });
83
+ await node_fs_1.default.promises.rename(temporary, destination);
84
+ }
85
+ finally {
86
+ await node_fs_1.default.promises.rm(temporary, { force: true });
87
+ }
88
+ }
89
+ async function removeEmptyParents(file, stop) {
90
+ let directory = node_path_1.default.dirname(file);
91
+ while (directory !== stop && (0, path_1.isInside)(stop, directory)) {
92
+ try {
93
+ await node_fs_1.default.promises.rmdir(directory);
94
+ }
95
+ catch {
96
+ return;
97
+ }
98
+ directory = node_path_1.default.dirname(directory);
99
+ }
100
+ }
@@ -0,0 +1,12 @@
1
+ export { runAsync } from './cli';
2
+ export { buildHarmonyAsync } from './buildHap/build';
3
+ export type { HarmonyBuildOptions, HarmonyBuildResult } from './buildHap/build';
4
+ export { exportEmbedAsync } from './exportEmbed/export';
5
+ export { resolveHarmonyBuildPlanAsync, resolveHarmonyToolchain } from './tools';
6
+ export { installHarmonyDependenciesAsync } from './run/install';
7
+ export { formatModulesResult, runModulesCommandAsync } from './modules/modules';
8
+ export type { ModulesCommandResult } from './modules/modules';
9
+ export { commitHarmonyNativeBuildCacheAsync, prepareHarmonyNativeBuildCacheAsync } from './run/cache';
10
+ export { runHarmonyAsync } from './run/run';
11
+ export type { HarmonyExportManifest } from './exportEmbed/manifest';
12
+ export type { HarmonyRunOptions, HarmonyRunResult } from './run/run';
@@ -0,0 +1,42 @@
1
+ import { type BuildType, type Diagnostic, type ModuleDescriptor, type ModuleSource } from '@expo-harmony/expo-modules-autolinking';
2
+ import type { ModulesAction } from './options';
3
+ interface ModulesCommandOptions {
4
+ action: ModulesAction;
5
+ nativeModulesDir?: string;
6
+ packageName?: string;
7
+ variant?: BuildType;
8
+ }
9
+ interface ListedModule {
10
+ packageName: string;
11
+ packageVersion: string;
12
+ source: ModuleSource;
13
+ supportsHarmony: boolean;
14
+ }
15
+ interface ModulesListResult {
16
+ action: 'list';
17
+ duplicates: ReadonlyArray<{
18
+ packageName: string;
19
+ revisions: ReadonlyArray<{
20
+ path: string;
21
+ version: string;
22
+ }>;
23
+ }>;
24
+ missingIncludes: ReadonlyArray<string>;
25
+ modules: ReadonlyArray<ListedModule>;
26
+ ok: boolean;
27
+ }
28
+ interface ModulesInspectResult {
29
+ action: 'inspect';
30
+ modules: ReadonlyArray<ModuleDescriptor>;
31
+ ok: boolean;
32
+ }
33
+ interface ModulesVerifyResult {
34
+ action: 'verify';
35
+ diagnostics: ReadonlyArray<Diagnostic>;
36
+ modules: ReadonlyArray<ModuleDescriptor>;
37
+ ok: boolean;
38
+ }
39
+ export type ModulesCommandResult = ModulesInspectResult | ModulesListResult | ModulesVerifyResult;
40
+ declare function runModulesCommandAsync(projectRoot: string, options: ModulesCommandOptions): Promise<ModulesCommandResult>;
41
+ declare function formatModulesResult(result: ModulesCommandResult): string;
42
+ export { formatModulesResult, runModulesCommandAsync };
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatModulesResult = formatModulesResult;
4
+ exports.runModulesCommandAsync = runModulesCommandAsync;
5
+ const expo_modules_autolinking_1 = require("@expo-harmony/expo-modules-autolinking");
6
+ const errors_1 = require("../errors");
7
+ function apiOptions(projectRoot, options) {
8
+ return {
9
+ projectRoot,
10
+ platform: 'harmony',
11
+ ...(options.nativeModulesDir === undefined ? {} : { nativeModulesDir: options.nativeModulesDir }),
12
+ ...(options.variant === undefined ? {} : { buildType: options.variant }),
13
+ };
14
+ }
15
+ async function runModulesCommandAsync(projectRoot, options) {
16
+ const shared = apiOptions(projectRoot, options);
17
+ if (options.action === 'list') {
18
+ const result = await (0, expo_modules_autolinking_1.searchModulesAsync)(shared);
19
+ return {
20
+ action: 'list',
21
+ duplicates: result.duplicates,
22
+ missingIncludes: result.missingIncludes,
23
+ modules: result.modules.map(module => ({
24
+ packageName: module.packageName,
25
+ packageVersion: module.packageVersion,
26
+ source: module.source,
27
+ supportsHarmony: module.supportsHarmony,
28
+ })),
29
+ ok: result.missingIncludes.length === 0,
30
+ };
31
+ }
32
+ if (options.action === 'inspect') {
33
+ const modules = await (0, expo_modules_autolinking_1.resolveModulesAsync)(shared);
34
+ const selected = options.packageName === undefined
35
+ ? modules
36
+ : modules.filter(module => module.packageName === options.packageName);
37
+ if (options.packageName !== undefined && selected.length === 0) {
38
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_MODULE_NOT_FOUND', `Harmony module '${options.packageName}' was not discovered. Run 'expo-harmony modules list' to inspect candidates.`, { operation: 'modules-inspect' });
39
+ }
40
+ return { action: 'inspect', modules: selected, ok: true };
41
+ }
42
+ const result = await (0, expo_modules_autolinking_1.verifyModulesAsync)(shared);
43
+ return {
44
+ action: 'verify',
45
+ diagnostics: result.diagnostics,
46
+ modules: result.modules,
47
+ ok: result.valid,
48
+ };
49
+ }
50
+ function formatDiagnostic(diagnostic) {
51
+ const subject = diagnostic.packageName ? ` ${diagnostic.packageName}` : '';
52
+ return `${diagnostic.severity === 'error' ? '✗' : '!'} [${diagnostic.code}]${subject}: ${diagnostic.message}`;
53
+ }
54
+ function formatModulesResult(result) {
55
+ if (result.action === 'list') {
56
+ const lines = result.modules.map(module => (`${module.supportsHarmony ? '✓' : '-'} ${module.packageName}@${module.packageVersion} [${module.source}]`));
57
+ for (const packageName of result.missingIncludes) {
58
+ lines.push(`✗ missing required package ${packageName}`);
59
+ }
60
+ for (const duplicate of result.duplicates) {
61
+ lines.push(`! duplicate ${duplicate.packageName}: ${duplicate.revisions.map(item => item.version).join(', ')}`);
62
+ }
63
+ return lines.length === 0 ? 'No native module candidates were discovered.' : lines.join('\n');
64
+ }
65
+ if (result.action === 'verify') {
66
+ if (result.diagnostics.length === 0) {
67
+ return `✓ Verified ${result.modules.length} Harmony native module(s).`;
68
+ }
69
+ return result.diagnostics.map(formatDiagnostic).join('\n');
70
+ }
71
+ return result.modules.map((module) => {
72
+ const arkTsModules = module.harmony.modules.join(', ') || 'none';
73
+ const har = module.arkTs
74
+ ? `${module.arkTs.ohPackageName} <- ${module.arkTs.harPath}`
75
+ : 'none';
76
+ return [
77
+ `${module.packageName}@${module.packageVersion}`,
78
+ ` source: ${module.source}`,
79
+ ` packageRoot: ${module.packageRoot}`,
80
+ ` ArkTS modules: ${arkTsModules}`,
81
+ ` HAR: ${har}`,
82
+ ].join('\n');
83
+ }).join('\n');
84
+ }
@@ -0,0 +1,10 @@
1
+ export type ModulesAction = 'inspect' | 'list' | 'verify';
2
+ declare function parseModulesArgs(argv: string[]): {
3
+ action: ModulesAction;
4
+ help: boolean;
5
+ project: string;
6
+ packageName: string;
7
+ nativeModulesDir: string;
8
+ variant: "debug" | "release" | undefined;
9
+ };
10
+ export { parseModulesArgs };
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseModulesArgs = parseModulesArgs;
4
+ const args_1 = require("../args");
5
+ const errors_1 = require("../errors");
6
+ const ModuleOptions = {
7
+ ...args_1.CommonOptions,
8
+ 'package': { short: 'p', type: 'string' },
9
+ 'variant': { type: 'string' },
10
+ 'native-modules-dir': { type: 'string' },
11
+ };
12
+ function parseModulesArgs(argv) {
13
+ const { positionals, values } = (0, args_1.parseArgs)(ModuleOptions, argv);
14
+ const action = positionals[0];
15
+ if (values.help && action === undefined) {
16
+ return {
17
+ action: 'list',
18
+ help: true,
19
+ project: undefined,
20
+ packageName: values.package,
21
+ nativeModulesDir: values['native-modules-dir'],
22
+ variant: values.variant,
23
+ };
24
+ }
25
+ if (!action || !['inspect', 'list', 'verify'].includes(action)) {
26
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', 'modules requires one of: list, inspect, verify.', { operation: 'parse-arguments' });
27
+ }
28
+ if (positionals.length > 2) {
29
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Unexpected modules positional argument: ${positionals[2]}`, { operation: 'parse-arguments' });
30
+ }
31
+ if (values.variant !== undefined && !['debug', 'release'].includes(values.variant)) {
32
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `--variant must be debug or release, received: ${values.variant}`, { operation: 'parse-arguments' });
33
+ }
34
+ if (values.package !== undefined && action !== 'inspect') {
35
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--package is supported by modules inspect only.', { operation: 'parse-arguments' });
36
+ }
37
+ return {
38
+ action,
39
+ help: Boolean(values.help),
40
+ project: positionals[1],
41
+ packageName: values.package,
42
+ nativeModulesDir: values['native-modules-dir'],
43
+ variant: values.variant,
44
+ };
45
+ }
@@ -0,0 +1,4 @@
1
+ declare function isInside(root: string, target: string): boolean;
2
+ declare function toPosixPath(value: string): string;
3
+ declare function assertSafeRelative(relative: string, label: string): string;
4
+ export { assertSafeRelative, isInside, toPosixPath };
package/build/path.js ADDED
@@ -0,0 +1,27 @@
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.assertSafeRelative = assertSafeRelative;
7
+ exports.isInside = isInside;
8
+ exports.toPosixPath = toPosixPath;
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ function isInside(root, target) {
11
+ const relative = node_path_1.default.relative(root, target);
12
+ return relative === ''
13
+ || (relative !== '..' && !relative.startsWith(`..${node_path_1.default.sep}`) && !node_path_1.default.isAbsolute(relative));
14
+ }
15
+ function toPosixPath(value) {
16
+ return value.split(node_path_1.default.sep).join('/');
17
+ }
18
+ function assertSafeRelative(relative, label) {
19
+ const segments = typeof relative === 'string' ? relative.split('/') : [];
20
+ const native = segments.join(node_path_1.default.sep);
21
+ if (!relative || relative.includes('\\') || relative.includes('\0')
22
+ || segments.some(segment => !segment || segment === '.' || segment === '..')
23
+ || node_path_1.default.isAbsolute(native)) {
24
+ throw new Error(`${label} contains an unsafe path.`);
25
+ }
26
+ return native;
27
+ }
@@ -0,0 +1,6 @@
1
+ export type { Change as CheckChange } from '@expo-harmony/prebuild-config/check';
2
+ interface CheckOptions {
3
+ buildType?: 'debug' | 'release';
4
+ }
5
+ declare function checkAsync(project: any, options?: CheckOptions): Promise<import("@expo-harmony/prebuild-config/check").Result>;
6
+ export { checkAsync };