@expo-harmony/prebuild-config 55.0.0-harmony.1
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.md +68 -0
- package/build/index.js +26 -0
- package/package.json +87 -0
- package/src/api/signing.ts +4 -0
- package/src/buildDescriptor.ts +277 -0
- package/src/check.ts +390 -0
- package/src/dependencies.ts +123 -0
- package/src/errors.ts +23 -0
- package/src/generatedFiles.ts +39 -0
- package/src/index.ts +25 -0
- package/src/manifest.ts +303 -0
- package/src/mods/withAutolinkingMods.ts +92 -0
- package/src/mods/withEntryMods.ts +332 -0
- package/src/mods/withPreparationMod.ts +85 -0
- package/src/mods/withProjectMods.ts +258 -0
- package/src/mods/withSourceMods.ts +32 -0
- package/src/native.ts +10 -0
- package/src/packageMetadata.ts +8 -0
- package/src/plugin.ts +7 -0
- package/src/reconcile.ts +70 -0
- package/src/renderers.ts +90 -0
- package/src/signing.ts +251 -0
- package/src/stale.ts +103 -0
- package/src/template.ts +172 -0
- package/src/withHarmonyPrebuildConfig.ts +47 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
getHarmonyConfigPlugins,
|
|
6
|
+
HarmonyPaths,
|
|
7
|
+
withColors,
|
|
8
|
+
withEntryBuildProfile,
|
|
9
|
+
withEntryHvigor,
|
|
10
|
+
withEntryOhPackage,
|
|
11
|
+
withMedia,
|
|
12
|
+
withModuleJson,
|
|
13
|
+
withProfiles,
|
|
14
|
+
withStrings,
|
|
15
|
+
} from '@expo-harmony/config-plugins';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
createHarmonyBuildDescriptor,
|
|
19
|
+
HarmonyPlatformDirectory,
|
|
20
|
+
} from '../buildDescriptor';
|
|
21
|
+
import { readTemplateSource, resolveTemplateFile } from '../dependencies';
|
|
22
|
+
import { HarmonyPrebuildError } from '../errors';
|
|
23
|
+
import {
|
|
24
|
+
appendUnique,
|
|
25
|
+
readRecord,
|
|
26
|
+
upsertManagedNamed,
|
|
27
|
+
upsertNamed,
|
|
28
|
+
} from '../reconcile';
|
|
29
|
+
import * as render from '../renderers';
|
|
30
|
+
import { removeStaleExtensionAbilities, removeStaleResources } from '../stale';
|
|
31
|
+
|
|
32
|
+
function setResource(items, name, value) {
|
|
33
|
+
const resources = (Array.isArray(items) ? items : [])
|
|
34
|
+
.filter(item => item?.name !== name);
|
|
35
|
+
|
|
36
|
+
resources.push({ name, value });
|
|
37
|
+
resources.sort((left, right) => left.name.localeCompare(right.name, 'en'));
|
|
38
|
+
|
|
39
|
+
return resources;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function replaceMediaBase(files, base, descriptor?) {
|
|
43
|
+
for (const name of Object.keys(files)) {
|
|
44
|
+
if (path.parse(name).name === base) delete files[name];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (descriptor) {
|
|
48
|
+
files[descriptor.name] = { source: descriptor.source, replaceBase: true };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function reconcileRuntimeMetadata(items) {
|
|
53
|
+
const names = new Set([
|
|
54
|
+
'OPTLazyForEach',
|
|
55
|
+
'can_preview_text',
|
|
56
|
+
'halfLeading',
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
return (Array.isArray(items) ? items : [])
|
|
60
|
+
.filter(item => !names.has(item?.name))
|
|
61
|
+
.concat([
|
|
62
|
+
{ name: 'OPTLazyForEach', value: 'true' },
|
|
63
|
+
{ name: 'can_preview_text', value: 'true' },
|
|
64
|
+
{ name: 'halfLeading', value: 'true' },
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isAbilityValueClaimed(plugins, field, value) {
|
|
69
|
+
return plugins.some(plugin => plugin.ability?.[field] === value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getSourceExtension(value) {
|
|
73
|
+
const extension = path.extname(value || '').toLowerCase();
|
|
74
|
+
|
|
75
|
+
if (!['.png', '.jpg', '.jpeg', '.svg', '.webp'].includes(extension)) {
|
|
76
|
+
throw new HarmonyPrebuildError(
|
|
77
|
+
'ERR_HARMONY_CONFIG_INVALID',
|
|
78
|
+
`Unsupported Harmony icon format: ${extension || '(none)'}`,
|
|
79
|
+
{ file: value, operation: 'render-icons' }
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return extension;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function resolveInputFile(root, value) {
|
|
87
|
+
const file = path.resolve(root, value);
|
|
88
|
+
|
|
89
|
+
let stat;
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
stat = fs.statSync(file);
|
|
93
|
+
} catch (cause) {
|
|
94
|
+
throw new HarmonyPrebuildError(
|
|
95
|
+
'ERR_HARMONY_CONFIG_INVALID',
|
|
96
|
+
`Harmony input file does not exist: ${file}`,
|
|
97
|
+
{ cause, file, operation: 'render-icons' }
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!stat.isFile()) {
|
|
102
|
+
throw new HarmonyPrebuildError(
|
|
103
|
+
'ERR_HARMONY_CONFIG_INVALID',
|
|
104
|
+
`Harmony input is not a file: ${file}`,
|
|
105
|
+
{ file, operation: 'render-icons' }
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return file;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function withEntryMods(config, harmony) {
|
|
113
|
+
config = withEntryBuildProfile(config, (mod) => {
|
|
114
|
+
const build = readRecord(mod.modResults.buildOption);
|
|
115
|
+
const native = readRecord(build.externalNativeOptions);
|
|
116
|
+
|
|
117
|
+
mod.modResults = {
|
|
118
|
+
...mod.modResults,
|
|
119
|
+
apiType: 'stageMode',
|
|
120
|
+
buildOption: {
|
|
121
|
+
...build,
|
|
122
|
+
externalNativeOptions: {
|
|
123
|
+
...native,
|
|
124
|
+
path: './src/main/cpp/CMakeLists.txt',
|
|
125
|
+
abiFilters: harmony.abiFilters,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
targets: upsertNamed(
|
|
129
|
+
upsertNamed(mod.modResults.targets, 'default', existing => ({
|
|
130
|
+
...existing,
|
|
131
|
+
name: 'default',
|
|
132
|
+
runtimeOS: 'HarmonyOS',
|
|
133
|
+
})),
|
|
134
|
+
'ohosTest',
|
|
135
|
+
existing => ({ ...existing, name: 'ohosTest' })
|
|
136
|
+
),
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return mod;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
config = withEntryOhPackage(config, (mod) => {
|
|
143
|
+
mod.modResults = {
|
|
144
|
+
...mod.modResults,
|
|
145
|
+
name: harmony.moduleName,
|
|
146
|
+
version: harmony.versionName,
|
|
147
|
+
description: `${harmony.label} Harmony entry module`,
|
|
148
|
+
license: mod.modResults.license || 'MIT',
|
|
149
|
+
dependencies: mod.modResults.dependencies || {},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return mod;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
config = withEntryHvigor(config, async (mod) => {
|
|
156
|
+
const build = createHarmonyBuildDescriptor(harmony, mod._internal?.harmonySigningConfig?.name ?? null);
|
|
157
|
+
const relative = path.posix.relative(build.harmonyRoot, build.projectFiles.moduleHvigor);
|
|
158
|
+
|
|
159
|
+
mod.modResults = render.renderCanonical(
|
|
160
|
+
await readTemplateSource(relative),
|
|
161
|
+
build.projectFiles.moduleHvigor
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
return mod;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
config = withModuleJson(config, (mod) => {
|
|
168
|
+
const home = {
|
|
169
|
+
entities: ['entity.system.home'],
|
|
170
|
+
actions: ['action.system.home'],
|
|
171
|
+
};
|
|
172
|
+
const module = removeStaleExtensionAbilities(
|
|
173
|
+
readRecord(mod.modResults.module),
|
|
174
|
+
HarmonyPaths.toPosixRelative(
|
|
175
|
+
mod.modRequest.projectRoot,
|
|
176
|
+
path.join(mod.modRequest.platformProjectRoot, HarmonyPaths.HARMONY_PATHS.moduleJson)
|
|
177
|
+
),
|
|
178
|
+
mod._internal?.harmonyStalePluginFiles || []
|
|
179
|
+
);
|
|
180
|
+
const previous = mod._internal?.harmonyPreviousManagedIdentity;
|
|
181
|
+
const plugins = getHarmonyConfigPlugins(mod);
|
|
182
|
+
|
|
183
|
+
const abilities = upsertManagedNamed(
|
|
184
|
+
module.abilities,
|
|
185
|
+
harmony.abilityName,
|
|
186
|
+
previous?.abilityName,
|
|
187
|
+
'EntryAbility',
|
|
188
|
+
existing => ({
|
|
189
|
+
...existing,
|
|
190
|
+
name: harmony.abilityName,
|
|
191
|
+
srcEntry: './ets/entryability/EntryAbility.ets',
|
|
192
|
+
description: '$string:expo_harmony_ability_desc',
|
|
193
|
+
icon: '$media:app_icon',
|
|
194
|
+
label: '$string:expo_harmony_ability_label',
|
|
195
|
+
startWindowIcon: isAbilityValueClaimed(
|
|
196
|
+
plugins,
|
|
197
|
+
'startWindowIcon',
|
|
198
|
+
existing.startWindowIcon
|
|
199
|
+
)
|
|
200
|
+
? existing.startWindowIcon
|
|
201
|
+
: '$media:app_icon',
|
|
202
|
+
startWindowBackground: isAbilityValueClaimed(
|
|
203
|
+
plugins,
|
|
204
|
+
'startWindowBackground',
|
|
205
|
+
existing.startWindowBackground
|
|
206
|
+
)
|
|
207
|
+
? existing.startWindowBackground
|
|
208
|
+
: '$color:expo_harmony_start_window_background',
|
|
209
|
+
exported: true,
|
|
210
|
+
visible: true,
|
|
211
|
+
orientation: harmony.nativeOrientation,
|
|
212
|
+
skills: appendUnique([home], harmony.skills),
|
|
213
|
+
}),
|
|
214
|
+
'abilityName'
|
|
215
|
+
);
|
|
216
|
+
const metadata = reconcileRuntimeMetadata(module.metadata);
|
|
217
|
+
|
|
218
|
+
mod.modResults = {
|
|
219
|
+
...mod.modResults,
|
|
220
|
+
module: {
|
|
221
|
+
...module,
|
|
222
|
+
name: harmony.moduleName,
|
|
223
|
+
type: 'entry',
|
|
224
|
+
description: '$string:expo_harmony_module_desc',
|
|
225
|
+
mainElement: harmony.abilityName,
|
|
226
|
+
deviceTypes: harmony.deviceTypes,
|
|
227
|
+
deliveryWithInstall: true,
|
|
228
|
+
installationFree: false,
|
|
229
|
+
pages: '$profile:main_pages',
|
|
230
|
+
requestPermissions: harmony.permissions,
|
|
231
|
+
querySchemes: harmony.querySchemes,
|
|
232
|
+
metadata,
|
|
233
|
+
abilities,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
return mod;
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
config = withStrings(config, (mod) => {
|
|
241
|
+
mod.modResults = removeStaleResources(
|
|
242
|
+
mod.modResults,
|
|
243
|
+
'strings',
|
|
244
|
+
mod._internal?.harmonyStaleConfigPlugins || []
|
|
245
|
+
);
|
|
246
|
+
mod.modResults.app ??= {};
|
|
247
|
+
mod.modResults.entry ??= {};
|
|
248
|
+
|
|
249
|
+
mod.modResults.app.string = setResource(
|
|
250
|
+
mod.modResults.app.string,
|
|
251
|
+
'app_name',
|
|
252
|
+
harmony.label
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
let strings = mod.modResults.entry.string || [];
|
|
256
|
+
|
|
257
|
+
strings = setResource(
|
|
258
|
+
strings,
|
|
259
|
+
'expo_harmony_ability_desc',
|
|
260
|
+
`${harmony.label} main ability`
|
|
261
|
+
);
|
|
262
|
+
strings = setResource(strings, 'expo_harmony_ability_label', harmony.label);
|
|
263
|
+
strings = setResource(
|
|
264
|
+
strings,
|
|
265
|
+
'expo_harmony_module_desc',
|
|
266
|
+
`${harmony.label} entry module`
|
|
267
|
+
);
|
|
268
|
+
mod.modResults.entry.string = strings;
|
|
269
|
+
|
|
270
|
+
return mod;
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
config = withColors(config, (mod) => {
|
|
274
|
+
mod.modResults = removeStaleResources(
|
|
275
|
+
mod.modResults,
|
|
276
|
+
'colors',
|
|
277
|
+
mod._internal?.harmonyStaleConfigPlugins || []
|
|
278
|
+
);
|
|
279
|
+
mod.modResults.entry ??= {};
|
|
280
|
+
mod.modResults.entryDark ??= {};
|
|
281
|
+
|
|
282
|
+
mod.modResults.entry.color = setResource(
|
|
283
|
+
mod.modResults.entry.color,
|
|
284
|
+
'expo_harmony_start_window_background',
|
|
285
|
+
harmony.backgroundColor
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
return mod;
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
config = withMedia(config, (mod) => {
|
|
292
|
+
mod.modResults = removeStaleResources(
|
|
293
|
+
mod.modResults,
|
|
294
|
+
'media',
|
|
295
|
+
mod._internal?.harmonyStaleConfigPlugins || []
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const icon = harmony.icon
|
|
299
|
+
? resolveInputFile(mod.modRequest.projectRoot, harmony.icon)
|
|
300
|
+
: null;
|
|
301
|
+
const sources = icon
|
|
302
|
+
? { app: icon, entry: icon }
|
|
303
|
+
: {
|
|
304
|
+
app: resolveTemplateFile(`${HarmonyPlatformDirectory}/${HarmonyPaths.RESOURCE_PATHS.media.app}/app_icon.svg`),
|
|
305
|
+
entry: resolveTemplateFile(`${HarmonyPlatformDirectory}/${HarmonyPaths.RESOURCE_PATHS.media.entry}/app_icon.svg`),
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
for (const [scope, source] of Object.entries(sources)) {
|
|
309
|
+
mod.modResults[scope] ??= {};
|
|
310
|
+
replaceMediaBase(mod.modResults[scope], 'app_icon', {
|
|
311
|
+
name: `app_icon${getSourceExtension(source)}`,
|
|
312
|
+
source,
|
|
313
|
+
});
|
|
314
|
+
replaceMediaBase(mod.modResults[scope], 'app_icon_round');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return mod;
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
config = withProfiles(config, (mod) => {
|
|
321
|
+
mod.modResults = {
|
|
322
|
+
...mod.modResults,
|
|
323
|
+
src: appendUnique(mod.modResults.src, ['pages/Index']),
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
return mod;
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
return config;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export { withEntryMods };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
getHarmonyConfigPlugins,
|
|
6
|
+
recordManagedFile,
|
|
7
|
+
withHarmonyDangerousMod,
|
|
8
|
+
} from '@expo-harmony/config-plugins';
|
|
9
|
+
|
|
10
|
+
import { createHarmonyBuildDescriptor } from '../buildDescriptor';
|
|
11
|
+
import { HarmonyPrebuildError } from '../errors';
|
|
12
|
+
import { ensureGitignoreEntryAsync } from '../generatedFiles';
|
|
13
|
+
import { readSigningConfigFile } from '../signing';
|
|
14
|
+
import {
|
|
15
|
+
findStaleConfigPlugins,
|
|
16
|
+
readPreviousCngManifestAsync,
|
|
17
|
+
removeStalePluginFilesAsync,
|
|
18
|
+
} from '../stale';
|
|
19
|
+
|
|
20
|
+
function withPreparationMod(config, harmony) {
|
|
21
|
+
return withHarmonyDangerousMod(config, async (mod) => {
|
|
22
|
+
const root = mod.modRequest.projectRoot;
|
|
23
|
+
const platform = mod.modRequest.platformProjectRoot;
|
|
24
|
+
const manifest = await readPreviousCngManifestAsync(root);
|
|
25
|
+
const plugins = getHarmonyConfigPlugins(mod);
|
|
26
|
+
const stale = findStaleConfigPlugins(manifest, plugins);
|
|
27
|
+
|
|
28
|
+
mod._internal ??= {};
|
|
29
|
+
mod._internal.harmonyPreviousSigningConfigName
|
|
30
|
+
= typeof manifest?.signingConfigName === 'string'
|
|
31
|
+
? manifest.signingConfigName
|
|
32
|
+
: null;
|
|
33
|
+
mod._internal.harmonyPreviousManagedIdentity
|
|
34
|
+
= manifest?.managedIdentity && typeof manifest.managedIdentity === 'object'
|
|
35
|
+
? manifest.managedIdentity
|
|
36
|
+
: null;
|
|
37
|
+
mod._internal.harmonyConfigPlugins = plugins;
|
|
38
|
+
mod._internal.harmonyStaleConfigPlugins = stale;
|
|
39
|
+
|
|
40
|
+
mod._internal.harmonyStalePluginFiles = await removeStalePluginFilesAsync(root, manifest, stale);
|
|
41
|
+
|
|
42
|
+
const packed = path.join(platform, 'gitignore');
|
|
43
|
+
const gitignore = path.join(platform, '.gitignore');
|
|
44
|
+
|
|
45
|
+
if (fs.existsSync(packed)) {
|
|
46
|
+
if (fs.existsSync(gitignore)) {
|
|
47
|
+
const [source, current] = await Promise.all([
|
|
48
|
+
fs.promises.readFile(packed),
|
|
49
|
+
fs.promises.readFile(gitignore),
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
if (!source.equals(Uint8Array.from(current))) {
|
|
53
|
+
throw new HarmonyPrebuildError(
|
|
54
|
+
'ERR_HARMONY_CONFIG_INVALID',
|
|
55
|
+
'The packed Harmony gitignore conflicts with an existing harmony/.gitignore.',
|
|
56
|
+
{ file: gitignore, operation: 'restore-gitignore' }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await fs.promises.rm(packed);
|
|
61
|
+
} else {
|
|
62
|
+
await fs.promises.rename(packed, gitignore);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const build = createHarmonyBuildDescriptor(harmony, null);
|
|
67
|
+
const profile = path.posix.relative(build.harmonyRoot, build.projectFiles.projectBuildProfile);
|
|
68
|
+
|
|
69
|
+
await ensureGitignoreEntryAsync(gitignore, `/${profile}`);
|
|
70
|
+
recordManagedFile(mod, gitignore, 'dangerous');
|
|
71
|
+
|
|
72
|
+
if (harmony.signingConfigFile) {
|
|
73
|
+
const signing = await readSigningConfigFile(
|
|
74
|
+
root,
|
|
75
|
+
harmony.signingConfigFile
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
mod._internal.harmonySigningConfig = signing.config;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return mod;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { withPreparationMod };
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
withAppJson,
|
|
5
|
+
withHvigorConfig,
|
|
6
|
+
withNativeInputsStamp,
|
|
7
|
+
withProjectBuildProfile,
|
|
8
|
+
withReactNativeConfig,
|
|
9
|
+
withRootHvigor,
|
|
10
|
+
withRootOhPackage,
|
|
11
|
+
} from '@expo-harmony/config-plugins';
|
|
12
|
+
import { loadConfigAsync as loadReactNativeCliConfigAsync } from '@react-native-community/cli-config';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
createHarmonyBuildDescriptor,
|
|
16
|
+
harmonyModuleSourcePath,
|
|
17
|
+
resolveHarmonyBuildPath,
|
|
18
|
+
} from '../buildDescriptor';
|
|
19
|
+
import {
|
|
20
|
+
readTemplateSource,
|
|
21
|
+
resolvePackageVersion,
|
|
22
|
+
resolveRnohHvigorPlugin,
|
|
23
|
+
toRelativeDependency,
|
|
24
|
+
} from '../dependencies';
|
|
25
|
+
import { HarmonyPrebuildError } from '../errors';
|
|
26
|
+
import { readRecord, replaceManagedString, upsertManagedNamed, upsertNamed } from '../reconcile';
|
|
27
|
+
import * as render from '../renderers';
|
|
28
|
+
|
|
29
|
+
function hasRnohLinkCommand(config) {
|
|
30
|
+
const names = new Set((config?.commands || []).map(command => command?.name));
|
|
31
|
+
return names.has('link-harmony');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function loadReactNativeConfigAsync(root, file) {
|
|
35
|
+
try {
|
|
36
|
+
return await loadReactNativeCliConfigAsync({ projectRoot: root });
|
|
37
|
+
} catch (cause) {
|
|
38
|
+
throw new HarmonyPrebuildError(
|
|
39
|
+
'ERR_HARMONY_CONFIG_INVALID',
|
|
40
|
+
`Cannot load the existing React Native CLI config: ${file}`,
|
|
41
|
+
{ cause, file, operation: 'react-native-config' }
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function withProjectMods(config, harmony) {
|
|
47
|
+
config = withReactNativeConfig(config, async (mod) => {
|
|
48
|
+
const root = mod.modRequest.projectRoot;
|
|
49
|
+
const request = mod.modRequest as typeof mod.modRequest & {
|
|
50
|
+
modFile: string;
|
|
51
|
+
modFileExists: boolean;
|
|
52
|
+
};
|
|
53
|
+
const file = request.modFile;
|
|
54
|
+
|
|
55
|
+
if (request.modFileExists) {
|
|
56
|
+
if (!hasRnohLinkCommand(await loadReactNativeConfigAsync(root, file))) {
|
|
57
|
+
throw new HarmonyPrebuildError(
|
|
58
|
+
'ERR_HARMONY_CONFIG_INVALID',
|
|
59
|
+
'The React Native CLI config must expose the RNOH link-harmony command.',
|
|
60
|
+
{ file, operation: 'react-native-config' }
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return mod;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
mod.modResults = render.renderReactNativeConfig();
|
|
68
|
+
|
|
69
|
+
return mod;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
config = withAppJson(config, (mod) => {
|
|
73
|
+
const app = mod.modResults.app && typeof mod.modResults.app === 'object'
|
|
74
|
+
? mod.modResults.app
|
|
75
|
+
: {};
|
|
76
|
+
|
|
77
|
+
mod.modResults = {
|
|
78
|
+
...mod.modResults,
|
|
79
|
+
app: {
|
|
80
|
+
...app,
|
|
81
|
+
bundleName: harmony.bundleName,
|
|
82
|
+
icon: '$media:app_icon',
|
|
83
|
+
label: '$string:app_name',
|
|
84
|
+
vendor: harmony.vendor,
|
|
85
|
+
versionCode: harmony.versionCode,
|
|
86
|
+
versionName: harmony.versionName,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return mod;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
config = withProjectBuildProfile(config, (mod) => {
|
|
94
|
+
const build = createHarmonyBuildDescriptor(
|
|
95
|
+
harmony,
|
|
96
|
+
mod._internal?.harmonySigningConfig?.name ?? null
|
|
97
|
+
);
|
|
98
|
+
const { moduleName, productName, targetName } = build.identity;
|
|
99
|
+
|
|
100
|
+
const profile = mod.modResults;
|
|
101
|
+
const app = readRecord(profile.app);
|
|
102
|
+
const signing = mod._internal?.harmonySigningConfig;
|
|
103
|
+
const previousSigning = mod._internal?.harmonyPreviousSigningConfigName;
|
|
104
|
+
const previous = mod._internal?.harmonyPreviousManagedIdentity;
|
|
105
|
+
|
|
106
|
+
const products = upsertManagedNamed(
|
|
107
|
+
app.products,
|
|
108
|
+
productName,
|
|
109
|
+
previous?.productName,
|
|
110
|
+
'default',
|
|
111
|
+
(existing) => {
|
|
112
|
+
const product = {
|
|
113
|
+
...existing,
|
|
114
|
+
name: productName,
|
|
115
|
+
compatibleSdkVersion: harmony.compatibleSdkVersionString,
|
|
116
|
+
targetSdkVersion: harmony.targetSdkVersionString,
|
|
117
|
+
runtimeOS: 'HarmonyOS',
|
|
118
|
+
buildOption: { ...(existing.buildOption || {}), nativeCompiler: 'BiSheng' },
|
|
119
|
+
...(signing ? { signingConfig: signing.name } : {}),
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
if (!signing && product.signingConfig === previousSigning) {
|
|
123
|
+
delete product.signingConfig;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return product;
|
|
127
|
+
},
|
|
128
|
+
'productName'
|
|
129
|
+
);
|
|
130
|
+
const modules = upsertManagedNamed(
|
|
131
|
+
profile.modules,
|
|
132
|
+
moduleName,
|
|
133
|
+
previous?.moduleName,
|
|
134
|
+
'entry',
|
|
135
|
+
existing => ({
|
|
136
|
+
...existing,
|
|
137
|
+
name: moduleName,
|
|
138
|
+
srcPath: harmonyModuleSourcePath(build),
|
|
139
|
+
targets: upsertNamed(existing.targets, targetName, target => ({
|
|
140
|
+
...target,
|
|
141
|
+
name: targetName,
|
|
142
|
+
applyToProducts: replaceManagedString(
|
|
143
|
+
target.applyToProducts,
|
|
144
|
+
productName,
|
|
145
|
+
previous?.productName,
|
|
146
|
+
'default'
|
|
147
|
+
),
|
|
148
|
+
})),
|
|
149
|
+
}),
|
|
150
|
+
'moduleName'
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
let configs = app.signingConfigs;
|
|
154
|
+
|
|
155
|
+
if (signing) {
|
|
156
|
+
configs = upsertManagedNamed(
|
|
157
|
+
configs,
|
|
158
|
+
signing.name,
|
|
159
|
+
previousSigning,
|
|
160
|
+
'',
|
|
161
|
+
() => signing,
|
|
162
|
+
'signingConfigName'
|
|
163
|
+
);
|
|
164
|
+
} else if (previousSigning) {
|
|
165
|
+
configs = (Array.isArray(configs) ? configs : [])
|
|
166
|
+
.filter(item => item?.name !== previousSigning);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
mod.modResults = {
|
|
170
|
+
...profile,
|
|
171
|
+
app: {
|
|
172
|
+
...app,
|
|
173
|
+
products,
|
|
174
|
+
buildModeSet: upsertNamed(
|
|
175
|
+
upsertNamed(app.buildModeSet, 'debug', existing => ({ ...existing, name: 'debug' })),
|
|
176
|
+
'release',
|
|
177
|
+
existing => ({ ...existing, name: 'release' })
|
|
178
|
+
),
|
|
179
|
+
...(configs === undefined ? {} : { signingConfigs: configs }),
|
|
180
|
+
},
|
|
181
|
+
modules,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return mod;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
config = withRootOhPackage(config, (mod) => {
|
|
188
|
+
const version = resolvePackageVersion(
|
|
189
|
+
mod.modRequest.projectRoot,
|
|
190
|
+
'@react-native-oh/react-native-harmony'
|
|
191
|
+
);
|
|
192
|
+
const dependencies = { ...readRecord(mod.modResults.dependencies) };
|
|
193
|
+
const overrides = { ...readRecord(mod.modResults.overrides) };
|
|
194
|
+
|
|
195
|
+
mod.modResults = {
|
|
196
|
+
...mod.modResults,
|
|
197
|
+
name: harmony.bundleName,
|
|
198
|
+
version: harmony.versionName,
|
|
199
|
+
license: mod.modResults.license || 'MIT',
|
|
200
|
+
dependencies: {
|
|
201
|
+
...dependencies,
|
|
202
|
+
'@rnoh/react-native-openharmony': version,
|
|
203
|
+
},
|
|
204
|
+
overrides: {
|
|
205
|
+
...overrides,
|
|
206
|
+
'@rnoh/react-native-openharmony': version,
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
return mod;
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
config = withRootHvigor(config, async (mod) => {
|
|
214
|
+
const build = createHarmonyBuildDescriptor(
|
|
215
|
+
harmony,
|
|
216
|
+
mod._internal?.harmonySigningConfig?.name ?? null
|
|
217
|
+
);
|
|
218
|
+
const relative = path.posix.relative(build.harmonyRoot, build.projectFiles.rootHvigor);
|
|
219
|
+
const source = await readTemplateSource(relative);
|
|
220
|
+
|
|
221
|
+
mod.modResults = render.renderRootHvigor(source, build);
|
|
222
|
+
|
|
223
|
+
return mod;
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
config = withNativeInputsStamp(config, async (mod) => {
|
|
227
|
+
const build = createHarmonyBuildDescriptor(harmony, mod._internal?.harmonySigningConfig?.name ?? null);
|
|
228
|
+
const relative = path.posix.relative(build.harmonyRoot, build.projectFiles.nativeInputsStamp);
|
|
229
|
+
const source = await readTemplateSource(relative);
|
|
230
|
+
|
|
231
|
+
mod.modResults = render.renderCanonical(source, build.projectFiles.nativeInputsStamp);
|
|
232
|
+
|
|
233
|
+
return mod;
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
config = withHvigorConfig(config, (mod) => {
|
|
237
|
+
const root = mod.modRequest.projectRoot;
|
|
238
|
+
const build = createHarmonyBuildDescriptor(harmony, mod._internal?.harmonySigningConfig?.name ?? null);
|
|
239
|
+
const directory = path.dirname(resolveHarmonyBuildPath(root, build.projectFiles.hvigorConfig));
|
|
240
|
+
const plugin = toRelativeDependency(directory, resolveRnohHvigorPlugin(root));
|
|
241
|
+
|
|
242
|
+
mod.modResults = {
|
|
243
|
+
...mod.modResults,
|
|
244
|
+
dependencies: {
|
|
245
|
+
...readRecord(mod.modResults.dependencies),
|
|
246
|
+
'@rnoh/hvigor-plugin': plugin,
|
|
247
|
+
},
|
|
248
|
+
execution: mod.modResults.execution || {},
|
|
249
|
+
logging: mod.modResults.logging || {},
|
|
250
|
+
debugging: mod.modResults.debugging || {},
|
|
251
|
+
nodeOptions: mod.modResults.nodeOptions || {},
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
return mod;
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
return config;
|
|
258
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HarmonyPaths, withArkTSPackageProvider, withCMakeLists, withCppPackageProvider,
|
|
3
|
+
withEntryAbility, withIndexPage, withWorker,
|
|
4
|
+
} from '@expo-harmony/config-plugins';
|
|
5
|
+
|
|
6
|
+
import { readTemplateSource } from '../dependencies';
|
|
7
|
+
import * as render from '../renderers';
|
|
8
|
+
|
|
9
|
+
const SourceMods = [
|
|
10
|
+
[withEntryAbility, HarmonyPaths.HARMONY_PATHS.entryAbility, render.renderEntryAbility],
|
|
11
|
+
[withIndexPage, HarmonyPaths.HARMONY_PATHS.indexPage, render.renderIndexPage],
|
|
12
|
+
[withWorker, HarmonyPaths.HARMONY_PATHS.worker],
|
|
13
|
+
[withArkTSPackageProvider, HarmonyPaths.HARMONY_PATHS.arktsPackageProvider, render.renderArktsPackageProvider],
|
|
14
|
+
[withCppPackageProvider, HarmonyPaths.HARMONY_PATHS.cppPackageProvider, render.renderCppPackageProvider],
|
|
15
|
+
[withCMakeLists, HarmonyPaths.HARMONY_PATHS.cmakeLists, render.renderCmakeLists],
|
|
16
|
+
] as const;
|
|
17
|
+
|
|
18
|
+
export function withSourceMods(config, harmony) {
|
|
19
|
+
for (const [plugin, relative, renderer] of SourceMods) {
|
|
20
|
+
config = plugin(config, async (mod) => {
|
|
21
|
+
const source = await readTemplateSource(relative);
|
|
22
|
+
|
|
23
|
+
mod.modResults = renderer
|
|
24
|
+
? renderer(source, harmony)
|
|
25
|
+
: render.renderCanonical(source, `harmony/${relative}`);
|
|
26
|
+
|
|
27
|
+
return mod;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return config;
|
|
32
|
+
}
|