babel-preset-expo 10.0.2 → 11.0.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/README.md +18 -0
- package/build/client-module-proxy-plugin.d.ts +1 -0
- package/build/client-module-proxy-plugin.js +57 -0
- package/build/common.d.ts +2 -0
- package/build/common.js +18 -14
- package/build/define-plugin.d.ts +12 -0
- package/build/define-plugin.js +140 -0
- package/build/environment-restricted-imports.d.ts +8 -0
- package/build/environment-restricted-imports.js +60 -0
- package/build/expo-inline-manifest-plugin.js +2 -3
- package/build/expo-router-plugin.js +1 -2
- package/build/index.d.ts +2 -0
- package/build/index.js +49 -29
- package/build/inline-env-vars.d.ts +0 -9
- package/build/inline-env-vars.js +1 -33
- package/build/minify-platform-select-plugin.d.ts +11 -0
- package/build/minify-platform-select-plugin.js +73 -0
- package/build/restricted-react-api-plugin.d.ts +7 -0
- package/build/restricted-react-api-plugin.js +115 -0
- package/build/web-preset.d.ts +11 -0
- package/build/web-preset.js +73 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -42,6 +42,24 @@ If the `bundler` is not defined, it will default to checking if a `babel-loader`
|
|
|
42
42
|
|
|
43
43
|
## Options
|
|
44
44
|
|
|
45
|
+
### `minifyTypeofWindow`
|
|
46
|
+
|
|
47
|
+
Set `minifyTypeofWindow: false` to preserve the `typeof window` check in your code, e.g. `if (typeof window === 'undefined')` -> `if (true)` in servers. This is useful when you're using libraries that mock the window object on native or in the server.
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
[
|
|
51
|
+
'babel-preset-expo',
|
|
52
|
+
{
|
|
53
|
+
// If your native app doesn't polyfill `window` then setting this to `false` can reduce bundle size.
|
|
54
|
+
native: {
|
|
55
|
+
minifyTypeofWindow: true,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Defaults to `false` for server environments and web, `true` for native platforms to support legacy browser polyfills.
|
|
62
|
+
|
|
45
63
|
### `reanimated`
|
|
46
64
|
|
|
47
65
|
`boolean`, defaults to `true`. Set `reanimated: false` to disable adding the `react-native-reanimated/plugin` when `react-native-reanimated` is installed.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function reactClientReferencesPlugin(): babel.PluginObj;
|
|
@@ -0,0 +1,57 @@
|
|
|
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.reactClientReferencesPlugin = reactClientReferencesPlugin;
|
|
7
|
+
/**
|
|
8
|
+
* Copyright © 2024 650 Industries.
|
|
9
|
+
*/
|
|
10
|
+
const core_1 = require("@babel/core");
|
|
11
|
+
const url_1 = __importDefault(require("url"));
|
|
12
|
+
function reactClientReferencesPlugin() {
|
|
13
|
+
return {
|
|
14
|
+
name: 'expo-client-references',
|
|
15
|
+
visitor: {
|
|
16
|
+
Program(path, state) {
|
|
17
|
+
const isUseClient = path.node.directives.some((directive) => directive.value.value === 'use client');
|
|
18
|
+
// TODO: use server can be added to scopes inside of the file. https://github.com/facebook/react/blob/29fbf6f62625c4262035f931681c7b7822ca9843/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js#L55
|
|
19
|
+
const isUseServer = path.node.directives.some((directive) => directive.value.value === 'use server');
|
|
20
|
+
if (isUseClient && isUseServer) {
|
|
21
|
+
throw path.buildCodeFrameError("It's not possible to have both `use client` and `use server` directives in the same file.");
|
|
22
|
+
}
|
|
23
|
+
const filePath = state.file.opts.filename;
|
|
24
|
+
if (!filePath) {
|
|
25
|
+
// This can happen in tests or systems that use Babel standalone.
|
|
26
|
+
throw new Error('[Babel] Expected a filename to be set in the state');
|
|
27
|
+
}
|
|
28
|
+
const outputKey = url_1.default.pathToFileURL(filePath).href;
|
|
29
|
+
// File starts with "use client" directive.
|
|
30
|
+
if (!isUseClient && !isUseServer) {
|
|
31
|
+
// Do nothing for code that isn't marked as a client component.
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Clear the body
|
|
35
|
+
if (isUseClient) {
|
|
36
|
+
path.node.body = [];
|
|
37
|
+
path.node.directives = [];
|
|
38
|
+
path.pushContainer('body', core_1.template.ast `module.exports = require("react-server-dom-webpack/server").createClientModuleProxy(${JSON.stringify(outputKey)});`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
path.pushContainer('body', core_1.template.ast `
|
|
42
|
+
;(() => {
|
|
43
|
+
if (typeof module.exports === 'function') {
|
|
44
|
+
require('react-server-dom-webpack/server').registerServerReference(module.exports, ${JSON.stringify(outputKey)}, null);
|
|
45
|
+
} else {
|
|
46
|
+
for (const key in module.exports) {
|
|
47
|
+
if (typeof module.exports[key] === 'function') {
|
|
48
|
+
require('react-server-dom-webpack/server').registerServerReference(module.exports[key], ${JSON.stringify(outputKey)}, key);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
})()`);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
package/build/common.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export declare function hasModule(name: string): boolean;
|
|
|
3
3
|
export declare function getBundler(caller: any): any;
|
|
4
4
|
export declare function getPlatform(caller: any): any;
|
|
5
5
|
export declare function getPossibleProjectRoot(caller: any): any;
|
|
6
|
+
/** If bundling for a react-server target. */
|
|
7
|
+
export declare function getIsReactServer(caller: any): boolean;
|
|
6
8
|
export declare function getIsDev(caller: any): any;
|
|
7
9
|
export declare function getIsFastRefreshEnabled(caller: any): any;
|
|
8
10
|
export declare function getIsProd(caller: any): boolean;
|
package/build/common.js
CHANGED
|
@@ -3,7 +3,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.hasModule = hasModule;
|
|
7
|
+
exports.getBundler = getBundler;
|
|
8
|
+
exports.getPlatform = getPlatform;
|
|
9
|
+
exports.getPossibleProjectRoot = getPossibleProjectRoot;
|
|
10
|
+
exports.getIsReactServer = getIsReactServer;
|
|
11
|
+
exports.getIsDev = getIsDev;
|
|
12
|
+
exports.getIsFastRefreshEnabled = getIsFastRefreshEnabled;
|
|
13
|
+
exports.getIsProd = getIsProd;
|
|
14
|
+
exports.getIsNodeModule = getIsNodeModule;
|
|
15
|
+
exports.getBaseUrl = getBaseUrl;
|
|
16
|
+
exports.getIsServer = getIsServer;
|
|
17
|
+
exports.getExpoRouterAbsoluteAppRoot = getExpoRouterAbsoluteAppRoot;
|
|
18
|
+
exports.getInlineEnvVarsEnabled = getInlineEnvVarsEnabled;
|
|
19
|
+
exports.getAsyncRoutes = getAsyncRoutes;
|
|
7
20
|
const path_1 = __importDefault(require("path"));
|
|
8
21
|
function hasModule(name) {
|
|
9
22
|
try {
|
|
@@ -16,7 +29,6 @@ function hasModule(name) {
|
|
|
16
29
|
throw error;
|
|
17
30
|
}
|
|
18
31
|
}
|
|
19
|
-
exports.hasModule = hasModule;
|
|
20
32
|
/** Determine which bundler is being used. */
|
|
21
33
|
function getBundler(caller) {
|
|
22
34
|
if (!caller)
|
|
@@ -33,7 +45,6 @@ function getBundler(caller) {
|
|
|
33
45
|
// Assume anything else is Metro.
|
|
34
46
|
return 'metro';
|
|
35
47
|
}
|
|
36
|
-
exports.getBundler = getBundler;
|
|
37
48
|
function getPlatform(caller) {
|
|
38
49
|
if (!caller)
|
|
39
50
|
return null;
|
|
@@ -46,7 +57,6 @@ function getPlatform(caller) {
|
|
|
46
57
|
// unknown
|
|
47
58
|
return caller.platform;
|
|
48
59
|
}
|
|
49
|
-
exports.getPlatform = getPlatform;
|
|
50
60
|
function getPossibleProjectRoot(caller) {
|
|
51
61
|
if (!caller)
|
|
52
62
|
return null;
|
|
@@ -55,39 +65,36 @@ function getPossibleProjectRoot(caller) {
|
|
|
55
65
|
// unknown
|
|
56
66
|
return process.env.EXPO_PROJECT_ROOT;
|
|
57
67
|
}
|
|
58
|
-
|
|
68
|
+
/** If bundling for a react-server target. */
|
|
69
|
+
function getIsReactServer(caller) {
|
|
70
|
+
return caller?.isReactServer ?? false;
|
|
71
|
+
}
|
|
59
72
|
function getIsDev(caller) {
|
|
60
73
|
if (caller?.isDev != null)
|
|
61
74
|
return caller.isDev;
|
|
62
75
|
// https://babeljs.io/docs/options#envname
|
|
63
76
|
return process.env.BABEL_ENV === 'development' || process.env.NODE_ENV === 'development';
|
|
64
77
|
}
|
|
65
|
-
exports.getIsDev = getIsDev;
|
|
66
78
|
function getIsFastRefreshEnabled(caller) {
|
|
67
79
|
if (!caller)
|
|
68
80
|
return false;
|
|
69
81
|
return caller.isHMREnabled && !caller.isServer && !caller.isNodeModule && getIsDev(caller);
|
|
70
82
|
}
|
|
71
|
-
exports.getIsFastRefreshEnabled = getIsFastRefreshEnabled;
|
|
72
83
|
function getIsProd(caller) {
|
|
73
84
|
if (caller?.isDev != null)
|
|
74
85
|
return caller.isDev === false;
|
|
75
86
|
// https://babeljs.io/docs/options#envname
|
|
76
87
|
return process.env.BABEL_ENV === 'production' || process.env.NODE_ENV === 'production';
|
|
77
88
|
}
|
|
78
|
-
exports.getIsProd = getIsProd;
|
|
79
89
|
function getIsNodeModule(caller) {
|
|
80
90
|
return caller?.isNodeModule ?? false;
|
|
81
91
|
}
|
|
82
|
-
exports.getIsNodeModule = getIsNodeModule;
|
|
83
92
|
function getBaseUrl(caller) {
|
|
84
93
|
return caller?.baseUrl ?? '';
|
|
85
94
|
}
|
|
86
|
-
exports.getBaseUrl = getBaseUrl;
|
|
87
95
|
function getIsServer(caller) {
|
|
88
96
|
return caller?.isServer ?? false;
|
|
89
97
|
}
|
|
90
|
-
exports.getIsServer = getIsServer;
|
|
91
98
|
function getExpoRouterAbsoluteAppRoot(caller) {
|
|
92
99
|
const rootModuleId = caller?.routerRoot ?? './app';
|
|
93
100
|
if (path_1.default.isAbsolute(rootModuleId)) {
|
|
@@ -96,7 +103,6 @@ function getExpoRouterAbsoluteAppRoot(caller) {
|
|
|
96
103
|
const projectRoot = getPossibleProjectRoot(caller) || '/';
|
|
97
104
|
return path_1.default.join(projectRoot, rootModuleId);
|
|
98
105
|
}
|
|
99
|
-
exports.getExpoRouterAbsoluteAppRoot = getExpoRouterAbsoluteAppRoot;
|
|
100
106
|
function getInlineEnvVarsEnabled(caller) {
|
|
101
107
|
const isWebpack = getBundler(caller) === 'webpack';
|
|
102
108
|
const isDev = getIsDev(caller);
|
|
@@ -107,7 +113,6 @@ function getInlineEnvVarsEnabled(caller) {
|
|
|
107
113
|
// Servers have env vars left as-is to read from the environment.
|
|
108
114
|
return !isNodeModule && !isWebpack && !isDev && !isServer && !preserveEnvVars;
|
|
109
115
|
}
|
|
110
|
-
exports.getInlineEnvVarsEnabled = getInlineEnvVarsEnabled;
|
|
111
116
|
function getAsyncRoutes(caller) {
|
|
112
117
|
const isServer = getIsServer(caller);
|
|
113
118
|
if (isServer) {
|
|
@@ -120,4 +125,3 @@ function getAsyncRoutes(caller) {
|
|
|
120
125
|
}
|
|
121
126
|
return caller?.asyncRoutes ?? false;
|
|
122
127
|
}
|
|
123
|
-
exports.getAsyncRoutes = getAsyncRoutes;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2024 650 Industries.
|
|
3
|
+
* Copyright (c) 2016 Formidable
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import * as t from '@babel/types';
|
|
9
|
+
declare const plugin: ({ types }: {
|
|
10
|
+
types: typeof t;
|
|
11
|
+
}) => babel.PluginObj;
|
|
12
|
+
export default plugin;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright © 2024 650 Industries.
|
|
4
|
+
* Copyright (c) 2016 Formidable
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
26
|
+
if (mod && mod.__esModule) return mod;
|
|
27
|
+
var result = {};
|
|
28
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
29
|
+
__setModuleDefault(result, mod);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
const t = __importStar(require("@babel/types"));
|
|
34
|
+
/**
|
|
35
|
+
* Replace a node with a given value. If the replacement results in a BinaryExpression, it will be
|
|
36
|
+
* evaluated. For example, if the result of the replacement is `var x = "production" === "production"`
|
|
37
|
+
* The evaluation will make a second replacement resulting in `var x = true`
|
|
38
|
+
*/
|
|
39
|
+
function replaceAndEvaluateNode(nodePath, replacement) {
|
|
40
|
+
nodePath.replaceWith(t.valueToNode(replacement));
|
|
41
|
+
if (nodePath.parentPath && nodePath.parentPath.isBinaryExpression()) {
|
|
42
|
+
const result = nodePath.parentPath.evaluate();
|
|
43
|
+
if (result.confident) {
|
|
44
|
+
nodePath.parentPath.replaceWith(t.valueToNode(result.value));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Checks if the given identifier is an ES module import
|
|
50
|
+
* @param {babelNode} identifierNodePath The node to check
|
|
51
|
+
* @return {boolean} Indicates if the provided node is an import specifier or references one
|
|
52
|
+
*/
|
|
53
|
+
const isImportIdentifier = (identifierNodePath) => {
|
|
54
|
+
if (identifierNodePath.container &&
|
|
55
|
+
!Array.isArray(identifierNodePath.container) &&
|
|
56
|
+
'type' in identifierNodePath.container) {
|
|
57
|
+
return (identifierNodePath.container.type === 'ImportDefaultSpecifier' ||
|
|
58
|
+
identifierNodePath.container.type === 'ImportSpecifier');
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
};
|
|
62
|
+
const memberExpressionComparator = (nodePath, value) => nodePath.matchesPattern(value);
|
|
63
|
+
const identifierComparator = (nodePath, value) => 'name' in nodePath.node && nodePath.node.name === value;
|
|
64
|
+
const unaryExpressionComparator = (nodePath, value) => {
|
|
65
|
+
if ('argument' in nodePath.node && nodePath.node.argument && 'name' in nodePath.node?.argument) {
|
|
66
|
+
return nodePath.node.argument.name === value;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
};
|
|
70
|
+
const isLeftHandSideOfAssignmentExpression = (node, parent) => t.isAssignmentExpression(parent) && parent.left === node;
|
|
71
|
+
const TYPEOF_PREFIX = 'typeof ';
|
|
72
|
+
const plugin = ({ types }) => {
|
|
73
|
+
const processNode = (replacements, nodePath, comparator) => {
|
|
74
|
+
const replacementKey = Object.keys(replacements).find((value) => comparator(nodePath, value));
|
|
75
|
+
if (typeof replacementKey === 'string' &&
|
|
76
|
+
replacements != null &&
|
|
77
|
+
replacementKey in replacements) {
|
|
78
|
+
replaceAndEvaluateNode(nodePath, replacements[replacementKey]);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
name: 'expo-define-globals',
|
|
83
|
+
visitor: {
|
|
84
|
+
// process.env.NODE_ENV;
|
|
85
|
+
MemberExpression(nodePath, state) {
|
|
86
|
+
if (
|
|
87
|
+
// Prevent rewriting if the member expression is on the left-hand side of an assignment
|
|
88
|
+
isLeftHandSideOfAssignmentExpression(nodePath.node, nodePath.parent)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const replacements = state.opts;
|
|
92
|
+
assertOptions(replacements);
|
|
93
|
+
processNode(replacements, nodePath, memberExpressionComparator);
|
|
94
|
+
},
|
|
95
|
+
// const x = { version: VERSION };
|
|
96
|
+
Identifier(nodePath, state) {
|
|
97
|
+
const binding = nodePath.scope?.getBinding(nodePath.node.name);
|
|
98
|
+
if (binding ||
|
|
99
|
+
// Don't transform import identifiers. This is meant to mimic webpack's
|
|
100
|
+
// DefinePlugin behavior.
|
|
101
|
+
isImportIdentifier(nodePath) ||
|
|
102
|
+
// Do not transform Object keys / properties unless they are computed like {[key]: value}
|
|
103
|
+
(nodePath.key === 'key' &&
|
|
104
|
+
nodePath.parent &&
|
|
105
|
+
'computed' in nodePath.parent &&
|
|
106
|
+
nodePath.parent.computed === false) ||
|
|
107
|
+
(nodePath.key === 'property' &&
|
|
108
|
+
nodePath.parent &&
|
|
109
|
+
'computed' in nodePath.parent &&
|
|
110
|
+
nodePath.parent.computed === false)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const replacements = state.opts;
|
|
114
|
+
assertOptions(replacements);
|
|
115
|
+
processNode(replacements, nodePath, identifierComparator);
|
|
116
|
+
},
|
|
117
|
+
// typeof window
|
|
118
|
+
UnaryExpression(nodePath, state) {
|
|
119
|
+
if (nodePath.node.operator !== 'typeof') {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const replacements = state.opts;
|
|
123
|
+
assertOptions(replacements);
|
|
124
|
+
const typeofValues = {};
|
|
125
|
+
Object.keys(replacements).forEach((key) => {
|
|
126
|
+
if (key.substring(0, TYPEOF_PREFIX.length) === TYPEOF_PREFIX) {
|
|
127
|
+
typeofValues[key.substring(TYPEOF_PREFIX.length)] = replacements[key];
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
processNode(typeofValues, nodePath, unaryExpressionComparator);
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
function assertOptions(opts) {
|
|
136
|
+
if (opts == null || typeof opts !== 'object') {
|
|
137
|
+
throw new Error('define plugin expects an object as options');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.default = plugin;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2024 650 Industries.
|
|
3
|
+
*/
|
|
4
|
+
import { ConfigAPI, types } from '@babel/core';
|
|
5
|
+
/** Prevent importing certain known imports in given environments. This is for sanity to ensure a module never accidentally gets imported unexpectedly. */
|
|
6
|
+
export declare function environmentRestrictedImportsPlugin(api: ConfigAPI & {
|
|
7
|
+
types: typeof types;
|
|
8
|
+
}): babel.PluginObj;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.environmentRestrictedImportsPlugin = environmentRestrictedImportsPlugin;
|
|
4
|
+
const common_1 = require("./common");
|
|
5
|
+
const FORBIDDEN_CLIENT_IMPORTS = ['server-only'];
|
|
6
|
+
const FORBIDDEN_REACT_SERVER_IMPORTS = ['client-only'];
|
|
7
|
+
/** Prevent importing certain known imports in given environments. This is for sanity to ensure a module never accidentally gets imported unexpectedly. */
|
|
8
|
+
function environmentRestrictedImportsPlugin(api) {
|
|
9
|
+
const { types: t } = api;
|
|
10
|
+
const isReactServer = api.caller(common_1.getIsReactServer);
|
|
11
|
+
const forbiddenPackages = isReactServer
|
|
12
|
+
? FORBIDDEN_REACT_SERVER_IMPORTS
|
|
13
|
+
: FORBIDDEN_CLIENT_IMPORTS;
|
|
14
|
+
function checkSource(source, path) {
|
|
15
|
+
forbiddenPackages.forEach((forbiddenImport) => {
|
|
16
|
+
if (source === forbiddenImport) {
|
|
17
|
+
if (isReactServer) {
|
|
18
|
+
throw path.buildCodeFrameError(`Importing '${forbiddenImport}' module is not allowed in a React server bundle. Add the "use client" directive to this file or one of the parent modules to allow importing this module.`);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
throw path.buildCodeFrameError(`Importing '${forbiddenImport}' module is not allowed in a client component.`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
name: 'expo-environment-restricted-imports-plugin',
|
|
28
|
+
visitor: {
|
|
29
|
+
ImportDeclaration(path) {
|
|
30
|
+
checkSource(path.node.source.value, path);
|
|
31
|
+
},
|
|
32
|
+
ExportAllDeclaration(path) {
|
|
33
|
+
if (path.node.source) {
|
|
34
|
+
checkSource(path.node.source.value, path);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
ExportNamedDeclaration(path) {
|
|
38
|
+
if (path.node.source) {
|
|
39
|
+
checkSource(path.node.source.value, path);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
CallExpression(path) {
|
|
43
|
+
if ((('name' in path.node.callee && path.node.callee.name === 'require') ||
|
|
44
|
+
(t.isMemberExpression(path.node.callee) &&
|
|
45
|
+
'name' in path.node.callee.property &&
|
|
46
|
+
['resolveWeak', 'importAll', 'importDefault'].includes(path.node.callee.property.name))) &&
|
|
47
|
+
path.node.arguments.length > 0 &&
|
|
48
|
+
t.isStringLiteral(path.node.arguments[0])) {
|
|
49
|
+
checkSource(path.node.arguments[0].value, path);
|
|
50
|
+
}
|
|
51
|
+
// Handle dynamic import() syntax
|
|
52
|
+
else if (path.node.callee.type === 'Import' &&
|
|
53
|
+
path.node.arguments.length > 0 &&
|
|
54
|
+
t.isStringLiteral(path.node.arguments[0])) {
|
|
55
|
+
checkSource(path.node.arguments[0].value, path);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.expoInlineManifestPlugin =
|
|
3
|
+
exports.expoInlineManifestPlugin = expoInlineManifestPlugin;
|
|
4
4
|
const config_1 = require("expo/config");
|
|
5
5
|
const common_1 = require("./common");
|
|
6
6
|
const debug = require('debug')('expo:babel:inline-manifest');
|
|
@@ -23,7 +23,7 @@ const RESTRICTED_MANIFEST_FIELDS = [
|
|
|
23
23
|
'android',
|
|
24
24
|
// Hide internal / build values
|
|
25
25
|
'plugins',
|
|
26
|
-
'hooks',
|
|
26
|
+
'hooks', // hooks no longer exists in the typescript type but should still be removed
|
|
27
27
|
'_internal',
|
|
28
28
|
// Remove metro-specific values
|
|
29
29
|
'assetBundlePatterns',
|
|
@@ -146,4 +146,3 @@ function expoInlineManifestPlugin(api) {
|
|
|
146
146
|
},
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
|
-
exports.expoInlineManifestPlugin = expoInlineManifestPlugin;
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.expoRouterBabelPlugin =
|
|
6
|
+
exports.expoRouterBabelPlugin = expoRouterBabelPlugin;
|
|
7
7
|
const core_1 = require("@babel/core");
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const resolve_from_1 = __importDefault(require("resolve-from"));
|
|
@@ -78,4 +78,3 @@ function expoRouterBabelPlugin(api) {
|
|
|
78
78
|
},
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
-
exports.expoRouterBabelPlugin = expoRouterBabelPlugin;
|
package/build/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ type BabelPresetExpoPlatformOptions = {
|
|
|
13
13
|
disableFlowStripTypesTransform?: boolean;
|
|
14
14
|
enableBabelRuntime?: boolean;
|
|
15
15
|
unstable_transformProfile?: 'default' | 'hermes-stable' | 'hermes-canary';
|
|
16
|
+
/** Enable `typeof window` runtime checks. The default behavior is to minify `typeof window` on web clients to `"object"` and `"undefined"` on servers. */
|
|
17
|
+
minifyTypeofWindow?: boolean;
|
|
16
18
|
};
|
|
17
19
|
export type BabelPresetExpoOptions = BabelPresetExpoPlatformOptions & {
|
|
18
20
|
/** Web-specific settings. */
|
package/build/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const client_module_proxy_plugin_1 = require("./client-module-proxy-plugin");
|
|
3
4
|
const common_1 = require("./common");
|
|
5
|
+
const environment_restricted_imports_1 = require("./environment-restricted-imports");
|
|
4
6
|
const expo_inline_manifest_plugin_1 = require("./expo-inline-manifest-plugin");
|
|
5
7
|
const expo_router_plugin_1 = require("./expo-router-plugin");
|
|
6
8
|
const inline_env_vars_1 = require("./inline-env-vars");
|
|
7
9
|
const lazyImports_1 = require("./lazyImports");
|
|
10
|
+
const restricted_react_api_plugin_1 = require("./restricted-react-api-plugin");
|
|
8
11
|
function getOptions(options, platform) {
|
|
9
12
|
const tag = platform === 'web' ? 'web' : 'native';
|
|
10
13
|
return {
|
|
@@ -18,9 +21,12 @@ function babelPresetExpo(api, options = {}) {
|
|
|
18
21
|
let platform = api.caller((caller) => caller?.platform);
|
|
19
22
|
const engine = api.caller((caller) => caller?.engine) ?? 'default';
|
|
20
23
|
const isDev = api.caller(common_1.getIsDev);
|
|
24
|
+
const isServer = api.caller(common_1.getIsServer);
|
|
25
|
+
const isReactServer = api.caller(common_1.getIsReactServer);
|
|
21
26
|
const isFastRefreshEnabled = api.caller(common_1.getIsFastRefreshEnabled);
|
|
22
27
|
const baseUrl = api.caller(common_1.getBaseUrl);
|
|
23
28
|
const supportsStaticESM = api.caller((caller) => caller?.supportsStaticESM);
|
|
29
|
+
const isServerEnv = isServer || isReactServer;
|
|
24
30
|
// Unlike `isDev`, this will be `true` when the bundler is explicitly set to `production`,
|
|
25
31
|
// i.e. `false` when testing, development, or used with a bundler that doesn't specify the correct inputs.
|
|
26
32
|
const isProduction = api.caller(common_1.getIsProd);
|
|
@@ -31,6 +37,9 @@ function babelPresetExpo(api, options = {}) {
|
|
|
31
37
|
platform = 'web';
|
|
32
38
|
}
|
|
33
39
|
const platformOptions = getOptions(options, platform);
|
|
40
|
+
if (platformOptions.useTransformReactJSXExperimental != null) {
|
|
41
|
+
throw new Error(`babel-preset-expo: The option 'useTransformReactJSXExperimental' has been removed in favor of { jsxRuntime: 'classic' }.`);
|
|
42
|
+
}
|
|
34
43
|
if (platformOptions.disableImportExportTransform == null) {
|
|
35
44
|
if (platform === 'web') {
|
|
36
45
|
// Only disable import/export transform when Webpack is used because
|
|
@@ -52,27 +61,39 @@ function babelPresetExpo(api, options = {}) {
|
|
|
52
61
|
// `@react-native/babel-preset` configures this plugin with `{ loose: true }`, which breaks all
|
|
53
62
|
// getters and setters in spread objects. We need to add this plugin ourself without that option.
|
|
54
63
|
// @see https://github.com/expo/expo/pull/11960#issuecomment-887796455
|
|
55
|
-
extraPlugins.push([
|
|
56
|
-
require.resolve('@babel/plugin-transform-object-rest-spread'),
|
|
57
|
-
{ loose: false },
|
|
58
|
-
]);
|
|
64
|
+
extraPlugins.push([require('@babel/plugin-transform-object-rest-spread'), { loose: false }]);
|
|
59
65
|
}
|
|
60
|
-
else {
|
|
66
|
+
else if (!isServerEnv) {
|
|
61
67
|
// This is added back on hermes to ensure the react-jsx-dev plugin (`@babel/preset-react`) works as expected when
|
|
62
68
|
// JSX is used in a function body. This is technically not required in production, but we
|
|
63
69
|
// should retain the same behavior since it's hard to debug the differences.
|
|
64
70
|
extraPlugins.push(require('@babel/plugin-transform-parameters'));
|
|
65
71
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
const inlines = {
|
|
73
|
+
'process.env.EXPO_OS': platform,
|
|
74
|
+
// 'typeof document': isServerEnv ? 'undefined' : 'object',
|
|
75
|
+
};
|
|
76
|
+
// `typeof window` is left in place for native + client environments.
|
|
77
|
+
const minifyTypeofWindow = (platformOptions.minifyTypeofWindow ?? isServerEnv) || platform === 'web';
|
|
78
|
+
if (minifyTypeofWindow !== false) {
|
|
79
|
+
// This nets out slightly faster in development when considering the cost of bundling server dependencies.
|
|
80
|
+
inlines['typeof window'] = isServerEnv ? 'undefined' : 'object';
|
|
81
|
+
}
|
|
82
|
+
if (isProduction) {
|
|
83
|
+
inlines['process.env.NODE_ENV'] = 'production';
|
|
84
|
+
inlines['__DEV__'] = false;
|
|
85
|
+
inlines['Platform.OS'] = platform;
|
|
86
|
+
}
|
|
87
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
88
|
+
inlines['process.env.EXPO_BASE_URL'] = baseUrl;
|
|
89
|
+
}
|
|
90
|
+
extraPlugins.push([require('./define-plugin'), inlines]);
|
|
91
|
+
if (isProduction) {
|
|
92
|
+
// Metro applies a version of this plugin too but it does it after the Platform modules have been transformed to CJS, this breaks the transform.
|
|
93
|
+
// Here, we'll apply it before the commonjs transform, in production only, to ensure `Platform.OS` is replaced with a string literal.
|
|
71
94
|
extraPlugins.push([
|
|
72
|
-
require('
|
|
95
|
+
require('./minify-platform-select-plugin'),
|
|
73
96
|
{
|
|
74
|
-
dev: isDev,
|
|
75
|
-
inlinePlatform: true,
|
|
76
97
|
platform,
|
|
77
98
|
},
|
|
78
99
|
]);
|
|
@@ -80,17 +101,6 @@ function babelPresetExpo(api, options = {}) {
|
|
|
80
101
|
if (platformOptions.useTransformReactJSXExperimental != null) {
|
|
81
102
|
throw new Error(`babel-preset-expo: The option 'useTransformReactJSXExperimental' has been removed in favor of { jsxRuntime: 'classic' }.`);
|
|
82
103
|
}
|
|
83
|
-
// Allow jest tests to redefine the environment variables.
|
|
84
|
-
if (process.env.NODE_ENV !== 'test') {
|
|
85
|
-
extraPlugins.push([
|
|
86
|
-
inline_env_vars_1.expoInlineTransformEnvVars,
|
|
87
|
-
{
|
|
88
|
-
// These values should not be prefixed with `EXPO_PUBLIC_`, so we don't
|
|
89
|
-
// squat user-defined environment variables.
|
|
90
|
-
EXPO_BASE_URL: baseUrl,
|
|
91
|
-
},
|
|
92
|
-
]);
|
|
93
|
-
}
|
|
94
104
|
// Only apply in non-server, for metro-only, in production environments, when the user hasn't disabled the feature.
|
|
95
105
|
// Webpack uses DefinePlugin for environment variables.
|
|
96
106
|
// Development uses an uncached serializer.
|
|
@@ -100,7 +110,7 @@ function babelPresetExpo(api, options = {}) {
|
|
|
100
110
|
extraPlugins.push(inline_env_vars_1.expoInlineEnvVars);
|
|
101
111
|
}
|
|
102
112
|
if (platform === 'web') {
|
|
103
|
-
extraPlugins.push(require
|
|
113
|
+
extraPlugins.push(require('babel-plugin-react-native-web'));
|
|
104
114
|
// Webpack uses the DefinePlugin to provide the manifest to `expo-constants`.
|
|
105
115
|
if (bundler !== 'webpack') {
|
|
106
116
|
extraPlugins.push(expo_inline_manifest_plugin_1.expoInlineManifestPlugin);
|
|
@@ -109,6 +119,14 @@ function babelPresetExpo(api, options = {}) {
|
|
|
109
119
|
if ((0, common_1.hasModule)('expo-router')) {
|
|
110
120
|
extraPlugins.push(expo_router_plugin_1.expoRouterBabelPlugin);
|
|
111
121
|
}
|
|
122
|
+
// Ensure these only run when the user opts-in to bundling for a react server to prevent unexpected behavior for
|
|
123
|
+
// users who are bundling using the client-only system.
|
|
124
|
+
if (isReactServer) {
|
|
125
|
+
extraPlugins.push(client_module_proxy_plugin_1.reactClientReferencesPlugin);
|
|
126
|
+
extraPlugins.push(restricted_react_api_plugin_1.environmentRestrictedReactAPIsPlugin);
|
|
127
|
+
}
|
|
128
|
+
// This plugin is fine to run whenever as the server-only imports were introduced as part of RSC and shouldn't be used in any client code.
|
|
129
|
+
extraPlugins.push(environment_restricted_imports_1.environmentRestrictedImportsPlugin);
|
|
112
130
|
if (isFastRefreshEnabled) {
|
|
113
131
|
extraPlugins.push([
|
|
114
132
|
require('react-refresh/babel'),
|
|
@@ -118,6 +136,8 @@ function babelPresetExpo(api, options = {}) {
|
|
|
118
136
|
},
|
|
119
137
|
]);
|
|
120
138
|
}
|
|
139
|
+
// Use the simpler babel preset for web and server environments (both web and native SSR).
|
|
140
|
+
const isModernEngine = platform === 'web' || isServerEnv;
|
|
121
141
|
return {
|
|
122
142
|
presets: [
|
|
123
143
|
[
|
|
@@ -125,7 +145,7 @@ function babelPresetExpo(api, options = {}) {
|
|
|
125
145
|
// specifically use the `@react-native/babel-preset` installed by this package (ex:
|
|
126
146
|
// `babel-preset-expo/node_modules/`). This way the preset will not change unintentionally.
|
|
127
147
|
// Reference: https://github.com/expo/expo/pull/4685#discussion_r307143920
|
|
128
|
-
require('@react-native/babel-preset'),
|
|
148
|
+
isModernEngine ? require('./web-preset') : require('@react-native/babel-preset'),
|
|
129
149
|
{
|
|
130
150
|
// Defaults to undefined, set to `true` to disable `@babel/plugin-transform-flow-strip-types`
|
|
131
151
|
disableFlowStripTypesTransform: platformOptions.disableFlowStripTypesTransform,
|
|
@@ -184,12 +204,12 @@ function babelPresetExpo(api, options = {}) {
|
|
|
184
204
|
plugins: [
|
|
185
205
|
...extraPlugins,
|
|
186
206
|
// TODO: Remove
|
|
187
|
-
[require
|
|
188
|
-
require
|
|
207
|
+
[require('@babel/plugin-proposal-decorators'), { legacy: true }],
|
|
208
|
+
require('@babel/plugin-transform-export-namespace-from'),
|
|
189
209
|
// Automatically add `react-native-reanimated/plugin` when the package is installed.
|
|
190
210
|
// TODO: Move to be a customTransformOption.
|
|
191
211
|
(0, common_1.hasModule)('react-native-reanimated') &&
|
|
192
|
-
platformOptions.reanimated !== false && [require
|
|
212
|
+
platformOptions.reanimated !== false && [require('react-native-reanimated/plugin')],
|
|
193
213
|
].filter(Boolean),
|
|
194
214
|
};
|
|
195
215
|
}
|
|
@@ -2,12 +2,3 @@ import { ConfigAPI, PluginObj, types } from '@babel/core';
|
|
|
2
2
|
export declare function expoInlineEnvVars(api: ConfigAPI & {
|
|
3
3
|
types: typeof types;
|
|
4
4
|
}): PluginObj;
|
|
5
|
-
/**
|
|
6
|
-
* Given a set of options like `{ EXPO_BASE_URL: '/' }`, inline the values into the bundle.
|
|
7
|
-
* This is used for build settings that are always available and not configurable at runtime.
|
|
8
|
-
*
|
|
9
|
-
* Webpack uses DefinePlugin for similar functionality.
|
|
10
|
-
*/
|
|
11
|
-
export declare function expoInlineTransformEnvVars(api: ConfigAPI & {
|
|
12
|
-
types: typeof types;
|
|
13
|
-
}): PluginObj;
|
package/build/inline-env-vars.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.expoInlineEnvVars = expoInlineEnvVars;
|
|
4
4
|
const debug = require('debug')('expo:babel:env-vars');
|
|
5
5
|
function expoInlineEnvVars(api) {
|
|
6
6
|
const { types: t } = api;
|
|
@@ -26,35 +26,3 @@ function expoInlineEnvVars(api) {
|
|
|
26
26
|
},
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
exports.expoInlineEnvVars = expoInlineEnvVars;
|
|
30
|
-
/**
|
|
31
|
-
* Given a set of options like `{ EXPO_BASE_URL: '/' }`, inline the values into the bundle.
|
|
32
|
-
* This is used for build settings that are always available and not configurable at runtime.
|
|
33
|
-
*
|
|
34
|
-
* Webpack uses DefinePlugin for similar functionality.
|
|
35
|
-
*/
|
|
36
|
-
function expoInlineTransformEnvVars(api) {
|
|
37
|
-
const { types: t } = api;
|
|
38
|
-
function isFirstInAssign(path) {
|
|
39
|
-
return t.isAssignmentExpression(path.parent) && path.parent.left === path.node;
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
name: 'expo-inline-transform-environment-variables',
|
|
43
|
-
visitor: {
|
|
44
|
-
MemberExpression(path, state) {
|
|
45
|
-
const options = state.opts;
|
|
46
|
-
if (path.get('object').matchesPattern('process.env')) {
|
|
47
|
-
// @ts-expect-error: missing types
|
|
48
|
-
const key = path.toComputedKey();
|
|
49
|
-
if (t.isStringLiteral(key) &&
|
|
50
|
-
!isFirstInAssign(path) &&
|
|
51
|
-
options[key.value] !== undefined) {
|
|
52
|
-
debug('Inlining transform setting in %s: %s', state.filename || '[unknown file]', key.value);
|
|
53
|
-
path.replaceWith(t.valueToNode(options[key.value]));
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
exports.expoInlineTransformEnvVars = expoInlineTransformEnvVars;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2024 650 Industries.
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
import { ConfigAPI, types } from '@babel/core';
|
|
9
|
+
export default function minifyPlatformSelectPlugin({ types: t, }: ConfigAPI & {
|
|
10
|
+
types: typeof types;
|
|
11
|
+
}): babel.PluginObj;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright © 2024 650 Industries.
|
|
4
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.default = minifyPlatformSelectPlugin;
|
|
11
|
+
const core_1 = require("@babel/core");
|
|
12
|
+
function minifyPlatformSelectPlugin({ types: t, }) {
|
|
13
|
+
return {
|
|
14
|
+
visitor: {
|
|
15
|
+
CallExpression(path, state) {
|
|
16
|
+
const node = path.node;
|
|
17
|
+
const arg = node.arguments[0];
|
|
18
|
+
const opts = state.opts;
|
|
19
|
+
if (isPlatformSelect(path) && core_1.types.isObjectExpression(arg)) {
|
|
20
|
+
if (hasStaticProperties(arg)) {
|
|
21
|
+
let fallback;
|
|
22
|
+
if (opts.platform === 'web') {
|
|
23
|
+
fallback = () => findProperty(arg, 'default', () => t.identifier('undefined'));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
fallback = () => findProperty(arg, 'native', () => findProperty(arg, 'default', () => t.identifier('undefined')));
|
|
27
|
+
}
|
|
28
|
+
path.replaceWith(findProperty(arg, opts.platform, fallback));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function isPlatformSelect(path) {
|
|
36
|
+
return (core_1.types.isMemberExpression(path.node.callee) &&
|
|
37
|
+
core_1.types.isIdentifier(path.node.callee.object) &&
|
|
38
|
+
core_1.types.isIdentifier(path.node.callee.property) &&
|
|
39
|
+
path.node.callee.object.name === 'Platform' &&
|
|
40
|
+
path.node.callee.property.name === 'select' &&
|
|
41
|
+
core_1.types.isObjectExpression(path.node.arguments[0]));
|
|
42
|
+
}
|
|
43
|
+
function findProperty(objectExpression, key, fallback) {
|
|
44
|
+
let value = null;
|
|
45
|
+
for (const p of objectExpression.properties) {
|
|
46
|
+
if (!core_1.types.isObjectProperty(p) && !core_1.types.isObjectMethod(p)) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if ((core_1.types.isIdentifier(p.key) && p.key.name === key) ||
|
|
50
|
+
(core_1.types.isStringLiteral(p.key) && p.key.value === key)) {
|
|
51
|
+
if (core_1.types.isObjectProperty(p)) {
|
|
52
|
+
value = p.value;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
else if (core_1.types.isObjectMethod(p)) {
|
|
56
|
+
value = core_1.types.toExpression(p);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return value ?? fallback();
|
|
62
|
+
}
|
|
63
|
+
function hasStaticProperties(objectExpression) {
|
|
64
|
+
return objectExpression.properties.every((p) => {
|
|
65
|
+
if (('computed' in p && p.computed) || core_1.types.isSpreadElement(p)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (core_1.types.isObjectMethod(p) && p.kind !== 'method') {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return core_1.types.isIdentifier(p.key) || core_1.types.isStringLiteral(p.key);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.environmentRestrictedReactAPIsPlugin = environmentRestrictedReactAPIsPlugin;
|
|
4
|
+
const INVALID_SERVER_REACT_DOM_APIS = [
|
|
5
|
+
'findDOMNode',
|
|
6
|
+
'flushSync',
|
|
7
|
+
'unstable_batchedUpdates',
|
|
8
|
+
'useFormStatus',
|
|
9
|
+
'useFormState',
|
|
10
|
+
];
|
|
11
|
+
// From the React docs: https://github.com/vercel/next.js/blob/d43a387d271263f2c1c4da6b9db826e382fc489c/packages/next-swc/crates/next-custom-transforms/src/transforms/react_server_components.rs#L665-L681
|
|
12
|
+
const INVALID_SERVER_REACT_APIS = [
|
|
13
|
+
'Component',
|
|
14
|
+
'createContext',
|
|
15
|
+
'createFactory',
|
|
16
|
+
'PureComponent',
|
|
17
|
+
'useDeferredValue',
|
|
18
|
+
'useEffect',
|
|
19
|
+
'useImperativeHandle',
|
|
20
|
+
'useInsertionEffect',
|
|
21
|
+
'useLayoutEffect',
|
|
22
|
+
'useReducer',
|
|
23
|
+
'useRef',
|
|
24
|
+
'useState',
|
|
25
|
+
'useSyncExternalStore',
|
|
26
|
+
'useTransition',
|
|
27
|
+
'useOptimistic',
|
|
28
|
+
];
|
|
29
|
+
function isNodeModule(path) {
|
|
30
|
+
return path != null && /[\\/]node_modules[\\/]/.test(path);
|
|
31
|
+
}
|
|
32
|
+
// Restricts imports from `react` and `react-dom` when using React Server Components.
|
|
33
|
+
const FORBIDDEN_IMPORTS = {
|
|
34
|
+
react: INVALID_SERVER_REACT_APIS,
|
|
35
|
+
'react-dom': INVALID_SERVER_REACT_DOM_APIS,
|
|
36
|
+
};
|
|
37
|
+
function environmentRestrictedReactAPIsPlugin(api) {
|
|
38
|
+
const { types: t } = api;
|
|
39
|
+
return {
|
|
40
|
+
name: 'expo-environment-restricted-react-api-plugin',
|
|
41
|
+
visitor: {
|
|
42
|
+
ImportDeclaration(path, state) {
|
|
43
|
+
// Skip node_modules
|
|
44
|
+
if (isNodeModule(state.file.opts.filename)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const sourceValue = path.node.source.value;
|
|
48
|
+
const forbiddenList = FORBIDDEN_IMPORTS[sourceValue];
|
|
49
|
+
if (forbiddenList) {
|
|
50
|
+
path.node.specifiers.forEach((specifier) => {
|
|
51
|
+
if (t.isImportSpecifier(specifier)) {
|
|
52
|
+
const importName = t.isStringLiteral(specifier.imported)
|
|
53
|
+
? specifier.imported.value
|
|
54
|
+
: specifier.imported.name;
|
|
55
|
+
// Check for both named and namespace imports
|
|
56
|
+
const isForbidden = forbiddenList.includes(importName);
|
|
57
|
+
if (isForbidden) {
|
|
58
|
+
if (['Component', 'PureComponent'].includes(importName)) {
|
|
59
|
+
// Add special handling for `Component` since it is different to a function API.
|
|
60
|
+
throw path.buildCodeFrameError(`Client-only "${sourceValue}" API "${importName}" cannot be imported in a React server component. Add the "use client" directive to the top of this file or one of the parent files to enable running this stateful code on a user's device.`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const forbiddenImports = path.scope.getData('forbiddenImports') ?? new Map();
|
|
64
|
+
if (!forbiddenImports.has(sourceValue))
|
|
65
|
+
forbiddenImports.set(sourceValue, new Set());
|
|
66
|
+
forbiddenImports.get(sourceValue).add(importName);
|
|
67
|
+
path.scope.setData('forbiddenImports', forbiddenImports);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const importName = t.isStringLiteral(specifier.local)
|
|
73
|
+
? specifier.local
|
|
74
|
+
: specifier.local.name;
|
|
75
|
+
// Save namespace import for later checks in MemberExpression
|
|
76
|
+
path.scope.setData('importedNamespace', { [importName]: sourceValue });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
// Match against `var _useState = useState(0),`
|
|
82
|
+
VariableDeclarator(path) {
|
|
83
|
+
const importedSpecifiers = path.scope.getData('forbiddenImports');
|
|
84
|
+
if (!importedSpecifiers)
|
|
85
|
+
return;
|
|
86
|
+
importedSpecifiers.forEach((forbiddenApis, importName) => {
|
|
87
|
+
if (t.isCallExpression(path.node.init) && t.isIdentifier(path.node.init.callee)) {
|
|
88
|
+
if (forbiddenApis.has(path.node.init.callee.name)) {
|
|
89
|
+
throw path.buildCodeFrameError(`Client-only "useState" API cannot be used in a React server component. Add the "use client" directive to the top of this file or one of the parent files to enable running this stateful code on a user's device.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
MemberExpression(path) {
|
|
95
|
+
const importedNamespaces = path.scope.getData('importedNamespace') || {};
|
|
96
|
+
Object.keys(importedNamespaces).forEach((namespace) => {
|
|
97
|
+
const library = importedNamespaces[namespace];
|
|
98
|
+
const forbiddenList = FORBIDDEN_IMPORTS[library];
|
|
99
|
+
const objectName = t.isIdentifier(path.node.object) ? path.node.object.name : null;
|
|
100
|
+
if (objectName === namespace &&
|
|
101
|
+
forbiddenList &&
|
|
102
|
+
t.isIdentifier(path.node.property) &&
|
|
103
|
+
forbiddenList.includes(path.node.property.name)) {
|
|
104
|
+
// Throw a special error for class components since it's not always clear why they cannot be used in RSC.
|
|
105
|
+
// e.g. https://x.com/Baconbrix/status/1749223042440392806?s=20
|
|
106
|
+
if (path.node.property.name === 'Component') {
|
|
107
|
+
throw path.buildCodeFrameError(`Class components cannot be used in a React server component due to their ability to contain stateful and interactive APIs that cannot be statically evaluated in non-interactive environments such as a server or at build-time. Migrate to a function component, or add the "use client" directive to the top of this file or one of the parent files to render this class component on a user's device.`);
|
|
108
|
+
}
|
|
109
|
+
throw path.buildCodeFrameError(`Client-only "${namespace}" API "${path.node.property.name}" cannot be used in a React server component. Add the "use client" directive to the top of this file or one of the parent files to enable running this stateful code on a user's device.`);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2024 650 Industries.
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*
|
|
8
|
+
* A fork of `@react-native/babel-preset` but with everything unrelated to web/ssr removed.
|
|
9
|
+
* https://github.com/facebook/react-native/blob/2af1da42ff517232f1309efed7565fe9ddbbac77/packages/react-native-babel-preset/src/configs/main.js#L1
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright © 2024 650 Industries.
|
|
4
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
* A fork of `@react-native/babel-preset` but with everything unrelated to web/ssr removed.
|
|
10
|
+
* https://github.com/facebook/react-native/blob/2af1da42ff517232f1309efed7565fe9ddbbac77/packages/react-native-babel-preset/src/configs/main.js#L1
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
// use `this.foo = bar` instead of `this.defineProperty('foo', ...)`
|
|
14
|
+
const loose = true;
|
|
15
|
+
const defaultPlugins = [
|
|
16
|
+
[require('@babel/plugin-syntax-flow')],
|
|
17
|
+
[require('babel-plugin-transform-flow-enums')],
|
|
18
|
+
[require('@babel/plugin-transform-private-methods'), { loose }],
|
|
19
|
+
[require('@babel/plugin-transform-private-property-in-object'), { loose }],
|
|
20
|
+
// [require('@babel/plugin-syntax-dynamic-import')],
|
|
21
|
+
[require('@babel/plugin-syntax-export-default-from')],
|
|
22
|
+
// [require('@babel/plugin-syntax-nullish-coalescing-operator')],
|
|
23
|
+
// [require('@babel/plugin-syntax-optional-chaining')],
|
|
24
|
+
// [require('@babel/plugin-transform-unicode-regex')],
|
|
25
|
+
];
|
|
26
|
+
module.exports = function (babel, options) {
|
|
27
|
+
const extraPlugins = [];
|
|
28
|
+
// NOTE: We also remove `@react-native/babel-plugin-codegen` since it doesn't seem needed on web.
|
|
29
|
+
if (!options || !options.disableImportExportTransform) {
|
|
30
|
+
extraPlugins.push([require('@babel/plugin-proposal-export-default-from')], [
|
|
31
|
+
require('@babel/plugin-transform-modules-commonjs'),
|
|
32
|
+
{
|
|
33
|
+
strict: false,
|
|
34
|
+
strictMode: false, // prevent "use strict" injections
|
|
35
|
+
lazy: options.lazyImportExportTransform,
|
|
36
|
+
allowTopLevelThis: true, // dont rewrite global `this` -> `undefined`
|
|
37
|
+
},
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
if (!options || options.enableBabelRuntime !== false) {
|
|
41
|
+
// Allows configuring a specific runtime version to optimize output
|
|
42
|
+
const isVersion = typeof options?.enableBabelRuntime === 'string';
|
|
43
|
+
extraPlugins.push([
|
|
44
|
+
require('@babel/plugin-transform-runtime'),
|
|
45
|
+
{
|
|
46
|
+
helpers: true,
|
|
47
|
+
regenerator: false,
|
|
48
|
+
...(isVersion && { version: options.enableBabelRuntime }),
|
|
49
|
+
},
|
|
50
|
+
]);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
comments: false,
|
|
54
|
+
compact: true,
|
|
55
|
+
presets: [
|
|
56
|
+
// TypeScript support
|
|
57
|
+
[require('@babel/preset-typescript'), { allowNamespaces: true }],
|
|
58
|
+
],
|
|
59
|
+
overrides: [
|
|
60
|
+
// the flow strip types plugin must go BEFORE class properties!
|
|
61
|
+
// there'll be a test case that fails if you don't.
|
|
62
|
+
{
|
|
63
|
+
plugins: [require('@babel/plugin-transform-flow-strip-types')],
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
plugins: defaultPlugins,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
plugins: extraPlugins,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "babel-preset-expo",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.0",
|
|
4
4
|
"description": "The Babel preset for Expo projects",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"files": [
|
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@babel/plugin-proposal-decorators": "^7.12.9",
|
|
45
|
-
"@babel/plugin-transform-object-rest-spread": "^7.12.13",
|
|
46
45
|
"@babel/plugin-transform-export-namespace-from": "^7.22.11",
|
|
47
|
-
"@babel/
|
|
48
|
-
"@babel/preset-react": "^7.22.15",
|
|
46
|
+
"@babel/plugin-transform-object-rest-spread": "^7.12.13",
|
|
49
47
|
"@babel/plugin-transform-parameters": "^7.22.15",
|
|
50
|
-
"@
|
|
51
|
-
"babel-
|
|
48
|
+
"@babel/preset-typescript": "^7.23.0",
|
|
49
|
+
"@babel/preset-react": "^7.22.15",
|
|
50
|
+
"@react-native/babel-preset": "~0.74.80",
|
|
51
|
+
"babel-plugin-react-native-web": "~0.19.10",
|
|
52
52
|
"react-refresh": "0.14.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"expo-module-scripts": "^3.3.0",
|
|
57
57
|
"jest": "^29.2.1"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "4165b8d72e1b9a1889c2767534cc619e21468110"
|
|
60
60
|
}
|