expo-harmony-toolkit 1.8.1 → 1.8.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.
package/README.en.md CHANGED
@@ -9,7 +9,7 @@
9
9
  <p>
10
10
  <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/actions/workflows/ci.yml"><img alt="Checks" src="https://img.shields.io/badge/checks-passing-16a34a?style=flat-square&logo=githubactions&logoColor=white"></a>
11
11
  <a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-0f766e?style=flat-square"></a>
12
- <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.8.1-111827?style=flat-square"></a>
12
+ <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.8.2-111827?style=flat-square"></a>
13
13
  <a href="./docs/support-matrix.md"><img alt="Matrix" src="https://img.shields.io/badge/matrix-expo55--rnoh082--ui--stack-2563eb?style=flat-square"></a>
14
14
  <img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG-059669?style=flat-square">
15
15
  </p>
@@ -48,7 +48,7 @@
48
48
  <!-- GENERATED:readme-current-status:start -->
49
49
  | Item | Status |
50
50
  | --- | --- |
51
- | Current version | `v1.8.1` |
51
+ | Current version | `v1.8.2` |
52
52
  | Support model | `verified + preview + experimental` |
53
53
  | Public `verified` matrix | `expo55-rnoh082-ui-stack` |
54
54
  | Supported input | Managed/CNG Expo projects |
@@ -261,6 +261,7 @@ Additional preview evidence semantics:
261
261
  - `bundle/debugBuild` are marked as `automated`
262
262
  - `device` is marked as `manual-doc`, which means a human acceptance record exists rather than CI automation
263
263
  - `release` is marked as `none`, which means no release evidence exists yet
264
+ - `v1.8.2` additionally records local signing plus simulator install/start evidence for the ccnubox release HAP; this only proves the minimal app-shell release-HAP path and does not close per-capability release acceptance
264
265
 
265
266
  Manual Harmony acceptance still requires:
266
267
 
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  <p>
10
10
  <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/actions/workflows/ci.yml"><img alt="Checks" src="https://img.shields.io/badge/checks-passing-16a34a?style=flat-square&logo=githubactions&logoColor=white"></a>
11
11
  <a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-0f766e?style=flat-square"></a>
12
- <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.8.1-111827?style=flat-square"></a>
12
+ <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.8.2-111827?style=flat-square"></a>
13
13
  <a href="./docs/support-matrix.md"><img alt="Matrix" src="https://img.shields.io/badge/matrix-expo55--rnoh082--ui--stack-2563eb?style=flat-square"></a>
14
14
  <img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG-059669?style=flat-square">
15
15
  </p>
@@ -48,7 +48,7 @@
48
48
  <!-- GENERATED:readme-current-status:start -->
49
49
  | 项目 | 说明 |
50
50
  | --- | --- |
51
- | 当前版本 | `v1.8.1` |
51
+ | 当前版本 | `v1.8.2` |
52
52
  | 支持模型 | `verified + preview + experimental` |
53
53
  | 唯一 `verified` 公开矩阵 | `expo55-rnoh082-ui-stack` |
54
54
  | 输入范围 | Managed/CNG Expo 项目 |
@@ -267,6 +267,7 @@ preview 证据的额外说明:
267
267
  - `bundle/debugBuild` 标记为 `automated`
268
268
  - `device` 标记为 `manual-doc`,表示已有人工验收记录,不等于 CI 自动验收
269
269
  - `release` 标记为 `none`,表示当前仍没有 release 证据
270
+ - `v1.8.2` 额外记录了 ccnubox release HAP 的本地签名、模拟器安装与启动证据;这只证明 app-shell release HAP 最低链路,不关闭单 capability release acceptance
270
271
 
271
272
  手动 Harmony 验收继续要求:
272
273
 
@@ -17,21 +17,24 @@ exports.AUTOLINKED_FILE_PATHS = [
17
17
  path_1.default.join('harmony', 'entry', 'src', 'main', 'cpp', 'RNOHPackagesFactory.h'),
18
18
  path_1.default.join('harmony', 'entry', 'src', 'main', 'cpp', 'autolinking.cmake'),
19
19
  ];
