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

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 +4 -4
@@ -0,0 +1,139 @@
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.checkAsync = checkAsync;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_os_1 = __importDefault(require("node:os"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const check_1 = require("@expo-harmony/prebuild-config/check");
11
+ const errors_1 = require("../errors");
12
+ const path_1 = require("../path");
13
+ const process_1 = require("../process");
14
+ const projectLock_1 = require("../projectLock");
15
+ const tools_1 = require("../tools");
16
+ const expo_1 = require("../expo");
17
+ const template_1 = require("./template");
18
+ const IgnoredProjectDirectories = new Set(['.expo', '.git', '.hvigor', '.yarn', 'node_modules']);
19
+ const IgnoredHarmonyGeneratedDirectories = new Set([
20
+ '.cxx',
21
+ '.git',
22
+ '.hvigor',
23
+ 'build',
24
+ 'node_modules',
25
+ 'oh_modules',
26
+ ]);
27
+ function mirrorRoot(temp, project) {
28
+ const absolute = node_path_1.default.resolve(project);
29
+ const parsed = node_path_1.default.parse(absolute);
30
+ const volume = parsed.root.replace(/[^A-Za-z0-9]+/gu, '') || 'root';
31
+ const segments = absolute.slice(parsed.root.length).split(node_path_1.default.sep).filter(Boolean);
32
+ return node_path_1.default.join(temp, 'filesystem', volume, ...segments);
33
+ }
34
+ async function linkModulesAsync(source, target) {
35
+ await node_fs_1.default.promises.mkdir(target, { recursive: true });
36
+ for (const entry of await node_fs_1.default.promises.readdir(source, { withFileTypes: true })) {
37
+ const from = node_path_1.default.join(source, entry.name);
38
+ const to = node_path_1.default.join(target, entry.name);
39
+ if (entry.name.startsWith('@') && entry.isDirectory() && !entry.isSymbolicLink()) {
40
+ await node_fs_1.default.promises.mkdir(to);
41
+ for (const child of await node_fs_1.default.promises.readdir(from, { withFileTypes: true })) {
42
+ await node_fs_1.default.promises.symlink(node_path_1.default.join(from, child.name), node_path_1.default.join(to, child.name), process.platform === 'win32' && (child.isDirectory() || child.isSymbolicLink())
43
+ ? 'junction'
44
+ : child.isDirectory() ? 'dir' : 'file');
45
+ }
46
+ continue;
47
+ }
48
+ await node_fs_1.default.promises.symlink(from, to, process.platform === 'win32' && (entry.isDirectory() || entry.isSymbolicLink())
49
+ ? 'junction'
50
+ : entry.isDirectory() ? 'dir' : 'file');
51
+ }
52
+ }
53
+ function isAppLocalHarmonyPath(project, source) {
54
+ const segments = node_path_1.default.relative(project, source).split(node_path_1.default.sep);
55
+ return segments.length >= 3
56
+ && segments[0] === 'modules'
57
+ && segments[1] !== ''
58
+ && segments[2] === 'harmony';
59
+ }
60
+ function shouldCopyPrebuildCheckPath(project, source, plan) {
61
+ const relative = node_path_1.default.relative(project, source);
62
+ if (!relative)
63
+ return true;
64
+ const segments = relative.split(node_path_1.default.sep);
65
+ if (IgnoredProjectDirectories.has(segments[0]))
66
+ return false;
67
+ const nativeRoot = (0, path_1.isInside)(plan.harmonyRoot, source)
68
+ ? plan.harmonyRoot
69
+ : isAppLocalHarmonyPath(project, source)
70
+ ? node_path_1.default.join(project, ...segments.slice(0, 3))
71
+ : null;
72
+ if (nativeRoot) {
73
+ const native = node_path_1.default.relative(nativeRoot, source).split(node_path_1.default.sep);
74
+ if (native.some(segment => IgnoredHarmonyGeneratedDirectories.has(segment)))
75
+ return false;
76
+ }
77
+ return source !== plan.exportPaths.bundle;
78
+ }
79
+ async function copyAsync(project, target, plan, temp = node_path_1.default.dirname(target)) {
80
+ await node_fs_1.default.promises.cp(project, target, {
81
+ recursive: true,
82
+ filter: source => shouldCopyPrebuildCheckPath(project, source, plan),
83
+ });
84
+ const modules = node_path_1.default.join(project, 'node_modules');
85
+ if (!node_fs_1.default.existsSync(modules)) {
86
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_DEPENDENCIES_MISSING', 'node_modules is required for --check.', {
87
+ operation: 'check',
88
+ });
89
+ }
90
+ // Keep a real node_modules directory in the isolated project and link its
91
+ // entries. Dependency scanners then retain the isolated lexical package path
92
+ // (including scoped packages) instead of collapsing the entire node_modules
93
+ // root to the source project's realpath. The packages remain read-only and
94
+ // are never copied or modified by --check.
95
+ await linkModulesAsync(modules, node_path_1.default.join(target, 'node_modules'));
96
+ await (0, check_1.stageAsync)(project, target, temp);
97
+ }
98
+ async function checkUnlockedAsync(project, options) {
99
+ project = node_path_1.default.resolve(project);
100
+ const plan = await (0, tools_1.resolveHarmonyBuildPlanAsync)(project);
101
+ const temp = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'expo-harmony-check-'));
102
+ const expected = mirrorRoot(temp, project);
103
+ let packed;
104
+ try {
105
+ await copyAsync(project, expected, plan, temp);
106
+ packed = await (0, template_1.packAsync)(project);
107
+ const expo = (0, expo_1.resolveExpoCli)(project);
108
+ const result = await (0, process_1.spawnAsync)(process.execPath, [
109
+ expo.cliPath,
110
+ 'prebuild',
111
+ expected,
112
+ '--platform', 'harmony',
113
+ '--template', packed.tarball,
114
+ '--no-install',
115
+ ], {
116
+ capture: true,
117
+ cwd: expected,
118
+ env: {
119
+ ...(0, tools_1.createHarmonyToolchainEnv)(),
120
+ ...packed.env,
121
+ ...(options.buildType ? { EXPO_HARMONY_BUILD_TYPE: options.buildType } : {}),
122
+ EXPO_HARMONY_CHECK_MIRROR_ROOT: temp,
123
+ },
124
+ operation: 'check-prebuild',
125
+ });
126
+ if (result.code !== 0) {
127
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_MANIFEST_DRIFT', `Isolated Expo prebuild failed:\n${result.stderr || result.stdout}`, { exitCode: result.code, operation: 'check-prebuild' });
128
+ }
129
+ return (0, check_1.compareAsync)(project, expected);
130
+ }
131
+ finally {
132
+ if (packed)
133
+ await packed.cleanup();
134
+ await node_fs_1.default.promises.rm(temp, { recursive: true, force: true });
135
+ }
136
+ }
137
+ async function checkAsync(project, options = {}) {
138
+ return (0, projectLock_1.withHarmonyProjectLockAsync)(project, 'prebuild-check', () => checkUnlockedAsync(project, options));
139
+ }
@@ -0,0 +1,2 @@
1
+ declare function assertSafeCleanTarget(projectRoot: any): Promise<string>;
2
+ export { assertSafeCleanTarget };
@@ -0,0 +1,50 @@
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.assertSafeCleanTarget = assertSafeCleanTarget;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const build_descriptor_1 = require("@expo-harmony/prebuild-config/build-descriptor");
10
+ const errors_1 = require("../errors");
11
+ const path_1 = require("../path");
12
+ const tools_1 = require("../tools");
13
+ async function assertSafeCleanTarget(projectRoot) {
14
+ const root = await node_fs_1.default.promises.realpath(projectRoot);
15
+ const plan = await (0, tools_1.resolveHarmonyBuildPlanIfPresentAsync)(root);
16
+ const target = plan?.harmonyRoot ?? node_path_1.default.join(root, build_descriptor_1.HarmonyPlatformDirectory);
17
+ let stat;
18
+ try {
19
+ stat = await node_fs_1.default.promises.lstat(target);
20
+ }
21
+ catch (error) {
22
+ if (error.code === 'ENOENT')
23
+ return target;
24
+ throw new errors_1.HarmonyCliError(error.code || 'ERR_HARMONY_CLEAN_TARGET', error.message || `Cannot inspect the Harmony clean target: ${target}. Please delete it manually.`, { cause: error, exitCode: error.exitCode, operation: error.operation });
25
+ }
26
+ if (!plan) {
27
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CLEAN_TARGET', `Refusing to clean ${target} because its CNG manifest is missing. Please delete it manually or run prebuild without --clean.`, { operation: 'clean' });
28
+ }
29
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
30
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CLEAN_TARGET', `Refusing to clean a non-directory or symlink: ${target}. Please delete it manually.`, {
31
+ operation: 'clean',
32
+ });
33
+ }
34
+ const real = await node_fs_1.default.promises.realpath(target);
35
+ if (real !== target || !(0, path_1.isInside)(root, real) || real === root || real === node_path_1.default.parse(real).root) {
36
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CLEAN_TARGET', `Unsafe Harmony clean target: ${real}. Please delete it manually.`, { operation: 'clean' });
37
+ }
38
+ const marker = plan.projectFiles.templateMarker;
39
+ let markerStat;
40
+ try {
41
+ markerStat = await node_fs_1.default.promises.lstat(marker);
42
+ }
43
+ catch (cause) {
44
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CLEAN_TARGET', `Refusing to clean ${real} because its template marker cannot be inspected. Please delete it manually.`, { cause, operation: 'clean' });
45
+ }
46
+ if (!(0, path_1.isInside)(real, marker) || !markerStat.isFile() || markerStat.isSymbolicLink()) {
47
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CLEAN_TARGET', `Refusing to clean ${real} because its template marker is unsafe. Please delete it manually.`, { operation: 'clean' });
48
+ }
49
+ return real;
50
+ }
@@ -0,0 +1,10 @@
1
+ declare function parsePrebuildArgs(argv: string[], options?: {
2
+ allowProject?: boolean;
3
+ }): {
4
+ check: boolean;
5
+ clean: boolean;
6
+ help: boolean;
7
+ passthrough: string[];
8
+ project: string;
9
+ };
10
+ export { parsePrebuildArgs };
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parsePrebuildArgs = parsePrebuildArgs;
4
+ const errors_1 = require("../errors");
5
+ const args_1 = require("../args");
6
+ const PrebuildOptions = {
7
+ ...args_1.CommonOptions,
8
+ 'bun': { type: 'boolean' },
9
+ 'check': { type: 'boolean' },
10
+ 'clean': { type: 'boolean' },
11
+ 'no-install': { type: 'boolean' },
12
+ 'npm': { type: 'boolean' },
13
+ 'platform': { short: 'p', type: 'string' },
14
+ 'pnpm': { type: 'boolean' },
15
+ 'skip-dependency-update': { type: 'string' },
16
+ 'template': { short: 't', type: 'string' },
17
+ 'yarn': { type: 'boolean' },
18
+ };
19
+ function parsePrebuildArgs(argv, options = {}) {
20
+ const { positionals, values } = (0, args_1.parseArgs)(PrebuildOptions, argv);
21
+ if (values.platform !== undefined) {
22
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--platform is fixed to harmony by expo-harmony.', { operation: 'parse-arguments' });
23
+ }
24
+ if (values.template !== undefined) {
25
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--template is managed by expo-harmony.', { operation: 'parse-arguments' });
26
+ }
27
+ if (positionals.length > (options.allowProject ? 1 : 0)) {
28
+ const unexpected = positionals[options.allowProject ? 1 : 0];
29
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Unexpected prebuild positional argument: ${unexpected}`, { operation: 'parse-arguments' });
30
+ }
31
+ const packageManagerFlags = ['npm', 'yarn', 'pnpm', 'bun'].filter(name => values[name]);
32
+ if (packageManagerFlags.length > 1) {
33
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', 'Choose at most one package manager: --npm, --yarn, --pnpm, or --bun.', { operation: 'parse-arguments' });
34
+ }
35
+ const passthrough = [];
36
+ for (const name of ['clean', 'npm', 'yarn', 'pnpm', 'bun', 'no-install']) {
37
+ if (values[name])
38
+ passthrough.push(`--${name}`);
39
+ }
40
+ if (values['skip-dependency-update'] !== undefined) {
41
+ passthrough.push('--skip-dependency-update', values['skip-dependency-update']);
42
+ }
43
+ return {
44
+ check: Boolean(values.check),
45
+ clean: Boolean(values.clean),
46
+ help: Boolean(values.help),
47
+ passthrough,
48
+ project: positionals[0],
49
+ };
50
+ }
@@ -0,0 +1,6 @@
1
+ interface PrebuildOptions {
2
+ buildType?: 'debug' | 'release';
3
+ capture?: boolean;
4
+ }
5
+ declare function prebuildParsedAsync(projectRoot: string, passthrough: string[], options?: PrebuildOptions): Promise<import("../process").ProcessResult>;
6
+ export { prebuildParsedAsync };
@@ -0,0 +1,62 @@
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.prebuildParsedAsync = prebuildParsedAsync;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const errors_1 = require("../errors");
9
+ const process_1 = require("../process");
10
+ const projectLock_1 = require("../projectLock");
11
+ const expo_1 = require("../expo");
12
+ const tools_1 = require("../tools");
13
+ const clean_1 = require("./clean");
14
+ const template_1 = require("./template");
15
+ async function prebuildParsedUnlockedAsync(projectRoot, passthrough, options = {}) {
16
+ if (passthrough.includes('--clean'))
17
+ await (0, clean_1.assertSafeCleanTarget)(projectRoot);
18
+ const expo = (0, expo_1.resolveExpoCli)(projectRoot);
19
+ const packed = await (0, template_1.packAsync)(projectRoot);
20
+ try {
21
+ const result = await (0, process_1.spawnAsync)(process.execPath, [
22
+ expo.cliPath,
23
+ 'prebuild', projectRoot,
24
+ '--platform', 'harmony',
25
+ '--template', packed.tarball,
26
+ ...passthrough,
27
+ ], {
28
+ capture: Boolean(options.capture),
29
+ cwd: projectRoot,
30
+ env: {
31
+ ...(0, tools_1.createHarmonyToolchainEnv)(),
32
+ ...packed.env,
33
+ ...(options.buildType ? { EXPO_HARMONY_BUILD_TYPE: options.buildType } : {}),
34
+ },
35
+ operation: 'expo-prebuild',
36
+ outputLimit: 4 * 1024 * 1024,
37
+ });
38
+ if (result.code !== 0) {
39
+ const diagnostics = options.capture ? (0, process_1.formatDiagnostics)(result) : '';
40
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_PREBUILD_FAILED', `Expo prebuild exited with code ${result.code}.${diagnostics ? `\n${diagnostics}` : ''}`, {
41
+ exitCode: result.code,
42
+ operation: 'expo-prebuild',
43
+ });
44
+ }
45
+ try {
46
+ const plan = await (0, tools_1.resolveHarmonyBuildPlanAsync)(projectRoot, {
47
+ buildMode: options.buildType,
48
+ });
49
+ await node_fs_1.default.promises.access(plan.projectFiles.templateMarker, node_fs_1.default.constants.R_OK);
50
+ }
51
+ catch (cause) {
52
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'Expo prebuild exited successfully but the Harmony marker or CNG manifest is invalid.', { cause, operation: 'verify-prebuild' });
53
+ }
54
+ return result;
55
+ }
56
+ finally {
57
+ await packed.cleanup();
58
+ }
59
+ }
60
+ async function prebuildParsedAsync(projectRoot, passthrough, options = {}) {
61
+ return (0, projectLock_1.withHarmonyProjectLockAsync)(projectRoot, 'prebuild', () => prebuildParsedUnlockedAsync(projectRoot, passthrough, options));
62
+ }
@@ -0,0 +1,8 @@
1
+ declare function packAsync(project: string): Promise<{
2
+ tarball: string;
3
+ env: {
4
+ [x: string]: string;
5
+ };
6
+ cleanup(): Promise<void>;
7
+ }>;
8
+ export { packAsync, };
@@ -0,0 +1,86 @@
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.packAsync = packAsync;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_module_1 = require("node:module");
9
+ const node_os_1 = __importDefault(require("node:os"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const errors_1 = require("../errors");
12
+ const process_1 = require("../process");
13
+ function resolve(project) {
14
+ const load = (0, node_module_1.createRequire)(node_path_1.default.join(project, 'package.json'));
15
+ let api;
16
+ try {
17
+ api = load('@expo-harmony/prebuild-config/template');
18
+ }
19
+ catch (cause) {
20
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'Cannot load @expo-harmony/prebuild-config/template from the project. Update the project-local prebuild config to a compatible version.', { cause, operation: 'resolve-template' });
21
+ }
22
+ if (typeof api?.ROOT_ENV !== 'string' || typeof api.resolveBundled !== 'function') {
23
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'The project-local @expo-harmony/prebuild-config template API is invalid.', { operation: 'resolve-template' });
24
+ }
25
+ let template;
26
+ try {
27
+ template = api.resolveBundled();
28
+ }
29
+ catch (cause) {
30
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'Cannot resolve the Harmony template declared by the project-local prebuild config.', { cause, operation: 'resolve-template' });
31
+ }
32
+ if (!template
33
+ || typeof template.root !== 'string'
34
+ || !node_path_1.default.isAbsolute(template.root)
35
+ || typeof template.version !== 'string'
36
+ || !template.version) {
37
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'The project-local @expo-harmony/prebuild-config returned an invalid template descriptor.', { operation: 'resolve-template' });
38
+ }
39
+ return {
40
+ env: { [api.ROOT_ENV]: template.root },
41
+ root: template.root,
42
+ };
43
+ }
44
+ async function packAsync(project) {
45
+ const template = resolve(project);
46
+ const temp = await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'expo-harmony-template-'));
47
+ const result = await (0, process_1.spawnAsync)('npm', [
48
+ 'pack',
49
+ '--json',
50
+ '--ignore-scripts',
51
+ '--cache', node_path_1.default.join(temp, '.npm-cache'),
52
+ '--pack-destination', temp,
53
+ template.root,
54
+ ], { capture: true, cwd: project, operation: 'pack-template' });
55
+ if (result.code !== 0) {
56
+ await node_fs_1.default.promises.rm(temp, { recursive: true, force: true });
57
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', `npm pack failed: ${result.stderr.trim()}`, {
58
+ exitCode: result.code,
59
+ operation: 'pack-template',
60
+ });
61
+ }
62
+ let packs;
63
+ try {
64
+ packs = JSON.parse(result.stdout);
65
+ }
66
+ catch (cause) {
67
+ await node_fs_1.default.promises.rm(temp, { recursive: true, force: true });
68
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'npm pack returned invalid JSON.', {
69
+ cause,
70
+ operation: 'pack-template',
71
+ });
72
+ }
73
+ const filename = packs?.[0]?.filename;
74
+ const tarball = filename && node_path_1.default.join(temp, filename);
75
+ if (!tarball || !node_fs_1.default.existsSync(tarball)) {
76
+ await node_fs_1.default.promises.rm(temp, { recursive: true, force: true });
77
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_TEMPLATE_INVALID', 'npm pack did not create a template tarball.', { operation: 'pack-template' });
78
+ }
79
+ return {
80
+ tarball,
81
+ env: template.env,
82
+ async cleanup() {
83
+ await node_fs_1.default.promises.rm(temp, { recursive: true, force: true });
84
+ },
85
+ };
86
+ }
@@ -0,0 +1,31 @@
1
+ export interface ProcessOptions {
2
+ capture?: boolean;
3
+ cwd?: string;
4
+ env?: NodeJS.ProcessEnv;
5
+ onStderr?: (chunk: Uint8Array) => void;
6
+ onStdout?: (chunk: Uint8Array) => void;
7
+ operation?: string;
8
+ outputLimit?: number;
9
+ signal?: AbortSignal;
10
+ stdio?: 'inherit' | 'pipe';
11
+ stopGraceMs?: number;
12
+ timeoutMs?: number;
13
+ }
14
+ export interface ProcessResult {
15
+ code: number;
16
+ signal: NodeJS.Signals | null;
17
+ stderr: string;
18
+ stdout: string;
19
+ timedOut: boolean;
20
+ }
21
+ declare function formatDiagnostics(result: Pick<ProcessResult, 'stderr' | 'stdout'>, limit?: number): string;
22
+ declare function spawnAsync(command: string, args: string[], options?: ProcessOptions): Promise<ProcessResult>;
23
+ declare function startManagedProcess(command: string, args: string[], options?: ProcessOptions): {
24
+ child: any;
25
+ completion: Promise<ProcessResult>;
26
+ getStderr: () => string;
27
+ getStdout: () => string;
28
+ stop: (signal?: NodeJS.Signals, graceMs?: number) => Promise<any>;
29
+ wasStopped: () => boolean;
30
+ };
31
+ export { formatDiagnostics, spawnAsync, startManagedProcess };