@expo-harmony/cli 55.0.26-harmony.1 → 55.0.26-harmony.10
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 +87 -38
- package/build/args.d.ts +19 -0
- package/build/args.js +27 -0
- package/build/bin/expo-harmony.d.ts +2 -0
- package/build/bin/expo-harmony.js +1 -1
- package/build/buildHap/build.d.ts +18 -0
- package/build/buildHap/build.js +38 -0
- package/build/buildHap/index.d.ts +2 -0
- package/build/buildHap/index.js +19 -0
- package/build/buildHap/options.d.ts +7 -0
- package/build/buildHap/options.js +30 -0
- package/build/cli.d.ts +3 -0
- package/build/cli.js +40 -0
- package/build/command.d.ts +9 -0
- package/build/command.js +20 -0
- package/build/development/manifest.d.ts +1 -0
- package/build/development/manifest.js +100 -0
- package/build/development/protocol.d.ts +2 -0
- package/build/development/protocol.js +26 -0
- package/build/development/session.d.ts +5 -0
- package/build/development/session.js +66 -0
- package/build/doctor/doctor.d.ts +20 -0
- package/build/doctor/doctor.js +226 -0
- package/build/doctor/index.d.ts +2 -0
- package/build/doctor/index.js +16 -0
- package/build/doctor/options.d.ts +5 -0
- package/build/doctor/options.js +15 -0
- package/build/entry.d.ts +2 -0
- package/build/entry.js +47 -0
- package/build/errors.d.ts +12 -0
- package/build/errors.js +16 -0
- package/build/expo.d.ts +19 -0
- package/build/expo.js +53 -0
- package/build/exportEmbed/export.d.ts +18 -0
- package/build/exportEmbed/export.js +124 -0
- package/build/exportEmbed/index.d.ts +2 -0
- package/build/exportEmbed/index.js +19 -0
- package/build/exportEmbed/manifest.d.ts +44 -0
- package/build/exportEmbed/manifest.js +171 -0
- package/build/exportEmbed/options.d.ts +7 -0
- package/build/exportEmbed/options.js +22 -0
- package/build/file.d.ts +10 -0
- package/build/file.js +110 -0
- package/build/help.d.ts +1 -0
- package/build/help.js +44 -0
- package/build/index.d.ts +14 -0
- package/build/index.js +6 -5
- package/build/internal/development.d.ts +1 -0
- package/build/internal/development.js +8 -0
- package/build/internal/export.d.ts +1 -0
- package/build/internal/export.js +80 -0
- package/build/internal/prebuild.d.ts +3 -0
- package/build/internal/prebuild.js +9 -0
- package/build/log.d.ts +4 -0
- package/build/log.js +6 -0
- package/build/modules/index.d.ts +2 -0
- package/build/modules/index.js +16 -0
- package/build/modules/modules.d.ts +42 -0
- package/build/modules/modules.js +84 -0
- package/build/modules/options.d.ts +10 -0
- package/build/modules/options.js +45 -0
- package/build/native/bare.d.ts +4 -0
- package/build/native/bare.js +130 -0
- package/build/native/build.d.ts +15 -0
- package/build/native/build.js +69 -0
- package/build/native/cache.d.ts +11 -0
- package/build/native/cache.js +73 -0
- package/build/native/hvigor.d.ts +3 -0
- package/build/native/hvigor.js +83 -0
- package/build/native/install.d.ts +5 -0
- package/build/native/install.js +13 -0
- package/build/native/prepareProject.d.ts +11 -0
- package/build/native/prepareProject.js +68 -0
- package/build/native/project.d.ts +8 -0
- package/build/native/project.js +58 -0
- package/build/native/toolchain.d.ts +16 -0
- package/build/native/toolchain.js +172 -0
- package/build/native/types.d.ts +47 -0
- package/build/native/types.js +2 -0
- package/build/path.d.ts +4 -0
- package/build/path.js +27 -0
- package/build/prebuild/check.d.ts +10 -0
- package/build/prebuild/check.js +151 -0
- package/build/prebuild/clean.d.ts +2 -0
- package/build/prebuild/clean.js +50 -0
- package/build/prebuild/index.d.ts +2 -0
- package/build/prebuild/index.js +51 -0
- package/build/prebuild/options.d.ts +10 -0
- package/build/prebuild/options.js +50 -0
- package/build/prebuild/prebuild.d.ts +6 -0
- package/build/prebuild/prebuild.js +70 -0
- package/build/prebuild/template.d.ts +8 -0
- package/build/prebuild/template.js +86 -0
- package/build/process.d.ts +41 -0
- package/build/process.js +324 -0
- package/build/profile.d.ts +1 -0
- package/build/profile.js +12 -0
- package/build/project.d.ts +2 -0
- package/build/project.js +23 -0
- package/build/projectLock.d.ts +2 -0
- package/build/projectLock.js +185 -0
- package/build/run/devices.d.ts +29 -0
- package/build/run/devices.js +216 -0
- package/build/run/emulators.d.ts +13 -0
- package/build/run/emulators.js +82 -0
- package/build/run/index.d.ts +2 -0
- package/build/run/index.js +25 -0
- package/build/run/metro.d.ts +21 -0
- package/build/run/metro.js +162 -0
- package/build/run/options.d.ts +14 -0
- package/build/run/options.js +62 -0
- package/build/run/run.d.ts +46 -0
- package/build/run/run.js +135 -0
- package/build/runtime/config.d.ts +35 -0
- package/build/runtime/config.js +118 -0
- package/build/runtime/contract.d.ts +5 -0
- package/build/runtime/contract.js +108 -0
- package/build/runtime/fingerprint.d.ts +1 -0
- package/build/runtime/fingerprint.js +44 -0
- package/build/runtime/index.d.ts +2 -0
- package/build/runtime/index.js +50 -0
- package/build/start/index.d.ts +2 -0
- package/build/start/index.js +38 -0
- package/build/start/options.d.ts +9 -0
- package/build/start/options.js +38 -0
- package/build/updates/export.d.ts +14 -0
- package/build/updates/export.js +46 -0
- package/build/updates/index.d.ts +2 -0
- package/build/updates/index.js +96 -0
- package/build/upstream.d.ts +9 -0
- package/build/upstream.js +17 -0
- package/package.json +21 -5
- package/src/bin/expo-harmony.ts +2 -2
- package/src/buildHap/build.ts +13 -105
- package/src/buildHap/index.ts +19 -0
- package/src/cli.ts +27 -225
- package/src/command.ts +24 -0
- package/src/development/manifest.ts +106 -0
- package/src/development/protocol.ts +37 -0
- package/src/development/session.ts +83 -0
- package/src/doctor/doctor.ts +67 -62
- package/src/doctor/index.ts +16 -0
- package/src/exportEmbed/export.ts +73 -72
- package/src/exportEmbed/index.ts +17 -0
- package/src/file.ts +20 -11
- package/src/help.ts +41 -0
- package/src/index.ts +5 -3
- package/src/internal/development.ts +8 -0
- package/src/internal/export.ts +82 -0
- package/src/internal/prebuild.ts +3 -0
- package/src/log.ts +5 -0
- package/src/modules/index.ts +16 -0
- package/src/native/bare.ts +145 -0
- package/src/native/build.ts +83 -0
- package/src/{run → native}/cache.ts +10 -9
- package/src/native/hvigor.ts +88 -0
- package/src/native/install.ts +17 -0
- package/src/native/prepareProject.ts +99 -0
- package/src/native/project.ts +88 -0
- package/src/{tools.ts → native/toolchain.ts} +38 -140
- package/src/native/types.ts +44 -0
- package/src/prebuild/check.ts +56 -31
- package/src/prebuild/clean.ts +3 -3
- package/src/prebuild/index.ts +56 -0
- package/src/prebuild/prebuild.ts +11 -1
- package/src/prebuild/template.ts +2 -2
- package/src/process.ts +175 -64
- package/src/profile.ts +13 -0
- package/src/projectLock.ts +9 -1
- package/src/run/devices.ts +16 -11
- package/src/run/emulators.ts +4 -6
- package/src/run/index.ts +25 -0
- package/src/run/metro.ts +10 -1
- package/src/run/options.ts +2 -0
- package/src/run/run.ts +63 -129
- package/src/runtime/config.ts +150 -0
- package/src/runtime/contract.ts +165 -0
- package/src/runtime/fingerprint.ts +39 -0
- package/src/runtime/index.ts +54 -0
- package/src/start/index.ts +37 -0
- package/src/start/options.ts +12 -0
- package/src/updates/export.ts +60 -0
- package/src/updates/index.ts +89 -0
- package/src/buildHap/common.ts +0 -145
- package/src/run/install.ts +0 -41
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { linkModulesAsync } from '@expo-harmony/expo-modules-autolinking';
|
|
3
|
+
|
|
4
|
+
import { doctorAsync } from '../doctor/doctor';
|
|
5
|
+
import { HarmonyCliError } from '../errors';
|
|
6
|
+
import { progress, type Log } from '../log';
|
|
7
|
+
import { timedAsync } from '../profile';
|
|
8
|
+
import { checkAsync } from '../prebuild/check';
|
|
9
|
+
import { prebuildParsedAsync } from '../prebuild/prebuild';
|
|
10
|
+
import { createHarmonyToolchainEnv } from './toolchain';
|
|
11
|
+
import { resolveHarmonyBuildPlanAsync, resolveHarmonyBuildPlanIfPresentAsync } from './project';
|
|
12
|
+
import type { HarmonyBuildPlan } from './types';
|
|
13
|
+
|
|
14
|
+
interface PrepareProjectOptions {
|
|
15
|
+
io?: Log;
|
|
16
|
+
requireDeviceTools?: boolean;
|
|
17
|
+
check?: boolean;
|
|
18
|
+
sync: boolean;
|
|
19
|
+
variant: 'debug' | 'release';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function prepareNativeProjectAsync(
|
|
23
|
+
root: string,
|
|
24
|
+
options: PrepareProjectOptions,
|
|
25
|
+
steps: Record<string, number>
|
|
26
|
+
): Promise<HarmonyBuildPlan> {
|
|
27
|
+
const plan = await timedAsync(steps, 'buildPlan', () => resolveHarmonyBuildPlanIfPresentAsync(root, {
|
|
28
|
+
buildMode: options.variant,
|
|
29
|
+
}));
|
|
30
|
+
const exists = Boolean(plan && fs.existsSync(plan.harmonyRoot));
|
|
31
|
+
|
|
32
|
+
if (plan?.workflow === 'bare' && options.sync) {
|
|
33
|
+
throw new HarmonyCliError('ERR_HARMONY_BARE_SYNC', '--sync regenerates CNG projects and cannot be used with a manually maintained bare project.', { operation: 'build' });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
progress(options, 'Checking the Harmony project');
|
|
37
|
+
const doctor = await timedAsync(steps, 'doctor', () => doctorAsync(root, {
|
|
38
|
+
requireBuildTools: true,
|
|
39
|
+
requireDeviceTools: options.requireDeviceTools,
|
|
40
|
+
validateGeneratedProject: exists,
|
|
41
|
+
validateModules: false,
|
|
42
|
+
}));
|
|
43
|
+
if (!doctor.ok) {
|
|
44
|
+
const checks = doctor.checks.filter(check => check.status === 'error').map(check => check.id);
|
|
45
|
+
|
|
46
|
+
throw new HarmonyCliError('ERR_HARMONY_DOCTOR_FAILED', `Harmony doctor found blocking checks: ${checks.join(', ') || 'unknown'}.`, { operation: 'doctor' });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (plan?.workflow === 'bare') {
|
|
50
|
+
progress(options, 'Autolinking the bare Harmony project');
|
|
51
|
+
await timedAsync(steps, 'autolinking', () => linkModulesAsync({
|
|
52
|
+
projectRoot: root,
|
|
53
|
+
harmonyProjectPath: plan.harmonyRoot,
|
|
54
|
+
buildType: options.variant,
|
|
55
|
+
env: createHarmonyToolchainEnv(),
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
steps.prebuild = 0;
|
|
59
|
+
steps.cngCheck = 0;
|
|
60
|
+
|
|
61
|
+
return plan;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!exists || options.sync) {
|
|
65
|
+
progress(options, exists
|
|
66
|
+
? 'Synchronizing the generated Harmony project'
|
|
67
|
+
: 'Generating the missing Harmony project');
|
|
68
|
+
await timedAsync(steps, 'prebuild', () => prebuildParsedAsync(
|
|
69
|
+
root,
|
|
70
|
+
[],
|
|
71
|
+
{ buildType: options.variant }
|
|
72
|
+
));
|
|
73
|
+
steps.cngCheck = 0;
|
|
74
|
+
|
|
75
|
+
return timedAsync(steps, 'buildPlan', () => resolveHarmonyBuildPlanAsync(root, { buildMode: options.variant }));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
steps.prebuild = 0;
|
|
79
|
+
if (options.check === false) {
|
|
80
|
+
steps.cngCheck = 0;
|
|
81
|
+
return plan;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
progress(options, 'Checking CNG ownership and drift');
|
|
85
|
+
const check = await timedAsync(steps, 'cngCheck', () => checkAsync(root, { buildType: options.variant }));
|
|
86
|
+
if (!check.clean) {
|
|
87
|
+
const summary = check.changes.slice(0, 8).map(change => `${change.type}:${change.path}`).join(', ');
|
|
88
|
+
|
|
89
|
+
throw new HarmonyCliError(
|
|
90
|
+
'ERR_HARMONY_MANIFEST_DRIFT',
|
|
91
|
+
`Generated Harmony files differ from CNG desired state${summary ? ` (${summary})` : ''}. Run expo-harmony prebuild or retry with --sync.`,
|
|
92
|
+
{ operation: 'check' }
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return plan;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { prepareNativeProjectAsync };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { readManifestIfPresentAsync, resolveHarmonyBuildPath, type HarmonyBuildDescriptor } from '@expo-harmony/prebuild-config/internal';
|
|
2
|
+
|
|
3
|
+
import { HarmonyCliError } from '../errors';
|
|
4
|
+
import { isBareHarmonyProject, resolveBareHarmonyBuildPlan } from './bare';
|
|
5
|
+
import type { CngHarmonyBuildPlan, HarmonyBuildPlan } from './types';
|
|
6
|
+
|
|
7
|
+
function createHarmonyBuildPlan(
|
|
8
|
+
root: string,
|
|
9
|
+
build: HarmonyBuildDescriptor,
|
|
10
|
+
mode: 'debug' | 'release'
|
|
11
|
+
): CngHarmonyBuildPlan {
|
|
12
|
+
const resolve = relative => resolveHarmonyBuildPath(root, relative);
|
|
13
|
+
const variant = build.variants[mode];
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
workflow: 'cng',
|
|
17
|
+
abilityName: build.identity.abilityName,
|
|
18
|
+
buildMode: mode,
|
|
19
|
+
bundleName: build.identity.bundleName,
|
|
20
|
+
expectedHap: resolve(variant.expectedHap),
|
|
21
|
+
exportPaths: Object.fromEntries(
|
|
22
|
+
Object.entries(build.export).map(([name, relative]) => [name, resolve(relative)])
|
|
23
|
+
) as HarmonyBuildPlan['exportPaths'],
|
|
24
|
+
harmonyRoot: resolve(build.harmonyRoot),
|
|
25
|
+
hvigorArgs: [...variant.hvigorArgs],
|
|
26
|
+
moduleName: build.identity.moduleName,
|
|
27
|
+
moduleRoot: resolve(build.moduleRoot),
|
|
28
|
+
nativeCache: {
|
|
29
|
+
invalidationRoots: build.nativeCache.invalidationRoots.map(resolve),
|
|
30
|
+
stateFile: resolve(build.nativeCache.stateFile),
|
|
31
|
+
},
|
|
32
|
+
nativeInputs: {
|
|
33
|
+
lockfile: resolve(build.nativeInputs.lockfile),
|
|
34
|
+
manifest: resolve(build.nativeInputs.manifest),
|
|
35
|
+
},
|
|
36
|
+
productName: build.identity.productName,
|
|
37
|
+
projectFiles: Object.fromEntries(
|
|
38
|
+
Object.entries(build.projectFiles).map(([name, relative]) => [name, resolve(relative)])
|
|
39
|
+
) as CngHarmonyBuildPlan['projectFiles'],
|
|
40
|
+
targetName: build.identity.targetName,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function resolveHarmonyBuildPlanIfPresentAsync(
|
|
45
|
+
root: string,
|
|
46
|
+
options: { buildMode?: 'debug' | 'release' } = {}
|
|
47
|
+
): Promise<HarmonyBuildPlan | null> {
|
|
48
|
+
const mode = options.buildMode || 'debug';
|
|
49
|
+
if (!['debug', 'release'].includes(mode)) {
|
|
50
|
+
throw new HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Harmony buildMode must be debug or release, received: ${mode}`, { operation: 'resolve-build' });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let manifest;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
manifest = await readManifestIfPresentAsync(root);
|
|
57
|
+
} catch (cause) {
|
|
58
|
+
throw new HarmonyCliError(
|
|
59
|
+
cause.code || 'ERR_HARMONY_TEMPLATE_INVALID',
|
|
60
|
+
`Cannot read the generated Harmony build descriptor: ${cause.message}`,
|
|
61
|
+
{ cause, operation: 'resolve-build' }
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!manifest) {
|
|
66
|
+
return isBareHarmonyProject(root) ? resolveBareHarmonyBuildPlan(root, mode) : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return createHarmonyBuildPlan(root, manifest.build, mode);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function resolveHarmonyBuildPlanAsync(
|
|
73
|
+
root: string,
|
|
74
|
+
options: { buildMode?: 'debug' | 'release' } = {}
|
|
75
|
+
): Promise<HarmonyBuildPlan> {
|
|
76
|
+
const plan = await resolveHarmonyBuildPlanIfPresentAsync(root, options);
|
|
77
|
+
if (!plan) {
|
|
78
|
+
throw new HarmonyCliError(
|
|
79
|
+
'ERR_HARMONY_MANIFEST_DRIFT',
|
|
80
|
+
'Cannot read the generated Harmony build descriptor because the CNG manifest is missing.',
|
|
81
|
+
{ operation: 'resolve-build' }
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return plan;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { resolveHarmonyBuildPlanAsync, resolveHarmonyBuildPlanIfPresentAsync };
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
resolveHarmonyBuildPath,
|
|
6
|
-
type HarmonyBuildDescriptor,
|
|
7
|
-
} from '@expo-harmony/prebuild-config/build-descriptor';
|
|
8
|
-
import { readManifestIfPresentAsync } from '@expo-harmony/prebuild-config/check';
|
|
9
|
-
|
|
10
|
-
import { HarmonyCliError } from './errors';
|
|
3
|
+
import { resolveHarmonyCommand } from '@expo-harmony/expo-modules-autolinking/tool-command';
|
|
11
4
|
|
|
12
5
|
export interface HarmonyTool {
|
|
13
6
|
args: string[];
|
|
@@ -23,23 +16,6 @@ export interface HarmonyToolchain {
|
|
|
23
16
|
toolsRoot: string | null;
|
|
24
17
|
}
|
|
25
18
|
|
|
26
|
-
export interface HarmonyBuildPlan {
|
|
27
|
-
abilityName: string;
|
|
28
|
-
buildMode: 'debug' | 'release';
|
|
29
|
-
bundleName: string;
|
|
30
|
-
expectedHap: string;
|
|
31
|
-
exportPaths: HarmonyBuildDescriptor['export'];
|
|
32
|
-
harmonyRoot: string;
|
|
33
|
-
hvigorArgs: string[];
|
|
34
|
-
moduleName: string;
|
|
35
|
-
moduleRoot: string;
|
|
36
|
-
nativeCache: HarmonyBuildDescriptor['nativeCache'];
|
|
37
|
-
nativeInputs: HarmonyBuildDescriptor['nativeInputs'];
|
|
38
|
-
projectFiles: HarmonyBuildDescriptor['projectFiles'];
|
|
39
|
-
productName: string;
|
|
40
|
-
targetName: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
19
|
function existingFile(candidates: string[]): string | null {
|
|
44
20
|
return candidates.find(candidate => fs.existsSync(candidate)) || null;
|
|
45
21
|
}
|
|
@@ -54,7 +30,7 @@ const RequiredSdkComponents = Object.freeze([
|
|
|
54
30
|
'default/openharmony/toolchains/oh-uni-package.json',
|
|
55
31
|
]);
|
|
56
32
|
|
|
57
|
-
function sdkRootsNear(seed) {
|
|
33
|
+
function sdkRootsNear(seed: string): string[] {
|
|
58
34
|
const roots = [];
|
|
59
35
|
let cursor = path.resolve(seed);
|
|
60
36
|
|
|
@@ -74,7 +50,7 @@ function sdkRootsNear(seed) {
|
|
|
74
50
|
return roots;
|
|
75
51
|
}
|
|
76
52
|
|
|
77
|
-
function resolveHarmonySdkRoot(env, platform) {
|
|
53
|
+
function resolveHarmonySdkRoot(env: NodeJS.ProcessEnv, platform: NodeJS.Platform): string | null {
|
|
78
54
|
const seeds = [
|
|
79
55
|
env.DEVECO_SDK_HOME,
|
|
80
56
|
env.HARMONY_HOME,
|
|
@@ -92,7 +68,7 @@ function resolveHarmonySdkRoot(env, platform) {
|
|
|
92
68
|
)) || null;
|
|
93
69
|
}
|
|
94
70
|
|
|
95
|
-
function devEcoInstallRoots(sdkHome) {
|
|
71
|
+
function devEcoInstallRoots(sdkHome: string): string[] {
|
|
96
72
|
const ancestors = [sdkHome, path.dirname(sdkHome), path.dirname(path.dirname(sdkHome))];
|
|
97
73
|
const sdkDirectory = ancestors.find(candidate => path.basename(candidate).toLowerCase() === 'sdk');
|
|
98
74
|
const roots = [
|
|
@@ -104,7 +80,7 @@ function devEcoInstallRoots(sdkHome) {
|
|
|
104
80
|
return [...new Set(roots)];
|
|
105
81
|
}
|
|
106
82
|
|
|
107
|
-
function devEcoLayouts(sdkHome) {
|
|
83
|
+
function devEcoLayouts(sdkHome: string) {
|
|
108
84
|
return devEcoInstallRoots(sdkHome).flatMap(root => [
|
|
109
85
|
{
|
|
110
86
|
nodeRoot: path.join(root, 'tools', 'node'),
|
|
@@ -117,7 +93,11 @@ function devEcoLayouts(sdkHome) {
|
|
|
117
93
|
]);
|
|
118
94
|
}
|
|
119
95
|
|
|
120
|
-
function devEcoNode(
|
|
96
|
+
function devEcoNode(
|
|
97
|
+
layout: { nodeRoot: string },
|
|
98
|
+
platform: NodeJS.Platform,
|
|
99
|
+
env: NodeJS.ProcessEnv
|
|
100
|
+
): Pick<HarmonyTool, 'command' | 'source'> | null {
|
|
121
101
|
if (env.HARMONY_NODE) return { command: env.HARMONY_NODE, source: 'override' };
|
|
122
102
|
|
|
123
103
|
const executable = platform === 'win32' ? 'node.exe' : 'node';
|
|
@@ -133,51 +113,49 @@ function resolveHarmonyToolchain(): HarmonyToolchain {
|
|
|
133
113
|
const env = process.env;
|
|
134
114
|
const platform = process.platform;
|
|
135
115
|
|
|
136
|
-
// Hvigor
|
|
137
|
-
// Reject metadata-only candidates so doctor cannot claim an SDK is
|
|
138
|
-
// buildable without its HMS, OpenHarmony, native, ETS, and toolchain parts.
|
|
116
|
+
// Hvigor needs the complete SDK root containing default/, not default/ itself.
|
|
139
117
|
const sdkHome = resolveHarmonySdkRoot(env, platform);
|
|
140
118
|
const layouts = sdkHome ? devEcoLayouts(sdkHome) : [];
|
|
141
119
|
|
|
142
120
|
let ohpm: HarmonyTool;
|
|
143
121
|
if (env.HARMONY_OHPM) {
|
|
144
|
-
ohpm = {
|
|
122
|
+
ohpm = { ...resolveHarmonyCommand('ohpm', [], env), source: 'override' };
|
|
145
123
|
} else {
|
|
146
|
-
const
|
|
124
|
+
const entry = layouts.map(layout => ({
|
|
125
|
+
node: devEcoNode(layout, platform, { ...env, HARMONY_NODE: env.HARMONY_OHPM_NODE || env.HARMONY_NODE }),
|
|
126
|
+
script: path.join(layout.toolsRoot, 'ohpm', 'bin', 'pm-cli.js'),
|
|
127
|
+
})).find(candidate => candidate.node && fs.existsSync(candidate.script));
|
|
128
|
+
const command = existingFile(layouts.map(layout => path.join(
|
|
147
129
|
layout.toolsRoot,
|
|
148
130
|
'ohpm',
|
|
149
131
|
'bin',
|
|
150
132
|
platform === 'win32' ? 'ohpm.bat' : 'ohpm'
|
|
151
133
|
)));
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
134
|
+
|
|
135
|
+
ohpm = entry
|
|
136
|
+
? { args: [entry.script], command: entry.node.command, source: 'deveco' }
|
|
137
|
+
: command
|
|
138
|
+
? { args: [], command, source: 'deveco' }
|
|
139
|
+
: { args: [], command: platform === 'win32' ? 'ohpm.bat' : 'ohpm', source: 'path' };
|
|
155
140
|
}
|
|
156
141
|
|
|
157
142
|
let hvigor: HarmonyTool;
|
|
158
143
|
if (env.HARMONY_HVIGORW) {
|
|
159
|
-
|
|
160
|
-
hvigor = {
|
|
161
|
-
args: [env.HARMONY_HVIGORW],
|
|
162
|
-
command: env.HARMONY_NODE || process.execPath,
|
|
163
|
-
source: 'override',
|
|
164
|
-
};
|
|
165
|
-
} else {
|
|
166
|
-
hvigor = { args: [], command: env.HARMONY_HVIGORW, source: 'override' };
|
|
167
|
-
}
|
|
144
|
+
hvigor = { ...resolveHarmonyCommand('hvigorw', [], env), source: 'override' };
|
|
168
145
|
} else {
|
|
169
|
-
const
|
|
170
|
-
layout,
|
|
171
|
-
node: devEcoNode(layout, platform, env),
|
|
146
|
+
const entry = layouts.map(layout => ({
|
|
147
|
+
node: devEcoNode(layout, platform, { ...env, HARMONY_NODE: env.HARMONY_HVIGOR_NODE || env.HARMONY_NODE }),
|
|
172
148
|
script: path.join(layout.toolsRoot, 'hvigor', 'bin', 'hvigorw.js'),
|
|
173
149
|
})).find(candidate => candidate.node && fs.existsSync(candidate.script));
|
|
174
|
-
|
|
175
|
-
|
|
150
|
+
|
|
151
|
+
hvigor = entry
|
|
152
|
+
? { args: [entry.script], command: entry.node.command, source: 'deveco' }
|
|
176
153
|
: { args: [], command: platform === 'win32' ? 'hvigorw.bat' : 'hvigorw', source: 'path' };
|
|
177
154
|
}
|
|
178
155
|
|
|
179
156
|
const toolsRoot = layouts.find(layout => (
|
|
180
157
|
hvigor.args[0]?.startsWith(`${layout.toolsRoot}${path.sep}`)
|
|
158
|
+
|| ohpm.args[0]?.startsWith(`${layout.toolsRoot}${path.sep}`)
|
|
181
159
|
|| ohpm.command.startsWith(`${layout.toolsRoot}${path.sep}`)
|
|
182
160
|
))?.toolsRoot || null;
|
|
183
161
|
|
|
@@ -201,10 +179,14 @@ function resolveHarmonyToolchain(): HarmonyToolchain {
|
|
|
201
179
|
function createHarmonyToolchainEnv(toolchain = resolveHarmonyToolchain()): NodeJS.ProcessEnv {
|
|
202
180
|
return {
|
|
203
181
|
...process.env,
|
|
204
|
-
HARMONY_OHPM: toolchain.ohpm.command,
|
|
205
|
-
|
|
182
|
+
HARMONY_OHPM: toolchain.ohpm.args[0] || toolchain.ohpm.command,
|
|
183
|
+
HARMONY_OHPM_NODE: toolchain.ohpm.args.length > 0 ? toolchain.ohpm.command : undefined,
|
|
184
|
+
// Script-based tools must retain both their script and Node executable.
|
|
206
185
|
HARMONY_HVIGORW: toolchain.hvigor.args[0] || toolchain.hvigor.command,
|
|
207
|
-
|
|
186
|
+
HARMONY_HVIGOR_NODE: toolchain.hvigor.args.length > 0 ? toolchain.hvigor.command : undefined,
|
|
187
|
+
...(toolchain.hvigor.args.length > 0
|
|
188
|
+
? { HARMONY_NODE: toolchain.hvigor.command }
|
|
189
|
+
: toolchain.ohpm.args.length > 0 ? { HARMONY_NODE: toolchain.ohpm.command } : {}),
|
|
208
190
|
...(toolchain.sdkHome && !process.env.DEVECO_SDK_HOME
|
|
209
191
|
? { DEVECO_SDK_HOME: toolchain.sdkHome }
|
|
210
192
|
: {}),
|
|
@@ -229,88 +211,4 @@ function resolveHarmonyEmulator(toolchain: HarmonyToolchain): HarmonyTool {
|
|
|
229
211
|
: { args: [], command: executable, source: 'path' };
|
|
230
212
|
}
|
|
231
213
|
|
|
232
|
-
|
|
233
|
-
projectRoot: string,
|
|
234
|
-
build: HarmonyBuildDescriptor,
|
|
235
|
-
mode: 'debug' | 'release'
|
|
236
|
-
): HarmonyBuildPlan {
|
|
237
|
-
const resolve = relative => resolveHarmonyBuildPath(projectRoot, relative);
|
|
238
|
-
const variant = build.variants[mode];
|
|
239
|
-
|
|
240
|
-
return {
|
|
241
|
-
abilityName: build.identity.abilityName,
|
|
242
|
-
buildMode: mode,
|
|
243
|
-
bundleName: build.identity.bundleName,
|
|
244
|
-
expectedHap: resolve(variant.expectedHap),
|
|
245
|
-
exportPaths: Object.fromEntries(
|
|
246
|
-
Object.entries(build.export).map(([name, relative]) => [name, resolve(relative)])
|
|
247
|
-
) as HarmonyBuildPlan['exportPaths'],
|
|
248
|
-
harmonyRoot: resolve(build.harmonyRoot),
|
|
249
|
-
hvigorArgs: [...variant.hvigorArgs],
|
|
250
|
-
moduleName: build.identity.moduleName,
|
|
251
|
-
moduleRoot: resolve(build.moduleRoot),
|
|
252
|
-
nativeCache: {
|
|
253
|
-
invalidationRoots: build.nativeCache.invalidationRoots.map(resolve),
|
|
254
|
-
stateFile: resolve(build.nativeCache.stateFile),
|
|
255
|
-
},
|
|
256
|
-
nativeInputs: {
|
|
257
|
-
lockfile: resolve(build.nativeInputs.lockfile),
|
|
258
|
-
manifest: resolve(build.nativeInputs.manifest),
|
|
259
|
-
},
|
|
260
|
-
productName: build.identity.productName,
|
|
261
|
-
projectFiles: Object.fromEntries(
|
|
262
|
-
Object.entries(build.projectFiles).map(([name, relative]) => [name, resolve(relative)])
|
|
263
|
-
) as HarmonyBuildPlan['projectFiles'],
|
|
264
|
-
targetName: build.identity.targetName,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
async function resolveHarmonyBuildPlanIfPresentAsync(
|
|
269
|
-
projectRoot: string,
|
|
270
|
-
options: { buildMode?: 'debug' | 'release' } = {}
|
|
271
|
-
): Promise<HarmonyBuildPlan | null> {
|
|
272
|
-
const mode = options.buildMode || 'debug';
|
|
273
|
-
if (!['debug', 'release'].includes(mode)) {
|
|
274
|
-
throw new HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Harmony buildMode must be debug or release, received: ${mode}`, { operation: 'resolve-build' });
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
let manifest;
|
|
278
|
-
|
|
279
|
-
try {
|
|
280
|
-
manifest = await readManifestIfPresentAsync(projectRoot);
|
|
281
|
-
} catch (cause) {
|
|
282
|
-
throw new HarmonyCliError(
|
|
283
|
-
cause.code || 'ERR_HARMONY_TEMPLATE_INVALID',
|
|
284
|
-
`Cannot read the generated Harmony build descriptor: ${cause.message}`,
|
|
285
|
-
{ cause, operation: 'resolve-build' }
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (!manifest) return null;
|
|
290
|
-
|
|
291
|
-
return createHarmonyBuildPlan(projectRoot, manifest.build, mode);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
async function resolveHarmonyBuildPlanAsync(
|
|
295
|
-
projectRoot: string,
|
|
296
|
-
options: { buildMode?: 'debug' | 'release' } = {}
|
|
297
|
-
): Promise<HarmonyBuildPlan> {
|
|
298
|
-
const plan = await resolveHarmonyBuildPlanIfPresentAsync(projectRoot, options);
|
|
299
|
-
if (!plan) {
|
|
300
|
-
throw new HarmonyCliError(
|
|
301
|
-
'ERR_HARMONY_MANIFEST_DRIFT',
|
|
302
|
-
'Cannot read the generated Harmony build descriptor because the CNG manifest is missing.',
|
|
303
|
-
{ operation: 'resolve-build' }
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return plan;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export {
|
|
311
|
-
createHarmonyToolchainEnv,
|
|
312
|
-
resolveHarmonyBuildPlanAsync,
|
|
313
|
-
resolveHarmonyBuildPlanIfPresentAsync,
|
|
314
|
-
resolveHarmonyEmulator,
|
|
315
|
-
resolveHarmonyToolchain,
|
|
316
|
-
};
|
|
214
|
+
export { createHarmonyToolchainEnv, resolveHarmonyEmulator, resolveHarmonyToolchain };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type HarmonyBuildMode = 'debug' | 'release';
|
|
2
|
+
|
|
3
|
+
interface NativeBuildPlan {
|
|
4
|
+
abilityName: string;
|
|
5
|
+
buildMode: HarmonyBuildMode;
|
|
6
|
+
bundleName: string;
|
|
7
|
+
expectedHap: string;
|
|
8
|
+
exportPaths: {
|
|
9
|
+
bundle: string;
|
|
10
|
+
manifest: string;
|
|
11
|
+
metadataRoot: string;
|
|
12
|
+
rawfileRoot: string;
|
|
13
|
+
sourceMap: string;
|
|
14
|
+
};
|
|
15
|
+
harmonyRoot: string;
|
|
16
|
+
hvigorArgs: string[];
|
|
17
|
+
moduleName: string;
|
|
18
|
+
moduleRoot: string;
|
|
19
|
+
nativeCache: { invalidationRoots: string[]; stateFile: string };
|
|
20
|
+
nativeInputs: { lockfile: string; manifest: string };
|
|
21
|
+
projectFiles: {
|
|
22
|
+
hvigorConfig: string;
|
|
23
|
+
moduleHvigor: string;
|
|
24
|
+
moduleJson: string;
|
|
25
|
+
projectBuildProfile: string;
|
|
26
|
+
rootHvigor: string;
|
|
27
|
+
};
|
|
28
|
+
productName: string;
|
|
29
|
+
targetName: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface BareHarmonyBuildPlan extends NativeBuildPlan {
|
|
33
|
+
workflow: 'bare';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface CngHarmonyBuildPlan extends NativeBuildPlan {
|
|
37
|
+
workflow: 'cng';
|
|
38
|
+
projectFiles: NativeBuildPlan['projectFiles'] & {
|
|
39
|
+
nativeInputsStamp: string;
|
|
40
|
+
templateMarker: string;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type HarmonyBuildPlan = BareHarmonyBuildPlan | CngHarmonyBuildPlan;
|
package/src/prebuild/check.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import os from 'node:os';
|
|
3
4
|
import path from 'node:path';
|
|
@@ -5,20 +6,22 @@ import path from 'node:path';
|
|
|
5
6
|
import {
|
|
6
7
|
compareAsync,
|
|
7
8
|
stageAsync,
|
|
8
|
-
} from '@expo-harmony/prebuild-config/
|
|
9
|
+
} from '@expo-harmony/prebuild-config/internal';
|
|
9
10
|
|
|
10
11
|
import { HarmonyCliError } from '../errors';
|
|
11
12
|
import { isInside } from '../path';
|
|
12
13
|
import { spawnAsync } from '../process';
|
|
13
14
|
import { withHarmonyProjectLockAsync } from '../projectLock';
|
|
14
|
-
import { createHarmonyToolchainEnv
|
|
15
|
+
import { createHarmonyToolchainEnv } from '../native/toolchain';
|
|
16
|
+
import { resolveHarmonyBuildPlanAsync } from '../native/project';
|
|
17
|
+
import { type HarmonyBuildPlan } from '../native/types';
|
|
15
18
|
import { resolveExpoCli } from '../expo';
|
|
16
19
|
import { packAsync } from './template';
|
|
17
20
|
|
|
18
|
-
export type { Change as CheckChange } from '@expo-harmony/prebuild-config/
|
|
21
|
+
export type { Change as CheckChange } from '@expo-harmony/prebuild-config/internal';
|
|
19
22
|
|
|
20
|
-
const
|
|
21
|
-
const
|
|
23
|
+
const IgnoredDirectories = new Set(['.expo', '.git', '.hvigor', '.yarn', 'node_modules']);
|
|
24
|
+
const GeneratedDirectories = new Set([
|
|
22
25
|
'.cxx',
|
|
23
26
|
'.git',
|
|
24
27
|
'.hvigor',
|
|
@@ -40,7 +43,28 @@ function mirrorRoot(temp, project) {
|
|
|
40
43
|
return path.join(temp, 'filesystem', volume, ...segments);
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
async function
|
|
46
|
+
async function linkModuleEntryAsync(
|
|
47
|
+
source: string,
|
|
48
|
+
target: string,
|
|
49
|
+
entry: fs.Dirent
|
|
50
|
+
) {
|
|
51
|
+
const directory = entry.isSymbolicLink()
|
|
52
|
+
? (await fs.promises.stat(source)).isDirectory()
|
|
53
|
+
: entry.isDirectory();
|
|
54
|
+
|
|
55
|
+
if (process.platform === 'win32' && !directory) {
|
|
56
|
+
await fs.promises.copyFile(source, target);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await fs.promises.symlink(
|
|
61
|
+
path.resolve(source),
|
|
62
|
+
target,
|
|
63
|
+
directory ? process.platform === 'win32' ? 'junction' : 'dir' : 'file'
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function linkModulesAsync(source: string, target: string) {
|
|
44
68
|
await fs.promises.mkdir(target, { recursive: true });
|
|
45
69
|
|
|
46
70
|
for (const entry of await fs.promises.readdir(source, { withFileTypes: true })) {
|
|
@@ -49,25 +73,18 @@ async function linkModulesAsync(source, target) {
|
|
|
49
73
|
|
|
50
74
|
if (entry.name.startsWith('@') && entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
51
75
|
await fs.promises.mkdir(to);
|
|
76
|
+
|
|
52
77
|
for (const child of await fs.promises.readdir(from, { withFileTypes: true })) {
|
|
53
|
-
await
|
|
78
|
+
await linkModuleEntryAsync(
|
|
54
79
|
path.join(from, child.name),
|
|
55
80
|
path.join(to, child.name),
|
|
56
|
-
|
|
57
|
-
? 'junction'
|
|
58
|
-
: child.isDirectory() ? 'dir' : 'file'
|
|
81
|
+
child
|
|
59
82
|
);
|
|
60
83
|
}
|
|
61
84
|
continue;
|
|
62
85
|
}
|
|
63
86
|
|
|
64
|
-
await
|
|
65
|
-
from,
|
|
66
|
-
to,
|
|
67
|
-
process.platform === 'win32' && (entry.isDirectory() || entry.isSymbolicLink())
|
|
68
|
-
? 'junction'
|
|
69
|
-
: entry.isDirectory() ? 'dir' : 'file'
|
|
70
|
-
);
|
|
87
|
+
await linkModuleEntryAsync(from, to, entry);
|
|
71
88
|
}
|
|
72
89
|
}
|
|
73
90
|
|
|
@@ -89,16 +106,18 @@ function shouldCopyPrebuildCheckPath(
|
|
|
89
106
|
if (!relative) return true;
|
|
90
107
|
|
|
91
108
|
const segments = relative.split(path.sep);
|
|
92
|
-
if (
|
|
109
|
+
if (IgnoredDirectories.has(segments[0])) return false;
|
|
93
110
|
|
|
94
|
-
const
|
|
111
|
+
const root = isInside(plan.harmonyRoot, source)
|
|
95
112
|
? plan.harmonyRoot
|
|
96
113
|
: isAppLocalHarmonyPath(project, source)
|
|
97
114
|
? path.join(project, ...segments.slice(0, 3))
|
|
98
115
|
: null;
|
|
99
|
-
if (
|
|
100
|
-
const native = path.relative(
|
|
101
|
-
if (
|
|
116
|
+
if (root) {
|
|
117
|
+
const native = path.relative(root, source).split(path.sep);
|
|
118
|
+
if (GeneratedDirectories.has(native[0])
|
|
119
|
+
|| (GeneratedDirectories.has(native[1])
|
|
120
|
+
&& fs.existsSync(path.join(root, native[0], 'src/main/module.json5')))) return false;
|
|
102
121
|
}
|
|
103
122
|
|
|
104
123
|
return source !== plan.exportPaths.bundle;
|
|
@@ -112,6 +131,7 @@ async function copyAsync(
|
|
|
112
131
|
) {
|
|
113
132
|
await fs.promises.cp(project, target, {
|
|
114
133
|
recursive: true,
|
|
134
|
+
verbatimSymlinks: true,
|
|
115
135
|
filter: source => shouldCopyPrebuildCheckPath(project, source, plan),
|
|
116
136
|
});
|
|
117
137
|
|
|
@@ -123,16 +143,16 @@ async function copyAsync(
|
|
|
123
143
|
});
|
|
124
144
|
}
|
|
125
145
|
|
|
126
|
-
//
|
|
127
|
-
// entries. Dependency scanners then retain the isolated lexical package path
|
|
128
|
-
// (including scoped packages) instead of collapsing the entire node_modules
|
|
129
|
-
// root to the source project's realpath. The packages remain read-only and
|
|
130
|
-
// are never copied or modified by --check.
|
|
146
|
+
// Link individual packages so dependency scanners retain paths inside the mirror.
|
|
131
147
|
await linkModulesAsync(modules, path.join(target, 'node_modules'));
|
|
132
148
|
await stageAsync(project, target, temp);
|
|
133
149
|
}
|
|
134
150
|
|
|
135
|
-
async function
|
|
151
|
+
async function withGeneratedProjectAsync<T>(
|
|
152
|
+
project: string,
|
|
153
|
+
options: CheckOptions & { clean?: boolean; skipPatches?: boolean },
|
|
154
|
+
action: (expected: string, checksum: string) => Promise<T>
|
|
155
|
+
): Promise<T> {
|
|
136
156
|
project = path.resolve(project);
|
|
137
157
|
const plan = await resolveHarmonyBuildPlanAsync(project);
|
|
138
158
|
const temp = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'expo-harmony-check-'));
|
|
@@ -141,6 +161,8 @@ async function checkUnlockedAsync(project, options: CheckOptions) {
|
|
|
141
161
|
|
|
142
162
|
try {
|
|
143
163
|
await copyAsync(project, expected, plan, temp);
|
|
164
|
+
if (options.clean) await fs.promises.rm(path.join(expected, 'harmony'), { recursive: true, force: true });
|
|
165
|
+
|
|
144
166
|
packed = await packAsync(project);
|
|
145
167
|
|
|
146
168
|
const expo = resolveExpoCli(project);
|
|
@@ -159,6 +181,7 @@ async function checkUnlockedAsync(project, options: CheckOptions) {
|
|
|
159
181
|
...packed.env,
|
|
160
182
|
...(options.buildType ? { EXPO_HARMONY_BUILD_TYPE: options.buildType } : {}),
|
|
161
183
|
EXPO_HARMONY_CHECK_MIRROR_ROOT: temp,
|
|
184
|
+
...(options.skipPatches ? { EXPO_HARMONY_SKIP_PATCHES: '1' } : {}),
|
|
162
185
|
},
|
|
163
186
|
operation: 'check-prebuild',
|
|
164
187
|
});
|
|
@@ -171,7 +194,9 @@ async function checkUnlockedAsync(project, options: CheckOptions) {
|
|
|
171
194
|
);
|
|
172
195
|
}
|
|
173
196
|
|
|
174
|
-
|
|
197
|
+
const checksum = createHash('md5').update(Uint8Array.from(await fs.promises.readFile(packed.tarball))).digest('hex');
|
|
198
|
+
|
|
199
|
+
return await action(expected, checksum);
|
|
175
200
|
} finally {
|
|
176
201
|
if (packed) await packed.cleanup();
|
|
177
202
|
await fs.promises.rm(temp, { recursive: true, force: true });
|
|
@@ -182,8 +207,8 @@ async function checkAsync(project, options: CheckOptions = {}) {
|
|
|
182
207
|
return withHarmonyProjectLockAsync(
|
|
183
208
|
project,
|
|
184
209
|
'prebuild-check',
|
|
185
|
-
() =>
|
|
210
|
+
() => withGeneratedProjectAsync(project, options, expected => compareAsync(project, expected))
|
|
186
211
|
);
|
|
187
212
|
}
|
|
188
213
|
|
|
189
|
-
export { checkAsync };
|
|
214
|
+
export { checkAsync, withGeneratedProjectAsync };
|