first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

215
node_modules/babel-preset-expo/README.md generated vendored Normal file
View File

@@ -0,0 +1,215 @@
# babel-preset-expo
This preset extends the default React Native preset (`@react-native/babel-preset`) and adds support for tree shaking, bundle splitting, React Server Components, Hermes compilation, advanced dead-code elimination, reanimated, Expo DOM components, server-side rendering, and more...
You can use this preset in any React Native project as a drop-in replacement for `@react-native/babel-preset`.
If you have problems with the code in this repository, please file issues & bug reports
at https://github.com/expo/expo.
## Expo Bundler Spec Compliance
A bundler must follow these requirements if they are to be considered spec compliant for use with a **universal React** (Expo) project.
## Options
### `react-compiler`
Settings to pass to `babel-plugin-react-compiler`. Set as `false` to disable the plugin. As of SDK 51, you must also enable `experiments.reactCompiler: true` in the `app.json`.
```js
[
'babel-preset-expo',
{
'react-compiler': {
sources: (filename) => {
// Match file names to include in the React Compiler.
return filename.includes('src/path/to/dir');
},
},
},
];
```
### `minifyTypeofWindow`
Set `minifyTypeofWindow: true` to transform `typeof window` checks in your code, e.g. `if (typeof window === 'object')` -> `if (true)` in clients. This is useful when you're using libraries that mock the window object on native or in the server.
```js
[
'babel-preset-expo',
{
// If your native app doesn't polyfill `window` then setting this to `false` can reduce bundle size.
native: {
minifyTypeofWindow: true,
},
},
];
```
Defaults to `true` for server environments, and `false` for client environments to support legacy browser polyfills and web workers.
### `reanimated`
`boolean`, defaults to `true`. Set `reanimated: false` to disable adding the `react-native-reanimated/plugin` when `react-native-reanimated` is installed.
### `worklets`
`boolean`, `boolean`, defaults to `true`. Set `worklets: false` to disable adding the `react-native-worklets/plugin` when `react-native-worklets` is installed. Applies only when using standalone `react-native-worklets` or `react-native-reanimated 4`.
### [`jsxRuntime`](https://babeljs.io/docs/en/babel-plugin-transform-react-jsx#runtime)
`classic | automatic`, defaults to `automatic`
- `automatic` automatically convert JSX to JS without the need to `import React from 'react'` in every file. Be sure to follow the rest of the [setup guide](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#how-to-upgrade-to-the-new-jsx-transform) after enabling this, otherwise ESLint and other tools will throw warnings.
- `classic` does not automatically import anything, React must imported into every file that uses JSX syntax.
```js
[
'babel-preset-expo',
{
jsxRuntime: 'classic',
},
];
```
This property is passed down to [`@babel/plugin-transform-react-jsx`](https://babeljs.io/docs/en/babel-plugin-transform-react-jsx). This flag does nothing when `useTransformReactJSXExperimental` is set to `true` because `@babel/plugin-transform-react-jsx` is omitted.
### [`jsxImportSource`](https://babeljs.io/docs/en/babel-plugin-transform-react-jsx#importsource)
`string`, defaults to `react`
This option allows specifying a custom import source for importing functions.
```js
[
'babel-preset-expo',
{
jsxRuntime: 'automatic',
jsxImportSource: 'react',
},
];
```
This property is passed down to [`@babel/plugin-transform-react-jsx`](https://babeljs.io/docs/en/babel-plugin-transform-react-jsx). This options does nothing when `jsxRuntime` is not set to `automatic`.
### [`lazyImports`](https://babeljs.io/docs/en/babel-plugin-transform-modules-commonjs#lazy)
Changes Babel's compiled `import` statements to be lazily evaluated when their imported bindings are used for the first time.
_Note:_ this option has an effect only when the `disableImportExportTransform` option is set to `false`. On Android and iOS, `disableImportExportTransform` defaults to `false`, and on web it defaults to `true` to allow for tree shaking.
This can improve the initial load time of your app because evaluating dependencies up front is sometimes entirely un-necessary, particularly when the dependencies have no side effects.
The value of `lazyImports` has a few possible effects:
- `null` - [@react-native/babel-preset](https://github.com/facebook/react-native/tree/main/packages/react-native-babel-preset) will handle it. (Learn more about it here: https://github.com/facebook/metro/commit/23e3503dde5f914f3e642ef214f508d0a699851d)
- `false` - No lazy initialization of any imported module.
- `true` - Lazy-init all imported modules except local imports (e.g., `./foo`), certain Expo packages that have side effects, and the two cases mentioned [here](https://babeljs.io/docs/en/babel-plugin-transform-modules-commonjs#lazy).
- `Array<string>` - [babel-plugin-transform-modules-commonjs](https://babeljs.io/docs/en/babel-plugin-transform-modules-commonjs#lazy) will handle it.
- `(string) => boolean` - [babel-plugin-transform-modules-commonjs](https://babeljs.io/docs/en/babel-plugin-transform-modules-commonjs#lazy) will handle it.
If you choose to do this, you can also access the list of Expo packages that have side effects by using `const lazyImportsBlacklist = require('babel-preset-expo/lazy-imports-blacklist');` which returns a `Set`.
**default:** `null`
```js
[
'babel-preset-expo',
{
lazyImports: true
}
],
```
### `disableImportExportTransform`
Pass `true` to disable the transform that converts import/export to `module.exports`. Avoid setting this property directly. If you're using Metro, set `experimentalImportSupport: true` instead to ensure the entire pipeline is configured correctly.
```js
// metro.config.js
config.transformer.getTransformOptions = async () => ({
transform: {
// Setting this to `true` will automatically toggle `disableImportExportTransform` in `babel-preset-expo`.
experimentalImportSupport: true,
},
});
```
If `undefined` (default), this will be set automatically via `caller.supportsStaticESM` which is set by the bundler.
```js
[
'babel-preset-expo',
{
disableImportExportTransform: true
}
],
```
### `unstable_transformProfile`
Changes the engine preset in `@react-native/babel-preset` based on the JavaScript engine that is being targeted. In Expo SDK 50 and greater, this is automatically set based on the [`jsEngine`](https://docs.expo.dev/versions/latest/config/app/#jsengine) option in your `app.json`.
### `unstable_transformImportMeta`
Enable that transform that converts `import.meta` to `globalThis.__ExpoImportMetaRegistry`, defaults to `false` in client bundles and `true` for server bundles.
> **Note:** Use this option at your own risk. If the JavaScript engine supports `import.meta` natively, this transformation may interfere with the native implementation.
### `enableBabelRuntime`
Passed to `@react-native/babel-preset`.
### `disableFlowStripTypesTransform`
Passed to `@react-native/babel-preset`.
## Platform-specific options
All options can be passed in the platform-specific objects `native` and `web` to provide different settings on different platforms. For example, if you'd like to only apply `disableImportExportTransform` on web, use the following:
```js
[
'babel-preset-expo',
{
// Default value:
disableImportExportTransform: false,
web: {
// Web-specific value:
disableImportExportTransform: true,
},
},
];
```
Platform-specific options have higher priority over top-level options.
### Babel Loader
The Babel loading mechanism must include the following properties on its `caller`.
#### platform
A `platform` property denoting the target platform. If the `platform` is not defined, it will default to using `web` when the `bundler` is `webpack` -- this is temporary and will throw an error in the future.
| Value | Description |
| --------- | ----------------------- |
| `ios` | Runs on iOS devices |
| `android` | Runs on Android devices |
| `web` | Runs in web browsers |
#### bundler
A `bundler` property denoting the name of the bundler that is being used to create the JavaScript bundle.
If the `bundler` is not defined, it will default to checking if a `babel-loader` is used, if so then `webpack` will be used, otherwise it will default to `metro`.
| Value | Description |
| --------- | -------------------------------- |
| `metro` | Bundling with [Metro][metro] |
| `webpack` | Bundling with [Webpack][webpack] |
[metro]: https://facebook.github.io/metro/
[webpack]: https://webpack.js.org/

View File

@@ -0,0 +1,10 @@
/**
* Copyright (c) 650 Industries (Expo). All rights reserved.
* Copyright (c) 2014-present Sebastian McKenzie and other contributors
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { ConfigAPI, PluginObj } from '@babel/core';
declare const _default: ({ types: t }: ConfigAPI & typeof import("@babel/core")) => PluginObj;
export default _default;

View File

@@ -0,0 +1,50 @@
"use strict";
/**
* Copyright (c) 650 Industries (Expo). All rights reserved.
* Copyright (c) 2014-present Sebastian McKenzie and other contributors
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
Object.defineProperty(exports, "__esModule", { value: true });
// Original: https://github.com/babel/babel/blob/e5c8dc7330cb2f66c37637677609df90b31ff0de/packages/babel-plugin-transform-export-namespace-from/src/index.ts
// NOTE: Original plugin asserts that Babel version 7 or newer is used. This was removed for simplicity.
exports.default = ({ types: t }) => ({
name: 'transform-export-namespace-from',
manipulateOptions: process.env.BABEL_8_BREAKING
? undefined
: (_, parser) => parser.plugins.push('exportNamespaceFrom'),
visitor: {
ExportNamedDeclaration(path) {
const { node, scope } = path;
const { specifiers } = node;
const index = t.isExportDefaultSpecifier(specifiers[0]) ? 1 : 0;
if (!t.isExportNamespaceSpecifier(specifiers[index]))
return;
const nodes = [];
if (index === 1) {
nodes.push(t.exportNamedDeclaration(null, [specifiers.shift()], node.source));
}
const specifier = specifiers.shift();
const { exported } = specifier;
const uid = scope.generateUidIdentifier(
// @ts-expect-error Identifier ?? StringLiteral
exported.name ?? exported.value);
nodes.push(withLocation(t.importDeclaration([t.importNamespaceSpecifier(uid)],
// @ts-expect-error
t.cloneNode(node.source)), node.loc), withLocation(t.exportNamedDeclaration(null, [t.exportSpecifier(t.cloneNode(uid), exported)]), node.loc));
if (node.specifiers.length >= 1) {
nodes.push(node);
}
const [importDeclaration] = path.replaceWithMultiple(nodes);
path.scope.registerDeclaration(importDeclaration);
},
},
});
// Inspired by https://github.com/facebook/metro/blob/8e48aa823378962beccbe37d85f1aff2c34b28b1/packages/metro-transform-plugins/src/import-export-plugin.js#L143
function withLocation(node, loc) {
if (!node.loc) {
return { ...node, loc };
}
return node;
}

View File

@@ -0,0 +1,5 @@
/**
* Copyright © 2024 650 Industries.
*/
import type { ConfigAPI, PluginObj } from '@babel/core';
export declare function reactClientReferencesPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj;

View File

@@ -0,0 +1,226 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.reactClientReferencesPlugin = reactClientReferencesPlugin;
const node_path_1 = require("node:path");
const node_url_1 = __importDefault(require("node:url"));
const common_1 = require("./common");
function reactClientReferencesPlugin(api) {
const { template, types } = api;
const isReactServer = api.caller(common_1.getIsReactServer);
const possibleProjectRoot = api.caller(common_1.getPossibleProjectRoot);
return {
name: 'expo-client-references',
visitor: {
Program(path, state) {
const isUseClient = path.node.directives.some((directive) => directive.value.value === 'use client' ||
// Convert DOM Components to client proxies in React Server environments.
directive.value.value === 'use dom');
// 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
const isUseServer = path.node.directives.some((directive) => directive.value.value === 'use server');
if (isUseClient && isUseServer) {
throw path.buildCodeFrameError('It\'s not possible to have both "use client" and "use server" directives in the same file.');
}
if (!isUseClient && !isUseServer) {
return;
}
const filePath = state.file.opts.filename;
if (!filePath) {
// This can happen in tests or systems that use Babel standalone.
throw new Error('[Babel] Expected a filename to be set in the state');
}
const projectRoot = possibleProjectRoot || state.file.opts.root || '';
// TODO: Replace with opaque paths in production.
const outputKey = './' + (0, common_1.toPosixPath)((0, node_path_1.relative)(projectRoot, filePath));
// const outputKey = isProd
// ? './' + getRelativePath(projectRoot, filePath)
// : url.pathToFileURL(filePath).href;
function iterateExports(callback, type) {
const exportNames = new Set();
// Collect all of the exports
path.traverse({
ExportNamedDeclaration(exportPath) {
if (exportPath.node.declaration) {
if (exportPath.node.declaration.type === 'VariableDeclaration') {
exportPath.node.declaration.declarations.forEach((declaration) => {
if (declaration.id.type === 'Identifier') {
const exportName = declaration.id.name;
exportNames.add(exportName);
callback(exportName, exportPath);
}
});
}
else if (exportPath.node.declaration.type === 'FunctionDeclaration') {
const exportName = exportPath.node.declaration.id?.name;
if (exportName) {
exportNames.add(exportName);
callback(exportName, exportPath);
}
}
else if (exportPath.node.declaration.type === 'ClassDeclaration') {
const exportName = exportPath.node.declaration.id?.name;
if (exportName) {
exportNames.add(exportName);
callback(exportName, exportPath);
}
}
else if (![
'InterfaceDeclaration',
'TSInterfaceDeclaration',
'TSTypeAliasDeclaration',
'TypeAlias',
].includes(exportPath.node.declaration.type)) {
// TODO: What is this type?
console.warn(`[babel-preset-expo] Unsupported export specifier for "use ${type}":`, exportPath.node.declaration.type);
}
}
else {
exportPath.node.specifiers.forEach((specifier) => {
if (types.isIdentifier(specifier.exported)) {
const exportName = specifier.exported.name;
exportNames.add(exportName);
callback(exportName, exportPath);
}
else {
// TODO: What is this type?
console.warn(`[babel-preset-expo] Unsupported export specifier for "use ${type}":`, specifier);
}
});
}
},
ExportDefaultDeclaration(path) {
exportNames.add('default');
callback('default', path);
},
ExportAllDeclaration(exportPath) {
if (exportPath.node.source) {
// exportNames.add('*');
callback('*', exportPath);
}
},
});
return exportNames;
}
// File starts with "use client" directive.
if (isUseServer) {
if (isReactServer) {
// The "use server" transform for react-server is in a different plugin.
return;
}
// Assert that assignment to `module.exports` or `exports` is not allowed.
path.traverse({
AssignmentExpression(path) {
if (types.isMemberExpression(path.node.left) &&
'name' in path.node.left.object &&
(path.node.left.object.name === 'module' ||
path.node.left.object.name === 'exports')) {
throw path.buildCodeFrameError('Assignment to `module.exports` or `exports` is not allowed in a "use server" file. Only async functions can be exported.');
}
},
// Also check Object.assign
CallExpression(path) {
if (types.isMemberExpression(path.node.callee) &&
'name' in path.node.callee.property &&
'name' in path.node.callee.object &&
path.node.callee.property.name === 'assign' &&
(path.node.callee.object.name === 'Object' ||
path.node.callee.object.name === 'exports')) {
throw path.buildCodeFrameError('Assignment to `module.exports` or `exports` is not allowed in a "use server" file. Only async functions can be exported.');
}
},
});
// Handle "use server" in the client.
const proxyModule = [
`import { createServerReference } from 'react-server-dom-webpack/client';`,
`import { callServerRSC } from 'expo-router/rsc/internal';`,
];
const getProxy = (exportName) => {
return `createServerReference(${JSON.stringify(`${outputKey}#${exportName}`)}, callServerRSC)`;
};
const pushProxy = (exportName, path) => {
if (exportName === 'default') {
proxyModule.push(`export default ${getProxy(exportName)};`);
}
else if (exportName === '*') {
throw path.buildCodeFrameError('Re-exporting all modules is not supported in a "use server" file. Only async functions can be exported.');
}
else {
proxyModule.push(`export const ${exportName} = ${getProxy(exportName)};`);
}
};
// We need to add all of the exports to support `export * from './module'` which iterates the keys of the module.
// Collect all of the exports
const proxyExports = iterateExports(pushProxy, 'client');
// Clear the body
path.node.body = [];
path.node.directives = [];
path.pushContainer('body', template.ast(proxyModule.join('\n')));
assertExpoMetadata(state.file.metadata);
// Store the proxy export names for testing purposes.
state.file.metadata.proxyExports = [...proxyExports];
// Save the server action reference in the metadata.
state.file.metadata.reactServerReference = node_url_1.default.pathToFileURL(filePath).href;
}
else if (isUseClient) {
if (!isReactServer) {
// Do nothing for "use client" on the client.
return;
}
// HACK: Mock out the polyfill that doesn't run through the normal bundler pipeline.
if (filePath.endsWith('@react-native/js-polyfills/console.js') ||
filePath.endsWith('@react-native\\js-polyfills\\console.js')) {
// Clear the body
path.node.body = [];
path.node.directives = [];
return;
}
// We need to add all of the exports to support `export * from './module'` which iterates the keys of the module.
const proxyModule = [
`const proxy = /*@__PURE__*/ require("react-server-dom-webpack/server").createClientModuleProxy(${JSON.stringify(outputKey)});`,
`module.exports = proxy;`,
];
const pushProxy = (exportName) => {
if (exportName === 'default') {
proxyModule.push(`export default require("react-server-dom-webpack/server").registerClientReference(function () {
throw new Error(${JSON.stringify(`Attempted to call the default export of ${filePath} from the server but it's on the client. ` +
`It's not possible to invoke a client function from the server, it can ` +
`only be rendered as a Component or passed to props of a Client Component.`)});
}, ${JSON.stringify(outputKey)}, ${JSON.stringify(exportName)});`);
}
else if (exportName === '*') {
// Do nothing because we have the top-level hack to inject module.exports.
}
else {
proxyModule.push(`export const ${exportName} = require("react-server-dom-webpack/server").registerClientReference(function () {
throw new Error(${JSON.stringify(`Attempted to call ${exportName}() of ${filePath} from the server but ${exportName} is on the client. ` +
`It's not possible to invoke a client function from the server, it can ` +
`only be rendered as a Component or passed to props of a Client Component.`)});
}, ${JSON.stringify(outputKey)}, ${JSON.stringify(exportName)});`);
}
};
// TODO: How to handle `export * from './module'`?
// TODO: How to handle module.exports, do we just assert that it isn't supported with server components?
// Collect all of the exports
const proxyExports = iterateExports(pushProxy, 'client');
// Clear the body
path.node.body = [];
path.node.directives = [];
path.pushContainer('body', template.ast(proxyModule.join('\n')));
assertExpoMetadata(state.file.metadata);
// Store the proxy export names for testing purposes.
state.file.metadata.proxyExports = [...proxyExports];
// Save the client reference in the metadata.
state.file.metadata.reactClientReference = node_url_1.default.pathToFileURL(filePath).href;
}
},
},
};
}
function assertExpoMetadata(metadata) {
if (metadata && typeof metadata === 'object') {
return;
}
throw new Error('Expected Babel state.file.metadata to be an object');
}

26
node_modules/babel-preset-expo/build/common.d.ts generated vendored Normal file
View File

@@ -0,0 +1,26 @@
import type { NodePath, types as t } from '@babel/core';
export declare function hasModule(name: string): boolean;
/** Determine which bundler is being used. */
export declare function getBundler(caller?: any): "metro" | "webpack" | null;
export declare function getPlatform(caller?: any): string | null | undefined;
export declare function getPossibleProjectRoot(caller?: any): string | null | undefined;
/** If bundling for a react-server target. */
export declare function getIsReactServer(caller?: any): boolean;
export declare function getIsDev(caller?: any): boolean;
export declare function getIsFastRefreshEnabled(caller?: any): boolean;
export declare function getIsProd(caller?: any): boolean;
export declare function getIsNodeModule(caller?: any): boolean;
export declare function getBaseUrl(caller?: any): string;
export declare function getReactCompiler(caller?: any): boolean;
export declare function getIsServer(caller?: any): boolean;
export declare function getIsLoaderBundle(caller?: any): boolean;
export declare function getMetroSourceType(caller?: any): "script" | "module" | "asset" | undefined;
export declare function getBabelRuntimeVersion(caller?: any): string;
export declare function getExpoRouterAbsoluteAppRoot(caller?: any): string;
export declare function getInlineEnvVarsEnabled(caller?: any): boolean;
export declare function getAsyncRoutes(caller?: any): boolean;
export declare function createAddNamedImportOnce(t: typeof import('@babel/core').types): (path: NodePath<t.Node>, name: string, source: string) => any;
/**
* Convert any platform-specific path to a POSIX path.
*/
export declare function toPosixPath(filePath: string): string;

206
node_modules/babel-preset-expo/build/common.js generated vendored Normal file
View File

@@ -0,0 +1,206 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasModule = hasModule;
exports.getBundler = getBundler;
exports.getPlatform = getPlatform;
exports.getPossibleProjectRoot = getPossibleProjectRoot;
exports.getIsReactServer = getIsReactServer;
exports.getIsDev = getIsDev;
exports.getIsFastRefreshEnabled = getIsFastRefreshEnabled;
exports.getIsProd = getIsProd;
exports.getIsNodeModule = getIsNodeModule;
exports.getBaseUrl = getBaseUrl;
exports.getReactCompiler = getReactCompiler;
exports.getIsServer = getIsServer;
exports.getIsLoaderBundle = getIsLoaderBundle;
exports.getMetroSourceType = getMetroSourceType;
exports.getBabelRuntimeVersion = getBabelRuntimeVersion;
exports.getExpoRouterAbsoluteAppRoot = getExpoRouterAbsoluteAppRoot;
exports.getInlineEnvVarsEnabled = getInlineEnvVarsEnabled;
exports.getAsyncRoutes = getAsyncRoutes;
exports.createAddNamedImportOnce = createAddNamedImportOnce;
exports.toPosixPath = toPosixPath;
// @ts-expect-error: missing types
const helper_module_imports_1 = require("@babel/helper-module-imports");
const node_path_1 = __importDefault(require("node:path"));
function hasModule(name) {
try {
return !!require.resolve(name);
}
catch (error) {
if (error.code === 'MODULE_NOT_FOUND' && error.message.includes(name)) {
return false;
}
throw error;
}
}
/** Determine which bundler is being used. */
function getBundler(caller) {
assertExpoBabelCaller(caller);
if (!caller)
return null;
if (caller.bundler)
return caller.bundler;
if (
// Known tools that use `webpack`-mode via `babel-loader`: `@expo/webpack-config`, Next.js <10
caller.name === 'babel-loader' ||
// NextJS 11 uses this custom caller name.
caller.name === 'next-babel-turbo-loader') {
return 'webpack';
}
// Assume anything else is Metro.
return 'metro';
}
function getPlatform(caller) {
assertExpoBabelCaller(caller);
if (!caller)
return null;
if (caller.platform)
return caller.platform;
const bundler = getBundler(caller);
if (bundler === 'webpack') {
return 'web';
}
// unknown
return caller.platform;
}
function getPossibleProjectRoot(caller) {
assertExpoBabelCaller(caller);
if (!caller)
return null;
if (caller.projectRoot)
return caller.projectRoot;
// unknown
return process.env.EXPO_PROJECT_ROOT;
}
/** If bundling for a react-server target. */
function getIsReactServer(caller) {
assertExpoBabelCaller(caller);
return caller?.isReactServer ?? false;
}
function assertExpoBabelCaller(caller) { }
function getIsDev(caller) {
assertExpoBabelCaller(caller);
if (caller?.isDev != null)
return caller.isDev;
// https://babeljs.io/docs/options#envname
return process.env.BABEL_ENV === 'development' || process.env.NODE_ENV === 'development';
}
function getIsFastRefreshEnabled(caller) {
assertExpoBabelCaller(caller);
if (!caller)
return false;
// NOTE(@kitten): `isHMREnabled` is always true in `@expo/metro-config`.
// However, we still use this option to ensure fast refresh is only enabled in supported runtimes (Metro + Expo)
return !!caller.isHMREnabled && !caller.isServer && !caller.isNodeModule && getIsDev(caller);
}
function getIsProd(caller) {
assertExpoBabelCaller(caller);
if (caller?.isDev != null)
return caller.isDev === false;
// https://babeljs.io/docs/options#envname
return process.env.BABEL_ENV === 'production' || process.env.NODE_ENV === 'production';
}
function getIsNodeModule(caller) {
return caller?.isNodeModule ?? false;
}
function getBaseUrl(caller) {
assertExpoBabelCaller(caller);
return caller?.baseUrl ?? '';
}
function getReactCompiler(caller) {
assertExpoBabelCaller(caller);
return caller?.supportsReactCompiler ?? false;
}
function getIsServer(caller) {
assertExpoBabelCaller(caller);
return caller?.isServer ?? false;
}
function getIsLoaderBundle(caller) {
assertExpoBabelCaller(caller);
return caller?.isLoaderBundle ?? false;
}
function getMetroSourceType(caller) {
assertExpoBabelCaller(caller);
return caller?.metroSourceType;
}
function getBabelRuntimeVersion(caller) {
assertExpoBabelCaller(caller);
let babelRuntimeVersion;
if (typeof caller?.babelRuntimeVersion === 'string') {
babelRuntimeVersion = caller.babelRuntimeVersion;
}
else {
try {
babelRuntimeVersion = require('@babel/runtime/package.json').version;
}
catch (error) {
if (error.code !== 'MODULE_NOT_FOUND')
throw error;
}
}
// NOTE(@kitten): The default shouldn't be higher than `expo/package.json`'s `@babel/runtime` version
// or `babel-preset-expo/package.json`'s peer dependency range for `@babel/runtime`
return babelRuntimeVersion ?? '^7.20.0';
}
function getExpoRouterAbsoluteAppRoot(caller) {
assertExpoBabelCaller(caller);
const rootModuleId = caller?.routerRoot ?? './app';
if (node_path_1.default.isAbsolute(rootModuleId)) {
return rootModuleId;
}
const projectRoot = getPossibleProjectRoot(caller) || '/';
return node_path_1.default.join(projectRoot, rootModuleId);
}
function getInlineEnvVarsEnabled(caller) {
assertExpoBabelCaller(caller);
const isWebpack = getBundler(caller) === 'webpack';
const isServer = getIsServer(caller);
const isNodeModule = getIsNodeModule(caller);
const preserveEnvVars = caller?.preserveEnvVars;
// Development env vars are added using references to enable HMR in development.
// Servers have env vars left as-is to read from the environment.
return !isNodeModule && !isWebpack && !isServer && !preserveEnvVars;
}
function getAsyncRoutes(caller) {
assertExpoBabelCaller(caller);
const isServer = getIsServer(caller);
if (isServer) {
return false;
}
const isProd = getIsProd(caller);
const platform = getPlatform(caller);
if (platform !== 'web' && isProd) {
return false;
}
return caller?.asyncRoutes ?? false;
}
const getOrCreateInMap = (map, key, create) => {
if (!map.has(key)) {
const result = create();
map.set(key, result);
return [result, true];
}
return [map.get(key), false];
};
function createAddNamedImportOnce(t) {
const addedImportsCache = new Map();
return function addNamedImportOnce(path, name, source) {
const [sourceCache] = getOrCreateInMap(addedImportsCache, source, () => new Map());
const [identifier, didCreate] = getOrCreateInMap(sourceCache, name, () => (0, helper_module_imports_1.addNamed)(path, name, source));
// for cached imports, we need to clone the resulting identifier, because otherwise
// '@babel/plugin-transform-modules-commonjs' won't replace the references to the import for some reason.
// this is a helper for that.
return didCreate ? identifier : t.cloneNode(identifier);
};
}
const REGEXP_REPLACE_SLASHES = /\\/g;
/**
* Convert any platform-specific path to a POSIX path.
*/
function toPosixPath(filePath) {
return filePath.replace(REGEXP_REPLACE_SLASHES, '/');
}

View File

@@ -0,0 +1,12 @@
/**
* Copyright © 2024 650 Industries.
* Copyright (c) 2016 Formidable
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { ConfigAPI, PluginObj, PluginPass } from '@babel/core';
declare function definePlugin({ types: t, }: ConfigAPI & typeof import('@babel/core')): PluginObj<PluginPass & {
opts: Record<string, null | boolean | string>;
}>;
export default definePlugin;

135
node_modules/babel-preset-expo/build/define-plugin.js generated vendored Normal file
View File

@@ -0,0 +1,135 @@
"use strict";
/**
* Copyright © 2024 650 Industries.
* Copyright (c) 2016 Formidable
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const TYPEOF_PREFIX = 'typeof ';
function definePlugin({ types: t, }) {
/**
* Replace a node with a given value. If the replacement results in a BinaryExpression, it will be
* evaluated. For example, if the result of the replacement is `var x = "production" === "production"`
* The evaluation will make a second replacement resulting in `var x = true`
*/
function replaceAndEvaluateNode(nodePath, replacement) {
nodePath.replaceWith(t.valueToNode(replacement));
if (nodePath.parentPath?.isBinaryExpression()) {
const result = nodePath.parentPath.evaluate();
if (result.confident) {
nodePath.parentPath.replaceWith(t.valueToNode(result.value));
}
}
}
/** Get the root identifier name from a member expression (e.g., "process" from "process.env.NODE_ENV") */
function getMemberExpressionRoot(node) {
let current = node;
while (t.isMemberExpression(current)) {
current = current.object;
}
return t.isIdentifier(current) ? current.name : null;
}
return {
name: 'expo-define-globals',
pre() {
// Pre-process replacements once per file
const identifiers = new Map();
const memberPatterns = [];
const typeofValues = new Map();
const memberRoots = new Set();
for (const key of Object.keys(this.opts)) {
const value = this.opts[key];
if (key.startsWith(TYPEOF_PREFIX)) {
// "typeof window" -> typeofValues["window"]
typeofValues.set(key.slice(TYPEOF_PREFIX.length), value);
}
else if (key.includes('.')) {
// "process.env.NODE_ENV" -> memberPatterns, extract "process" as root
memberPatterns.push([key, value]);
const root = key.split('.')[0];
memberRoots.add(root);
}
else {
// "__DEV__" -> identifiers
identifiers.set(key, value);
}
}
this.processed = {
identifiers,
memberPatterns,
typeofValues,
memberRoots,
};
},
visitor: {
// process.env.NODE_ENV;
MemberExpression(nodePath, state) {
// Prevent rewriting if the member expression is on the left-hand side of an assignment
if (t.isAssignmentExpression(nodePath.parent) && nodePath.parent.left === nodePath.node) {
return;
}
const { memberPatterns, memberRoots } = state.processed;
if (memberPatterns.length === 0)
return;
// Quick filter: check if root matches any known pattern root
const root = getMemberExpressionRoot(nodePath.node);
if (!root || !memberRoots.has(root))
return;
// Check against patterns
for (const [pattern, replacement] of memberPatterns) {
if (nodePath.matchesPattern(pattern)) {
replaceAndEvaluateNode(nodePath, replacement);
return;
}
}
},
// const x = { version: VERSION };
ReferencedIdentifier(nodePath, state) {
const { identifiers } = state.processed;
if (identifiers.size === 0)
return;
const name = nodePath.node.name;
// Quick check: is this identifier in our replacements?
if (!identifiers.has(name))
return;
// Check for binding (locally defined variable shadows replacement)
if (nodePath.scope?.getBinding(name))
return;
// Don't transform import identifiers (mimics webpack's DefinePlugin behavior)
const container = nodePath.container;
if (container &&
!Array.isArray(container) &&
'type' in container &&
(container.type === 'ImportDefaultSpecifier' || container.type === 'ImportSpecifier')) {
return;
}
// Do not transform Object keys / properties unless they are computed like {[key]: value}
if ((nodePath.key === 'key' || nodePath.key === 'property') &&
nodePath.parent &&
'computed' in nodePath.parent &&
nodePath.parent.computed === false) {
return;
}
replaceAndEvaluateNode(nodePath, identifiers.get(name));
},
// typeof window
UnaryExpression(nodePath, state) {
if (nodePath.node.operator !== 'typeof')
return;
const { typeofValues } = state.processed;
if (typeofValues.size === 0)
return;
const argument = nodePath.node.argument;
if (!t.isIdentifier(argument))
return;
const replacement = typeofValues.get(argument.name);
if (replacement !== undefined) {
replaceAndEvaluateNode(nodePath, replacement);
}
},
},
};
}
exports.default = definePlugin;

View File

@@ -0,0 +1,5 @@
/**
* Copyright © 2024 650 Industries.
*/
import type { PluginObj, ConfigAPI } from '@babel/core';
export declare function detectDynamicExports(api: ConfigAPI & typeof import('@babel/core')): PluginObj;

View File

@@ -0,0 +1,87 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectDynamicExports = detectDynamicExports;
const debug = require('debug')('expo:babel:exports');
// A babel pass to detect the usage of `module.exports` or `exports` in a module for use in
// export all expansion passes during tree shaking.
function detectDynamicExports(api) {
const { types: t } = api;
return {
name: 'expo-detect-dynamic-exports',
pre(file) {
assertExpoMetadata(file.metadata);
file.metadata.hasCjsExports = false;
},
visitor: {
// Any usage of `module.exports` or `exports` will mark the module as non-static.
// module.exports.a = 1;
// exports.a = 1;
CallExpression(path, state) {
assertExpoMetadata(state.file.metadata);
if (state.file.metadata.hasCjsExports)
return;
const callee = path.node.callee;
if (
// Object.assign(...)
t.isMemberExpression(callee) &&
t.isIdentifier(callee.object, { name: 'Object' }) &&
t.isIdentifier(callee.property, { name: 'assign' }) &&
// Allow `Object.assign(module.exports)` since it does nothing. Must have a second argument.
path.node.arguments.length > 1) {
const isModuleExports = t.isMemberExpression(path.node.arguments[0]) &&
t.isIdentifier(path.node.arguments[0].object, { name: 'module' }) &&
// Second argument is `exports` or 'exports'
// .exports
(t.isIdentifier(path.node.arguments[0].property, { name: 'exports' }) ||
// ['exports']
(t.isStringLiteral(path.node.arguments[0].property) &&
path.node.arguments[0].property.value === 'exports'));
// NOTE: Cannot match `['exp' + 'orts']`. We'd need to run after minification to match that confidently.
// Check for Object.assign(module.exports, ...), Object.assign(exports, ...)
if (
// module.exports
isModuleExports ||
// exports
t.isIdentifier(path.node.arguments[0], { name: 'exports' })) {
debug('Found Object.assign to module.exports or exports at ' + path.node.loc?.start.line);
state.file.metadata.hasCjsExports = true;
}
}
},
AssignmentExpression(path, state) {
assertExpoMetadata(state.file.metadata);
if (state.file.metadata.hasCjsExports)
return;
const left = path.node.left;
// Detect module.exports.foo = ... or exports.foo = ...
if ((t.isMemberExpression(left) &&
((t.isIdentifier(left.object, { name: 'module' }) &&
(t.isIdentifier(left.property, { name: 'exports' }) ||
(t.isStringLiteral(left.property) && left.property.value === 'exports'))) ||
t.isIdentifier(left.object, { name: 'exports' }))) ||
('object' in left &&
t.isMemberExpression(left.object) &&
t.isIdentifier(left.object.object, { name: 'module' }) &&
(t.isIdentifier(left.object.property, { name: 'exports' }) ||
(t.isStringLiteral(left.object.property) &&
left.object.property.value === 'exports')))) {
debug('Found assignment to module.exports or exports at ' + path.node.loc?.start.line);
state.file.metadata.hasCjsExports = true;
}
else if (t.isIdentifier(left, { name: 'exports' }) &&
path.scope.hasGlobal('exports') &&
// Ensure left is not defined in any scope
!path.scope.hasBinding('exports')) {
debug('Found assignment to exports at ' + path.node.loc?.start.line);
state.file.metadata.hasCjsExports = true;
}
},
},
};
}
function assertExpoMetadata(metadata) {
if (metadata && typeof metadata === 'object') {
return;
}
throw new Error('Expected Babel state.file.metadata to be an object');
}

View File

@@ -0,0 +1,6 @@
/**
* Copyright © 2024 650 Industries.
*/
import type { ConfigAPI, PluginObj } from '@babel/core';
/** Prevent importing certain known imports in given environments. This is for sanity to ensure a module never accidentally gets imported unexpectedly. */
export declare function environmentRestrictedImportsPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj;

View File

@@ -0,0 +1,60 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.environmentRestrictedImportsPlugin = environmentRestrictedImportsPlugin;
const common_1 = require("./common");
const FORBIDDEN_CLIENT_IMPORTS = ['server-only'];
const FORBIDDEN_REACT_SERVER_IMPORTS = ['client-only'];
/** Prevent importing certain known imports in given environments. This is for sanity to ensure a module never accidentally gets imported unexpectedly. */
function environmentRestrictedImportsPlugin(api) {
const { types: t } = api;
const isReactServer = api.caller(common_1.getIsReactServer);
const forbiddenPackages = isReactServer
? FORBIDDEN_REACT_SERVER_IMPORTS
: FORBIDDEN_CLIENT_IMPORTS;
function checkSource(source, path) {
forbiddenPackages.forEach((forbiddenImport) => {
if (source === forbiddenImport) {
if (isReactServer) {
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.`);
}
else {
throw path.buildCodeFrameError(`Importing '${forbiddenImport}' module is not allowed in a client component.`);
}
}
});
}
return {
name: 'expo-environment-restricted-imports-plugin',
visitor: {
ImportDeclaration(path) {
checkSource(path.node.source.value, path);
},
ExportAllDeclaration(path) {
if (path.node.source) {
checkSource(path.node.source.value, path);
}
},
ExportNamedDeclaration(path) {
if (path.node.source) {
checkSource(path.node.source.value, path);
}
},
CallExpression(path) {
if ((('name' in path.node.callee && path.node.callee.name === 'require') ||
(t.isMemberExpression(path.node.callee) &&
'name' in path.node.callee.property &&
['resolveWeak', 'importAll', 'importDefault'].includes(path.node.callee.property.name))) &&
path.node.arguments.length > 0 &&
t.isStringLiteral(path.node.arguments[0])) {
checkSource(path.node.arguments[0].value, path);
}
// Handle dynamic import() syntax
else if (path.node.callee.type === 'Import' &&
path.node.arguments.length > 0 &&
t.isStringLiteral(path.node.arguments[0])) {
checkSource(path.node.arguments[0].value, path);
}
},
},
};
}

View File

@@ -0,0 +1,6 @@
import type { ConfigAPI, PluginObj, PluginPass } from '@babel/core';
interface InlineManifestState extends PluginPass {
projectRoot: string;
}
export declare function expoInlineManifestPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj<InlineManifestState>;
export {};

View File

@@ -0,0 +1,191 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.expoInlineManifestPlugin = expoInlineManifestPlugin;
const common_1 = require("./common");
const debug = require('debug')('expo:babel:inline-manifest');
// Convert expo value to PWA value
function ensurePWAOrientation(orientation) {
if (orientation) {
const webOrientation = orientation.toLowerCase();
if (webOrientation !== 'default') {
return webOrientation;
}
}
return undefined;
}
const RESTRICTED_MANIFEST_FIELDS = [
'androidNavigationBar',
'androidStatusBar',
'privacy',
// Remove iOS and Android.
'ios',
'android',
// Hide internal / build values
'plugins',
'hooks', // hooks no longer exists in the typescript type but should still be removed
'_internal',
// Remove metro-specific values
'assetBundlePatterns',
];
function getExpoConstantsManifest(projectRoot) {
const config = getConfigMemo(projectRoot);
if (!config)
return null;
const manifest = applyWebDefaults(config);
for (const field of RESTRICTED_MANIFEST_FIELDS) {
delete manifest[field];
}
return manifest;
}
function applyWebDefaults({ config, appName, webName }) {
const appJSON = config.exp;
// For RN CLI support
const { web: webManifest = {}, splash = {}, ios = {}, android = {} } = appJSON;
const languageISOCode = webManifest.lang;
const primaryColor = appJSON.primaryColor;
const description = appJSON.description;
// The theme_color sets the color of the tool bar, and may be reflected in the app's preview in task switchers.
const webThemeColor = webManifest.themeColor || primaryColor;
const dir = webManifest.dir;
const shortName = webManifest.shortName || webName;
const display = webManifest.display;
const startUrl = webManifest.startUrl;
const { scope, crossorigin } = webManifest;
const barStyle = webManifest.barStyle;
const orientation = ensurePWAOrientation(webManifest.orientation || appJSON.orientation);
/**
* **Splash screen background color**
* `https://developers.google.com/web/fundamentals/web-app-manifest/#splash-screen`
* The background_color should be the same color as the load page,
* to provide a smooth transition from the splash screen to your app.
*/
const backgroundColor = webManifest.backgroundColor || splash.backgroundColor; // No default background color
return {
...appJSON,
name: appName,
description,
primaryColor,
// Ensure these objects exist
ios: {
...ios,
},
android: {
...android,
},
web: {
...webManifest,
meta: undefined,
build: undefined,
scope,
crossorigin,
description,
startUrl,
shortName,
display,
orientation,
dir,
barStyle,
backgroundColor,
themeColor: webThemeColor,
lang: languageISOCode,
name: webName,
},
};
}
function getExpoAppManifest(projectRoot) {
if (process.env.APP_MANIFEST) {
return process.env.APP_MANIFEST;
}
const exp = getExpoConstantsManifest(projectRoot);
if (exp) {
debug('public manifest', exp);
return JSON.stringify(exp);
}
else {
debug('public manifest is null. `expo/config` is not available');
return null;
}
}
let configMemo;
function getConfigMemo(projectRoot) {
if (configMemo === undefined) {
let expoConfig;
try {
// This is an optional dependency. In practice, it will resolve in all Expo projects/apps
// since `expo` is a direct dependency in those. If `babel-preset-expo` is used independently
// this will fail and we won't return a config
expoConfig = require('expo/config');
}
catch (error) {
if ('code' in error && error.code === 'MODULE_NOT_FOUND') {
return (configMemo = null);
}
throw error;
}
const { getConfig, getNameFromConfig } = expoConfig;
const config = getConfig(projectRoot, {
isPublicConfig: true,
skipSDKVersionRequirement: true,
});
// rn-cli apps use a displayName value as well.
const { appName, webName } = getNameFromConfig(config.exp);
configMemo = {
config,
appName,
webName,
};
}
return configMemo;
}
// Convert `process.env.APP_MANIFEST` to a modified web-specific variation of the app.json public manifest.
function expoInlineManifestPlugin(api) {
const { types: t } = api;
const isReactServer = api.caller(common_1.getIsReactServer);
const platform = api.caller(common_1.getPlatform);
const possibleProjectRoot = api.caller(common_1.getPossibleProjectRoot);
const shouldInline = platform === 'web' || isReactServer;
// Early exit: return a no-op plugin if we're not going to inline anything
if (!shouldInline) {
return {
name: 'expo-inline-manifest-plugin',
visitor: {},
};
}
return {
name: 'expo-inline-manifest-plugin',
pre() {
this.projectRoot = possibleProjectRoot || this.file.opts.root || '';
},
visitor: {
MemberExpression(path, state) {
// We're looking for: process.env.APP_MANIFEST
// This visitor is called on every MemberExpression, so we need fast checks
// Quick check: the property we're looking for is 'APP_MANIFEST'
// The parent must be a MemberExpression with property 'APP_MANIFEST'
const parent = path.parentPath;
if (!parent?.isMemberExpression())
return;
// Check if parent's property is APP_MANIFEST (most selective check first)
const parentProp = parent.node.property;
if (!t.isIdentifier(parentProp) || parentProp.name !== 'APP_MANIFEST')
return;
// Now verify this is process.env
const obj = path.node.object;
const prop = path.node.property;
if (!t.isIdentifier(obj) || obj.name !== 'process')
return;
if (!t.isIdentifier(prop) || prop.name !== 'env')
return;
// Skip if this is an assignment target
if (parent.parentPath?.isAssignmentExpression())
return;
// Surfaces the `app.json` (config) as an environment variable which is then parsed by
// `expo-constants` https://docs.expo.dev/versions/latest/sdk/constants/
const manifest = getExpoAppManifest(state.projectRoot);
if (manifest !== null) {
parent.replaceWith(t.stringLiteral(manifest));
}
},
},
};
}

View File

@@ -0,0 +1,13 @@
/**
* Copyright © 2024 650 Industries.
*/
import type { ConfigAPI, PluginObj } from '@babel/core';
/**
* Inlines environment variables to configure the process:
*
* EXPO_PROJECT_ROOT
* EXPO_ROUTER_ABS_APP_ROOT
* EXPO_ROUTER_APP_ROOT
* EXPO_ROUTER_IMPORT_MODE
*/
export declare function expoRouterBabelPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj;

View File

@@ -0,0 +1,96 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.expoRouterBabelPlugin = expoRouterBabelPlugin;
const node_path_1 = __importDefault(require("node:path"));
const common_1 = require("./common");
const debug = require('debug')('expo:babel:router');
/**
* Compute the relative path from the current file to the app folder.
*
* Previously this was computed relative to `expo-router/entry`, but that breaks
* when packages are hoisted to unexpected locations (e.g., with Bun in monorepos).
* By using the actual file being transformed, the relative path is always correct
* regardless of where the package is installed.
*/
function getExpoRouterAppRoot(currentFile, appFolder) {
const appRoot = node_path_1.default.relative(node_path_1.default.dirname(currentFile), appFolder);
debug('getExpoRouterAppRoot', currentFile, appFolder, appRoot);
return appRoot;
}
/**
* Inlines environment variables to configure the process:
*
* EXPO_PROJECT_ROOT
* EXPO_ROUTER_ABS_APP_ROOT
* EXPO_ROUTER_APP_ROOT
* EXPO_ROUTER_IMPORT_MODE
*/
function expoRouterBabelPlugin(api) {
const { types: t } = api;
const possibleProjectRoot = api.caller(common_1.getPossibleProjectRoot);
const asyncRoutes = api.caller(common_1.getAsyncRoutes);
const routerAbsoluteRoot = api.caller(common_1.getExpoRouterAbsoluteAppRoot);
const importMode = asyncRoutes ? 'lazy' : 'sync';
return {
name: 'expo-router',
pre() {
const state = this;
state.projectRoot = possibleProjectRoot || this.file.opts.root || '';
// Check test env at transform time, not module load time
state.isTestEnv = process.env.NODE_ENV === 'test';
},
visitor: {
MemberExpression(path, state) {
// Quick check: skip if not accessing something on an object named 'process'
const object = path.node.object;
if (!t.isMemberExpression(object))
return;
const objectOfObject = object.object;
if (!t.isIdentifier(objectOfObject) || objectOfObject.name !== 'process')
return;
// Now check if it's process.env
if (!t.isIdentifier(object.property) || object.property.name !== 'env')
return;
// Skip if this is an assignment target
if (t.isAssignmentExpression(path.parent) && path.parent.left === path.node)
return;
// Get the property key
const key = path.toComputedKey();
if (!t.isStringLiteral(key))
return;
const keyValue = key.value;
// Check each possible env var
switch (keyValue) {
case 'EXPO_PROJECT_ROOT':
path.replaceWith(t.stringLiteral(state.projectRoot));
return;
case 'EXPO_ROUTER_IMPORT_MODE':
path.replaceWith(t.stringLiteral(importMode));
return;
default:
break;
}
// Skip app root transforms in tests (handled by testing-library utils)
if (state.isTestEnv)
return;
switch (keyValue) {
case 'EXPO_ROUTER_ABS_APP_ROOT':
path.replaceWith(t.stringLiteral(routerAbsoluteRoot));
return;
case 'EXPO_ROUTER_APP_ROOT': {
// Use the actual file being transformed to compute the relative path.
// This ensures the path is correct regardless of package hoisting.
const filename = state.filename || state.file.opts.filename;
if (!filename) {
throw new Error('babel-preset-expo: Unable to determine filename for EXPO_ROUTER_APP_ROOT transformation');
}
path.replaceWith(t.stringLiteral(getExpoRouterAppRoot(filename, routerAbsoluteRoot)));
}
}
},
},
};
}

View File

@@ -0,0 +1,2 @@
import type { ConfigAPI, PluginObj } from '@babel/core';
export declare function expoImportMetaTransformPluginFactory(pluginEnabled: boolean): (api: ConfigAPI & typeof import("@babel/core")) => PluginObj;

View File

@@ -0,0 +1,29 @@
"use strict";
// Copyright 2015-present 650 Industries. All rights reserved.
Object.defineProperty(exports, "__esModule", { value: true });
exports.expoImportMetaTransformPluginFactory = expoImportMetaTransformPluginFactory;
const common_1 = require("./common");
function expoImportMetaTransformPluginFactory(pluginEnabled) {
return (api) => {
const { types: t } = api;
const platform = api.caller(common_1.getPlatform);
return {
name: 'expo-import-meta-transform',
visitor: {
MetaProperty(path) {
const { node } = path;
if (node.meta.name === 'import' && node.property.name === 'meta') {
if (!pluginEnabled) {
if (platform !== 'web') {
throw path.buildCodeFrameError('`import.meta` is not supported in Hermes. Enable the polyfill `unstable_transformImportMeta` in babel-preset-expo to use this syntax.');
}
return;
}
const replacement = t.memberExpression(t.identifier('globalThis'), t.identifier('__ExpoImportMetaRegistry'));
path.replaceWith(replacement);
}
},
},
};
};
}

49
node_modules/babel-preset-expo/build/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,49 @@
import type { ConfigAPI, TransformOptions } from '@babel/core';
import type { PluginOptions as ReactCompilerOptions } from 'babel-plugin-react-compiler';
type BabelPresetExpoPlatformOptions = {
/** Disable or configure the `@babel/plugin-proposal-decorators` plugin. */
decorators?: false | {
legacy?: boolean;
version?: number;
};
/** Enable or disable adding the Reanimated plugin by default. @default `true` */
reanimated?: boolean;
/** Enable or disable adding the Worklets plugin by default. Only applies when
* using `react-native-worklets` or Reanimated 4. @default `true`
*/
worklets?: boolean;
/** @deprecated Set `jsxRuntime: 'classic'` to disable automatic JSX handling. */
useTransformReactJSXExperimental?: boolean;
/** Change the policy for handling JSX in a file. Passed to `plugin-transform-react-jsx`. @default `'automatic'` */
jsxRuntime?: 'classic' | 'automatic';
/** Change the source module ID to use when importing an automatic JSX import. Only applied when `jsxRuntime` is `'automatic'` (default). Passed to `plugin-transform-react-jsx`. @default `'react'` */
jsxImportSource?: string;
lazyImports?: boolean;
disableImportExportTransform?: boolean;
disableDeepImportWarnings?: boolean;
disableFlowStripTypesTransform?: boolean;
enableBabelRuntime?: boolean | string;
unstable_transformProfile?: 'default' | 'hermes-stable' | 'hermes-canary';
/** Settings to pass to `babel-plugin-react-compiler`. Set as `false` to disable the plugin. */
'react-compiler'?: false | ReactCompilerOptions;
/** Only set to `false` to disable `react-refresh/babel` forcefully, defaults to `undefined` */
enableReactFastRefresh?: boolean;
/** Enable `typeof window` runtime checks. The default behavior is to minify `typeof window` on web clients to `"object"` and `"undefined"` on servers. */
minifyTypeofWindow?: boolean;
/**
* Enable that transform that converts `import.meta` to `globalThis.__ExpoImportMetaRegistry`.
*
* > **Note:** Use this option at your own risk. If the JavaScript engine supports `import.meta` natively, this transformation may interfere with the native implementation.
*
* @default `false` on client and `true` on server.
*/
unstable_transformImportMeta?: boolean;
};
export type BabelPresetExpoOptions = BabelPresetExpoPlatformOptions & {
/** Web-specific settings. */
web?: BabelPresetExpoPlatformOptions;
/** Native-specific settings. */
native?: BabelPresetExpoPlatformOptions;
};
declare function babelPresetExpo(api: ConfigAPI, options?: BabelPresetExpoOptions): TransformOptions;
export default babelPresetExpo;

325
node_modules/babel-preset-expo/build/index.js generated vendored Normal file
View File

@@ -0,0 +1,325 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const client_module_proxy_plugin_1 = require("./client-module-proxy-plugin");
const common_1 = require("./common");
const environment_restricted_imports_1 = require("./environment-restricted-imports");
const expo_inline_manifest_plugin_1 = require("./expo-inline-manifest-plugin");
const expo_router_plugin_1 = require("./expo-router-plugin");
const import_meta_transform_plugin_1 = require("./import-meta-transform-plugin");
const inline_env_vars_1 = require("./inline-env-vars");
const lazyImports_1 = require("./lazyImports");
const restricted_react_api_plugin_1 = require("./restricted-react-api-plugin");
const server_actions_plugin_1 = require("./server-actions-plugin");
const server_data_loaders_plugin_1 = require("./server-data-loaders-plugin");
const use_dom_directive_plugin_1 = require("./use-dom-directive-plugin");
const widgets_plugin_1 = require("./widgets-plugin");
function getOptions(options, platform) {
const tag = platform === 'web' ? 'web' : 'native';
return {
...options,
...options[tag],
};
}
function babelPresetExpo(api, options = {}) {
const bundler = api.caller(common_1.getBundler);
const isWebpack = bundler === 'webpack';
let platform = api.caller((caller) => caller?.platform);
const engine = api.caller((caller) => caller?.engine) ?? 'default';
const isDev = api.caller(common_1.getIsDev);
const isNodeModule = api.caller(common_1.getIsNodeModule);
const isServer = api.caller(common_1.getIsServer);
const isReactServer = api.caller(common_1.getIsReactServer);
const isFastRefreshEnabled = api.caller(common_1.getIsFastRefreshEnabled);
const isReactCompilerEnabled = api.caller(common_1.getReactCompiler);
const metroSourceType = api.caller(common_1.getMetroSourceType);
const baseUrl = api.caller(common_1.getBaseUrl);
const supportsStaticESM = api.caller((caller) => caller?.supportsStaticESM);
const isServerEnv = isServer || isReactServer;
// Unlike `isDev`, this will be `true` when the bundler is explicitly set to `production`,
// i.e. `false` when testing, development, or used with a bundler that doesn't specify the correct inputs.
const isProduction = api.caller(common_1.getIsProd);
const inlineEnvironmentVariables = api.caller(common_1.getInlineEnvVarsEnabled);
// If the `platform` prop is not defined then this must be a custom config that isn't
// defining a platform in the babel-loader. Currently this may happen with Next.js + Expo web.
if (!platform && isWebpack) {
platform = 'web';
}
// Use the simpler babel preset for web and server environments (both web and native SSR).
const isModernEngine = platform === 'web' || isServerEnv;
const platformOptions = getOptions(options, platform);
// If the input is a script, we're unable to add any dependencies. Since the @babel/runtime transformer
// adds extra dependencies (requires/imports) we need to disable it
if (metroSourceType === 'script') {
platformOptions.enableBabelRuntime = false;
}
if (platformOptions.useTransformReactJSXExperimental != null) {
throw new Error(`babel-preset-expo: The option 'useTransformReactJSXExperimental' has been removed in favor of { jsxRuntime: 'classic' }.`);
}
if (platformOptions.disableImportExportTransform == null) {
if (platform === 'web') {
// Only disable import/export transform when Webpack is used because
// Metro does not support tree-shaking.
platformOptions.disableImportExportTransform = supportsStaticESM ?? isWebpack;
}
else {
platformOptions.disableImportExportTransform = supportsStaticESM ?? false;
}
}
if (platformOptions.unstable_transformProfile == null) {
platformOptions.unstable_transformProfile = engine === 'hermes' ? 'hermes-stable' : 'default';
}
// Note that if `options.lazyImports` is not set (i.e., `null` or `undefined`),
// `@react-native/babel-preset` will handle it.
const lazyImportsOption = platformOptions?.lazyImports;
const extraPlugins = [];
// Add compiler as soon as possible to prevent other plugins from modifying the code.
if (isReactCompilerEnabled &&
// Don't run compiler on node modules, it can only safely be run on the user's code.
!isNodeModule &&
// Only run for client code. It's unclear if compiler has any benefits for React Server Components.
// NOTE: We might want to allow running it to prevent hydration errors.
!isServerEnv &&
// Give users the ability to opt-out of the feature, per-platform.
platformOptions['react-compiler'] !== false) {
const reactCompilerOptions = platformOptions['react-compiler'];
const reactCompilerOptOutDirectives = new Set([
// We need to opt-out for our widgets, since they're stringified functions that output Swift UI JSX
'widget',
// We need to manually include the default opt-out directives, since they get overridden
// See:
// - https://github.com/facebook/react/blob/e0cc720/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts#L48C1-L48C77
// - https://github.com/facebook/react/blob/e0cc720/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts#L69-L86
'use no memo',
'use no forget',
// Add the user's override but preserve defaults above to avoid the pitfall of them being removed
...(reactCompilerOptions?.customOptOutDirectives ?? []),
]);
extraPlugins.push([
require('babel-plugin-react-compiler'),
{
target: '19',
environment: {
enableResetCacheOnSourceFileChanges: !isProduction,
...(platformOptions['react-compiler']?.environment ?? {}),
},
panicThreshold: isDev ? undefined : 'NONE',
...reactCompilerOptions,
// See: https://github.com/facebook/react/blob/074d96b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Options.ts#L160-L163
customOptOutDirectives: [...reactCompilerOptOutDirectives],
},
]);
}
if (engine !== 'hermes') {
// `@react-native/babel-preset` configures this plugin with `{ loose: true }`, which breaks all
// getters and setters in spread objects. We need to add this plugin ourself without that option.
// @see https://github.com/expo/expo/pull/11960#issuecomment-887796455
extraPlugins.push([
require('@babel/plugin-transform-object-rest-spread'),
// Assume no dependence on getters or evaluation order. See https://github.com/babel/babel/pull/11520
{ loose: true, useBuiltIns: true },
]);
}
else if (!isModernEngine) {
// This is added back on hermes to ensure the react-jsx-dev plugin (`@babel/preset-react`) works as expected when
// JSX is used in a function body. This is technically not required in production, but we
// should retain the same behavior since it's hard to debug the differences.
extraPlugins.push(require('@babel/plugin-transform-parameters'),
// Add support for class static blocks.
[require('@babel/plugin-transform-class-static-block'), { loose: true }]);
}
const inlines = {
'process.env.EXPO_OS': platform,
// 'typeof document': isServerEnv ? 'undefined' : 'object',
'process.env.EXPO_SERVER': !!isServerEnv,
};
// `typeof window` is left in place for native + client environments.
// NOTE(@kitten): We're temporarily disabling this default optimization for Web targets due to Web Workers
// We're currently not passing metadata to indicate we're transforming for a Web Worker to disable this automatically
const minifyTypeofWindow = platformOptions.minifyTypeofWindow ?? isServerEnv;
if (minifyTypeofWindow !== false) {
// This nets out slightly faster in development when considering the cost of bundling server dependencies.
inlines['typeof window'] = isServerEnv ? 'undefined' : 'object';
}
if (isProduction) {
inlines['process.env.NODE_ENV'] = 'production';
inlines['__DEV__'] = false;
inlines['Platform.OS'] = platform;
}
if (process.env.NODE_ENV !== 'test') {
inlines['process.env.EXPO_BASE_URL'] = baseUrl;
}
extraPlugins.push([require('./define-plugin'), inlines]);
if (isProduction) {
// 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.
// Here, we'll apply it before the commonjs transform, in production only, to ensure `Platform.OS` is replaced with a string literal.
extraPlugins.push([
require('./minify-platform-select-plugin'),
{
platform,
},
]);
}
if (platformOptions.useTransformReactJSXExperimental != null) {
throw new Error(`babel-preset-expo: The option 'useTransformReactJSXExperimental' has been removed in favor of { jsxRuntime: 'classic' }.`);
}
// Only apply in non-server, for metro-only, in production environments, when the user hasn't disabled the feature.
// Webpack uses DefinePlugin for environment variables.
// Development uses an uncached serializer.
// Servers read from the environment.
// Users who disable the feature may be using a different babel plugin.
if (inlineEnvironmentVariables) {
extraPlugins.push(inline_env_vars_1.expoInlineEnvVars);
}
if (platform === 'web') {
extraPlugins.push(require('babel-plugin-react-native-web'));
}
// Webpack uses the DefinePlugin to provide the manifest to `expo-constants`.
if (bundler !== 'webpack') {
extraPlugins.push(expo_inline_manifest_plugin_1.expoInlineManifestPlugin);
}
if ((0, common_1.hasModule)('expo-router')) {
extraPlugins.push(expo_router_plugin_1.expoRouterBabelPlugin);
// Process `loader()` functions for client, loader and server bundles (excluding RSC)
// - Client bundles: Remove loader exports, they run on server only
// - Server bundles: Keep loader exports (needed for SSG)
// - Loader-only bundles: Keep only loader exports, remove everything else
if (!isReactServer) {
extraPlugins.push(server_data_loaders_plugin_1.serverDataLoadersPlugin);
}
}
extraPlugins.push(client_module_proxy_plugin_1.reactClientReferencesPlugin);
// Ensure these only run when the user opts-in to bundling for a react server to prevent unexpected behavior for
// users who are bundling using the client-only system.
if (isReactServer) {
extraPlugins.push(server_actions_plugin_1.reactServerActionsPlugin);
extraPlugins.push(restricted_react_api_plugin_1.environmentRestrictedReactAPIsPlugin);
}
else {
// DOM components must run after "use client" and only in client environments.
extraPlugins.push(use_dom_directive_plugin_1.expoUseDomDirectivePlugin);
}
// 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.
extraPlugins.push(environment_restricted_imports_1.environmentRestrictedImportsPlugin);
// Transform widget component JSX expressions to capture widget components for native-side evaluation.
// This enables the native side to re-evaluate widget components with updated props without re-sending the entire layout.
if ((0, common_1.hasModule)('expo-widgets')) {
extraPlugins.push(widgets_plugin_1.widgetsPlugin);
}
if (platformOptions.enableReactFastRefresh ||
(isFastRefreshEnabled && platformOptions.enableReactFastRefresh !== false)) {
extraPlugins.push([
require('react-refresh/babel'),
{
// We perform the env check to enable `isFastRefreshEnabled`, unless the plugin is force-enabled
skipEnvCheck: platformOptions.enableReactFastRefresh !== true,
},
]);
}
if (platformOptions.disableImportExportTransform) {
extraPlugins.push([require('./detect-dynamic-exports').detectDynamicExports]);
}
const polyfillImportMeta = platformOptions.unstable_transformImportMeta ?? isServerEnv;
extraPlugins.push((0, import_meta_transform_plugin_1.expoImportMetaTransformPluginFactory)(polyfillImportMeta === true));
return {
presets: [
(() => {
const presetOpts = {
// Defaults to undefined, set to `true` to disable `@babel/plugin-transform-flow-strip-types`
disableFlowStripTypesTransform: platformOptions.disableFlowStripTypesTransform,
// Defaults to Babel caller's `babelRuntimeVersion` or the version of `@babel/runtime` for this package's peer
// Set to `false` to disable `@babel/plugin-transform-runtime`
enableBabelRuntime: platformOptions.enableBabelRuntime == null ||
platformOptions.enableBabelRuntime === true
? (0, common_1.getBabelRuntimeVersion)()
: platformOptions.enableBabelRuntime,
// This reduces the amount of transforms required, as Hermes supports many modern language features.
unstable_transformProfile: platformOptions.unstable_transformProfile,
// Set true to disable `@babel/plugin-transform-react-jsx` and
// the deprecated packages `@babel/plugin-transform-react-jsx-self`, and `@babel/plugin-transform-react-jsx-source`.
//
// Otherwise, you'll sometime get errors like the following (starting in Expo SDK 43, React Native 64, React 17):
//
// TransformError App.js: /path/to/App.js: Duplicate __self prop found. You are most likely using the deprecated transform-react-jsx-self Babel plugin.
// Both __source and __self are automatically set when using the automatic jsxRuntime. Remove transform-react-jsx-source and transform-react-jsx-self from your Babel config.
useTransformReactJSXExperimental: true,
// This will never be used regardless because `useTransformReactJSXExperimental` is set to `true`.
// https://github.com/facebook/react-native/blob/a4a8695cec640e5cf12be36a0c871115fbce9c87/packages/react-native-babel-preset/src/configs/main.js#L151
withDevTools: false,
disableImportExportTransform: platformOptions.disableImportExportTransform,
disableDeepImportWarnings: platformOptions.disableDeepImportWarnings,
lazyImportExportTransform: lazyImportsOption === true
? (importModuleSpecifier) => {
// Do not lazy-initialize packages that are local imports (similar to `lazy: true`
// behavior) or are in the blacklist.
return !(importModuleSpecifier.includes('./') || lazyImports_1.lazyImports.has(importModuleSpecifier));
}
: // Pass the option directly to `@react-native/babel-preset`, which in turn
// passes it to `babel-plugin-transform-modules-commonjs`
lazyImportsOption,
dev: isDev,
};
if (isModernEngine) {
return [require('./web-preset'), presetOpts];
}
// We use `require` here instead of directly using the package name because we want to
// specifically use the `@react-native/babel-preset` installed by this package (ex:
// `babel-preset-expo/node_modules/`). This way the preset will not change unintentionally.
// Reference: https://github.com/expo/expo/pull/4685#discussion_r307143920
const { getPreset } = require('@react-native/babel-preset');
// We need to customize the `@react-native/babel-preset` to ensure that the `@babel/plugin-transform-export-namespace-from`
// plugin is run after the TypeScript plugins. This is normally handled by the combination of standard `@babel/preset-env` and `@babel/preset-typescript` but React Native
// doesn't do that and we can't rely on Hermes spec compliance enough to use standard presets.
const babelPresetReactNativeEnv = getPreset(null, presetOpts);
// Add the `@babel/plugin-transform-export-namespace-from` plugin to the preset but ensure it runs after
// the TypeScript plugins to ensure namespace type exports (TypeScript 5.0+) `export type * as Types from './module';`
// are stripped before the transform. Otherwise the transform will extraneously include the types as syntax.
babelPresetReactNativeEnv.overrides.push({
plugins: [require('./babel-plugin-transform-export-namespace-from')],
});
return babelPresetReactNativeEnv;
})(),
// React support with similar options to Metro.
// We override this logic outside of the metro preset so we can add support for
// React 17 automatic JSX transformations.
// The only known issue is the plugin `@babel/plugin-transform-react-display-name` will be run twice,
// once in the Metro plugin, and another time here.
[
require('@babel/preset-react'),
{
development: isDev,
// Defaults to `automatic`, pass in `classic` to disable auto JSX transformations.
runtime: platformOptions?.jsxRuntime || 'automatic',
...(platformOptions &&
platformOptions.jsxRuntime !== 'classic' && {
importSource: (platformOptions && platformOptions.jsxImportSource) || 'react',
}),
// NOTE: Unexposed props:
// pragma?: string;
// pragmaFrag?: string;
// pure?: string;
// throwIfNamespace?: boolean;
// useBuiltIns?: boolean;
// useSpread?: boolean;
},
],
],
plugins: [
...extraPlugins,
// TODO: Remove
platformOptions.decorators !== false && [
require('@babel/plugin-proposal-decorators'),
platformOptions.decorators ?? { legacy: true },
],
// Automatically add `react-native-reanimated/plugin` when the package is installed.
// TODO: Move to be a customTransformOption.
(0, common_1.hasModule)('react-native-worklets') &&
platformOptions.worklets !== false &&
platformOptions.reanimated !== false
? [require('react-native-worklets/plugin')]
: (0, common_1.hasModule)('react-native-reanimated') &&
platformOptions.reanimated !== false && [require('react-native-reanimated/plugin')],
].filter(Boolean),
};
}
exports.default = babelPresetExpo;
module.exports = babelPresetExpo;

View File

@@ -0,0 +1,2 @@
import type { ConfigAPI, PluginObj } from '@babel/core';
export declare function expoInlineEnvVars(api: ConfigAPI & typeof import('@babel/core')): PluginObj;

View File

@@ -0,0 +1,82 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.expoInlineEnvVars = expoInlineEnvVars;
const common_1 = require("./common");
const debug = require('debug')('expo:babel:env-vars');
function expoInlineEnvVars(api) {
const { types: t } = api;
const isProduction = api.caller(common_1.getIsProd);
function isProcessEnv(path) {
const { object } = path.node;
if (!t.isMemberExpression(object) ||
!t.isIdentifier(object.object) ||
object.object.name !== 'process') {
return false;
}
const { property } = object;
if (t.isIdentifier(property)) {
return property.name === 'env';
}
else if (t.isStringLiteral(property)) {
return property.value === 'env';
}
else {
return false;
}
}
function toMemberProperty(path) {
const { property } = path.node;
if (t.isStringLiteral(property)) {
return property.value;
}
else if (t.isIdentifier(property)) {
return property.name;
}
else {
return undefined;
}
}
/** If the `path.node` being assigned to (`left = right`) */
function isAssignment(path) {
return t.isAssignmentExpression(path.parent) && path.parent.left === path.node;
}
function memberExpressionVisitor(path, state) {
if (!isProcessEnv(path) || isAssignment(path))
return;
const key = toMemberProperty(path);
if (key != null && key.startsWith('EXPO_PUBLIC_')) {
debug(`${isProduction ? 'Inlining' : 'Referencing'} environment variable in %s: %s`, state.filename, key);
publicEnvVars.add(key);
if (isProduction) {
path.replaceWith(t.valueToNode(process.env[key]));
}
else {
path.replaceWith(t.memberExpression(addEnvImport(), t.identifier(key)));
}
}
}
let addEnvImport;
const publicEnvVars = new Set();
return {
name: 'expo-inline-or-reference-env-vars',
pre(file) {
const addNamedImportOnce = (0, common_1.createAddNamedImportOnce)(t);
addEnvImport = () => {
return addNamedImportOnce(file.path, 'env', 'expo/virtual/env');
};
},
visitor: {
OptionalMemberExpression: memberExpressionVisitor,
MemberExpression: memberExpressionVisitor,
},
post(file) {
assertExpoMetadata(file.metadata);
file.metadata.publicEnvVars = Array.from(publicEnvVars);
},
};
}
function assertExpoMetadata(metadata) {
if (!metadata || typeof metadata !== 'object') {
throw new Error('Expected Babel state.file.metadata to be an object');
}
}

View File

@@ -0,0 +1,2 @@
/** These Expo packages may have side-effects and should not be lazily initialized. */
export declare const lazyImports: Set<string>;

5
node_modules/babel-preset-expo/build/lazyImports.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.lazyImports = void 0;
/** These Expo packages may have side-effects and should not be lazily initialized. */
exports.lazyImports = new Set(['expo', 'expo-asset', 'expo-task-manager']);

View File

@@ -0,0 +1,9 @@
/**
* Copyright © 2024 650 Industries.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { ConfigAPI, PluginObj } from '@babel/core';
export default function minifyPlatformSelectPlugin({ types: t, }: ConfigAPI & typeof import('@babel/core')): PluginObj;

View File

@@ -0,0 +1,72 @@
"use strict";
/**
* Copyright © 2024 650 Industries.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = minifyPlatformSelectPlugin;
function minifyPlatformSelectPlugin({ types: t, }) {
function isPlatformSelect(path) {
return (t.isMemberExpression(path.node.callee) &&
t.isIdentifier(path.node.callee.object) &&
t.isIdentifier(path.node.callee.property) &&
path.node.callee.object.name === 'Platform' &&
path.node.callee.property.name === 'select' &&
t.isObjectExpression(path.node.arguments[0]));
}
function findProperty(objectExpression, key, fallback) {
let value = null;
for (const p of objectExpression.properties) {
if (!t.isObjectProperty(p) && !t.isObjectMethod(p)) {
continue;
}
if ((t.isIdentifier(p.key) && p.key.name === key) ||
(t.isStringLiteral(p.key) && p.key.value === key)) {
if (t.isObjectProperty(p)) {
value = p.value;
break;
}
else if (t.isObjectMethod(p)) {
value = t.toExpression(p);
break;
}
}
}
return value ?? fallback();
}
function hasStaticProperties(objectExpression) {
return objectExpression.properties.every((p) => {
if (('computed' in p && p.computed) || t.isSpreadElement(p)) {
return false;
}
if (t.isObjectMethod(p) && p.kind !== 'method') {
return false;
}
return t.isIdentifier(p.key) || t.isStringLiteral(p.key);
});
}
return {
visitor: {
CallExpression(path, state) {
const node = path.node;
const arg = node.arguments[0];
const opts = state.opts;
if (isPlatformSelect(path) && t.isObjectExpression(arg)) {
if (hasStaticProperties(arg)) {
let fallback;
if (opts.platform === 'web') {
fallback = () => findProperty(arg, 'default', () => t.identifier('undefined'));
}
else {
fallback = () => findProperty(arg, 'native', () => findProperty(arg, 'default', () => t.identifier('undefined')));
}
path.replaceWith(findProperty(arg, opts.platform, fallback));
}
}
},
},
};
}

View File

@@ -0,0 +1,5 @@
/**
* Copyright © 2024 650 Industries.
*/
import type { ConfigAPI, PluginObj } from '@babel/core';
export declare function environmentRestrictedReactAPIsPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj;

View File

@@ -0,0 +1,115 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.environmentRestrictedReactAPIsPlugin = environmentRestrictedReactAPIsPlugin;
const INVALID_SERVER_REACT_DOM_APIS = [
'findDOMNode',
'flushSync',
'unstable_batchedUpdates',
'useFormStatus',
'useFormState',
];
// 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
const INVALID_SERVER_REACT_APIS = [
'Component',
'createContext',
'createFactory',
'PureComponent',
'useDeferredValue',
'useEffect',
'useImperativeHandle',
'useInsertionEffect',
'useLayoutEffect',
'useReducer',
'useRef',
'useState',
'useSyncExternalStore',
'useTransition',
'useOptimistic',
];
function isNodeModule(path) {
return path != null && /[\\/]node_modules[\\/]/.test(path);
}
// Restricts imports from `react` and `react-dom` when using React Server Components.
const FORBIDDEN_IMPORTS = {
react: INVALID_SERVER_REACT_APIS,
'react-dom': INVALID_SERVER_REACT_DOM_APIS,
};
function environmentRestrictedReactAPIsPlugin(api) {
const { types: t } = api;
return {
name: 'expo-environment-restricted-react-api-plugin',
visitor: {
ImportDeclaration(path, state) {
// Skip node_modules
if (isNodeModule(state.file.opts.filename)) {
return;
}
const sourceValue = path.node.source.value;
const forbiddenList = FORBIDDEN_IMPORTS[sourceValue];
if (forbiddenList) {
path.node.specifiers.forEach((specifier) => {
if (t.isImportSpecifier(specifier)) {
const importName = t.isStringLiteral(specifier.imported)
? specifier.imported.value
: specifier.imported.name;
// Check for both named and namespace imports
const isForbidden = forbiddenList.includes(importName);
if (isForbidden) {
if (['Component', 'PureComponent'].includes(importName)) {
// Add special handling for `Component` since it is different to a function API.
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.`);
}
else {
const forbiddenImports = path.scope.getData('forbiddenImports') ?? new Map();
if (!forbiddenImports.has(sourceValue))
forbiddenImports.set(sourceValue, new Set());
forbiddenImports.get(sourceValue).add(importName);
path.scope.setData('forbiddenImports', forbiddenImports);
}
}
}
else {
const importName = t.isStringLiteral(specifier.local)
? specifier.local
: specifier.local.name;
// Save namespace import for later checks in MemberExpression
path.scope.setData('importedNamespace', { [importName]: sourceValue });
}
});
}
},
// Match against `var _useState = useState(0),`
VariableDeclarator(path) {
const importedSpecifiers = path.scope.getData('forbiddenImports');
if (!importedSpecifiers)
return;
importedSpecifiers.forEach((forbiddenApis, importName) => {
if (t.isCallExpression(path.node.init) && t.isIdentifier(path.node.init.callee)) {
if (forbiddenApis.has(path.node.init.callee.name)) {
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.`);
}
}
});
},
MemberExpression(path) {
const importedNamespaces = path.scope.getData('importedNamespace') || {};
Object.keys(importedNamespaces).forEach((namespace) => {
const library = importedNamespaces[namespace];
const forbiddenList = FORBIDDEN_IMPORTS[library];
const objectName = t.isIdentifier(path.node.object) ? path.node.object.name : null;
if (objectName === namespace &&
forbiddenList &&
t.isIdentifier(path.node.property) &&
forbiddenList.includes(path.node.property.name)) {
// Throw a special error for class components since it's not always clear why they cannot be used in RSC.
// e.g. https://x.com/Baconbrix/status/1749223042440392806?s=20
if (path.node.property.name === 'Component') {
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.`);
}
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.`);
}
});
},
},
};
}

View File

@@ -0,0 +1,11 @@
/**
* Copyright © 2024 650 Industries.
* Copyright © 2024 2023 lubieowoce
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* https://github.com/lubieowoce/tangle/blob/5229666fb317d0da9363363fc46dc542ba51e4f7/packages/babel-rsc/src/babel-rsc-actions.ts#L1C1-L909C25
*/
import type { ConfigAPI, PluginObj, PluginPass } from '@babel/core';
export declare function reactServerActionsPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj<PluginPass>;

View File

@@ -0,0 +1,602 @@
"use strict";
/**
* Copyright © 2024 650 Industries.
* Copyright © 2024 2023 lubieowoce
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* https://github.com/lubieowoce/tangle/blob/5229666fb317d0da9363363fc46dc542ba51e4f7/packages/babel-rsc/src/babel-rsc-actions.ts#L1C1-L909C25
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.reactServerActionsPlugin = reactServerActionsPlugin;
const node_path_1 = require("node:path");
const node_url_1 = __importDefault(require("node:url"));
const common_1 = require("./common");
const debug = require('debug')('expo:babel:server-actions');
const LAZY_WRAPPER_VALUE_KEY = 'value';
function reactServerActionsPlugin(api) {
const { types: t } = api;
// React doesn't like non-enumerable properties on serialized objects (see `isSimpleObject`),
// so we have to use closure scope for the cache (instead of a non-enumerable `this._cache`)
const _buildLazyWrapperHelper = api.template(`(thunk) => {
let cache;
return {
get ${LAZY_WRAPPER_VALUE_KEY}() {
return cache || (cache = thunk());
}
}
}`);
const buildLazyWrapperHelper = () => {
return _buildLazyWrapperHelper().expression;
};
const possibleProjectRoot = api.caller(common_1.getPossibleProjectRoot);
let addReactImport;
let wrapBoundArgs;
let getActionModuleId;
const extractInlineActionToTopLevel = (path, _state, { body, freeVariables, }) => {
const actionModuleId = getActionModuleId();
const moduleScope = path.scope.getProgramParent();
const extractedIdentifier = moduleScope.generateUidIdentifier('$$INLINE_ACTION');
let extractedFunctionParams = [...path.node.params];
let extractedFunctionBody = body.body;
if (freeVariables.length > 0) {
// only add a closure object if we're not closing over anything.
// const [x, y, z] = await _decryptActionBoundArgs(await $$CLOSURE.value);
const closureParam = path.scope.generateUidIdentifier('$$CLOSURE');
const freeVarsPat = t.arrayPattern(freeVariables.map((variable) => t.identifier(variable)));
const closureExpr = t.memberExpression(closureParam, t.identifier(LAZY_WRAPPER_VALUE_KEY));
extractedFunctionParams = [closureParam, ...path.node.params];
extractedFunctionBody = [
t.variableDeclaration('var', [
t.variableDeclarator(t.assignmentPattern(freeVarsPat, closureExpr)),
]),
...extractedFunctionBody,
];
}
const wrapInRegister = (expr, exportedName) => {
const expoRegisterServerReferenceId = addReactImport();
return t.callExpression(expoRegisterServerReferenceId, [
expr,
t.stringLiteral(actionModuleId),
t.stringLiteral(exportedName),
]);
};
const isArrowFn = path.isArrowFunctionExpression();
const extractedFunctionExpr = wrapInRegister(isArrowFn
? t.arrowFunctionExpression(extractedFunctionParams, t.blockStatement(extractedFunctionBody), true)
: t.functionExpression(path.node.id, extractedFunctionParams, t.blockStatement(extractedFunctionBody), false, true), extractedIdentifier.name);
// Create a top-level declaration for the extracted function.
const bindingKind = 'var';
const functionDeclaration = t.exportNamedDeclaration(t.variableDeclaration(bindingKind, [
t.variableDeclarator(extractedIdentifier, extractedFunctionExpr),
]));
// Insert the declaration as close to the original declaration as possible.
const isPathFunctionInTopLevel = path.find((p) => p.isProgram()) === path;
const decl = isPathFunctionInTopLevel ? path : findImmediatelyEnclosingDeclaration(path);
let inserted;
const canInsertExportNextToPath = (decl) => {
if (!decl) {
return false;
}
if (decl.parentPath?.isProgram()) {
return true;
}
return false;
};
const findNearestPathThatSupportsInsertBefore = (decl) => {
let current = decl;
// Check if current scope is suitable for `export` insertion
while (current && !current.isProgram()) {
if (canInsertExportNextToPath(current)) {
return current;
}
const parentPath = current.parentPath;
if (!parentPath) {
return null;
}
current = parentPath;
}
if (current.isFunction()) {
// Don't insert exports inside functions
return null;
}
return current;
};
const topLevelDecl = decl ? findNearestPathThatSupportsInsertBefore(decl) : null;
if (topLevelDecl) {
// If it's a variable declaration, insert before its parent statement to avoid syntax errors
const targetPath = topLevelDecl.isVariableDeclarator()
? topLevelDecl.parentPath
: topLevelDecl;
[inserted] = targetPath.insertBefore(functionDeclaration);
moduleScope.registerBinding(bindingKind, inserted);
inserted.addComment('leading', ' hoisted action: ' + (getFnPathName(path) ?? '<anonymous>'), true);
}
else {
// Fallback to inserting after the last import if no enclosing declaration is found
const programBody = moduleScope.path.get('body');
const lastImportPath = (Array.isArray(programBody) ? programBody : [programBody]).findLast((statement) => {
return statement.isImportDeclaration();
});
[inserted] = lastImportPath.insertAfter(functionDeclaration);
moduleScope.registerBinding(bindingKind, inserted);
inserted.addComment('leading', ' hoisted action: ' + (getFnPathName(path) ?? '<anonymous>'), true);
}
return {
inserted,
extractedIdentifier,
getReplacement: () => getInlineActionReplacement({
id: extractedIdentifier,
freeVariables,
}),
};
};
const getInlineActionReplacement = ({ id, freeVariables, }) => {
if (freeVariables.length === 0) {
return id;
}
const capturedVarsExpr = t.arrayExpression(freeVariables.map((variable) => t.identifier(variable)));
const boundArgs = wrapBoundArgs(capturedVarsExpr);
// _ACTION.bind(null, { get value() { return _encryptActionBoundArgs([x, y, z]) } })
return t.callExpression(t.memberExpression(id, t.identifier('bind')), [
t.nullLiteral(),
boundArgs,
]);
};
function hasUseServerDirective(path) {
const { body } = path.node;
return t.isBlockStatement(body) && body.directives.some((d) => d.value.value === 'use server');
}
return {
name: 'expo-server-actions',
pre(file) {
const projectRoot = possibleProjectRoot || file.opts.root || '';
if (!file.code.includes('use server')) {
file.path.skip();
return;
}
assertExpoMetadata(file.metadata);
file.metadata.extractedActions = [];
file.metadata.isModuleMarkedWithUseServerDirective = false;
const addNamedImportOnce = (0, common_1.createAddNamedImportOnce)(t);
addReactImport = () => {
return addNamedImportOnce(file.path, 'registerServerReference', 'react-server-dom-webpack/server');
};
getActionModuleId = once(() => {
// Create relative file path hash.
return './' + (0, common_1.toPosixPath)((0, node_path_1.relative)(projectRoot, file.opts.filename));
});
const defineBoundArgsWrapperHelper = once(() => {
const id = this.file.path.scope.generateUidIdentifier('wrapBoundArgs');
this.file.path.scope.push({
id,
kind: 'var',
init: buildLazyWrapperHelper(),
});
return id;
});
wrapBoundArgs = (expr) => {
const wrapperFn = t.cloneNode(defineBoundArgsWrapperHelper());
return t.callExpression(wrapperFn, [t.arrowFunctionExpression([], expr)]);
};
},
visitor: {
Program(path, state) {
if (path.node.directives.some((d) => d.value.value === 'use server')) {
assertExpoMetadata(state.file.metadata);
state.file.metadata.isModuleMarkedWithUseServerDirective = true;
// remove the directive so that downstream consumers don't transform the module again.
path.node.directives = path.node.directives.filter((d) => d.value.value !== 'use server');
}
},
// `() => {}`
ArrowFunctionExpression(path, state) {
const { body } = path.node;
if (!t.isBlockStatement(body) || !hasUseServerDirective(path)) {
return;
}
assertIsAsyncFn(path);
const freeVariables = getFreeVariables(path);
const tlb = getTopLevelBinding(path);
const { extractedIdentifier, getReplacement } = extractInlineActionToTopLevel(path, state, {
freeVariables,
body,
});
path.replaceWith(getReplacement());
assertExpoMetadata(state.file.metadata);
state.file.metadata.extractedActions.push({
localName: tlb?.identifier.name,
exportedName: extractedIdentifier.name,
});
},
// `function foo() { ... }`
FunctionDeclaration(path, state) {
if (!hasUseServerDirective(path)) {
return;
}
assertIsAsyncFn(path);
const fnId = path.node.id;
if (!fnId) {
throw path.buildCodeFrameError('Internal error: expected FunctionDeclaration to have a name');
}
const freeVariables = getFreeVariables(path);
const { extractedIdentifier, getReplacement } = extractInlineActionToTopLevel(path, state, {
freeVariables,
body: path.node.body,
});
const tlb = getTopLevelBinding(path);
if (tlb) {
// we're at the top level, and we might be enclosed within a `export` decl.
// we have to keep the export in place, because it might be used elsewhere,
// so we can't just remove this node.
// replace the function decl with a (hopefully) equivalent var declaration
// `var [name] = $$INLINE_ACTION_{N}`
const bindingKind = 'var';
const [inserted] = path.replaceWith(t.variableDeclaration(bindingKind, [t.variableDeclarator(fnId, extractedIdentifier)]));
tlb.scope.registerBinding(bindingKind, inserted);
}
else {
// note: if we do this *after* adding the new declaration, the bindings get messed up
path.remove();
// add a declaration in the place where the function decl would be hoisted to.
// (this avoids issues with functions defined after `return`, see `test-cases/named-after-return.jsx`)
path.scope.push({
id: fnId,
init: getReplacement(),
kind: 'var',
unique: true,
});
}
assertExpoMetadata(state.file.metadata);
state.file.metadata.extractedActions.push({
localName: tlb?.identifier.name,
exportedName: extractedIdentifier.name,
});
},
// `const foo = function() { ... }`
FunctionExpression(path, state) {
if (!hasUseServerDirective(path)) {
return;
}
assertIsAsyncFn(path);
const { body } = path.node;
const freeVariables = getFreeVariables(path);
// TODO: look for usages of the name (if present), that's technically possible
// const fnId = path.node.id;
const { extractedIdentifier, getReplacement } = extractInlineActionToTopLevel(path, state, {
freeVariables,
body,
});
const tlb = getTopLevelBinding(path);
assertExpoMetadata(state.file.metadata);
path.replaceWith(getReplacement());
state.file.metadata.extractedActions.push({
localName: tlb?.identifier.name,
exportedName: extractedIdentifier.name,
});
},
// Top-level "use server"
ExportDefaultDeclaration(path, state) {
assertExpoMetadata(state.file.metadata);
if (!state.file.metadata.isModuleMarkedWithUseServerDirective) {
return;
}
// Convert `export default function foo() {}` to `function foo() {}; export { foo as default }`
if (path.node.declaration) {
if (t.isFunctionDeclaration(path.node.declaration)) {
let { id } = path.node.declaration;
if (id == null) {
const moduleScope = path.scope.getProgramParent();
const extractedIdentifier = moduleScope.generateUidIdentifier('$$INLINE_ACTION');
id = extractedIdentifier;
// Transform `async function () {}` to `async function $$INLINE_ACTION() {}`
path.node.declaration.id = extractedIdentifier;
}
const exportedSpecifier = t.exportSpecifier(id, t.identifier('default'));
path.replaceWith(path.node.declaration);
path.insertAfter(t.exportNamedDeclaration(null, [exportedSpecifier]));
}
else {
// Convert anonymous function expressions to named function expressions and export them as default.
// export default foo = async () => {}
// vvv
// const foo = async () => {}
// (() => _registerServerReference(foo, "file:///unknown", "default"))();
// export { foo as default };
if (t.isAssignmentExpression(path.node.declaration) &&
t.isArrowFunctionExpression(path.node.declaration.right)) {
if (!t.isIdentifier(path.node.declaration.left)) {
throw path.buildCodeFrameError(`Expected an assignment to an identifier but found ${path.node.declaration.left.type}.`);
}
const { left, right } = path.node.declaration;
const id = left;
const exportedSpecifier = t.exportSpecifier(id, t.identifier('default'));
// Replace `export default foo = async () => {}` with `const foo = async () => {}`
path.replaceWith(t.variableDeclaration('var', [t.variableDeclarator(id, right)]));
// Insert `(() => _registerServerReference(foo, "file:///unknown", "default"))();`
path.insertAfter(t.exportNamedDeclaration(null, [exportedSpecifier]));
}
else if (t.isArrowFunctionExpression(path.node.declaration) &&
path.node.declaration) {
// export default async () => {}
// Give the function a name
// const $$INLINE_ACTION = async () => {}
const moduleScope = path.scope.getProgramParent();
const extractedIdentifier = moduleScope.generateUidIdentifier('$$INLINE_ACTION');
// @ts-expect-error: Transform `export default async () => {}` to `const $$INLINE_ACTION = async () => {}`
path.node.declaration = t.variableDeclaration('var', [
t.variableDeclarator(extractedIdentifier, path.node.declaration),
]);
// Strip the `export default`
path.replaceWith(path.node.declaration);
// export { $$INLINE_ACTION as default }
const exportedSpecifier = t.exportSpecifier(extractedIdentifier, t.identifier('default'));
path.insertAfter(t.exportNamedDeclaration(null, [exportedSpecifier]));
}
else if (
// Match `export default foo;`
t.isIdentifier(path.node.declaration)) {
// Ensure the `path.node.declaration` is a function or a variable for a function.
const binding = path.scope.getBinding(path.node.declaration.name);
const isServerActionType = t.isFunctionDeclaration(binding?.path.node ?? path.node.declaration) ||
t.isArrowFunctionExpression(binding?.path.node ?? path.node.declaration) ||
// `const foo = async () => {}`
(t.isVariableDeclarator(binding?.path.node) &&
t.isArrowFunctionExpression(binding?.path.node.init));
if (isServerActionType) {
// Convert `export default foo;` to `export { foo as default };`
const exportedSpecifier = t.exportSpecifier(path.node.declaration, t.identifier('default'));
path.replaceWith(t.exportNamedDeclaration(null, [exportedSpecifier]));
}
}
else {
// Unclear when this happens.
throw path.buildCodeFrameError(`Cannot create server action. Expected a assignment expression but found ${path.node.declaration.type}.`);
}
}
}
else {
// TODO: Unclear when this happens.
throw path.buildCodeFrameError(`Not implemented: 'export default' declarations in "use server" files. Try using 'export { name as default }' instead.`);
}
},
ExportNamedDeclaration(path, state) {
assertExpoMetadata(state.file.metadata);
if (!state.file.metadata.isModuleMarkedWithUseServerDirective) {
return;
}
// Skip type-only exports (`export type { Foo } from '...'` or `export { type Foo }`)
if (path.node.exportKind === 'type') {
return;
}
// This can happen with `export {};` and TypeScript types.
if (!path.node.declaration && !path.node.specifiers.length) {
return;
}
const actionModuleId = getActionModuleId();
const createRegisterCall = (identifier, exported = identifier) => {
const exportedName = t.isIdentifier(exported) ? exported.name : exported.value;
const call = t.callExpression(addReactImport(), [
identifier,
t.stringLiteral(actionModuleId),
t.stringLiteral(exportedName),
]);
// Wrap call with `;(() => { ... })();` to avoid issues with ASI
return t.expressionStatement(t.callExpression(t.arrowFunctionExpression([], call), []));
};
if (path.node.specifiers.length > 0) {
for (const specifier of path.node.specifiers) {
// `export * as ns from './foo';`
if (t.isExportNamespaceSpecifier(specifier)) {
throw path.buildCodeFrameError('Namespace exports for server actions are not supported. Re-export named actions instead: export { foo } from "./bar".');
}
else if (t.isExportDefaultSpecifier(specifier)) {
// NOTE: This is handled by ExportDefaultDeclaration
// `export default foo;`
throw path.buildCodeFrameError('Internal error while extracting server actions. Expected `export default variable;` to be extracted. (ExportDefaultSpecifier in ExportNamedDeclaration)');
}
else if (t.isExportSpecifier(specifier)) {
// Skip TypeScript type re-exports (e.g., `export { type Foo }`)
if (specifier.exportKind === 'type') {
continue;
}
// `export { foo };`
// `export { foo as [bar|default] };`
const localName = specifier.local.name;
const exportedName = t.isIdentifier(specifier.exported)
? specifier.exported.name
: specifier.exported.value;
// if we're reexporting an existing action under a new name, we shouldn't register() it again.
if (!state.file.metadata.extractedActions.some((info) => info.localName === localName)) {
// referencing the function's local identifier here *should* be safe (w.r.t. TDZ) because
// 1. if it's a `export async function foo() {}`, the declaration will be hoisted,
// so it's safe to reference no matter how the declarations are ordered
// 2. if it's an `export const foo = async () => {}`, then the standalone `export { foo }`
// has to follow the definition, so we can reference it right before the export decl as well
path.insertBefore(createRegisterCall(specifier.local, specifier.exported));
}
state.file.metadata.extractedActions.push({ localName, exportedName });
}
}
return;
}
if (!path.node.declaration) {
throw path.buildCodeFrameError(`Internal error: Unexpected 'ExportNamedDeclaration' without declarations`);
}
const identifiers = (() => {
const innerPath = path.get('declaration');
if (innerPath.isVariableDeclaration()) {
return innerPath.get('declarations').map((d) => {
// TODO: insert `typeof <identifier> === 'function'` check -- it's a variable, so it could be anything
const id = d.node.id;
if (!t.isIdentifier(id)) {
// TODO
throw innerPath.buildCodeFrameError('Unimplemented');
}
return id;
});
}
else if (innerPath.isFunctionDeclaration()) {
if (!innerPath.get('async')) {
throw innerPath.buildCodeFrameError(`Functions exported from "use server" files must be async.`);
}
return [innerPath.get('id').node];
}
else if (
// TypeScript type exports
innerPath.isTypeAlias() ||
innerPath.isTSDeclareFunction() ||
innerPath.isTSInterfaceDeclaration() ||
innerPath.isTSTypeAliasDeclaration()) {
return [];
}
else {
throw innerPath.buildCodeFrameError(`Unimplemented server action export`);
}
})();
path.insertAfter(identifiers.map((identifier) => createRegisterCall(identifier)));
for (const identifier of identifiers) {
state.file.metadata.extractedActions.push({
localName: identifier.name,
exportedName: identifier.name,
});
}
},
},
post(file) {
assertExpoMetadata(file.metadata);
if (!file.metadata.extractedActions?.length) {
return;
}
debug('extracted actions', file.metadata.extractedActions);
const payload = {
id: getActionModuleId(),
names: file.metadata.extractedActions.map((e) => e.exportedName),
};
const stashedData = 'rsc/actions: ' + JSON.stringify(payload);
// Add comment for debugging the bundle, we use the babel metadata for accessing the data.
file.path.addComment('leading', stashedData);
const filePath = file.opts.filename;
if (!filePath) {
// This can happen in tests or systems that use Babel standalone.
throw new Error('[Babel] Expected a filename to be set in the state');
}
const outputKey = node_url_1.default.pathToFileURL(filePath).href;
file.metadata.reactServerActions = payload;
file.metadata.reactServerReference = outputKey;
},
};
}
const getFreeVariables = (path) => {
const freeVariablesSet = new Set();
const programScope = path.scope.getProgramParent();
path.traverse({
Identifier(innerPath) {
const { name } = innerPath.node;
if (!innerPath.isReferencedIdentifier()) {
debug('skipping - not referenced');
return;
}
if (freeVariablesSet.has(name)) {
// we've already determined this name to be a free var. no point in recomputing.
debug('skipping - already registered');
return;
}
const binding = innerPath.scope.getBinding(name);
if (!binding) {
// probably a global, or an unbound variable. ignore it.
debug('skipping - global or unbound, skipping');
return;
}
if (binding.scope === programScope) {
// module-level declaration. no need to close over it.
debug('skipping - module-level binding');
return;
}
if (
// function args or a var at the top-level of its body
binding.scope === path.scope ||
// decls from blocks within the function
isChildScope({
parent: path.scope,
child: binding.scope,
root: programScope,
})) {
// the binding came from within the function = it's not closed-over, so don't add it.
debug('skipping - declared within function');
return;
}
// we've (hopefully) eliminated all the other cases, so we should treat this as a free var.
debug('adding');
freeVariablesSet.add(name);
},
});
return [...freeVariablesSet].sort();
};
const getFnPathName = (path) => {
return path.isArrowFunctionExpression() ? undefined : path.node?.id?.name;
};
const isChildScope = ({ root, parent, child, }) => {
let curScope = child;
while (curScope !== root) {
if (curScope.parent === parent) {
return true;
}
curScope = curScope.parent;
}
return false;
};
function findImmediatelyEnclosingDeclaration(path) {
let currentPath = path;
while (!currentPath.isProgram()) {
if (
// const foo = async () => { ... }
// ^^^^^^^^^^^^^^^^^^^^^^^^^
currentPath.isVariableDeclarator() ||
// async function foo() { ... }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
currentPath.isDeclaration()) {
return currentPath;
}
// if we encounter an expression on the way, this isn't a top level decl, and needs to be hoisted.
// e.g. `export const foo = withAuth(async () => { ... })`
if (currentPath !== path && currentPath.isExpression()) {
return null;
}
if (!currentPath.parentPath) {
return null;
}
currentPath = currentPath.parentPath;
}
return null;
}
const getTopLevelBinding = (path) => {
const decl = findImmediatelyEnclosingDeclaration(path);
if (!decl || !('id' in decl.node) || !decl.node.id || !('name' in decl.node.id))
return null;
const declBinding = decl.scope.getBinding(decl.node.id.name);
return declBinding.scope === path.scope.getProgramParent() ? declBinding : null;
};
const assertIsAsyncFn = (path) => {
if (!path.node.async) {
throw path.buildCodeFrameError(`functions marked with "use server" must be async`);
}
};
const once = (fn) => {
let cache = { has: false };
return () => {
if (cache.has)
return cache.value;
cache = { has: true, value: fn() };
return cache.value;
};
};
function assertExpoMetadata(metadata) {
if (!metadata || typeof metadata !== 'object') {
throw new Error('Expected Babel state.file.metadata to be an object');
}
}

View File

@@ -0,0 +1,8 @@
/**
* Copyright © 2025 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { ConfigAPI, PluginObj } from '@babel/core';
export declare function serverDataLoadersPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj;

View File

@@ -0,0 +1,147 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serverDataLoadersPlugin = serverDataLoadersPlugin;
const common_1 = require("./common");
const debug = require('debug')('expo:babel:server-data-loaders');
const LOADER_EXPORT_NAME = 'loader';
function serverDataLoadersPlugin(api) {
const { types: t } = api;
const routerAbsoluteRoot = api.caller(common_1.getExpoRouterAbsoluteAppRoot);
const isLoaderBundle = api.caller(common_1.getIsLoaderBundle);
return {
name: 'expo-server-data-loaders',
visitor: {
ExportDefaultDeclaration(path, state) {
// Early exit if file is not within the `app/` directory
if (!isInAppDirectory(state.file.opts.filename ?? '', routerAbsoluteRoot)) {
return;
}
// Only remove default exports in loader-only bundles
if (!isLoaderBundle) {
return;
}
debug('Loader bundle: removing default export from', state.file.opts.filename);
markForConstantFolding(state);
path.remove();
},
ExportNamedDeclaration(path, state) {
// Early exit if file is not within the `app/` directory
if (!isInAppDirectory(state.file.opts.filename ?? '', routerAbsoluteRoot)) {
debug('Skipping file outside app directory:', state.file.opts.filename);
return;
}
debug(`Processing ${isLoaderBundle ? 'loader' : 'client'} bundle:`, state.file.opts.filename);
const { declaration, specifiers } = path.node;
// Is this a type export like `export type Foo`?
const isTypeExport = path.node.exportKind === 'type';
// NOTE(@hassankhan): We should add proper handling for specifiers too
const hasSpecifiers = specifiers.length > 0;
if (isTypeExport || hasSpecifiers) {
return;
}
// Handles `export function loader() { ... }`
if (t.isFunctionDeclaration(declaration)) {
const name = declaration.id?.name;
if (name && isLoaderIdentifier(name)) {
// Mark the file as having a loader (for all bundle types)
markWithLoaderReference(state);
if (!isLoaderBundle) {
// Client bundles: remove loader
debug('Found and removed loader function declaration');
markForConstantFolding(state);
path.remove();
}
// Loader bundle: keep the loader
}
else if (name && isLoaderBundle) {
// Loader bundle: remove non-loader function declarations
debug('Loader bundle: removing non-loader function declaration:', name);
markForConstantFolding(state);
path.remove();
}
}
// Handles `export const loader = ...`
if (t.isVariableDeclaration(declaration)) {
let hasModified = false;
// Check if any declaration is a loader
const hasLoaderDeclaration = declaration.declarations.some((declarator) => {
const name = t.isIdentifier(declarator.id) ? declarator.id.name : null;
return name && isLoaderIdentifier(name);
});
// Mark the file as having a loader (for all bundle types)
if (hasLoaderDeclaration) {
markWithLoaderReference(state);
}
if (isLoaderBundle) {
// Loader bundle: keep only loader declarations, remove others
declaration.declarations = declaration.declarations.filter((declarator) => {
const name = t.isIdentifier(declarator.id) ? declarator.id.name : null;
if (name && !isLoaderIdentifier(name)) {
debug('Loader bundle: removing non-loader variable declaration:', name);
hasModified = true;
return false;
}
return true;
});
}
else {
// Client bundles: remove loader declarations
declaration.declarations = declaration.declarations.filter((declarator) => {
const name = t.isIdentifier(declarator.id) ? declarator.id.name : null;
if (name && isLoaderIdentifier(name)) {
debug('Found and removed loader variable declaration');
hasModified = true;
return false;
}
return true;
});
}
if (hasModified) {
markForConstantFolding(state);
// If all declarations were removed, remove the export
if (declaration.declarations.length === 0) {
path.remove();
}
}
}
},
},
};
}
/**
* Checks if identifier name is `loader`
*/
function isLoaderIdentifier(name) {
return name === LOADER_EXPORT_NAME;
}
function assertExpoMetadata(metadata) {
if (metadata && typeof metadata === 'object') {
return;
}
throw new Error('Expected Babel state.file.metadata to be an object');
}
/**
* Check if file is within the `app/` directory
*/
function isInAppDirectory(filePath, routerRoot) {
const normalizedFilePath = (0, common_1.toPosixPath)(filePath);
const normalizedAppRoot = (0, common_1.toPosixPath)(routerRoot);
return normalizedFilePath.startsWith(normalizedAppRoot + '/');
}
/**
* Marks a file for Metro's constant folding. This will work for both development and production bundles.
*
* @see packages/@expo/metro-config/src/transform-worker/metro-transform-worker.ts#transformJS
*/
function markForConstantFolding(state) {
assertExpoMetadata(state.file.metadata);
state.file.metadata.performConstantFolding = true;
}
/**
* Sets the `loaderReference` metadata to the file path. This is used to collect all modules with
* loaders in the Metro serializer.
*/
function markWithLoaderReference(state) {
assertExpoMetadata(state.file.metadata);
state.file.metadata.loaderReference = state.file.opts.filename ?? undefined;
}

View File

@@ -0,0 +1,5 @@
/**
* Copyright © 2024 650 Industries.
*/
import type { ConfigAPI, PluginObj } from '@babel/core';
export declare function expoUseDomDirectivePlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj;

View File

@@ -0,0 +1,128 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.expoUseDomDirectivePlugin = expoUseDomDirectivePlugin;
const node_crypto_1 = __importDefault(require("node:crypto"));
const node_path_1 = require("node:path");
const node_url_1 = __importDefault(require("node:url"));
const common_1 = require("./common");
function expoUseDomDirectivePlugin(api) {
const { template, types: t } = api;
const isProduction = api.caller(common_1.getIsProd);
const platform = api.caller((caller) => caller?.platform);
const projectRoot = api.caller(common_1.getPossibleProjectRoot);
return {
name: 'expo-use-dom-directive',
visitor: {
Program(path, state) {
// Native only feature.
if (platform === 'web') {
return;
}
const hasUseDomDirective = path.node.directives.some((directive) => directive.value.value === 'use dom');
const filePath = state.file.opts.filename;
if (!filePath) {
// This can happen in tests or systems that use Babel standalone.
throw new Error('[Babel] Expected a filename to be set in the state');
}
// File starts with "use dom" directive.
if (!hasUseDomDirective) {
// Do nothing for code that isn't marked as a dom component.
return;
}
let displayName = 'Component';
// Assert that a default export must exist and that no other exports should be present.
// NOTE: In the future we could support other exports with extraction.
let hasDefaultExport = false;
// Collect all of the exports
path.traverse({
ExportNamedDeclaration(path) {
const declaration = path.node.declaration;
if (t.isTypeAlias(declaration) ||
t.isInterfaceDeclaration(declaration) ||
t.isTSTypeAliasDeclaration(declaration) ||
t.isTSInterfaceDeclaration(declaration)) {
// Allows type exports
return;
}
throw path.buildCodeFrameError('Modules with the "use dom" directive only support a single default export.');
},
ExportDefaultDeclaration(path) {
hasDefaultExport = true;
if (t.isFunctionDeclaration(path.node.declaration) && path.node.declaration.id) {
displayName = path.node.declaration.id.name;
}
},
});
if (!hasDefaultExport) {
throw path.buildCodeFrameError('The "use dom" directive requires a default export to be present in the file.');
}
// Assert that _layout routes cannot be used in DOM components.
const fileBasename = (0, node_path_1.basename)(filePath);
if (projectRoot &&
// Detecting if the file is in the router root would be extensive as it would cause a more complex
// cache key for each file. Instead, let's just check if the file is in the project root and is not a node_module,
// then we can assert that users should not use `_layout` or `+api` with "use dom".
filePath.includes(projectRoot) &&
!filePath.match(/node_modules/)) {
if (fileBasename.match(/^_layout\.[jt]sx?$/)) {
throw path.buildCodeFrameError('Layout routes cannot be marked as DOM components because they cannot render native views.');
}
else if (
// No API routes
fileBasename.match(/\+api\.[jt]sx?$/)) {
throw path.buildCodeFrameError('API routes cannot be marked as DOM components.');
}
}
const outputKey = node_url_1.default.pathToFileURL(filePath).href;
// Removes all imports using babel API, that will disconnect import bindings from the program.
// plugin-transform-typescript TSX uses the bindings to remove type imports.
// If the DOM component has `import React from 'react';`,
// the plugin-transform-typescript treats it as an typed import and removes it.
// That will further cause undefined `React` error.
path.traverse({
ImportDeclaration(path) {
path.remove();
},
});
path.node.body = [];
path.node.directives = [];
// Create template with declaration first
const proxyModuleTemplate = `
import React from 'react';
import { WebView } from 'expo/dom/internal';
${isProduction
? `const filePath = "${node_crypto_1.default.createHash('md5').update(outputKey).digest('hex')}.html";`
: `const filePath = "${fileBasename}?file=" + ${JSON.stringify(outputKey)};`}
const _Expo_DOMProxyComponent = React.forwardRef((props, ref) => {
return React.createElement(WebView, { ref, ...props, filePath });
});
if (__DEV__) _Expo_DOMProxyComponent.displayName = ${JSON.stringify(`DOM(${displayName})`)};
export default _Expo_DOMProxyComponent;
`;
// Convert template to AST and push to body
const ast = template.ast(proxyModuleTemplate);
const results = path.pushContainer('body', ast);
// Find and register the component declaration
results.forEach((nodePath) => {
if (t.isVariableDeclaration(nodePath.node) &&
'name' in nodePath.node.declarations[0]?.id &&
nodePath.node.declarations[0].id.name === '_Expo_DOMProxyComponent') {
path.scope.registerDeclaration(nodePath);
}
});
assertExpoMetadata(state.file.metadata);
// Save the client reference in the metadata.
state.file.metadata.expoDomComponentReference = outputKey;
},
},
};
}
function assertExpoMetadata(metadata) {
if (metadata && typeof metadata === 'object') {
return;
}
throw new Error('Expected Babel state.file.metadata to be an object');
}

11
node_modules/babel-preset-expo/build/web-preset.d.ts generated vendored Normal file
View File

@@ -0,0 +1,11 @@
/**
* Copyright © 2024 650 Industries.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* A fork of `@react-native/babel-preset` but with everything unrelated to web/ssr removed.
* https://github.com/facebook/react-native/blob/2af1da42ff517232f1309efed7565fe9ddbbac77/packages/react-native-babel-preset/src/configs/main.js#L1
*/
export {};

74
node_modules/babel-preset-expo/build/web-preset.js generated vendored Normal file
View File

@@ -0,0 +1,74 @@
"use strict";
/**
* Copyright © 2024 650 Industries.
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* A fork of `@react-native/babel-preset` but with everything unrelated to web/ssr removed.
* https://github.com/facebook/react-native/blob/2af1da42ff517232f1309efed7565fe9ddbbac77/packages/react-native-babel-preset/src/configs/main.js#L1
*/
Object.defineProperty(exports, "__esModule", { value: true });
const common_1 = require("./common");
// use `this.foo = bar` instead of `this.defineProperty('foo', ...)`
const loose = true;
const defaultPlugins = [
// This is required for parsing React Native with RSC enabled :/
[require('babel-plugin-syntax-hermes-parser'), { parseLangTypes: 'flow' }],
//
[require('babel-plugin-transform-flow-enums')],
[require('@babel/plugin-transform-class-static-block'), { loose }],
[require('@babel/plugin-transform-private-methods'), { loose }],
[require('@babel/plugin-transform-private-property-in-object'), { loose }],
[require('@babel/plugin-syntax-export-default-from')],
[require('./babel-plugin-transform-export-namespace-from')],
];
module.exports = function (babel, options) {
const extraPlugins = [];
// NOTE: We also remove `@react-native/babel-plugin-codegen` since it doesn't seem needed on web.
if (!options || !options.disableImportExportTransform) {
extraPlugins.push([require('@babel/plugin-proposal-export-default-from')], [
require('@babel/plugin-transform-modules-commonjs'),
{
strict: false,
strictMode: false, // prevent "use strict" injections
lazy: options.lazyImportExportTransform,
allowTopLevelThis: true, // dont rewrite global `this` -> `undefined`
},
]);
}
if (!options || options.enableBabelRuntime !== false) {
extraPlugins.push([
require('@babel/plugin-transform-runtime'),
{
helpers: true,
regenerator: false,
enableBabelRuntime: options.enableBabelRuntime == null || options.enableBabelRuntime === true
? (0, common_1.getBabelRuntimeVersion)()
: options.enableBabelRuntime,
},
]);
}
return {
comments: false,
compact: true,
presets: [
// TypeScript support
[require('@babel/preset-typescript'), { allowNamespaces: true }],
],
overrides: [
// the flow strip types plugin must go BEFORE class properties!
// there'll be a test case that fails if you don't.
{
plugins: [require('@babel/plugin-transform-flow-strip-types')],
},
{
plugins: defaultPlugins,
},
{
plugins: extraPlugins,
},
],
};
};

View File

@@ -0,0 +1,15 @@
/**
* Copyright © 2026 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* Babel plugin that transforms widget component JSX expressions.
*/
import type { ConfigAPI, NodePath, PluginObj, PluginPass, types as t } from '@babel/core';
export declare function widgetsPlugin(api: ConfigAPI & typeof import('@babel/core')): PluginObj<PluginPass & {
widgetComponents?: Map<NodePath<t.Function>, {
propNames: Set<string>;
propsIdentifier?: string;
}>;
}>;

119
node_modules/babel-preset-expo/build/widgets-plugin.js generated vendored Normal file
View File

@@ -0,0 +1,119 @@
"use strict";
/**
* Copyright © 2026 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* Babel plugin that transforms widget component JSX expressions.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.widgetsPlugin = widgetsPlugin;
const generator = __importStar(require("@babel/generator"));
function widgetsPlugin(api) {
const { types: t } = api;
return {
name: 'expo-widgets',
visitor: {
['FunctionDeclaration|FunctionExpression']: {
exit(path) {
if (!isWidgetFunction(path)) {
return;
}
removeWidgetDirective(path.node.body);
const code = generateWidgetFunctionString(t, path.node);
const literal = buildTemplateLiteral(t, code);
if (path.parentPath.isExportDefaultDeclaration()) {
path.parentPath.replaceWith(t.exportDefaultDeclaration(literal));
return;
}
if (path.node.id) {
path.replaceWith(t.variableDeclaration('var', [t.variableDeclarator(path.node.id, literal)]));
}
else {
path.replaceWith(literal);
}
},
},
ArrowFunctionExpression: {
exit(path) {
if (!isWidgetFunction(path)) {
return;
}
// Check above will guarantee body is a BlockStatement
removeWidgetDirective(path.node.body);
const code = generateWidgetFunctionString(t, path.node);
const literal = buildTemplateLiteral(t, code);
path.replaceWith(literal);
},
},
ObjectMethod: {
exit(path) {
if (!isWidgetFunction(path)) {
return;
}
removeWidgetDirective(path.node.body);
const code = generateWidgetFunctionString(t, path.node);
const literal = buildTemplateLiteral(t, code);
path.replaceWith(t.objectProperty(path.node.key, literal, path.node.computed));
},
},
},
};
function isWidgetFunction(path) {
if (!t.isBlockStatement(path.node.body)) {
return false;
}
return path.node.body.directives.some((directive) => t.isDirectiveLiteral(directive.value) && directive.value.value === 'widget');
}
function removeWidgetDirective(body) {
const widgetDirectiveIndex = body.directives.findIndex((directive) => t.isDirectiveLiteral(directive.value) && directive.value.value === 'widget');
if (widgetDirectiveIndex !== -1) {
body.directives.splice(widgetDirectiveIndex, 1);
}
}
}
function generateWidgetFunctionString(t, node) {
const expression = t.functionExpression(null, node.params, node.body, node.generator, node.async);
return generator.generate(expression, { compact: true }).code;
}
function buildTemplateLiteral(t, code) {
const raw = escapeTemplateLiteral(code);
return t.templateLiteral([t.templateElement({ raw, cooked: raw }, true)], []);
}
function escapeTemplateLiteral(value) {
return value.replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
}

View File

@@ -0,0 +1,6 @@
/**
* These Expo packages may have side-effects and should not be lazily initialized.
*/
'use strict';
module.exports = require('./build/lazyImports').lazyImports;

94
node_modules/babel-preset-expo/package.json generated vendored Normal file
View File

@@ -0,0 +1,94 @@
{
"name": "babel-preset-expo",
"version": "55.0.10",
"description": "The Babel preset for Expo projects",
"main": "build/index.js",
"files": [
"build",
"lazy-imports-blacklist.js"
],
"scripts": {
"build": "expo-module build",
"clean": "expo-module clean",
"lint": "expo-module lint",
"test": "expo-module test",
"prepare": "expo-module prepare",
"prepublishOnly": "expo-module prepublishOnly",
"expo-module": "expo-module"
},
"repository": {
"type": "git",
"url": "git+https://github.com/expo/expo.git",
"directory": "packages/babel-preset-expo"
},
"keywords": [
"babel",
"babel-preset",
"expo",
"expo-web",
"react-native",
"react-native-web",
"metro",
"webpack"
],
"author": "Expo <support@expo.dev>",
"license": "MIT",
"bugs": {
"url": "https://github.com/expo/expo/issues"
},
"homepage": "https://github.com/expo/expo/tree/main/packages/babel-preset-expo#readme",
"eslintConfig": {
"extends": "universe/node"
},
"peerDependencies": {
"@babel/runtime": "^7.20.0",
"react-refresh": ">=0.14.0 <1.0.0",
"expo": "*",
"expo-widgets": "^55.0.2"
},
"peerDependenciesMeta": {
"@babel/runtime": {
"optional": true
},
"expo": {
"optional": true
},
"expo-widgets": {
"optional": true
}
},
"dependencies": {
"@babel/generator": "^7.20.5",
"@babel/helper-module-imports": "^7.25.9",
"@babel/plugin-proposal-decorators": "^7.12.9",
"@babel/plugin-syntax-export-default-from": "^7.24.7",
"@babel/plugin-proposal-export-default-from": "^7.24.7",
"@babel/plugin-transform-class-static-block": "^7.27.1",
"@babel/plugin-transform-export-namespace-from": "^7.25.9",
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
"@babel/plugin-transform-private-methods": "^7.24.7",
"@babel/plugin-transform-private-property-in-object": "^7.24.7",
"@babel/plugin-transform-runtime": "^7.24.7",
"@babel/plugin-transform-modules-commonjs": "^7.24.8",
"@babel/plugin-transform-object-rest-spread": "^7.24.7",
"@babel/plugin-transform-parameters": "^7.24.7",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.0",
"@react-native/babel-preset": "0.83.2",
"babel-plugin-react-compiler": "^1.0.0",
"babel-plugin-react-native-web": "~0.21.0",
"babel-plugin-transform-flow-enums": "^0.0.2",
"babel-plugin-syntax-hermes-parser": "^0.32.0",
"debug": "^4.3.4",
"resolve-from": "^5.0.0"
},
"devDependencies": {
"@babel/core": "^7.26.0",
"@types/babel__core": "^7.20.5",
"@expo/metro": "~54.2.0",
"expo-module-scripts": "^55.0.2",
"jest": "^29.2.1",
"react-refresh": "^0.14.2"
},
"gitHead": "a4cc8863fe41188075ad91433917bd8e01abbd59"
}