@expo-harmony/expo-modules-autolinking 55.0.25-harmony.5 → 55.0.27-harmony.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/build/index.d.ts +3 -2
  2. package/build/index.js +4 -1
  3. package/build/src/autolinking/link.d.ts +4 -2
  4. package/build/src/autolinking/link.js +34 -41
  5. package/build/src/autolinking/prepare.d.ts +15 -0
  6. package/build/src/autolinking/prepare.js +39 -0
  7. package/build/src/autolinking/verify.js +1 -0
  8. package/build/src/commands/link.js +5 -2
  9. package/build/src/harmony/configuredBuild.d.ts +2 -0
  10. package/build/src/harmony/configuredBuild.js +264 -0
  11. package/build/src/harmony/har.d.ts +6 -0
  12. package/build/src/harmony/har.js +160 -0
  13. package/build/src/harmony/manifest/index.js +1 -0
  14. package/build/src/harmony/materialize.d.ts +12 -2
  15. package/build/src/harmony/materialize.js +14 -8
  16. package/build/src/harmony/ohpm/dependencies.js +4 -1
  17. package/build/src/harmony/persistence/canonicalize.js +6 -0
  18. package/build/src/harmony/transaction/stageProject.js +3 -1
  19. package/build/src/metadata/schema.d.ts +0 -1
  20. package/build/src/metadata/schema.js +47 -18
  21. package/build/src/types.d.ts +8 -0
  22. package/harmony-module.schema.json +5 -0
  23. package/index.ts +4 -1
  24. package/package.json +7 -3
  25. package/src/autolinking/link.ts +39 -51
  26. package/src/autolinking/prepare.ts +46 -0
  27. package/src/autolinking/verify.ts +1 -1
  28. package/src/commands/link.ts +6 -3
  29. package/src/harmony/configuredBuild.ts +289 -0
  30. package/src/harmony/har.ts +130 -0
  31. package/src/harmony/manifest/index.ts +1 -0
  32. package/src/harmony/materialize.ts +17 -7
  33. package/src/harmony/ohpm/dependencies.ts +4 -1
  34. package/src/harmony/persistence/canonicalize.ts +6 -0
  35. package/src/harmony/transaction/stageProject.ts +3 -1
  36. package/src/metadata/schema.ts +52 -16
  37. package/src/types.ts +4 -0
package/build/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { linkModulesAsync } from './src/autolinking/link';
1
+ import { linkModulesAsync, linkPreparedModulesAsync } from './src/autolinking/link';
2
+ import { prepareModulesAsync } from './src/autolinking/prepare';
2
3
  import { canonicalizeAutolinkingArtifacts } from './src/harmony/persistence/canonicalize';
3
4
  import { resolveModulesAsync } from './src/autolinking/resolve';
4
5
  import { searchModulesAsync } from './src/autolinking/search';
5
6
  import { verifyModulesAsync } from './src/autolinking/verify';
6
7
  import { normalizeHarmonyModuleMetadata } from './src/metadata/schema';
7
- export { canonicalizeAutolinkingArtifacts, linkModulesAsync, normalizeHarmonyModuleMetadata, resolveModulesAsync, searchModulesAsync, verifyModulesAsync, };
8
+ export { canonicalizeAutolinkingArtifacts, linkModulesAsync, linkPreparedModulesAsync, prepareModulesAsync, normalizeHarmonyModuleMetadata, resolveModulesAsync, searchModulesAsync, verifyModulesAsync, };
8
9
  export type { BuildType, CanonicalizeAutolinkingArtifactsOptions, CanonicalizedAutolinkingArtifacts, Diagnostic, DiagnosticSource, ExpoMetadata, HostMetadata, HarmonyModuleMetadata, LinkOptions, LinkResult, Logger, Manifest, ModuleDescriptor, ModuleArtifactDescriptor, ModuleSource, Platform, ResolveOptions, RnohMetadata, SearchOptions, SearchRecord, SearchResult, VerificationResult, VerifyOptions, } from './src/types';
9
10
  export { createNativeModuleContracts } from './src/harmony/manifest/runtime';
10
11
  export { ohpmDependenciesFromManifest } from './src/harmony/manifest';
