@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/ownership.js
CHANGED
|
@@ -89,7 +89,17 @@ function normalizeHarmonyConfigPlugins(value) {
|
|
|
89
89
|
throw new TypeError(`Invalid Harmony config-plugin ownership: owner ${plugin.owner} is duplicated.`);
|
|
90
90
|
}
|
|
91
91
|
owners.add(plugin.owner);
|
|
92
|
+
let files;
|
|
93
|
+
if (plugin.files !== undefined) {
|
|
94
|
+
if (!Array.isArray(plugin.files) || plugin.files.some(file => typeof file !== 'string'
|
|
95
|
+
|| !file || file.includes('\\') || file.includes('\0') || /^[A-Za-z]:/.test(file)
|
|
96
|
+
|| file.split('/').some(part => !part || part === '.' || part === '..'))) {
|
|
97
|
+
throw new TypeError(`Invalid Harmony config-plugin ownership: ${plugin.owner}.files must contain relative file paths.`);
|
|
98
|
+
}
|
|
99
|
+
files = [...new Set(plugin.files)].sort();
|
|
100
|
+
}
|
|
92
101
|
return {
|
|
102
|
+
...(files === undefined ? {} : { files }),
|
|
93
103
|
ability: normalizeAbility(plugin.ability),
|
|
94
104
|
owner: plugin.owner,
|
|
95
105
|
resources: normalizeResources(plugin.resources),
|
|
@@ -101,6 +111,13 @@ function registerHarmonyConfigPlugin(config, owner, claims = {}) {
|
|
|
101
111
|
const plugin = normalizeHarmonyConfigPlugins([{ ...claims, owner }])[0];
|
|
102
112
|
config._internal ??= {};
|
|
103
113
|
const plugins = normalizeHarmonyConfigPlugins(config._internal.harmonyConfigPlugins);
|
|
114
|
+
for (const other of plugins) {
|
|
115
|
+
if (other.owner === owner)
|
|
116
|
+
continue;
|
|
117
|
+
const duplicate = plugin.files?.find(file => other.files?.includes(file));
|
|
118
|
+
if (duplicate)
|
|
119
|
+
throw new TypeError(`Harmony file ${duplicate} is claimed by both ${other.owner} and ${owner}.`);
|
|
120
|
+
}
|
|
104
121
|
config._internal.harmonyConfigPlugins = normalizeHarmonyConfigPlugins([
|
|
105
122
|
...plugins.filter(item => item.owner !== owner),
|
|
106
123
|
plugin,
|
package/build/paths.d.ts
CHANGED
|
@@ -3,13 +3,13 @@ export interface HarmonyManagedPaths {
|
|
|
3
3
|
readonly projectBuildProfile: string;
|
|
4
4
|
readonly rootOhPackage: string;
|
|
5
5
|
readonly rootHvigor: string;
|
|
6
|
-
readonly nativeInputsStamp: string;
|
|
7
6
|
readonly hvigorConfig: string;
|
|
8
7
|
readonly entryBuildProfile: string;
|
|
9
8
|
readonly entryOhPackage: string;
|
|
10
9
|
readonly entryHvigor: string;
|
|
11
10
|
readonly moduleJson: string;
|
|
12
11
|
readonly profiles: string;
|
|
12
|
+
readonly abilityStage: string;
|
|
13
13
|
readonly entryAbility: string;
|
|
14
14
|
readonly indexPage: string;
|
|
15
15
|
readonly worker: string;
|
|
@@ -45,18 +45,21 @@ export interface HarmonyPathsApi {
|
|
|
45
45
|
readonly RESOURCE_PATHS: HarmonyResourcePaths;
|
|
46
46
|
assertNoExternalSymlink(root: string, target: string): Promise<void>;
|
|
47
47
|
isInside(root: string, target: string): boolean;
|
|
48
|
-
resolveHarmonyPath(
|
|
49
|
-
resolveProjectPath(
|
|
50
|
-
toPosixRelative(
|
|
48
|
+
resolveHarmonyPath(directory: string, relative: string): Promise<string>;
|
|
49
|
+
resolveProjectPath(root: string, name: keyof HarmonyProjectPaths): Promise<string>;
|
|
50
|
+
toPosixRelative(root: string, target: string): string;
|
|
51
51
|
}
|
|
52
|
-
declare const ManagedPaths: HarmonyManagedPaths
|
|
52
|
+
declare const ManagedPaths: HarmonyManagedPaths & {
|
|
53
|
+
readonly nativeInputsStamp: string;
|
|
54
|
+
};
|
|
53
55
|
declare const ProjectPaths: HarmonyProjectPaths;
|
|
54
56
|
declare const ProjectPathCandidates: HarmonyProjectPathCandidates;
|
|
55
57
|
declare const ResourcePaths: HarmonyResourcePaths;
|
|
56
58
|
export declare function isInside(root: string, target: string): boolean;
|
|
57
59
|
export declare function assertNoExternalSymlink(root: string, target: string): Promise<void>;
|
|
58
|
-
export declare function resolveHarmonyPath(
|
|
59
|
-
export declare function resolveProjectPath(
|
|
60
|
-
export declare function toPosixRelative(
|
|
61
|
-
|
|
62
|
-
export
|
|
60
|
+
export declare function resolveHarmonyPath(directory: string, relative: string): Promise<string>;
|
|
61
|
+
export declare function resolveProjectPath(root: string, name: keyof HarmonyProjectPaths): Promise<string>;
|
|
62
|
+
export declare function toPosixRelative(root: string, target: string): string;
|
|
63
|
+
declare const NativeInputsStampPath: string;
|
|
64
|
+
export declare const HarmonyPaths: Readonly<HarmonyPathsApi>;
|
|
65
|
+
export { ManagedPaths, NativeInputsStampPath, ProjectPathCandidates, ProjectPaths, ResourcePaths };
|
package/build/paths.js
CHANGED
|
@@ -3,7 +3,7 @@ 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.ResourcePaths = exports.ProjectPaths = exports.ProjectPathCandidates = exports.ManagedPaths = exports.HarmonyPaths = void 0;
|
|
6
|
+
exports.ResourcePaths = exports.ProjectPaths = exports.ProjectPathCandidates = exports.NativeInputsStampPath = exports.ManagedPaths = exports.HarmonyPaths = void 0;
|
|
7
7
|
exports.isInside = isInside;
|
|
8
8
|
exports.assertNoExternalSymlink = assertNoExternalSymlink;
|
|
9
9
|
exports.resolveHarmonyPath = resolveHarmonyPath;
|
|
@@ -24,6 +24,7 @@ const ManagedPaths = Object.freeze({
|
|
|
24
24
|
entryHvigor: 'entry/hvigorfile.ts',
|
|
25
25
|
moduleJson: 'entry/src/main/module.json5',
|
|
26
26
|
profiles: 'entry/src/main/resources/base/profile/main_pages.json',
|
|
27
|
+
abilityStage: 'entry/src/main/ets/abilitystage/EntryAbilityStage.ets',
|
|
27
28
|
entryAbility: 'entry/src/main/ets/entryability/EntryAbility.ets',
|
|
28
29
|
indexPage: 'entry/src/main/ets/pages/Index.ets',
|
|
29
30
|
worker: 'entry/src/main/ets/workers/RNOHWorker.ets',
|
|
@@ -37,8 +38,7 @@ const ProjectPaths = Object.freeze({
|
|
|
37
38
|
});
|
|
38
39
|
exports.ProjectPaths = ProjectPaths;
|
|
39
40
|
const ProjectPathCandidates = Object.freeze({
|
|
40
|
-
//
|
|
41
|
-
// search places. The first entry is also the file created for new projects.
|
|
41
|
+
// Match React Native CLI lookup precedence; new projects use the first path.
|
|
42
42
|
reactNativeConfig: Object.freeze([
|
|
43
43
|
'react-native.config.js',
|
|
44
44
|
'react-native.config.cjs',
|
|
@@ -69,55 +69,56 @@ function isInside(root, target) {
|
|
|
69
69
|
|| (!relative.startsWith(`..${node_path_1.default.sep}`) && relative !== '..' && !node_path_1.default.isAbsolute(relative));
|
|
70
70
|
}
|
|
71
71
|
async function assertNoExternalSymlink(root, target) {
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const relative = node_path_1.default.relative(
|
|
75
|
-
let
|
|
72
|
+
const absolute = node_path_1.default.resolve(root);
|
|
73
|
+
const canonical = await node_fs_1.default.promises.realpath(absolute).catch(() => absolute);
|
|
74
|
+
const relative = node_path_1.default.relative(absolute, target);
|
|
75
|
+
let current = absolute;
|
|
76
76
|
for (const segment of relative.split(node_path_1.default.sep).filter(Boolean)) {
|
|
77
|
-
|
|
77
|
+
current = node_path_1.default.join(current, segment);
|
|
78
78
|
let stat;
|
|
79
79
|
try {
|
|
80
|
-
stat = await node_fs_1.default.promises.lstat(
|
|
80
|
+
stat = await node_fs_1.default.promises.lstat(current);
|
|
81
81
|
}
|
|
82
82
|
catch (cause) {
|
|
83
83
|
if (cause.code === 'ENOENT')
|
|
84
84
|
break;
|
|
85
|
-
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_PATH_INVALID', `Cannot inspect managed Harmony path ${
|
|
85
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_PATH_INVALID', `Cannot inspect managed Harmony path ${current}: ${cause.message}`, { cause, file: current, operation: 'resolve-path' });
|
|
86
86
|
}
|
|
87
87
|
if (stat.isSymbolicLink()) {
|
|
88
|
-
const real = await node_fs_1.default.promises.realpath(
|
|
89
|
-
if (!isInside(
|
|
90
|
-
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_PATH_ESCAPE', `Refusing to follow a symlink outside the Harmony project: ${
|
|
88
|
+
const real = await node_fs_1.default.promises.realpath(current);
|
|
89
|
+
if (!isInside(canonical, real)) {
|
|
90
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_PATH_ESCAPE', `Refusing to follow a symlink outside the Harmony project: ${current}`, { file: current, operation: 'resolve-path' });
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
-
async function resolveHarmonyPath(
|
|
96
|
-
if (typeof
|
|
97
|
-
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_PATH_ESCAPE', `Invalid managed path: ${
|
|
95
|
+
async function resolveHarmonyPath(directory, relative) {
|
|
96
|
+
if (typeof relative !== 'string' || !relative || node_path_1.default.isAbsolute(relative)) {
|
|
97
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_PATH_ESCAPE', `Invalid managed path: ${relative}`, { operation: 'resolve-path' });
|
|
98
98
|
}
|
|
99
|
-
const root = node_path_1.default.resolve(
|
|
100
|
-
const target = node_path_1.default.resolve(root,
|
|
99
|
+
const root = node_path_1.default.resolve(directory);
|
|
100
|
+
const target = node_path_1.default.resolve(root, relative);
|
|
101
101
|
if (!isInside(root, target)) {
|
|
102
|
-
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_PATH_ESCAPE', `Managed path escapes the Harmony project: ${
|
|
102
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_PATH_ESCAPE', `Managed path escapes the Harmony project: ${relative}`, { file: target, operation: 'resolve-path' });
|
|
103
103
|
}
|
|
104
104
|
await assertNoExternalSymlink(root, target);
|
|
105
105
|
return target;
|
|
106
106
|
}
|
|
107
|
-
async function resolveProjectPath(
|
|
108
|
-
const candidates = ProjectPathCandidates[
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `Multiple files provide harmony.${modName}: ${existing.map(file => node_path_1.default.basename(file)).join(', ')}`, { file: existing[0], operation: `harmony.${modName}.read` });
|
|
107
|
+
async function resolveProjectPath(root, name) {
|
|
108
|
+
const candidates = ProjectPathCandidates[name] || [ProjectPaths[name]];
|
|
109
|
+
for (const candidate of candidates) {
|
|
110
|
+
if (node_fs_1.default.existsSync(node_path_1.default.join(root, candidate)))
|
|
111
|
+
return resolveHarmonyPath(root, candidate);
|
|
113
112
|
}
|
|
114
|
-
return
|
|
113
|
+
return resolveHarmonyPath(root, candidates[0]);
|
|
115
114
|
}
|
|
116
|
-
function toPosixRelative(
|
|
117
|
-
return node_path_1.default.relative(
|
|
115
|
+
function toPosixRelative(root, target) {
|
|
116
|
+
return node_path_1.default.relative(root, target).split(node_path_1.default.sep).join('/');
|
|
118
117
|
}
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
const { nativeInputsStamp: NativeInputsStampPath, ...PublicManagedPaths } = ManagedPaths;
|
|
119
|
+
exports.NativeInputsStampPath = NativeInputsStampPath;
|
|
120
|
+
exports.HarmonyPaths = Object.freeze({
|
|
121
|
+
HARMONY_PATHS: Object.freeze(PublicManagedPaths),
|
|
121
122
|
PROJECT_PATH_CANDIDATES: ProjectPathCandidates,
|
|
122
123
|
PROJECT_PATHS: ProjectPaths,
|
|
123
124
|
RESOURCE_PATHS: ResourcePaths,
|
|
@@ -126,4 +127,4 @@ exports.HarmonyPaths = {
|
|
|
126
127
|
resolveHarmonyPath,
|
|
127
128
|
resolveProjectPath,
|
|
128
129
|
toPosixRelative,
|
|
129
|
-
};
|
|
130
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HarmonyPermission } from './config';
|
|
2
|
+
import { type HarmonyModuleJson } from './manifest';
|
|
3
|
+
import type { HarmonyConfigPlugin } from './pluginTypes';
|
|
4
|
+
export declare function mergePermissions(current: readonly HarmonyPermission[], additions: readonly HarmonyPermission[]): HarmonyPermission[];
|
|
5
|
+
export declare function ensurePermissions(manifest: HarmonyModuleJson, permissions: readonly HarmonyPermission[]): HarmonyModuleJson;
|
|
6
|
+
export declare function removePermissions(manifest: HarmonyModuleJson, names: readonly string[]): HarmonyModuleJson;
|
|
7
|
+
export declare const withHarmonyPermissions: HarmonyConfigPlugin<readonly HarmonyPermission[]>;
|
|
8
|
+
export declare const HarmonyPermissions: Readonly<{
|
|
9
|
+
ensurePermissions: typeof ensurePermissions;
|
|
10
|
+
mergePermissions: typeof mergePermissions;
|
|
11
|
+
removePermissions: typeof removePermissions;
|
|
12
|
+
}>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HarmonyPermissions = exports.withHarmonyPermissions = void 0;
|
|
4
|
+
exports.mergePermissions = mergePermissions;
|
|
5
|
+
exports.ensurePermissions = ensurePermissions;
|
|
6
|
+
exports.removePermissions = removePermissions;
|
|
7
|
+
const manifest_1 = require("./manifest");
|
|
8
|
+
const mods_1 = require("./mods");
|
|
9
|
+
const normalizeConfig_1 = require("./normalizeConfig");
|
|
10
|
+
function mergePermissions(current, additions) {
|
|
11
|
+
const result = new Map();
|
|
12
|
+
for (const input of [...current, ...additions]) {
|
|
13
|
+
const permission = (0, normalizeConfig_1.normalizePermission)(input);
|
|
14
|
+
const existing = result.get(permission.name);
|
|
15
|
+
const scene = existing?.usedScene || permission.usedScene;
|
|
16
|
+
const abilities = [...new Set([
|
|
17
|
+
...(existing?.usedScene?.abilities ?? []),
|
|
18
|
+
...(permission.usedScene?.abilities ?? []),
|
|
19
|
+
])];
|
|
20
|
+
result.set(permission.name, {
|
|
21
|
+
...existing,
|
|
22
|
+
...permission,
|
|
23
|
+
...(scene
|
|
24
|
+
? {
|
|
25
|
+
usedScene: {
|
|
26
|
+
...(abilities.length ? { abilities } : {}),
|
|
27
|
+
when: existing?.usedScene?.when === 'always' || permission.usedScene?.when === 'always' ? 'always' : 'inuse',
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
: {}),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return [...result.values()];
|
|
34
|
+
}
|
|
35
|
+
function ensurePermissions(manifest, permissions) {
|
|
36
|
+
const module = (0, manifest_1.getModuleOrThrow)(manifest);
|
|
37
|
+
module.requestPermissions = mergePermissions(module.requestPermissions ?? [], permissions);
|
|
38
|
+
return manifest;
|
|
39
|
+
}
|
|
40
|
+
function removePermissions(manifest, names) {
|
|
41
|
+
const module = (0, manifest_1.getModuleOrThrow)(manifest);
|
|
42
|
+
if (module.requestPermissions) {
|
|
43
|
+
module.requestPermissions = module.requestPermissions.filter(permission => !names.includes(permission.name));
|
|
44
|
+
}
|
|
45
|
+
return manifest;
|
|
46
|
+
}
|
|
47
|
+
const withHarmonyPermissions = (config, permissions) => {
|
|
48
|
+
const additions = mergePermissions([], permissions);
|
|
49
|
+
if (config.harmony) {
|
|
50
|
+
config.harmony = { ...config.harmony, permissions: mergePermissions(config.harmony.permissions ?? [], additions) };
|
|
51
|
+
}
|
|
52
|
+
return (0, mods_1.withModuleJson)(config, (mod) => {
|
|
53
|
+
if (mod.modResults.module === undefined)
|
|
54
|
+
mod.modResults.module = {};
|
|
55
|
+
ensurePermissions(mod.modResults, additions);
|
|
56
|
+
return mod;
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
exports.withHarmonyPermissions = withHarmonyPermissions;
|
|
60
|
+
exports.HarmonyPermissions = Object.freeze({ ensurePermissions, mergePermissions, removePermissions });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ExportedConfigWithProps } from '@expo/config-plugins';
|
|
2
|
+
import type { ExpoConfigWithHarmony, ExpoHarmonyPlatform, HarmonyConfig } from './config';
|
|
3
|
+
export type HarmonyConfigPlugin<Props = void> = <Config extends ExpoConfigWithHarmony>(config: Config, props: Props) => Config;
|
|
4
|
+
export type HarmonyModConfig<T> = Omit<ExportedConfigWithProps<T>, 'platforms' | 'modRawConfig' | 'modRequest'> & {
|
|
5
|
+
platforms?: ExpoHarmonyPlatform[];
|
|
6
|
+
harmony?: HarmonyConfig;
|
|
7
|
+
modRawConfig: ExpoConfigWithHarmony;
|
|
8
|
+
modRequest: Omit<ExportedConfigWithProps<T>['modRequest'], 'platform' | 'nextMod'> & {
|
|
9
|
+
platform: 'harmony';
|
|
10
|
+
nextMod?: HarmonyModAction<T>;
|
|
11
|
+
modFile?: string;
|
|
12
|
+
modFileExists?: boolean;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export type HarmonyModAction<T = unknown> = (config: HarmonyModConfig<T>) => HarmonyModConfig<T> | Promise<HarmonyModConfig<T>>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface HarmonyResourceItem extends Record<string, unknown> {
|
|
2
|
+
name: string;
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
5
|
+
export interface HarmonyResourceFile extends Record<string, unknown> {
|
|
6
|
+
string?: HarmonyResourceItem[];
|
|
7
|
+
color?: HarmonyResourceItem[];
|
|
8
|
+
}
|
|
9
|
+
export declare function setString(file: HarmonyResourceFile, item: HarmonyResourceItem): HarmonyResourceFile;
|
|
10
|
+
export declare function removeString(file: HarmonyResourceFile, name: string): HarmonyResourceFile;
|
|
11
|
+
export declare function setColor(file: HarmonyResourceFile, item: HarmonyResourceItem): HarmonyResourceFile;
|
|
12
|
+
export declare function removeColor(file: HarmonyResourceFile, name: string): HarmonyResourceFile;
|
|
13
|
+
export declare function toArgb(value: string | number): string;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setString = setString;
|
|
4
|
+
exports.removeString = removeString;
|
|
5
|
+
exports.setColor = setColor;
|
|
6
|
+
exports.removeColor = removeColor;
|
|
7
|
+
exports.toArgb = toArgb;
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
function setItem(file, kind, item) {
|
|
10
|
+
if (!item || typeof item.name !== 'string' || !/^[A-Za-z0-9_.-]+$/.test(item.name)
|
|
11
|
+
|| item.name.includes('..') || typeof item.value !== 'string') {
|
|
12
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_RESOURCE_INVALID', `Invalid Harmony ${kind} resource.`, { operation: 'edit-resource' });
|
|
13
|
+
}
|
|
14
|
+
const items = readItems(file, kind);
|
|
15
|
+
const existing = items.find(value => value?.name === item.name);
|
|
16
|
+
file[kind] = [...items.filter(value => value?.name !== item.name), { ...existing, ...item }];
|
|
17
|
+
return file;
|
|
18
|
+
}
|
|
19
|
+
function readItems(file, kind) {
|
|
20
|
+
if (file[kind] !== undefined && !Array.isArray(file[kind])) {
|
|
21
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_RESOURCE_INVALID', `Harmony ${kind} resources must be an array.`, { operation: 'edit-resource' });
|
|
22
|
+
}
|
|
23
|
+
return file[kind] ?? [];
|
|
24
|
+
}
|
|
25
|
+
function setString(file, item) {
|
|
26
|
+
return setItem(file, 'string', item);
|
|
27
|
+
}
|
|
28
|
+
function removeString(file, name) {
|
|
29
|
+
const items = readItems(file, 'string');
|
|
30
|
+
if (file.string)
|
|
31
|
+
file.string = items.filter(item => item?.name !== name);
|
|
32
|
+
return file;
|
|
33
|
+
}
|
|
34
|
+
function setColor(file, item) {
|
|
35
|
+
return setItem(file, 'color', item);
|
|
36
|
+
}
|
|
37
|
+
function removeColor(file, name) {
|
|
38
|
+
const items = readItems(file, 'color');
|
|
39
|
+
if (file.color)
|
|
40
|
+
file.color = items.filter(item => item?.name !== name);
|
|
41
|
+
return file;
|
|
42
|
+
}
|
|
43
|
+
function toArgb(value) {
|
|
44
|
+
let hex;
|
|
45
|
+
if (typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 0xffffffff) {
|
|
46
|
+
hex = value.toString(16).padStart(8, '0');
|
|
47
|
+
}
|
|
48
|
+
else if (typeof value === 'string' && /^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/i.test(value)) {
|
|
49
|
+
hex = value.slice(1);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', 'Color must be #RRGGBB, #RRGGBBAA or an unsigned RGBA integer.', { operation: 'convert-color' });
|
|
53
|
+
}
|
|
54
|
+
return `#${hex.length === 8 ? hex.slice(6, 8) + hex.slice(0, 6) : hex}`.toUpperCase();
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo-harmony/config-plugins",
|
|
3
|
-
"version": "55.0.10-harmony.
|
|
3
|
+
"version": "55.0.10-harmony.4",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"react-native",
|
|
6
6
|
"expo",
|
|
@@ -29,12 +29,17 @@
|
|
|
29
29
|
"require": "./build/index.js",
|
|
30
30
|
"default": "./build/index.js"
|
|
31
31
|
},
|
|
32
|
-
"./
|
|
32
|
+
"./package.json": "./package.json",
|
|
33
|
+
"./internal": {
|
|
34
|
+
"types": "./build/internal.d.ts",
|
|
35
|
+
"require": "./build/internal.js",
|
|
36
|
+
"default": "./build/internal.js"
|
|
37
|
+
},
|
|
38
|
+
"./internal/native-inputs": {
|
|
33
39
|
"types": "./build/nativeInputs.d.ts",
|
|
34
40
|
"require": "./build/nativeInputs.js",
|
|
35
41
|
"default": "./build/nativeInputs.js"
|
|
36
|
-
}
|
|
37
|
-
"./package.json": "./package.json"
|
|
42
|
+
}
|
|
38
43
|
},
|
|
39
44
|
"dependencies": {
|
|
40
45
|
"@expo/config-plugins": "55.0.10",
|
|
@@ -44,11 +49,6 @@
|
|
|
44
49
|
"peerDependencies": {
|
|
45
50
|
"@expo/config": "55.0.17"
|
|
46
51
|
},
|
|
47
|
-
"peerDependenciesMeta": {
|
|
48
|
-
"@expo/config": {
|
|
49
|
-
"optional": true
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "18.19.0",
|
|
54
54
|
"typescript": "^6.0.3"
|
package/src/compiler.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { compileModsAsync, type ExportedConfig, type ModPlatform } from '@expo/config-plugins';
|
|
2
|
+
|
|
3
|
+
import type { ExpoConfigWithHarmony } from './config';
|
|
4
|
+
import { withHarmonyBaseMods } from './mods';
|
|
5
|
+
|
|
6
|
+
export interface HarmonyCompileOptions {
|
|
7
|
+
projectRoot: string;
|
|
8
|
+
introspect?: boolean;
|
|
9
|
+
ignoreExistingNativeFiles?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function compileHarmonyModsAsync<Config extends ExpoConfigWithHarmony>(
|
|
13
|
+
config: Config,
|
|
14
|
+
options: HarmonyCompileOptions
|
|
15
|
+
): Promise<Config> {
|
|
16
|
+
return await compileModsAsync(withHarmonyBaseMods(config) as ExportedConfig, {
|
|
17
|
+
...options,
|
|
18
|
+
platforms: ['harmony' as ModPlatform],
|
|
19
|
+
assertMissingModProviders: true,
|
|
20
|
+
}) as Config;
|
|
21
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRunOncePlugin as expoCreateRunOncePlugin,
|
|
3
|
+
withPlugins as expoWithPlugins,
|
|
4
|
+
withRunOnce as expoWithRunOnce,
|
|
5
|
+
withStaticPlugin as expoWithStaticPlugin,
|
|
6
|
+
type ConfigPlugin,
|
|
7
|
+
} from '@expo/config-plugins';
|
|
8
|
+
|
|
9
|
+
import type { HarmonyConfigPlugin } from './pluginTypes';
|
|
10
|
+
import type { ExpoConfigWithHarmony } from './config';
|
|
11
|
+
|
|
12
|
+
// Expo plugins accept the narrower platform union. Method variance allows them
|
|
13
|
+
// alongside Harmony plugins without losing contextual types for inline callbacks.
|
|
14
|
+
type CompatiblePlugin<Props = void> = {
|
|
15
|
+
apply(config: ExpoConfigWithHarmony, props: Props): ExpoConfigWithHarmony;
|
|
16
|
+
}['apply'];
|
|
17
|
+
|
|
18
|
+
export type HarmonyPluginReference = string | CompatiblePlugin<never>;
|
|
19
|
+
export type HarmonyStaticPlugin = HarmonyPluginReference | [HarmonyPluginReference, unknown];
|
|
20
|
+
|
|
21
|
+
export const withPlugins = expoWithPlugins as unknown as HarmonyConfigPlugin<HarmonyStaticPlugin[]>;
|
|
22
|
+
export const withRunOnce = expoWithRunOnce as unknown as HarmonyConfigPlugin<{
|
|
23
|
+
plugin: CompatiblePlugin;
|
|
24
|
+
name: string;
|
|
25
|
+
version?: string;
|
|
26
|
+
}>;
|
|
27
|
+
export const withStaticPlugin = expoWithStaticPlugin as unknown as HarmonyConfigPlugin<{
|
|
28
|
+
plugin: HarmonyStaticPlugin;
|
|
29
|
+
projectRoot?: string;
|
|
30
|
+
fallback?: CompatiblePlugin<{ _resolverError: Error } & Record<string, unknown>>;
|
|
31
|
+
}>;
|
|
32
|
+
|
|
33
|
+
export function createRunOncePlugin<Props>(
|
|
34
|
+
plugin: HarmonyConfigPlugin<Props>, name: string, version?: string
|
|
35
|
+
): HarmonyConfigPlugin<Props>;
|
|
36
|
+
export function createRunOncePlugin<Props>(
|
|
37
|
+
plugin: ConfigPlugin<Props>, name: string, version?: string
|
|
38
|
+
): HarmonyConfigPlugin<Props>;
|
|
39
|
+
export function createRunOncePlugin(
|
|
40
|
+
plugin: ConfigPlugin<never> | HarmonyConfigPlugin<never>,
|
|
41
|
+
name: string,
|
|
42
|
+
version?: string
|
|
43
|
+
): HarmonyConfigPlugin<never> {
|
|
44
|
+
return expoCreateRunOncePlugin(plugin as ConfigPlugin<never>, name, version) as HarmonyConfigPlugin<never>;
|
|
45
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -18,25 +18,14 @@ export interface HarmonyPermission {
|
|
|
18
18
|
usedScene?: { abilities?: string[]; when?: 'inuse' | 'always' };
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export type HarmonySkillUri = Record<string, string | number | boolean>;
|
|
22
|
+
|
|
21
23
|
export interface HarmonySkill {
|
|
22
24
|
entities?: string[];
|
|
23
25
|
actions?: string[];
|
|
24
|
-
uris?:
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface HarmonyFontDefinition {
|
|
28
|
-
path: string;
|
|
29
|
-
weight?: number;
|
|
30
|
-
style?: 'normal' | 'italic';
|
|
26
|
+
uris?: HarmonySkillUri[];
|
|
31
27
|
}
|
|
32
28
|
|
|
33
|
-
export type HarmonyFont
|
|
34
|
-
= | string
|
|
35
|
-
| {
|
|
36
|
-
fontFamily: string;
|
|
37
|
-
fontDefinitions: HarmonyFontDefinition[];
|
|
38
|
-
};
|
|
39
|
-
|
|
40
29
|
export interface HarmonyConfig {
|
|
41
30
|
bundleName: string;
|
|
42
31
|
moduleName?: string;
|
|
@@ -46,14 +35,11 @@ export interface HarmonyConfig {
|
|
|
46
35
|
versionCode?: number;
|
|
47
36
|
versionName?: string;
|
|
48
37
|
targetApiVersion?: number;
|
|
49
|
-
/** Harmony SDK label written to build-profile.json5, e.g. 6.0.0(20). Its API must match targetApiVersion. */
|
|
50
38
|
targetSdkVersion?: number | string;
|
|
51
|
-
/** API number, or a full Harmony SDK label ending in the API number. */
|
|
52
39
|
compatibleSdkVersion?: number | string;
|
|
53
40
|
deviceTypes?: HarmonyDeviceType[];
|
|
54
41
|
permissions?: HarmonyPermission[];
|
|
55
42
|
skills?: HarmonySkill[];
|
|
56
|
-
/** URL schemes the app may inspect with Linking.canOpenURL. http, https, and expo.scheme are added automatically. */
|
|
57
43
|
querySchemes?: string[];
|
|
58
44
|
icon?: string;
|
|
59
45
|
label?: string;
|
|
@@ -63,8 +49,6 @@ export interface HarmonyConfig {
|
|
|
63
49
|
jsEngine?: 'hermes';
|
|
64
50
|
abiFilters?: string[];
|
|
65
51
|
signingConfigFile?: string;
|
|
66
|
-
/** Fonts bundled when the `@expo-harmony/expo-font` config plugin is registered. */
|
|
67
|
-
fonts?: HarmonyFont[];
|
|
68
52
|
}
|
|
69
53
|
|
|
70
54
|
export type ExpoConfigWithHarmony = Omit<ExpoConfig, 'platforms'> & {
|