expo-harmony-toolkit 1.7.2 → 1.8.0

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 (69) hide show
  1. package/README.en.md +25 -11
  2. package/README.md +27 -13
  3. package/build/core/autolinking.d.ts +10 -0
  4. package/build/core/autolinking.js +421 -0
  5. package/build/core/build/commands.d.ts +19 -0
  6. package/build/core/build/commands.js +61 -0
  7. package/build/core/build/compatibilityShims.d.ts +1 -0
  8. package/build/core/build/compatibilityShims.js +272 -0
  9. package/build/core/build/localHar.d.ts +13 -0
  10. package/build/core/build/localHar.js +337 -0
  11. package/build/core/build/reporting.d.ts +19 -0
  12. package/build/core/build/reporting.js +67 -0
  13. package/build/core/build/rnohCompatibility.d.ts +1 -0
  14. package/build/core/build/rnohCompatibility.js +38 -0
  15. package/build/core/build.d.ts +4 -12
  16. package/build/core/build.js +70 -762
  17. package/build/core/constants.d.ts +2 -1
  18. package/build/core/constants.js +3 -2
  19. package/build/core/dependencyInspection.d.ts +2 -0
  20. package/build/core/dependencyInspection.js +130 -0
  21. package/build/core/env.js +1 -1
  22. package/build/core/report.js +162 -10
  23. package/build/core/signing.d.ts +2 -0
  24. package/build/core/signing.js +30 -0
  25. package/build/core/template/capabilityRegistry.d.ts +9 -0
  26. package/build/core/template/capabilityRegistry.js +84 -0
  27. package/build/core/template/expoModulesCoreShim.d.ts +2 -0
  28. package/build/core/template/expoModulesCoreShim.js +279 -0
  29. package/build/core/template/metro.d.ts +2 -0
  30. package/build/core/template/metro.js +213 -0
  31. package/build/core/template/nativeFiles.d.ts +8 -0
  32. package/build/core/template/nativeFiles.js +333 -0
  33. package/build/core/template/renderers/camera.d.ts +4 -0
  34. package/build/core/template/renderers/camera.js +612 -0
  35. package/build/core/template/renderers/fileSystem.d.ts +4 -0
  36. package/build/core/template/renderers/fileSystem.js +814 -0
  37. package/build/core/template/renderers/imagePicker.d.ts +4 -0
  38. package/build/core/template/renderers/imagePicker.js +753 -0
  39. package/build/core/template/renderers/location.d.ts +4 -0
  40. package/build/core/template/renderers/location.js +836 -0
  41. package/build/core/template/runtimeShims.d.ts +2 -0
  42. package/build/core/template/runtimeShims.js +192 -0
  43. package/build/core/template/support.d.ts +11 -0
  44. package/build/core/template/support.js +108 -0
  45. package/build/core/template.d.ts +3 -6
  46. package/build/core/template.js +49 -4457
  47. package/build/data/capabilities.js +21 -4
  48. package/build/data/dependencyCatalog.js +10 -0
  49. package/build/data/publicDocs.d.ts +11 -0
  50. package/build/data/publicDocs.js +28 -0
  51. package/build/data/validatedMatrices.js +9 -1
  52. package/build/docs/render.d.ts +9 -0
  53. package/build/docs/render.js +250 -0
  54. package/build/types.d.ts +19 -0
  55. package/docs/cli-build.md +1 -1
  56. package/docs/npm-release.md +1 -0
  57. package/docs/official-app-shell-sample.md +2 -1
  58. package/docs/official-minimal-sample.md +2 -1
  59. package/docs/official-native-capabilities-sample.md +12 -2
  60. package/docs/official-ui-stack-sample.md +2 -1
  61. package/docs/roadmap.md +146 -51
  62. package/docs/signing-and-release.md +3 -0
  63. package/docs/support-matrix.md +59 -13
  64. package/package.json +13 -5
  65. package/docs/v1.5.1-acceptance.md +0 -385
  66. package/docs/v1.6.0-acceptance.md +0 -193
  67. package/docs/v1.7.0-acceptance.md +0 -111
  68. package/docs/v1.7.1-acceptance.md +0 -111
  69. package/docs/v1.7.2-acceptance.md +0 -37
@@ -3,46 +3,63 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.renderBuildReport = void 0;
6
7
  exports.bundleProject = bundleProject;
7
8
  exports.buildHapProject = buildHapProject;
8
- exports.renderBuildReport = renderBuildReport;
9
9
  exports.getDesiredHarmonyScripts = getDesiredHarmonyScripts;
10
- const node_child_process_1 = require("node:child_process");
11
- const node_util_1 = require("node:util");
12
10
  const fs_extra_1 = __importDefault(require("fs-extra"));
13
- const json5_1 = __importDefault(require("json5"));
14
- const os_1 = __importDefault(require("os"));
15
11
  const path_1 = __importDefault(require("path"));
16
- const constants_1 = require("./constants");
17
12
  const env_1 = require("./env");
18
13
  const javascriptDependencies_1 = require("./javascriptDependencies");
19
14
  const project_1 = require("./project");
20
15
  const template_1 = require("./template");
