expo-harmony-toolkit 1.5.0
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/LICENSE +21 -0
- package/README.en.md +197 -0
- package/README.md +197 -0
- package/app.plugin.js +1 -0
- package/bin/expo-harmony.js +9 -0
- package/build/cli.d.ts +1 -0
- package/build/cli.js +56 -0
- package/build/commands/buildHap.d.ts +5 -0
- package/build/commands/buildHap.js +26 -0
- package/build/commands/bundle.d.ts +4 -0
- package/build/commands/bundle.js +18 -0
- package/build/commands/doctor.d.ts +7 -0
- package/build/commands/doctor.js +24 -0
- package/build/commands/env.d.ts +5 -0
- package/build/commands/env.js +22 -0
- package/build/commands/init.d.ts +5 -0
- package/build/commands/init.js +29 -0
- package/build/commands/syncTemplate.d.ts +5 -0
- package/build/commands/syncTemplate.js +23 -0
- package/build/core/build.d.ts +25 -0
- package/build/core/build.js +434 -0
- package/build/core/constants.d.ts +21 -0
- package/build/core/constants.js +32 -0
- package/build/core/env.d.ts +8 -0
- package/build/core/env.js +185 -0
- package/build/core/metadata.d.ts +9 -0
- package/build/core/metadata.js +54 -0
- package/build/core/project.d.ts +18 -0
- package/build/core/project.js +206 -0
- package/build/core/report.d.ts +4 -0
- package/build/core/report.js +319 -0
- package/build/core/template.d.ts +6 -0
- package/build/core/template.js +1030 -0
- package/build/data/compatibilityMatrix.d.ts +2 -0
- package/build/data/compatibilityMatrix.js +99 -0
- package/build/data/dependencyCatalog.d.ts +2 -0
- package/build/data/dependencyCatalog.js +108 -0
- package/build/data/uiStack.d.ts +39 -0
- package/build/data/uiStack.js +48 -0
- package/build/data/validatedMatrices.d.ts +3 -0
- package/build/data/validatedMatrices.js +94 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +27 -0
- package/build/plugin.d.ts +7 -0
- package/build/plugin.js +76 -0
- package/build/types.d.ts +182 -0
- package/build/types.js +2 -0
- package/docs/cli-build.md +99 -0
- package/docs/npm-release.md +89 -0
- package/docs/official-app-shell-sample.md +39 -0
- package/docs/official-minimal-sample.md +32 -0
- package/docs/official-ui-stack-sample.md +77 -0
- package/docs/roadmap.md +67 -0
- package/docs/signing-and-release.md +57 -0
- package/docs/support-matrix.md +149 -0
- package/package.json +78 -0
- package/templates/harmony/AppScope/app.json5 +10 -0
- package/templates/harmony/AppScope/resources/base/element/string.json +8 -0
- package/templates/harmony/AppScope/resources/base/media/app_icon.png +0 -0
- package/templates/harmony/README.md +31 -0
- package/templates/harmony/build-profile.json5 +37 -0
- package/templates/harmony/codelinter.json +19 -0
- package/templates/harmony/entry/build-profile.json5 +18 -0
- package/templates/harmony/entry/hvigorfile.ts +13 -0
- package/templates/harmony/entry/oh-package.json5 +14 -0
- package/templates/harmony/entry/src/main/cpp/CMakeLists.txt +55 -0
- package/templates/harmony/entry/src/main/cpp/PackageProvider.cpp +12 -0
- package/templates/harmony/entry/src/main/ets/PackageProvider.ets +6 -0
- package/templates/harmony/entry/src/main/ets/entryability/EntryAbility.ets +11 -0
- package/templates/harmony/entry/src/main/ets/pages/Index.ets +42 -0
- package/templates/harmony/entry/src/main/ets/workers/RNOHWorker.ets +8 -0
- package/templates/harmony/entry/src/main/module.json5 +31 -0
- package/templates/harmony/entry/src/main/resources/base/element/color.json +8 -0
- package/templates/harmony/entry/src/main/resources/base/element/string.json +16 -0
- package/templates/harmony/entry/src/main/resources/base/media/background.png +0 -0
- package/templates/harmony/entry/src/main/resources/base/media/foreground.png +0 -0
- package/templates/harmony/entry/src/main/resources/base/media/layered_image.json +6 -0
- package/templates/harmony/entry/src/main/resources/base/media/startIcon.png +0 -0
- package/templates/harmony/entry/src/main/resources/base/profile/main_pages.json +5 -0
- package/templates/harmony/entry/src/main/resources/rawfile/.gitkeep +1 -0
- package/templates/harmony/hvigor/hvigor-config.json5 +10 -0
- package/templates/harmony/hvigorfile.ts +7 -0
- package/templates/harmony/oh-package.json5 +13 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildEnvReport = buildEnvReport;
|
|
7
|
+
exports.renderEnvReport = renderEnvReport;
|
|
8
|
+
exports.getStrictEnvExitCode = getStrictEnvExitCode;
|
|
9
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const constants_1 = require("./constants");
|
|
12
|
+
const project_1 = require("./project");
|
|
13
|
+
const DEFAULT_DEVECO_STUDIO_CANDIDATES = [
|
|
14
|
+
'/Applications/DevEco-Studio.app',
|
|
15
|
+
path_1.default.join(process.env.HOME ?? '', 'Applications', 'DevEco-Studio.app'),
|
|
16
|
+
];
|
|
17
|
+
async function buildEnvReport(projectRoot, options = {}) {
|
|
18
|
+
const runtimeEnv = options.env ?? process.env;
|
|
19
|
+
const useDefaultLookups = runtimeEnv.EXPO_HARMONY_DISABLE_DEFAULT_PATHS !== '1';
|
|
20
|
+
const resolvedProjectRoot = await (0, project_1.resolveProjectRoot)(projectRoot);
|
|
21
|
+
await (0, project_1.ensureProjectPackageJsonPath)(resolvedProjectRoot);
|
|
22
|
+
const devecoStudioPath = await resolveExistingPath([
|
|
23
|
+
runtimeEnv.EXPO_HARMONY_DEVECO_STUDIO_PATH,
|
|
24
|
+
runtimeEnv.DEVECO_STUDIO_PATH,
|
|
25
|
+
runtimeEnv.DEVECO_STUDIO_HOME,
|
|
26
|
+
...(useDefaultLookups ? DEFAULT_DEVECO_STUDIO_CANDIDATES : []),
|
|
27
|
+
]);
|
|
28
|
+
const sdkRootCandidate = runtimeEnv.EXPO_HARMONY_DEVECO_SDK_ROOT ??
|
|
29
|
+
runtimeEnv.DEVECO_SDK_HOME ??
|
|
30
|
+
runtimeEnv.OHOS_SDK_HOME ??
|
|
31
|
+
(devecoStudioPath ? path_1.default.join(devecoStudioPath, 'Contents', 'sdk') : null);
|
|
32
|
+
const sdkRoot = await resolveExistingPath([sdkRootCandidate]);
|
|
33
|
+
const javaPath = await resolveExistingPath([
|
|
34
|
+
runtimeEnv.EXPO_HARMONY_JAVA_PATH,
|
|
35
|
+
runtimeEnv.JAVA_HOME ? path_1.default.join(runtimeEnv.JAVA_HOME, 'bin', 'java') : null,
|
|
36
|
+
useDefaultLookups ? findExecutableInPath('java', runtimeEnv) : null,
|
|
37
|
+
]);
|
|
38
|
+
const ohpmPath = await resolveExistingPath([
|
|
39
|
+
runtimeEnv.EXPO_HARMONY_OHPM_PATH,
|
|
40
|
+
devecoStudioPath ? path_1.default.join(devecoStudioPath, 'Contents', 'tools', 'ohpm', 'bin', 'ohpm') : null,
|
|
41
|
+
useDefaultLookups ? findExecutableInPath('ohpm', runtimeEnv) : null,
|
|
42
|
+
]);
|
|
43
|
+
const hvigorPath = await resolveExistingPath([
|
|
44
|
+
runtimeEnv.EXPO_HARMONY_HVIGOR_PATH,
|
|
45
|
+
devecoStudioPath ? path_1.default.join(devecoStudioPath, 'Contents', 'tools', 'hvigor', 'bin', 'hvigorw.js') : null,
|
|
46
|
+
devecoStudioPath ? path_1.default.join(devecoStudioPath, 'Contents', 'tools', 'hvigor', 'bin', 'hvigorw') : null,
|
|
47
|
+
useDefaultLookups ? findExecutableInPath('hvigorw', runtimeEnv) : null,
|
|
48
|
+
useDefaultLookups ? findExecutableInPath('hvigor', runtimeEnv) : null,
|
|
49
|
+
]);
|
|
50
|
+
const hdcPath = await resolveExistingPath([
|
|
51
|
+
runtimeEnv.EXPO_HARMONY_HDC_PATH,
|
|
52
|
+
sdkRoot ? path_1.default.join(sdkRoot, 'default', 'openharmony', 'toolchains', 'hdc') : null,
|
|
53
|
+
useDefaultLookups ? findExecutableInPath('hdc', runtimeEnv) : null,
|
|
54
|
+
]);
|
|
55
|
+
const harmonyProjectRootCandidate = path_1.default.join(resolvedProjectRoot, 'harmony');
|
|
56
|
+
const harmonyProjectRoot = (await fs_extra_1.default.pathExists(harmonyProjectRootCandidate))
|
|
57
|
+
? harmonyProjectRootCandidate
|
|
58
|
+
: null;
|
|
59
|
+
const signingConfigured = harmonyProjectRoot
|
|
60
|
+
? await detectSigningConfiguration(path_1.default.join(harmonyProjectRoot, 'build-profile.json5'))
|
|
61
|
+
: false;
|
|
62
|
+
const blockingIssues = [];
|
|
63
|
+
const advisories = [];
|
|
64
|
+
const warnings = [];
|
|
65
|
+
if (!devecoStudioPath || !sdkRoot) {
|
|
66
|
+
blockingIssues.push({
|
|
67
|
+
code: 'env.deveco_sdk.missing',
|
|
68
|
+
message: 'DevEco Studio or its Harmony SDK could not be located. Set EXPO_HARMONY_DEVECO_STUDIO_PATH or install DevEco Studio locally.',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (!hvigorPath) {
|
|
72
|
+
blockingIssues.push({
|
|
73
|
+
code: 'env.hvigor.missing',
|
|
74
|
+
message: 'Hvigor could not be located. Install the DevEco hvigor toolchain or set EXPO_HARMONY_HVIGOR_PATH.',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (!hdcPath) {
|
|
78
|
+
blockingIssues.push({
|
|
79
|
+
code: 'env.hdc.missing',
|
|
80
|
+
message: 'The Harmony device connector hdc could not be located. Install the DevEco device toolchain or set EXPO_HARMONY_HDC_PATH.',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (!javaPath) {
|
|
84
|
+
warnings.push('Java could not be located from JAVA_HOME or PATH. Hvigor may still fail even when the DevEco SDK is present.');
|
|
85
|
+
}
|
|
86
|
+
if (!ohpmPath) {
|
|
87
|
+
warnings.push('ohpm could not be located. CLI HAP builds will fail until the DevEco package manager is available.');
|
|
88
|
+
}
|
|
89
|
+
if (!harmonyProjectRoot) {
|
|
90
|
+
warnings.push('Harmony sidecar files are not present yet. Run expo-harmony init before bundle or build-hap.');
|
|
91
|
+
}
|
|
92
|
+
else if (!signingConfigured) {
|
|
93
|
+
advisories.push({
|
|
94
|
+
code: 'env.signing.missing',
|
|
95
|
+
message: 'Harmony build-profile.json5 does not declare any signingConfigs yet. Debug GUI flows may still work, but release builds require signing.',
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
generatedAt: new Date().toISOString(),
|
|
100
|
+
projectRoot: resolvedProjectRoot,
|
|
101
|
+
toolkitVersion: constants_1.TOOLKIT_VERSION,
|
|
102
|
+
devecoStudioPath,
|
|
103
|
+
sdkRoot,
|
|
104
|
+
harmonyProjectRoot,
|
|
105
|
+
javaPath,
|
|
106
|
+
ohpmPath,
|
|
107
|
+
hvigorPath,
|
|
108
|
+
hdcPath,
|
|
109
|
+
signingConfigured,
|
|
110
|
+
status: blockingIssues.length === 0 ? 'ready' : 'blocked',
|
|
111
|
+
blockingIssues,
|
|
112
|
+
advisories,
|
|
113
|
+
warnings,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function renderEnvReport(report) {
|
|
117
|
+
const lines = [
|
|
118
|
+
'Expo Harmony env report',
|
|
119
|
+
`Project: ${report.projectRoot}`,
|
|
120
|
+
`Status: ${report.status}`,
|
|
121
|
+
`DevEco Studio: ${report.devecoStudioPath ?? 'not found'}`,
|
|
122
|
+
`SDK root: ${report.sdkRoot ?? 'not found'}`,
|
|
123
|
+
`Harmony project: ${report.harmonyProjectRoot ?? 'not found'}`,
|
|
124
|
+
`Java: ${report.javaPath ?? 'not found'}`,
|
|
125
|
+
`ohpm: ${report.ohpmPath ?? 'not found'}`,
|
|
126
|
+
`hvigor: ${report.hvigorPath ?? 'not found'}`,
|
|
127
|
+
`hdc: ${report.hdcPath ?? 'not found'}`,
|
|
128
|
+
`Signing: ${report.signingConfigured ? 'configured' : 'not configured'}`,
|
|
129
|
+
];
|
|
130
|
+
if (report.blockingIssues.length > 0) {
|
|
131
|
+
lines.push('', 'Blocking issues:', ...report.blockingIssues.map(renderIssueLine));
|
|
132
|
+
}
|
|
133
|
+
if (report.advisories.length > 0) {
|
|
134
|
+
lines.push('', 'Advisories:', ...report.advisories.map(renderIssueLine));
|
|
135
|
+
}
|
|
136
|
+
if (report.warnings.length > 0) {
|
|
137
|
+
lines.push('', 'Warnings:', ...report.warnings.map((warning) => `- ${warning}`));
|
|
138
|
+
}
|
|
139
|
+
return lines.join('\n');
|
|
140
|
+
}
|
|
141
|
+
function getStrictEnvExitCode() {
|
|
142
|
+
return constants_1.STRICT_ENV_EXIT_CODE;
|
|
143
|
+
}
|
|
144
|
+
async function resolveExistingPath(candidates) {
|
|
145
|
+
for (const candidate of candidates) {
|
|
146
|
+
if (!candidate) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const resolvedCandidate = path_1.default.resolve(candidate);
|
|
150
|
+
if (await fs_extra_1.default.pathExists(resolvedCandidate)) {
|
|
151
|
+
return resolvedCandidate;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
function findExecutableInPath(executableName, runtimeEnv) {
|
|
157
|
+
const pathValue = runtimeEnv.PATH;
|
|
158
|
+
if (!pathValue) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
for (const directory of pathValue.split(path_1.default.delimiter)) {
|
|
162
|
+
if (!directory) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const candidatePath = path_1.default.join(directory, executableName);
|
|
166
|
+
if (fs_extra_1.default.existsSync(candidatePath)) {
|
|
167
|
+
return candidatePath;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
async function detectSigningConfiguration(buildProfilePath) {
|
|
173
|
+
if (!(await fs_extra_1.default.pathExists(buildProfilePath))) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
const contents = await fs_extra_1.default.readFile(buildProfilePath, 'utf8');
|
|
177
|
+
const signingConfigsMatch = contents.match(/signingConfigs\s*:\s*\[([\s\S]*?)\]/m);
|
|
178
|
+
if (!signingConfigsMatch) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
return signingConfigsMatch[1].trim().length > 0;
|
|
182
|
+
}
|
|
183
|
+
function renderIssueLine(issue) {
|
|
184
|
+
return `- ${issue.code}: ${issue.message}${issue.subject ? ` (${issue.subject})` : ''}`;
|
|
185
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BuildReport, EnvReport, ToolkitConfig, ToolkitManifest } from '../types';
|
|
2
|
+
export declare function getManifestPath(projectRoot: string): string;
|
|
3
|
+
export declare function getToolkitConfigPath(projectRoot: string): string;
|
|
4
|
+
export declare function getEnvReportPath(projectRoot: string): string;
|
|
5
|
+
export declare function getBuildReportPath(projectRoot: string): string;
|
|
6
|
+
export declare function readManifest(projectRoot: string): Promise<ToolkitManifest | null>;
|
|
7
|
+
export declare function readToolkitConfig(projectRoot: string): Promise<ToolkitConfig | null>;
|
|
8
|
+
export declare function writeEnvReport(projectRoot: string, report: EnvReport, outputPath?: string): Promise<string>;
|
|
9
|
+
export declare function writeBuildReport(projectRoot: string, report: BuildReport, outputPath?: string): Promise<string>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getManifestPath = getManifestPath;
|
|
7
|
+
exports.getToolkitConfigPath = getToolkitConfigPath;
|
|
8
|
+
exports.getEnvReportPath = getEnvReportPath;
|
|
9
|
+
exports.getBuildReportPath = getBuildReportPath;
|
|
10
|
+
exports.readManifest = readManifest;
|
|
11
|
+
exports.readToolkitConfig = readToolkitConfig;
|
|
12
|
+
exports.writeEnvReport = writeEnvReport;
|
|
13
|
+
exports.writeBuildReport = writeBuildReport;
|
|
14
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const constants_1 = require("./constants");
|
|
17
|
+
function getManifestPath(projectRoot) {
|
|
18
|
+
return path_1.default.join(projectRoot, constants_1.GENERATED_DIR, constants_1.MANIFEST_FILENAME);
|
|
19
|
+
}
|
|
20
|
+
function getToolkitConfigPath(projectRoot) {
|
|
21
|
+
return path_1.default.join(projectRoot, constants_1.GENERATED_DIR, constants_1.TOOLKIT_CONFIG_FILENAME);
|
|
22
|
+
}
|
|
23
|
+
function getEnvReportPath(projectRoot) {
|
|
24
|
+
return path_1.default.join(projectRoot, constants_1.GENERATED_DIR, constants_1.ENV_REPORT_FILENAME);
|
|
25
|
+
}
|
|
26
|
+
function getBuildReportPath(projectRoot) {
|
|
27
|
+
return path_1.default.join(projectRoot, constants_1.GENERATED_DIR, constants_1.BUILD_REPORT_FILENAME);
|
|
28
|
+
}
|
|
29
|
+
async function readManifest(projectRoot) {
|
|
30
|
+
const manifestPath = getManifestPath(projectRoot);
|
|
31
|
+
if (!(await fs_extra_1.default.pathExists(manifestPath))) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return (await fs_extra_1.default.readJson(manifestPath));
|
|
35
|
+
}
|
|
36
|
+
async function readToolkitConfig(projectRoot) {
|
|
37
|
+
const toolkitConfigPath = getToolkitConfigPath(projectRoot);
|
|
38
|
+
if (!(await fs_extra_1.default.pathExists(toolkitConfigPath))) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return (await fs_extra_1.default.readJson(toolkitConfigPath));
|
|
42
|
+
}
|
|
43
|
+
async function writeEnvReport(projectRoot, report, outputPath) {
|
|
44
|
+
const resolvedOutputPath = outputPath ?? getEnvReportPath(projectRoot);
|
|
45
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(resolvedOutputPath));
|
|
46
|
+
await fs_extra_1.default.writeJson(resolvedOutputPath, report, { spaces: 2 });
|
|
47
|
+
return resolvedOutputPath;
|
|
48
|
+
}
|
|
49
|
+
async function writeBuildReport(projectRoot, report, outputPath) {
|
|
50
|
+
const resolvedOutputPath = outputPath ?? getBuildReportPath(projectRoot);
|
|
51
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(resolvedOutputPath));
|
|
52
|
+
await fs_extra_1.default.writeJson(resolvedOutputPath, report, { spaces: 2 });
|
|
53
|
+
return resolvedOutputPath;
|
|
54
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { DependencySource, ExpoHarmonyPluginProps, HarmonyIdentifiers, LoadedProject, PackageJson } from '../types';
|
|
2
|
+
export declare function resolveProjectRoot(projectRoot?: string): Promise<string>;
|
|
3
|
+
export declare function loadProject(projectRoot: string): Promise<LoadedProject>;
|
|
4
|
+
export declare function ensureProjectPackageJsonPath(projectRoot: string): Promise<string>;
|
|
5
|
+
export declare function findAppConfigPath(projectRoot: string): Promise<string | null>;
|
|
6
|
+
export declare function collectDeclaredDependencies(packageJson: PackageJson): Array<{
|
|
7
|
+
name: string;
|
|
8
|
+
version: string;
|
|
9
|
+
source: DependencySource;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function hasDeclaredDependency(packageJson: PackageJson, dependencyName: string): boolean;
|
|
12
|
+
export declare function collectExpoPlugins(expoConfig: Record<string, any>): string[];
|
|
13
|
+
export declare function collectExpoSchemes(expoConfig: Record<string, any>): string[];
|
|
14
|
+
export declare function deriveHarmonyIdentifiers(expoConfig: Record<string, any>, packageJson?: PackageJson, props?: ExpoHarmonyPluginProps): HarmonyIdentifiers;
|
|
15
|
+
export declare function detectExpoSdkVersion(packageJson: PackageJson): number | null;
|
|
16
|
+
export declare function getExpoSdkWarning(expoSdkVersion: number | null): string | null;
|
|
17
|
+
export declare function resolveRnohHvigorPluginFilename(projectRoot: string): Promise<string>;
|
|
18
|
+
export declare function createGeneratedSha(contents: string | Buffer): string;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveProjectRoot = resolveProjectRoot;
|
|
7
|
+
exports.loadProject = loadProject;
|
|
8
|
+
exports.ensureProjectPackageJsonPath = ensureProjectPackageJsonPath;
|
|
9
|
+
exports.findAppConfigPath = findAppConfigPath;
|
|
10
|
+
exports.collectDeclaredDependencies = collectDeclaredDependencies;
|
|
11
|
+
exports.hasDeclaredDependency = hasDeclaredDependency;
|
|
12
|
+
exports.collectExpoPlugins = collectExpoPlugins;
|
|
13
|
+
exports.collectExpoSchemes = collectExpoSchemes;
|
|
14
|
+
exports.deriveHarmonyIdentifiers = deriveHarmonyIdentifiers;
|
|
15
|
+
exports.detectExpoSdkVersion = detectExpoSdkVersion;
|
|
16
|
+
exports.getExpoSdkWarning = getExpoSdkWarning;
|
|
17
|
+
exports.resolveRnohHvigorPluginFilename = resolveRnohHvigorPluginFilename;
|
|
18
|
+
exports.createGeneratedSha = createGeneratedSha;
|
|
19
|
+
const config_1 = require("@expo/config");
|
|
20
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
21
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
22
|
+
const path_1 = __importDefault(require("path"));
|
|
23
|
+
const semver_1 = __importDefault(require("semver"));
|
|
24
|
+
const constants_1 = require("./constants");
|
|
25
|
+
const APP_CONFIG_CANDIDATES = [
|
|
26
|
+
'app.json',
|
|
27
|
+
'app.config.ts',
|
|
28
|
+
'app.config.js',
|
|
29
|
+
'app.config.mjs',
|
|
30
|
+
'app.config.cjs',
|
|
31
|
+
];
|
|
32
|
+
async function resolveProjectRoot(projectRoot) {
|
|
33
|
+
return path_1.default.resolve(projectRoot ?? process.cwd());
|
|
34
|
+
}
|
|
35
|
+
async function loadProject(projectRoot) {
|
|
36
|
+
const resolvedRoot = await resolveProjectRoot(projectRoot);
|
|
37
|
+
const packageJsonPath = await ensureProjectPackageJsonPath(resolvedRoot);
|
|
38
|
+
const packageJson = (await fs_extra_1.default.readJson(packageJsonPath));
|
|
39
|
+
const appConfigPath = await findAppConfigPath(resolvedRoot);
|
|
40
|
+
if (!appConfigPath && !hasExpoDependency(packageJson)) {
|
|
41
|
+
throw new Error(`No Expo app config found in ${resolvedRoot}. Expected app.json/app.config.* or an expo dependency.`);
|
|
42
|
+
}
|
|
43
|
+
const expoConfig = await readExpoConfig(resolvedRoot, appConfigPath);
|
|
44
|
+
return {
|
|
45
|
+
projectRoot: resolvedRoot,
|
|
46
|
+
packageJson,
|
|
47
|
+
expoConfig,
|
|
48
|
+
appConfigPath,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function ensureProjectPackageJsonPath(projectRoot) {
|
|
52
|
+
const resolvedRoot = await resolveProjectRoot(projectRoot);
|
|
53
|
+
const packageJsonPath = path_1.default.join(resolvedRoot, 'package.json');
|
|
54
|
+
if (!(await fs_extra_1.default.pathExists(packageJsonPath))) {
|
|
55
|
+
throw new Error(`No package.json found at ${resolvedRoot}`);
|
|
56
|
+
}
|
|
57
|
+
return packageJsonPath;
|
|
58
|
+
}
|
|
59
|
+
async function findAppConfigPath(projectRoot) {
|
|
60
|
+
for (const candidate of APP_CONFIG_CANDIDATES) {
|
|
61
|
+
const candidatePath = path_1.default.join(projectRoot, candidate);
|
|
62
|
+
if (await fs_extra_1.default.pathExists(candidatePath)) {
|
|
63
|
+
return candidatePath;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
function collectDeclaredDependencies(packageJson) {
|
|
69
|
+
const entries = [];
|
|
70
|
+
for (const [source, section] of [
|
|
71
|
+
['dependency', packageJson.dependencies],
|
|
72
|
+
['devDependency', packageJson.devDependencies],
|
|
73
|
+
['peerDependency', packageJson.peerDependencies],
|
|
74
|
+
]) {
|
|
75
|
+
for (const [name, version] of Object.entries(section ?? {})) {
|
|
76
|
+
entries.push({ name, version, source });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
80
|
+
}
|
|
81
|
+
function hasDeclaredDependency(packageJson, dependencyName) {
|
|
82
|
+
return Boolean(packageJson.dependencies?.[dependencyName] ||
|
|
83
|
+
packageJson.devDependencies?.[dependencyName] ||
|
|
84
|
+
packageJson.peerDependencies?.[dependencyName]);
|
|
85
|
+
}
|
|
86
|
+
function collectExpoPlugins(expoConfig) {
|
|
87
|
+
const plugins = expoConfig.plugins;
|
|
88
|
+
if (!Array.isArray(plugins)) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
const names = new Set();
|
|
92
|
+
for (const plugin of plugins) {
|
|
93
|
+
if (typeof plugin === 'string') {
|
|
94
|
+
names.add(plugin);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (Array.isArray(plugin) && typeof plugin[0] === 'string') {
|
|
98
|
+
names.add(plugin[0]);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return [...names].sort((left, right) => left.localeCompare(right));
|
|
102
|
+
}
|
|
103
|
+
function collectExpoSchemes(expoConfig) {
|
|
104
|
+
const rawScheme = expoConfig.scheme;
|
|
105
|
+
if (typeof rawScheme === 'string' && rawScheme.trim().length > 0) {
|
|
106
|
+
return [rawScheme.trim()];
|
|
107
|
+
}
|
|
108
|
+
if (Array.isArray(rawScheme)) {
|
|
109
|
+
return rawScheme
|
|
110
|
+
.filter((value) => typeof value === 'string' && value.trim().length > 0)
|
|
111
|
+
.map((value) => value.trim())
|
|
112
|
+
.sort((left, right) => left.localeCompare(right));
|
|
113
|
+
}
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
function deriveHarmonyIdentifiers(expoConfig, packageJson, props) {
|
|
117
|
+
const slug = sanitizeSlug(expoConfig.slug ?? packageJson?.name ?? 'expo-harmony-app');
|
|
118
|
+
const appName = String(expoConfig.name ?? packageJson?.name ?? 'Expo Harmony App');
|
|
119
|
+
const androidPackage = normalizeIdentifier(expoConfig.android?.package);
|
|
120
|
+
const iosBundleIdentifier = normalizeIdentifier(expoConfig.ios?.bundleIdentifier);
|
|
121
|
+
return {
|
|
122
|
+
appName,
|
|
123
|
+
slug,
|
|
124
|
+
bundleName: normalizeIdentifier(props?.bundleName) ??
|
|
125
|
+
androidPackage ??
|
|
126
|
+
iosBundleIdentifier ??
|
|
127
|
+
`com.expoharmony.${slug}`,
|
|
128
|
+
entryModuleName: sanitizeIdentifierSegment(props?.entryModuleName ?? 'entry'),
|
|
129
|
+
androidPackage,
|
|
130
|
+
iosBundleIdentifier,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function detectExpoSdkVersion(packageJson) {
|
|
134
|
+
const rawVersion = packageJson.dependencies?.expo ??
|
|
135
|
+
packageJson.devDependencies?.expo ??
|
|
136
|
+
packageJson.peerDependencies?.expo;
|
|
137
|
+
if (!rawVersion) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
const coerced = semver_1.default.coerce(rawVersion);
|
|
141
|
+
return coerced?.major ?? null;
|
|
142
|
+
}
|
|
143
|
+
function getExpoSdkWarning(expoSdkVersion) {
|
|
144
|
+
if (expoSdkVersion === null) {
|
|
145
|
+
return 'Expo SDK version could not be detected from package.json.';
|
|
146
|
+
}
|
|
147
|
+
if (!constants_1.SUPPORTED_EXPO_SDKS.includes(expoSdkVersion)) {
|
|
148
|
+
return `Expo SDK ${expoSdkVersion} detected. The toolkit currently validates only against Expo SDK ${constants_1.SUPPORTED_EXPO_SDKS.join(' and ')} project shapes.`;
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
async function resolveRnohHvigorPluginFilename(projectRoot) {
|
|
153
|
+
const harmonyCliDir = path_1.default.join(projectRoot, 'node_modules', '@react-native-oh', 'react-native-harmony-cli', 'harmony');
|
|
154
|
+
if (!(await fs_extra_1.default.pathExists(harmonyCliDir))) {
|
|
155
|
+
return constants_1.DEFAULT_HVIGOR_PLUGIN_FILENAME;
|
|
156
|
+
}
|
|
157
|
+
const entries = await fs_extra_1.default.readdir(harmonyCliDir);
|
|
158
|
+
const tgzEntry = entries.find((entry) => entry.startsWith('rnoh-hvigor-plugin-') && entry.endsWith('.tgz'));
|
|
159
|
+
return tgzEntry ?? constants_1.DEFAULT_HVIGOR_PLUGIN_FILENAME;
|
|
160
|
+
}
|
|
161
|
+
function createGeneratedSha(contents) {
|
|
162
|
+
return node_crypto_1.default.createHash('sha1').update(contents).digest('hex');
|
|
163
|
+
}
|
|
164
|
+
async function readExpoConfig(projectRoot, appConfigPath) {
|
|
165
|
+
try {
|
|
166
|
+
const { exp } = (0, config_1.getConfig)(projectRoot, {
|
|
167
|
+
skipSDKVersionRequirement: true,
|
|
168
|
+
isPublicConfig: true,
|
|
169
|
+
});
|
|
170
|
+
return exp;
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
if (!appConfigPath || !appConfigPath.endsWith('app.json')) {
|
|
174
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
175
|
+
throw new Error(`Failed to load Expo config: ${message}`);
|
|
176
|
+
}
|
|
177
|
+
const appJson = await fs_extra_1.default.readJson(appConfigPath);
|
|
178
|
+
return (appJson.expo ?? appJson);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function hasExpoDependency(packageJson) {
|
|
182
|
+
return Boolean(packageJson.dependencies?.expo ||
|
|
183
|
+
packageJson.devDependencies?.expo ||
|
|
184
|
+
packageJson.peerDependencies?.expo);
|
|
185
|
+
}
|
|
186
|
+
function sanitizeSlug(value) {
|
|
187
|
+
return String(value)
|
|
188
|
+
.trim()
|
|
189
|
+
.toLowerCase()
|
|
190
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
191
|
+
.replace(/^-+|-+$/g, '') || 'expo-harmony-app';
|
|
192
|
+
}
|
|
193
|
+
function normalizeIdentifier(value) {
|
|
194
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const trimmed = value.trim();
|
|
198
|
+
return /^[a-zA-Z][a-zA-Z0-9_.]+$/.test(trimmed) ? trimmed : null;
|
|
199
|
+
}
|
|
200
|
+
function sanitizeIdentifierSegment(value) {
|
|
201
|
+
const cleaned = String(value)
|
|
202
|
+
.trim()
|
|
203
|
+
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
204
|
+
.replace(/^_+/, '');
|
|
205
|
+
return cleaned.length > 0 ? cleaned : 'entry';
|
|
206
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { DoctorReport } from '../types';
|
|
2
|
+
export declare function buildDoctorReport(projectRoot: string): Promise<DoctorReport>;
|
|
3
|
+
export declare function writeDoctorReport(projectRoot: string, report: DoctorReport, outputPath?: string): Promise<string>;
|
|
4
|
+
export declare function renderDoctorReport(report: DoctorReport): string;
|