@expo-harmony/config-plugins 55.0.10-harmony.2 → 55.0.10-harmony.4
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 +203 -11
- package/build/compiler.d.ts +7 -0
- package/build/compiler.js +12 -0
- package/build/composition.d.ts +24 -0
- package/build/composition.js +11 -0
- package/build/config.d.ts +2 -15
- package/build/customFiles.d.ts +15 -0
- package/build/customFiles.js +108 -0
- package/build/files.d.ts +1 -1
- package/build/files.js +2 -2
- package/build/generatedFiles.d.ts +15 -0
- package/build/generatedFiles.js +70 -0
- package/build/index.d.ts +20 -5
- package/build/index.js +53 -6
- package/build/internal.d.ts +3 -0
- package/build/internal.js +13 -0
- package/build/manifest.d.ts +38 -0
- package/build/manifest.js +71 -0
- package/build/mods.d.ts +37 -31
- package/build/mods.js +118 -86
- package/build/normalizeConfig.d.ts +10 -3
- package/build/normalizeConfig.js +45 -47
- package/build/ownership.d.ts +4 -3
- package/build/ownership.js +17 -0
- package/build/paths.d.ts +13 -10
- package/build/paths.js +32 -31
- package/build/permissions.d.ts +12 -0
- package/build/permissions.js +60 -0
- package/build/pluginTypes.d.ts +15 -0
- package/build/pluginTypes.js +2 -0
- package/build/resources.d.ts +13 -0
- package/build/resources.js +55 -0
- package/package.json +9 -9
- package/src/compiler.ts +21 -0
- package/src/composition.ts +45 -0
- package/src/config.ts +3 -19
- package/src/customFiles.ts +142 -0
- package/src/files.ts +3 -4
- package/src/generatedFiles.ts +85 -0
- package/src/index.ts +20 -11
- package/src/internal.ts +3 -0
- package/src/manifest.ts +151 -0
- package/src/mods.ts +180 -128
- package/src/nativeInputs.ts +0 -1
- package/src/normalizeConfig.ts +56 -59
- package/src/ownership.ts +25 -5
- package/src/paths.ts +41 -45
- package/src/permissions.ts +68 -0
- package/src/pluginTypes.ts +24 -0
- package/src/resources.ts +65 -0
package/build/mods.js
CHANGED
|
@@ -3,10 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.withHarmonyResources = exports.withHarmonyDangerousMod = exports.withCngManifest = exports.withHarmonyAutolinking = exports.withCMakeLists = exports.withCppPackageProvider = exports.withArkTSPackageProvider = exports.withWorker = exports.withIndexPage = exports.withEntryAbility = exports.withProfiles = exports.withMedia = exports.withColors = exports.withStrings = exports.withModuleJson = exports.withEntryHvigor = exports.withEntryOhPackage = exports.withEntryBuildProfile = exports.withHvigorConfig = exports.withNativeInputsStamp = exports.withRootHvigor = exports.withRootOhPackage = exports.withProjectBuildProfile = exports.withAppJson = exports.withReactNativeConfig = exports.HarmonyModNames = void 0;
|
|
6
|
+
exports.withHarmonyResources = exports.withHarmonyDangerousMod = exports.withProjectPatch = exports.withPreparation = exports.withCngManifest = exports.withHarmonyAutolinking = exports.withCMakeLists = exports.withCppPackageProvider = exports.withArkTSPackageProvider = exports.withWorker = exports.withIndexPage = exports.withEntryAbility = exports.withAbilityStage = exports.withProfiles = exports.withMedia = exports.withColors = exports.withStrings = exports.withModuleJson = exports.withEntryHvigor = exports.withEntryOhPackage = exports.withEntryBuildProfile = exports.withHvigorConfig = exports.withNativeInputsStamp = exports.withRootHvigor = exports.withRootOhPackage = exports.withProjectBuildProfile = exports.withAppJson = exports.withReactNativeConfig = exports.HarmonyModNames = void 0;
|
|
7
7
|
exports.recordManagedFile = recordManagedFile;
|
|
8
8
|
exports.withHarmonyBaseMods = withHarmonyBaseMods;
|
|
9
9
|
exports.withHarmonyMod = withHarmonyMod;
|
|
10
|
+
exports.registerMod = registerMod;
|
|
10
11
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
12
13
|
const config_plugins_1 = require("@expo/config-plugins");
|
|
@@ -28,6 +29,7 @@ const TextMods = new Set([
|
|
|
28
29
|
'rootHvigor',
|
|
29
30
|
'nativeInputsStamp',
|
|
30
31
|
'entryHvigor',
|
|
32
|
+
'abilityStage',
|
|
31
33
|
'entryAbility',
|
|
32
34
|
'indexPage',
|
|
33
35
|
'worker',
|
|
@@ -36,10 +38,11 @@ const TextMods = new Set([
|
|
|
36
38
|
'cmakeLists',
|
|
37
39
|
]);
|
|
38
40
|
const ResourceMods = new Set(['strings', 'colors', 'media']);
|
|
39
|
-
const VirtualMods = new Set(['dangerous', 'autolinking', 'manifest']);
|
|
41
|
+
const VirtualMods = new Set(['dangerous', 'autolinking', 'manifest', 'prepare', 'patch']);
|
|
40
42
|
const ResourceModNames = ['strings', 'colors', 'media'];
|
|
41
|
-
|
|
43
|
+
const AllModNames = Object.freeze([
|
|
42
44
|
'dangerous',
|
|
45
|
+
'prepare',
|
|
43
46
|
'reactNativeConfig',
|
|
44
47
|
'appJson',
|
|
45
48
|
'projectBuildProfile',
|
|
@@ -55,6 +58,7 @@ exports.HarmonyModNames = Object.freeze([
|
|
|
55
58
|
'colors',
|
|
56
59
|
'media',
|
|
57
60
|
'profiles',
|
|
61
|
+
'abilityStage',
|
|
58
62
|
'entryAbility',
|
|
59
63
|
'indexPage',
|
|
60
64
|
'worker',
|
|
@@ -62,8 +66,10 @@ exports.HarmonyModNames = Object.freeze([
|
|
|
62
66
|
'cppPackageProvider',
|
|
63
67
|
'cmakeLists',
|
|
64
68
|
'autolinking',
|
|
69
|
+
'patch',
|
|
65
70
|
'manifest',
|
|
66
71
|
]);
|
|
72
|
+
exports.HarmonyModNames = Object.freeze(AllModNames.filter((name) => name !== 'manifest' && name !== 'nativeInputsStamp' && name !== 'prepare' && name !== 'patch'));
|
|
67
73
|
function recordManagedFile(config, file, owner) {
|
|
68
74
|
config._internal ??= {};
|
|
69
75
|
config._internal.harmonyManagedFiles ??= [];
|
|
@@ -73,35 +79,41 @@ function recordManagedFile(config, file, owner) {
|
|
|
73
79
|
files.sort((left, right) => left.path.localeCompare(right.path, 'en'));
|
|
74
80
|
config._internal.harmonyManagedFiles = files;
|
|
75
81
|
}
|
|
76
|
-
function assertModResults(
|
|
82
|
+
function assertModResults(name, value, kind) {
|
|
77
83
|
const valid = kind === 'text'
|
|
78
84
|
? typeof value === 'string'
|
|
79
85
|
: value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
80
86
|
if (!valid) {
|
|
81
|
-
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_MOD_RESULTS_INVALID', `harmony.${
|
|
87
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_MOD_RESULTS_INVALID', `harmony.${name} must return ${kind === 'text' ? 'a string' : 'an object'} modResults.`, { operation: `harmony.${name}.write` });
|
|
82
88
|
}
|
|
83
89
|
}
|
|
84
|
-
function withFileProvider(config,
|
|
90
|
+
function withFileProvider(config, name) {
|
|
85
91
|
return (0, config_plugins_1.withBaseMod)(config, {
|
|
86
92
|
platform: 'harmony',
|
|
87
|
-
mod:
|
|
93
|
+
mod: name,
|
|
88
94
|
isProvider: true,
|
|
89
|
-
isIntrospective: JsonMods.has(
|
|
90
|
-
saveToInternal: JsonMods.has(
|
|
95
|
+
isIntrospective: JsonMods.has(name),
|
|
96
|
+
saveToInternal: JsonMods.has(name),
|
|
91
97
|
async action(value) {
|
|
92
|
-
const { nextMod, ...
|
|
93
|
-
const
|
|
94
|
-
const root =
|
|
95
|
-
const file =
|
|
96
|
-
? await (0, paths_1.resolveProjectPath)(root,
|
|
97
|
-
: await (0, paths_1.resolveHarmonyPath)(root, paths_1.ManagedPaths[
|
|
98
|
-
const exists = node_fs_1.default.existsSync(file);
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
98
|
+
const { nextMod: next, ...request } = value.modRequest;
|
|
99
|
+
const project = name in paths_1.ProjectPaths;
|
|
100
|
+
const root = project ? request.projectRoot : request.platformProjectRoot;
|
|
101
|
+
const file = project
|
|
102
|
+
? await (0, paths_1.resolveProjectPath)(root, name)
|
|
103
|
+
: await (0, paths_1.resolveHarmonyPath)(root, paths_1.ManagedPaths[name]);
|
|
104
|
+
const exists = !request.ignoreExistingNativeFiles && node_fs_1.default.existsSync(file);
|
|
105
|
+
const template = value._internal?.harmonyTemplateDirectory;
|
|
106
|
+
const input = !exists && !project && typeof template === 'string'
|
|
107
|
+
? await (0, paths_1.resolveHarmonyPath)(template, paths_1.ManagedPaths[name])
|
|
108
|
+
: file;
|
|
109
|
+
const data = request.ignoreExistingNativeFiles && input === file
|
|
110
|
+
? (JsonMods.has(name) ? {} : '')
|
|
111
|
+
: JsonMods.has(name)
|
|
112
|
+
? await (0, files_1.readJson5)(input, {}, name)
|
|
113
|
+
: await (0, files_1.readText)(file);
|
|
114
|
+
const context = { ...request, modFile: file, modFileExists: exists };
|
|
115
|
+
const result = await next({ ...value, modRequest: context, modResults: data });
|
|
116
|
+
assertModResults(name, result.modResults, JsonMods.has(name) ? 'json' : 'text');
|
|
105
117
|
if (!result.modRequest.introspect) {
|
|
106
118
|
if (typeof result.modResults === 'string') {
|
|
107
119
|
await (0, files_1.atomicWrite)(file, result.modResults.replace(/\r\n?/g, '\n').replace(/\n?$/, '\n'));
|
|
@@ -109,17 +121,19 @@ function withFileProvider(config, modName) {
|
|
|
109
121
|
else {
|
|
110
122
|
await (0, files_1.writeJson5)(file, result.modResults);
|
|
111
123
|
}
|
|
112
|
-
recordManagedFile(result, file,
|
|
124
|
+
recordManagedFile(result, file, name);
|
|
113
125
|
}
|
|
114
126
|
return result;
|
|
115
127
|
},
|
|
116
128
|
});
|
|
117
129
|
}
|
|
118
|
-
async function readResourceMap(root, entries,
|
|
130
|
+
async function readResourceMap(root, entries, name, template, ignore = false) {
|
|
119
131
|
const resources = {};
|
|
120
132
|
for (const [scope, relative] of Object.entries(entries)) {
|
|
121
133
|
const file = await (0, paths_1.resolveHarmonyPath)(root, relative);
|
|
122
|
-
|
|
134
|
+
const fallback = ignore || !node_fs_1.default.existsSync(file);
|
|
135
|
+
const input = fallback && template ? await (0, paths_1.resolveHarmonyPath)(template, relative) : file;
|
|
136
|
+
resources[scope] = ignore && input === file ? {} : await (0, files_1.readJson5)(input, {}, name);
|
|
123
137
|
}
|
|
124
138
|
return resources;
|
|
125
139
|
}
|
|
@@ -162,16 +176,14 @@ async function writeMediaMap(root, media, config, previous = {}) {
|
|
|
162
176
|
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `Harmony media ${scope}/${name} must use a descriptor object.`, { operation: 'harmony.media.write' });
|
|
163
177
|
}
|
|
164
178
|
const item = descriptor;
|
|
165
|
-
|
|
166
|
-
const hasContent = Object.hasOwn(item, 'content');
|
|
167
|
-
if (hasSource === hasContent) {
|
|
179
|
+
if (Object.hasOwn(item, 'source') === Object.hasOwn(item, 'content')) {
|
|
168
180
|
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `Harmony media ${scope}/${name} must define exactly one of source or content.`, { operation: 'harmony.media.write' });
|
|
169
181
|
}
|
|
170
182
|
if (item.replaceBase !== undefined && typeof item.replaceBase !== 'boolean') {
|
|
171
183
|
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `Harmony media ${scope}/${name} replaceBase must be a boolean.`, { operation: 'harmony.media.write' });
|
|
172
184
|
}
|
|
173
185
|
let content;
|
|
174
|
-
if (
|
|
186
|
+
if (Object.hasOwn(item, 'source')) {
|
|
175
187
|
if (typeof item.source !== 'string' || item.source.length === 0) {
|
|
176
188
|
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `Harmony media ${scope}/${name} source must be a non-empty path.`, { operation: 'harmony.media.write' });
|
|
177
189
|
}
|
|
@@ -191,9 +203,6 @@ async function writeMediaMap(root, media, config, previous = {}) {
|
|
|
191
203
|
});
|
|
192
204
|
}
|
|
193
205
|
}
|
|
194
|
-
// Resolve and read every desired input before removing stale resources. A
|
|
195
|
-
// malformed descriptor or missing source therefore cannot partially mutate
|
|
196
|
-
// an otherwise valid generated project.
|
|
197
206
|
for (const [scope, names] of Object.entries(previous)) {
|
|
198
207
|
if (!(scope in paths_1.ResourcePaths.media))
|
|
199
208
|
continue;
|
|
@@ -210,9 +219,9 @@ async function writeMediaMap(root, media, config, previous = {}) {
|
|
|
210
219
|
await node_fs_1.default.promises.mkdir(directory, { recursive: true });
|
|
211
220
|
if (replaceBase) {
|
|
212
221
|
const base = node_path_1.default.parse(name).name;
|
|
213
|
-
for (const
|
|
214
|
-
if (node_path_1.default.parse(
|
|
215
|
-
await node_fs_1.default.promises.rm(node_path_1.default.join(directory,
|
|
222
|
+
for (const entry of await node_fs_1.default.promises.readdir(directory)) {
|
|
223
|
+
if (node_path_1.default.parse(entry).name === base && entry !== name) {
|
|
224
|
+
await node_fs_1.default.promises.rm(node_path_1.default.join(directory, entry), { force: true });
|
|
216
225
|
}
|
|
217
226
|
}
|
|
218
227
|
}
|
|
@@ -221,47 +230,47 @@ async function writeMediaMap(root, media, config, previous = {}) {
|
|
|
221
230
|
recordManagedFile(config, file, 'media');
|
|
222
231
|
}
|
|
223
232
|
}
|
|
224
|
-
function withResourceProvider(config,
|
|
225
|
-
const
|
|
233
|
+
function withResourceProvider(config, name) {
|
|
234
|
+
const introspective = name !== 'media';
|
|
226
235
|
return (0, config_plugins_1.withBaseMod)(config, {
|
|
227
236
|
platform: 'harmony',
|
|
228
|
-
mod:
|
|
237
|
+
mod: name,
|
|
229
238
|
isProvider: true,
|
|
230
|
-
isIntrospective,
|
|
231
|
-
saveToInternal:
|
|
239
|
+
isIntrospective: introspective,
|
|
240
|
+
saveToInternal: introspective,
|
|
232
241
|
async action(value) {
|
|
233
|
-
const { nextMod, ...
|
|
234
|
-
const root =
|
|
235
|
-
const data =
|
|
236
|
-
?
|
|
237
|
-
|
|
238
|
-
|
|
242
|
+
const { nextMod: next, ...request } = value.modRequest;
|
|
243
|
+
const root = request.platformProjectRoot;
|
|
244
|
+
const data = name === 'media'
|
|
245
|
+
? (request.ignoreExistingNativeFiles
|
|
246
|
+
? Object.fromEntries(Object.keys(paths_1.ResourcePaths.media).map(scope => [scope, {}]))
|
|
247
|
+
: await readMediaMap(root))
|
|
248
|
+
: await readResourceMap(root, paths_1.ResourcePaths[name], name, value._internal?.harmonyTemplateDirectory, request.ignoreExistingNativeFiles);
|
|
249
|
+
const previous = name === 'media'
|
|
239
250
|
? Object.fromEntries(Object.entries(data).map(([scope, files]) => [scope, Object.keys(files)]))
|
|
240
251
|
: {};
|
|
241
|
-
const result = await
|
|
242
|
-
assertModResults(
|
|
252
|
+
const result = await next({ ...value, modRequest: request, modResults: data });
|
|
253
|
+
assertModResults(name, result.modResults, 'resource');
|
|
243
254
|
if (!result.modRequest.introspect) {
|
|
244
|
-
if (
|
|
255
|
+
if (name === 'media') {
|
|
245
256
|
await writeMediaMap(root, result.modResults, result, previous);
|
|
246
257
|
}
|
|
247
258
|
else {
|
|
248
|
-
const paths = paths_1.ResourcePaths[
|
|
259
|
+
const paths = paths_1.ResourcePaths[name];
|
|
249
260
|
for (const [scope, resource] of Object.entries(result.modResults)) {
|
|
250
261
|
if (!(scope in paths))
|
|
251
262
|
continue;
|
|
252
|
-
// Harmony's resource compiler requires the JSON root to contain
|
|
253
|
-
// exactly one resource-kind member. An empty scope serialized as
|
|
254
|
-
// `{}` is invalid, so omit it until a plugin contributes content.
|
|
255
263
|
if (!resource || typeof resource !== 'object' || Array.isArray(resource)) {
|
|
256
|
-
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `Harmony ${
|
|
264
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `Harmony ${name} scope ${scope} must be an object.`, { operation: `harmony.${name}.write` });
|
|
257
265
|
}
|
|
258
266
|
const file = await (0, paths_1.resolveHarmonyPath)(root, paths[scope]);
|
|
267
|
+
// Harmony's resource compiler rejects an empty JSON root.
|
|
259
268
|
if (Object.keys(resource).length === 0) {
|
|
260
269
|
await node_fs_1.default.promises.rm(file, { force: true });
|
|
261
270
|
continue;
|
|
262
271
|
}
|
|
263
272
|
await (0, files_1.writeJson5)(file, resource);
|
|
264
|
-
recordManagedFile(result, file,
|
|
273
|
+
recordManagedFile(result, file, name);
|
|
265
274
|
}
|
|
266
275
|
}
|
|
267
276
|
}
|
|
@@ -269,61 +278,63 @@ function withResourceProvider(config, modName) {
|
|
|
269
278
|
},
|
|
270
279
|
});
|
|
271
280
|
}
|
|
272
|
-
function withVirtualProvider(config,
|
|
281
|
+
function withVirtualProvider(config, name) {
|
|
273
282
|
return (0, config_plugins_1.withBaseMod)(config, {
|
|
274
283
|
platform: 'harmony',
|
|
275
|
-
mod:
|
|
284
|
+
mod: name,
|
|
276
285
|
isProvider: true,
|
|
286
|
+
isIntrospective: name === 'prepare',
|
|
277
287
|
async action(value) {
|
|
278
|
-
const { nextMod, ...
|
|
279
|
-
return
|
|
288
|
+
const { nextMod: next, ...request } = value.modRequest;
|
|
289
|
+
return next({ ...value, modRequest: request, modResults: null });
|
|
280
290
|
},
|
|
281
291
|
});
|
|
282
292
|
}
|
|
283
|
-
function withProvider(config,
|
|
293
|
+
function withProvider(config, name) {
|
|
284
294
|
const actions = [];
|
|
285
295
|
config = (0, config_plugins_1.withMod)(config, {
|
|
286
296
|
platform: 'harmony',
|
|
287
|
-
mod:
|
|
297
|
+
mod: name,
|
|
288
298
|
async action(value) {
|
|
289
299
|
let result = value;
|
|
290
|
-
//
|
|
291
|
-
// that ordering for Harmony plugins registered after the provider.
|
|
300
|
+
// Late mods must follow Expo's last-registered-first order.
|
|
292
301
|
for (let index = actions.length - 1; index >= 0; index -= 1) {
|
|
293
302
|
result = await actions[index](result);
|
|
294
303
|
}
|
|
295
304
|
return result;
|
|
296
305
|
},
|
|
297
306
|
});
|
|
298
|
-
if (JsonMods.has(
|
|
299
|
-
config = withFileProvider(config,
|
|
307
|
+
if (JsonMods.has(name) || TextMods.has(name)) {
|
|
308
|
+
config = withFileProvider(config, name);
|
|
300
309
|
}
|
|
301
|
-
else if (ResourceMods.has(
|
|
302
|
-
config = withResourceProvider(config,
|
|
310
|
+
else if (ResourceMods.has(name)) {
|
|
311
|
+
config = withResourceProvider(config, name);
|
|
303
312
|
}
|
|
304
|
-
else if (VirtualMods.has(
|
|
305
|
-
config = withVirtualProvider(config,
|
|
313
|
+
else if (VirtualMods.has(name)) {
|
|
314
|
+
config = withVirtualProvider(config, name);
|
|
306
315
|
}
|
|
307
316
|
else {
|
|
308
|
-
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_MOD_NOT_REGISTERED', `Unknown Harmony mod: ${
|
|
317
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_MOD_NOT_REGISTERED', `Unknown Harmony mod: ${name}`, { operation: 'register-base-mods' });
|
|
309
318
|
}
|
|
310
319
|
const mods = config.mods;
|
|
311
|
-
const provider = mods?.harmony?.[
|
|
320
|
+
const provider = mods?.harmony?.[name];
|
|
312
321
|
if (provider?.isProvider)
|
|
313
322
|
provider.expoHarmonyLateActions = actions;
|
|
314
323
|
return config;
|
|
315
324
|
}
|
|
316
325
|
function withHarmonyBaseMods(config) {
|
|
317
|
-
for (const
|
|
326
|
+
for (const name of AllModNames) {
|
|
318
327
|
const mods = config.mods;
|
|
319
|
-
if (!mods?.harmony?.[
|
|
320
|
-
config = withProvider(config,
|
|
328
|
+
if (!mods?.harmony?.[name]?.isProvider)
|
|
329
|
+
config = withProvider(config, name);
|
|
321
330
|
}
|
|
322
331
|
const mods = config.mods;
|
|
323
332
|
const table = mods.harmony;
|
|
324
333
|
mods.harmony = Object.fromEntries([
|
|
325
|
-
...
|
|
326
|
-
...Object.entries(table).filter(([name]) => !
|
|
334
|
+
...AllModNames.filter(name => table[name] && name !== 'manifest' && name !== 'patch').map(name => [name, table[name]]),
|
|
335
|
+
...Object.entries(table).filter(([name]) => !AllModNames.includes(name)),
|
|
336
|
+
['patch', table.patch],
|
|
337
|
+
['manifest', table.manifest],
|
|
327
338
|
]);
|
|
328
339
|
return config;
|
|
329
340
|
}
|
|
@@ -331,17 +342,32 @@ function withHarmonyMod(config, tuple) {
|
|
|
331
342
|
if (!Array.isArray(tuple) || tuple.length !== 2) {
|
|
332
343
|
throw new TypeError('withHarmonyMod expects [modName, action].');
|
|
333
344
|
}
|
|
334
|
-
const [
|
|
335
|
-
if (!exports.HarmonyModNames.includes(
|
|
336
|
-
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_MOD_NOT_REGISTERED', `Cannot register Harmony mod: ${
|
|
345
|
+
const [name, action] = tuple;
|
|
346
|
+
if (!exports.HarmonyModNames.includes(name) || typeof action !== 'function') {
|
|
347
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_MOD_NOT_REGISTERED', `Cannot register Harmony mod: ${name}`, { operation: 'register-mod' });
|
|
337
348
|
}
|
|
349
|
+
return registerMod(config, name, action);
|
|
350
|
+
}
|
|
351
|
+
function registerMod(config, name, action) {
|
|
338
352
|
const mods = config.mods;
|
|
339
|
-
const provider = mods?.harmony?.[
|
|
353
|
+
const provider = mods?.harmony?.[name];
|
|
340
354
|
if (provider?.isProvider && provider.expoHarmonyLateActions) {
|
|
341
|
-
|
|
355
|
+
// Build an ordinary Expo interceptor on an isolated mod table, preserving
|
|
356
|
+
// its registration stack, error behavior and execution trace for late mods.
|
|
357
|
+
const carrier = (0, config_plugins_1.withMod)({ ...config, mods: {} }, {
|
|
358
|
+
platform: 'harmony',
|
|
359
|
+
mod: name,
|
|
360
|
+
action: action,
|
|
361
|
+
});
|
|
362
|
+
const interceptor = carrier.mods.harmony[name];
|
|
363
|
+
provider.expoHarmonyLateActions.push(interceptor);
|
|
342
364
|
return config;
|
|
343
365
|
}
|
|
344
|
-
return (0, config_plugins_1.withMod)(config, {
|
|
366
|
+
return (0, config_plugins_1.withMod)(config, {
|
|
367
|
+
platform: 'harmony',
|
|
368
|
+
mod: name,
|
|
369
|
+
action: action,
|
|
370
|
+
});
|
|
345
371
|
}
|
|
346
372
|
const withReactNativeConfig = (config, action) => withHarmonyMod(config, ['reactNativeConfig', action]);
|
|
347
373
|
exports.withReactNativeConfig = withReactNativeConfig;
|
|
@@ -353,7 +379,7 @@ const withRootOhPackage = (config, action) => withHarmonyMod(config, ['rootOhPac
|
|
|
353
379
|
exports.withRootOhPackage = withRootOhPackage;
|
|
354
380
|
const withRootHvigor = (config, action) => withHarmonyMod(config, ['rootHvigor', action]);
|
|
355
381
|
exports.withRootHvigor = withRootHvigor;
|
|
356
|
-
const withNativeInputsStamp = (config, action) =>
|
|
382
|
+
const withNativeInputsStamp = (config, action) => registerMod(config, 'nativeInputsStamp', action);
|
|
357
383
|
exports.withNativeInputsStamp = withNativeInputsStamp;
|
|
358
384
|
const withHvigorConfig = (config, action) => withHarmonyMod(config, ['hvigorConfig', action]);
|
|
359
385
|
exports.withHvigorConfig = withHvigorConfig;
|
|
@@ -373,6 +399,8 @@ const withMedia = (config, action) => withHarmonyMod(config, ['media', action]);
|
|
|
373
399
|
exports.withMedia = withMedia;
|
|
374
400
|
const withProfiles = (config, action) => withHarmonyMod(config, ['profiles', action]);
|
|
375
401
|
exports.withProfiles = withProfiles;
|
|
402
|
+
const withAbilityStage = (config, action) => withHarmonyMod(config, ['abilityStage', action]);
|
|
403
|
+
exports.withAbilityStage = withAbilityStage;
|
|
376
404
|
const withEntryAbility = (config, action) => withHarmonyMod(config, ['entryAbility', action]);
|
|
377
405
|
exports.withEntryAbility = withEntryAbility;
|
|
378
406
|
const withIndexPage = (config, action) => withHarmonyMod(config, ['indexPage', action]);
|
|
@@ -387,13 +415,17 @@ const withCMakeLists = (config, action) => withHarmonyMod(config, ['cmakeLists',
|
|
|
387
415
|
exports.withCMakeLists = withCMakeLists;
|
|
388
416
|
const withHarmonyAutolinking = (config, action) => withHarmonyMod(config, ['autolinking', action]);
|
|
389
417
|
exports.withHarmonyAutolinking = withHarmonyAutolinking;
|
|
390
|
-
const withCngManifest = (config, action) =>
|
|
418
|
+
const withCngManifest = (config, action) => registerMod(config, 'manifest', action);
|
|
391
419
|
exports.withCngManifest = withCngManifest;
|
|
420
|
+
const withPreparation = (config, action) => registerMod(config, 'prepare', action);
|
|
421
|
+
exports.withPreparation = withPreparation;
|
|
422
|
+
const withProjectPatch = (config, action) => registerMod(config, 'patch', action);
|
|
423
|
+
exports.withProjectPatch = withProjectPatch;
|
|
392
424
|
const withHarmonyDangerousMod = (config, action) => withHarmonyMod(config, ['dangerous', action]);
|
|
393
425
|
exports.withHarmonyDangerousMod = withHarmonyDangerousMod;
|
|
394
426
|
const withHarmonyResources = (config, action) => {
|
|
395
|
-
for (const
|
|
396
|
-
withHarmonyMod(config, [
|
|
427
|
+
for (const name of ResourceModNames) {
|
|
428
|
+
withHarmonyMod(config, [name, action]);
|
|
397
429
|
}
|
|
398
430
|
return config;
|
|
399
431
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExpoConfigWithHarmony, HarmonyConfig } from './config';
|
|
1
|
+
import type { ExpoConfigWithHarmony, HarmonyConfig, HarmonyPermission } from './config';
|
|
2
2
|
type HarmonyExpoConfig = ExpoConfigWithHarmony;
|
|
3
3
|
interface NormalizedHarmonyConfig {
|
|
4
4
|
abiFilters: string[];
|
|
@@ -22,7 +22,14 @@ interface NormalizedHarmonyConfig {
|
|
|
22
22
|
versionCode: number;
|
|
23
23
|
versionName: string;
|
|
24
24
|
}
|
|
25
|
-
declare
|
|
25
|
+
export declare function normalizePermission(permission: HarmonyPermission): {
|
|
26
|
+
name: string;
|
|
27
|
+
reason?: string;
|
|
28
|
+
usedScene?: {
|
|
29
|
+
abilities?: string[];
|
|
30
|
+
when: "always" | "inuse";
|
|
31
|
+
};
|
|
32
|
+
};
|
|
26
33
|
declare function normalizeHarmonyConfig(config: HarmonyExpoConfig): NormalizedHarmonyConfig;
|
|
27
|
-
export {
|
|
34
|
+
export { normalizeHarmonyConfig };
|
|
28
35
|
export type { HarmonyExpoConfig, NormalizedHarmonyConfig };
|
package/build/normalizeConfig.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.normalizePermission = normalizePermission;
|
|
4
4
|
exports.normalizeHarmonyConfig = normalizeHarmonyConfig;
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
|
-
const
|
|
6
|
+
const resources_1 = require("./resources");
|
|
7
|
+
const BundlePattern = /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*){2,}$/;
|
|
7
8
|
const IdentifierPattern = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
8
9
|
const ColorPattern = /^(#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{8})$/;
|
|
9
|
-
const
|
|
10
|
+
const Orientations = new Set([
|
|
10
11
|
'default',
|
|
11
12
|
'portrait',
|
|
12
13
|
'landscape',
|
|
@@ -14,17 +15,17 @@ const ValidOrientations = new Set([
|
|
|
14
15
|
'landscape_inverted',
|
|
15
16
|
'auto_rotation',
|
|
16
17
|
]);
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const HarmonySdkVersions = new Map([
|
|
18
|
+
const DeviceTypes = new Set(['phone', 'tablet', '2in1']);
|
|
19
|
+
const MinimumApi = 20;
|
|
20
|
+
const TargetApi = 24;
|
|
21
|
+
const SdkVersions = new Map([
|
|
22
22
|
[13, '5.0.1(13)'],
|
|
23
|
+
[14, '5.0.2(14)'],
|
|
23
24
|
[20, '6.0.0(20)'],
|
|
25
|
+
[21, '6.0.1(21)'],
|
|
24
26
|
[23, '6.1.0(23)'],
|
|
25
27
|
[24, '6.1.1(24)'],
|
|
26
28
|
]);
|
|
27
|
-
exports.HarmonySdkVersions = HarmonySdkVersions;
|
|
28
29
|
class HarmonyConfigError extends errors_1.HarmonyConfigPluginError {
|
|
29
30
|
constructor(message) {
|
|
30
31
|
super('ERR_HARMONY_CONFIG_INVALID', message, { operation: 'normalize-config' });
|
|
@@ -53,10 +54,7 @@ function normalizeColor(value, field, fallback) {
|
|
|
53
54
|
if (typeof color !== 'string' || !ColorPattern.test(color)) {
|
|
54
55
|
throw new HarmonyConfigError(`${field} must be #RRGGBB or #RRGGBBAA.`);
|
|
55
56
|
}
|
|
56
|
-
|
|
57
|
-
return result.length === 9
|
|
58
|
-
? `#${result.slice(7, 9)}${result.slice(1, 7)}`
|
|
59
|
-
: result;
|
|
57
|
+
return (0, resources_1.toArgb)(color);
|
|
60
58
|
}
|
|
61
59
|
function parseSdkApi(value, field) {
|
|
62
60
|
if (value === undefined)
|
|
@@ -82,14 +80,14 @@ function resolveSdkVersion(api, field, value) {
|
|
|
82
80
|
}
|
|
83
81
|
else {
|
|
84
82
|
const version = readString(value, `harmony.${field}`);
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
throw new HarmonyConfigError(`harmony.${field} describes API ${
|
|
83
|
+
const parsed = parseSdkApi(version, field);
|
|
84
|
+
if (parsed !== api) {
|
|
85
|
+
throw new HarmonyConfigError(`harmony.${field} describes API ${parsed}, but the configured API level is ${api}.`);
|
|
88
86
|
}
|
|
89
87
|
return version;
|
|
90
88
|
}
|
|
91
89
|
}
|
|
92
|
-
const version =
|
|
90
|
+
const version = SdkVersions.get(api);
|
|
93
91
|
if (!version) {
|
|
94
92
|
throw new HarmonyConfigError(`Harmony API ${api} has no built-in SDK label. Set harmony.${field} explicitly so new SDK releases do not require a package update.`);
|
|
95
93
|
}
|
|
@@ -157,7 +155,9 @@ function normalizeSkill(skill) {
|
|
|
157
155
|
}
|
|
158
156
|
const result = {};
|
|
159
157
|
for (const [key, value] of Object.entries(uri)) {
|
|
160
|
-
result[key] =
|
|
158
|
+
result[key] = typeof value === 'boolean' || (typeof value === 'number' && Number.isFinite(value))
|
|
159
|
+
? value
|
|
160
|
+
: readString(value, `harmony.skills[].uris[].${key}`);
|
|
161
161
|
}
|
|
162
162
|
return result;
|
|
163
163
|
});
|
|
@@ -176,38 +176,32 @@ function normalizeHarmonyConfig(config) {
|
|
|
176
176
|
if (!harmony || typeof harmony !== 'object' || Array.isArray(harmony)) {
|
|
177
177
|
throw new HarmonyConfigError('Expo config must contain a harmony object.');
|
|
178
178
|
}
|
|
179
|
-
const
|
|
180
|
-
if (!
|
|
179
|
+
const bundle = readString(harmony.bundleName, 'harmony.bundleName');
|
|
180
|
+
if (!BundlePattern.test(bundle)) {
|
|
181
181
|
throw new HarmonyConfigError('harmony.bundleName must contain at least three valid dot-separated segments.');
|
|
182
182
|
}
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
if (!IdentifierPattern.test(
|
|
183
|
+
const module = readString(harmony.moduleName, 'harmony.moduleName', 'entry');
|
|
184
|
+
const ability = readString(harmony.abilityName, 'harmony.abilityName', 'EntryAbility');
|
|
185
|
+
if (!IdentifierPattern.test(module)) {
|
|
186
186
|
throw new HarmonyConfigError('harmony.moduleName must be a valid Harmony identifier.');
|
|
187
187
|
}
|
|
188
|
-
if (!IdentifierPattern.test(
|
|
188
|
+
if (!IdentifierPattern.test(ability)) {
|
|
189
189
|
throw new HarmonyConfigError('harmony.abilityName must be a valid Harmony identifier.');
|
|
190
190
|
}
|
|
191
|
-
const
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (compatibleApi < SupportedMinimumHarmonyApi) {
|
|
198
|
-
throw new HarmonyConfigError(`Harmony compatible API must be ${SupportedMinimumHarmonyApi} or newer.`);
|
|
191
|
+
const version = readString(harmony.versionName, 'harmony.versionName', config.version || '1.0.0');
|
|
192
|
+
const code = readPositiveInteger(harmony.versionCode, 'versionCode', 1);
|
|
193
|
+
const target = readPositiveInteger(harmony.targetApiVersion, 'targetApiVersion', parseSdkApi(harmony.targetSdkVersion, 'targetSdkVersion') ?? TargetApi);
|
|
194
|
+
const compatible = readPositiveInteger(parseSdkApi(harmony.compatibleSdkVersion, 'compatibleSdkVersion') ?? undefined, 'compatibleSdkVersion', MinimumApi);
|
|
195
|
+
if (compatible < MinimumApi) {
|
|
196
|
+
throw new HarmonyConfigError(`Harmony compatible API must be ${MinimumApi} or newer.`);
|
|
199
197
|
}
|
|
200
|
-
if (
|
|
198
|
+
if (compatible > target) {
|
|
201
199
|
throw new HarmonyConfigError('Harmony compatibleSdkVersion cannot exceed targetApiVersion.');
|
|
202
200
|
}
|
|
203
201
|
const orientation = harmony.orientation || config.orientation || 'default';
|
|
204
|
-
if (!
|
|
202
|
+
if (!Orientations.has(orientation)) {
|
|
205
203
|
throw new HarmonyConfigError(`Unsupported Harmony orientation: ${orientation}`);
|
|
206
204
|
}
|
|
207
|
-
const style = harmony.userInterfaceStyle || config.userInterfaceStyle || 'light';
|
|
208
|
-
if (!['light', 'dark', 'automatic'].includes(style)) {
|
|
209
|
-
throw new HarmonyConfigError(`Unsupported Harmony UI style: ${style}`);
|
|
210
|
-
}
|
|
211
205
|
const background = normalizeColor(harmony.backgroundColor || config.backgroundColor, 'harmony.backgroundColor', '#FFFFFF');
|
|
212
206
|
const engine = harmony.jsEngine || 'hermes';
|
|
213
207
|
if (engine !== 'hermes') {
|
|
@@ -225,6 +219,10 @@ function normalizeHarmonyConfig(config) {
|
|
|
225
219
|
if (!permissions.some(permission => permission.name === 'ohos.permission.INTERNET')) {
|
|
226
220
|
permissions.unshift({ name: 'ohos.permission.INTERNET' });
|
|
227
221
|
}
|
|
222
|
+
if (config.updates?.checkAutomatically === 'WIFI_ONLY'
|
|
223
|
+
&& !permissions.some(permission => permission.name === 'ohos.permission.GET_NETWORK_INFO')) {
|
|
224
|
+
permissions.push({ name: 'ohos.permission.GET_NETWORK_INFO' });
|
|
225
|
+
}
|
|
228
226
|
const schemes = readExpoSchemes(config);
|
|
229
227
|
let skills = (harmony.skills || []).map(normalizeSkill);
|
|
230
228
|
for (const scheme of schemes) {
|
|
@@ -250,27 +248,27 @@ function normalizeHarmonyConfig(config) {
|
|
|
250
248
|
: readString(harmony.signingConfigFile, 'harmony.signingConfigFile');
|
|
251
249
|
const devices = harmony.deviceTypes === undefined
|
|
252
250
|
? ['phone', 'tablet']
|
|
253
|
-
: normalizeStringArray(harmony.deviceTypes, 'harmony.deviceTypes',
|
|
251
|
+
: normalizeStringArray(harmony.deviceTypes, 'harmony.deviceTypes', DeviceTypes);
|
|
254
252
|
return Object.freeze({
|
|
255
253
|
abiFilters: abis,
|
|
256
|
-
abilityName,
|
|
254
|
+
abilityName: ability,
|
|
257
255
|
backgroundColor: background,
|
|
258
|
-
bundleName,
|
|
259
|
-
compatibleSdkVersionString: resolveSdkVersion(
|
|
256
|
+
bundleName: bundle,
|
|
257
|
+
compatibleSdkVersionString: resolveSdkVersion(compatible, 'compatibleSdkVersion', harmony.compatibleSdkVersion),
|
|
260
258
|
deviceTypes: devices,
|
|
261
259
|
icon: harmony.icon || config.icon,
|
|
262
260
|
label: readString(harmony.label, 'harmony.label', config.name),
|
|
263
|
-
moduleName,
|
|
261
|
+
moduleName: module,
|
|
264
262
|
nativeOrientation: orientation === 'default' ? 'unspecified' : orientation,
|
|
265
263
|
permissions,
|
|
266
264
|
productName: readString(harmony.productName, 'harmony.productName', 'default'),
|
|
267
265
|
querySchemes: queries,
|
|
268
266
|
signingConfigFile: signing,
|
|
269
267
|
skills,
|
|
270
|
-
targetApiVersion:
|
|
271
|
-
targetSdkVersionString: resolveSdkVersion(
|
|
268
|
+
targetApiVersion: target,
|
|
269
|
+
targetSdkVersionString: resolveSdkVersion(target, 'targetSdkVersion', harmony.targetSdkVersion),
|
|
272
270
|
vendor: readString(harmony.vendor, 'harmony.vendor', 'expo-harmony'),
|
|
273
|
-
versionCode,
|
|
274
|
-
versionName,
|
|
271
|
+
versionCode: code,
|
|
272
|
+
versionName: version,
|
|
275
273
|
});
|
|
276
274
|
}
|
package/build/ownership.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ExpoConfigWithHarmony } from './config';
|
|
2
2
|
export interface HarmonyConfigPluginOwnership {
|
|
3
3
|
readonly owner: string;
|
|
4
|
+
readonly files?: readonly string[];
|
|
4
5
|
readonly ability?: Readonly<Partial<{
|
|
5
6
|
startWindowBackground: string;
|
|
6
7
|
startWindowIcon: string;
|
|
@@ -12,5 +13,5 @@ export interface HarmonyConfigPluginOwnership {
|
|
|
12
13
|
}>>;
|
|
13
14
|
}
|
|
14
15
|
export declare function normalizeHarmonyConfigPlugins(value: unknown): readonly HarmonyConfigPluginOwnership[];
|
|
15
|
-
export declare function registerHarmonyConfigPlugin(config:
|
|
16
|
-
export declare function getHarmonyConfigPlugins(config:
|
|
16
|
+
export declare function registerHarmonyConfigPlugin<Config extends ExpoConfigWithHarmony>(config: Config, owner: string, claims?: Omit<HarmonyConfigPluginOwnership, 'owner'>): Config;
|
|
17
|
+
export declare function getHarmonyConfigPlugins(config: ExpoConfigWithHarmony): readonly HarmonyConfigPluginOwnership[];
|