@expo-harmony/config-plugins 55.0.10-harmony.3 → 55.0.10-harmony.5
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 +4 -4
- package/build/config.d.ts +1 -0
- package/build/index.d.ts +3 -1
- package/build/index.js +5 -1
- package/build/mods.d.ts +2 -1
- package/build/mods.js +5 -1
- package/build/normalizeConfig.d.ts +1 -1
- package/build/normalizeConfig.js +21 -55
- package/build/paths.d.ts +1 -0
- package/build/paths.js +1 -0
- package/build/sdkVersion.d.ts +6 -0
- package/build/sdkVersion.js +52 -0
- package/package.json +1 -4
- package/src/config.ts +1 -0
- package/src/index.ts +4 -0
- package/src/mods.ts +11 -0
- package/src/normalizeConfig.ts +28 -89
- package/src/paths.ts +4 -0
- package/src/sdkVersion.ts +69 -0
package/README.md
CHANGED
|
@@ -40,9 +40,9 @@ module.exports = defineExpoHarmonyConfig({
|
|
|
40
40
|
| `vendor` | `expo-harmony` |
|
|
41
41
|
| `versionCode` | `1` |
|
|
42
42
|
| `versionName` | 配置里的 `version`,没有则 `1.0.0` |
|
|
43
|
-
| `targetApiVersion` | `
|
|
44
|
-
| `targetSdkVersion` | 数字或 SDK
|
|
45
|
-
| `compatibleSdkVersion` | `
|
|
43
|
+
| `targetApiVersion` | 旧版别名,建议改用 `expo-build-properties` 的 `targetSdkVersion` |
|
|
44
|
+
| `targetSdkVersion` | 数字或 SDK 字符串,默认 `24`,兼容旧 `targetApiVersion` |
|
|
45
|
+
| `compatibleSdkVersion` | `20`,不能低于 20,也不能大于 target |
|
|
46
46
|
| `deviceTypes` | `['phone', 'tablet']` |
|
|
47
47
|
| `permissions` | 权限列表,默认包含 `ohos.permission.INTERNET` |
|
|
48
48
|
| `skills` | 技能列表,`scheme` 会追加对应项 |
|
|
@@ -56,7 +56,7 @@ module.exports = defineExpoHarmonyConfig({
|
|
|
56
56
|
| `abiFilters` | `['arm64-v8a', 'x86_64']` |
|
|
57
57
|
| `signingConfigFile` | 签名配置文件路径,不设置则忽略 |
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
SDK/ABI 的这些旧字段继续兼容,新项目建议使用 `@expo-harmony/expo-build-properties`。`targetSdkVersion` 和 `compatibleSdkVersion` 可以写数字或带 API 级别的 SDK 标签;API 26 起使用 `26.0.0` 这样的分段版本。内置旧标签覆盖 API 13、14、20、21、23、24,已知 API 不接受错误的标签。`targetApiVersion` 与 `targetSdkVersion` 同时存在时须一致。`backgroundColor` 写 `#RRGGBB` 或 `#RRGGBBAA`,写入原生工程时转成 ARGB。`jsEngine` 目前只能是 `hermes`。
|
|
60
60
|
|
|
61
61
|
`normalizeHarmonyConfig(config)` 返回补齐默认值并校验后的配置。
|
|
62
62
|
|
package/build/config.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export interface HarmonyConfig {
|
|
|
26
26
|
vendor?: string;
|
|
27
27
|
versionCode?: number;
|
|
28
28
|
versionName?: string;
|
|
29
|
+
/** @deprecated Configure targetSdkVersion in @expo-harmony/expo-build-properties. */
|
|
29
30
|
targetApiVersion?: number;
|
|
30
31
|
targetSdkVersion?: number | string;
|
|
31
32
|
compatibleSdkVersion?: number | string;
|
package/build/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type { HarmonyConfigPluginErrorOptions } from './errors';
|
|
|
11
11
|
export { atomicWrite, stableJson as stableHarmonyJson } from './files';
|
|
12
12
|
export { normalizeHarmonyConfig } from './normalizeConfig';
|
|
13
13
|
export type { HarmonyExpoConfig, NormalizedHarmonyConfig } from './normalizeConfig';
|
|
14
|
-
export { HarmonyModNames as HARMONY_MOD_NAMES, recordManagedFile, withAppJson, withArkTSPackageProvider, withCMakeLists, withColors, withCppPackageProvider, withEntryAbility, withEntryBuildProfile, withEntryHvigor, withEntryOhPackage, withHarmonyAutolinking, withHarmonyBaseMods, withHarmonyDangerousMod, withHarmonyMod, withHarmonyResources, withHvigorConfig, withIndexPage, withMedia, withModuleJson, withProfiles, withProjectBuildProfile, withReactNativeConfig, withRootHvigor, withRootOhPackage, withStrings, withWorker, } from './mods';
|
|
14
|
+
export { HarmonyModNames as HARMONY_MOD_NAMES, recordManagedFile, withAppJson, withArkTSPackageProvider, withCMakeLists, withColors, withCppPackageProvider, withAbilityStage, withEntryAbility, withEntryBuildProfile, withEntryHvigor, withEntryOhPackage, withHarmonyAutolinking, withHarmonyBaseMods, withHarmonyDangerousMod, withHarmonyMod, withHarmonyResources, withHvigorConfig, withIndexPage, withMedia, withModuleJson, withProfiles, withProjectBuildProfile, withReactNativeConfig, withRootHvigor, withRootOhPackage, withStrings, withWorker, } from './mods';
|
|
15
15
|
export type { HarmonyJson, HarmonyMediaDescriptor, HarmonyMediaMap, HarmonyModName, HarmonyResourceMap, } from './mods';
|
|
16
16
|
export { registerHarmonyConfigPlugin } from './ownership';
|
|
17
17
|
export type { HarmonyConfigPluginOwnership } from './ownership';
|
|
@@ -25,3 +25,5 @@ export type { HarmonyResourceFile, HarmonyResourceItem } from './resources';
|
|
|
25
25
|
export { withHarmonyGeneratedFiles, withRawfile } from './generatedFiles';
|
|
26
26
|
export type { HarmonyFileDescriptor, HarmonyFileMap, HarmonyGeneratedFilesOptions } from './generatedFiles';
|
|
27
27
|
export { HarmonyPermissions, withHarmonyPermissions } from './permissions';
|
|
28
|
+
export { parseHarmonySdkVersion, compareHarmonyApiVersions } from './sdkVersion';
|
|
29
|
+
export type { HarmonySdkVersion } from './sdkVersion';
|
package/build/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.withHarmonyPermissions = exports.HarmonyPermissions = exports.withRawfile = exports.withHarmonyGeneratedFiles = exports.HarmonyResources = exports.HarmonyManifest = exports.HarmonyPaths = exports.registerHarmonyConfigPlugin = exports.withWorker = exports.withStrings = exports.withRootOhPackage = exports.withRootHvigor = exports.withReactNativeConfig = exports.withProjectBuildProfile = exports.withProfiles = exports.withModuleJson = exports.withMedia = exports.withIndexPage = exports.withHvigorConfig = exports.withHarmonyResources = exports.withHarmonyMod = exports.withHarmonyDangerousMod = exports.withHarmonyBaseMods = exports.withHarmonyAutolinking = exports.withEntryOhPackage = exports.withEntryHvigor = exports.withEntryBuildProfile = exports.withEntryAbility = exports.withCppPackageProvider = exports.withColors = exports.withCMakeLists = exports.withArkTSPackageProvider = exports.withAppJson = exports.recordManagedFile = exports.HARMONY_MOD_NAMES = exports.normalizeHarmonyConfig = exports.stableHarmonyJson = exports.atomicWrite = exports.HarmonyConfigPluginError = exports.withHarmonyJsonFile = exports.createHarmonyFileMod = exports.compileHarmonyModsAsync = exports.withStaticPlugin = exports.withRunOnce = exports.withPlugins = exports.createRunOncePlugin = exports.defineExpoHarmonyConfig = void 0;
|
|
36
|
+
exports.compareHarmonyApiVersions = exports.parseHarmonySdkVersion = exports.withHarmonyPermissions = exports.HarmonyPermissions = exports.withRawfile = exports.withHarmonyGeneratedFiles = exports.HarmonyResources = exports.HarmonyManifest = exports.HarmonyPaths = exports.registerHarmonyConfigPlugin = exports.withWorker = exports.withStrings = exports.withRootOhPackage = exports.withRootHvigor = exports.withReactNativeConfig = exports.withProjectBuildProfile = exports.withProfiles = exports.withModuleJson = exports.withMedia = exports.withIndexPage = exports.withHvigorConfig = exports.withHarmonyResources = exports.withHarmonyMod = exports.withHarmonyDangerousMod = exports.withHarmonyBaseMods = exports.withHarmonyAutolinking = exports.withEntryOhPackage = exports.withEntryHvigor = exports.withEntryBuildProfile = exports.withEntryAbility = exports.withAbilityStage = exports.withCppPackageProvider = exports.withColors = exports.withCMakeLists = exports.withArkTSPackageProvider = exports.withAppJson = exports.recordManagedFile = exports.HARMONY_MOD_NAMES = exports.normalizeHarmonyConfig = exports.stableHarmonyJson = exports.atomicWrite = exports.HarmonyConfigPluginError = exports.withHarmonyJsonFile = exports.createHarmonyFileMod = exports.compileHarmonyModsAsync = exports.withStaticPlugin = exports.withRunOnce = exports.withPlugins = exports.createRunOncePlugin = exports.defineExpoHarmonyConfig = void 0;
|
|
37
37
|
var config_1 = require("./config");
|
|
38
38
|
Object.defineProperty(exports, "defineExpoHarmonyConfig", { enumerable: true, get: function () { return config_1.defineExpoHarmonyConfig; } });
|
|
39
39
|
var composition_1 = require("./composition");
|
|
@@ -61,6 +61,7 @@ Object.defineProperty(exports, "withArkTSPackageProvider", { enumerable: true, g
|
|
|
61
61
|
Object.defineProperty(exports, "withCMakeLists", { enumerable: true, get: function () { return mods_1.withCMakeLists; } });
|
|
62
62
|
Object.defineProperty(exports, "withColors", { enumerable: true, get: function () { return mods_1.withColors; } });
|
|
63
63
|
Object.defineProperty(exports, "withCppPackageProvider", { enumerable: true, get: function () { return mods_1.withCppPackageProvider; } });
|
|
64
|
+
Object.defineProperty(exports, "withAbilityStage", { enumerable: true, get: function () { return mods_1.withAbilityStage; } });
|
|
64
65
|
Object.defineProperty(exports, "withEntryAbility", { enumerable: true, get: function () { return mods_1.withEntryAbility; } });
|
|
65
66
|
Object.defineProperty(exports, "withEntryBuildProfile", { enumerable: true, get: function () { return mods_1.withEntryBuildProfile; } });
|
|
66
67
|
Object.defineProperty(exports, "withEntryHvigor", { enumerable: true, get: function () { return mods_1.withEntryHvigor; } });
|
|
@@ -93,3 +94,6 @@ Object.defineProperty(exports, "withRawfile", { enumerable: true, get: function
|
|
|
93
94
|
var permissions_1 = require("./permissions");
|
|
94
95
|
Object.defineProperty(exports, "HarmonyPermissions", { enumerable: true, get: function () { return permissions_1.HarmonyPermissions; } });
|
|
95
96
|
Object.defineProperty(exports, "withHarmonyPermissions", { enumerable: true, get: function () { return permissions_1.withHarmonyPermissions; } });
|
|
97
|
+
var sdkVersion_1 = require("./sdkVersion");
|
|
98
|
+
Object.defineProperty(exports, "parseHarmonySdkVersion", { enumerable: true, get: function () { return sdkVersion_1.parseHarmonySdkVersion; } });
|
|
99
|
+
Object.defineProperty(exports, "compareHarmonyApiVersions", { enumerable: true, get: function () { return sdkVersion_1.compareHarmonyApiVersions; } });
|
package/build/mods.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { ExpoConfigWithHarmony } from './config';
|
|
|
3
3
|
import type { HarmonyModuleJson } from './manifest';
|
|
4
4
|
import type { HarmonyConfigPlugin, HarmonyModAction } from './pluginTypes';
|
|
5
5
|
import type { HarmonyResourceFile } from './resources';
|
|
6
|
-
export type HarmonyModName = 'dangerous' | 'reactNativeConfig' | 'appJson' | 'projectBuildProfile' | 'rootOhPackage' | 'rootHvigor' | 'hvigorConfig' | 'entryBuildProfile' | 'entryOhPackage' | 'entryHvigor' | 'moduleJson' | 'strings' | 'colors' | 'media' | 'profiles' | 'entryAbility' | 'indexPage' | 'worker' | 'arktsPackageProvider' | 'cppPackageProvider' | 'cmakeLists' | 'autolinking';
|
|
6
|
+
export type HarmonyModName = 'dangerous' | 'reactNativeConfig' | 'appJson' | 'projectBuildProfile' | 'rootOhPackage' | 'rootHvigor' | 'hvigorConfig' | 'entryBuildProfile' | 'entryOhPackage' | 'entryHvigor' | 'moduleJson' | 'strings' | 'colors' | 'media' | 'profiles' | 'abilityStage' | 'entryAbility' | 'indexPage' | 'worker' | 'arktsPackageProvider' | 'cppPackageProvider' | 'cmakeLists' | 'autolinking';
|
|
7
7
|
export type HarmonyJson = Record<string, unknown>;
|
|
8
8
|
export type HarmonyResourceMap = Record<string, HarmonyResourceFile>;
|
|
9
9
|
export type HarmonyMediaDescriptor = {
|
|
@@ -42,6 +42,7 @@ export declare const withStrings: HarmonyConfigPlugin<HarmonyModAction<HarmonyRe
|
|
|
42
42
|
export declare const withColors: HarmonyConfigPlugin<HarmonyModAction<HarmonyResourceMap>>;
|
|
43
43
|
export declare const withMedia: HarmonyConfigPlugin<HarmonyModAction<HarmonyMediaMap>>;
|
|
44
44
|
export declare const withProfiles: HarmonyConfigPlugin<HarmonyModAction<HarmonyJson>>;
|
|
45
|
+
export declare const withAbilityStage: HarmonyConfigPlugin<HarmonyModAction<string>>;
|
|
45
46
|
export declare const withEntryAbility: HarmonyConfigPlugin<HarmonyModAction<string>>;
|
|
46
47
|
export declare const withIndexPage: HarmonyConfigPlugin<HarmonyModAction<string>>;
|
|
47
48
|
export declare const withWorker: HarmonyConfigPlugin<HarmonyModAction<string>>;
|
package/build/mods.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.withHarmonyResources = exports.withHarmonyDangerousMod = exports.withProjectPatch = exports.withPreparation = 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;
|
|
@@ -29,6 +29,7 @@ const TextMods = new Set([
|
|
|
29
29
|
'rootHvigor',
|
|
30
30
|
'nativeInputsStamp',
|
|
31
31
|
'entryHvigor',
|
|
32
|
+
'abilityStage',
|
|
32
33
|
'entryAbility',
|
|
33
34
|
'indexPage',
|
|
34
35
|
'worker',
|
|
@@ -57,6 +58,7 @@ const AllModNames = Object.freeze([
|
|
|
57
58
|
'colors',
|
|
58
59
|
'media',
|
|
59
60
|
'profiles',
|
|
61
|
+
'abilityStage',
|
|
60
62
|
'entryAbility',
|
|
61
63
|
'indexPage',
|
|
62
64
|
'worker',
|
|
@@ -397,6 +399,8 @@ const withMedia = (config, action) => withHarmonyMod(config, ['media', action]);
|
|
|
397
399
|
exports.withMedia = withMedia;
|
|
398
400
|
const withProfiles = (config, action) => withHarmonyMod(config, ['profiles', action]);
|
|
399
401
|
exports.withProfiles = withProfiles;
|
|
402
|
+
const withAbilityStage = (config, action) => withHarmonyMod(config, ['abilityStage', action]);
|
|
403
|
+
exports.withAbilityStage = withAbilityStage;
|
|
400
404
|
const withEntryAbility = (config, action) => withHarmonyMod(config, ['entryAbility', action]);
|
|
401
405
|
exports.withEntryAbility = withEntryAbility;
|
|
402
406
|
const withIndexPage = (config, action) => withHarmonyMod(config, ['indexPage', action]);
|
|
@@ -16,7 +16,7 @@ interface NormalizedHarmonyConfig {
|
|
|
16
16
|
querySchemes: string[];
|
|
17
17
|
signingConfigFile?: string;
|
|
18
18
|
skills: NonNullable<HarmonyConfig['skills']>;
|
|
19
|
-
targetApiVersion: number;
|
|
19
|
+
targetApiVersion: number | string;
|
|
20
20
|
targetSdkVersionString: string;
|
|
21
21
|
vendor: string;
|
|
22
22
|
versionCode: number;
|
package/build/normalizeConfig.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.normalizePermission = normalizePermission;
|
|
|
4
4
|
exports.normalizeHarmonyConfig = normalizeHarmonyConfig;
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
6
|
const resources_1 = require("./resources");
|
|
7
|
+
const sdkVersion_1 = require("./sdkVersion");
|
|
7
8
|
const BundlePattern = /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*){2,}$/;
|
|
8
9
|
const IdentifierPattern = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
9
10
|
const ColorPattern = /^(#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{8})$/;
|
|
@@ -16,16 +17,8 @@ const Orientations = new Set([
|
|
|
16
17
|
'auto_rotation',
|
|
17
18
|
]);
|
|
18
19
|
const DeviceTypes = new Set(['phone', 'tablet', '2in1']);
|
|
19
|
-
const MinimumApi =
|
|
20
|
+
const MinimumApi = 20;
|
|
20
21
|
const TargetApi = 24;
|
|
21
|
-
const SdkVersions = new Map([
|
|
22
|
-
[13, '5.0.1(13)'],
|
|
23
|
-
[14, '5.0.2(14)'],
|
|
24
|
-
[20, '6.0.0(20)'],
|
|
25
|
-
[21, '6.0.1(21)'],
|
|
26
|
-
[23, '6.1.0(23)'],
|
|
27
|
-
[24, '6.1.1(24)'],
|
|
28
|
-
]);
|
|
29
22
|
class HarmonyConfigError extends errors_1.HarmonyConfigPluginError {
|
|
30
23
|
constructor(message) {
|
|
31
24
|
super('ERR_HARMONY_CONFIG_INVALID', message, { operation: 'normalize-config' });
|
|
@@ -56,43 +49,6 @@ function normalizeColor(value, field, fallback) {
|
|
|
56
49
|
}
|
|
57
50
|
return (0, resources_1.toArgb)(color);
|
|
58
51
|
}
|
|
59
|
-
function parseSdkApi(value, field) {
|
|
60
|
-
if (value === undefined)
|
|
61
|
-
return null;
|
|
62
|
-
if (typeof value === 'number' && Number.isSafeInteger(value) && value > 0)
|
|
63
|
-
return value;
|
|
64
|
-
if (typeof value !== 'string') {
|
|
65
|
-
throw new HarmonyConfigError(`harmony.${field} must be a positive API number or an SDK label.`);
|
|
66
|
-
}
|
|
67
|
-
const match = /\((\d+)\)$/u.exec(value.trim());
|
|
68
|
-
const api = Number(match?.[1]);
|
|
69
|
-
if (!match || !Number.isSafeInteger(api) || api <= 0) {
|
|
70
|
-
throw new HarmonyConfigError(`harmony.${field} must end with its API level in parentheses, for example "6.0.0(20)".`);
|
|
71
|
-
}
|
|
72
|
-
return api;
|
|
73
|
-
}
|
|
74
|
-
function resolveSdkVersion(api, field, value) {
|
|
75
|
-
if (value !== undefined) {
|
|
76
|
-
if (typeof value === 'number' && Number.isSafeInteger(value) && value > 0) {
|
|
77
|
-
if (value !== api) {
|
|
78
|
-
throw new HarmonyConfigError(`harmony.${field} is API ${value}, but the configured API level is ${api}.`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
const version = readString(value, `harmony.${field}`);
|
|
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}.`);
|
|
86
|
-
}
|
|
87
|
-
return version;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
const version = SdkVersions.get(api);
|
|
91
|
-
if (!version) {
|
|
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.`);
|
|
93
|
-
}
|
|
94
|
-
return version;
|
|
95
|
-
}
|
|
96
52
|
function normalizeStringArray(value, field, allowed) {
|
|
97
53
|
if (!Array.isArray(value) || value.length === 0) {
|
|
98
54
|
throw new HarmonyConfigError(`${field} must be a non-empty array.`);
|
|
@@ -190,13 +146,19 @@ function normalizeHarmonyConfig(config) {
|
|
|
190
146
|
}
|
|
191
147
|
const version = readString(harmony.versionName, 'harmony.versionName', config.version || '1.0.0');
|
|
192
148
|
const code = readPositiveInteger(harmony.versionCode, 'versionCode', 1);
|
|
193
|
-
const target =
|
|
194
|
-
|
|
195
|
-
|
|
149
|
+
const target = (0, sdkVersion_1.parseHarmonySdkVersion)(harmony.targetSdkVersion ?? harmony.targetApiVersion ?? TargetApi, 'harmony.targetSdkVersion');
|
|
150
|
+
if (harmony.targetApiVersion !== undefined) {
|
|
151
|
+
const alias = (0, sdkVersion_1.parseHarmonySdkVersion)(harmony.targetApiVersion, 'harmony.targetApiVersion');
|
|
152
|
+
if ((0, sdkVersion_1.compareHarmonyApiVersions)(alias.api, target.api) !== 0) {
|
|
153
|
+
throw new HarmonyConfigError('harmony.targetApiVersion conflicts with harmony.targetSdkVersion.');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const compatible = (0, sdkVersion_1.parseHarmonySdkVersion)(harmony.compatibleSdkVersion ?? MinimumApi, 'harmony.compatibleSdkVersion');
|
|
157
|
+
if ((0, sdkVersion_1.compareHarmonyApiVersions)(compatible.api, MinimumApi) < 0) {
|
|
196
158
|
throw new HarmonyConfigError(`Harmony compatible API must be ${MinimumApi} or newer.`);
|
|
197
159
|
}
|
|
198
|
-
if (compatible >
|
|
199
|
-
throw new HarmonyConfigError('Harmony compatibleSdkVersion cannot exceed
|
|
160
|
+
if ((0, sdkVersion_1.compareHarmonyApiVersions)(compatible.api, target.api) > 0) {
|
|
161
|
+
throw new HarmonyConfigError('Harmony compatibleSdkVersion cannot exceed targetSdkVersion.');
|
|
200
162
|
}
|
|
201
163
|
const orientation = harmony.orientation || config.orientation || 'default';
|
|
202
164
|
if (!Orientations.has(orientation)) {
|
|
@@ -211,7 +173,7 @@ function normalizeHarmonyConfig(config) {
|
|
|
211
173
|
? ['arm64-v8a', 'x86_64']
|
|
212
174
|
: normalizeStringArray(harmony.abiFilters, 'harmony.abiFilters');
|
|
213
175
|
for (const abi of abis) {
|
|
214
|
-
if (
|
|
176
|
+
if (!['arm64-v8a', 'x86_64'].includes(abi)) {
|
|
215
177
|
throw new HarmonyConfigError(`Invalid Harmony ABI filter: ${abi}`);
|
|
216
178
|
}
|
|
217
179
|
}
|
|
@@ -219,6 +181,10 @@ function normalizeHarmonyConfig(config) {
|
|
|
219
181
|
if (!permissions.some(permission => permission.name === 'ohos.permission.INTERNET')) {
|
|
220
182
|
permissions.unshift({ name: 'ohos.permission.INTERNET' });
|
|
221
183
|
}
|
|
184
|
+
if (config.updates?.checkAutomatically === 'WIFI_ONLY'
|
|
185
|
+
&& !permissions.some(permission => permission.name === 'ohos.permission.GET_NETWORK_INFO')) {
|
|
186
|
+
permissions.push({ name: 'ohos.permission.GET_NETWORK_INFO' });
|
|
187
|
+
}
|
|
222
188
|
const schemes = readExpoSchemes(config);
|
|
223
189
|
let skills = (harmony.skills || []).map(normalizeSkill);
|
|
224
190
|
for (const scheme of schemes) {
|
|
@@ -250,7 +216,7 @@ function normalizeHarmonyConfig(config) {
|
|
|
250
216
|
abilityName: ability,
|
|
251
217
|
backgroundColor: background,
|
|
252
218
|
bundleName: bundle,
|
|
253
|
-
compatibleSdkVersionString:
|
|
219
|
+
compatibleSdkVersionString: compatible.native,
|
|
254
220
|
deviceTypes: devices,
|
|
255
221
|
icon: harmony.icon || config.icon,
|
|
256
222
|
label: readString(harmony.label, 'harmony.label', config.name),
|
|
@@ -261,8 +227,8 @@ function normalizeHarmonyConfig(config) {
|
|
|
261
227
|
querySchemes: queries,
|
|
262
228
|
signingConfigFile: signing,
|
|
263
229
|
skills,
|
|
264
|
-
targetApiVersion: target,
|
|
265
|
-
targetSdkVersionString:
|
|
230
|
+
targetApiVersion: target.api,
|
|
231
|
+
targetSdkVersionString: target.native,
|
|
266
232
|
vendor: readString(harmony.vendor, 'harmony.vendor', 'expo-harmony'),
|
|
267
233
|
versionCode: code,
|
|
268
234
|
versionName: version,
|
package/build/paths.d.ts
CHANGED
package/build/paths.js
CHANGED
|
@@ -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',
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface HarmonySdkVersion {
|
|
2
|
+
api: number | string;
|
|
3
|
+
native: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function parseHarmonySdkVersion(value: unknown, field?: string): HarmonySdkVersion;
|
|
6
|
+
export declare function compareHarmonyApiVersions(left: number | string, right: number | string): number;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseHarmonySdkVersion = parseHarmonySdkVersion;
|
|
4
|
+
exports.compareHarmonyApiVersions = compareHarmonyApiVersions;
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const Labels = new Map([
|
|
7
|
+
[13, '5.0.1(13)'], [14, '5.0.2(14)'], [20, '6.0.0(20)'],
|
|
8
|
+
[21, '6.0.1(21)'], [23, '6.1.0(23)'], [24, '6.1.1(24)'],
|
|
9
|
+
]);
|
|
10
|
+
function parseHarmonySdkVersion(value, field = 'SDK version') {
|
|
11
|
+
if (typeof value === 'number') {
|
|
12
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 999) {
|
|
13
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `${field}: expected a positive API level.`);
|
|
14
|
+
}
|
|
15
|
+
if (value >= 26)
|
|
16
|
+
return { api: `${value}.0.0`, native: `${value}.0.0` };
|
|
17
|
+
const native = Labels.get(value);
|
|
18
|
+
if (!native) {
|
|
19
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `${field}: API ${value} requires an explicit HarmonyOS SDK label.`);
|
|
20
|
+
}
|
|
21
|
+
return { api: value, native };
|
|
22
|
+
}
|
|
23
|
+
if (typeof value !== 'string') {
|
|
24
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `${field}: expected an API number or HarmonyOS SDK version.`);
|
|
25
|
+
}
|
|
26
|
+
const version = value.trim();
|
|
27
|
+
const dotted = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(version);
|
|
28
|
+
if (dotted && Number(dotted[1]) >= 26
|
|
29
|
+
&& dotted.slice(1).every(part => String(Number(part)) === part)) {
|
|
30
|
+
return { api: version, native: version };
|
|
31
|
+
}
|
|
32
|
+
const label = /^\d+\.\d+\.\d+\(([1-9]\d*)\)$/.exec(version);
|
|
33
|
+
if (label && Number(label[1]) < 26) {
|
|
34
|
+
const api = Number(label[1]);
|
|
35
|
+
const known = Labels.get(api);
|
|
36
|
+
if (known && known !== version) {
|
|
37
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `${field}: API ${api} uses SDK label ${known}.`);
|
|
38
|
+
}
|
|
39
|
+
return { api, native: version };
|
|
40
|
+
}
|
|
41
|
+
throw new errors_1.HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `${field}: expected an SDK label such as "6.1.1(24)", or a dotted API version such as "26.0.0".`);
|
|
42
|
+
}
|
|
43
|
+
function compareHarmonyApiVersions(left, right) {
|
|
44
|
+
const a = String(left).split('.').map(Number);
|
|
45
|
+
const b = String(right).split('.').map(Number);
|
|
46
|
+
for (let i = 0; i < 3; i++) {
|
|
47
|
+
const delta = (a[i] ?? 0) - (b[i] ?? 0);
|
|
48
|
+
if (delta)
|
|
49
|
+
return delta;
|
|
50
|
+
}
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
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.5",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"react-native",
|
|
6
6
|
"expo",
|
|
@@ -46,9 +46,6 @@
|
|
|
46
46
|
"@expo/config-types": "^55.0.5",
|
|
47
47
|
"json5": "2.2.3"
|
|
48
48
|
},
|
|
49
|
-
"peerDependencies": {
|
|
50
|
-
"@expo/config": "55.0.17"
|
|
51
|
-
},
|
|
52
49
|
"devDependencies": {
|
|
53
50
|
"@types/node": "18.19.0",
|
|
54
51
|
"typescript": "^6.0.3"
|
package/src/config.ts
CHANGED
|
@@ -34,6 +34,7 @@ export interface HarmonyConfig {
|
|
|
34
34
|
vendor?: string;
|
|
35
35
|
versionCode?: number;
|
|
36
36
|
versionName?: string;
|
|
37
|
+
/** @deprecated Configure targetSdkVersion in @expo-harmony/expo-build-properties. */
|
|
37
38
|
targetApiVersion?: number;
|
|
38
39
|
targetSdkVersion?: number | string;
|
|
39
40
|
compatibleSdkVersion?: number | string;
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ export {
|
|
|
30
30
|
withCMakeLists,
|
|
31
31
|
withColors,
|
|
32
32
|
withCppPackageProvider,
|
|
33
|
+
withAbilityStage,
|
|
33
34
|
withEntryAbility,
|
|
34
35
|
withEntryBuildProfile,
|
|
35
36
|
withEntryHvigor,
|
|
@@ -77,3 +78,6 @@ export type { HarmonyResourceFile, HarmonyResourceItem } from './resources';
|
|
|
77
78
|
export { withHarmonyGeneratedFiles, withRawfile } from './generatedFiles';
|
|
78
79
|
export type { HarmonyFileDescriptor, HarmonyFileMap, HarmonyGeneratedFilesOptions } from './generatedFiles';
|
|
79
80
|
export { HarmonyPermissions, withHarmonyPermissions } from './permissions';
|
|
81
|
+
|
|
82
|
+
export { parseHarmonySdkVersion, compareHarmonyApiVersions } from './sdkVersion';
|
|
83
|
+
export type { HarmonySdkVersion } from './sdkVersion';
|
package/src/mods.ts
CHANGED
|
@@ -34,6 +34,7 @@ export type HarmonyModName
|
|
|
34
34
|
| 'colors'
|
|
35
35
|
| 'media'
|
|
36
36
|
| 'profiles'
|
|
37
|
+
| 'abilityStage'
|
|
37
38
|
| 'entryAbility'
|
|
38
39
|
| 'indexPage'
|
|
39
40
|
| 'worker'
|
|
@@ -97,6 +98,7 @@ const TextMods = new Set<FileModName>([
|
|
|
97
98
|
'rootHvigor',
|
|
98
99
|
'nativeInputsStamp',
|
|
99
100
|
'entryHvigor',
|
|
101
|
+
'abilityStage',
|
|
100
102
|
'entryAbility',
|
|
101
103
|
'indexPage',
|
|
102
104
|
'worker',
|
|
@@ -126,6 +128,7 @@ const AllModNames: readonly AnyModName[] = Object.freeze([
|
|
|
126
128
|
'colors',
|
|
127
129
|
'media',
|
|
128
130
|
'profiles',
|
|
131
|
+
'abilityStage',
|
|
129
132
|
'entryAbility',
|
|
130
133
|
'indexPage',
|
|
131
134
|
'worker',
|
|
@@ -195,6 +198,7 @@ function withFileProvider(config: ExpoConfig, name: FileModName): ExpoConfig {
|
|
|
195
198
|
|
|
196
199
|
const context = { ...request, modFile: file, modFileExists: exists } as typeof value.modRequest;
|
|
197
200
|
const result = await next!({ ...value, modRequest: context, modResults: data });
|
|
201
|
+
|
|
198
202
|
assertModResults(name, result.modResults, JsonMods.has(name) ? 'json' : 'text');
|
|
199
203
|
|
|
200
204
|
if (!result.modRequest.introspect) {
|
|
@@ -239,6 +243,7 @@ async function readMediaMap(root: string): Promise<HarmonyMediaMap> {
|
|
|
239
243
|
const directory = await resolveHarmonyPath(root, relative);
|
|
240
244
|
|
|
241
245
|
media[scope] = {};
|
|
246
|
+
|
|
242
247
|
let names: string[] = [];
|
|
243
248
|
|
|
244
249
|
try {
|
|
@@ -292,6 +297,7 @@ async function writeMediaMap(
|
|
|
292
297
|
{ operation: 'harmony.media.write' }
|
|
293
298
|
);
|
|
294
299
|
}
|
|
300
|
+
|
|
295
301
|
if (!descriptor || typeof descriptor !== 'object' || Array.isArray(descriptor)) {
|
|
296
302
|
throw new HarmonyConfigPluginError(
|
|
297
303
|
'ERR_HARMONY_CONFIG_INVALID',
|
|
@@ -308,6 +314,7 @@ async function writeMediaMap(
|
|
|
308
314
|
{ operation: 'harmony.media.write' }
|
|
309
315
|
);
|
|
310
316
|
}
|
|
317
|
+
|
|
311
318
|
if (item.replaceBase !== undefined && typeof item.replaceBase !== 'boolean') {
|
|
312
319
|
throw new HarmonyConfigPluginError(
|
|
313
320
|
'ERR_HARMONY_CONFIG_INVALID',
|
|
@@ -414,6 +421,7 @@ function withResourceProvider(config: ExpoConfig, name: ResourceModName): ExpoCo
|
|
|
414
421
|
: {};
|
|
415
422
|
|
|
416
423
|
const result = await next!({ ...value, modRequest: request, modResults: data });
|
|
424
|
+
|
|
417
425
|
assertModResults(name, result.modResults, 'resource');
|
|
418
426
|
|
|
419
427
|
if (!result.modRequest.introspect) {
|
|
@@ -563,6 +571,7 @@ export function registerMod<T, Config extends ExpoConfigWithHarmony>(
|
|
|
563
571
|
action: action as unknown as Parameters<typeof withMod<T>>[1]['action'],
|
|
564
572
|
});
|
|
565
573
|
const interceptor = (carrier.mods as unknown as Record<string, Record<string, HarmonyModAction>>).harmony[name];
|
|
574
|
+
|
|
566
575
|
provider.expoHarmonyLateActions.push(interceptor);
|
|
567
576
|
|
|
568
577
|
return config;
|
|
@@ -605,6 +614,8 @@ export const withMedia: HarmonyConfigPlugin<HarmonyModAction<HarmonyMediaMap>>
|
|
|
605
614
|
= (config, action) => withHarmonyMod(config, ['media', action]);
|
|
606
615
|
export const withProfiles: HarmonyConfigPlugin<HarmonyModAction<HarmonyJson>>
|
|
607
616
|
= (config, action) => withHarmonyMod(config, ['profiles', action]);
|
|
617
|
+
export const withAbilityStage: HarmonyConfigPlugin<HarmonyModAction<string>>
|
|
618
|
+
= (config, action) => withHarmonyMod(config, ['abilityStage', action]);
|
|
608
619
|
export const withEntryAbility: HarmonyConfigPlugin<HarmonyModAction<string>>
|
|
609
620
|
= (config, action) => withHarmonyMod(config, ['entryAbility', action]);
|
|
610
621
|
export const withIndexPage: HarmonyConfigPlugin<HarmonyModAction<string>>
|
package/src/normalizeConfig.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
} from './config';
|
|
8
8
|
import { HarmonyConfigPluginError } from './errors';
|
|
9
9
|
import { toArgb } from './resources';
|
|
10
|
+
import { compareHarmonyApiVersions, parseHarmonySdkVersion } from './sdkVersion';
|
|
10
11
|
|
|
11
12
|
type HarmonyExpoConfig = ExpoConfigWithHarmony;
|
|
12
13
|
|
|
@@ -26,7 +27,7 @@ interface NormalizedHarmonyConfig {
|
|
|
26
27
|
querySchemes: string[];
|
|
27
28
|
signingConfigFile?: string;
|
|
28
29
|
skills: NonNullable<HarmonyConfig['skills']>;
|
|
29
|
-
targetApiVersion: number;
|
|
30
|
+
targetApiVersion: number | string;
|
|
30
31
|
targetSdkVersionString: string;
|
|
31
32
|
vendor: string;
|
|
32
33
|
versionCode: number;
|
|
@@ -45,16 +46,8 @@ const Orientations = new Set([
|
|
|
45
46
|
'auto_rotation',
|
|
46
47
|
]);
|
|
47
48
|
const DeviceTypes = new Set<HarmonyDeviceType>(['phone', 'tablet', '2in1']);
|
|
48
|
-
const MinimumApi =
|
|
49
|
+
const MinimumApi = 20;
|
|
49
50
|
const TargetApi = 24;
|
|
50
|
-
const SdkVersions = new Map([
|
|
51
|
-
[13, '5.0.1(13)'],
|
|
52
|
-
[14, '5.0.2(14)'],
|
|
53
|
-
[20, '6.0.0(20)'],
|
|
54
|
-
[21, '6.0.1(21)'],
|
|
55
|
-
[23, '6.1.0(23)'],
|
|
56
|
-
[24, '6.1.1(24)'],
|
|
57
|
-
]);
|
|
58
51
|
|
|
59
52
|
class HarmonyConfigError extends HarmonyConfigPluginError {
|
|
60
53
|
constructor(message: string) {
|
|
@@ -96,58 +89,6 @@ function normalizeColor(value: unknown, field: string, fallback: string): string
|
|
|
96
89
|
return toArgb(color);
|
|
97
90
|
}
|
|
98
91
|
|
|
99
|
-
function parseSdkApi(value: unknown, field: string): number | null {
|
|
100
|
-
if (value === undefined) return null;
|
|
101
|
-
if (typeof value === 'number' && Number.isSafeInteger(value) && value > 0) return value;
|
|
102
|
-
if (typeof value !== 'string') {
|
|
103
|
-
throw new HarmonyConfigError(`harmony.${field} must be a positive API number or an SDK label.`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const match = /\((\d+)\)$/u.exec(value.trim());
|
|
107
|
-
const api = Number(match?.[1]);
|
|
108
|
-
|
|
109
|
-
if (!match || !Number.isSafeInteger(api) || api <= 0) {
|
|
110
|
-
throw new HarmonyConfigError(
|
|
111
|
-
`harmony.${field} must end with its API level in parentheses, for example "6.0.0(20)".`
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return api;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function resolveSdkVersion(api: number, field: string, value?: unknown): string {
|
|
119
|
-
if (value !== undefined) {
|
|
120
|
-
if (typeof value === 'number' && Number.isSafeInteger(value) && value > 0) {
|
|
121
|
-
if (value !== api) {
|
|
122
|
-
throw new HarmonyConfigError(
|
|
123
|
-
`harmony.${field} is API ${value}, but the configured API level is ${api}.`
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
} else {
|
|
127
|
-
const version = readString(value, `harmony.${field}`);
|
|
128
|
-
const parsed = parseSdkApi(version, field);
|
|
129
|
-
|
|
130
|
-
if (parsed !== api) {
|
|
131
|
-
throw new HarmonyConfigError(
|
|
132
|
-
`harmony.${field} describes API ${parsed}, but the configured API level is ${api}.`
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return version;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const version = SdkVersions.get(api);
|
|
141
|
-
|
|
142
|
-
if (!version) {
|
|
143
|
-
throw new HarmonyConfigError(
|
|
144
|
-
`Harmony API ${api} has no built-in SDK label. Set harmony.${field} explicitly so new SDK releases do not require a package update.`
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return version;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
92
|
function normalizeStringArray<T extends string = string>(
|
|
152
93
|
value: unknown,
|
|
153
94
|
field: string,
|
|
@@ -286,24 +227,25 @@ function normalizeHarmonyConfig(config: HarmonyExpoConfig): NormalizedHarmonyCon
|
|
|
286
227
|
const version = readString(harmony.versionName, 'harmony.versionName', config.version || '1.0.0');
|
|
287
228
|
const code = readPositiveInteger(harmony.versionCode, 'versionCode', 1);
|
|
288
229
|
|
|
289
|
-
const target =
|
|
290
|
-
harmony.targetApiVersion,
|
|
291
|
-
'targetApiVersion',
|
|
292
|
-
parseSdkApi(harmony.targetSdkVersion, 'targetSdkVersion') ?? TargetApi
|
|
293
|
-
);
|
|
294
|
-
const compatible = readPositiveInteger(
|
|
295
|
-
parseSdkApi(harmony.compatibleSdkVersion, 'compatibleSdkVersion') ?? undefined,
|
|
296
|
-
'compatibleSdkVersion',
|
|
297
|
-
MinimumApi
|
|
230
|
+
const target = parseHarmonySdkVersion(
|
|
231
|
+
harmony.targetSdkVersion ?? harmony.targetApiVersion ?? TargetApi, 'harmony.targetSdkVersion'
|
|
298
232
|
);
|
|
299
233
|
|
|
300
|
-
if (
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
)
|
|
234
|
+
if (harmony.targetApiVersion !== undefined) {
|
|
235
|
+
const alias = parseHarmonySdkVersion(harmony.targetApiVersion, 'harmony.targetApiVersion');
|
|
236
|
+
|
|
237
|
+
if (compareHarmonyApiVersions(alias.api, target.api) !== 0) {
|
|
238
|
+
throw new HarmonyConfigError('harmony.targetApiVersion conflicts with harmony.targetSdkVersion.');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const compatible = parseHarmonySdkVersion(harmony.compatibleSdkVersion ?? MinimumApi, 'harmony.compatibleSdkVersion');
|
|
243
|
+
|
|
244
|
+
if (compareHarmonyApiVersions(compatible.api, MinimumApi) < 0) {
|
|
245
|
+
throw new HarmonyConfigError(`Harmony compatible API must be ${MinimumApi} or newer.`);
|
|
304
246
|
}
|
|
305
|
-
if (compatible >
|
|
306
|
-
throw new HarmonyConfigError('Harmony compatibleSdkVersion cannot exceed
|
|
247
|
+
if (compareHarmonyApiVersions(compatible.api, target.api) > 0) {
|
|
248
|
+
throw new HarmonyConfigError('Harmony compatibleSdkVersion cannot exceed targetSdkVersion.');
|
|
307
249
|
}
|
|
308
250
|
|
|
309
251
|
const orientation = harmony.orientation || config.orientation || 'default';
|
|
@@ -327,7 +269,7 @@ function normalizeHarmonyConfig(config: HarmonyExpoConfig): NormalizedHarmonyCon
|
|
|
327
269
|
: normalizeStringArray(harmony.abiFilters, 'harmony.abiFilters');
|
|
328
270
|
|
|
329
271
|
for (const abi of abis) {
|
|
330
|
-
if (
|
|
272
|
+
if (!['arm64-v8a', 'x86_64'].includes(abi)) {
|
|
331
273
|
throw new HarmonyConfigError(`Invalid Harmony ABI filter: ${abi}`);
|
|
332
274
|
}
|
|
333
275
|
}
|
|
@@ -338,6 +280,11 @@ function normalizeHarmonyConfig(config: HarmonyExpoConfig): NormalizedHarmonyCon
|
|
|
338
280
|
permissions.unshift({ name: 'ohos.permission.INTERNET' });
|
|
339
281
|
}
|
|
340
282
|
|
|
283
|
+
if (config.updates?.checkAutomatically === 'WIFI_ONLY'
|
|
284
|
+
&& !permissions.some(permission => permission.name === 'ohos.permission.GET_NETWORK_INFO')) {
|
|
285
|
+
permissions.push({ name: 'ohos.permission.GET_NETWORK_INFO' });
|
|
286
|
+
}
|
|
287
|
+
|
|
341
288
|
const schemes = readExpoSchemes(config);
|
|
342
289
|
let skills = (harmony.skills || []).map(normalizeSkill);
|
|
343
290
|
|
|
@@ -378,11 +325,7 @@ function normalizeHarmonyConfig(config: HarmonyExpoConfig): NormalizedHarmonyCon
|
|
|
378
325
|
abilityName: ability,
|
|
379
326
|
backgroundColor: background,
|
|
380
327
|
bundleName: bundle,
|
|
381
|
-
compatibleSdkVersionString:
|
|
382
|
-
compatible,
|
|
383
|
-
'compatibleSdkVersion',
|
|
384
|
-
harmony.compatibleSdkVersion
|
|
385
|
-
),
|
|
328
|
+
compatibleSdkVersionString: compatible.native,
|
|
386
329
|
deviceTypes: devices,
|
|
387
330
|
icon: harmony.icon || config.icon,
|
|
388
331
|
label: readString(harmony.label, 'harmony.label', config.name),
|
|
@@ -393,12 +336,8 @@ function normalizeHarmonyConfig(config: HarmonyExpoConfig): NormalizedHarmonyCon
|
|
|
393
336
|
querySchemes: queries,
|
|
394
337
|
signingConfigFile: signing,
|
|
395
338
|
skills,
|
|
396
|
-
targetApiVersion: target,
|
|
397
|
-
targetSdkVersionString:
|
|
398
|
-
target,
|
|
399
|
-
'targetSdkVersion',
|
|
400
|
-
harmony.targetSdkVersion
|
|
401
|
-
),
|
|
339
|
+
targetApiVersion: target.api,
|
|
340
|
+
targetSdkVersionString: target.native,
|
|
402
341
|
vendor: readString(harmony.vendor, 'harmony.vendor', 'expo-harmony'),
|
|
403
342
|
versionCode: code,
|
|
404
343
|
versionName: version,
|
package/src/paths.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface HarmonyManagedPaths {
|
|
|
14
14
|
readonly entryHvigor: string;
|
|
15
15
|
readonly moduleJson: string;
|
|
16
16
|
readonly profiles: string;
|
|
17
|
+
readonly abilityStage: string;
|
|
17
18
|
readonly entryAbility: string;
|
|
18
19
|
readonly indexPage: string;
|
|
19
20
|
readonly worker: string;
|
|
@@ -60,6 +61,7 @@ const ManagedPaths: HarmonyManagedPaths & { readonly nativeInputsStamp: string }
|
|
|
60
61
|
entryHvigor: 'entry/hvigorfile.ts',
|
|
61
62
|
moduleJson: 'entry/src/main/module.json5',
|
|
62
63
|
profiles: 'entry/src/main/resources/base/profile/main_pages.json',
|
|
64
|
+
abilityStage: 'entry/src/main/ets/abilitystage/EntryAbilityStage.ets',
|
|
63
65
|
entryAbility: 'entry/src/main/ets/entryability/EntryAbility.ets',
|
|
64
66
|
indexPage: 'entry/src/main/ets/pages/Index.ets',
|
|
65
67
|
worker: 'entry/src/main/ets/workers/RNOHWorker.ets',
|
|
@@ -114,6 +116,7 @@ export async function assertNoExternalSymlink(root: string, target: string): Pro
|
|
|
114
116
|
current = path.join(current, segment);
|
|
115
117
|
|
|
116
118
|
let stat: fs.Stats;
|
|
119
|
+
|
|
117
120
|
try {
|
|
118
121
|
stat = await fs.promises.lstat(current);
|
|
119
122
|
} catch (cause) {
|
|
@@ -173,6 +176,7 @@ export async function resolveProjectPath(
|
|
|
173
176
|
name: keyof HarmonyProjectPaths
|
|
174
177
|
): Promise<string> {
|
|
175
178
|
const candidates = ProjectPathCandidates[name] || [ProjectPaths[name]];
|
|
179
|
+
|
|
176
180
|
for (const candidate of candidates) {
|
|
177
181
|
if (fs.existsSync(path.join(root, candidate))) return resolveHarmonyPath(root, candidate);
|
|
178
182
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { HarmonyConfigPluginError } from './errors';
|
|
2
|
+
|
|
3
|
+
export interface HarmonySdkVersion {
|
|
4
|
+
api: number | string;
|
|
5
|
+
native: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const Labels = new Map([
|
|
9
|
+
[13, '5.0.1(13)'], [14, '5.0.2(14)'], [20, '6.0.0(20)'],
|
|
10
|
+
[21, '6.0.1(21)'], [23, '6.1.0(23)'], [24, '6.1.1(24)'],
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
export function parseHarmonySdkVersion(value: unknown, field = 'SDK version'): HarmonySdkVersion {
|
|
14
|
+
if (typeof value === 'number') {
|
|
15
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 999) {
|
|
16
|
+
throw new HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `${field}: expected a positive API level.`);
|
|
17
|
+
}
|
|
18
|
+
if (value >= 26) return { api: `${value}.0.0`, native: `${value}.0.0` };
|
|
19
|
+
|
|
20
|
+
const native = Labels.get(value);
|
|
21
|
+
|
|
22
|
+
if (!native) {
|
|
23
|
+
throw new HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `${field}: API ${value} requires an explicit HarmonyOS SDK label.`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return { api: value, native };
|
|
27
|
+
}
|
|
28
|
+
if (typeof value !== 'string') {
|
|
29
|
+
throw new HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `${field}: expected an API number or HarmonyOS SDK version.`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const version = value.trim();
|
|
33
|
+
const dotted = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(version);
|
|
34
|
+
|
|
35
|
+
if (dotted && Number(dotted[1]) >= 26
|
|
36
|
+
&& dotted.slice(1).every(part => String(Number(part)) === part)) {
|
|
37
|
+
return { api: version, native: version };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const label = /^\d+\.\d+\.\d+\(([1-9]\d*)\)$/.exec(version);
|
|
41
|
+
|
|
42
|
+
if (label && Number(label[1]) < 26) {
|
|
43
|
+
const api = Number(label[1]);
|
|
44
|
+
const known = Labels.get(api);
|
|
45
|
+
|
|
46
|
+
if (known && known !== version) {
|
|
47
|
+
throw new HarmonyConfigPluginError('ERR_HARMONY_CONFIG_INVALID', `${field}: API ${api} uses SDK label ${known}.`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { api, native: version };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new HarmonyConfigPluginError(
|
|
54
|
+
'ERR_HARMONY_CONFIG_INVALID',
|
|
55
|
+
`${field}: expected an SDK label such as "6.1.1(24)", or a dotted API version such as "26.0.0".`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function compareHarmonyApiVersions(left: number | string, right: number | string): number {
|
|
60
|
+
const a = String(left).split('.').map(Number);
|
|
61
|
+
const b = String(right).split('.').map(Number);
|
|
62
|
+
|
|
63
|
+
for (let i = 0; i < 3; i++) {
|
|
64
|
+
const delta = (a[i] ?? 0) - (b[i] ?? 0);
|
|
65
|
+
if (delta) return delta;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return 0;
|
|
69
|
+
}
|