@nx/expo 23.2.0-beta.7 → 23.2.0-beta.8
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.
|
@@ -1 +1,4 @@
|
|
|
1
|
-
export declare function getResolveRequest(extensions: string[], exportsConditionNames?: string[], mainFields?: string[]
|
|
1
|
+
export declare function getResolveRequest(extensions: string[], exportsConditionNames?: string[], mainFields?: string[], anchor?: {
|
|
2
|
+
appRoot?: string;
|
|
3
|
+
usesExpoMetro: boolean;
|
|
4
|
+
}): (_context: any, realModuleName: string, platform: string | null) => any;
|
|
@@ -9,29 +9,28 @@ const enhanced_resolve_1 = require("enhanced-resolve");
|
|
|
9
9
|
const path_1 = require("path");
|
|
10
10
|
const fs = tslib_1.__importStar(require("fs"));
|
|
11
11
|
const devkit_1 = require("@nx/devkit");
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* `metro-resolver` package. We prefer `@expo/metro` and fall back to the
|
|
20
|
-
* standalone package to stay compatible with both.
|
|
21
|
-
*/
|
|
22
|
-
function getMetroResolver() {
|
|
12
|
+
const metroResolverCache = new Map();
|
|
13
|
+
// The resolver must come from the same Metro instance Expo uses: `@expo/metro`
|
|
14
|
+
// on SDK 55+, standalone `metro-resolver` on 53/54. Resolve from the app root
|
|
15
|
+
// so each app gets its own SDK's copy.
|
|
16
|
+
function getMetroResolver(appRoot, usesExpoMetro) {
|
|
17
|
+
const cacheKey = `${appRoot ?? ''}|${usesExpoMetro}`;
|
|
18
|
+
let metroResolver = metroResolverCache.get(cacheKey);
|
|
23
19
|
if (!metroResolver) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
const candidates = usesExpoMetro
|
|
21
|
+
? ['@expo/metro/metro-resolver', 'metro-resolver']
|
|
22
|
+
: ['metro-resolver', '@expo/metro/metro-resolver'];
|
|
23
|
+
for (const candidate of candidates) {
|
|
28
24
|
try {
|
|
29
|
-
metroResolver = require(
|
|
30
|
-
|
|
31
|
-
catch (error) {
|
|
32
|
-
throw new Error('Unable to load Metro resolver. Install `@expo/metro` (Expo SDK 55+) or `metro-resolver` (>= 0.82.0).');
|
|
25
|
+
metroResolver = require(require.resolve(candidate, appRoot ? { paths: [appRoot] } : undefined));
|
|
26
|
+
break;
|
|
33
27
|
}
|
|
28
|
+
catch { }
|
|
29
|
+
}
|
|
30
|
+
if (!metroResolver) {
|
|
31
|
+
throw new Error('Unable to load Metro resolver. Install `@expo/metro` (Expo SDK 55+) or `metro-resolver` (>= 0.82.0).');
|
|
34
32
|
}
|
|
33
|
+
metroResolverCache.set(cacheKey, metroResolver);
|
|
35
34
|
}
|
|
36
35
|
return metroResolver;
|
|
37
36
|
}
|
|
@@ -41,13 +40,17 @@ function getMetroResolver() {
|
|
|
41
40
|
* This resolve function requires projectRoot to be set to
|
|
42
41
|
* workspace root in order modules and assets to be registered and watched.
|
|
43
42
|
*/
|
|
44
|
-
function getResolveRequest(extensions, exportsConditionNames = [], mainFields = []
|
|
43
|
+
function getResolveRequest(extensions, exportsConditionNames = [], mainFields = [],
|
|
44
|
+
// which app is being bundled; default keeps the process-wide preference
|
|
45
|
+
anchor = {
|
|
46
|
+
usesExpoMetro: true,
|
|
47
|
+
}) {
|
|
45
48
|
return function (_context, realModuleName, platform) {
|
|
46
49
|
const debug = process.env.NX_REACT_NATIVE_DEBUG === 'true';
|
|
47
50
|
const { resolveRequest, ...context } = _context;
|
|
48
51
|
const resolvedPath = resolveRequestFromContext(resolveRequest, _context, realModuleName, platform, debug) ??
|
|
49
|
-
defaultMetroResolver(context, realModuleName, platform, debug) ??
|
|
50
|
-
tsconfigPathsResolver(context, extensions, realModuleName, platform, debug) ??
|
|
52
|
+
defaultMetroResolver(context, realModuleName, platform, debug, anchor) ??
|
|
53
|
+
tsconfigPathsResolver(context, extensions, realModuleName, platform, debug, anchor) ??
|
|
51
54
|
pnpmResolver(extensions, context, realModuleName, debug, exportsConditionNames, mainFields);
|
|
52
55
|
if (resolvedPath) {
|
|
53
56
|
return resolvedPath;
|
|
@@ -71,9 +74,9 @@ function resolveRequestFromContext(resolveRequest, context, realModuleName, plat
|
|
|
71
74
|
* This function try to resolve path using metro's default resolver
|
|
72
75
|
* @returns path if resolved, else undefined
|
|
73
76
|
*/
|
|
74
|
-
function defaultMetroResolver(context, realModuleName, platform, debug) {
|
|
77
|
+
function defaultMetroResolver(context, realModuleName, platform, debug, anchor) {
|
|
75
78
|
try {
|
|
76
|
-
const resolver = getMetroResolver();
|
|
79
|
+
const resolver = getMetroResolver(anchor.appRoot, anchor.usesExpoMetro);
|
|
77
80
|
return resolver.resolve(context, realModuleName, platform);
|
|
78
81
|
}
|
|
79
82
|
catch {
|
|
@@ -109,11 +112,11 @@ function pnpmResolver(extensions, context, realModuleName, debug, exportsConditi
|
|
|
109
112
|
* This function try to resolve files that are specified in tsconfig's paths
|
|
110
113
|
* @returns path if resolved, else undefined
|
|
111
114
|
*/
|
|
112
|
-
function tsconfigPathsResolver(context, extensions, realModuleName, platform, debug) {
|
|
115
|
+
function tsconfigPathsResolver(context, extensions, realModuleName, platform, debug, anchor) {
|
|
113
116
|
try {
|
|
114
117
|
const tsConfigPathMatcher = getMatcher(debug);
|
|
115
118
|
const match = tsConfigPathMatcher(realModuleName, undefined, undefined, extensions.map((ext) => `.${ext}`));
|
|
116
|
-
const resolver = getMetroResolver();
|
|
119
|
+
const resolver = getMetroResolver(anchor.appRoot, anchor.usesExpoMetro);
|
|
117
120
|
return resolver.resolve(context, match, platform);
|
|
118
121
|
}
|
|
119
122
|
catch {
|
|
@@ -1,36 +1,66 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.appUsesExpoMetro = appUsesExpoMetro;
|
|
3
4
|
exports.withNxMetro = withNxMetro;
|
|
4
5
|
const devkit_1 = require("@nx/devkit");
|
|
5
6
|
const node_fs_1 = require("node:fs");
|
|
6
7
|
const path_1 = require("path");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* `@expo/metro-config`'s `getDefaultConfig` is built against. Older SDKs
|
|
15
|
-
* (53/54) use the standalone `metro-config` package. We prefer `@expo/metro`
|
|
16
|
-
* and fall back to the standalone package to stay compatible with both.
|
|
17
|
-
*/
|
|
18
|
-
function getMetroConfig() {
|
|
8
|
+
const metroConfigCache = new Map();
|
|
9
|
+
// `mergeConfig` must come from the same Metro instance as the app's
|
|
10
|
+
// `getDefaultConfig`: `@expo/metro` on SDK 55+, standalone `metro-config` on
|
|
11
|
+
// 53/54. Resolve from the app root so each app gets its own SDK's copy.
|
|
12
|
+
function getMetroConfig(appRoot, usesExpoMetro) {
|
|
13
|
+
const cacheKey = `${appRoot ?? ''}|${usesExpoMetro}`;
|
|
14
|
+
let metroConfig = metroConfigCache.get(cacheKey);
|
|
19
15
|
if (!metroConfig) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
const candidates = usesExpoMetro
|
|
17
|
+
? ['@expo/metro/metro-config', 'metro-config']
|
|
18
|
+
: ['metro-config', '@expo/metro/metro-config'];
|
|
19
|
+
for (const candidate of candidates) {
|
|
24
20
|
try {
|
|
25
|
-
metroConfig = require(
|
|
26
|
-
|
|
27
|
-
catch (error) {
|
|
28
|
-
throw new Error('Unable to load Metro config. Install `@expo/metro` (Expo SDK 55+) or `metro-config` (>= 0.82.0).');
|
|
21
|
+
metroConfig = require(require.resolve(candidate, appRoot ? { paths: [appRoot] } : undefined));
|
|
22
|
+
break;
|
|
29
23
|
}
|
|
24
|
+
catch { }
|
|
30
25
|
}
|
|
26
|
+
if (!metroConfig) {
|
|
27
|
+
throw new Error('Unable to load Metro config. Install `@expo/metro` (Expo SDK 55+) or `metro-config` (>= 0.82.0).');
|
|
28
|
+
}
|
|
29
|
+
metroConfigCache.set(cacheKey, metroConfig);
|
|
31
30
|
}
|
|
32
31
|
return metroConfig;
|
|
33
32
|
}
|
|
33
|
+
// SDK 55+ ships Metro through `@expo/metro`. Resolve `expo` from the app's
|
|
34
|
+
// own root: probing from this plugin's location reads whichever copy is
|
|
35
|
+
// hoisted, which can belong to a sibling app on a different SDK.
|
|
36
|
+
function appUsesExpoMetro(appRoot) {
|
|
37
|
+
try {
|
|
38
|
+
const expoPkgPath = require.resolve('expo/package.json', appRoot ? { paths: [appRoot] } : undefined);
|
|
39
|
+
return parseInt(require(expoPkgPath).version, 10) >= 55;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// expo not resolvable; probe `@expo/metro` process-wide (no root entry
|
|
43
|
+
// point, so resolve a known subpath)
|
|
44
|
+
try {
|
|
45
|
+
require.resolve('@expo/metro/metro-config');
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function appOwnsExpo(appNodeModules) {
|
|
54
|
+
const appExpo = (0, path_1.join)(appNodeModules, 'expo');
|
|
55
|
+
if (!(0, node_fs_1.existsSync)(appExpo))
|
|
56
|
+
return false;
|
|
57
|
+
const hoistedExpo = (0, path_1.join)(devkit_1.workspaceRoot, 'node_modules', 'expo');
|
|
58
|
+
// no hoisted copy: the app-local one is the only expo, and Expo's HMR
|
|
59
|
+
// rewrite resolves strictly from `projectRoot`, so anchor at the app
|
|
60
|
+
if (!(0, node_fs_1.existsSync)(hoistedExpo))
|
|
61
|
+
return true;
|
|
62
|
+
return (0, node_fs_1.realpathSync)(appExpo) !== (0, node_fs_1.realpathSync)(hoistedExpo);
|
|
63
|
+
}
|
|
34
64
|
const metro_resolver_1 = require("./metro-resolver");
|
|
35
65
|
function withNxMetro(userConfig, opts = {}) {
|
|
36
66
|
const extensions = ['', 'ts', 'tsx', 'js', 'jsx', 'json'];
|
|
@@ -46,30 +76,30 @@ function withNxMetro(userConfig, opts = {}) {
|
|
|
46
76
|
watchFolders = watchFolders.concat(opts.watchFolders);
|
|
47
77
|
}
|
|
48
78
|
watchFolders = [...new Set(watchFolders)].filter((folder) => (0, node_fs_1.existsSync)(folder));
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// `
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
usesExpoMetro = true;
|
|
63
|
-
}
|
|
64
|
-
catch { }
|
|
79
|
+
// `getDefaultConfig(__dirname)` set this to the app directory
|
|
80
|
+
const appRoot = userConfig.projectRoot;
|
|
81
|
+
const usesExpoMetro = appUsesExpoMetro(appRoot);
|
|
82
|
+
const appNodeModules = appRoot ? (0, path_1.join)(appRoot, 'node_modules') : null;
|
|
83
|
+
const hasAppNodeModules = !!appNodeModules && (0, node_fs_1.existsSync)(appNodeModules);
|
|
84
|
+
// an app pinning its own `expo` must stay anchored at the app, or the
|
|
85
|
+
// bundle picks up the hoisted copy alongside its own. Compare real paths:
|
|
86
|
+
// `ensureNodeModulesSymlink` links the whole app node_modules to the
|
|
87
|
+
// workspace's, which is not an app-owned copy.
|
|
88
|
+
const ownsExpo = hasAppNodeModules && appOwnsExpo(appNodeModules);
|
|
89
|
+
// SDK 55+ resolves Babel config and the HMR client relative to
|
|
90
|
+
// `projectRoot`; SDK 53/54 apps relying on hoisted Expo need the workspace
|
|
91
|
+
// root so `originModulePath` stays workspace-relative for the Nx resolver.
|
|
65
92
|
const nxConfig = {
|
|
66
|
-
...(usesExpoMetro ? {} : { projectRoot: devkit_1.workspaceRoot }),
|
|
93
|
+
...(usesExpoMetro || ownsExpo ? {} : { projectRoot: devkit_1.workspaceRoot }),
|
|
67
94
|
resolver: {
|
|
68
|
-
resolveRequest: (0, metro_resolver_1.getResolveRequest)(extensions, opts.exportsConditionNames, opts.mainFields),
|
|
69
|
-
nodeModulesPaths: [
|
|
95
|
+
resolveRequest: (0, metro_resolver_1.getResolveRequest)(extensions, opts.exportsConditionNames, opts.mainFields, { appRoot, usesExpoMetro }),
|
|
96
|
+
nodeModulesPaths: [
|
|
97
|
+
...(hasAppNodeModules ? [appNodeModules] : []),
|
|
98
|
+
(0, path_1.join)(devkit_1.workspaceRoot, 'node_modules'),
|
|
99
|
+
],
|
|
70
100
|
},
|
|
71
101
|
watchFolders,
|
|
72
102
|
};
|
|
73
|
-
const { mergeConfig } = getMetroConfig();
|
|
103
|
+
const { mergeConfig } = getMetroConfig(appRoot, usesExpoMetro);
|
|
74
104
|
return mergeConfig(userConfig, nxConfig);
|
|
75
105
|
}
|
|
@@ -60,27 +60,52 @@ function serveAsync(workspaceRoot, projectRoot, options) {
|
|
|
60
60
|
env: process.env,
|
|
61
61
|
stdio: ['inherit', 'pipe', 'pipe', 'ipc'],
|
|
62
62
|
});
|
|
63
|
+
let settled = false;
|
|
64
|
+
const settleResolve = (cp) => {
|
|
65
|
+
if (settled)
|
|
66
|
+
return;
|
|
67
|
+
settled = true;
|
|
68
|
+
resolve(cp);
|
|
69
|
+
};
|
|
70
|
+
const settleReject = (err) => {
|
|
71
|
+
if (settled)
|
|
72
|
+
return;
|
|
73
|
+
settled = true;
|
|
74
|
+
reject(err);
|
|
75
|
+
};
|
|
76
|
+
// Expo bundles on first request, so waiting on a bundle log line deadlocks
|
|
77
|
+
// against a consumer that only requests once we report ready (@nx/cypress).
|
|
78
|
+
// /status answers packager-status:running once Metro's bundler is ready.
|
|
79
|
+
void (async () => {
|
|
80
|
+
while (!settled) {
|
|
81
|
+
if ((await (0, is_packager_running_1.isPackagerRunning)(options.port)) === 'running') {
|
|
82
|
+
settleResolve(childProcess);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
86
|
+
}
|
|
87
|
+
})();
|
|
63
88
|
childProcess.stdout.on('data', (data) => {
|
|
64
89
|
process.stdout.write(data);
|
|
65
90
|
if (data.toString().includes('Bundling complete') ||
|
|
66
91
|
data.toString().includes('Bundled')) {
|
|
67
|
-
|
|
92
|
+
settleResolve(childProcess);
|
|
68
93
|
}
|
|
69
94
|
});
|
|
70
95
|
childProcess.stderr.on('data', (data) => {
|
|
71
96
|
process.stderr.write(data);
|
|
72
97
|
});
|
|
73
98
|
childProcess.on('error', (err) => {
|
|
74
|
-
|
|
99
|
+
settleReject(err);
|
|
75
100
|
});
|
|
76
101
|
childProcess.on('exit', (code, signal) => {
|
|
77
102
|
if (code === null)
|
|
78
103
|
code = (0, internal_1.signalToCode)(signal);
|
|
79
104
|
if (code === 0) {
|
|
80
|
-
|
|
105
|
+
settleResolve(childProcess);
|
|
81
106
|
}
|
|
82
107
|
else {
|
|
83
|
-
|
|
108
|
+
settleReject(code);
|
|
84
109
|
}
|
|
85
110
|
});
|
|
86
111
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/expo",
|
|
3
|
-
"version": "23.2.0-beta.
|
|
3
|
+
"version": "23.2.0-beta.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Expo Plugin for Nx contains executors and generators for managing and developing an expo application within your workspace. For example, you can directly build for different target platforms as well as generate projects and publish your code.",
|
|
6
6
|
"keywords": [
|
|
@@ -61,18 +61,18 @@
|
|
|
61
61
|
"semver": "^7.6.3",
|
|
62
62
|
"tsconfig-paths": "^4.1.2",
|
|
63
63
|
"tslib": "^2.3.0",
|
|
64
|
-
"@nx/devkit": "23.2.0-beta.
|
|
65
|
-
"@nx/eslint": "23.2.0-beta.
|
|
66
|
-
"@nx/js": "23.2.0-beta.
|
|
67
|
-
"@nx/react": "23.2.0-beta.
|
|
64
|
+
"@nx/devkit": "23.2.0-beta.8",
|
|
65
|
+
"@nx/eslint": "23.2.0-beta.8",
|
|
66
|
+
"@nx/js": "23.2.0-beta.8",
|
|
67
|
+
"@nx/react": "23.2.0-beta.8"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"expo": ">=53.0.0",
|
|
71
71
|
"@expo/metro": ">= 55.0.0",
|
|
72
72
|
"metro-config": ">= 0.82.0",
|
|
73
73
|
"metro-resolver": ">= 0.82.0",
|
|
74
|
-
"@nx/cypress": "23.2.0-beta.
|
|
75
|
-
"@nx/playwright": "23.2.0-beta.
|
|
74
|
+
"@nx/cypress": "23.2.0-beta.8",
|
|
75
|
+
"@nx/playwright": "23.2.0-beta.8"
|
|
76
76
|
},
|
|
77
77
|
"peerDependenciesMeta": {
|
|
78
78
|
"@nx/cypress": {
|
|
@@ -95,11 +95,11 @@
|
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
|
-
"nx": "23.2.0-beta.
|
|
98
|
+
"nx": "23.2.0-beta.8"
|
|
99
99
|
},
|
|
100
100
|
"optionalDependencies": {
|
|
101
|
-
"@nx/detox": "23.2.0-beta.
|
|
102
|
-
"@nx/rollup": "23.2.0-beta.
|
|
101
|
+
"@nx/detox": "23.2.0-beta.8",
|
|
102
|
+
"@nx/rollup": "23.2.0-beta.8"
|
|
103
103
|
},
|
|
104
104
|
"executors": "./executors.json",
|
|
105
105
|
"ng-update": {
|