babel-preset-expo 54.0.9 → 54.0.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/build/define-plugin.d.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
import type { ConfigAPI, PluginObj } from '@babel/core';
|
|
9
|
-
declare function definePlugin({ types: t }: ConfigAPI & typeof import('@babel/core')): PluginObj
|
|
8
|
+
import type { ConfigAPI, PluginObj, PluginPass } from '@babel/core';
|
|
9
|
+
declare function definePlugin({ types: t, }: ConfigAPI & typeof import('@babel/core')): PluginObj<PluginPass & {
|
|
10
|
+
opts: Record<string, null | boolean | string>;
|
|
11
|
+
}>;
|
|
10
12
|
export default definePlugin;
|
package/build/define-plugin.js
CHANGED
|
@@ -7,30 +7,8 @@
|
|
|
7
7
|
* LICENSE file in the root directory of this source tree.
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
/**
|
|
11
|
-
* Checks if the given identifier is an ES module import
|
|
12
|
-
* @param {babelNode} identifierNodePath The node to check
|
|
13
|
-
* @return {boolean} Indicates if the provided node is an import specifier or references one
|
|
14
|
-
*/
|
|
15
|
-
const isImportIdentifier = (identifierNodePath) => {
|
|
16
|
-
if (identifierNodePath.container &&
|
|
17
|
-
!Array.isArray(identifierNodePath.container) &&
|
|
18
|
-
'type' in identifierNodePath.container) {
|
|
19
|
-
return (identifierNodePath.container.type === 'ImportDefaultSpecifier' ||
|
|
20
|
-
identifierNodePath.container.type === 'ImportSpecifier');
|
|
21
|
-
}
|
|
22
|
-
return false;
|
|
23
|
-
};
|
|
24
|
-
const memberExpressionComparator = (nodePath, value) => nodePath.matchesPattern(value);
|
|
25
|
-
const identifierComparator = (nodePath, value) => 'name' in nodePath.node && nodePath.node.name === value;
|
|
26
|
-
const unaryExpressionComparator = (nodePath, value) => {
|
|
27
|
-
if ('argument' in nodePath.node && nodePath.node.argument && 'name' in nodePath.node?.argument) {
|
|
28
|
-
return nodePath.node.argument.name === value;
|
|
29
|
-
}
|
|
30
|
-
return false;
|
|
31
|
-
};
|
|
32
10
|
const TYPEOF_PREFIX = 'typeof ';
|
|
33
|
-
function definePlugin({ types: t }) {
|
|
11
|
+
function definePlugin({ types: t, }) {
|
|
34
12
|
/**
|
|
35
13
|
* Replace a node with a given value. If the replacement results in a BinaryExpression, it will be
|
|
36
14
|
* evaluated. For example, if the result of the replacement is `var x = "production" === "production"`
|
|
@@ -38,79 +16,120 @@ function definePlugin({ types: t }) {
|
|
|
38
16
|
*/
|
|
39
17
|
function replaceAndEvaluateNode(nodePath, replacement) {
|
|
40
18
|
nodePath.replaceWith(t.valueToNode(replacement));
|
|
41
|
-
if (nodePath.parentPath
|
|
19
|
+
if (nodePath.parentPath?.isBinaryExpression()) {
|
|
42
20
|
const result = nodePath.parentPath.evaluate();
|
|
43
21
|
if (result.confident) {
|
|
44
22
|
nodePath.parentPath.replaceWith(t.valueToNode(result.value));
|
|
45
23
|
}
|
|
46
24
|
}
|
|
47
25
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
replacementKey in replacements) {
|
|
54
|
-
replaceAndEvaluateNode(nodePath, replacements[replacementKey]);
|
|
26
|
+
/** Get the root identifier name from a member expression (e.g., "process" from "process.env.NODE_ENV") */
|
|
27
|
+
function getMemberExpressionRoot(node) {
|
|
28
|
+
let current = node;
|
|
29
|
+
while (t.isMemberExpression(current)) {
|
|
30
|
+
current = current.object;
|
|
55
31
|
}
|
|
56
|
-
|
|
32
|
+
return t.isIdentifier(current) ? current.name : null;
|
|
33
|
+
}
|
|
57
34
|
return {
|
|
58
35
|
name: 'expo-define-globals',
|
|
36
|
+
pre() {
|
|
37
|
+
// Pre-process replacements once per file
|
|
38
|
+
const identifiers = new Map();
|
|
39
|
+
const memberPatterns = [];
|
|
40
|
+
const typeofValues = new Map();
|
|
41
|
+
const memberRoots = new Set();
|
|
42
|
+
for (const key of Object.keys(this.opts)) {
|
|
43
|
+
const value = this.opts[key];
|
|
44
|
+
if (key.startsWith(TYPEOF_PREFIX)) {
|
|
45
|
+
// "typeof window" -> typeofValues["window"]
|
|
46
|
+
typeofValues.set(key.slice(TYPEOF_PREFIX.length), value);
|
|
47
|
+
}
|
|
48
|
+
else if (key.includes('.')) {
|
|
49
|
+
// "process.env.NODE_ENV" -> memberPatterns, extract "process" as root
|
|
50
|
+
memberPatterns.push([key, value]);
|
|
51
|
+
const root = key.split('.')[0];
|
|
52
|
+
memberRoots.add(root);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// "__DEV__" -> identifiers
|
|
56
|
+
identifiers.set(key, value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
this.processed = {
|
|
60
|
+
identifiers,
|
|
61
|
+
memberPatterns,
|
|
62
|
+
typeofValues,
|
|
63
|
+
memberRoots,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
59
66
|
visitor: {
|
|
60
67
|
// process.env.NODE_ENV;
|
|
61
68
|
MemberExpression(nodePath, state) {
|
|
62
|
-
if (
|
|
63
69
|
// Prevent rewriting if the member expression is on the left-hand side of an assignment
|
|
64
|
-
|
|
70
|
+
if (t.isAssignmentExpression(nodePath.parent) && nodePath.parent.left === nodePath.node) {
|
|
65
71
|
return;
|
|
66
72
|
}
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
const { memberPatterns, memberRoots } = state.processed;
|
|
74
|
+
if (memberPatterns.length === 0)
|
|
75
|
+
return;
|
|
76
|
+
// Quick filter: check if root matches any known pattern root
|
|
77
|
+
const root = getMemberExpressionRoot(nodePath.node);
|
|
78
|
+
if (!root || !memberRoots.has(root))
|
|
79
|
+
return;
|
|
80
|
+
// Check against patterns
|
|
81
|
+
for (const [pattern, replacement] of memberPatterns) {
|
|
82
|
+
if (nodePath.matchesPattern(pattern)) {
|
|
83
|
+
replaceAndEvaluateNode(nodePath, replacement);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
70
87
|
},
|
|
71
88
|
// const x = { version: VERSION };
|
|
72
89
|
ReferencedIdentifier(nodePath, state) {
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
const { identifiers } = state.processed;
|
|
91
|
+
if (identifiers.size === 0)
|
|
92
|
+
return;
|
|
93
|
+
const name = nodePath.node.name;
|
|
94
|
+
// Quick check: is this identifier in our replacements?
|
|
95
|
+
if (!identifiers.has(name))
|
|
96
|
+
return;
|
|
97
|
+
// Check for binding (locally defined variable shadows replacement)
|
|
98
|
+
if (nodePath.scope?.getBinding(name))
|
|
99
|
+
return;
|
|
100
|
+
// Don't transform import identifiers (mimics webpack's DefinePlugin behavior)
|
|
101
|
+
const container = nodePath.container;
|
|
102
|
+
if (container &&
|
|
103
|
+
!Array.isArray(container) &&
|
|
104
|
+
'type' in container &&
|
|
105
|
+
(container.type === 'ImportDefaultSpecifier' || container.type === 'ImportSpecifier')) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Do not transform Object keys / properties unless they are computed like {[key]: value}
|
|
109
|
+
if ((nodePath.key === 'key' || nodePath.key === 'property') &&
|
|
110
|
+
nodePath.parent &&
|
|
111
|
+
'computed' in nodePath.parent &&
|
|
112
|
+
nodePath.parent.computed === false) {
|
|
87
113
|
return;
|
|
88
114
|
}
|
|
89
|
-
|
|
90
|
-
assertOptions(replacements);
|
|
91
|
-
processNode(replacements, nodePath, identifierComparator);
|
|
115
|
+
replaceAndEvaluateNode(nodePath, identifiers.get(name));
|
|
92
116
|
},
|
|
93
117
|
// typeof window
|
|
94
118
|
UnaryExpression(nodePath, state) {
|
|
95
|
-
if (nodePath.node.operator !== 'typeof')
|
|
119
|
+
if (nodePath.node.operator !== 'typeof')
|
|
120
|
+
return;
|
|
121
|
+
const { typeofValues } = state.processed;
|
|
122
|
+
if (typeofValues.size === 0)
|
|
123
|
+
return;
|
|
124
|
+
const argument = nodePath.node.argument;
|
|
125
|
+
if (!t.isIdentifier(argument))
|
|
96
126
|
return;
|
|
127
|
+
const replacement = typeofValues.get(argument.name);
|
|
128
|
+
if (replacement !== undefined) {
|
|
129
|
+
replaceAndEvaluateNode(nodePath, replacement);
|
|
97
130
|
}
|
|
98
|
-
const replacements = state.opts;
|
|
99
|
-
assertOptions(replacements);
|
|
100
|
-
const typeofValues = {};
|
|
101
|
-
Object.keys(replacements).forEach((key) => {
|
|
102
|
-
if (key.substring(0, TYPEOF_PREFIX.length) === TYPEOF_PREFIX) {
|
|
103
|
-
typeofValues[key.substring(TYPEOF_PREFIX.length)] = replacements[key];
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
processNode(typeofValues, nodePath, unaryExpressionComparator);
|
|
107
131
|
},
|
|
108
132
|
},
|
|
109
133
|
};
|
|
110
134
|
}
|
|
111
|
-
function assertOptions(opts) {
|
|
112
|
-
if (opts == null || typeof opts !== 'object') {
|
|
113
|
-
throw new Error('define plugin expects an object as options');
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
135
|
exports.default = definePlugin;
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
import type { ConfigAPI, PluginObj } from '@babel/core';
|
|
2
|
-
|
|
1
|
+
import type { ConfigAPI, PluginObj, PluginPass } from '@babel/core';
|
|
2
|
+
interface InlineManifestState extends PluginPass {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function expoInlineManifestPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj<InlineManifestState>;
|
|
6
|
+
export {};
|
|
@@ -4,7 +4,7 @@ exports.expoInlineManifestPlugin = expoInlineManifestPlugin;
|
|
|
4
4
|
const common_1 = require("./common");
|
|
5
5
|
const debug = require('debug')('expo:babel:inline-manifest');
|
|
6
6
|
// Convert expo value to PWA value
|
|
7
|
-
function
|
|
7
|
+
function ensurePWAOrientation(orientation) {
|
|
8
8
|
if (orientation) {
|
|
9
9
|
const webOrientation = orientation.toLowerCase();
|
|
10
10
|
if (webOrientation !== 'default') {
|
|
@@ -52,7 +52,7 @@ function applyWebDefaults({ config, appName, webName }) {
|
|
|
52
52
|
const startUrl = webManifest.startUrl;
|
|
53
53
|
const { scope, crossorigin } = webManifest;
|
|
54
54
|
const barStyle = webManifest.barStyle;
|
|
55
|
-
const orientation =
|
|
55
|
+
const orientation = ensurePWAOrientation(webManifest.orientation || appJSON.orientation);
|
|
56
56
|
/**
|
|
57
57
|
* **Splash screen background color**
|
|
58
58
|
* `https://developers.google.com/web/fundamentals/web-app-manifest/#splash-screen`
|
|
@@ -144,34 +144,46 @@ function expoInlineManifestPlugin(api) {
|
|
|
144
144
|
const platform = api.caller(common_1.getPlatform);
|
|
145
145
|
const possibleProjectRoot = api.caller(common_1.getPossibleProjectRoot);
|
|
146
146
|
const shouldInline = platform === 'web' || isReactServer;
|
|
147
|
+
// Early exit: return a no-op plugin if we're not going to inline anything
|
|
148
|
+
if (!shouldInline) {
|
|
149
|
+
return {
|
|
150
|
+
name: 'expo-inline-manifest-plugin',
|
|
151
|
+
visitor: {},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
147
154
|
return {
|
|
148
155
|
name: 'expo-inline-manifest-plugin',
|
|
156
|
+
pre() {
|
|
157
|
+
this.projectRoot = possibleProjectRoot || this.file.opts.root || '';
|
|
158
|
+
},
|
|
149
159
|
visitor: {
|
|
150
160
|
MemberExpression(path, state) {
|
|
151
|
-
//
|
|
152
|
-
|
|
161
|
+
// We're looking for: process.env.APP_MANIFEST
|
|
162
|
+
// This visitor is called on every MemberExpression, so we need fast checks
|
|
163
|
+
// Quick check: the property we're looking for is 'APP_MANIFEST'
|
|
164
|
+
// The parent must be a MemberExpression with property 'APP_MANIFEST'
|
|
165
|
+
const parent = path.parentPath;
|
|
166
|
+
if (!parent?.isMemberExpression())
|
|
153
167
|
return;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
168
|
+
// Check if parent's property is APP_MANIFEST (most selective check first)
|
|
169
|
+
const parentProp = parent.node.property;
|
|
170
|
+
if (!t.isIdentifier(parentProp) || parentProp.name !== 'APP_MANIFEST')
|
|
157
171
|
return;
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
172
|
+
// Now verify this is process.env
|
|
173
|
+
const obj = path.node.object;
|
|
174
|
+
const prop = path.node.property;
|
|
175
|
+
if (!t.isIdentifier(obj) || obj.name !== 'process')
|
|
176
|
+
return;
|
|
177
|
+
if (!t.isIdentifier(prop) || prop.name !== 'env')
|
|
178
|
+
return;
|
|
179
|
+
// Skip if this is an assignment target
|
|
180
|
+
if (parent.parentPath?.isAssignmentExpression())
|
|
161
181
|
return;
|
|
162
|
-
}
|
|
163
|
-
const projectRoot = possibleProjectRoot || state.file.opts.root || '';
|
|
164
|
-
if (
|
|
165
182
|
// Surfaces the `app.json` (config) as an environment variable which is then parsed by
|
|
166
183
|
// `expo-constants` https://docs.expo.dev/versions/latest/sdk/constants/
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
!parent.parentPath.isAssignmentExpression()) {
|
|
171
|
-
const manifest = getExpoAppManifest(projectRoot);
|
|
172
|
-
if (manifest !== null) {
|
|
173
|
-
parent.replaceWith(t.stringLiteral(manifest));
|
|
174
|
-
}
|
|
184
|
+
const manifest = getExpoAppManifest(state.projectRoot);
|
|
185
|
+
if (manifest !== null) {
|
|
186
|
+
parent.replaceWith(t.stringLiteral(manifest));
|
|
175
187
|
}
|
|
176
188
|
},
|
|
177
189
|
},
|
|
@@ -5,14 +5,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.expoRouterBabelPlugin = expoRouterBabelPlugin;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
-
const resolve_from_1 = __importDefault(require("resolve-from"));
|
|
9
8
|
const common_1 = require("./common");
|
|
10
9
|
const debug = require('debug')('expo:babel:router');
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Compute the relative path from the current file to the app folder.
|
|
12
|
+
*
|
|
13
|
+
* Previously this was computed relative to `expo-router/entry`, but that breaks
|
|
14
|
+
* when packages are hoisted to unexpected locations (e.g., with Bun in monorepos).
|
|
15
|
+
* By using the actual file being transformed, the relative path is always correct
|
|
16
|
+
* regardless of where the package is installed.
|
|
17
|
+
*/
|
|
18
|
+
function getExpoRouterAppRoot(currentFile, appFolder) {
|
|
19
|
+
const appRoot = node_path_1.default.relative(node_path_1.default.dirname(currentFile), appFolder);
|
|
20
|
+
debug('getExpoRouterAppRoot', currentFile, appFolder, appRoot);
|
|
16
21
|
return appRoot;
|
|
17
22
|
}
|
|
18
23
|
/**
|
|
@@ -28,35 +33,61 @@ function expoRouterBabelPlugin(api) {
|
|
|
28
33
|
const possibleProjectRoot = api.caller(common_1.getPossibleProjectRoot);
|
|
29
34
|
const asyncRoutes = api.caller(common_1.getAsyncRoutes);
|
|
30
35
|
const routerAbsoluteRoot = api.caller(common_1.getExpoRouterAbsoluteAppRoot);
|
|
31
|
-
|
|
32
|
-
return t.isAssignmentExpression(path.parent) && path.parent.left === path.node;
|
|
33
|
-
}
|
|
36
|
+
const importMode = asyncRoutes ? 'lazy' : 'sync';
|
|
34
37
|
return {
|
|
35
38
|
name: 'expo-router',
|
|
39
|
+
pre() {
|
|
40
|
+
const state = this;
|
|
41
|
+
state.projectRoot = possibleProjectRoot || this.file.opts.root || '';
|
|
42
|
+
// Check test env at transform time, not module load time
|
|
43
|
+
state.isTestEnv = process.env.NODE_ENV === 'test';
|
|
44
|
+
},
|
|
36
45
|
visitor: {
|
|
37
46
|
MemberExpression(path, state) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
// Quick check: skip if not accessing something on an object named 'process'
|
|
48
|
+
const object = path.node.object;
|
|
49
|
+
if (!t.isMemberExpression(object))
|
|
50
|
+
return;
|
|
51
|
+
const objectOfObject = object.object;
|
|
52
|
+
if (!t.isIdentifier(objectOfObject) || objectOfObject.name !== 'process')
|
|
53
|
+
return;
|
|
54
|
+
// Now check if it's process.env
|
|
55
|
+
if (!t.isIdentifier(object.property) || object.property.name !== 'env')
|
|
56
|
+
return;
|
|
57
|
+
// Skip if this is an assignment target
|
|
58
|
+
if (t.isAssignmentExpression(path.parent) && path.parent.left === path.node)
|
|
59
|
+
return;
|
|
60
|
+
// Get the property key
|
|
61
|
+
const key = path.toComputedKey();
|
|
62
|
+
if (!t.isStringLiteral(key))
|
|
63
|
+
return;
|
|
64
|
+
const keyValue = key.value;
|
|
65
|
+
// Check each possible env var
|
|
66
|
+
switch (keyValue) {
|
|
67
|
+
case 'EXPO_PROJECT_ROOT':
|
|
68
|
+
path.replaceWith(t.stringLiteral(state.projectRoot));
|
|
69
|
+
return;
|
|
70
|
+
case 'EXPO_ROUTER_IMPORT_MODE':
|
|
71
|
+
path.replaceWith(t.stringLiteral(importMode));
|
|
72
|
+
return;
|
|
73
|
+
default:
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
// Skip app root transforms in tests (handled by testing-library utils)
|
|
77
|
+
if (state.isTestEnv)
|
|
78
|
+
return;
|
|
79
|
+
switch (keyValue) {
|
|
80
|
+
case 'EXPO_ROUTER_ABS_APP_ROOT':
|
|
81
|
+
path.replaceWith(t.stringLiteral(routerAbsoluteRoot));
|
|
82
|
+
return;
|
|
83
|
+
case 'EXPO_ROUTER_APP_ROOT': {
|
|
84
|
+
// Use the actual file being transformed to compute the relative path.
|
|
85
|
+
// This ensures the path is correct regardless of package hoisting.
|
|
86
|
+
const filename = state.filename || state.file.opts.filename;
|
|
87
|
+
if (!filename) {
|
|
88
|
+
throw new Error('babel-preset-expo: Unable to determine filename for EXPO_ROUTER_APP_ROOT transformation');
|
|
59
89
|
}
|
|
90
|
+
path.replaceWith(t.stringLiteral(getExpoRouterAppRoot(filename, routerAbsoluteRoot)));
|
|
60
91
|
}
|
|
61
92
|
}
|
|
62
93
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "babel-preset-expo",
|
|
3
|
-
"version": "54.0.
|
|
3
|
+
"version": "54.0.10",
|
|
4
4
|
"description": "The Babel preset for Expo projects",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"files": [
|
|
@@ -85,5 +85,5 @@
|
|
|
85
85
|
"jest": "^29.2.1",
|
|
86
86
|
"react-refresh": "^0.14.2"
|
|
87
87
|
},
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "a68247841f460700a3066c7bc7954a72591b26bc"
|
|
89
89
|
}
|