20
- const HARMONY_PACKAGE_JSON_NORMALIZERS = {
21
- '@react-native-oh-tpl/react-native-gesture-handler': (packageJson) => {
22
- const harmony = packageJson.harmony && typeof packageJson.harmony === 'object' && !Array.isArray(packageJson.harmony)
23
- ? { ...packageJson.harmony }
24
- : null;
25
- if (!harmony || !('codegenConfig' in harmony)) {
26
- return null;
27
- }
28
- delete harmony.codegenConfig;
29
- return {
30
- ...packageJson,
31
- harmony,
32
- };
33
- },
20
+ const stripHarmonyCodegenConfig = (packageJson) => {
21
+ const harmony = packageJson.harmony && typeof packageJson.harmony === 'object' && !Array.isArray(packageJson.harmony)
22
+ ? { ...packageJson.harmony }
23
+ : null;
24
+ if (!harmony || !('codegenConfig' in harmony)) {
25
+ return null;
26
+ }
27
+ delete harmony.codegenConfig;
28
+ return {
29
+ ...packageJson,
30
+ harmony,
31
+ };
34
32
  };
33
+ const ADAPTERS_REQUIRING_RNOH_CODEGEN = new Set([
34
+ '@react-native-oh-tpl/camera-roll',
35
+ '@react-native-oh-tpl/react-native-fs',
36
+ ]);
37
+ const HARMONY_PACKAGE_JSON_NORMALIZERS = Object.fromEntries(uiStack_1.HARMONY_NATIVE_ADAPTERS.filter((adapter) => !ADAPTERS_REQUIRING_RNOH_CODEGEN.has(adapter.adapterPackageName)).map((adapter) => [adapter.adapterPackageName, stripHarmonyCodegenConfig]));
35
38
  class AutolinkingFailureError extends Error {
36
39
  constructor(stage, command, options = {}) {
37
40
  const details = [
@@ -100,6 +103,7 @@ async function normalizeKnownHarmonyPackageJsons(projectRoot) {
100
103
  async function generateAutolinkingArtifacts(projectRoot, harmonyRootPackageContents) {
101
104
  const rnohCliPackageJsonPath = resolveProjectPackageJson(projectRoot, '@react-native-oh/react-native-harmony-cli');
102
105
  const managedOhPackageContents = await buildManagedHarmonyRootPackageContents(projectRoot, harmonyRootPackageContents);
106
+ const installedHarmonyAdapters = await resolveInstalledHarmonyAdapters(projectRoot);
103
107
  const managedAutolinkingEntries = await resolveManagedAutolinkingEntries(projectRoot);
104
108
  if (!rnohCliPackageJsonPath) {
105
109
  return createEmptyAutolinkingArtifacts(managedOhPackageContents, managedAutolinkingEntries);
@@ -113,7 +117,9 @@ async function generateAutolinkingArtifacts(projectRoot, harmonyRootPackageConte
113
117
  await fs_extra_1.default.ensureDir(path_1.default.join(temporaryHarmonyRoot, 'entry', 'src', 'main', 'ets'));
114
118
  await fs_extra_1.default.ensureDir(path_1.default.join(temporaryHarmonyRoot, 'entry', 'src', 'main', 'cpp'));
115
119
  await fs_extra_1.default.writeFile(path_1.default.join(temporaryHarmonyRoot, 'oh-package.json5'), harmonyRootPackageContents);
116
- await runRnohLinkHarmonyCommand(projectRoot, rnohCliPackageJsonPath, temporaryHarmonyRoot, commandDescription);
120
+ await runRnohLinkHarmonyCommand(projectRoot, rnohCliPackageJsonPath, temporaryHarmonyRoot, commandDescription, installedHarmonyAdapters
121
+ .filter(({ adapter }) => adapter.supportsAutolinking)
122
+ .map(({ adapter }) => adapter.adapterPackageName));
117
123
  await assertGeneratedAutolinkingFilesExist(temporaryHarmonyRoot, commandDescription);
118
124
  let normalizedEtsFactoryContents;
119
125
  let normalizedCppFactoryContents;
@@ -144,17 +150,17 @@ async function generateAutolinkingArtifacts(projectRoot, harmonyRootPackageConte
144
150
  }
145
151
  }
146
152
  async function resolveManagedAutolinkingEntries(projectRoot) {
147
- const entries = await Promise.all(uiStack_1.UI_STACK_VALIDATED_ADAPTERS.map(async (adapter) => {
148
- if (adapter.supportsAutolinking || !adapter.managedAutolinking) {
153
+ const entries = await Promise.all(uiStack_1.HARMONY_NATIVE_ADAPTERS.map(async (adapter) => {
154
+ if (!hasManagedAutolinking(adapter)) {
149
155
  return null;
150
156
  }
151
- const dependencySpecifier = await resolveHarmonyAdapterHarDependency(projectRoot, adapter.adapterPackageName);
157
+ const dependencySpecifier = await resolveHarmonyAdapterHarDependency(projectRoot, adapter);
152
158
  if (!dependencySpecifier) {
153
159
  return null;
154
160
  }
155
161
  return {
156
- adapterPackageName: adapter.adapterPackageName,
157
162
  ...adapter.managedAutolinking,
163
+ adapterPackageName: adapter.adapterPackageName,
158
164
  };
159
165
  }));
160
166
  return entries.filter((entry) => entry !== null);
@@ -179,25 +185,28 @@ async function normalizeAutolinkingEtsFactoryContents(projectRoot, contents) {
179
185
  let gestureHandlerPackageImportName = null;
180
186
  normalizedContents = normalizedContents.replace(/import\s+([A-Za-z_$][\w$]*)\s+from ['"]@react-native-oh-tpl\/react-native-gesture-handler['"];?/, (_match, importName) => {
181
187
  gestureHandlerPackageImportName = importName;
182
- return "import { GestureHandlerPackage } from '@react-native-oh-tpl/react-native-gesture-handler/ts';";
188
+ return "import RnohReactNativeHarmonyGestureHandlerPackage from '@react-native-oh-tpl/react-native-gesture-handler';";
183
189
  });
184
190
  if (!gestureHandlerPackageImportName) {
185
191
  return normalizedContents;
186
192
  }
187
- return normalizedContents.replace(new RegExp(`new\\s+${escapeRegExp(gestureHandlerPackageImportName)}\\(ctx\\)`, 'g'), 'new GestureHandlerPackage(ctx)');
193
+ return normalizedContents.replace(new RegExp(`new\\s+${escapeRegExp(gestureHandlerPackageImportName)}\\(ctx\\)`, 'g'), 'new RnohReactNativeHarmonyGestureHandlerPackage(ctx)');
188
194
  }
189
195
  function injectManagedAutolinkingIntoEtsFactory(contents, entries) {
190
196
  if (entries.length === 0) {
191
197
  return contents;
192
198
  }
193
199
  const missingImports = entries
194
- .filter((entry) => !contents.includes(`from '${entry.etsImportPath}'`))
200
+ .filter((entry) => entry.etsImportPath &&
201
+ entry.etsPackageName &&
202
+ !contents.includes(`from '${entry.etsImportPath}'`))
195
203
  .map((entry) => `import { ${entry.etsPackageName} } from '${entry.etsImportPath}';`);
196
204
  if (missingImports.length > 0) {
197
205
  contents = contents.replace("import type { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony';", `import type { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony';\n${missingImports.join('\n')}`);
198
206
  }
199
207
  const missingFactoryEntries = entries
200
- .filter((entry) => !contents.includes(`new ${entry.etsPackageName}(ctx)`))
208
+ .filter((entry) => entry.etsPackageName &&
209
+ !contents.includes(`new ${entry.etsPackageName}(ctx)`))
201
210
  .map((entry) => ` new ${entry.etsPackageName}(ctx),`);
202
211
  if (missingFactoryEntries.length > 0) {
203
212
  contents = contents.replace(/return \[\n/, `return [\n${missingFactoryEntries.join('\n')}\n`);
@@ -220,20 +229,25 @@ async function normalizeAutolinkingCppFactoryContents(projectRoot, contents) {
220
229
  }
221
230
  return contents
222
231
  .replace(/#include "ReactNativeOhTplReactNativeGestureHandlerPackage\.h"/, '#include "RnohReactNativeHarmonyGestureHandlerPackage.h"')
223
- .replace(/\brnoh::ReactNativeOhTplReactNativeGestureHandlerPackage\b/g, 'rnoh::RnohReactNativeHarmonyGestureHandlerPackage');
232
+ .replace(/\brnoh::ReactNativeOhTplReactNativeGestureHandlerPackage\b/g, 'rnoh::RnohReactNativeHarmonyGestureHandlerPackage')
233
+ .replace(/#include "ReactNativeOhTplReactNativeScreensPackage\.h"/, '#include "ScreensPackage.h"')
234
+ .replace(/std::make_shared<rnoh::ReactNativeOhTplReactNativeScreensPackage>\(ctx\)/g, 'std::make_shared<ScreensPackage>(ctx)');
224
235
  }
225
236
  function injectManagedAutolinkingIntoCppFactory(contents, entries) {
226
237
  if (entries.length === 0) {
227
238
  return contents;
228
239
  }
229
240
  const missingIncludes = entries
230
- .filter((entry) => !contents.includes(`#include "${entry.cppHeaderName}"`))
241
+ .filter((entry) => entry.cppHeaderName &&
242
+ entry.cppPackageName &&
243
+ !contents.includes(`#include "${entry.cppHeaderName}"`))
231
244
  .map((entry) => `#include "${entry.cppHeaderName}"`);
232
245
  if (missingIncludes.length > 0) {
233
246
  contents = contents.replace('#include "RNOH/Package.h"', `#include "RNOH/Package.h"\n${missingIncludes.join('\n')}`);
234
247
  }
235
248
  const missingFactoryEntries = entries
236
- .filter((entry) => !contents.includes(`std::make_shared<rnoh::${entry.cppPackageName}>(ctx)`))
249
+ .filter((entry) => entry.cppPackageName &&
250
+ !contents.includes(`std::make_shared<rnoh::${entry.cppPackageName}>(ctx)`))
237
251
  .map((entry) => ` std::make_shared<rnoh::${entry.cppPackageName}>(ctx),`);
238
252
  if (missingFactoryEntries.length > 0) {
239
253
  contents = contents.replace(/return \{\n/, `return {\n${missingFactoryEntries.join('\n')}\n`);
@@ -245,20 +259,22 @@ function injectManagedAutolinkingIntoCmake(contents, entries) {
245
259
  return contents;
246
260
  }
247
261
  const missingSubdirectories = entries
248
- .filter((entry) => !contents.includes(`./${entry.cmakeTargetName}`))
262
+ .filter((entry) => entry.cmakeTargetName &&
263
+ !contents.includes(`./${entry.cmakeTargetName}`))
249
264
  .map((entry) => ` add_subdirectory("\${OH_MODULES_DIR}/${entry.adapterPackageName}/src/main/cpp" ./${entry.cmakeTargetName})`);
250
265
  if (missingSubdirectories.length > 0) {
251
266
  contents = contents.replace(/function\(autolink_libraries target\)\n/, `function(autolink_libraries target)\n${missingSubdirectories.join('\n')}\n`);
252
267
  }
253
268
  const missingLibraryTargets = entries
254
- .filter((entry) => !contents.includes(` ${entry.cmakeTargetName}`))
269
+ .filter((entry) => entry.cmakeTargetName &&
270
+ !contents.includes(` ${entry.cmakeTargetName}`))
255
271
  .map((entry) => ` ${entry.cmakeTargetName}`);
256
272
  if (missingLibraryTargets.length > 0) {
257
273
  contents = contents.replace(/set\(AUTOLINKED_LIBRARIES\n/, `set(AUTOLINKED_LIBRARIES\n${missingLibraryTargets.join('\n')}\n`);
258
274
  }
259
275
  return contents;
260
276
  }
261
- async function runRnohLinkHarmonyCommand(projectRoot, rnohCliPackageJsonPath, harmonyProjectPath, commandDescription) {
277
+ async function runRnohLinkHarmonyCommand(projectRoot, rnohCliPackageJsonPath, harmonyProjectPath, commandDescription, includeNpmPackages) {
262
278
  const rnohCliRoot = path_1.default.dirname(rnohCliPackageJsonPath);
263
279
  let commandLinkHarmony;
264
280
  try {
@@ -277,7 +293,7 @@ async function runRnohLinkHarmonyCommand(projectRoot, rnohCliPackageJsonPath, ha
277
293
  cppRnohPackagesFactoryPathRelativeToHarmony: './entry/src/main/cpp/RNOHPackagesFactory.h',
278
294
  etsRnohPackagesFactoryPathRelativeToHarmony: './entry/src/main/ets/RNOHPackagesFactory.ets',
279
295
  ohPackagePathRelativeToHarmony: './oh-package.json5',
280
- includeNpmPackages: uiStack_1.UI_STACK_ADAPTER_PACKAGE_NAMES,
296
+ includeNpmPackages,
281
297
  });
282
298
  }
283
299
  catch (error) {
@@ -316,11 +332,8 @@ async function buildManagedHarmonyRootPackageContents(projectRoot, harmonyRootPa
316
332
  ...(parsedPackageJson.dependencies ?? {}),
317
333
  ...(await readPreservedHarmonyRootDependencies(projectRoot)),
318
334
  };
319
- for (const adapter of uiStack_1.UI_STACK_VALIDATED_ADAPTERS) {
320
- const dependencySpecifier = await resolveHarmonyAdapterHarDependency(projectRoot, adapter.adapterPackageName);
321
- if (dependencySpecifier) {
322
- dependencies[adapter.adapterPackageName] = dependencySpecifier;
323
- }
335
+ for (const { adapter, dependencySpecifier } of await resolveInstalledHarmonyAdapters(projectRoot)) {
336
+ dependencies[adapter.adapterPackageName] = dependencySpecifier;
324
337
  }
325
338
  parsedPackageJson.dependencies = sortRecordByKey(dependencies);
326
339
  return json5_1.default.stringify(parsedPackageJson, null, 2) + '\n';
@@ -332,31 +345,40 @@ async function readPreservedHarmonyRootDependencies(projectRoot) {
332
345
  }
333
346
  const currentHarmonyRootPackage = json5_1.default.parse(await fs_extra_1.default.readFile(harmonyRootPackagePath, 'utf8'));
334
347
  const preservedDependencies = {};
335
- const validatedAdapterPackageNames = new Set(uiStack_1.UI_STACK_ADAPTER_PACKAGE_NAMES);
348
+ const managedAdapterPackageNames = new Set(uiStack_1.HARMONY_NATIVE_ADAPTER_PACKAGE_NAMES);
336
349
  for (const [packageName, specifier] of Object.entries(currentHarmonyRootPackage.dependencies ?? {})) {
337
350
  if (typeof specifier !== 'string') {
338
351
  continue;
339
352
  }
340
- if (validatedAdapterPackageNames.has(packageName)) {
353
+ if (managedAdapterPackageNames.has(packageName)) {
341
354
  continue;
342
355
  }
343
356
  preservedDependencies[packageName] = specifier;
344
357
  }
345
358
  return preservedDependencies;
346
359
  }
347
- async function resolveHarmonyAdapterHarDependency(projectRoot, adapterPackageName) {
348
- const adapterEntry = uiStack_1.UI_STACK_VALIDATED_ADAPTERS.find((candidate) => candidate.adapterPackageName === adapterPackageName);
349
- if (!adapterEntry) {
350
- return null;
351
- }
352
- const adapterRoot = path_1.default.join(projectRoot, 'node_modules', ...adapterPackageName.split('/'));
353
- const harPath = path_1.default.join(adapterRoot, 'harmony', adapterEntry.harmonyHarFileName);
360
+ async function resolveHarmonyAdapterHarDependency(projectRoot, adapter) {
361
+ const adapterRoot = path_1.default.join(projectRoot, 'node_modules', ...adapter.adapterPackageName.split('/'));
362
+ const harPath = path_1.default.join(adapterRoot, 'harmony', adapter.harmonyHarFileName);
354
363
  if (!(await fs_extra_1.default.pathExists(harPath))) {
355
364
  return null;
356
365
  }
357
366
  const relativeHarPath = path_1.default.relative(path_1.default.join(projectRoot, 'harmony'), harPath).replace(/\\/g, '/');
358
367
  return `file:${relativeHarPath}`;
359
368
  }
369
+ async function resolveInstalledHarmonyAdapters(projectRoot) {
370
+ const entries = [];
371
+ for (const adapter of uiStack_1.HARMONY_NATIVE_ADAPTERS) {
372
+ const dependencySpecifier = await resolveHarmonyAdapterHarDependency(projectRoot, adapter);
373
+ if (dependencySpecifier) {
374
+ entries.push({ adapter, dependencySpecifier });
375
+ }
376
+ }
377
+ return entries;
378
+ }
379
+ function hasManagedAutolinking(adapter) {
380
+ return 'managedAutolinking' in adapter && Boolean(adapter.managedAutolinking);
381
+ }
360
382
  function createEmptyAutolinkingArtifacts(harmonyRootPackageContents, managedAutolinkingEntries) {
361
383
  return {
362
384
  ohPackageContents: harmonyRootPackageContents,
@@ -188,10 +188,19 @@ function createGestureHandlerCompatibilityDescriptorHeader(componentName) {
188
188
  ].join('\n');
189
189
  }
190
190
  function patchGestureHandlerCompatibilityPackageHeader(contents) {
191
- if (contents.includes('createComponentDescriptorProviders() override;')) {
192
- return contents;
191
+ let nextContents = contents;
192
+ if (!nextContents.includes('createTurboModuleFactoryDelegate() override;')) {
193
+ nextContents = nextContents.replace(/ EventEmitRequestHandlers createEventEmitRequestHandlers\(\);\r?\n/, [
194
+ ' std::unique_ptr<TurboModuleFactoryDelegate> createTurboModuleFactoryDelegate() override;',
195
+ '',
196
+ ' EventEmitRequestHandlers createEventEmitRequestHandlers();',
197
+ '',
198
+ ].join('\n'));
199
+ }
200
+ if (nextContents.includes('createComponentDescriptorProviders() override;')) {
201
+ return nextContents;
193
202
  }
194
- return contents.replace(' std::vector<ArkTSMessageHandler::Shared> createArkTSMessageHandlers() override;\n', [
203
+ return nextContents.replace(/ std::vector<ArkTSMessageHandler::Shared> createArkTSMessageHandlers\(\) override;\r?\n/, [
195
204
  ' std::vector<facebook::react::ComponentDescriptorProvider> createComponentDescriptorProviders() override;',
196
205
  '',
197
206
  ' ComponentJSIBinderByString createComponentJSIBinderByName() override;',
@@ -202,13 +211,16 @@ function patchGestureHandlerCompatibilityPackageHeader(contents) {
202
211
  function patchGestureHandlerCompatibilityPackageSource(contents) {
203
212
  let nextContents = contents;
204
213
  if (!nextContents.includes('#include "RNOHCorePackage/ComponentBinders/ViewComponentJSIBinder.h"')) {
205
- nextContents = nextContents.replace('#include "RNOH/RNInstanceCAPI.h"\n', '#include "RNOH/RNInstanceCAPI.h"\n#include "RNOHCorePackage/ComponentBinders/ViewComponentJSIBinder.h"\n');
214
+ nextContents = nextContents.replace(/#include "RNOH\/RNInstanceCAPI\.h"\r?\n/, '#include "RNOH/RNInstanceCAPI.h"\n#include "RNOH/ArkTSTurboModule.h"\n#include "RNOHCorePackage/ComponentBinders/ViewComponentJSIBinder.h"\n');
215
+ }
216
+ else if (!nextContents.includes('#include "RNOH/ArkTSTurboModule.h"')) {
217
+ nextContents = nextContents.replace(/#include "RNOH\/RNInstanceCAPI\.h"\r?\n/, '#include "RNOH/RNInstanceCAPI.h"\n#include "RNOH/ArkTSTurboModule.h"\n');
206
218
  }
207
219
  if (!nextContents.includes('#include "generated/RNGestureHandlerButtonComponentDescriptor.h"')) {
208
- nextContents = nextContents.replace('#include "componentInstances/RNGestureHandlerRootViewComponentInstance.h"\n', '#include "componentInstances/RNGestureHandlerRootViewComponentInstance.h"\n#include "generated/RNGestureHandlerButtonComponentDescriptor.h"\n#include "generated/RNGestureHandlerRootViewComponentDescriptor.h"\n');
220
+ nextContents = nextContents.replace(/#include "componentInstances\/RNGestureHandlerRootViewComponentInstance\.h"\r?\n/, '#include "componentInstances/RNGestureHandlerRootViewComponentInstance.h"\n#include "generated/RNGestureHandlerButtonComponentDescriptor.h"\n#include "generated/RNGestureHandlerRootViewComponentDescriptor.h"\n');
209
221
  }
210
222
  if (!nextContents.includes('class RNGestureHandlerComponentJSIBinder')) {
211
- nextContents = nextContents.replace('using namespace rnoh;\nusing namespace facebook;\n\n', [
223
+ nextContents = nextContents.replace(/using namespace rnoh;\r?\nusing namespace facebook;\r?\n\r?\n/, [
212
224
  'using namespace rnoh;',
213
225
  'using namespace facebook;',
214
226
  '',
@@ -244,8 +256,52 @@ function patchGestureHandlerCompatibilityPackageSource(contents) {
244
256
  '',
245
257
  ].join('\n'));
246
258
  }
259
+ if (!nextContents.includes('class RNGestureHandlerTurboModule : public ArkTSTurboModule')) {
260
+ nextContents = nextContents.replace(/using namespace rnoh;\r?\nusing namespace facebook;\r?\n\r?\n/, [
261
+ 'using namespace rnoh;',
262
+ 'using namespace facebook;',
263
+ '',
264
+ 'class RNGestureHandlerTurboModule : public ArkTSTurboModule {',
265
+ 'public:',
266
+ ' RNGestureHandlerTurboModule(const ArkTSTurboModule::Context ctx, const std::string name)',
267
+ ' : ArkTSTurboModule(ctx, name) {',
268
+ ' methodMap_ = {',
269
+ ' ARK_METHOD_METADATA(handleSetJSResponder, 2),',
270
+ ' ARK_METHOD_METADATA(handleClearJSResponder, 0),',
271
+ ' ARK_METHOD_METADATA(createGestureHandler, 3),',
272
+ ' ARK_METHOD_METADATA(attachGestureHandler, 3),',
273
+ ' ARK_METHOD_METADATA(updateGestureHandler, 2),',
274
+ ' ARK_METHOD_METADATA(dropGestureHandler, 1),',
275
+ ' ARK_METHOD_METADATA(install, 0),',
276
+ ' ARK_METHOD_METADATA(flushOperations, 0),',
277
+ ' };',
278
+ ' }',
279
+ '};',
280
+ '',
281
+ 'class RNGestureHandlerTurboModuleFactoryDelegate : public TurboModuleFactoryDelegate {',
282
+ 'public:',
283
+ ' SharedTurboModule createTurboModule(Context ctx, const std::string &name) const override {',
284
+ ' if (name == "RNGestureHandlerModule") {',
285
+ ' return std::make_shared<RNGestureHandlerTurboModule>(ctx, name);',
286
+ ' }',
287
+ ' return nullptr;',
288
+ ' };',
289
+ '};',
290
+ '',
291
+ ].join('\n'));
292
+ }
293
+ if (!nextContents.includes('RnohReactNativeHarmonyGestureHandlerPackage::createTurboModuleFactoryDelegate()')) {
294
+ nextContents = nextContents.replace(/EventEmitRequestHandlers RnohReactNativeHarmonyGestureHandlerPackage::createEventEmitRequestHandlers\(\) \{\r?\n/, [
295
+ 'std::unique_ptr<TurboModuleFactoryDelegate>',
296
+ 'RnohReactNativeHarmonyGestureHandlerPackage::createTurboModuleFactoryDelegate() {',
297
+ ' return std::make_unique<RNGestureHandlerTurboModuleFactoryDelegate>();',
298
+ '}',
299
+ '',
300
+ 'EventEmitRequestHandlers RnohReactNativeHarmonyGestureHandlerPackage::createEventEmitRequestHandlers() {\n',
301
+ ].join('\n'));
302
+ }
247
303
  if (!nextContents.includes('RnohReactNativeHarmonyGestureHandlerPackage::createComponentDescriptorProviders()')) {
248
- nextContents = nextContents.replace('EventEmitRequestHandlers RnohReactNativeHarmonyGestureHandlerPackage::createEventEmitRequestHandlers() {\n', [
304
+ nextContents = nextContents.replace(/EventEmitRequestHandlers RnohReactNativeHarmonyGestureHandlerPackage::createEventEmitRequestHandlers\(\) \{\r?\n/, [
249
305
  'std::vector<facebook::react::ComponentDescriptorProvider>',
250
306
  'RnohReactNativeHarmonyGestureHandlerPackage::createComponentDescriptorProviders() {',
251
307
  ' return {',
@@ -265,7 +321,7 @@ function patchGestureHandlerCompatibilityPackageSource(contents) {
265
321
  ' };',
266
322
  '}',
267
323
  '',
268
- 'EventEmitRequestHandlers RnohReactNativeHarmonyGestureHandlerPackage::createEventEmitRequestHandlers() {',
324
+ 'EventEmitRequestHandlers RnohReactNativeHarmonyGestureHandlerPackage::createEventEmitRequestHandlers() {\n',
269
325
  ].join('\n'));
270
326
  }
271
327
  return nextContents;
@@ -9,5 +9,7 @@ export declare function normalizeLocalHarDependencies(harmonyProjectRoot: string
9
9
  }>;
10
10
  export declare function ensureHarmonyBuildProfileSupportsNormalizedLocalDeps(harmonyProjectRoot: string, localHarPackages: NormalizedLocalHarPackage[]): Promise<() => Promise<void>>;
11
11
  export declare function ensureRnohGeneratedTsShim(harmonyProjectRoot: string): Promise<void>;
12
+ export declare function patchRnohGeneratedCodegenForNormalizedLocalPackage(harmonyProjectRoot: string): Promise<void>;
13
+ export declare function patchKnownHarmonyAdapterCodegenOutputs(harmonyProjectRoot: string): Promise<void>;
12
14
  export declare function alignRnohCodegenWithNormalizedLocalPackage(harmonyProjectRoot: string, localHarPackages: NormalizedLocalHarPackage[]): Promise<() => Promise<void>>;
13
15
  export declare function findHarmonyArtifacts(harmonyProjectRoot: string): Promise<string[]>;
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.normalizeLocalHarDependencies = normalizeLocalHarDependencies;
7
7
  exports.ensureHarmonyBuildProfileSupportsNormalizedLocalDeps = ensureHarmonyBuildProfileSupportsNormalizedLocalDeps;
8
8
  exports.ensureRnohGeneratedTsShim = ensureRnohGeneratedTsShim;
9
+ exports.patchRnohGeneratedCodegenForNormalizedLocalPackage = patchRnohGeneratedCodegenForNormalizedLocalPackage;
10
+ exports.patchKnownHarmonyAdapterCodegenOutputs = patchKnownHarmonyAdapterCodegenOutputs;
9
11
  exports.alignRnohCodegenWithNormalizedLocalPackage = alignRnohCodegenWithNormalizedLocalPackage;
10
12
  exports.findHarmonyArtifacts = findHarmonyArtifacts;
11
13
  const node_child_process_1 = require("node:child_process");
@@ -273,6 +275,47 @@ async function ensureRnohGeneratedTsShim(harmonyProjectRoot) {
273
275
  await fs_extra_1.default.ensureDir(path_1.default.dirname(shimPath));
274
276
  await fs_extra_1.default.writeFile(shimPath, `export * from '${relativeTarget}';\n`);
275
277
  }
278
+ async function patchRnohGeneratedCodegenForNormalizedLocalPackage(harmonyProjectRoot) {
279
+ const generatedRootPath = path_1.default.join(harmonyProjectRoot, 'expo-harmony-local-deps', 'rnoh-react-native-openharmony-react_native_openharmony', 'generated');
280
+ if (!(await fs_extra_1.default.pathExists(generatedRootPath))) {
281
+ return;
282
+ }
283
+ const generatedFilePaths = await listFilesRecursively(generatedRootPath);
284
+ await Promise.all(generatedFilePaths
285
+ .filter((filePath) => filePath.endsWith('.ts') || filePath.endsWith('.ets'))
286
+ .map(async (filePath) => {
287
+ const originalContents = await fs_extra_1.default.readFile(filePath, 'utf8');
288
+ let updatedContents = originalContents.replace(/from\s+['"]@rnoh\/react-native-openharmony\/ts['"]/g, () => {
289
+ let relativeTarget = path_1.default.relative(path_1.default.dirname(filePath), path_1.default.join(harmonyProjectRoot, 'expo-harmony-local-deps', 'rnoh-react-native-openharmony-react_native_openharmony', 'ts.ts'));
290
+ relativeTarget = relativeTarget.replace(/\\/g, '/').replace(/\.ts$/, '');
291
+ return `from '${relativeTarget.startsWith('.') ? relativeTarget : `./${relativeTarget}`}'`;
292
+ });
293
+ if (path_1.default.basename(filePath) === 'RNCCameraRoll.ts') {
294
+ updatedContents = updatedContents.replace(/getPhotoThumbnail\(internalID: string, options: Object\): Promise<void>;/, 'getPhotoThumbnail(internalID: string, options: Object): Promise<Object>;');
295
+ }
296
+ if (updatedContents !== originalContents) {
297
+ await fs_extra_1.default.writeFile(filePath, updatedContents);
298
+ }
299
+ }));
300
+ }
301
+ async function patchKnownHarmonyAdapterCodegenOutputs(harmonyProjectRoot) {
302
+ await patchSafeAreaContextGeneratedProps(harmonyProjectRoot);
303
+ }
304
+ async function patchSafeAreaContextGeneratedProps(harmonyProjectRoot) {
305
+ const propsHeaderPath = path_1.default.join(harmonyProjectRoot, 'oh_modules', '@react-native-oh-tpl', 'react-native-safe-area-context', 'src', 'main', 'cpp', 'generated', 'react', 'renderer', 'components', 'react_native_safe_area_context', 'Props.h');
306
+ if (!(await fs_extra_1.default.pathExists(propsHeaderPath))) {
307
+ return;
308
+ }
309
+ const originalContents = await fs_extra_1.default.readFile(propsHeaderPath, 'utf8');
310
+ let updatedContents = originalContents.replace(/\bbutter::map<std::string, RawValue>/g, 'std::unordered_map<std::string, RawValue>');
311
+ if (updatedContents !== originalContents &&
312
+ !updatedContents.includes('#include <unordered_map>')) {
313
+ updatedContents = updatedContents.replace('#include <react/renderer/components/view/ViewProps.h>', '#include <unordered_map>\n\n#include <react/renderer/components/view/ViewProps.h>');
314
+ }
315
+ if (updatedContents !== originalContents) {
316
+ await fs_extra_1.default.writeFile(propsHeaderPath, updatedContents);
317
+ }
318
+ }
276
319
  async function alignRnohCodegenWithNormalizedLocalPackage(harmonyProjectRoot, localHarPackages) {
277
320
  const rnohPackage = localHarPackages.find((localPackage) => localPackage.packageName === '@rnoh/react-native-openharmony');
278
321
  if (!rnohPackage) {
@@ -312,6 +355,20 @@ async function alignRnohCodegenWithNormalizedLocalPackage(harmonyProjectRoot, lo
312
355
  await fs_extra_1.default.remove(backupRootPath);
313
356
  };
314
357
  }
358
+ async function listFilesRecursively(rootPath) {
359
+ const entries = await fs_extra_1.default.readdir(rootPath, { withFileTypes: true });
360
+ const nestedEntries = await Promise.all(entries.map(async (entry) => {
361
+ const entryPath = path_1.default.join(rootPath, entry.name);
362
+ if (entry.isDirectory()) {
363
+ return listFilesRecursively(entryPath);
364
+ }
365
+ if (entry.isFile()) {
366
+ return [entryPath];
367
+ }
368
+ return [];
369
+ }));
370
+ return nestedEntries.flat();
371
+ }
315
372
  async function findHarmonyArtifacts(harmonyProjectRoot) {
316
373
  const discoveredPaths = [];
317
374
  await walkDirectory(harmonyProjectRoot, async (entryPath) => {
@@ -9,6 +9,12 @@ const path_1 = __importDefault(require("path"));
9
9
  const RNOH_CLI_AUTOLINKING_TEMPLATE_RELATIVE_PATHS = [
10
10
  path_1.default.join('node_modules', '@react-native-oh', 'react-native-harmony-cli', 'dist', 'autolinking', 'Autolinking.js'),
11
11
  path_1.default.join('node_modules', '@react-native-oh', 'react-native-harmony-cli', 'src', 'autolinking', 'Autolinking.ts'),
12
+ path_1.default.join('node_modules', '@react-native-oh', 'react-native-harmony-cli', 'dist', 'commands', 'codegen-lib-harmony.js'),
13
+ path_1.default.join('node_modules', '@react-native-oh', 'react-native-harmony-cli', 'src', 'commands', 'codegen-lib-harmony.ts'),
14
+ path_1.default.join('node_modules', '@react-native-oh', 'react-native-harmony-cli', 'dist', 'codegen', 'generators', 'UberGeneratorV1.js'),
15
+ path_1.default.join('node_modules', '@react-native-oh', 'react-native-harmony-cli', 'src', 'codegen', 'generators', 'UberGeneratorV1.ts'),
16
+ path_1.default.join('node_modules', '@react-native-oh', 'react-native-harmony-cli', 'dist', 'codegen', 'templates', 'TurboModuleSpecTSTemplate.js'),
17
+ path_1.default.join('node_modules', '@react-native-oh', 'react-native-harmony-cli', 'src', 'codegen', 'templates', 'TurboModuleSpecTSTemplate.ts'),
12
18
  ];
13
19
  async function normalizeProjectRnohCliAutolinkingTemplates(projectRoot) {
14
20
  const originalContentsByPath = new Map();
@@ -34,5 +40,32 @@ async function normalizeProjectRnohCliAutolinkingTemplates(projectRoot) {
34
40
  function normalizeRnohCliAutolinkingTemplateContents(contents) {
35
41
  return contents
36
42
  .replace(/import type \{ RNPackageContext, RNOHPackage \} from '@rnoh\/react-native-openharmony';/g, "import type { RNPackageContext, RNPackage } from '@rnoh/react-native-openharmony';")
37
- .replace(/export function createRNOHPackages\(ctx: RNPackageContext\): RNOHPackage\[\] \{/g, 'export function createRNOHPackages(ctx: RNPackageContext): RNPackage[] {');
43
+ .replace(/export function createRNOHPackages\(ctx: RNPackageContext\): RNOHPackage\[\] \{/g, 'export function createRNOHPackages(ctx: RNPackageContext): RNPackage[] {')
44
+ .replace(/(['"])@rnoh\/react-native-openharmony\/ts\1/g, "'../../ts'")
45
+ .replace(/ addMethod\(method: Method\) {\n this\.methods\.push\(method\);\n }/, ` addMethod(method: Method) {
46
+ if (method.name === 'getPhotoThumbnail' && method.returnType === 'Promise<void>') {
47
+ method = { ...method, returnType: 'Promise<Object>' };
48
+ }
49
+ this.methods.push(method);
50
+ }`)
51
+ .replace(/ addMethod\(method\) {\n this\.methods\.push\(method\);\n }/, ` addMethod(method) {
52
+ if (method.name === 'getPhotoThumbnail' && method.returnType === 'Promise<void>') {
53
+ method = { ...method, returnType: 'Promise<Object>' };
54
+ }
55
+ this.methods.push(method);
56
+ }`)
57
+ .replace(' const isRNOHModulePath = this.etsOutputPath.getValue().endsWith(`${RNOH_OHOS_NAME}/generated`.replace(/\\//g, pathUtils.sep));', ` const normalizedEtsOutputPath = this.etsOutputPath.getValue().replace(/\\\\/g, '/');
58
+ const isRNOHModulePath =
59
+ normalizedEtsOutputPath.endsWith(\`\${RNOH_OHOS_NAME}/generated\`) ||
60
+ normalizedEtsOutputPath.endsWith('rnoh-react-native-openharmony-react_native_openharmony/generated');`)
61
+ .replace(' const isRNOHModulePath = this.etsOutputPath.getValue().endsWith(`${RNOH_OHOS_NAME}/generated`.replace(/\\//g, path_1.default.sep));', ` const normalizedEtsOutputPath = this.etsOutputPath.getValue().replace(/\\\\/g, '/');
62
+ const isRNOHModulePath = normalizedEtsOutputPath.endsWith(\`\${RNOH_OHOS_NAME}/generated\`) ||
63
+ normalizedEtsOutputPath.endsWith('rnoh-react-native-openharmony-react_native_openharmony/generated');`)
64
+ .replace(/ const isRNOHModulePath = this\.etsOutputPath\.getValue\(\)\.endsWith\(`\$\{RNOH_OHOS_NAME\}\/generated`\.replace\(\/\\\/\/g, pathUtils\.sep\)\);/, ` const normalizedEtsOutputPath = this.etsOutputPath.getValue().replace(/\\\\/g, '/');
65
+ const isRNOHModulePath =
66
+ normalizedEtsOutputPath.endsWith(\`\${RNOH_OHOS_NAME}/generated\`) ||
67
+ normalizedEtsOutputPath.endsWith('rnoh-react-native-openharmony-react_native_openharmony/generated');`)
68
+ .replace(/ const isRNOHModulePath = this\.etsOutputPath\.getValue\(\)\.endsWith\(`\$\{RNOH_OHOS_NAME\}\/generated`\.replace\(\/\\\/\/g, path_1\.default\.sep\)\);/, ` const normalizedEtsOutputPath = this.etsOutputPath.getValue().replace(/\\\\/g, '/');
69
+ const isRNOHModulePath = normalizedEtsOutputPath.endsWith(\`\${RNOH_OHOS_NAME}/generated\`) ||
70
+ normalizedEtsOutputPath.endsWith('rnoh-react-native-openharmony-react_native_openharmony/generated');`);
38
71
  }
@@ -352,6 +352,8 @@ async function buildHapProject(projectRoot, options) {
352
352
  if (!normalizedLocalHarPackages.some((localPackage) => localPackage.packageName === '@rnoh/react-native-openharmony')) {
353
353
  await (0, localHar_1.ensureRnohGeneratedTsShim)(harmonyProjectRoot);
354
354
  }
355
+ await (0, localHar_1.patchRnohGeneratedCodegenForNormalizedLocalPackage)(harmonyProjectRoot);
356
+ await (0, localHar_1.patchKnownHarmonyAdapterCodegenOutputs)(harmonyProjectRoot);
355
357
  const hvigorCommand = (0, commands_1.buildInvocation)(envReport.hvigorPath, [
356
358
  'assembleHap',
357
359
  '--no-daemon',
@@ -1,6 +1,6 @@
1
1
  export declare const TOOLKIT_PACKAGE_NAME = "expo-harmony-toolkit";
2
2
  export declare const CLI_NAME = "expo-harmony";
3
- export declare const TOOLKIT_VERSION = "1.8.1";
3
+ export declare const TOOLKIT_VERSION = "1.8.2";
4
4
  export declare const TEMPLATE_VERSION = "rnoh-0.82.18";
5
5
  export declare const RNOH_VERSION = "0.82.18";
6
6
  export declare const RNOH_CLI_VERSION = "0.82.18";
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DESIRED_PACKAGE_SCRIPTS = exports.HARMONY_RUNTIME_PRELUDE_RELATIVE_PATH = exports.HARMONY_ROUTER_ENTRY_FILENAME = exports.STRICT_ENV_EXIT_CODE = exports.STRICT_DOCTOR_EXIT_CODE = exports.DEFAULT_HVIGOR_PLUGIN_FILENAME = exports.PREBUILD_METADATA_FILENAME = exports.TOOLKIT_CONFIG_FILENAME = exports.BUILD_REPORT_FILENAME = exports.ENV_REPORT_FILENAME = exports.DOCTOR_REPORT_FILENAME = exports.MANIFEST_FILENAME = exports.SIGNING_LOCAL_EXAMPLE_FILENAME = exports.SIGNING_LOCAL_FILENAME = exports.GENERATED_SHIMS_DIR = exports.GENERATED_DIR = exports.SUPPORTED_EXPO_SDKS = exports.RNOH_CLI_VERSION = exports.RNOH_VERSION = exports.TEMPLATE_VERSION = exports.TOOLKIT_VERSION = exports.CLI_NAME = exports.TOOLKIT_PACKAGE_NAME = void 0;
4
4
  exports.TOOLKIT_PACKAGE_NAME = 'expo-harmony-toolkit';
5
5
  exports.CLI_NAME = 'expo-harmony';
6
- exports.TOOLKIT_VERSION = '1.8.1';
6
+ exports.TOOLKIT_VERSION = '1.8.2';
7
7
  exports.TEMPLATE_VERSION = 'rnoh-0.82.18';
8
8
  exports.RNOH_VERSION = '0.82.18';
9
9
  exports.RNOH_CLI_VERSION = '0.82.18';
@@ -254,9 +254,17 @@ async function collectBlockingIssues(projectRoot, expoConfig, packageJson, expoP
254
254
  subject: dependencyName,
255
255
  });
256
256
  }
257
+ if (rule.specifiers &&
258
+ !rule.specifiers.some((specifier) => matchesDependencySpecifier(dependency.version, specifier))) {
259
+ issues.push({
260
+ code: 'dependency.specifier_mismatch',
261
+ message: `Dependency ${dependencyName} does not match any validated dependency spec: ${rule.specifiers.join(', ')}.`,
262
+ subject: dependencyName,
263
+ });
264
+ }
257
265
  }
258
266
  for (const dependency of dependencies) {
259
- if (!isDependencyAllowedForTargetTier(dependency.name, matrix, targetTier)) {
267
+ if (shouldBlockDependencyForTargetTier(dependency, matrix, targetTier)) {
260
268
  issues.push({
261
269
  code: 'dependency.not_allowed',
262
270
  message: `${dependency.name} is outside the ${targetTier} support tier for ${matrix.id}.`,
@@ -526,6 +534,15 @@ function isDependencyAllowedForTargetTier(dependencyName, matrix, targetTier) {
526
534
  const capability = capabilities_1.CAPABILITY_BY_PACKAGE[dependencyName];
527
535
  return capability ? (0, capabilities_1.isSupportTierAllowed)(capability.supportTier, targetTier) : false;
528
536
  }
537
+ function shouldBlockDependencyForTargetTier(dependency, matrix, targetTier) {
538
+ if (dependency.source === 'devDependency') {
539
+ return false;
540
+ }
541
+ if (dependency.buildabilityRisk === 'js-only-unknown') {
542
+ return false;
543
+ }
544
+ return !isDependencyAllowedForTargetTier(dependency.name, matrix, targetTier);
545
+ }
529
546
  function matchesVersionRange(rawVersion, range) {
530
547
  const coerced = semver_1.default.coerce(rawVersion);
531
548
  if (!coerced) {
@@ -155,5 +155,7 @@ function getCapabilityDefinitionsForProject(packageJson, options = {}) {
155
155
  (0, project_1.hasDeclaredDependency)(packageJson, definition.packageName)).sort((left, right) => left.packageName.localeCompare(right.packageName));
156
156
  }
157
157
  function collectCapabilityHarmonyPermissions(packageJson, options = {}) {
158
- return Array.from(new Set(getCapabilityDefinitionsForProject(packageJson, options).flatMap((definition) => definition.harmonyPermissions))).sort((left, right) => left.localeCompare(right));
158
+ return Array.from(new Set(getCapabilityDefinitionsForProject(packageJson, options)
159
+ .filter((definition) => definition.runtimeMode !== 'shim')
160
+ .flatMap((definition) => definition.harmonyPermissions))).sort((left, right) => left.localeCompare(right));
159
161
  }