babel-preset-expo 11.1.0-canary-20241008-90b13ad → 11.1.0-canary-20241018-75a0b25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/client-module-proxy-plugin.d.ts +5 -1
- package/build/client-module-proxy-plugin.js +128 -71
- package/build/index.js +10 -10
- package/build/server-actions-plugin.d.ts +13 -0
- package/build/server-actions-plugin.js +518 -0
- package/build/use-dom-directive-plugin.d.ts +4 -2
- package/build/use-dom-directive-plugin.js +9 -0
- package/package.json +5 -5
|
@@ -9,7 +9,9 @@ exports.reactClientReferencesPlugin = void 0;
|
|
|
9
9
|
*/
|
|
10
10
|
const core_1 = require("@babel/core");
|
|
11
11
|
const url_1 = __importDefault(require("url"));
|
|
12
|
-
|
|
12
|
+
const common_1 = require("./common");
|
|
13
|
+
function reactClientReferencesPlugin(api) {
|
|
14
|
+
const isReactServer = api.caller(common_1.getIsReactServer);
|
|
13
15
|
return {
|
|
14
16
|
name: 'expo-client-references',
|
|
15
17
|
visitor: {
|
|
@@ -22,90 +24,145 @@ function reactClientReferencesPlugin() {
|
|
|
22
24
|
if (isUseClient && isUseServer) {
|
|
23
25
|
throw path.buildCodeFrameError("It's not possible to have both `use client` and `use server` directives in the same file.");
|
|
24
26
|
}
|
|
27
|
+
if (!isUseClient && !isUseServer) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
25
30
|
const filePath = state.file.opts.filename;
|
|
26
31
|
if (!filePath) {
|
|
27
32
|
// This can happen in tests or systems that use Babel standalone.
|
|
28
33
|
throw new Error('[Babel] Expected a filename to be set in the state');
|
|
29
34
|
}
|
|
30
|
-
// File starts with "use client" directive.
|
|
31
|
-
if (!isUseClient) {
|
|
32
|
-
// Do nothing for code that isn't marked as a client component.
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
35
|
const outputKey = url_1.default.pathToFileURL(filePath).href;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
ExportNamedDeclaration(exportPath) {
|
|
57
|
-
if (exportPath.node.declaration) {
|
|
58
|
-
if (exportPath.node.declaration.type === 'VariableDeclaration') {
|
|
59
|
-
exportPath.node.declaration.declarations.forEach((declaration) => {
|
|
60
|
-
if (declaration.id.type === 'Identifier') {
|
|
61
|
-
const exportName = declaration.id.name;
|
|
62
|
-
pushProxy(exportName);
|
|
36
|
+
function iterateExports(callback, type) {
|
|
37
|
+
const exportNames = new Set();
|
|
38
|
+
// Collect all of the exports
|
|
39
|
+
path.traverse({
|
|
40
|
+
ExportNamedDeclaration(exportPath) {
|
|
41
|
+
if (exportPath.node.declaration) {
|
|
42
|
+
if (exportPath.node.declaration.type === 'VariableDeclaration') {
|
|
43
|
+
exportPath.node.declaration.declarations.forEach((declaration) => {
|
|
44
|
+
if (declaration.id.type === 'Identifier') {
|
|
45
|
+
const exportName = declaration.id.name;
|
|
46
|
+
exportNames.add(exportName);
|
|
47
|
+
callback(exportName);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
else if (exportPath.node.declaration.type === 'FunctionDeclaration') {
|
|
52
|
+
const exportName = exportPath.node.declaration.id?.name;
|
|
53
|
+
if (exportName) {
|
|
54
|
+
exportNames.add(exportName);
|
|
55
|
+
callback(exportName);
|
|
63
56
|
}
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
else if (exportPath.node.declaration.type === 'FunctionDeclaration') {
|
|
67
|
-
const exportName = exportPath.node.declaration.id?.name;
|
|
68
|
-
if (exportName) {
|
|
69
|
-
pushProxy(exportName);
|
|
70
57
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
58
|
+
else if (exportPath.node.declaration.type === 'ClassDeclaration') {
|
|
59
|
+
const exportName = exportPath.node.declaration.id?.name;
|
|
60
|
+
if (exportName) {
|
|
61
|
+
exportNames.add(exportName);
|
|
62
|
+
callback(exportName);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (!['InterfaceDeclaration', 'TSTypeAliasDeclaration', 'TypeAlias'].includes(exportPath.node.declaration.type)) {
|
|
66
|
+
// TODO: What is this type?
|
|
67
|
+
console.warn(`[babel-preset-expo] Unsupported export specifier for "use ${type}":`, exportPath.node.declaration.type);
|
|
76
68
|
}
|
|
77
69
|
}
|
|
78
|
-
else
|
|
79
|
-
|
|
80
|
-
|
|
70
|
+
else {
|
|
71
|
+
exportPath.node.specifiers.forEach((specifier) => {
|
|
72
|
+
if (core_1.types.isIdentifier(specifier.exported)) {
|
|
73
|
+
const exportName = specifier.exported.name;
|
|
74
|
+
exportNames.add(exportName);
|
|
75
|
+
callback(exportName);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// TODO: What is this type?
|
|
79
|
+
console.warn(`[babel-preset-expo] Unsupported export specifier for "use ${type}":`, specifier);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
81
82
|
}
|
|
83
|
+
},
|
|
84
|
+
ExportDefaultDeclaration() {
|
|
85
|
+
exportNames.add('default');
|
|
86
|
+
callback('default');
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
return exportNames;
|
|
90
|
+
}
|
|
91
|
+
// File starts with "use client" directive.
|
|
92
|
+
if (isUseServer) {
|
|
93
|
+
if (isReactServer) {
|
|
94
|
+
// The "use server" transform for react-server is in a different plugin.
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Handle "use server" in the client.
|
|
98
|
+
const proxyModule = [
|
|
99
|
+
`import { createServerReference } from 'react-server-dom-webpack/client';`,
|
|
100
|
+
`import { callServerRSC } from 'expo-router/rsc/internal';`,
|
|
101
|
+
];
|
|
102
|
+
const getProxy = (exportName) => {
|
|
103
|
+
return `createServerReference(${JSON.stringify(`${outputKey}#${exportName}`)}, callServerRSC)`;
|
|
104
|
+
};
|
|
105
|
+
const pushProxy = (exportName) => {
|
|
106
|
+
if (exportName === 'default') {
|
|
107
|
+
proxyModule.push(`export default ${getProxy(exportName)};`);
|
|
82
108
|
}
|
|
83
109
|
else {
|
|
84
|
-
|
|
85
|
-
if (core_1.types.isIdentifier(specifier.exported)) {
|
|
86
|
-
const exportName = specifier.exported.name;
|
|
87
|
-
pushProxy(exportName);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
// TODO: What is this type?
|
|
91
|
-
console.warn('[babel-preset-expo] Unsupported export specifier for "use client":', specifier);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
110
|
+
proxyModule.push(`export const ${exportName} = ${getProxy(exportName)};`);
|
|
94
111
|
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
112
|
+
};
|
|
113
|
+
// We need to add all of the exports to support `export * from './module'` which iterates the keys of the module.
|
|
114
|
+
// Collect all of the exports
|
|
115
|
+
const proxyExports = iterateExports(pushProxy, 'client');
|
|
116
|
+
// Clear the body
|
|
117
|
+
path.node.body = [];
|
|
118
|
+
path.node.directives = [];
|
|
119
|
+
path.pushContainer('body', core_1.template.ast(proxyModule.join('\n')));
|
|
120
|
+
assertExpoMetadata(state.file.metadata);
|
|
121
|
+
// Store the proxy export names for testing purposes.
|
|
122
|
+
state.file.metadata.proxyExports = [...proxyExports];
|
|
123
|
+
// Save the server action reference in the metadata.
|
|
124
|
+
state.file.metadata.reactServerReference = outputKey;
|
|
125
|
+
}
|
|
126
|
+
else if (isUseClient) {
|
|
127
|
+
if (!isReactServer) {
|
|
128
|
+
// Do nothing for "use client" on the client.
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// HACK: Mock out the polyfill that doesn't run through the normal bundler pipeline.
|
|
132
|
+
if (filePath.endsWith('@react-native/js-polyfills/console.js')) {
|
|
133
|
+
// Clear the body
|
|
134
|
+
path.node.body = [];
|
|
135
|
+
path.node.directives = [];
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// We need to add all of the exports to support `export * from './module'` which iterates the keys of the module.
|
|
139
|
+
const proxyModule = [
|
|
140
|
+
`const proxy = /*@__PURE__*/ require("react-server-dom-webpack/server").createClientModuleProxy(${JSON.stringify(outputKey)});`,
|
|
141
|
+
`module.exports = proxy;`,
|
|
142
|
+
];
|
|
143
|
+
const getProxy = (exportName) => {
|
|
144
|
+
return `(/*@__PURE__*/ proxy[${JSON.stringify(exportName)}])`;
|
|
145
|
+
};
|
|
146
|
+
const pushProxy = (exportName) => {
|
|
147
|
+
if (exportName === 'default') {
|
|
148
|
+
proxyModule.push(`export default ${getProxy(exportName)};`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
proxyModule.push(`export const ${exportName} = ${getProxy(exportName)};`);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
// Collect all of the exports
|
|
155
|
+
const proxyExports = iterateExports(pushProxy, 'client');
|
|
156
|
+
// Clear the body
|
|
157
|
+
path.node.body = [];
|
|
158
|
+
path.node.directives = [];
|
|
159
|
+
path.pushContainer('body', core_1.template.ast(proxyModule.join('\n')));
|
|
160
|
+
assertExpoMetadata(state.file.metadata);
|
|
161
|
+
// Store the proxy export names for testing purposes.
|
|
162
|
+
state.file.metadata.proxyExports = [...proxyExports];
|
|
163
|
+
// Save the client reference in the metadata.
|
|
164
|
+
state.file.metadata.reactClientReference = outputKey;
|
|
165
|
+
}
|
|
109
166
|
},
|
|
110
167
|
},
|
|
111
168
|
};
|
package/build/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const expo_router_plugin_1 = require("./expo-router-plugin");
|
|
|
8
8
|
const inline_env_vars_1 = require("./inline-env-vars");
|
|
9
9
|
const lazyImports_1 = require("./lazyImports");
|
|
10
10
|
const restricted_react_api_plugin_1 = require("./restricted-react-api-plugin");
|
|
11
|
+
const server_actions_plugin_1 = require("./server-actions-plugin");
|
|
11
12
|
const use_dom_directive_plugin_1 = require("./use-dom-directive-plugin");
|
|
12
13
|
function getOptions(options, platform) {
|
|
13
14
|
const tag = platform === 'web' ? 'web' : 'native';
|
|
@@ -39,6 +40,8 @@ function babelPresetExpo(api, options = {}) {
|
|
|
39
40
|
if (!platform && isWebpack) {
|
|
40
41
|
platform = 'web';
|
|
41
42
|
}
|
|
43
|
+
// Use the simpler babel preset for web and server environments (both web and native SSR).
|
|
44
|
+
const isModernEngine = platform === 'web' || isServerEnv;
|
|
42
45
|
const platformOptions = getOptions(options, platform);
|
|
43
46
|
if (platformOptions.useTransformReactJSXExperimental != null) {
|
|
44
47
|
throw new Error(`babel-preset-expo: The option 'useTransformReactJSXExperimental' has been removed in favor of { jsxRuntime: 'classic' }.`);
|
|
@@ -97,13 +100,11 @@ function babelPresetExpo(api, options = {}) {
|
|
|
97
100
|
{ loose: true, useBuiltIns: true },
|
|
98
101
|
]);
|
|
99
102
|
}
|
|
100
|
-
else {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
extraPlugins.push(require('@babel/plugin-transform-parameters'));
|
|
106
|
-
}
|
|
103
|
+
else if (!isModernEngine) {
|
|
104
|
+
// This is added back on hermes to ensure the react-jsx-dev plugin (`@babel/preset-react`) works as expected when
|
|
105
|
+
// JSX is used in a function body. This is technically not required in production, but we
|
|
106
|
+
// should retain the same behavior since it's hard to debug the differences.
|
|
107
|
+
extraPlugins.push(require('@babel/plugin-transform-parameters'));
|
|
107
108
|
}
|
|
108
109
|
const inlines = {
|
|
109
110
|
'process.env.EXPO_OS': platform,
|
|
@@ -156,10 +157,11 @@ function babelPresetExpo(api, options = {}) {
|
|
|
156
157
|
if ((0, common_1.hasModule)('expo-router')) {
|
|
157
158
|
extraPlugins.push(expo_router_plugin_1.expoRouterBabelPlugin);
|
|
158
159
|
}
|
|
160
|
+
extraPlugins.push(client_module_proxy_plugin_1.reactClientReferencesPlugin);
|
|
159
161
|
// Ensure these only run when the user opts-in to bundling for a react server to prevent unexpected behavior for
|
|
160
162
|
// users who are bundling using the client-only system.
|
|
161
163
|
if (isReactServer) {
|
|
162
|
-
extraPlugins.push(
|
|
164
|
+
extraPlugins.push(server_actions_plugin_1.reactServerActionsPlugin);
|
|
163
165
|
extraPlugins.push(restricted_react_api_plugin_1.environmentRestrictedReactAPIsPlugin);
|
|
164
166
|
}
|
|
165
167
|
else {
|
|
@@ -180,8 +182,6 @@ function babelPresetExpo(api, options = {}) {
|
|
|
180
182
|
if (platformOptions.disableImportExportTransform) {
|
|
181
183
|
extraPlugins.push([require('./detect-dynamic-exports').detectDynamicExports]);
|
|
182
184
|
}
|
|
183
|
-
// Use the simpler babel preset for web and server environments (both web and native SSR).
|
|
184
|
-
const isModernEngine = platform === 'web' || isServerEnv;
|
|
185
185
|
return {
|
|
186
186
|
presets: [
|
|
187
187
|
[
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2024 650 Industries.
|
|
3
|
+
* Copyright © 2024 2023 lubieowoce
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*
|
|
8
|
+
* https://github.com/lubieowoce/tangle/blob/5229666fb317d0da9363363fc46dc542ba51e4f7/packages/babel-rsc/src/babel-rsc-actions.ts#L1C1-L909C25
|
|
9
|
+
*/
|
|
10
|
+
import { ConfigAPI, types, type PluginObj, type PluginPass } from '@babel/core';
|
|
11
|
+
export declare function reactServerActionsPlugin(api: ConfigAPI & {
|
|
12
|
+
types: typeof types;
|
|
13
|
+
}): PluginObj<PluginPass>;
|
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright © 2024 650 Industries.
|
|
4
|
+
* Copyright © 2024 2023 lubieowoce
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
* https://github.com/lubieowoce/tangle/blob/5229666fb317d0da9363363fc46dc542ba51e4f7/packages/babel-rsc/src/babel-rsc-actions.ts#L1C1-L909C25
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.reactServerActionsPlugin = void 0;
|
|
39
|
+
const core_1 = require("@babel/core");
|
|
40
|
+
// @ts-expect-error: missing types
|
|
41
|
+
const helper_module_imports_1 = require("@babel/helper-module-imports");
|
|
42
|
+
const t = __importStar(require("@babel/types"));
|
|
43
|
+
const node_path_1 = require("node:path");
|
|
44
|
+
const node_url_1 = require("node:url");
|
|
45
|
+
const url_1 = __importDefault(require("url"));
|
|
46
|
+
const common_1 = require("./common");
|
|
47
|
+
const debug = require('debug')('expo:babel:server-actions');
|
|
48
|
+
const LAZY_WRAPPER_VALUE_KEY = 'value';
|
|
49
|
+
// React doesn't like non-enumerable properties on serialized objects (see `isSimpleObject`),
|
|
50
|
+
// so we have to use closure scope for the cache (instead of a non-enumerable `this._cache`)
|
|
51
|
+
const _buildLazyWrapperHelper = (0, core_1.template)(`(thunk) => {
|
|
52
|
+
let cache;
|
|
53
|
+
return {
|
|
54
|
+
get ${LAZY_WRAPPER_VALUE_KEY}() {
|
|
55
|
+
return cache || (cache = thunk());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}`);
|
|
59
|
+
const buildLazyWrapperHelper = () => {
|
|
60
|
+
return _buildLazyWrapperHelper().expression;
|
|
61
|
+
};
|
|
62
|
+
function reactServerActionsPlugin(api) {
|
|
63
|
+
const possibleProjectRoot = api.caller(common_1.getPossibleProjectRoot);
|
|
64
|
+
let addReactImport;
|
|
65
|
+
let wrapBoundArgs;
|
|
66
|
+
let getActionModuleId;
|
|
67
|
+
const extractInlineActionToTopLevel = (path, _state, { body, freeVariables, }) => {
|
|
68
|
+
const actionModuleId = getActionModuleId();
|
|
69
|
+
const moduleScope = path.scope.getProgramParent();
|
|
70
|
+
const extractedIdentifier = moduleScope.generateUidIdentifier('$$INLINE_ACTION');
|
|
71
|
+
let extractedFunctionParams = [...path.node.params];
|
|
72
|
+
let extractedFunctionBody = body.body;
|
|
73
|
+
if (freeVariables.length > 0) {
|
|
74
|
+
// only add a closure object if we're not closing over anything.
|
|
75
|
+
// const [x, y, z] = await _decryptActionBoundArgs(await $$CLOSURE.value);
|
|
76
|
+
const closureParam = path.scope.generateUidIdentifier('$$CLOSURE');
|
|
77
|
+
const freeVarsPat = t.arrayPattern(freeVariables.map((variable) => t.identifier(variable)));
|
|
78
|
+
const closureExpr = t.memberExpression(closureParam, t.identifier(LAZY_WRAPPER_VALUE_KEY));
|
|
79
|
+
extractedFunctionParams = [closureParam, ...path.node.params];
|
|
80
|
+
extractedFunctionBody = [
|
|
81
|
+
t.variableDeclaration('var', [
|
|
82
|
+
t.variableDeclarator(t.assignmentPattern(freeVarsPat, closureExpr)),
|
|
83
|
+
]),
|
|
84
|
+
...extractedFunctionBody,
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
const wrapInRegister = (expr, exportedName) => {
|
|
88
|
+
const expoRegisterServerReferenceId = addReactImport();
|
|
89
|
+
return t.callExpression(expoRegisterServerReferenceId, [
|
|
90
|
+
expr,
|
|
91
|
+
t.stringLiteral(actionModuleId),
|
|
92
|
+
t.stringLiteral(exportedName),
|
|
93
|
+
]);
|
|
94
|
+
};
|
|
95
|
+
const isArrowFn = path.isArrowFunctionExpression();
|
|
96
|
+
const extractedFunctionExpr = wrapInRegister(isArrowFn
|
|
97
|
+
? t.arrowFunctionExpression(extractedFunctionParams, t.blockStatement(extractedFunctionBody), true)
|
|
98
|
+
: t.functionExpression(path.node.id, extractedFunctionParams, t.blockStatement(extractedFunctionBody), false, true), extractedIdentifier.name);
|
|
99
|
+
// Create a top-level declaration for the extracted function.
|
|
100
|
+
const bindingKind = 'const';
|
|
101
|
+
const functionDeclaration = t.exportNamedDeclaration(t.variableDeclaration(bindingKind, [
|
|
102
|
+
t.variableDeclarator(extractedIdentifier, extractedFunctionExpr),
|
|
103
|
+
]));
|
|
104
|
+
// TODO: this is cacheable, no need to recompute
|
|
105
|
+
const programBody = moduleScope.path.get('body');
|
|
106
|
+
const lastImportPath = findLast(Array.isArray(programBody) ? programBody : [programBody], (stmt) => stmt.isImportDeclaration());
|
|
107
|
+
const [inserted] = lastImportPath.insertAfter(functionDeclaration);
|
|
108
|
+
moduleScope.registerBinding(bindingKind, inserted);
|
|
109
|
+
inserted.addComment('leading', ' hoisted action: ' + (getFnPathName(path) ?? '<anonymous>'), true);
|
|
110
|
+
return {
|
|
111
|
+
extractedIdentifier,
|
|
112
|
+
getReplacement: () => getInlineActionReplacement({
|
|
113
|
+
id: extractedIdentifier,
|
|
114
|
+
freeVariables,
|
|
115
|
+
}),
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
const getInlineActionReplacement = ({ id, freeVariables, }) => {
|
|
119
|
+
if (freeVariables.length === 0) {
|
|
120
|
+
return id;
|
|
121
|
+
}
|
|
122
|
+
const capturedVarsExpr = t.arrayExpression(freeVariables.map((variable) => t.identifier(variable)));
|
|
123
|
+
const boundArgs = wrapBoundArgs(capturedVarsExpr);
|
|
124
|
+
// _ACTION.bind(null, { get value() { return _encryptActionBoundArgs([x, y, z]) } })
|
|
125
|
+
return t.callExpression(t.memberExpression(id, t.identifier('bind')), [
|
|
126
|
+
t.nullLiteral(),
|
|
127
|
+
boundArgs,
|
|
128
|
+
]);
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
name: 'expo-server-actions',
|
|
132
|
+
pre(file) {
|
|
133
|
+
const projectRoot = possibleProjectRoot || file.opts.root || '';
|
|
134
|
+
if (!file.code.includes('use server')) {
|
|
135
|
+
file.path.skip();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
assertExpoMetadata(file.metadata);
|
|
139
|
+
file.metadata.extractedActions = [];
|
|
140
|
+
file.metadata.isModuleMarkedWithUseServerDirective = false;
|
|
141
|
+
const addNamedImportOnce = createAddNamedImportOnce(t);
|
|
142
|
+
addReactImport = () => {
|
|
143
|
+
return addNamedImportOnce(file.path, 'registerServerReference', 'react-server-dom-webpack/server');
|
|
144
|
+
};
|
|
145
|
+
getActionModuleId = once(() => {
|
|
146
|
+
// Create relative file path hash.
|
|
147
|
+
return (0, node_url_1.pathToFileURL)((0, node_path_1.relative)(projectRoot, file.opts.filename)).href;
|
|
148
|
+
});
|
|
149
|
+
const defineBoundArgsWrapperHelper = once(() => {
|
|
150
|
+
const id = this.file.path.scope.generateUidIdentifier('wrapBoundArgs');
|
|
151
|
+
this.file.path.scope.push({
|
|
152
|
+
id,
|
|
153
|
+
kind: 'var',
|
|
154
|
+
init: buildLazyWrapperHelper(),
|
|
155
|
+
});
|
|
156
|
+
return id;
|
|
157
|
+
});
|
|
158
|
+
wrapBoundArgs = (expr) => {
|
|
159
|
+
const wrapperFn = t.cloneNode(defineBoundArgsWrapperHelper());
|
|
160
|
+
return t.callExpression(wrapperFn, [t.arrowFunctionExpression([], expr)]);
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
visitor: {
|
|
164
|
+
Program(path, state) {
|
|
165
|
+
if (path.node.directives.some((d) => d.value.value === 'use server')) {
|
|
166
|
+
assertExpoMetadata(state.file.metadata);
|
|
167
|
+
state.file.metadata.isModuleMarkedWithUseServerDirective = true;
|
|
168
|
+
// remove the directive so that downstream consumers don't transform the module again.
|
|
169
|
+
path.node.directives = path.node.directives.filter((d) => d.value.value !== 'use server');
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
// `() => {}`
|
|
173
|
+
ArrowFunctionExpression(path, state) {
|
|
174
|
+
const { body } = path.node;
|
|
175
|
+
if (!t.isBlockStatement(body) || !hasUseServerDirective(path)) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
assertIsAsyncFn(path);
|
|
179
|
+
const freeVariables = getFreeVariables(path);
|
|
180
|
+
const tlb = getTopLevelBinding(path);
|
|
181
|
+
const { extractedIdentifier, getReplacement } = extractInlineActionToTopLevel(path, state, {
|
|
182
|
+
freeVariables,
|
|
183
|
+
body,
|
|
184
|
+
});
|
|
185
|
+
path.replaceWith(getReplacement());
|
|
186
|
+
assertExpoMetadata(state.file.metadata);
|
|
187
|
+
state.file.metadata.extractedActions.push({
|
|
188
|
+
localName: tlb?.identifier.name,
|
|
189
|
+
exportedName: extractedIdentifier.name,
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
// `function foo() { ... }`
|
|
193
|
+
FunctionDeclaration(path, state) {
|
|
194
|
+
if (!hasUseServerDirective(path)) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
assertIsAsyncFn(path);
|
|
198
|
+
const fnId = path.node.id;
|
|
199
|
+
if (!fnId) {
|
|
200
|
+
throw path.buildCodeFrameError('Internal error: expected FunctionDeclaration to have a name');
|
|
201
|
+
}
|
|
202
|
+
const freeVariables = getFreeVariables(path);
|
|
203
|
+
const { extractedIdentifier, getReplacement } = extractInlineActionToTopLevel(path, state, {
|
|
204
|
+
freeVariables,
|
|
205
|
+
body: path.node.body,
|
|
206
|
+
});
|
|
207
|
+
const tlb = getTopLevelBinding(path);
|
|
208
|
+
if (tlb) {
|
|
209
|
+
// we're at the top level, and we might be enclosed within a `export` decl.
|
|
210
|
+
// we have to keep the export in place, because it might be used elsewhere,
|
|
211
|
+
// so we can't just remove this node.
|
|
212
|
+
// replace the function decl with a (hopefully) equivalent var declaration
|
|
213
|
+
// `var [name] = $$INLINE_ACTION_{N}`
|
|
214
|
+
// TODO: this'll almost certainly break when using default exports,
|
|
215
|
+
// but tangle's build doesn't support those anyway
|
|
216
|
+
const bindingKind = 'var';
|
|
217
|
+
const [inserted] = path.replaceWith(t.variableDeclaration(bindingKind, [t.variableDeclarator(fnId, extractedIdentifier)]));
|
|
218
|
+
tlb.scope.registerBinding(bindingKind, inserted);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// note: if we do this *after* adding the new declaration, the bindings get messed up
|
|
222
|
+
path.remove();
|
|
223
|
+
// add a declaration in the place where the function decl would be hoisted to.
|
|
224
|
+
// (this avoids issues with functions defined after `return`, see `test-cases/named-after-return.jsx`)
|
|
225
|
+
path.scope.push({
|
|
226
|
+
id: fnId,
|
|
227
|
+
init: getReplacement(),
|
|
228
|
+
kind: 'var',
|
|
229
|
+
unique: true,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
assertExpoMetadata(state.file.metadata);
|
|
233
|
+
state.file.metadata.extractedActions.push({
|
|
234
|
+
localName: tlb?.identifier.name,
|
|
235
|
+
exportedName: extractedIdentifier.name,
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
// `const foo = function() { ... }`
|
|
239
|
+
FunctionExpression(path, state) {
|
|
240
|
+
if (!hasUseServerDirective(path)) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
assertIsAsyncFn(path);
|
|
244
|
+
const { body } = path.node;
|
|
245
|
+
const freeVariables = getFreeVariables(path);
|
|
246
|
+
// TODO: look for usages of the name (if present), that's technically possible
|
|
247
|
+
// const fnId = path.node.id;
|
|
248
|
+
const { extractedIdentifier, getReplacement } = extractInlineActionToTopLevel(path, state, {
|
|
249
|
+
freeVariables,
|
|
250
|
+
body,
|
|
251
|
+
});
|
|
252
|
+
const tlb = getTopLevelBinding(path);
|
|
253
|
+
assertExpoMetadata(state.file.metadata);
|
|
254
|
+
path.replaceWith(getReplacement());
|
|
255
|
+
state.file.metadata.extractedActions.push({
|
|
256
|
+
localName: tlb?.identifier.name,
|
|
257
|
+
exportedName: extractedIdentifier.name,
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
// Top-level "use server"
|
|
261
|
+
ExportDefaultDeclaration(path, state) {
|
|
262
|
+
assertExpoMetadata(state.file.metadata);
|
|
263
|
+
if (!state.file.metadata.isModuleMarkedWithUseServerDirective) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
// TODO: support variable functions
|
|
267
|
+
throw path.buildCodeFrameError(`Not implemented: 'export default' declarations in "use server" files. Try using 'export { name as default }' instead.`);
|
|
268
|
+
},
|
|
269
|
+
ExportNamedDeclaration(path, state) {
|
|
270
|
+
assertExpoMetadata(state.file.metadata);
|
|
271
|
+
if (!state.file.metadata.isModuleMarkedWithUseServerDirective) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const registerServerReferenceId = addReactImport();
|
|
275
|
+
const actionModuleId = getActionModuleId();
|
|
276
|
+
const createRegisterCall = (identifier, exported = identifier) => {
|
|
277
|
+
const exportedName = t.isIdentifier(exported) ? exported.name : exported.value;
|
|
278
|
+
const call = t.callExpression(registerServerReferenceId, [
|
|
279
|
+
identifier,
|
|
280
|
+
t.stringLiteral(actionModuleId),
|
|
281
|
+
t.stringLiteral(exportedName),
|
|
282
|
+
]);
|
|
283
|
+
// Wrap call with `;(() => { ... })();` to avoid issues with ASI
|
|
284
|
+
return t.expressionStatement(t.callExpression(t.arrowFunctionExpression([], call), []));
|
|
285
|
+
};
|
|
286
|
+
if (path.node.specifiers.length > 0) {
|
|
287
|
+
for (const specifier of path.node.specifiers) {
|
|
288
|
+
// `export * as ns from './foo';`
|
|
289
|
+
if (t.isExportNamespaceSpecifier(specifier)) {
|
|
290
|
+
throw path.buildCodeFrameError('Not implemented: Namespace exports');
|
|
291
|
+
}
|
|
292
|
+
else if (t.isExportDefaultSpecifier(specifier)) {
|
|
293
|
+
// `export default foo;`
|
|
294
|
+
throw path.buildCodeFrameError('Not implemented (ExportDefaultSpecifier in ExportNamedDeclaration)');
|
|
295
|
+
}
|
|
296
|
+
else if (t.isExportSpecifier(specifier)) {
|
|
297
|
+
// `export { foo };`
|
|
298
|
+
// `export { foo as [bar|default] };`
|
|
299
|
+
const localName = specifier.local.name;
|
|
300
|
+
const exportedName = t.isIdentifier(specifier.exported)
|
|
301
|
+
? specifier.exported.name
|
|
302
|
+
: specifier.exported.value;
|
|
303
|
+
// if we're reexporting an existing action under a new name, we shouldn't register() it again.
|
|
304
|
+
if (!state.file.metadata.extractedActions.some((info) => info.localName === localName)) {
|
|
305
|
+
// referencing the function's local identifier here *should* be safe (w.r.t. TDZ) because
|
|
306
|
+
// 1. if it's a `export async function foo() {}`, the declaration will be hoisted,
|
|
307
|
+
// so it's safe to reference no matter how the declarations are ordered
|
|
308
|
+
// 2. if it's an `export const foo = async () => {}`, then the standalone `export { foo }`
|
|
309
|
+
// has to follow the definition, so we can reference it right before the export decl as well
|
|
310
|
+
path.insertBefore(createRegisterCall(specifier.local, specifier.exported));
|
|
311
|
+
}
|
|
312
|
+
state.file.metadata.extractedActions.push({ localName, exportedName });
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
throw path.buildCodeFrameError('Not implemented: whatever this is');
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (!path.node.declaration) {
|
|
321
|
+
throw path.buildCodeFrameError(`Internal error: Unexpected 'ExportNamedDeclaration' without declarations`);
|
|
322
|
+
}
|
|
323
|
+
const identifiers = (() => {
|
|
324
|
+
const innerPath = path.get('declaration');
|
|
325
|
+
if (innerPath.isVariableDeclaration()) {
|
|
326
|
+
return innerPath.get('declarations').map((d) => {
|
|
327
|
+
// TODO: insert `typeof <identifier> === 'function'` check -- it's a variable, so it could be anything
|
|
328
|
+
const id = d.node.id;
|
|
329
|
+
if (!t.isIdentifier(id)) {
|
|
330
|
+
// TODO
|
|
331
|
+
throw innerPath.buildCodeFrameError('Unimplemented');
|
|
332
|
+
}
|
|
333
|
+
return id;
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
else if (innerPath.isFunctionDeclaration()) {
|
|
337
|
+
if (!innerPath.get('async')) {
|
|
338
|
+
throw innerPath.buildCodeFrameError(`Functions exported from "use server" files must be async.`);
|
|
339
|
+
}
|
|
340
|
+
return [innerPath.get('id').node];
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
throw innerPath.buildCodeFrameError(`Unimplemented server action export`);
|
|
344
|
+
}
|
|
345
|
+
})();
|
|
346
|
+
path.insertAfter(identifiers.map((identifier) => createRegisterCall(identifier)));
|
|
347
|
+
for (const identifier of identifiers) {
|
|
348
|
+
state.file.metadata.extractedActions.push({
|
|
349
|
+
localName: identifier.name,
|
|
350
|
+
exportedName: identifier.name,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
post(file) {
|
|
356
|
+
assertExpoMetadata(file.metadata);
|
|
357
|
+
if (!file.metadata.extractedActions?.length) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
debug('extracted actions', file.metadata.extractedActions);
|
|
361
|
+
const payload = {
|
|
362
|
+
id: getActionModuleId(),
|
|
363
|
+
names: file.metadata.extractedActions.map((e) => e.exportedName),
|
|
364
|
+
};
|
|
365
|
+
const stashedData = 'rsc/actions: ' + JSON.stringify(payload);
|
|
366
|
+
// Add comment for debugging the bundle, we use the babel metadata for accessing the data.
|
|
367
|
+
file.path.addComment('leading', stashedData);
|
|
368
|
+
const filePath = file.opts.filename;
|
|
369
|
+
if (!filePath) {
|
|
370
|
+
// This can happen in tests or systems that use Babel standalone.
|
|
371
|
+
throw new Error('[Babel] Expected a filename to be set in the state');
|
|
372
|
+
}
|
|
373
|
+
const outputKey = url_1.default.pathToFileURL(filePath).href;
|
|
374
|
+
file.metadata.reactServerActions = payload;
|
|
375
|
+
file.metadata.reactServerReference = outputKey;
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
exports.reactServerActionsPlugin = reactServerActionsPlugin;
|
|
380
|
+
const getFreeVariables = (path) => {
|
|
381
|
+
const freeVariablesSet = new Set();
|
|
382
|
+
const programScope = path.scope.getProgramParent();
|
|
383
|
+
path.traverse({
|
|
384
|
+
Identifier(innerPath) {
|
|
385
|
+
const { name } = innerPath.node;
|
|
386
|
+
if (!innerPath.isReferencedIdentifier()) {
|
|
387
|
+
debug('skipping - not referenced');
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (freeVariablesSet.has(name)) {
|
|
391
|
+
// we've already determined this name to be a free var. no point in recomputing.
|
|
392
|
+
debug('skipping - already registered');
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
const binding = innerPath.scope.getBinding(name);
|
|
396
|
+
if (!binding) {
|
|
397
|
+
// probably a global, or an unbound variable. ignore it.
|
|
398
|
+
debug('skipping - global or unbound, skipping');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (binding.scope === programScope) {
|
|
402
|
+
// module-level declaration. no need to close over it.
|
|
403
|
+
debug('skipping - module-level binding');
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
if (
|
|
407
|
+
// function args or a var at the top-level of its body
|
|
408
|
+
binding.scope === path.scope ||
|
|
409
|
+
// decls from blocks within the function
|
|
410
|
+
isChildScope({
|
|
411
|
+
parent: path.scope,
|
|
412
|
+
child: binding.scope,
|
|
413
|
+
root: programScope,
|
|
414
|
+
})) {
|
|
415
|
+
// the binding came from within the function = it's not closed-over, so don't add it.
|
|
416
|
+
debug('skipping - declared within function');
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
// we've (hopefully) eliminated all the other cases, so we should treat this as a free var.
|
|
420
|
+
debug('adding');
|
|
421
|
+
freeVariablesSet.add(name);
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
return [...freeVariablesSet].sort();
|
|
425
|
+
};
|
|
426
|
+
const getFnPathName = (path) => {
|
|
427
|
+
return path.isArrowFunctionExpression() ? undefined : path.node.id.name;
|
|
428
|
+
};
|
|
429
|
+
const isChildScope = ({ root, parent, child, }) => {
|
|
430
|
+
let curScope = child;
|
|
431
|
+
while (curScope !== root) {
|
|
432
|
+
if (curScope.parent === parent) {
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
curScope = curScope.parent;
|
|
436
|
+
}
|
|
437
|
+
return false;
|
|
438
|
+
};
|
|
439
|
+
const findLast = (arr, predicate) => {
|
|
440
|
+
for (let i = arr.length - 1; i >= 0; i--) {
|
|
441
|
+
if (predicate(arr[i]))
|
|
442
|
+
return arr[i];
|
|
443
|
+
}
|
|
444
|
+
return undefined;
|
|
445
|
+
};
|
|
446
|
+
function findImmediatelyEnclosingDeclaration(path) {
|
|
447
|
+
let currentPath = path;
|
|
448
|
+
while (!currentPath.isProgram()) {
|
|
449
|
+
if (
|
|
450
|
+
// const foo = async () => { ... }
|
|
451
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
452
|
+
currentPath.isVariableDeclarator() ||
|
|
453
|
+
// async function foo() { ... }
|
|
454
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
455
|
+
currentPath.isDeclaration()) {
|
|
456
|
+
return currentPath;
|
|
457
|
+
}
|
|
458
|
+
// if we encounter an expression on the way, this isn't a top level decl, and needs to be hoisted.
|
|
459
|
+
// e.g. `export const foo = withAuth(async () => { ... })`
|
|
460
|
+
if (currentPath !== path && currentPath.isExpression()) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
if (!currentPath.parentPath) {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
currentPath = currentPath.parentPath;
|
|
467
|
+
}
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
const getTopLevelBinding = (path) => {
|
|
471
|
+
const decl = findImmediatelyEnclosingDeclaration(path);
|
|
472
|
+
if (!decl || !('id' in decl.node) || !decl.node.id || !('name' in decl.node.id))
|
|
473
|
+
return null;
|
|
474
|
+
const declBinding = decl.scope.getBinding(decl.node.id.name);
|
|
475
|
+
return declBinding.scope === path.scope.getProgramParent() ? declBinding : null;
|
|
476
|
+
};
|
|
477
|
+
const assertIsAsyncFn = (path) => {
|
|
478
|
+
if (!path.node.async) {
|
|
479
|
+
throw path.buildCodeFrameError(`functions marked with "use server" must be async`);
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
const once = (fn) => {
|
|
483
|
+
let cache = { has: false };
|
|
484
|
+
return () => {
|
|
485
|
+
if (cache.has)
|
|
486
|
+
return cache.value;
|
|
487
|
+
cache = { has: true, value: fn() };
|
|
488
|
+
return cache.value;
|
|
489
|
+
};
|
|
490
|
+
};
|
|
491
|
+
function assertExpoMetadata(metadata) {
|
|
492
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
493
|
+
throw new Error('Expected Babel state.file.metadata to be an object');
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
const getOrCreateInMap = (map, key, create) => {
|
|
497
|
+
if (!map.has(key)) {
|
|
498
|
+
const result = create();
|
|
499
|
+
map.set(key, result);
|
|
500
|
+
return [result, true];
|
|
501
|
+
}
|
|
502
|
+
return [map.get(key), false];
|
|
503
|
+
};
|
|
504
|
+
function hasUseServerDirective(path) {
|
|
505
|
+
const { body } = path.node;
|
|
506
|
+
return t.isBlockStatement(body) && body.directives.some((d) => d.value.value === 'use server');
|
|
507
|
+
}
|
|
508
|
+
const createAddNamedImportOnce = (t) => {
|
|
509
|
+
const addedImportsCache = new Map();
|
|
510
|
+
return function addNamedImportOnce(path, name, source) {
|
|
511
|
+
const [sourceCache] = getOrCreateInMap(addedImportsCache, source, () => new Map());
|
|
512
|
+
const [identifier, didCreate] = getOrCreateInMap(sourceCache, name, () => (0, helper_module_imports_1.addNamed)(path, name, source));
|
|
513
|
+
// for cached imports, we need to clone the resulting identifier, because otherwise
|
|
514
|
+
// '@babel/plugin-transform-modules-commonjs' won't replace the references to the import for some reason.
|
|
515
|
+
// this is a helper for that.
|
|
516
|
+
return didCreate ? identifier : t.cloneNode(identifier);
|
|
517
|
+
};
|
|
518
|
+
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Copyright © 2024 650 Industries.
|
|
3
3
|
*/
|
|
4
|
-
import { ConfigAPI } from '@babel/core';
|
|
5
|
-
export declare function expoUseDomDirectivePlugin(api: ConfigAPI
|
|
4
|
+
import { ConfigAPI, types } from '@babel/core';
|
|
5
|
+
export declare function expoUseDomDirectivePlugin(api: ConfigAPI & {
|
|
6
|
+
types: typeof types;
|
|
7
|
+
}): babel.PluginObj;
|
|
@@ -13,6 +13,7 @@ const path_1 = require("path");
|
|
|
13
13
|
const url_1 = __importDefault(require("url"));
|
|
14
14
|
const common_1 = require("./common");
|
|
15
15
|
function expoUseDomDirectivePlugin(api) {
|
|
16
|
+
const { types: t } = api;
|
|
16
17
|
// TODO: Is exporting
|
|
17
18
|
const isProduction = api.caller(common_1.getIsProd);
|
|
18
19
|
const platform = api.caller((caller) => caller?.platform);
|
|
@@ -41,6 +42,14 @@ function expoUseDomDirectivePlugin(api) {
|
|
|
41
42
|
// Collect all of the exports
|
|
42
43
|
path.traverse({
|
|
43
44
|
ExportNamedDeclaration(path) {
|
|
45
|
+
const declaration = path.node.declaration;
|
|
46
|
+
if (t.isTypeAlias(declaration) ||
|
|
47
|
+
t.isInterfaceDeclaration(declaration) ||
|
|
48
|
+
t.isTSTypeAliasDeclaration(declaration) ||
|
|
49
|
+
t.isTSInterfaceDeclaration(declaration)) {
|
|
50
|
+
// Allows type exports
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
44
53
|
throw path.buildCodeFrameError('Modules with the "use dom" directive only support a single default export.');
|
|
45
54
|
},
|
|
46
55
|
ExportDefaultDeclaration() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "babel-preset-expo",
|
|
3
|
-
"version": "11.1.0-canary-
|
|
3
|
+
"version": "11.1.0-canary-20241018-75a0b25",
|
|
4
4
|
"description": "The Babel preset for Expo projects",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"files": [
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
"@babel/plugin-transform-parameters": "^7.22.15",
|
|
49
49
|
"@babel/preset-react": "^7.22.15",
|
|
50
50
|
"@babel/preset-typescript": "^7.23.0",
|
|
51
|
-
"@react-native/babel-preset": "0.
|
|
52
|
-
"babel-plugin-react-native-web": "~0.19.
|
|
51
|
+
"@react-native/babel-preset": "0.76.0-rc.6",
|
|
52
|
+
"babel-plugin-react-native-web": "~0.19.13",
|
|
53
53
|
"react-refresh": "^0.14.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@babel/core": "^7.20.0",
|
|
65
65
|
"babel-plugin-react-compiler": "0.0.0-experimental-334f00b-20240725",
|
|
66
|
-
"expo-module-scripts": "3.6.0-canary-
|
|
66
|
+
"expo-module-scripts": "3.6.0-canary-20241018-75a0b25",
|
|
67
67
|
"jest": "^29.2.1"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "75a0b25b3e0c693e8b28e907d0eb3d4c4303d08b"
|
|
70
70
|
}
|