package/build/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ohpmDependenciesFromManifest = exports.createNativeModuleContracts = exports.verifyModulesAsync = exports.searchModulesAsync = exports.resolveModulesAsync = exports.normalizeHarmonyModuleMetadata = exports.linkModulesAsync = exports.canonicalizeAutolinkingArtifacts = void 0;
3
+ exports.ohpmDependenciesFromManifest = exports.createNativeModuleContracts = exports.verifyModulesAsync = exports.searchModulesAsync = exports.resolveModulesAsync = exports.normalizeHarmonyModuleMetadata = exports.prepareModulesAsync = exports.linkPreparedModulesAsync = exports.linkModulesAsync = exports.canonicalizeAutolinkingArtifacts = void 0;
4
4
  const link_1 = require("./src/autolinking/link");
5
5
  Object.defineProperty(exports, "linkModulesAsync", { enumerable: true, get: function () { return link_1.linkModulesAsync; } });
6
+ Object.defineProperty(exports, "linkPreparedModulesAsync", { enumerable: true, get: function () { return link_1.linkPreparedModulesAsync; } });
7
+ const prepare_1 = require("./src/autolinking/prepare");
8
+ Object.defineProperty(exports, "prepareModulesAsync", { enumerable: true, get: function () { return prepare_1.prepareModulesAsync; } });
6
9
  const canonicalize_1 = require("./src/harmony/persistence/canonicalize");
7
10
  Object.defineProperty(exports, "canonicalizeAutolinkingArtifacts", { enumerable: true, get: function () { return canonicalize_1.canonicalizeAutolinkingArtifacts; } });
8
11
  const resolve_1 = require("./src/autolinking/resolve");
@@ -1,3 +1,5 @@
1
1
  import type { LinkOptions, LinkResult } from '../types';
2
- declare function linkModulesAsync(rawOptions: LinkOptions): Promise<LinkResult>;
3
- export { linkModulesAsync };
2
+ import { prepareModulesAsync } from './prepare';
3
+ declare function linkPreparedModulesAsync(raw: LinkOptions, prepared: Awaited<ReturnType<typeof prepareModulesAsync>>): Promise<LinkResult>;
4
+ declare function linkModulesAsync(raw: LinkOptions): Promise<LinkResult>;
5
+ export { linkModulesAsync, linkPreparedModulesAsync };
@@ -4,20 +4,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.linkModulesAsync = linkModulesAsync;
7
+ exports.linkPreparedModulesAsync = linkPreparedModulesAsync;
7
8
  const node_path_1 = __importDefault(require("node:path"));
8
9
  const constants_1 = require("../config/constants");
9
10
  const host_1 = require("../harmony/providers/host");
10
11
  const generate_1 = require("../harmony/manifest/generate");
11
12
  const runtime_1 = require("../harmony/manifest/runtime");
12
13
  const errors_1 = require("../errors");
13
- const options_1 = require("../config/options");
14
14
  const link_1 = require("../harmony/rnoh/link");
15
15
  const stageProject_1 = require("../harmony/transaction/stageProject");
16
16
  const publish_1 = require("../harmony/transaction/publish");
17
17
  const previousState_1 = require("../harmony/transaction/previousState");
18
- const verify_1 = require("./verify");
19
18
  const values_1 = require("../utilities/values");