21
- const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
22
- const RNOH_GENERATED_TS_SHIM_RELATIVE_PATH = path_1.default.join('oh_modules', '@rnoh', 'react-native-openharmony', 'ts.ts');
23
- const RNOH_GENERATED_MODULE_ROOT_RELATIVE_PATH = path_1.default.join('oh_modules', '@rnoh', 'react-native-openharmony');
24
- const RNOH_NORMALIZED_TS_TARGET_RELATIVE_PATH = path_1.default.join('expo-harmony-local-deps', 'rnoh-react-native-openharmony-react_native_openharmony', 'ts.ts');
25
- const RNOH_CLI_AUTOLINKING_TEMPLATE_RELATIVE_PATHS = [
26
- path_1.default.join('node_modules', '@react-native-oh', 'react-native-harmony-cli', 'dist', 'autolinking', 'Autolinking.js'),
27
- path_1.default.join('node_modules', '@react-native-oh', 'react-native-harmony-cli', 'src', 'autolinking', 'Autolinking.ts'),
28
- ];
16
+ const autolinking_1 = require("./autolinking");
17
+ const localHar_1 = require("./build/localHar");
18
+ const commands_1 = require("./build/commands");
19
+ const reporting_1 = require("./build/reporting");
20
+ const rnohCompatibility_1 = require("./build/rnohCompatibility");
21
+ var reporting_2 = require("./build/reporting");
22
+ Object.defineProperty(exports, "renderBuildReport", { enumerable: true, get: function () { return reporting_2.renderBuildReport; } });
29
23
  async function bundleProject(projectRoot, options = {}) {
30
24
  const runtimeEnv = options.env ?? process.env;
31
- const runCommand = options.runner ?? defaultCommandRunner;
25
+ const runCommand = options.runner ?? commands_1.defaultCommandRunner;
32
26
  const loadedProject = await (0, project_1.loadProject)(projectRoot);
33
- const bundleOutputPath = path_1.default.join(loadedProject.projectRoot, 'harmony', 'entry', 'src', 'main', 'resources', 'rawfile', 'bundle.harmony.js');
34
- const assetsDestPath = path_1.default.join(loadedProject.projectRoot, 'harmony', 'entry', 'src', 'main', 'resources', 'rawfile', 'assets');
27
+ const bundleOutputPath = (0, reporting_1.getBundleOutputPath)(loadedProject.projectRoot);
28
+ const assetsDestPath = (0, reporting_1.getAssetsOutputPath)(loadedProject.projectRoot);
35
29
  const entryFileName = (0, template_1.resolveHarmonyBundleEntryFile)(loadedProject.packageJson);
36
30
  const entryFilePath = path_1.default.join(loadedProject.projectRoot, entryFileName);
37
31
  const metroConfigPath = path_1.default.join(loadedProject.projectRoot, 'metro.harmony.config.js');
38
32
  const reactNativeCliPath = path_1.default.join(loadedProject.projectRoot, 'node_modules', 'react-native', 'cli.js');
39
33
  const warnings = [];
40
34
  const blockingIssues = [];
41
- const syncResult = await (0, template_1.syncProjectTemplate)(loadedProject.projectRoot, false, {
42
- forceManagedPaths: template_1.BUILD_REQUIRED_MANAGED_FILE_PATHS,
43
- skipJavaScriptDependencyNormalization: options.skipTemplateSync,
44
- });
45
- warnings.push(...syncResult.warnings);
35
+ try {
36
+ const syncResult = await (0, template_1.syncProjectTemplate)(loadedProject.projectRoot, false, {
37
+ forceManagedPaths: template_1.BUILD_REQUIRED_MANAGED_FILE_PATHS,
38
+ skipJavaScriptDependencyNormalization: options.skipTemplateSync,
39
+ });
40
+ warnings.push(...syncResult.warnings);
41
+ }
42
+ catch (error) {
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ return (0, reporting_1.createBuildReport)({
45
+ projectRoot: loadedProject.projectRoot,
46
+ command: 'bundle',
47
+ mode: null,
48
+ status: 'failed',
49
+ entryFile: entryFilePath,
50
+ bundleOutputPath,
51
+ assetsDestPath,
52
+ artifactPaths: [],
53
+ blockingIssues: [
54
+ {
55
+ code: 'build.bundle.failed',
56
+ message,
57
+ },
58
+ ],
59
+ warnings,
60
+ steps: [],
61
+ });
62
+ }
46
63
  if (!(await fs_extra_1.default.pathExists(reactNativeCliPath))) {
47
64
  blockingIssues.push({
48
65
  code: 'build.bundle.failed',
@@ -61,7 +78,7 @@ async function bundleProject(projectRoot, options = {}) {
61
78
  message: 'metro.harmony.config.js is missing. Run expo-harmony init first.',
62
79
  });
63
80
  }
64
- const initialReport = createBuildReport({
81
+ const initialReport = (0, reporting_1.createBuildReport)({
65
82
  projectRoot: loadedProject.projectRoot,
66
83
  command: 'bundle',
67
84
  mode: null,
@@ -90,7 +107,7 @@ async function bundleProject(projectRoot, options = {}) {
90
107
  }
91
108
  catch (error) {
92
109
  const message = error instanceof Error ? error.message : String(error);
93
- return createBuildReport({
110
+ return (0, reporting_1.createBuildReport)({
94
111
  projectRoot: loadedProject.projectRoot,
95
112
  command: 'bundle',
96
113
  mode: null,
@@ -124,17 +141,17 @@ async function bundleProject(projectRoot, options = {}) {
124
141
  '--config',
125
142
  metroConfigPath,
126
143
  ];
127
- const bundleCommand = buildInvocation(process.execPath, bundleArgs);
144
+ const bundleCommand = (0, commands_1.buildInvocation)(process.execPath, bundleArgs);
128
145
  try {
129
146
  const bundleResult = await runCommand(bundleCommand.file, bundleCommand.args, {
130
147
  cwd: loadedProject.projectRoot,
131
148
  env: bundleEnvironment,
132
149
  });
133
150
  const steps = [
134
- createStepReport('bundle-harmony', bundleCommand.file, bundleCommand.args, loadedProject.projectRoot, bundleResult.exitCode),
151
+ (0, reporting_1.createStepReport)('bundle-harmony', bundleCommand.file, bundleCommand.args, loadedProject.projectRoot, bundleResult.exitCode),
135
152
  ];
136
153
  if (bundleResult.exitCode !== 0 || !(await fs_extra_1.default.pathExists(bundleOutputPath))) {
137
- return createBuildReport({
154
+ return (0, reporting_1.createBuildReport)({
138
155
  projectRoot: loadedProject.projectRoot,
139
156
  command: 'bundle',
140
157
  mode: null,
@@ -155,7 +172,7 @@ async function bundleProject(projectRoot, options = {}) {
155
172
  steps,
156
173
  });
157
174
  }
158
- return createBuildReport({
175
+ return (0, reporting_1.createBuildReport)({
159
176
  projectRoot: loadedProject.projectRoot,
160
177
  command: 'bundle',
161
178
  mode: null,
@@ -175,14 +192,15 @@ async function bundleProject(projectRoot, options = {}) {
175
192
  }
176
193
  async function buildHapProject(projectRoot, options) {
177
194
  const runtimeEnv = options.env ?? process.env;
178
- const runCommand = options.runner ?? defaultCommandRunner;
195
+ const runCommand = options.runner ?? commands_1.defaultCommandRunner;
179
196
  const loadedProject = await (0, project_1.loadProject)(projectRoot);
180
197
  const envReport = await (0, env_1.buildEnvReport)(loadedProject.projectRoot, {
181
198
  env: runtimeEnv,
182
199
  });
200
+ const relevantEnvAdvisories = envReport.advisories.filter((issue) => options.mode === 'release' || issue.code !== 'env.signing.missing');
183
201
  const warnings = [
184
202
  ...envReport.warnings.filter((warning) => !warning.startsWith('Harmony sidecar files are not present yet.')),
185
- ...envReport.advisories.map((issue) => `${issue.code}: ${issue.message}`),
203
+ ...relevantEnvAdvisories.map((issue) => `${issue.code}: ${issue.message}`),
186
204
  ];
187
205
  const blockingIssues = [];
188
206
  if (!envReport.hvigorPath) {
@@ -200,18 +218,18 @@ async function buildHapProject(projectRoot, options) {
200
218
  if (options.mode === 'release' && !envReport.signingConfigured) {
201
219
  blockingIssues.push({
202
220
  code: 'env.signing.missing',
203
- message: 'Release HAP builds require signingConfigs from .expo-harmony/signing.local.json or harmony/build-profile.json5.',
221
+ message: 'Release HAP builds require signingConfigs from .expo-harmony/signing.local.json or harmony/build-profile.json5. Start from .expo-harmony/signing.local.example.json and replace the placeholder credentials.',
204
222
  });
205
223
  }
206
224
  if (blockingIssues.length > 0) {
207
- return createBuildReport({
225
+ return (0, reporting_1.createBuildReport)({
208
226
  projectRoot: loadedProject.projectRoot,
209
227
  command: 'build-hap',
210
228
  mode: options.mode,
211
229
  status: 'failed',
212
230
  entryFile: path_1.default.join(loadedProject.projectRoot, (0, template_1.resolveHarmonyBundleEntryFile)(loadedProject.packageJson)),
213
- bundleOutputPath: getBundleOutputPath(loadedProject.projectRoot),
214
- assetsDestPath: getAssetsOutputPath(loadedProject.projectRoot),
231
+ bundleOutputPath: (0, reporting_1.getBundleOutputPath)(loadedProject.projectRoot),
232
+ assetsDestPath: (0, reporting_1.getAssetsOutputPath)(loadedProject.projectRoot),
215
233
  artifactPaths: [],
216
234
  blockingIssues,
217
235
  warnings,
@@ -224,7 +242,7 @@ async function buildHapProject(projectRoot, options) {
224
242
  });
225
243
  const harmonyProjectRoot = envReport.harmonyProjectRoot ?? path_1.default.join(loadedProject.projectRoot, 'harmony');
226
244
  if (bundleReport.status === 'failed') {
227
- return createBuildReport({
245
+ return (0, reporting_1.createBuildReport)({
228
246
  projectRoot: loadedProject.projectRoot,
229
247
  command: 'build-hap',
230
248
  mode: options.mode,
@@ -240,7 +258,7 @@ async function buildHapProject(projectRoot, options) {
240
258
  });
241
259
  }
242
260
  if (!(await fs_extra_1.default.pathExists(harmonyProjectRoot))) {
243
- return createBuildReport({
261
+ return (0, reporting_1.createBuildReport)({
244
262
  projectRoot: loadedProject.projectRoot,
245
263
  command: 'build-hap',
246
264
  mode: options.mode,
@@ -261,7 +279,7 @@ async function buildHapProject(projectRoot, options) {
261
279
  });
262
280
  }
263
281
  const steps = [...bundleReport.steps];
264
- const buildEnvironment = createHarmonyBuildEnvironment(runtimeEnv, envReport);
282
+ const buildEnvironment = (0, commands_1.createHarmonyBuildEnvironment)(runtimeEnv, envReport);
265
283
  let normalizedLocalHarPackages = [];
266
284
  let restoreNormalizedDependencies = async () => { };
267
285
  let restoreNormalizedLocalHarModules = async () => { };
@@ -269,18 +287,18 @@ async function buildHapProject(projectRoot, options) {
269
287
  let restoreNormalizedRnohCodegenAlignment = async () => { };
270
288
  let restoreNormalizedHarmonyPackageJsons = async () => { };
271
289
  try {
272
- const normalizedLocalHarDependencies = await normalizeLocalHarDependencies(harmonyProjectRoot);
290
+ const normalizedLocalHarDependencies = await (0, localHar_1.normalizeLocalHarDependencies)(harmonyProjectRoot);
273
291
  normalizedLocalHarPackages = normalizedLocalHarDependencies.packages;
274
292
  restoreNormalizedDependencies = normalizedLocalHarDependencies.restore;
275
- restoreNormalizedLocalHarModules = await ensureHarmonyBuildProfileSupportsNormalizedLocalDeps(harmonyProjectRoot, normalizedLocalHarDependencies.packages);
293
+ restoreNormalizedLocalHarModules = await (0, localHar_1.ensureHarmonyBuildProfileSupportsNormalizedLocalDeps)(harmonyProjectRoot, normalizedLocalHarDependencies.packages);
276
294
  restoreNormalizedRnohCliAutolinkingTemplates =
277
- await normalizeProjectRnohCliAutolinkingTemplates(loadedProject.projectRoot);
295
+ await (0, rnohCompatibility_1.normalizeProjectRnohCliAutolinkingTemplates)(loadedProject.projectRoot);
278
296
  restoreNormalizedHarmonyPackageJsons =
279
- await (0, template_1.normalizeKnownHarmonyPackageJsons)(loadedProject.projectRoot);
297
+ await (0, autolinking_1.normalizeKnownHarmonyPackageJsons)(loadedProject.projectRoot);
280
298
  }
281
299
  catch (error) {
282
300
  const message = error instanceof Error ? error.message : String(error);
283
- return createBuildReport({
301
+ return (0, reporting_1.createBuildReport)({
284
302
  projectRoot: loadedProject.projectRoot,
285
303
  command: 'build-hap',
286
304
  mode: options.mode,
@@ -300,16 +318,16 @@ async function buildHapProject(projectRoot, options) {
300
318
  steps,
301
319
  });
302
320
  }
303
- restoreNormalizedRnohCodegenAlignment = await alignRnohCodegenWithNormalizedLocalPackage(harmonyProjectRoot, normalizedLocalHarPackages);
304
- const ohpmCommand = buildInvocation(envReport.ohpmPath, ['install', '--all']);
321
+ restoreNormalizedRnohCodegenAlignment = await (0, localHar_1.alignRnohCodegenWithNormalizedLocalPackage)(harmonyProjectRoot, normalizedLocalHarPackages);
322
+ const ohpmCommand = (0, commands_1.buildInvocation)(envReport.ohpmPath, ['install', '--all']);
305
323
  try {
306
324
  const ohpmResult = await runCommand(ohpmCommand.file, ohpmCommand.args, {
307
325
  cwd: harmonyProjectRoot,
308
326
  env: buildEnvironment,
309
327
  });
310
- steps.push(createStepReport('ohpm install', ohpmCommand.file, ohpmCommand.args, harmonyProjectRoot, ohpmResult.exitCode));
328
+ steps.push((0, reporting_1.createStepReport)('ohpm install', ohpmCommand.file, ohpmCommand.args, harmonyProjectRoot, ohpmResult.exitCode));
311
329
  if (ohpmResult.exitCode !== 0) {
312
- return createBuildReport({
330
+ return (0, reporting_1.createBuildReport)({
313
331
  projectRoot: loadedProject.projectRoot,
314
332
  command: 'build-hap',
315
333
  mode: options.mode,
@@ -332,9 +350,9 @@ async function buildHapProject(projectRoot, options) {
332
350
  });
333
351
  }
334
352
  if (!normalizedLocalHarPackages.some((localPackage) => localPackage.packageName === '@rnoh/react-native-openharmony')) {
335
- await ensureRnohGeneratedTsShim(harmonyProjectRoot);
353
+ await (0, localHar_1.ensureRnohGeneratedTsShim)(harmonyProjectRoot);
336
354
  }
337
- const hvigorCommand = buildInvocation(envReport.hvigorPath, [
355
+ const hvigorCommand = (0, commands_1.buildInvocation)(envReport.hvigorPath, [
338
356
  'assembleHap',
339
357
  '--no-daemon',
340
358
  '-p',
@@ -346,10 +364,10 @@ async function buildHapProject(projectRoot, options) {
346
364
  cwd: harmonyProjectRoot,
347
365
  env: buildEnvironment,
348
366
  });
349
- steps.push(createStepReport('hvigor assembleHap', hvigorCommand.file, hvigorCommand.args, harmonyProjectRoot, hvigorResult.exitCode));
350
- const artifactPaths = await findHarmonyArtifacts(harmonyProjectRoot);
367
+ steps.push((0, reporting_1.createStepReport)('hvigor assembleHap', hvigorCommand.file, hvigorCommand.args, harmonyProjectRoot, hvigorResult.exitCode));
368
+ const artifactPaths = await (0, localHar_1.findHarmonyArtifacts)(harmonyProjectRoot);
351
369
  if (hvigorResult.exitCode !== 0 || artifactPaths.length === 0) {
352
- return createBuildReport({
370
+ return (0, reporting_1.createBuildReport)({
353
371
  projectRoot: loadedProject.projectRoot,
354
372
  command: 'build-hap',
355
373
  mode: options.mode,
@@ -371,7 +389,7 @@ async function buildHapProject(projectRoot, options) {
371
389
  steps,
372
390
  });
373
391
  }
374
- return createBuildReport({
392
+ return (0, reporting_1.createBuildReport)({
375
393
  projectRoot: loadedProject.projectRoot,
376
394
  command: 'build-hap',
377
395
  mode: options.mode,
@@ -394,716 +412,6 @@ async function buildHapProject(projectRoot, options) {
394
412
  await restoreNormalizedDependencies();
395
413
  }
396
414
  }
397
- function renderBuildReport(report) {
398
- const lines = [
399
- 'Expo Harmony build report',
400
- `Project: ${report.projectRoot}`,
401
- `Command: ${report.command}`,
402
- `Mode: ${report.mode ?? 'n/a'}`,
403
- `Status: ${report.status}`,
404
- `Harmony project: ${report.harmonyProjectRoot ?? 'not found'}`,
405
- `Entry file: ${report.entryFile ?? 'n/a'}`,
406
- `Bundle output: ${report.bundleOutputPath ?? 'n/a'}`,
407
- `Artifacts: ${report.artifactPaths.join(', ') || 'none'}`,
408
- '',
409
- 'Steps:',
410
- ...report.steps.map((step) => `- [${step.exitCode ?? 'n/a'}] ${step.label}: ${step.command} (cwd ${step.cwd})`),
411
- ];
412
- if (report.blockingIssues.length > 0) {
413
- lines.push('', 'Blocking issues:', ...report.blockingIssues.map((issue) => `- ${issue.code}: ${issue.message}${issue.subject ? ` (${issue.subject})` : ''}`));
414
- }
415
- if (report.warnings.length > 0) {
416
- lines.push('', 'Warnings:', ...report.warnings.map((warning) => `- ${warning}`));
417
- }
418
- return lines.join('\n');
419
- }
420
- function getBundleOutputPath(projectRoot) {
421
- return path_1.default.join(projectRoot, 'harmony', 'entry', 'src', 'main', 'resources', 'rawfile', 'bundle.harmony.js');
422
- }
423
- function getAssetsOutputPath(projectRoot) {
424
- return path_1.default.join(projectRoot, 'harmony', 'entry', 'src', 'main', 'resources', 'rawfile', 'assets');
425
- }
426
- function createBuildReport(input) {
427
- return {
428
- generatedAt: new Date().toISOString(),
429
- projectRoot: input.projectRoot,
430
- toolkitVersion: constants_1.TOOLKIT_VERSION,
431
- command: input.command,
432
- mode: input.mode,
433
- status: input.status,
434
- harmonyProjectRoot: input.harmonyProjectRoot ?? path_1.default.join(input.projectRoot, 'harmony'),
435
- entryFile: input.entryFile,
436
- bundleOutputPath: input.bundleOutputPath,
437
- assetsDestPath: input.assetsDestPath,
438
- artifactPaths: input.artifactPaths,
439
- blockingIssues: input.blockingIssues,
440
- warnings: input.warnings,
441
- steps: input.steps,
442
- };
443
- }
444
- function createStepReport(label, file, args, cwd, exitCode) {
445
- return {
446
- label,
447
- command: [file, ...args].join(' '),
448
- cwd,
449
- exitCode,
450
- };
451
- }
452
- function buildInvocation(file, args) {
453
- if (file.endsWith('.js')) {
454
- return {
455
- file: process.execPath,
456
- args: [file, ...args],
457
- };
458
- }
459
- return {
460
- file,
461
- args,
462
- };
463
- }
464
- function createHarmonyBuildEnvironment(runtimeEnv, envReport) {
465
- const buildEnvironment = {
466
- ...runtimeEnv,
467
- };
468
- if (envReport.sdkRoot) {
469
- buildEnvironment.DEVECO_SDK_HOME = envReport.sdkRoot;
470
- buildEnvironment.OHOS_BASE_SDK_HOME = envReport.sdkRoot;
471
- }
472
- if (envReport.devecoStudioPath) {
473
- buildEnvironment.NODE_HOME =
474
- buildEnvironment.NODE_HOME ??
475
- path_1.default.join(envReport.devecoStudioPath, 'Contents', 'tools', 'node');
476
- }
477
- return buildEnvironment;
478
- }
479
- async function normalizeLocalHarDependencies(harmonyProjectRoot) {
480
- const packagePaths = [
481
- path_1.default.join(harmonyProjectRoot, 'oh-package.json5'),
482
- path_1.default.join(harmonyProjectRoot, 'entry', 'oh-package.json5'),
483
- ];
484
- const extractionRoot = path_1.default.join(harmonyProjectRoot, 'expo-harmony-local-deps');
485
- const archiveCache = new Map();
486
- const originalContents = new Map();
487
- const packagesByDirectoryPath = new Map();
488
- await fs_extra_1.default.ensureDir(extractionRoot);
489
- for (const packagePath of packagePaths) {
490
- if (!(await fs_extra_1.default.pathExists(packagePath))) {
491
- continue;
492
- }
493
- const rawContents = await fs_extra_1.default.readFile(packagePath, 'utf8');
494
- const parsed = json5_1.default.parse(rawContents);
495
- let didChange = false;
496
- for (const sectionName of ['dependencies', 'devDependencies', 'overrides']) {
497
- const section = parsed[sectionName];
498
- if (!section || typeof section !== 'object' || Array.isArray(section)) {
499
- continue;
500
- }
501
- for (const [packageName, specifier] of Object.entries(section)) {
502
- if (typeof specifier !== 'string' || !specifier.startsWith('file:') || !specifier.endsWith('.har')) {
503
- continue;
504
- }
505
- const archivePath = path_1.default.resolve(harmonyProjectRoot, specifier.slice('file:'.length));
506
- const normalizedDirectory = archiveCache.get(archivePath) ??
507
- (await extractHarArchiveToDirectory(archivePath, extractionRoot, packageName));
508
- archiveCache.set(archivePath, normalizedDirectory);
509
- if (!packagesByDirectoryPath.has(normalizedDirectory)) {
510
- packagesByDirectoryPath.set(normalizedDirectory, await readNormalizedLocalHarPackageMetadata(normalizedDirectory, packageName));
511
- }
512
- let relativeDirectory = path_1.default.relative(harmonyProjectRoot, normalizedDirectory);
513
- if (!relativeDirectory.startsWith('.')) {
514
- relativeDirectory = `./${relativeDirectory}`;
515
- }
516
- const nextSpecifier = `file:${relativeDirectory.split(path_1.default.sep).join('/')}`;
517
- if (nextSpecifier !== specifier) {
518
- section[packageName] = nextSpecifier;
519
- didChange = true;
520
- }
521
- }
522
- }
523
- if (didChange) {
524
- originalContents.set(packagePath, rawContents);
525
- await fs_extra_1.default.writeFile(packagePath, JSON.stringify(parsed, null, 2) + '\n');
526
- }
527
- }
528
- return {
529
- packages: [...packagesByDirectoryPath.values()],
530
- restore: async () => {
531
- for (const [packagePath, rawContents] of originalContents) {
532
- await fs_extra_1.default.writeFile(packagePath, rawContents);
533
- }
534
- },
535
- };
536
- }
537
- async function extractHarArchiveToDirectory(archivePath, extractionRoot, packageName) {
538
- if (!(await fs_extra_1.default.pathExists(archivePath))) {
539
- throw new Error(`Local Harmony archive not found: ${archivePath}`);
540
- }
541
- const destinationPath = path_1.default.join(extractionRoot, `${sanitizePackageName(packageName)}-${path_1.default.basename(archivePath, path_1.default.extname(archivePath))}`);
542
- const stagingRoot = await fs_extra_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'expo-harmony-har-'));
543
- try {
544
- await execFileAsync('tar', ['-xzf', archivePath, '-C', stagingRoot], {
545
- maxBuffer: 20 * 1024 * 1024,
546
- });
547
- const packagedRoot = path_1.default.join(stagingRoot, 'package');
548
- const sourceRoot = (await fs_extra_1.default.pathExists(path_1.default.join(packagedRoot, 'oh-package.json5')))
549
- ? packagedRoot
550
- : stagingRoot;
551
- if (!(await fs_extra_1.default.pathExists(path_1.default.join(sourceRoot, 'oh-package.json5')))) {
552
- throw new Error(`oh-package.json5 not found after extracting ${archivePath}`);
553
- }
554
- await fs_extra_1.default.remove(destinationPath);
555
- await fs_extra_1.default.ensureDir(destinationPath);
556
- for (const entryName of await fs_extra_1.default.readdir(sourceRoot)) {
557
- await fs_extra_1.default.move(path_1.default.join(sourceRoot, entryName), path_1.default.join(destinationPath, entryName), {
558
- overwrite: true,
559
- });
560
- }
561
- await ensureNormalizedLocalHarCompatibilityShims(destinationPath, packageName);
562
- return destinationPath;
563
- }
564
- catch (error) {
565
- const message = error instanceof Error ? error.message : String(error);
566
- throw new Error(`Failed to normalize local Harmony archive ${archivePath}: ${message}`);
567
- }
568
- finally {
569
- await fs_extra_1.default.remove(stagingRoot);
570
- }
571
- }
572
- async function ensureNormalizedLocalHarCompatibilityShims(directoryPath, packageName) {
573
- if (packageName === '@react-native-oh-tpl/react-native-gesture-handler') {
574
- const gestureHandlerPackageHeaderPath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'RnohReactNativeHarmonyGestureHandlerPackage.h');
575
- const gestureHandlerPackagePath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'RnohReactNativeHarmonyGestureHandlerPackage.cpp');
576
- const gestureHandlerTurboModulePath = path_1.default.join(directoryPath, 'src', 'main', 'ets', 'rnoh', 'RNGestureHandlerModule.ts');
577
- if (await fs_extra_1.default.pathExists(gestureHandlerTurboModulePath)) {
578
- const gestureHandlerTurboModuleContents = await fs_extra_1.default.readFile(gestureHandlerTurboModulePath, 'utf8');
579
- let nextGestureHandlerTurboModuleContents = gestureHandlerTurboModuleContents;
580
- nextGestureHandlerTurboModuleContents = nextGestureHandlerTurboModuleContents.replace(/^import \{ TM \} from ["']@rnoh\/react-native-openharmony\/generated\/ts["'];?\r?\n/m, '');
581
- nextGestureHandlerTurboModuleContents = nextGestureHandlerTurboModuleContents.replace(/\s+implements TM\.RNGestureHandlerModule\.Spec/, '');
582
- if (nextGestureHandlerTurboModuleContents !== gestureHandlerTurboModuleContents) {
583
- await fs_extra_1.default.writeFile(gestureHandlerTurboModulePath, nextGestureHandlerTurboModuleContents);
584
- }
585
- }
586
- const gestureHandlerGeneratedDirectoryPath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'generated');
587
- await fs_extra_1.default.ensureDir(gestureHandlerGeneratedDirectoryPath);
588
- await fs_extra_1.default.writeFile(path_1.default.join(gestureHandlerGeneratedDirectoryPath, 'RNGestureHandlerButtonComponentDescriptor.h'), createGestureHandlerCompatibilityDescriptorHeader('RNGestureHandlerButton'));
589
- await fs_extra_1.default.writeFile(path_1.default.join(gestureHandlerGeneratedDirectoryPath, 'RNGestureHandlerRootViewComponentDescriptor.h'), createGestureHandlerCompatibilityDescriptorHeader('RNGestureHandlerRootView'));
590
- if (await fs_extra_1.default.pathExists(gestureHandlerPackageHeaderPath)) {
591
- const gestureHandlerPackageHeaderContents = await fs_extra_1.default.readFile(gestureHandlerPackageHeaderPath, 'utf8');
592
- const nextGestureHandlerPackageHeaderContents = patchGestureHandlerCompatibilityPackageHeader(gestureHandlerPackageHeaderContents);
593
- if (nextGestureHandlerPackageHeaderContents !== gestureHandlerPackageHeaderContents) {
594
- await fs_extra_1.default.writeFile(gestureHandlerPackageHeaderPath, nextGestureHandlerPackageHeaderContents);
595
- }
596
- }
597
- if (!(await fs_extra_1.default.pathExists(gestureHandlerPackagePath))) {
598
- return;
599
- }
600
- const gestureHandlerPackageContents = await fs_extra_1.default.readFile(gestureHandlerPackagePath, 'utf8');
601
- const nextGestureHandlerPackageContents = patchGestureHandlerCompatibilityPackageSource(gestureHandlerPackageContents);
602
- if (nextGestureHandlerPackageContents !== gestureHandlerPackageContents) {
603
- await fs_extra_1.default.writeFile(gestureHandlerPackagePath, nextGestureHandlerPackageContents);
604
- }
605
- if (!nextGestureHandlerPackageContents.includes('<react/renderer/debug/SystraceSection.h>')) {
606
- return;
607
- }
608
- const systraceCompatibilityShimPath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'react', 'renderer', 'debug', 'SystraceSection.h');
609
- await fs_extra_1.default.ensureDir(path_1.default.dirname(systraceCompatibilityShimPath));
610
- await fs_extra_1.default.writeFile(systraceCompatibilityShimPath, '#pragma once\n#include <cxxreact/SystraceSection.h>\n');
611
- return;
612
- }
613
- if (packageName === '@react-native-oh-tpl/react-native-reanimated') {
614
- const reanimatedCmakeListsPath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'CMakeLists.txt');
615
- const reanimatedMountHookHeaderPath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'Common', 'cpp', 'Fabric', 'ReanimatedMountHook.h');
616
- const reanimatedMountHookSourcePath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'Common', 'cpp', 'Fabric', 'ReanimatedMountHook.cpp');
617
- const nativeReanimatedModuleSourcePath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'Common', 'cpp', 'NativeModules', 'NativeReanimatedModule.cpp');
618
- if (await fs_extra_1.default.pathExists(reanimatedCmakeListsPath)) {
619
- const reanimatedCmakeListsContents = await fs_extra_1.default.readFile(reanimatedCmakeListsPath, 'utf8');
620
- if (reanimatedCmakeListsContents.includes('REACT_NATIVE_MINOR_VERSION=72')) {
621
- await fs_extra_1.default.writeFile(reanimatedCmakeListsPath, reanimatedCmakeListsContents.replace('REACT_NATIVE_MINOR_VERSION=72', 'REACT_NATIVE_MINOR_VERSION=82'));
622
- }
623
- }
624
- if (await fs_extra_1.default.pathExists(reanimatedMountHookHeaderPath)) {
625
- const reanimatedMountHookHeaderContents = await fs_extra_1.default.readFile(reanimatedMountHookHeaderPath, 'utf8');
626
- if (reanimatedMountHookHeaderContents.includes('double mountTime) noexcept override;')) {
627
- await fs_extra_1.default.writeFile(reanimatedMountHookHeaderPath, reanimatedMountHookHeaderContents.replace('double mountTime) noexcept override;', 'HighResTimeStamp mountTime) noexcept override;'));
628
- }
629
- }
630
- if (await fs_extra_1.default.pathExists(reanimatedMountHookSourcePath)) {
631
- const reanimatedMountHookSourceContents = await fs_extra_1.default.readFile(reanimatedMountHookSourcePath, 'utf8');
632
- if (reanimatedMountHookSourceContents.includes('double) noexcept {')) {
633
- await fs_extra_1.default.writeFile(reanimatedMountHookSourcePath, reanimatedMountHookSourceContents.replace('double) noexcept {', 'HighResTimeStamp) noexcept {'));
634
- }
635
- }
636
- if (await fs_extra_1.default.pathExists(nativeReanimatedModuleSourcePath)) {
637
- const nativeReanimatedModuleSourceContents = await fs_extra_1.default.readFile(nativeReanimatedModuleSourcePath, 'utf8');
638
- let nextNativeReanimatedModuleSourceContents = nativeReanimatedModuleSourceContents;
639
- if (nextNativeReanimatedModuleSourceContents.includes('<react/renderer/core/TraitCast.h>')) {
640
- nextNativeReanimatedModuleSourceContents = nextNativeReanimatedModuleSourceContents.replace('<react/renderer/core/TraitCast.h>', '<react/renderer/core/LayoutableShadowNode.h>');
641
- }
642
- if (nextNativeReanimatedModuleSourceContents.includes('#include <react/utils/CoreFeatures.h>')) {
643
- nextNativeReanimatedModuleSourceContents = nextNativeReanimatedModuleSourceContents.replace('#include <react/utils/CoreFeatures.h>\n', '');
644
- nextNativeReanimatedModuleSourceContents = nextNativeReanimatedModuleSourceContents.replace('#include <react/utils/CoreFeatures.h>\r\n', '');
645
- }
646
- if (nextNativeReanimatedModuleSourceContents.includes('traitCast<LayoutableShadowNode const *>(newestCloneOfShadowNode.get())')) {
647
- nextNativeReanimatedModuleSourceContents = nextNativeReanimatedModuleSourceContents.replace('traitCast<LayoutableShadowNode const *>(newestCloneOfShadowNode.get())', 'dynamic_cast<LayoutableShadowNode const *>(newestCloneOfShadowNode.get())');
648
- }
649
- if (nextNativeReanimatedModuleSourceContents.includes('shadowNodeFromValue(rt, shadowNodeWrapper)')) {
650
- nextNativeReanimatedModuleSourceContents =
651
- nextNativeReanimatedModuleSourceContents.replace(/auto (\w+) = shadowNodeFromValue\(rt, shadowNodeWrapper\);/g, 'auto $1 = Bridging<std::shared_ptr<const ShadowNode>>::fromJs(rt, shadowNodeWrapper);');
652
- }
653
- if (nextNativeReanimatedModuleSourceContents.includes('shadowNodeFromValue(rt, shadowNodeValue)')) {
654
- nextNativeReanimatedModuleSourceContents =
655
- nextNativeReanimatedModuleSourceContents.replace(/(ShadowNode::Shared|auto) (\w+) = shadowNodeFromValue\(rt, shadowNodeValue\);/g, 'auto $2 = Bridging<std::shared_ptr<const ShadowNode>>::fromJs(rt, shadowNodeValue);');
656
- }
657
- if (nextNativeReanimatedModuleSourceContents.includes('bool CoreFeatures::useNativeState;')) {
658
- nextNativeReanimatedModuleSourceContents = nextNativeReanimatedModuleSourceContents.replace(/#if REACT_NATIVE_MINOR_VERSION >= 73 && defined\(RCT_NEW_ARCH_ENABLED\)\r?\n\/\/ Android can't find the definition of this static field\r?\nbool CoreFeatures::useNativeState;\r?\n#endif\r?\n/m, '');
659
- }
660
- if (nextNativeReanimatedModuleSourceContents.includes('/* .shouldYield = */ [this]() {')) {
661
- nextNativeReanimatedModuleSourceContents =
662
- nextNativeReanimatedModuleSourceContents.replace(/#if REACT_NATIVE_MINOR_VERSION >= 72\r?\n\s*\/\* \.mountSynchronously = \*\/ true,\r?\n#endif\r?\n\s*\/\* \.shouldYield = \*\/ \[this\]\(\) \{\r?\n\s*return propsRegistry_->shouldReanimatedSkipCommit\(\);\r?\n\s*\}/m, '#if REACT_NATIVE_MINOR_VERSION >= 72\n'
663
- + ' /* .mountSynchronously = */ true,\n'
664
- + '#endif\n'
665
- + ' /* .source = */ ShadowTree::CommitSource::Unknown');
666
- }
667
- if (nextNativeReanimatedModuleSourceContents !== nativeReanimatedModuleSourceContents) {
668
- await fs_extra_1.default.writeFile(nativeReanimatedModuleSourcePath, nextNativeReanimatedModuleSourceContents);
669
- }
670
- }
671
- return;
672
- }
673
- if (packageName !== '@react-native-oh-tpl/react-native-svg') {
674
- return;
675
- }
676
- const svgGeneratedPropsPath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'generated', 'react', 'renderer', 'components', 'react_native_svg', 'Props.h');
677
- const svgPathComponentInstancePath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'componentInstances', 'RNSVGPathComponentInstance.h');
678
- const svgImageComponentDescriptorPath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'svgImage', 'RNSVGImageComponentDescriptor.h');
679
- const svgImageShadowNodePath = path_1.default.join(directoryPath, 'src', 'main', 'cpp', 'svgImage', 'RNSVGImageShadowNode.h');
680
- if (await fs_extra_1.default.pathExists(svgGeneratedPropsPath)) {
681
- const svgGeneratedPropsContents = await fs_extra_1.default.readFile(svgGeneratedPropsPath, 'utf8');
682
- if (svgGeneratedPropsContents.includes('butter::map<std::string, RawValue>')) {
683
- await fs_extra_1.default.writeFile(svgGeneratedPropsPath, svgGeneratedPropsContents.replace(/butter::map<std::string, RawValue>/g, 'std::unordered_map<std::string, RawValue>'));
684
- }
685
- }
686
- if (await fs_extra_1.default.pathExists(svgImageComponentDescriptorPath)) {
687
- const svgImageComponentDescriptorContents = await fs_extra_1.default.readFile(svgImageComponentDescriptorPath, 'utf8');
688
- let nextSvgImageComponentDescriptorContents = svgImageComponentDescriptorContents;
689
- if (nextSvgImageComponentDescriptorContents.includes('void adopt(ShadowNode::Unshared const &shadowNode) const override {')) {
690
- nextSvgImageComponentDescriptorContents =
691
- nextSvgImageComponentDescriptorContents.replace('void adopt(ShadowNode::Unshared const &shadowNode) const override {', 'void adopt(ShadowNode& shadowNode) const override {');
692
- }
693
- if (nextSvgImageComponentDescriptorContents.includes('auto imageShadowNode = std::static_pointer_cast<RNSVGImageShadowNode>(shadowNode);')) {
694
- nextSvgImageComponentDescriptorContents =
695
- nextSvgImageComponentDescriptorContents.replace('auto imageShadowNode = std::static_pointer_cast<RNSVGImageShadowNode>(shadowNode);', 'auto& imageShadowNode = static_cast<RNSVGImageShadowNode&>(shadowNode);');
696
- }
697
- if (nextSvgImageComponentDescriptorContents.includes('imageShadowNode->setImageManager(imageManager_);')) {
698
- nextSvgImageComponentDescriptorContents =
699
- nextSvgImageComponentDescriptorContents.replace('imageShadowNode->setImageManager(imageManager_);', 'imageShadowNode.setImageManager(imageManager_);');
700
- }
701
- if (nextSvgImageComponentDescriptorContents !== svgImageComponentDescriptorContents) {
702
- await fs_extra_1.default.writeFile(svgImageComponentDescriptorPath, nextSvgImageComponentDescriptorContents);
703
- }
704
- }
705
- if (await fs_extra_1.default.pathExists(svgImageShadowNodePath)) {
706
- const svgImageShadowNodeContents = await fs_extra_1.default.readFile(svgImageShadowNodePath, 'utf8');
707
- let nextSvgImageShadowNodeContents = svgImageShadowNodeContents;
708
- const svgImageShadowNodeNewline = svgImageShadowNodeContents.includes('\r\n') ? '\r\n' : '\n';
709
- if (!nextSvgImageShadowNodeContents.includes('#include <react/renderer/core/ShadowNodeFamily.h>')
710
- && nextSvgImageShadowNodeContents.includes('#include <react/renderer/components/view/ConcreteViewShadowNode.h>')) {
711
- nextSvgImageShadowNodeContents = nextSvgImageShadowNodeContents.replace('#include <react/renderer/components/view/ConcreteViewShadowNode.h>', '#include <react/renderer/components/view/ConcreteViewShadowNode.h>'
712
- + `${svgImageShadowNodeNewline}#include <react/renderer/core/ShadowNodeFamily.h>`);
713
- }
714
- const svgImageInitialStateSignaturePattern = /static RNSVGImageState initialStateData\(ShadowNodeFragment const &fragment,\r?\n\s*ShadowNodeFamilyFragment const &familyFragment,\r?\n\s*ComponentDescriptor const &componentDescriptor\) \{/m;
715
- if (svgImageInitialStateSignaturePattern.test(nextSvgImageShadowNodeContents)) {
716
- nextSvgImageShadowNodeContents = nextSvgImageShadowNodeContents.replace(svgImageInitialStateSignaturePattern, 'static RNSVGImageState initialStateData(const Props::Shared& /*props*/,'
717
- + `${svgImageShadowNodeNewline}`
718
- + ' const ShadowNodeFamily::Shared& /*family*/,'
719
- + `${svgImageShadowNodeNewline}`
720
- + ' const ComponentDescriptor& /*componentDescriptor*/) {');
721
- }
722
- if (nextSvgImageShadowNodeContents !== svgImageShadowNodeContents) {
723
- await fs_extra_1.default.writeFile(svgImageShadowNodePath, nextSvgImageShadowNodeContents);
724
- }
725
- }
726
- if (!(await fs_extra_1.default.pathExists(svgPathComponentInstancePath))) {
727
- return;
728
- }
729
- const svgPathComponentInstanceContents = await fs_extra_1.default.readFile(svgPathComponentInstancePath, 'utf8');
730
- if (!svgPathComponentInstanceContents.includes('Float m_cacheScale;')) {
731
- return;
732
- }
733
- await fs_extra_1.default.writeFile(svgPathComponentInstancePath, svgPathComponentInstanceContents.replace('Float m_cacheScale;', 'facebook::react::Float m_cacheScale;'));
734
- }
735
- function createGestureHandlerCompatibilityDescriptorHeader(componentName) {
736
- return [
737
- '#pragma once',
738
- '',
739
- '#include <react/renderer/components/view/ConcreteViewShadowNode.h>',
740
- '#include <react/renderer/core/ConcreteComponentDescriptor.h>',
741
- '',
742
- 'namespace facebook::react {',
743
- '',
744
- `inline constexpr char ${componentName}ComponentName[] = "${componentName}";`,
745
- '',
746
- `using ${componentName}ShadowNode = ConcreteViewShadowNode<${componentName}ComponentName>;`,
747
- `using ${componentName}ComponentDescriptor = ConcreteComponentDescriptor<${componentName}ShadowNode>;`,
748
- '',
749
- '} // namespace facebook::react',
750
- '',
751
- ].join('\n');
752
- }
753
- function patchGestureHandlerCompatibilityPackageHeader(contents) {
754
- if (contents.includes('createComponentDescriptorProviders() override;')) {
755
- return contents;
756
- }
757
- return contents.replace(' std::vector<ArkTSMessageHandler::Shared> createArkTSMessageHandlers() override;\n', [
758
- ' std::vector<facebook::react::ComponentDescriptorProvider> createComponentDescriptorProviders() override;',
759
- '',
760
- ' ComponentJSIBinderByString createComponentJSIBinderByName() override;',
761
- '',
762
- ' std::vector<ArkTSMessageHandler::Shared> createArkTSMessageHandlers() override;',
763
- ].join('\n'));
764
- }
765
- function patchGestureHandlerCompatibilityPackageSource(contents) {
766
- let nextContents = contents;
767
- if (!nextContents.includes('#include "RNOHCorePackage/ComponentBinders/ViewComponentJSIBinder.h"')) {
768
- nextContents = nextContents.replace('#include "RNOH/RNInstanceCAPI.h"\n', '#include "RNOH/RNInstanceCAPI.h"\n#include "RNOHCorePackage/ComponentBinders/ViewComponentJSIBinder.h"\n');
769
- }
770
- if (!nextContents.includes('#include "generated/RNGestureHandlerButtonComponentDescriptor.h"')) {
771
- nextContents = nextContents.replace('#include "componentInstances/RNGestureHandlerRootViewComponentInstance.h"\n', '#include "componentInstances/RNGestureHandlerRootViewComponentInstance.h"\n#include "generated/RNGestureHandlerButtonComponentDescriptor.h"\n#include "generated/RNGestureHandlerRootViewComponentDescriptor.h"\n');
772
- }
773
- if (!nextContents.includes('class RNGestureHandlerComponentJSIBinder')) {
774
- nextContents = nextContents.replace('using namespace rnoh;\nusing namespace facebook;\n\n', [
775
- 'using namespace rnoh;',
776
- 'using namespace facebook;',
777
- '',
778
- 'class RNGestureHandlerComponentJSIBinder : public ViewComponentJSIBinder {',
779
- 'protected:',
780
- ' facebook::jsi::Object createNativeProps(facebook::jsi::Runtime &rt) override {',
781
- ' auto nativeProps = ViewComponentJSIBinder::createNativeProps(rt);',
782
- ' nativeProps.setProperty(rt, "exclusive", "boolean");',
783
- ' nativeProps.setProperty(rt, "foreground", "boolean");',
784
- ' nativeProps.setProperty(rt, "borderless", "boolean");',
785
- ' nativeProps.setProperty(rt, "enabled", "boolean");',
786
- ' nativeProps.setProperty(rt, "rippleColor", "Color");',
787
- ' nativeProps.setProperty(rt, "rippleRadius", "number");',
788
- ' nativeProps.setProperty(rt, "touchSoundDisabled", "boolean");',
789
- ' return nativeProps;',
790
- ' }',
791
- '',
792
- ' facebook::jsi::Object createDirectEventTypes(facebook::jsi::Runtime &rt) override {',
793
- ' auto events = ViewComponentJSIBinder::createDirectEventTypes(rt);',
794
- ' events.setProperty(rt, "onGestureHandlerEvent", createDirectEvent(rt, "onGestureHandlerEvent"));',
795
- ' events.setProperty(',
796
- ' rt,',
797
- ' "onGestureHandlerStateChange",',
798
- ' createDirectEvent(rt, "onGestureHandlerStateChange"));',
799
- ' events.setProperty(rt, "topOnGestureHandlerEvent", createDirectEvent(rt, "onGestureHandlerEvent"));',
800
- ' events.setProperty(',
801
- ' rt,',
802
- ' "topOnGestureHandlerStateChange",',
803
- ' createDirectEvent(rt, "onGestureHandlerStateChange"));',
804
- ' return events;',
805
- ' }',
806
- '};',
807
- '',
808
- ].join('\n'));
809
- }
810
- if (!nextContents.includes('RnohReactNativeHarmonyGestureHandlerPackage::createComponentDescriptorProviders()')) {
811
- nextContents = nextContents.replace('EventEmitRequestHandlers RnohReactNativeHarmonyGestureHandlerPackage::createEventEmitRequestHandlers() {\n', [
812
- 'std::vector<facebook::react::ComponentDescriptorProvider>',
813
- 'RnohReactNativeHarmonyGestureHandlerPackage::createComponentDescriptorProviders() {',
814
- ' return {',
815
- ' facebook::react::concreteComponentDescriptorProvider<',
816
- ' facebook::react::RNGestureHandlerButtonComponentDescriptor>(),',
817
- ' facebook::react::concreteComponentDescriptorProvider<',
818
- ' facebook::react::RNGestureHandlerRootViewComponentDescriptor>(),',
819
- ' };',
820
- '}',
821
- '',
822
- 'ComponentJSIBinderByString',
823
- 'RnohReactNativeHarmonyGestureHandlerPackage::createComponentJSIBinderByName() {',
824
- ' auto componentJSIBinder = std::make_shared<RNGestureHandlerComponentJSIBinder>();',
825
- ' return {',
826
- ' {"RNGestureHandlerButton", componentJSIBinder},',
827
- ' {"RNGestureHandlerRootView", componentJSIBinder},',
828
- ' };',
829
- '}',
830
- '',
831
- 'EventEmitRequestHandlers RnohReactNativeHarmonyGestureHandlerPackage::createEventEmitRequestHandlers() {',
832
- ].join('\n'));
833
- }
834
- return nextContents;
835
- }
836
- function sanitizePackageName(packageName) {
837
- return packageName.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
838
- }
839
- async function readNormalizedLocalHarPackageMetadata(directoryPath, packageName) {
840
- await ensureNormalizedLocalHarModuleJson5(directoryPath);
841
- const moduleJsonPaths = [
842
- path_1.default.join(directoryPath, 'src', 'main', 'module.json5'),
843
- path_1.default.join(directoryPath, 'src', 'main', 'module.json'),
844
- ];
845
- let moduleName = sanitizePackageName(packageName);
846
- for (const moduleJsonPath of moduleJsonPaths) {
847
- if (!(await fs_extra_1.default.pathExists(moduleJsonPath))) {
848
- continue;
849
- }
850
- try {
851
- const parsed = json5_1.default.parse(await fs_extra_1.default.readFile(moduleJsonPath, 'utf8'));
852
- if (typeof parsed?.module?.name === 'string' && parsed.module.name.trim().length > 0) {
853
- moduleName = parsed.module.name.trim();
854
- break;
855
- }
856
- }
857
- catch {
858
- // Ignore malformed module metadata and fall back to a sanitized package name.
859
- }
860
- }
861
- return {
862
- packageName,
863
- moduleName,
864
- directoryPath,
865
- };
866
- }
867
- async function ensureNormalizedLocalHarModuleJson5(directoryPath) {
868
- const moduleJson5Path = path_1.default.join(directoryPath, 'src', 'main', 'module.json5');
869
- const moduleJsonPath = path_1.default.join(directoryPath, 'src', 'main', 'module.json');
870
- const sourcePath = (await fs_extra_1.default.pathExists(moduleJsonPath))
871
- ? moduleJsonPath
872
- : ((await fs_extra_1.default.pathExists(moduleJson5Path)) ? moduleJson5Path : null);
873
- if (!sourcePath) {
874
- return;
875
- }
876
- const parsed = json5_1.default.parse(await fs_extra_1.default.readFile(sourcePath, 'utf8'));
877
- const legacyModule = parsed.module && typeof parsed.module === 'object' && !Array.isArray(parsed.module)
878
- ? parsed.module
879
- : {};
880
- const normalizedModule = {
881
- name: typeof legacyModule.name === 'string' && legacyModule.name.trim().length > 0
882
- ? legacyModule.name.trim()
883
- : path_1.default.basename(directoryPath).replace(/[^a-zA-Z0-9]+/g, '_'),
884
- type: typeof legacyModule.type === 'string' ? legacyModule.type : 'har',
885
- deviceTypes: Array.isArray(legacyModule.deviceTypes) && legacyModule.deviceTypes.length > 0
886
- ? legacyModule.deviceTypes
887
- : ['default'],
888
- installationFree: typeof legacyModule.installationFree === 'boolean' ? legacyModule.installationFree : false,
889
- };
890
- const passthroughKeys = [
891
- 'srcEntrance',
892
- 'srcEntry',
893
- 'description',
894
- 'process',
895
- 'mainElement',
896
- 'uiSyntax',
897
- 'metadata',
898
- 'abilities',
899
- 'extensionAbilities',
900
- 'requestPermissions',
901
- 'definePermissions',
902
- 'testRunner',
903
- 'dependencies',
904
- 'libIsolation',
905
- 'routerMap',
906
- 'appStartup',
907
- 'crossAppSharedConfig',
908
- ];
909
- for (const key of passthroughKeys) {
910
- const value = legacyModule[key];
911
- if (value !== undefined) {
912
- normalizedModule[key] = value;
913
- }
914
- }
915
- if (typeof legacyModule.virtualMachine === 'string') {
916
- normalizedModule.virtualMachine = legacyModule.virtualMachine.includes('ark') ? 'ark' : 'default';
917
- }
918
- await fs_extra_1.default.writeFile(moduleJson5Path, JSON.stringify({
919
- module: normalizedModule,
920
- }, null, 2) + '\n');
921
- }
922
- async function ensureHarmonyBuildProfileSupportsNormalizedLocalDeps(harmonyProjectRoot, localHarPackages) {
923
- if (localHarPackages.length === 0) {
924
- return async () => { };
925
- }
926
- const buildProfilePath = path_1.default.join(harmonyProjectRoot, 'build-profile.json5');
927
- if (!(await fs_extra_1.default.pathExists(buildProfilePath))) {
928
- return async () => { };
929
- }
930
- const rawContents = await fs_extra_1.default.readFile(buildProfilePath, 'utf8');
931
- const parsed = json5_1.default.parse(rawContents);
932
- let didChange = false;
933
- const products = Array.isArray(parsed.app?.products) ? parsed.app.products : [];
934
- for (const product of products) {
935
- let buildOption = product.buildOption;
936
- if (!buildOption || typeof buildOption !== 'object' || Array.isArray(buildOption)) {
937
- buildOption = {};
938
- product.buildOption = buildOption;
939
- didChange = true;
940
- }
941
- let strictMode = buildOption.strictMode;
942
- if (!strictMode || typeof strictMode !== 'object' || Array.isArray(strictMode)) {
943
- strictMode = {};
944
- buildOption.strictMode = strictMode;
945
- didChange = true;
946
- }
947
- if (strictMode.useNormalizedOHMUrl !== true) {
948
- strictMode.useNormalizedOHMUrl = true;
949
- didChange = true;
950
- }
951
- }
952
- if (!Array.isArray(parsed.modules)) {
953
- parsed.modules = [];
954
- didChange = true;
955
- }
956
- for (const localHarPackage of localHarPackages) {
957
- let relativeDirectory = path_1.default.relative(harmonyProjectRoot, localHarPackage.directoryPath);
958
- relativeDirectory = relativeDirectory.split(path_1.default.sep).join('/');
959
- if (!relativeDirectory.startsWith('.')) {
960
- relativeDirectory = `./${relativeDirectory}`;
961
- }
962
- const existingModule = parsed.modules.find((moduleEntry) => {
963
- return (moduleEntry?.srcPath === relativeDirectory ||
964
- moduleEntry?.name === localHarPackage.moduleName);
965
- });
966
- if (existingModule) {
967
- if (existingModule.srcPath !== relativeDirectory) {
968
- existingModule.srcPath = relativeDirectory;
969
- didChange = true;
970
- }
971
- continue;
972
- }
973
- parsed.modules.push({
974
- name: localHarPackage.moduleName,
975
- srcPath: relativeDirectory,
976
- });
977
- didChange = true;
978
- }
979
- if (!didChange) {
980
- return async () => { };
981
- }
982
- await fs_extra_1.default.writeFile(buildProfilePath, JSON.stringify(parsed, null, 2) + '\n');
983
- return async () => {
984
- await fs_extra_1.default.writeFile(buildProfilePath, rawContents);
985
- };
986
- }
987
- async function ensureRnohGeneratedTsShim(harmonyProjectRoot) {
988
- const shimPath = path_1.default.join(harmonyProjectRoot, RNOH_GENERATED_TS_SHIM_RELATIVE_PATH);
989
- let relativeTarget = path_1.default.relative(path_1.default.dirname(shimPath), path_1.default.join(harmonyProjectRoot, RNOH_NORMALIZED_TS_TARGET_RELATIVE_PATH));
990
- relativeTarget = relativeTarget.replace(/\\/g, '/').replace(/\.ts$/, '');
991
- if (!relativeTarget.startsWith('.')) {
992
- relativeTarget = `./${relativeTarget}`;
993
- }
994
- await fs_extra_1.default.ensureDir(path_1.default.dirname(shimPath));
995
- await fs_extra_1.default.writeFile(shimPath, `export * from '${relativeTarget}';\n`);
996
- }
997
- async function alignRnohCodegenWithNormalizedLocalPackage(harmonyProjectRoot, localHarPackages) {
998
- const rnohPackage = localHarPackages.find((localPackage) => localPackage.packageName === '@rnoh/react-native-openharmony');
999
- if (!rnohPackage) {
1000
- return async () => { };
1001
- }
1002
- const hvigorFilePath = path_1.default.join(harmonyProjectRoot, 'entry', 'hvigorfile.ts');
1003
- if (!(await fs_extra_1.default.pathExists(hvigorFilePath))) {
1004
- return async () => { };
1005
- }
1006
- const originalHvigorFile = await fs_extra_1.default.readFile(hvigorFilePath, 'utf8');
1007
- let relativeRnohModulePath = path_1.default.relative(harmonyProjectRoot, rnohPackage.directoryPath);
1008
- relativeRnohModulePath = relativeRnohModulePath.split(path_1.default.sep).join('/');
1009
- if (!relativeRnohModulePath.startsWith('.')) {
1010
- relativeRnohModulePath = `./${relativeRnohModulePath}`;
1011
- }
1012
- const updatedHvigorFile = originalHvigorFile.replace(/rnohModulePath:\s*['"][^'"]+['"]/, `rnohModulePath: '${relativeRnohModulePath}'`);
1013
- if (updatedHvigorFile !== originalHvigorFile) {
1014
- await fs_extra_1.default.writeFile(hvigorFilePath, updatedHvigorFile);
1015
- }
1016
- const generatedModuleRootPath = path_1.default.join(harmonyProjectRoot, RNOH_GENERATED_MODULE_ROOT_RELATIVE_PATH);
1017
- const backupRootPath = await fs_extra_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'expo-harmony-rnoh-module-'));
1018
- const backupModuleRootPath = path_1.default.join(backupRootPath, 'react-native-openharmony');
1019
- let movedGeneratedModuleRoot = false;
1020
- if (await fs_extra_1.default.pathExists(generatedModuleRootPath)) {
1021
- await fs_extra_1.default.move(generatedModuleRootPath, backupModuleRootPath, { overwrite: true });
1022
- movedGeneratedModuleRoot = true;
1023
- }
1024
- return async () => {
1025
- if (updatedHvigorFile !== originalHvigorFile) {
1026
- await fs_extra_1.default.writeFile(hvigorFilePath, originalHvigorFile);
1027
- }
1028
- if (movedGeneratedModuleRoot) {
1029
- await fs_extra_1.default.remove(generatedModuleRootPath);
1030
- await fs_extra_1.default.ensureDir(path_1.default.dirname(generatedModuleRootPath));
1031
- await fs_extra_1.default.move(backupModuleRootPath, generatedModuleRootPath, { overwrite: true });
1032
- }
1033
- await fs_extra_1.default.remove(backupRootPath);
1034
- };
1035
- }
1036
- async function normalizeProjectRnohCliAutolinkingTemplates(projectRoot) {
1037
- const originalContentsByPath = new Map();
1038
- for (const relativePath of RNOH_CLI_AUTOLINKING_TEMPLATE_RELATIVE_PATHS) {
1039
- const targetPath = path_1.default.join(projectRoot, relativePath);
1040
- if (!(await fs_extra_1.default.pathExists(targetPath))) {
1041
- continue;
1042
- }
1043
- const currentContents = await fs_extra_1.default.readFile(targetPath, 'utf8');
1044
- const normalizedContents = normalizeRnohCliAutolinkingTemplateContents(currentContents);
1045
- if (normalizedContents === currentContents) {
1046
- continue;
1047
- }
1048
- originalContentsByPath.set(targetPath, currentContents);
1049
- await fs_extra_1.default.writeFile(targetPath, normalizedContents);
1050
- }
1051
- return async () => {
1052
- for (const [targetPath, originalContents] of originalContentsByPath.entries()) {
1053
- await fs_extra_1.default.writeFile(targetPath, originalContents);
1054
- }
1055
- };
1056
- }
1057
- function normalizeRnohCliAutolinkingTemplateContents(contents) {
1058
- return contents
1059
- .replace(/import type \{ RNPackageContext, RNOHPackage \} from '@rnoh\/react-native-openharmony';/g, "import type { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony';")
1060
- .replace(/export function createRNOHPackages\(ctx: RNPackageContext\): RNOHPackage\[\] \{/g, 'export function createRNOHPackages(ctx: RNPackageContext): RNPackage[] {');
1061
- }
1062
- async function findHarmonyArtifacts(harmonyProjectRoot) {
1063
- const discoveredPaths = [];
1064
- await walkDirectory(harmonyProjectRoot, async (entryPath) => {
1065
- if (entryPath.endsWith('.hap') || entryPath.endsWith('.app') || entryPath.endsWith('.hsp')) {
1066
- discoveredPaths.push(entryPath);
1067
- }
1068
- });
1069
- return discoveredPaths.sort((left, right) => left.localeCompare(right));
1070
- }
1071
- async function walkDirectory(currentPath, visitor) {
1072
- if (!(await fs_extra_1.default.pathExists(currentPath))) {
1073
- return;
1074
- }
1075
- const entries = await fs_extra_1.default.readdir(currentPath, { withFileTypes: true });
1076
- for (const entry of entries) {
1077
- const nextPath = path_1.default.join(currentPath, entry.name);
1078
- if (entry.isDirectory()) {
1079
- await walkDirectory(nextPath, visitor);
1080
- continue;
1081
- }
1082
- await visitor(nextPath);
1083
- }
1084
- }
1085
- async function defaultCommandRunner(file, args, options) {
1086
- try {
1087
- const result = await execFileAsync(file, args, {
1088
- cwd: options.cwd,
1089
- env: options.env,
1090
- maxBuffer: 20 * 1024 * 1024,
1091
- });
1092
- return {
1093
- exitCode: 0,
1094
- stdout: result.stdout,
1095
- stderr: result.stderr,
1096
- };
1097
- }
1098
- catch (error) {
1099
- const failed = error;
1100
- return {
1101
- exitCode: typeof failed.code === 'number' ? failed.code : 1,
1102
- stdout: failed.stdout ?? '',
1103
- stderr: failed.stderr ?? failed.message ?? '',
1104
- };
1105
- }
1106
- }
1107
415
  function getDesiredHarmonyScripts(projectRoot) {
1108
416
  return (0, project_1.loadProject)(projectRoot).then((loadedProject) => (0, template_1.buildDesiredPackageScripts)(loadedProject.packageJson));
1109
417
  }