@expo-harmony/prebuild-config 55.0.0-harmony.1 → 55.0.0-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.
- package/build/api/signing.d.ts +1 -0
- package/build/api/signing.js +5 -0
- package/build/buildDescriptor.d.ts +53 -0
- package/build/buildDescriptor.js +190 -0
- package/build/check.d.ts +16 -0
- package/build/check.js +254 -0
- package/build/dependencies.d.ts +6 -0
- package/build/dependencies.js +77 -0
- package/build/errors.d.ts +13 -0
- package/build/errors.js +17 -0
- package/build/generatedFiles.d.ts +2 -0
- package/build/generatedFiles.js +37 -0
- package/build/index.d.ts +8 -0
- package/build/manifest.d.ts +48 -0
- package/build/manifest.js +166 -0
- package/build/mods/withAutolinkingMods.d.ts +1 -0
- package/build/mods/withAutolinkingMods.js +58 -0
- package/build/mods/withEntryMods.d.ts +2 -0
- package/build/mods/withEntryMods.js +238 -0
- package/build/mods/withPreparationMod.d.ts +2 -0
- package/build/mods/withPreparationMod.js +61 -0
- package/build/mods/withProjectMods.d.ts +1 -0
- package/build/mods/withProjectMods.js +198 -0
- package/build/mods/withSourceMods.d.ts +1 -0
- package/build/mods/withSourceMods.js +59 -0
- package/build/native.d.ts +2 -0
- package/build/native.js +9 -0
- package/build/packageMetadata.d.ts +5 -0
- package/build/packageMetadata.js +10 -0
- package/build/plugin.d.ts +1 -0
- package/build/plugin.js +7 -0
- package/build/reconcile.d.ts +6 -0
- package/build/reconcile.js +47 -0
- package/build/renderers.d.ts +11 -0
- package/build/renderers.js +58 -0
- package/build/signing.d.ts +23 -0
- package/build/signing.js +140 -0
- package/build/stale.d.ts +6 -0
- package/build/stale.js +86 -0
- package/build/template.d.ts +19 -0
- package/build/template.js +108 -0
- package/build/withHarmonyPrebuildConfig.d.ts +8 -0
- package/build/withHarmonyPrebuildConfig.js +27 -0
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validateHarmonySigningConfigFile, type HarmonySigningConfig, } from '../signing';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateHarmonySigningConfigFile = void 0;
|
|
4
|
+
var signing_1 = require("../signing");
|
|
5
|
+
Object.defineProperty(exports, "validateHarmonySigningConfigFile", { enumerable: true, get: function () { return signing_1.validateHarmonySigningConfigFile; } });
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type NormalizedHarmonyConfig } from '@expo-harmony/config-plugins';
|
|
2
|
+
declare const BuildDescriptorSchemaVersion = 2;
|
|
3
|
+
declare const BuildModes: readonly ["debug", "release"];
|
|
4
|
+
declare const HarmonyPlatformDirectory = "harmony";
|
|
5
|
+
declare const HarmonyTemplateMarker = ".expo-harmony-template";
|
|
6
|
+
type HarmonyBuildMode = typeof BuildModes[number];
|
|
7
|
+
interface HarmonyBuildVariantDescriptor {
|
|
8
|
+
expectedHap: string;
|
|
9
|
+
hvigorArgs: string[];
|
|
10
|
+
}
|
|
11
|
+
interface HarmonyBuildDescriptor {
|
|
12
|
+
export: {
|
|
13
|
+
bundle: string;
|
|
14
|
+
manifest: string;
|
|
15
|
+
metadataRoot: string;
|
|
16
|
+
rawfileRoot: string;
|
|
17
|
+
sourceMap: string;
|
|
18
|
+
};
|
|
19
|
+
harmonyRoot: string;
|
|
20
|
+
identity: {
|
|
21
|
+
abilityName: string;
|
|
22
|
+
bundleName: string;
|
|
23
|
+
moduleName: string;
|
|
24
|
+
productName: string;
|
|
25
|
+
targetName: string;
|
|
26
|
+
};
|
|
27
|
+
moduleRoot: string;
|
|
28
|
+
nativeCache: {
|
|
29
|
+
invalidationRoots: string[];
|
|
30
|
+
stateFile: string;
|
|
31
|
+
};
|
|
32
|
+
nativeInputs: {
|
|
33
|
+
lockfile: string;
|
|
34
|
+
manifest: string;
|
|
35
|
+
};
|
|
36
|
+
projectFiles: {
|
|
37
|
+
hvigorConfig: string;
|
|
38
|
+
moduleHvigor: string;
|
|
39
|
+
moduleJson: string;
|
|
40
|
+
nativeInputsStamp: string;
|
|
41
|
+
projectBuildProfile: string;
|
|
42
|
+
rootHvigor: string;
|
|
43
|
+
templateMarker: string;
|
|
44
|
+
};
|
|
45
|
+
schemaVersion: typeof BuildDescriptorSchemaVersion;
|
|
46
|
+
variants: Record<HarmonyBuildMode, HarmonyBuildVariantDescriptor>;
|
|
47
|
+
}
|
|
48
|
+
declare function validateHarmonyBuildDescriptor(value: unknown): HarmonyBuildDescriptor;
|
|
49
|
+
declare function createHarmonyBuildDescriptor(config: NormalizedHarmonyConfig, signing: string | null): HarmonyBuildDescriptor;
|
|
50
|
+
declare function harmonyModuleSourcePath(build: HarmonyBuildDescriptor): string;
|
|
51
|
+
declare function resolveHarmonyBuildPath(root: string, file: string): string;
|
|
52
|
+
export { BuildDescriptorSchemaVersion, BuildModes, HarmonyPlatformDirectory, HarmonyTemplateMarker, createHarmonyBuildDescriptor, harmonyModuleSourcePath, resolveHarmonyBuildPath, validateHarmonyBuildDescriptor, };
|
|
53
|
+
export type { HarmonyBuildDescriptor, HarmonyBuildMode, HarmonyBuildVariantDescriptor, };
|
|
@@ -0,0 +1,190 @@
|
|
|
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.HarmonyTemplateMarker = exports.HarmonyPlatformDirectory = exports.BuildModes = exports.BuildDescriptorSchemaVersion = void 0;
|
|
7
|
+
exports.createHarmonyBuildDescriptor = createHarmonyBuildDescriptor;
|
|
8
|
+
exports.harmonyModuleSourcePath = harmonyModuleSourcePath;
|
|
9
|
+
exports.resolveHarmonyBuildPath = resolveHarmonyBuildPath;
|
|
10
|
+
exports.validateHarmonyBuildDescriptor = validateHarmonyBuildDescriptor;
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const config_plugins_1 = require("@expo-harmony/config-plugins");
|
|
13
|
+
const BuildDescriptorSchemaVersion = 2;
|
|
14
|
+
exports.BuildDescriptorSchemaVersion = BuildDescriptorSchemaVersion;
|
|
15
|
+
const BuildModes = Object.freeze(['debug', 'release']);
|
|
16
|
+
exports.BuildModes = BuildModes;
|
|
17
|
+
const HarmonyPlatformDirectory = 'harmony';
|
|
18
|
+
exports.HarmonyPlatformDirectory = HarmonyPlatformDirectory;
|
|
19
|
+
const HarmonyTemplateMarker = '.expo-harmony-template';
|
|
20
|
+
exports.HarmonyTemplateMarker = HarmonyTemplateMarker;
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
function isNonEmptyString(value) {
|
|
25
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
26
|
+
}
|
|
27
|
+
function isSafeRelativePath(value) {
|
|
28
|
+
if (!isNonEmptyString(value)
|
|
29
|
+
|| value.includes('\\')
|
|
30
|
+
|| node_path_1.default.posix.isAbsolute(value)
|
|
31
|
+
|| node_path_1.default.win32.isAbsolute(value)
|
|
32
|
+
|| node_path_1.default.posix.normalize(value) !== value) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const segments = value.split('/');
|
|
36
|
+
return segments.every(segment => segment && segment !== '.' && segment !== '..');
|
|
37
|
+
}
|
|
38
|
+
function isStrictDescendant(root, target) {
|
|
39
|
+
const relative = node_path_1.default.posix.relative(root, target);
|
|
40
|
+
return Boolean(relative)
|
|
41
|
+
&& relative !== '..'
|
|
42
|
+
&& !relative.startsWith('../')
|
|
43
|
+
&& !node_path_1.default.posix.isAbsolute(relative);
|
|
44
|
+
}
|
|
45
|
+
function validateVariant(value) {
|
|
46
|
+
if (!isRecord(value)
|
|
47
|
+
|| !isSafeRelativePath(value.expectedHap)
|
|
48
|
+
|| !Array.isArray(value.hvigorArgs)
|
|
49
|
+
|| value.hvigorArgs.length === 0
|
|
50
|
+
|| value.hvigorArgs.some(argument => !isNonEmptyString(argument) || /\0|[\r\n]/u.test(argument))) {
|
|
51
|
+
throw new TypeError('Harmony build descriptor contains an invalid build variant.');
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
function validateHarmonyBuildDescriptor(value) {
|
|
56
|
+
if (!isRecord(value) || value.schemaVersion !== BuildDescriptorSchemaVersion) {
|
|
57
|
+
throw new TypeError(`Unsupported Harmony build descriptor schema: ${isRecord(value) ? value.schemaVersion : 'invalid'}.`);
|
|
58
|
+
}
|
|
59
|
+
if (value.harmonyRoot !== HarmonyPlatformDirectory
|
|
60
|
+
|| !isSafeRelativePath(value.moduleRoot)
|
|
61
|
+
|| !isStrictDescendant(value.harmonyRoot, value.moduleRoot)) {
|
|
62
|
+
throw new TypeError('Harmony build descriptor contains invalid project roots.');
|
|
63
|
+
}
|
|
64
|
+
const harmonyRoot = value.harmonyRoot;
|
|
65
|
+
const moduleRoot = value.moduleRoot;
|
|
66
|
+
const identity = value.identity;
|
|
67
|
+
if (!isRecord(identity)
|
|
68
|
+
|| ['abilityName', 'bundleName', 'moduleName', 'productName', 'targetName']
|
|
69
|
+
.some(field => !isNonEmptyString(identity[field]))) {
|
|
70
|
+
throw new TypeError('Harmony build descriptor contains an invalid identity.');
|
|
71
|
+
}
|
|
72
|
+
const inputs = value.nativeInputs;
|
|
73
|
+
if (!isRecord(inputs)
|
|
74
|
+
|| !isSafeRelativePath(inputs.manifest)
|
|
75
|
+
|| !isSafeRelativePath(inputs.lockfile)
|
|
76
|
+
|| !isStrictDescendant(harmonyRoot, inputs.manifest)
|
|
77
|
+
|| !isStrictDescendant(harmonyRoot, inputs.lockfile)) {
|
|
78
|
+
throw new TypeError('Harmony build descriptor contains invalid native input paths.');
|
|
79
|
+
}
|
|
80
|
+
const cache = value.nativeCache;
|
|
81
|
+
if (!isRecord(cache)
|
|
82
|
+
|| !isSafeRelativePath(cache.stateFile)
|
|
83
|
+
|| !isStrictDescendant(moduleRoot, cache.stateFile)
|
|
84
|
+
|| !Array.isArray(cache.invalidationRoots)
|
|
85
|
+
|| cache.invalidationRoots.length === 0
|
|
86
|
+
|| cache.invalidationRoots.some(item => (!isSafeRelativePath(item) || !isStrictDescendant(moduleRoot, item)))) {
|
|
87
|
+
throw new TypeError('Harmony build descriptor contains invalid native cache paths.');
|
|
88
|
+
}
|
|
89
|
+
const files = value.projectFiles;
|
|
90
|
+
const roots = [
|
|
91
|
+
'hvigorConfig',
|
|
92
|
+
'nativeInputsStamp',
|
|
93
|
+
'projectBuildProfile',
|
|
94
|
+
'rootHvigor',
|
|
95
|
+
'templateMarker',
|
|
96
|
+
];
|
|
97
|
+
const modules = ['moduleHvigor', 'moduleJson'];
|
|
98
|
+
if (!isRecord(files)
|
|
99
|
+
|| [...roots, ...modules].some(field => !isSafeRelativePath(files[field]))
|
|
100
|
+
|| roots.some(field => !isStrictDescendant(harmonyRoot, files[field]))
|
|
101
|
+
|| modules.some(field => !isStrictDescendant(moduleRoot, files[field]))) {
|
|
102
|
+
throw new TypeError('Harmony build descriptor contains invalid project file paths.');
|
|
103
|
+
}
|
|
104
|
+
const output = value.export;
|
|
105
|
+
if (!isRecord(output)) {
|
|
106
|
+
throw new TypeError('Harmony build descriptor contains invalid export paths.');
|
|
107
|
+
}
|
|
108
|
+
const invalid = ['bundle', 'manifest', 'metadataRoot', 'rawfileRoot', 'sourceMap']
|
|
109
|
+
.some(field => !isSafeRelativePath(output[field]));
|
|
110
|
+
if (invalid
|
|
111
|
+
|| !isStrictDescendant(moduleRoot, output.rawfileRoot)
|
|
112
|
+
|| !isStrictDescendant(output.rawfileRoot, output.bundle)
|
|
113
|
+
|| !isStrictDescendant(output.metadataRoot, output.sourceMap)) {
|
|
114
|
+
throw new TypeError('Harmony build descriptor contains invalid export paths.');
|
|
115
|
+
}
|
|
116
|
+
if (!isRecord(value.variants)
|
|
117
|
+
|| Object.keys(value.variants).length !== BuildModes.length
|
|
118
|
+
|| BuildModes.some(mode => !Object.hasOwn(value.variants, mode))) {
|
|
119
|
+
throw new TypeError('Harmony build descriptor must define debug and release variants.');
|
|
120
|
+
}
|
|
121
|
+
for (const mode of BuildModes) {
|
|
122
|
+
const variant = validateVariant(value.variants[mode]);
|
|
123
|
+
if (!isStrictDescendant(moduleRoot, variant.expectedHap)) {
|
|
124
|
+
throw new TypeError(`Harmony ${mode} HAP output must be inside the module root.`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function createHarmonyBuildDescriptor(config, signing) {
|
|
130
|
+
const paths = config_plugins_1.HarmonyPaths.HARMONY_PATHS;
|
|
131
|
+
const harmony = HarmonyPlatformDirectory;
|
|
132
|
+
const module = `${harmony}/${node_path_1.default.posix.dirname(paths.entryBuildProfile)}`;
|
|
133
|
+
const target = 'default';
|
|
134
|
+
const rawfile = `${module}/src/main/resources/rawfile`;
|
|
135
|
+
const suffix = signing ? 'signed' : 'unsigned';
|
|
136
|
+
const variants = Object.fromEntries(BuildModes.map(mode => [mode, {
|
|
137
|
+
expectedHap: `${module}/build/${target}/outputs/${target}/${config.moduleName}-${target}-${suffix}.hap`,
|
|
138
|
+
hvigorArgs: [
|
|
139
|
+
'--mode', 'module',
|
|
140
|
+
'-p', `module=${config.moduleName}@${target}`,
|
|
141
|
+
'-p', `product=${config.productName}`,
|
|
142
|
+
'-p', `buildMode=${mode}`,
|
|
143
|
+
'--no-daemon',
|
|
144
|
+
'assembleHap',
|
|
145
|
+
],
|
|
146
|
+
}]));
|
|
147
|
+
return validateHarmonyBuildDescriptor({
|
|
148
|
+
export: {
|
|
149
|
+
bundle: `${rawfile}/hermes_bundle.hbc`,
|
|
150
|
+
manifest: '.expo/harmony/export-manifest.json',
|
|
151
|
+
metadataRoot: '.expo/harmony/export',
|
|
152
|
+
rawfileRoot: rawfile,
|
|
153
|
+
sourceMap: '.expo/harmony/export/hermes_bundle.hbc.map',
|
|
154
|
+
},
|
|
155
|
+
harmonyRoot: harmony,
|
|
156
|
+
identity: {
|
|
157
|
+
abilityName: config.abilityName,
|
|
158
|
+
bundleName: config.bundleName,
|
|
159
|
+
moduleName: config.moduleName,
|
|
160
|
+
productName: config.productName,
|
|
161
|
+
targetName: target,
|
|
162
|
+
},
|
|
163
|
+
moduleRoot: module,
|
|
164
|
+
nativeCache: {
|
|
165
|
+
invalidationRoots: [`${module}/.cxx`, `${module}/build`],
|
|
166
|
+
stateFile: `${module}/.cxx/.expo-harmony-native-dependencies.json`,
|
|
167
|
+
},
|
|
168
|
+
nativeInputs: {
|
|
169
|
+
lockfile: `${harmony}/oh-package-lock.json5`,
|
|
170
|
+
manifest: `${harmony}/${paths.rootOhPackage}`,
|
|
171
|
+
},
|
|
172
|
+
projectFiles: {
|
|
173
|
+
hvigorConfig: `${harmony}/${paths.hvigorConfig}`,
|
|
174
|
+
moduleHvigor: `${harmony}/${paths.entryHvigor}`,
|
|
175
|
+
moduleJson: `${harmony}/${paths.moduleJson}`,
|
|
176
|
+
nativeInputsStamp: `${harmony}/${paths.nativeInputsStamp}`,
|
|
177
|
+
projectBuildProfile: `${harmony}/${paths.projectBuildProfile}`,
|
|
178
|
+
rootHvigor: `${harmony}/${paths.rootHvigor}`,
|
|
179
|
+
templateMarker: `${harmony}/${HarmonyTemplateMarker}`,
|
|
180
|
+
},
|
|
181
|
+
schemaVersion: BuildDescriptorSchemaVersion,
|
|
182
|
+
variants,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function harmonyModuleSourcePath(build) {
|
|
186
|
+
return `./${node_path_1.default.posix.relative(build.harmonyRoot, build.moduleRoot)}`;
|
|
187
|
+
}
|
|
188
|
+
function resolveHarmonyBuildPath(root, file) {
|
|
189
|
+
return node_path_1.default.join(root, ...file.split('/'));
|
|
190
|
+
}
|
package/build/check.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type CngManifest } from './manifest';
|
|
2
|
+
interface Change {
|
|
3
|
+
path: string;
|
|
4
|
+
type: 'added' | 'changed' | 'deleted';
|
|
5
|
+
}
|
|
6
|
+
interface Result {
|
|
7
|
+
changes: Change[];
|
|
8
|
+
clean: boolean;
|
|
9
|
+
expected: CngManifest;
|
|
10
|
+
}
|
|
11
|
+
declare function readManifestIfPresentAsync(project: string): Promise<CngManifest | null>;
|
|
12
|
+
declare function readManifestAsync(project: string): Promise<CngManifest>;
|
|
13
|
+
declare function stageAsync(project: string, expected: string, temp: string): Promise<void>;
|
|
14
|
+
declare function compareAsync(project: string, generated: string): Promise<Result>;
|
|
15
|
+
export { compareAsync, readManifestAsync, readManifestIfPresentAsync, stageAsync, };
|
|
16
|
+
export type { Change, Result, };
|
package/build/check.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
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.compareAsync = compareAsync;
|
|
7
|
+
exports.readManifestAsync = readManifestAsync;
|
|
8
|
+
exports.readManifestIfPresentAsync = readManifestIfPresentAsync;
|
|
9
|
+
exports.stageAsync = stageAsync;
|
|
10
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const node_util_1 = require("node:util");
|
|
13
|
+
const config_1 = require("@expo/config");
|
|
14
|
+
const config_plugins_1 = require("@expo-harmony/config-plugins");
|
|
15
|
+
const expo_modules_autolinking_1 = require("@expo-harmony/expo-modules-autolinking");
|
|
16
|
+
const json5_1 = __importDefault(require("json5"));
|
|
17
|
+
const errors_1 = require("./errors");
|
|
18
|
+
const buildDescriptor_1 = require("./buildDescriptor");
|
|
19
|
+
const manifest_1 = require("./manifest");
|
|
20
|
+
const signing_1 = require("./signing");
|
|
21
|
+
function isInside(root, target) {
|
|
22
|
+
const relative = node_path_1.default.relative(root, target);
|
|
23
|
+
return relative === ''
|
|
24
|
+
|| (!relative.startsWith(`..${node_path_1.default.sep}`) && relative !== '..' && !node_path_1.default.isAbsolute(relative));
|
|
25
|
+
}
|
|
26
|
+
function mirrorPath(temp, source) {
|
|
27
|
+
const absolute = node_path_1.default.resolve(source);
|
|
28
|
+
const parsed = node_path_1.default.parse(absolute);
|
|
29
|
+
const volume = parsed.root.replace(/[^A-Za-z0-9]+/gu, '') || 'root';
|
|
30
|
+
const segments = absolute.slice(parsed.root.length).split(node_path_1.default.sep).filter(Boolean);
|
|
31
|
+
return node_path_1.default.join(temp, 'filesystem', volume, ...segments);
|
|
32
|
+
}
|
|
33
|
+
async function stageAutolinkingAsync(project, target) {
|
|
34
|
+
const source = node_path_1.default.join(project, '.expo/harmony/autolinking.json');
|
|
35
|
+
let stat;
|
|
36
|
+
try {
|
|
37
|
+
stat = await node_fs_1.default.promises.lstat(source);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (error.code === 'ENOENT')
|
|
41
|
+
return;
|
|
42
|
+
throw new errors_1.HarmonyPrebuildError(error.code || 'ERR_HARMONY_MANIFEST_DRIFT', error.message || `Cannot inspect the previous autolinking manifest: ${source}`, { cause: error, operation: error.operation || 'check' });
|
|
43
|
+
}
|
|
44
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
45
|
+
return;
|
|
46
|
+
const content = await node_fs_1.default.promises.readFile(source, 'utf8');
|
|
47
|
+
const file = node_path_1.default.join(target, '.expo/harmony/autolinking.json');
|
|
48
|
+
await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(file), { recursive: true });
|
|
49
|
+
await node_fs_1.default.promises.writeFile(file, content);
|
|
50
|
+
}
|
|
51
|
+
async function matchesIdentity(project, cng) {
|
|
52
|
+
const identity = cng.managedIdentity;
|
|
53
|
+
let profile;
|
|
54
|
+
let module;
|
|
55
|
+
try {
|
|
56
|
+
const [profileSource, moduleSource] = await Promise.all([
|
|
57
|
+
node_fs_1.default.promises.readFile((0, buildDescriptor_1.resolveHarmonyBuildPath)(project, cng.build.projectFiles.projectBuildProfile), 'utf8'),
|
|
58
|
+
node_fs_1.default.promises.readFile((0, buildDescriptor_1.resolveHarmonyBuildPath)(project, cng.build.projectFiles.moduleJson), 'utf8'),
|
|
59
|
+
]);
|
|
60
|
+
profile = json5_1.default.parse(profileSource);
|
|
61
|
+
module = json5_1.default.parse(moduleSource);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const moduleProfile = Array.isArray(profile?.modules)
|
|
67
|
+
? profile.modules.find(item => item?.name === identity.moduleName)
|
|
68
|
+
: null;
|
|
69
|
+
const target = Array.isArray(moduleProfile?.targets)
|
|
70
|
+
? moduleProfile.targets.find(item => item?.name === identity.targetName)
|
|
71
|
+
: null;
|
|
72
|
+
const products = Array.isArray(profile?.app?.products) ? profile.app.products : [];
|
|
73
|
+
const abilities = Array.isArray(module?.module?.abilities) ? module.module.abilities : [];
|
|
74
|
+
return module?.module?.name === identity.moduleName
|
|
75
|
+
&& module?.module?.mainElement === identity.abilityName
|
|
76
|
+
&& abilities.some(item => item?.name === identity.abilityName)
|
|
77
|
+
&& products.some(item => item?.name === identity.productName)
|
|
78
|
+
&& Array.isArray(target?.applyToProducts)
|
|
79
|
+
&& target.applyToProducts.includes(identity.productName);
|
|
80
|
+
}
|
|
81
|
+
async function readManifestIfPresentAsync(project) {
|
|
82
|
+
const file = (0, buildDescriptor_1.resolveHarmonyBuildPath)(project, manifest_1.CngManifestPath);
|
|
83
|
+
let source;
|
|
84
|
+
try {
|
|
85
|
+
source = await node_fs_1.default.promises.readFile(file, 'utf8');
|
|
86
|
+
}
|
|
87
|
+
catch (cause) {
|
|
88
|
+
if (cause.code === 'ENOENT')
|
|
89
|
+
return null;
|
|
90
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_MANIFEST_DRIFT', `Cannot read CNG manifest: ${file}`, { cause, file, operation: 'check' });
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
return (0, manifest_1.validateCngManifest)(JSON.parse(source), { file });
|
|
94
|
+
}
|
|
95
|
+
catch (cause) {
|
|
96
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_MANIFEST_DRIFT', `Cannot read CNG manifest: ${file}`, { cause, file, operation: 'check' });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function readManifestAsync(project) {
|
|
100
|
+
const manifest = await readManifestIfPresentAsync(project);
|
|
101
|
+
if (!manifest) {
|
|
102
|
+
const file = (0, buildDescriptor_1.resolveHarmonyBuildPath)(project, manifest_1.CngManifestPath);
|
|
103
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_MANIFEST_DRIFT', `Cannot read CNG manifest: ${file}`, { file, operation: 'check' });
|
|
104
|
+
}
|
|
105
|
+
return manifest;
|
|
106
|
+
}
|
|
107
|
+
async function stageManifestAsync(project, target) {
|
|
108
|
+
const source = (0, buildDescriptor_1.resolveHarmonyBuildPath)(project, manifest_1.CngManifestPath);
|
|
109
|
+
let stat;
|
|
110
|
+
try {
|
|
111
|
+
stat = await node_fs_1.default.promises.lstat(source);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error.code === 'ENOENT')
|
|
115
|
+
return;
|
|
116
|
+
throw new errors_1.HarmonyPrebuildError(error.code || 'ERR_HARMONY_MANIFEST_DRIFT', error.message || `Cannot inspect the previous CNG manifest: ${source}`, { cause: error, operation: error.operation || 'check' });
|
|
117
|
+
}
|
|
118
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
119
|
+
return;
|
|
120
|
+
let cng;
|
|
121
|
+
try {
|
|
122
|
+
cng = (0, manifest_1.validateCngManifest)(JSON.parse(await node_fs_1.default.promises.readFile(source, 'utf8')), {
|
|
123
|
+
file: source,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch (cause) {
|
|
127
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_MANIFEST_DRIFT', `Cannot stage the CNG manifest for isolated --check: ${source}`, { cause, file: source, operation: 'check' });
|
|
128
|
+
}
|
|
129
|
+
const config = (0, config_1.getConfig)(project, {
|
|
130
|
+
isModdedConfig: true,
|
|
131
|
+
skipSDKVersionRequirement: true,
|
|
132
|
+
}).exp;
|
|
133
|
+
const harmony = (0, config_plugins_1.normalizeHarmonyConfig)(config);
|
|
134
|
+
const hash = (0, manifest_1.hashSha256)((0, config_plugins_1.stableHarmonyJson)(harmony));
|
|
135
|
+
let identity = cng.managedIdentity;
|
|
136
|
+
let build = cng.build;
|
|
137
|
+
let signing = cng.signingConfigName;
|
|
138
|
+
if (hash === cng.inputs.configHash) {
|
|
139
|
+
// Reconstruct mutable re-entry state from authoritative inputs so a corrupt
|
|
140
|
+
// manifest cannot make isolated prebuild overwrite user-owned identities.
|
|
141
|
+
signing = harmony.signingConfigFile
|
|
142
|
+
? (await (0, signing_1.validateHarmonySigningConfigFile)(project, harmony.signingConfigFile)).name
|
|
143
|
+
: null;
|
|
144
|
+
build = (0, buildDescriptor_1.createHarmonyBuildDescriptor)(harmony, signing);
|
|
145
|
+
identity = {
|
|
146
|
+
abilityName: build.identity.abilityName,
|
|
147
|
+
moduleName: build.identity.moduleName,
|
|
148
|
+
productName: build.identity.productName,
|
|
149
|
+
targetName: build.identity.targetName,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
else if (!await matchesIdentity(project, cng)) {
|
|
153
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_MANIFEST_DRIFT', 'The CNG manifest identity does not match the generated Harmony project. Run prebuild to repair it before changing Harmony identity fields.', { operation: 'check' });
|
|
154
|
+
}
|
|
155
|
+
const file = (0, buildDescriptor_1.resolveHarmonyBuildPath)(target, manifest_1.CngManifestPath);
|
|
156
|
+
await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(file), { recursive: true });
|
|
157
|
+
await node_fs_1.default.promises.writeFile(file, (0, config_plugins_1.stableHarmonyJson)({
|
|
158
|
+
...cng,
|
|
159
|
+
build,
|
|
160
|
+
managedIdentity: identity,
|
|
161
|
+
signingConfigName: signing,
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
async function stageSigningAsync(project, temp) {
|
|
165
|
+
const config = (0, config_1.getConfig)(project, {
|
|
166
|
+
isModdedConfig: true,
|
|
167
|
+
skipSDKVersionRequirement: true,
|
|
168
|
+
}).exp;
|
|
169
|
+
const reference = config.harmony?.signingConfigFile;
|
|
170
|
+
if (!reference)
|
|
171
|
+
return;
|
|
172
|
+
const signing = await (0, signing_1.validateHarmonySigningConfigFile)(project, reference);
|
|
173
|
+
const inputs = [signing.file, ...Object.values(signing.materialFiles)];
|
|
174
|
+
for (const source of inputs) {
|
|
175
|
+
if (isInside(project, source))
|
|
176
|
+
continue;
|
|
177
|
+
const target = mirrorPath(temp, source);
|
|
178
|
+
if (!isInside(temp, target)) {
|
|
179
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_SIGNING_INVALID', `External Harmony signing input escapes the isolated --check workspace: ${source}`, { operation: 'check-signing' });
|
|
180
|
+
}
|
|
181
|
+
await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(target), { recursive: true });
|
|
182
|
+
await node_fs_1.default.promises.copyFile(source, target);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function stageAsync(project, expected, temp) {
|
|
186
|
+
await stageManifestAsync(project, expected);
|
|
187
|
+
await stageAutolinkingAsync(project, expected);
|
|
188
|
+
await stageSigningAsync(project, temp);
|
|
189
|
+
}
|
|
190
|
+
async function canonicalizeAutolinkingAsync(expected, project, cng) {
|
|
191
|
+
const manifestFile = node_path_1.default.join(expected, '.expo/harmony/autolinking.json');
|
|
192
|
+
const ohpmPath = cng.build.nativeInputs.manifest;
|
|
193
|
+
const ohpmFile = (0, buildDescriptor_1.resolveHarmonyBuildPath)(expected, ohpmPath);
|
|
194
|
+
try {
|
|
195
|
+
const [manifest, ohpm, generatedRoot, projectRoot] = await Promise.all([
|
|
196
|
+
node_fs_1.default.promises.readFile(manifestFile, 'utf8'),
|
|
197
|
+
node_fs_1.default.promises.readFile(ohpmFile, 'utf8'),
|
|
198
|
+
node_fs_1.default.promises.realpath(expected),
|
|
199
|
+
node_fs_1.default.promises.realpath(project),
|
|
200
|
+
]);
|
|
201
|
+
const result = (0, expo_modules_autolinking_1.canonicalizeAutolinkingArtifacts)({
|
|
202
|
+
manifestSource: manifest,
|
|
203
|
+
ohPackageSource: ohpm,
|
|
204
|
+
generatedProjectRoot: generatedRoot,
|
|
205
|
+
canonicalProjectRoot: projectRoot,
|
|
206
|
+
harmonyProjectPath: (0, buildDescriptor_1.resolveHarmonyBuildPath)(projectRoot, cng.build.harmonyRoot),
|
|
207
|
+
});
|
|
208
|
+
const manifestHash = (0, manifest_1.hashSha256)(result.manifestSource);
|
|
209
|
+
const ohpmHash = (0, manifest_1.hashSha256)(result.ohPackageSource);
|
|
210
|
+
cng.inputs.autolinkingHash = manifestHash;
|
|
211
|
+
const manifestEntry = cng.managedFiles.find(item => item.path === '.expo/harmony/autolinking.json');
|
|
212
|
+
const ohpmEntry = cng.managedFiles.find(item => item.path === ohpmPath);
|
|
213
|
+
if (manifestEntry)
|
|
214
|
+
manifestEntry.sha256 = manifestHash;
|
|
215
|
+
if (ohpmEntry)
|
|
216
|
+
ohpmEntry.sha256 = ohpmHash;
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_MANIFEST_DRIFT', 'Cannot canonicalize the isolated autolinking artifacts.', { cause: error, file: manifestFile, operation: 'check' });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async function compareAsync(project, generated) {
|
|
223
|
+
const expected = await readManifestAsync(generated);
|
|
224
|
+
await canonicalizeAutolinkingAsync(generated, project, expected);
|
|
225
|
+
const actual = await readManifestAsync(project);
|
|
226
|
+
const expectedFiles = new Map(expected.managedFiles.map(item => [item.path, item]));
|
|
227
|
+
const actualFiles = new Map(actual.managedFiles.map(item => [item.path, item]));
|
|
228
|
+
const changes = [];
|
|
229
|
+
for (const [relative, descriptor] of expectedFiles) {
|
|
230
|
+
const file = node_path_1.default.join(project, ...relative.split('/'));
|
|
231
|
+
let hash = null;
|
|
232
|
+
try {
|
|
233
|
+
hash = (0, manifest_1.hashSha256)(Uint8Array.from(await node_fs_1.default.promises.readFile(file)));
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
if (error.code !== 'ENOENT') {
|
|
237
|
+
throw new errors_1.HarmonyPrebuildError(error.code || 'ERR_HARMONY_MANIFEST_DRIFT', error.message || `Cannot read a managed Harmony file: ${file}`, { cause: error, file, operation: error.operation || 'check' });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (hash === null)
|
|
241
|
+
changes.push({ path: relative, type: 'added' });
|
|
242
|
+
else if (hash !== descriptor.sha256)
|
|
243
|
+
changes.push({ path: relative, type: 'changed' });
|
|
244
|
+
}
|
|
245
|
+
for (const relative of actualFiles.keys()) {
|
|
246
|
+
if (!expectedFiles.has(relative))
|
|
247
|
+
changes.push({ path: relative, type: 'deleted' });
|
|
248
|
+
}
|
|
249
|
+
if (!(0, node_util_1.isDeepStrictEqual)(expected, actual)) {
|
|
250
|
+
changes.push({ path: manifest_1.CngManifestPath, type: 'changed' });
|
|
251
|
+
}
|
|
252
|
+
changes.sort((left, right) => (left.path.localeCompare(right.path, 'en') || left.type.localeCompare(right.type, 'en')));
|
|
253
|
+
return { changes, clean: changes.length === 0, expected };
|
|
254
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare function resolvePackageVersion(root: any, name: any): string;
|
|
2
|
+
declare function resolveTemplateFile(relative: any): string;
|
|
3
|
+
declare function readTemplateSource(relative: any): Promise<string>;
|
|
4
|
+
declare function resolveRnohHvigorPlugin(root: any): string;
|
|
5
|
+
declare function toRelativeDependency(directory: any, file: any): string;
|
|
6
|
+
export { readTemplateSource, resolvePackageVersion, resolveRnohHvigorPlugin, resolveTemplateFile, toRelativeDependency, };
|
|
@@ -0,0 +1,77 @@
|
|
|
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.readTemplateSource = readTemplateSource;
|
|
7
|
+
exports.resolvePackageVersion = resolvePackageVersion;
|
|
8
|
+
exports.resolveRnohHvigorPlugin = resolveRnohHvigorPlugin;
|
|
9
|
+
exports.resolveTemplateFile = resolveTemplateFile;
|
|
10
|
+
exports.toRelativeDependency = toRelativeDependency;
|
|
11
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
|
+
const node_module_1 = require("node:module");
|
|
13
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
+
const errors_1 = require("./errors");
|
|
15
|
+
const template_1 = require("./template");
|
|
16
|
+
const resolveModule = (0, node_module_1.createRequire)(__filename).resolve;
|
|
17
|
+
function resolvePackageFile(root, name, relative) {
|
|
18
|
+
const logical = node_path_1.default.join(root, 'node_modules', ...name.split('/'), relative);
|
|
19
|
+
if (node_fs_1.default.existsSync(logical))
|
|
20
|
+
return logical;
|
|
21
|
+
let json;
|
|
22
|
+
try {
|
|
23
|
+
json = resolveModule(`${name}/package.json`, { paths: [root] });
|
|
24
|
+
}
|
|
25
|
+
catch (cause) {
|
|
26
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_DEPENDENCY_MISSING', `Unable to resolve ${name} from the project.`, { cause, operation: 'resolve-dependency' });
|
|
27
|
+
}
|
|
28
|
+
const resolved = node_path_1.default.join(node_path_1.default.dirname(json), relative);
|
|
29
|
+
if (!node_fs_1.default.existsSync(resolved)) {
|
|
30
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_DEPENDENCY_MISSING', `${name} is missing ${relative}.`, { file: resolved, operation: 'resolve-dependency' });
|
|
31
|
+
}
|
|
32
|
+
return resolved;
|
|
33
|
+
}
|
|
34
|
+
function resolvePackageVersion(root, name) {
|
|
35
|
+
const json = resolvePackageFile(root, name, 'package.json');
|
|
36
|
+
let version;
|
|
37
|
+
try {
|
|
38
|
+
version = JSON.parse(node_fs_1.default.readFileSync(json, 'utf8')).version;
|
|
39
|
+
}
|
|
40
|
+
catch (cause) {
|
|
41
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_DEPENDENCY_MISSING', `Cannot read ${name} version.`, { cause, file: json, operation: 'resolve-dependency' });
|
|
42
|
+
}
|
|
43
|
+
if (typeof version !== 'string' || !version) {
|
|
44
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_DEPENDENCY_MISSING', `${name} has no package version.`, { file: json, operation: 'resolve-dependency' });
|
|
45
|
+
}
|
|
46
|
+
return version;
|
|
47
|
+
}
|
|
48
|
+
function resolveTemplateFile(relative) {
|
|
49
|
+
const resolved = node_path_1.default.join((0, template_1.resolve)().root, relative);
|
|
50
|
+
if (!node_fs_1.default.existsSync(resolved)) {
|
|
51
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_DEPENDENCY_MISSING', `@expo-harmony/template is missing ${relative}.`, { file: resolved, operation: 'resolve-dependency' });
|
|
52
|
+
}
|
|
53
|
+
return resolved;
|
|
54
|
+
}
|
|
55
|
+
async function readTemplateSource(relative) {
|
|
56
|
+
return node_fs_1.default.promises.readFile(resolveTemplateFile(`harmony/${relative}`), 'utf8');
|
|
57
|
+
}
|
|
58
|
+
function resolveRnohHvigorPlugin(root) {
|
|
59
|
+
const json = resolvePackageFile(root, '@react-native-oh/react-native-harmony-cli', 'package.json');
|
|
60
|
+
const harmony = node_path_1.default.join(node_path_1.default.dirname(json), 'harmony');
|
|
61
|
+
let candidates;
|
|
62
|
+
try {
|
|
63
|
+
candidates = node_fs_1.default.readdirSync(harmony)
|
|
64
|
+
.filter(name => /^rnoh-hvigor-plugin-.+\.tgz$/u.test(name))
|
|
65
|
+
.sort();
|
|
66
|
+
}
|
|
67
|
+
catch (cause) {
|
|
68
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_DEPENDENCY_MISSING', '@react-native-oh/react-native-harmony-cli has no Harmony Hvigor plugin directory.', { cause, file: harmony, operation: 'resolve-dependency' });
|
|
69
|
+
}
|
|
70
|
+
if (candidates.length !== 1) {
|
|
71
|
+
throw new errors_1.HarmonyPrebuildError('ERR_HARMONY_DEPENDENCY_MISSING', '@react-native-oh/react-native-harmony-cli must contain exactly one RNOH Hvigor plugin archive.', { file: harmony, operation: 'resolve-dependency' });
|
|
72
|
+
}
|
|
73
|
+
return node_path_1.default.join(harmony, candidates[0]);
|
|
74
|
+
}
|
|
75
|
+
function toRelativeDependency(directory, file) {
|
|
76
|
+
return node_path_1.default.relative(directory, file).split(node_path_1.default.sep).join('/');
|
|
77
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface HarmonyPrebuildErrorOptions {
|
|
2
|
+
cause?: unknown;
|
|
3
|
+
file?: string;
|
|
4
|
+
operation?: string;
|
|
5
|
+
}
|
|
6
|
+
declare class HarmonyPrebuildError extends Error {
|
|
7
|
+
readonly code: string;
|
|
8
|
+
readonly file?: string;
|
|
9
|
+
readonly operation: string;
|
|
10
|
+
constructor(code: string, message: string, options?: HarmonyPrebuildErrorOptions);
|
|
11
|
+
}
|
|
12
|
+
export { HarmonyPrebuildError };
|
|
13
|
+
export type { HarmonyPrebuildErrorOptions };
|
package/build/errors.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HarmonyPrebuildError = void 0;
|
|
4
|
+
class HarmonyPrebuildError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
file;
|
|
7
|
+
operation;
|
|
8
|
+
constructor(code, message, options = {}) {
|
|
9
|
+
super(message, options.cause === undefined ? undefined : { cause: options.cause });
|
|
10
|
+
this.name = 'HarmonyPrebuildError';
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.operation = options.operation || 'prebuild';
|
|
13
|
+
if (options.file)
|
|
14
|
+
this.file = options.file;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.HarmonyPrebuildError = HarmonyPrebuildError;
|