20
- const materialize_1 = require("../harmony/materialize");
19
+ const prepare_1 = require("./prepare");
21
20
  function buildPublishEntries(staging, hostProviderSource, manifest) {
22
21
  return [
23
22
  ...Object.entries(constants_1.RnohArtifacts).map(([name, relative]) => ({
@@ -34,30 +33,9 @@ function buildPublishEntries(staging, hostProviderSource, manifest) {
34
33
  },
35
34
  ];
36
35
  }
37
- async function linkModulesAsync(rawOptions) {
38
- const options = await (0, options_1.normalizeOptionsAsync)(rawOptions);
39
- if (typeof rawOptions.harmonyProjectPath !== 'string' || !rawOptions.harmonyProjectPath) {
40
- throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', 'link requires harmonyProjectPath.', { stage: 'link' });
41
- }
42
- const verify = await (0, verify_1.verifyModulesAsync)((rawOptions.modules !== undefined
43
- ? { modules: rawOptions.modules }
44
- : { ...options, searchResult: rawOptions.searchResult }));
45
- (0, verify_1.assertVerificationSucceeded)(verify, 'link');
46
- const modules = verify.modules;
47
- const pendingSource = modules.filter(module => module.artifact.kind === 'local-source');
48
- if (pendingSource.length > 0) {
49
- if (pendingSource.some(module => module.source !== 'nativeModulesDir')) {
50
- throw new errors_1.HarmonyAutolinkingError('SOURCE_ARTIFACT_MATERIALIZATION_REQUIRED', 'Only nativeModulesDir modules may build a local Harmony HAR.', { stage: 'link', details: pendingSource.map(module => module.packageName).sort() });
51
- }
52
- await (0, materialize_1.materializeLocalSourcesAsync)(pendingSource, {
53
- dependencies: modules,
54
- projectRoot: options.projectRoot,
55
- timeoutMs: rawOptions.timeoutMs,
56
- outputLimit: rawOptions.outputLimit,
57
- env: rawOptions.env,
58
- });
59
- }
60
- const hostProviderSource = (0, host_1.renderArkTsHostProviderSource)(modules);
36
+ async function linkPreparedModulesAsync(raw, prepared) {
37
+ const { options, modules, diagnostics } = prepared;
38
+ const provider = (0, host_1.renderArkTsHostProviderSource)(modules);
61
39
  let staging;
62
40
  let failure;
63
41
  let cleanupWarning;
@@ -66,16 +44,16 @@ async function linkModulesAsync(rawOptions) {
66
44
  const previous = await (0, previousState_1.readPreviousAutolinkingStateAsync)(options.projectRoot);
67
45
  if (previous.warning) {
68
46
  warnings.push(previous.warning);
69
- (0, values_1.emitLog)(rawOptions.logger, 'warn', previous.warning.message, previous.warning);
47
+ (0, values_1.emitLog)(raw.logger, 'warn', previous.warning.message, previous.warning);
70
48
  }
71
49
  staging = await (0, stageProject_1.stageProjectAsync)({
72
50
  projectRoot: options.projectRoot,
73
- harmonyProjectPath: rawOptions.harmonyProjectPath,
74
- nodeModulesPath: rawOptions.nodeModulesPath,
51
+ harmonyProjectPath: raw.harmonyProjectPath,
52
+ nodeModulesPath: raw.nodeModulesPath,
75
53
  modules,
76
54
  buildType: options.buildType,
77
55
  previousManagedOhpmPackageNames: previous.managedOhpmPackageNames,
78
- rnohCliPackageJsonPath: rawOptions.rnohCliPackageJsonPath,
56
+ rnohCliPackageJsonPath: raw.rnohCliPackageJsonPath,
79
57
  });
80
58
  staging.rnohArtifacts = await (0, link_1.linkRnohAsync)({
81
59
  ...staging,
@@ -83,11 +61,11 @@ async function linkModulesAsync(rawOptions) {
83
61
  buildType: options.buildType,
84
62
  exclude: staging.rnohRuntimePackages,
85
63
  commandNodeModulesPath: staging.sourceNodeModulesPath,
86
- reactNativeExecutable: rawOptions.reactNativeExecutable,
87
- rnohCliPackageJsonPath: rawOptions.rnohCliPackageJsonPath,
88
- timeoutMs: rawOptions.timeoutMs,
89
- outputLimit: rawOptions.outputLimit,
90
- env: rawOptions.env,
64
+ reactNativeExecutable: raw.reactNativeExecutable,
65
+ rnohCliPackageJsonPath: raw.rnohCliPackageJsonPath,
66
+ timeoutMs: raw.timeoutMs,
67
+ outputLimit: raw.outputLimit,
68
+ env: raw.env,
91
69
  });
92
70
  const harmonyRoot = node_path_1.default.relative(staging.projectRoot, staging.harmonyProjectPath)
93
71
  .split(node_path_1.default.sep).join('/');
@@ -100,7 +78,19 @@ async function linkModulesAsync(rawOptions) {
100
78
  const published = await (0, publish_1.publishArtifactsAsync)({
101
79
  allowedRoot: staging.projectRoot,
102
80
  lockPath: node_path_1.default.join(staging.projectRoot, '.expo/harmony/autolinking.lock'),
103
- files: buildPublishEntries(staging, hostProviderSource, manifest),
81
+ files: [
82
+ ...buildPublishEntries(staging, provider, manifest),
83
+ ...(raw.moduleBuilds !== undefined
84
+ ? [{
85
+ target: node_path_1.default.join(staging.projectRoot, '.expo/harmony/module-build-options.json'),
86
+ content: JSON.stringify({
87
+ schemaVersion: 1,
88
+ moduleBuilds: raw.moduleBuilds,
89
+ moduleBuildSettings: raw.moduleBuildSettings,
90
+ }, null, 2) + '\n',
91
+ }]
92
+ : []),
93
+ ],
104
94
  stale: previous.artifacts.filter(artifact => !managed.some((relative) => {
105
95
  return artifact === node_path_1.default.join(staging.projectRoot, ...relative.split('/'));
106
96
  })),
@@ -112,10 +102,10 @@ async function linkModulesAsync(rawOptions) {
112
102
  managedArtifacts: [...managed],
113
103
  changedArtifacts: published.changed.map(target => node_path_1.default.relative(staging.projectRoot, target).split(node_path_1.default.sep).join('/')),
114
104
  unchangedArtifacts: published.unchanged.map(target => node_path_1.default.relative(staging.projectRoot, target).split(node_path_1.default.sep).join('/')),
115
- diagnostics: verify.diagnostics,
105
+ diagnostics,
116
106
  warnings,
117
107
  };
118
- (0, values_1.emitLog)(rawOptions.logger, 'info', 'Harmony autolinking completed.', {
108
+ (0, values_1.emitLog)(raw.logger, 'info', 'Harmony autolinking completed.', {
119
109
  moduleCount: result.modules.length,
120
110
  changedArtifactCount: result.changedArtifacts.length,
121
111
  });
@@ -123,7 +113,7 @@ async function linkModulesAsync(rawOptions) {
123
113
  }
124
114
  catch (error) {
125
115
  failure = error;
126
- (0, values_1.emitLog)(rawOptions.logger, 'error', 'Harmony autolinking failed.', {
116
+ (0, values_1.emitLog)(raw.logger, 'error', 'Harmony autolinking failed.', {
127
117
  code: error.code || 'ERR_EXPO_HARMONY_UNKNOWN',
128
118
  stage: error.stage || 'link',
129
119
  });
@@ -135,7 +125,7 @@ async function linkModulesAsync(rawOptions) {
135
125
  const warning = { ...cleanup };
136
126
  delete warning.cause;
137
127
  warnings.push(warning);
138
- (0, values_1.emitLog)(rawOptions.logger, 'warn', warning.message, warning);
128
+ (0, values_1.emitLog)(raw.logger, 'warn', warning.message, warning);
139
129
  if (failure)
140
130
  cleanupWarning = warning;
141
131
  }
@@ -151,3 +141,6 @@ async function linkModulesAsync(rawOptions) {
151
141
  : failure.details,
152
142
  });
153
143
  }
144
+ async function linkModulesAsync(raw) {
145
+ return linkPreparedModulesAsync(raw, await (0, prepare_1.prepareModulesAsync)(raw));
146
+ }
@@ -0,0 +1,15 @@
1
+ import type { LinkOptions } from '../types';
2
+ export declare function prepareModulesAsync(raw: LinkOptions): Promise<{
3
+ options: {
4
+ projectRoot: string;
5
+ platform: string;
6
+ searchPaths: string[];
7
+ nativeModulesDir: string;
8
+ exclude: string[];
9
+ include: string[];
10
+ moduleOverrides: Readonly<{}>;
11
+ buildType: any;
12
+ };
13
+ diagnostics: readonly import("../types").Diagnostic[];
14
+ modules: readonly import("../types").ModuleDescriptor[];
15
+ }>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prepareModulesAsync = prepareModulesAsync;
4
+ const errors_1 = require("../errors");
5
+ const options_1 = require("../config/options");
6
+ const materialize_1 = require("../harmony/materialize");
7
+ const configuredBuild_1 = require("../harmony/configuredBuild");
8
+ const verify_1 = require("./verify");
9
+ async function prepareModulesAsync(raw) {
10
+ const options = await (0, options_1.normalizeOptionsAsync)(raw);
11
+ if (typeof raw.harmonyProjectPath !== 'string' || !raw.harmonyProjectPath) {
12
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', 'link requires harmonyProjectPath.', { stage: 'link' });
13
+ }
14
+ const verify = await (0, verify_1.verifyModulesAsync)((raw.modules !== undefined
15
+ ? { modules: raw.modules }
16
+ : { ...options, searchResult: raw.searchResult }));
17
+ (0, verify_1.assertVerificationSucceeded)(verify, 'link');
18
+ const modules = verify.modules;
19
+ const pending = modules.filter(module => module.artifact.kind === 'local-source');
20
+ if (pending.length > 0) {
21
+ if (pending.some(module => module.source !== 'nativeModulesDir')) {
22
+ throw new errors_1.HarmonyAutolinkingError('SOURCE_ARTIFACT_MATERIALIZATION_REQUIRED', 'Only nativeModulesDir modules may build a local Harmony HAR.', { stage: 'link', details: pending.map(module => module.packageName).sort() });
23
+ }
24
+ await (0, materialize_1.materializeLocalSourcesAsync)(pending, {
25
+ dependencies: modules,
26
+ projectRoot: options.projectRoot,
27
+ timeoutMs: raw.timeoutMs,
28
+ outputLimit: raw.outputLimit,
29
+ env: raw.env,
30
+ });
31
+ }
32
+ return {
33
+ options,
34
+ diagnostics: verify.diagnostics,
35
+ modules: await (0, configuredBuild_1.buildConfiguredModulesAsync)(modules, {
36
+ ...raw, projectRoot: options.projectRoot, buildType: options.buildType,
37
+ }),
38
+ };
39
+ }
@@ -224,6 +224,7 @@ function searchRecordFromDescriptor(descriptor, declared) {
224
224
  ? {
225
225
  platforms: ['harmony'],
226
226
  harmony: {
227
+ ...(descriptor.harmony.buildOptionsFile ? { buildOptionsFile: descriptor.harmony.buildOptionsFile } : {}),
227
228
  modules,
228
229
  services,
229
230
  ...Object.fromEntries(host_1.HostMetadataFields.map(field => [
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerLinkCommand = registerLinkCommand;
4
4
  const link_1 = require("../autolinking/link");
5
+ const prepare_1 = require("../autolinking/prepare");
5
6
  const autolinkingOptions_1 = require("./autolinkingOptions");
6
7
  const output_1 = require("./output");
7
8
  function registerLinkCommand(program, io) {
@@ -14,7 +15,7 @@ function registerLinkCommand(program, io) {
14
15
  .option('--output-limit <bytes>', 'per-stream output capture limit', autolinkingOptions_1.parsePositiveInt)
15
16
  .option('-j, --json', 'output results in the plain JSON format', false)
16
17
  .action(async (searchPaths, options) => {
17
- (0, output_1.writeResult)(io, await (0, link_1.linkModulesAsync)({
18
+ const input = {
18
19
  ...(0, autolinkingOptions_1.toApiOptions)(options, searchPaths),
19
20
  harmonyProjectPath: options.harmonyProjectPath,
20
21
  nodeModulesPath: options.nodeModulesPath,
@@ -22,6 +23,8 @@ function registerLinkCommand(program, io) {
22
23
  rnohCliPackageJsonPath: options.rnohCliPackageJson,
23
24
  timeoutMs: options.timeoutMs,
24
25
  outputLimit: options.outputLimit,
25
- }), options);
26
+ };
27
+ const prepared = await (0, prepare_1.prepareModulesAsync)(input);
28
+ (0, output_1.writeResult)(io, await (0, link_1.linkPreparedModulesAsync)(input, prepared), options);
26
29
  });
27
30
  }
@@ -0,0 +1,2 @@
1
+ import type { LinkOptions, ModuleDescriptor } from '../types';
2
+ export declare function buildConfiguredModulesAsync(modules: ReadonlyArray<ModuleDescriptor>, raw: LinkOptions): Promise<ReadonlyArray<ModuleDescriptor>>;
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildConfiguredModulesAsync = buildConfiguredModulesAsync;
7
+ const node_crypto_1 = __importDefault(require("node:crypto"));
8
+ const promises_1 = __importDefault(require("node:fs/promises"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const json5_1 = __importDefault(require("json5"));
11
+ const errors_1 = require("../errors");
12
+ const values_1 = require("../utilities/values");
13
+ const dependencies_1 = require("./ohpm/dependencies");
14
+ const har_1 = require("./har");
15
+ const materialize_1 = require("./materialize");
16
+ const publish_1 = require("./transaction/publish");
17
+ const Ignored = new Set(['.git', '.hvigor', '.cxx', '.native-build', 'native-deps', 'build', 'node_modules', 'oh_modules', 'local.properties', 'oh-package-lock.json5', 'BuildProfile.ets']);
18
+ const InputsFile = '.expo/harmony/module-build-options.json';
19
+ async function readBuildInputs(options) {
20
+ if (options.moduleBuilds !== undefined)
21
+ return options;
22
+ let content;
23
+ try {
24
+ content = await promises_1.default.readFile(node_path_1.default.join(options.projectRoot, InputsFile), 'utf8');
25
+ }
26
+ catch (error) {
27
+ if (error.code === 'ENOENT')
28
+ return options;
29
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `Cannot read ${InputsFile}.`, {
30
+ cause: error,
31
+ stage: 'configured-module-build',
32
+ });
33
+ }
34
+ let saved;
35
+ try {
36
+ saved = JSON.parse(content);
37
+ }
38
+ catch (error) {
39
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `Invalid ${InputsFile}; run Harmony prebuild again.`, {
40
+ cause: error,
41
+ stage: 'configured-module-build',
42
+ });
43
+ }
44
+ if (!saved || typeof saved !== 'object' || saved.schemaVersion !== 1 || !saved.moduleBuilds) {
45
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `Invalid ${InputsFile}; run Harmony prebuild again.`, {
46
+ stage: 'configured-module-build',
47
+ });
48
+ }
49
+ return { ...options, moduleBuilds: saved.moduleBuilds, moduleBuildSettings: saved.moduleBuildSettings };
50
+ }
51
+ async function hashTree(hash, dir, depth = Infinity, metadataOnly = false) {
52
+ for (const entry of (await promises_1.default.readdir(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name, 'en'))) {
53
+ if (Ignored.has(entry.name) || entry.name.endsWith('.har'))
54
+ continue;
55
+ const file = node_path_1.default.join(dir, entry.name);
56
+ if (entry.isDirectory() && depth > 0) {
57
+ hash.update(entry.name + '/');
58
+ await hashTree(hash, file, depth - 1, metadataOnly);
59
+ }
60
+ else if (entry.isFile() && (!metadataOnly || /(?:package|sdk-pkg)\.json$/u.test(entry.name))) {
61
+ hash.update(entry.name + '\0').update(new Uint8Array(await promises_1.default.readFile(file)));
62
+ }
63
+ }
64
+ }
65
+ async function buildFingerprint(project, modules, options) {
66
+ const hash = node_crypto_1.default.createHash('sha256');
67
+ hash.update(new Uint8Array(await promises_1.default.readFile(__filename)));
68
+ hash.update(JSON.stringify([options.buildType, options.moduleBuildSettings]));
69
+ await hashTree(hash, project);
70
+ for (const { descriptor, mapping } of (0, dependencies_1.collectOhpmDeps)(modules)) {
71
+ const file = (0, dependencies_1.resolveOhpmSpecifier)(descriptor, mapping, { harmonyProjectPath: project });
72
+ hash.update(mapping.ohPackageName).update(new Uint8Array(await promises_1.default.readFile(node_path_1.default.resolve(project, file))));
73
+ }
74
+ const env = { ...process.env, ...options.env };
75
+ for (const key of Object.keys(env).sort()) {
76
+ if (/^(HARMONY_|DEVECO_|OHOS_)|^(PATH|CC|CXX|CFLAGS|CXXFLAGS|LDFLAGS)$/u.test(key))
77
+ hash.update(key + '=' + env[key]);
78
+ }
79
+ if (!env.DEVECO_SDK_HOME || !env.HARMONY_HVIGORW || !env.HARMONY_OHPM)
80
+ return null;
81
+ await hashTree(hash, env.DEVECO_SDK_HOME, 3, true);
82
+ for (const tool of [env.HARMONY_HVIGORW, env.HARMONY_OHPM]) {
83
+ hash.update(new Uint8Array(await promises_1.default.readFile(tool)));
84
+ const manifest = node_path_1.default.resolve(node_path_1.default.dirname(tool), '../package.json');
85
+ try {
86
+ hash.update(new Uint8Array(await promises_1.default.readFile(manifest)));
87
+ }
88
+ catch (error) {
89
+ if (error.code !== 'ENOENT') {
90
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `Cannot read build tool metadata: ${manifest}`, {
91
+ cause: error,
92
+ stage: 'configured-module-build',
93
+ });
94
+ }
95
+ }
96
+ }
97
+ return hash.digest('hex');
98
+ }
99
+ async function buildConfiguredModulesAsync(modules, raw) {
100
+ const options = await readBuildInputs(raw);
101
+ const builds = options.moduleBuilds ?? {};
102
+ if (!builds || typeof builds !== 'object' || Array.isArray(builds)) {
103
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', 'moduleBuilds must be an object.', {
104
+ stage: 'configured-module-build',
105
+ });
106
+ }
107
+ const linked = [...modules];
108
+ for (const name of Object.keys(builds).sort()) {
109
+ const index = linked.findIndex(item => item.packageName === name);
110
+ if (index < 0) {
111
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `Configured native module ${name} is not linked. Install it or remove its config plugin.`, {
112
+ stage: 'configured-module-build',
113
+ });
114
+ }
115
+ const item = linked[index];
116
+ const file = item.harmony.buildOptionsFile;
117
+ if (!item.arkTs || !file) {
118
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `${name} does not declare harmony.buildOptionsFile.`, {
119
+ stage: 'configured-module-build',
120
+ });
121
+ }
122
+ const source = await (0, values_1.resolveInsideAsync)(item.packageRoot, 'harmony', 'Harmony source project', { packageName: name, type: 'directory' });
123
+ const defaultsPath = await (0, values_1.resolveInsideAsync)(source, file, 'Harmony build options', { packageName: name, type: 'file' });
124
+ const defaults = JSON.parse(await promises_1.default.readFile(defaultsPath, 'utf8'));
125
+ const selected = builds[name];
126
+ if (!selected || typeof selected !== 'object' || Array.isArray(selected)) {
127
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `Build options for ${name} must be an object.`, {
128
+ stage: 'configured-module-build',
129
+ });
130
+ }
131
+ for (const [key, value] of Object.entries(selected)) {
132
+ if (!Object.hasOwn(defaults, key) || !['boolean', 'string', 'number'].includes(typeof value)
133
+ || typeof value !== typeof defaults[key] || (typeof value === 'number' && !Number.isFinite(value))) {
134
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `Invalid build option ${name}.${key}.`, {
135
+ stage: 'configured-module-build',
136
+ });
137
+ }
138
+ }
139
+ const root = await promises_1.default.realpath(options.projectRoot);
140
+ const cache = await (0, values_1.resolveInsideAsync)(root, '.expo/harmony/module-builds', 'Module build directory', { mustExist: false });
141
+ let dir = root;
142
+ for (const segment of ['.expo', 'harmony', 'module-builds']) {
143
+ dir = node_path_1.default.join(dir, segment);
144
+ try {
145
+ await promises_1.default.mkdir(dir);
146
+ }
147
+ catch (error) {
148
+ if (error.code !== 'EEXIST') {
149
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `Cannot create module build directory: ${dir}`, {
150
+ cause: error,
151
+ stage: 'configured-module-build',
152
+ });
153
+ }
154
+ }
155
+ const stat = await promises_1.default.lstat(dir);
156
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
157
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', 'Module build directory must not be a symbolic link.', {
158
+ stage: 'configured-module-build',
159
+ });
160
+ }
161
+ }
162
+ const temp = await promises_1.default.mkdtemp(node_path_1.default.join(cache, 'build-'));
163
+ const project = node_path_1.default.join(temp, 'harmony');
164
+ try {
165
+ await promises_1.default.cp(source, project, {
166
+ recursive: true,
167
+ dereference: true,
168
+ filter: async (entry) => {
169
+ if (Ignored.has(node_path_1.default.basename(entry)) || entry.endsWith('.har'))
170
+ return false;
171
+ if (!(0, values_1.isPathInside)(item.packageRoot, await promises_1.default.realpath(entry))) {
172
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `Harmony source link escapes ${name}: ${node_path_1.default.relative(source, entry)}`, {
173
+ stage: 'configured-module-build',
174
+ });
175
+ }
176
+ return true;
177
+ },
178
+ });
179
+ await promises_1.default.writeFile(node_path_1.default.join(project, file), JSON.stringify({ ...defaults, ...selected }, null, 2) + '\n');
180
+ const profilePath = node_path_1.default.join(project, 'build-profile.json5');
181
+ const profile = json5_1.default.parse(await promises_1.default.readFile(profilePath, 'utf8'));
182
+ const library = profile.modules?.find(entry => node_path_1.default.normalize(entry.srcPath) === 'library');
183
+ const product = profile.app?.products?.find(entry => entry.name === 'default');
184
+ if (!library || !product || !/^[A-Za-z][A-Za-z0-9_]*$/u.test(library.name)) {
185
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `${name} must declare a library module and default product in harmony/build-profile.json5.`, {
186
+ stage: 'configured-module-build',
187
+ });
188
+ }
189
+ const settings = options.moduleBuildSettings;
190
+ if (settings) {
191
+ if (!Array.isArray(settings.abiFilters) || settings.abiFilters.length === 0
192
+ || settings.abiFilters.some(abi => !['arm64-v8a', 'x86_64'].includes(abi))
193
+ || ![settings.compatibleSdkVersion, settings.targetSdkVersion].every(value => typeof value === 'string' && value.length > 0)) {
194
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', 'Invalid moduleBuildSettings; run Harmony prebuild again.', {
195
+ stage: 'configured-module-build',
196
+ });
197
+ }
198
+ product.compatibleSdkVersion = settings.compatibleSdkVersion;
199
+ product.targetSdkVersion = settings.targetSdkVersion;
200
+ const file = node_path_1.default.join(project, 'library/build-profile.json5');
201
+ const build = json5_1.default.parse(await promises_1.default.readFile(file, 'utf8'));
202
+ if (build.buildOption?.externalNativeOptions) {
203
+ build.buildOption.externalNativeOptions.abiFilters = settings.abiFilters;
204
+ }
205
+ await promises_1.default.writeFile(file, JSON.stringify(build, null, 2) + '\n');
206
+ }
207
+ await promises_1.default.writeFile(profilePath, JSON.stringify(profile, null, 2) + '\n');
208
+ const deps = linked.filter(module => module.packageName !== name);
209
+ const key = await buildFingerprint(project, deps, options);
210
+ const cached = key ? node_path_1.default.join(cache, `${key}.har`) : null;
211
+ if (cached && await (0, materialize_1.isNonEmptyRegularHarAsync)(cached, cache, name)) {
212
+ try {
213
+ const expected = JSON.parse(await promises_1.default.readFile(cached + '.json', 'utf8')).sha256;
214
+ const actual = node_crypto_1.default.createHash('sha256').update(new Uint8Array(await promises_1.default.readFile(cached))).digest('hex');
215
+ if (expected === actual) {
216
+ linked[index] = { ...item, arkTs: { ...item.arkTs, builtHarPath: cached } };
217
+ continue;
218
+ }
219
+ }
220
+ catch (error) {
221
+ if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
222
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `Cannot inspect cached HAR for ${name}.`, {
223
+ cause: error,
224
+ stage: 'configured-module-build',
225
+ });
226
+ }
227
+ }
228
+ }
229
+ const manifestPath = node_path_1.default.join(project, 'oh-package.json5');
230
+ const manifest = json5_1.default.parse(await promises_1.default.readFile(manifestPath, 'utf8'));
231
+ const overrides = {};
232
+ const versions = {};
233
+ for (const { descriptor, mapping } of (0, dependencies_1.collectOhpmDeps)(deps)) {
234
+ overrides[mapping.ohPackageName] = (0, dependencies_1.resolveOhpmSpecifier)(descriptor, mapping, { harmonyProjectPath: project });
235
+ versions[mapping.ohPackageName] = descriptor.packageVersion;
236
+ }
237
+ await promises_1.default.writeFile(manifestPath, JSON.stringify({ ...manifest, overrides: { ...manifest.overrides, ...overrides } }, null, 2) + '\n');
238
+ const build = {
239
+ executable: 'hvigorw', cwd: project,
240
+ args: ['--mode', 'module', '-p', `module=${library.name}@default`, '-p', 'product=default', '-p', `buildMode=${options.buildType ?? 'debug'}`, '--no-daemon', 'assembleHar'],
241
+ };
242
+ for (const step of (0, materialize_1.fixedLocalSourceBuildSteps)(build))
243
+ await (0, materialize_1.runFixedBuildStepAsync)(step, name, options);
244
+ const output = node_path_1.default.join(project, 'library/build/default/outputs/default', `${library.name}.har`);
245
+ if (!await (0, materialize_1.isNonEmptyRegularHarAsync)(output, project, name)) {
246
+ throw new errors_1.HarmonyAutolinkingError('INVALID_OPTIONS', `${name}: Hvigor did not produce a HAR.`, {
247
+ stage: 'configured-module-build',
248
+ });
249
+ }
250
+ const sourceManifest = json5_1.default.parse(await promises_1.default.readFile(node_path_1.default.join(project, 'library/oh-package.json5'), 'utf8'));
251
+ await (0, har_1.sanitizeHarmonyHar)(output, { sourceManifest, workspaceVersions: versions });
252
+ const digest = node_crypto_1.default.createHash('sha256').update(new Uint8Array(await promises_1.default.readFile(output))).digest('hex');
253
+ const target = cached || node_path_1.default.join(cache, `${digest}.har`);
254
+ await (0, publish_1.publishArtifactsAsync)({
255
+ allowedRoot: root, lockPath: node_path_1.default.join(cache, 'publish.lock'), files: [{ source: output, target }, { content: JSON.stringify({ sha256: digest }) + '\n', target: target + '.json' }],
256
+ });
257
+ linked[index] = { ...item, arkTs: { ...item.arkTs, builtHarPath: target } };
258
+ }
259
+ finally {
260
+ await promises_1.default.rm(temp, { recursive: true, force: true });
261
+ }
262
+ }
263
+ return linked;
264
+ }
@@ -0,0 +1,6 @@
1
+ export declare function sanitizeHarmonyHar(file: any, { sourceManifest: source, workspaceVersions: versions, moduleRoot, }?: {
2
+ sourceManifest?: Record<string, unknown>;
3
+ workspaceVersions?: Record<string, string>;
4
+ moduleRoot?: string;
5
+ }): Promise<void>;
6
+ export declare function assertPortableHarmonyHarSync(harPath: any): void;