@vxrn/compiler 1.1.397
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/LICENSE +45 -0
- package/dist/cjs/configure.cjs +34 -0
- package/dist/cjs/configure.js +29 -0
- package/dist/cjs/configure.js.map +6 -0
- package/dist/cjs/configure.native.js +35 -0
- package/dist/cjs/configure.native.js.map +6 -0
- package/dist/cjs/constants.cjs +65 -0
- package/dist/cjs/constants.js +34 -0
- package/dist/cjs/constants.js.map +6 -0
- package/dist/cjs/constants.native.js +65 -0
- package/dist/cjs/constants.native.js.map +6 -0
- package/dist/cjs/index.cjs +124 -0
- package/dist/cjs/index.js +113 -0
- package/dist/cjs/index.js.map +6 -0
- package/dist/cjs/index.native.js +123 -0
- package/dist/cjs/index.native.js.map +6 -0
- package/dist/cjs/refresh-runtime.cjs +295 -0
- package/dist/cjs/refresh-runtime.js +296 -0
- package/dist/cjs/refresh-runtime.js.map +6 -0
- package/dist/cjs/refresh-runtime.native.js +316 -0
- package/dist/cjs/refresh-runtime.native.js.map +6 -0
- package/dist/cjs/transformBabel.cjs +111 -0
- package/dist/cjs/transformBabel.js +116 -0
- package/dist/cjs/transformBabel.js.map +6 -0
- package/dist/cjs/transformBabel.native.js +152 -0
- package/dist/cjs/transformBabel.native.js.map +6 -0
- package/dist/cjs/transformSWC.cjs +261 -0
- package/dist/cjs/transformSWC.js +256 -0
- package/dist/cjs/transformSWC.js.map +6 -0
- package/dist/cjs/transformSWC.native.js +275 -0
- package/dist/cjs/transformSWC.native.js.map +6 -0
- package/dist/cjs/types.cjs +16 -0
- package/dist/cjs/types.js +14 -0
- package/dist/cjs/types.js.map +6 -0
- package/dist/cjs/types.native.js +15 -0
- package/dist/cjs/types.native.js.map +6 -0
- package/dist/esm/configure.js +13 -0
- package/dist/esm/configure.js.map +6 -0
- package/dist/esm/configure.mjs +10 -0
- package/dist/esm/configure.mjs.map +1 -0
- package/dist/esm/configure.native.js +13 -0
- package/dist/esm/configure.native.js.map +6 -0
- package/dist/esm/constants.js +18 -0
- package/dist/esm/constants.js.map +6 -0
- package/dist/esm/constants.mjs +38 -0
- package/dist/esm/constants.mjs.map +1 -0
- package/dist/esm/constants.native.js +41 -0
- package/dist/esm/constants.native.js.map +6 -0
- package/dist/esm/index.js +102 -0
- package/dist/esm/index.js.map +6 -0
- package/dist/esm/index.mjs +99 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +104 -0
- package/dist/esm/index.native.js.map +6 -0
- package/dist/esm/refresh-runtime.js +280 -0
- package/dist/esm/refresh-runtime.js.map +6 -0
- package/dist/esm/refresh-runtime.mjs +266 -0
- package/dist/esm/refresh-runtime.mjs.map +1 -0
- package/dist/esm/refresh-runtime.native.js +290 -0
- package/dist/esm/refresh-runtime.native.js.map +6 -0
- package/dist/esm/transformBabel.js +95 -0
- package/dist/esm/transformBabel.js.map +6 -0
- package/dist/esm/transformBabel.mjs +76 -0
- package/dist/esm/transformBabel.mjs.map +1 -0
- package/dist/esm/transformBabel.native.js +125 -0
- package/dist/esm/transformBabel.native.js.map +6 -0
- package/dist/esm/transformSWC.js +246 -0
- package/dist/esm/transformSWC.js.map +6 -0
- package/dist/esm/transformSWC.mjs +237 -0
- package/dist/esm/transformSWC.mjs.map +1 -0
- package/dist/esm/transformSWC.native.js +257 -0
- package/dist/esm/transformSWC.native.js.map +6 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/types.js.map +6 -0
- package/dist/esm/types.mjs +2 -0
- package/dist/esm/types.mjs.map +1 -0
- package/dist/esm/types.native.js +1 -0
- package/dist/esm/types.native.js.map +6 -0
- package/package.json +54 -0
- package/src/configure.ts +17 -0
- package/src/constants.ts +21 -0
- package/src/index.ts +166 -0
- package/src/refresh-runtime.js +619 -0
- package/src/transformBabel.ts +218 -0
- package/src/transformSWC.ts +381 -0
- package/src/types.ts +36 -0
- package/types/configure.d.ts +10 -0
- package/types/configure.d.ts.map +1 -0
- package/types/constants.d.ts +9 -0
- package/types/constants.d.ts.map +1 -0
- package/types/index.d.ts +12 -0
- package/types/index.d.ts.map +1 -0
- package/types/transformBabel.d.ts +12 -0
- package/types/transformBabel.d.ts.map +1 -0
- package/types/transformSWC.d.ts +7 -0
- package/types/transformSWC.d.ts.map +1 -0
- package/types/types.d.ts +29 -0
- package/types/types.d.ts.map +1 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import babel from '@babel/core'
|
|
2
|
+
import { relative } from 'node:path'
|
|
3
|
+
import { configuration } from './configure'
|
|
4
|
+
import { asyncGeneratorRegex, debug } from './constants'
|
|
5
|
+
import type { GetTransformProps, GetTransformResponse } from './types'
|
|
6
|
+
|
|
7
|
+
type Props = GetTransformProps & {
|
|
8
|
+
userSetting?: GetTransformResponse
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getBabelOptions(props: Props): babel.TransformOptions | null {
|
|
12
|
+
if (props.userSetting === 'babel') {
|
|
13
|
+
return {
|
|
14
|
+
plugins: getPlugins(props, true),
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (
|
|
18
|
+
typeof props.userSetting === 'undefined' ||
|
|
19
|
+
(typeof props.userSetting === 'object' && props.userSetting.transform === 'babel')
|
|
20
|
+
) {
|
|
21
|
+
if (props.userSetting?.excludeDefaultPlugins) {
|
|
22
|
+
return props.userSetting
|
|
23
|
+
}
|
|
24
|
+
const plugins = getPlugins(props)
|
|
25
|
+
if (plugins.length) {
|
|
26
|
+
return {
|
|
27
|
+
plugins,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const getPlugins = (props: Props, force = false) => {
|
|
35
|
+
let plugins: babel.PluginItem[] = []
|
|
36
|
+
|
|
37
|
+
if (force || shouldBabelGenerators(props)) {
|
|
38
|
+
plugins = getBasePlugins(props)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (shouldBabelReanimated(props)) {
|
|
42
|
+
debug?.(`Using babel reanimated on file`)
|
|
43
|
+
plugins.push('react-native-reanimated/plugin')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (shouldBabelReactCompiler(props)) {
|
|
47
|
+
debug?.(`Using babel react compiler on file`)
|
|
48
|
+
plugins.push(getBabelReactCompilerPlugin(props))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (shouldBabelReactNativeCodegen(props)) {
|
|
52
|
+
debug?.(`Using babel @react-native/babel-plugin-codegen on file`)
|
|
53
|
+
plugins.push('@react-native/babel-plugin-codegen')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return plugins
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Transform input to mostly ES5 compatible code, keep ESM syntax, and transform generators.
|
|
60
|
+
*/
|
|
61
|
+
export async function transformBabel(id: string, code: string, options: babel.TransformOptions) {
|
|
62
|
+
const compilerPlugin = options.plugins?.find((x) => x && x[0] === 'babel-plugin-react-compiler')
|
|
63
|
+
|
|
64
|
+
const out = await new Promise<string>((res, rej) => {
|
|
65
|
+
babel.transform(
|
|
66
|
+
code,
|
|
67
|
+
{
|
|
68
|
+
filename: id,
|
|
69
|
+
compact: false,
|
|
70
|
+
minified: false,
|
|
71
|
+
presets: ['@babel/preset-typescript'],
|
|
72
|
+
...options,
|
|
73
|
+
},
|
|
74
|
+
(err: any, result) => {
|
|
75
|
+
if (!result || err) {
|
|
76
|
+
return rej(err || 'no res')
|
|
77
|
+
}
|
|
78
|
+
res(result!.code!)
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
compilerPlugin &&
|
|
85
|
+
out.includes(compilerPlugin[1] === '18' ? `react-compiler-runtime` : `react/compiler-runtime`)
|
|
86
|
+
) {
|
|
87
|
+
console.info(` 🪄 [compiler] ${relative(process.cwd(), id)}`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return out
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const getBasePlugins = ({ development }: Props) =>
|
|
94
|
+
[
|
|
95
|
+
['@babel/plugin-transform-destructuring'],
|
|
96
|
+
['@babel/plugin-transform-react-jsx', { development }],
|
|
97
|
+
['@babel/plugin-transform-async-generator-functions'],
|
|
98
|
+
['@babel/plugin-transform-async-to-generator'],
|
|
99
|
+
[
|
|
100
|
+
'@babel/plugin-transform-runtime',
|
|
101
|
+
{
|
|
102
|
+
helpers: true,
|
|
103
|
+
// NOTE THIS WAS SPELLED WRONG BEFOER THIS COMMIT MAYBE IT WAS UNINTENTIONALLY WORKING
|
|
104
|
+
regenerator: false,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
] satisfies babel.PluginItem[]
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* ----- react native codegen ----
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
// Codegen specification files need to go through the react-native codegen babel plugin.
|
|
114
|
+
// See:
|
|
115
|
+
// * https://reactnative.dev/docs/fabric-native-components-introduction#1-define-specification-for-codegen
|
|
116
|
+
// * https://reactnative.dev/docs/turbo-native-modules-introduction#1-declare-typed-specification
|
|
117
|
+
|
|
118
|
+
const NATIVE_COMPONENT_RE = /NativeComponent\.[jt]sx?$/
|
|
119
|
+
const SPEC_FILE_RE = /[\/\\]specs?[\/\\]/
|
|
120
|
+
|
|
121
|
+
const shouldBabelReactNativeCodegen = ({ id, environment }: Props) => {
|
|
122
|
+
return (
|
|
123
|
+
(environment === 'ios' || environment === 'android') &&
|
|
124
|
+
(NATIVE_COMPONENT_RE.test(id) || SPEC_FILE_RE.test(id))
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* ----- react compiler -----
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
const shouldBabelReactCompiler = (props: Props) => {
|
|
133
|
+
if (!configuration.enableCompiler) {
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
136
|
+
if (Array.isArray(configuration.enableCompiler)) {
|
|
137
|
+
if (!configuration.enableCompiler.includes(props.environment)) {
|
|
138
|
+
return false
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!/.*(.tsx?)$/.test(props.id)) return false
|
|
142
|
+
if (props.code.startsWith('// disable-compiler')) return false
|
|
143
|
+
// may want to disable in node modules? but rare to have tsx in node mods
|
|
144
|
+
return true
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const getBabelReactCompilerPlugin = (props: Props) => {
|
|
148
|
+
const target =
|
|
149
|
+
props.reactForRNVersion === '18' &&
|
|
150
|
+
(props.environment === 'ios' || props.environment === 'android')
|
|
151
|
+
? '18'
|
|
152
|
+
: '19'
|
|
153
|
+
|
|
154
|
+
return ['babel-plugin-react-compiler', { target }]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* ----- generators ------
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
function shouldBabelGenerators({ code }: Props) {
|
|
162
|
+
if (process.env.VXRN_USE_BABEL_FOR_GENERATORS) {
|
|
163
|
+
return asyncGeneratorRegex.test(code)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* ------- reanimated --------
|
|
169
|
+
*/
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Taken from https://github.com/software-mansion/react-native-reanimated/blob/3.15.1/packages/react-native-reanimated/plugin/src/autoworkletization.ts#L19-L59, need to check if this is up-to-date when supporting newer versions of react-native-reanimated.
|
|
173
|
+
*/
|
|
174
|
+
const REANIMATED_AUTOWORKLETIZATION_KEYWORDS = [
|
|
175
|
+
'worklet',
|
|
176
|
+
'useAnimatedGestureHandler',
|
|
177
|
+
'useAnimatedScrollHandler',
|
|
178
|
+
'useFrameCallback',
|
|
179
|
+
'useAnimatedStyle',
|
|
180
|
+
'useAnimatedProps',
|
|
181
|
+
'createAnimatedPropAdapter',
|
|
182
|
+
'useDerivedValue',
|
|
183
|
+
'useAnimatedReaction',
|
|
184
|
+
'useWorkletCallback',
|
|
185
|
+
'withTiming',
|
|
186
|
+
'withSpring',
|
|
187
|
+
'withDecay',
|
|
188
|
+
'withRepeat',
|
|
189
|
+
'runOnUI',
|
|
190
|
+
'executeOnUIRuntimeSync',
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Regex to test if a piece of code should be processed by react-native-reanimated's Babel plugin.
|
|
195
|
+
*/
|
|
196
|
+
const REANIMATED_REGEX = new RegExp(REANIMATED_AUTOWORKLETIZATION_KEYWORDS.join('|'))
|
|
197
|
+
|
|
198
|
+
const REANIMATED_IGNORED_PATHS = [
|
|
199
|
+
// React and React Native libraries are not likely to use reanimated.
|
|
200
|
+
// This can also avoid the "[BABEL] Note: The code generator has deoptimised the styling of ... as it exceeds the max of 500KB" warning since the react-native source code also contains `useAnimatedProps`.
|
|
201
|
+
'react-native-prebuilt',
|
|
202
|
+
'node_modules/.vxrn/react-native',
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
const REANIMATED_IGNORED_PATHS_REGEX = new RegExp(
|
|
206
|
+
REANIMATED_IGNORED_PATHS.map((s) => s.replace(/\//g, '/')).join('|')
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
function shouldBabelReanimated({ code, id }: Props) {
|
|
210
|
+
if (!configuration.enableReanimated) {
|
|
211
|
+
return false
|
|
212
|
+
}
|
|
213
|
+
if (!REANIMATED_IGNORED_PATHS_REGEX.test(id) && REANIMATED_REGEX.test(code)) {
|
|
214
|
+
// console.info(` 🪄 [reanimated] ${relative(process.cwd(), id)}`)
|
|
215
|
+
return true
|
|
216
|
+
}
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import {
|
|
2
|
+
transform,
|
|
3
|
+
type Output,
|
|
4
|
+
type ParserConfig,
|
|
5
|
+
type Options as SWCOptions,
|
|
6
|
+
type TransformConfig,
|
|
7
|
+
} from '@swc/core'
|
|
8
|
+
import type { SourceMapPayload } from 'node:module'
|
|
9
|
+
import { extname } from 'node:path'
|
|
10
|
+
import { merge } from 'ts-deepmerge'
|
|
11
|
+
import { configuration } from './configure'
|
|
12
|
+
import { asyncGeneratorRegex, debug, parsers, runtimePublicPath } from './constants'
|
|
13
|
+
import type { Options } from './types'
|
|
14
|
+
|
|
15
|
+
export async function transformSWC(
|
|
16
|
+
id: string,
|
|
17
|
+
code: string,
|
|
18
|
+
options: Options & { es5?: boolean },
|
|
19
|
+
swcOptions?: SWCOptions
|
|
20
|
+
) {
|
|
21
|
+
if (id.includes('.vite')) {
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
id = id
|
|
26
|
+
.split('?')[0]
|
|
27
|
+
// fixes hmr
|
|
28
|
+
.replace(process.cwd(), '')
|
|
29
|
+
|
|
30
|
+
if (id === runtimePublicPath) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const parser = getParser(id, options.forceJSX)
|
|
35
|
+
|
|
36
|
+
if (!parser) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const enableNativeCSS =
|
|
41
|
+
configuration.enableNativeCSS &&
|
|
42
|
+
// temp fix idk why this error:
|
|
43
|
+
// node_modules/react-native-reanimated/src/component/LayoutAnimationConfig.tsx (19:9): "createInteropElement" is not exported by "../../node_modules/react-native-css-interop/dist/runtime/jsx-dev-runtime.js", imported by "node_modules/react-native-reanimated/src/component/LayoutAnimationConfig.tsx
|
|
44
|
+
!id.includes('node_modules')
|
|
45
|
+
|
|
46
|
+
const refresh =
|
|
47
|
+
options.environment !== 'ssr' && !options.production && !options.noHMR && !options.forceJSX
|
|
48
|
+
|
|
49
|
+
const reactConfig = {
|
|
50
|
+
refresh,
|
|
51
|
+
development: !options.forceJSX && !options.production,
|
|
52
|
+
runtime: 'automatic',
|
|
53
|
+
importSource: 'react',
|
|
54
|
+
...(enableNativeCSS
|
|
55
|
+
? {
|
|
56
|
+
importSource: 'react-native-css-interop',
|
|
57
|
+
pragma: 'createInteropElement',
|
|
58
|
+
// swc doesnt actually change the import right
|
|
59
|
+
runtime: 'classic',
|
|
60
|
+
}
|
|
61
|
+
: {}),
|
|
62
|
+
} satisfies TransformConfig['react']
|
|
63
|
+
|
|
64
|
+
const transformOptions = ((): SWCOptions => {
|
|
65
|
+
if (options.environment === 'client' || options.environment === 'ssr') {
|
|
66
|
+
return {
|
|
67
|
+
sourceMaps: true,
|
|
68
|
+
jsc: {
|
|
69
|
+
target: 'es2020',
|
|
70
|
+
parser,
|
|
71
|
+
transform: {
|
|
72
|
+
useDefineForClassFields: true,
|
|
73
|
+
react: reactConfig,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const shouldEs5Transform =
|
|
80
|
+
options.es5 || (!process.env.VXRN_USE_BABEL_FOR_GENERATORS && asyncGeneratorRegex.test(code))
|
|
81
|
+
|
|
82
|
+
const opts: SWCOptions = shouldEs5Transform
|
|
83
|
+
? {
|
|
84
|
+
jsc: {
|
|
85
|
+
parser,
|
|
86
|
+
target: 'es5',
|
|
87
|
+
transform: {
|
|
88
|
+
useDefineForClassFields: true,
|
|
89
|
+
react: reactConfig,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
: {
|
|
94
|
+
...(!options.forceJSX && { env: SWC_ENV }),
|
|
95
|
+
jsc: {
|
|
96
|
+
...(options.forceJSX && { target: 'esnext' }),
|
|
97
|
+
parser,
|
|
98
|
+
transform: {
|
|
99
|
+
useDefineForClassFields: true,
|
|
100
|
+
react: reactConfig,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
sourceMaps: shouldSourceMap(),
|
|
107
|
+
module: {
|
|
108
|
+
importInterop: 'none',
|
|
109
|
+
type: 'nodenext',
|
|
110
|
+
},
|
|
111
|
+
...(options.mode === 'serve-cjs' && {
|
|
112
|
+
module: {
|
|
113
|
+
importInterop: 'none',
|
|
114
|
+
type: 'commonjs',
|
|
115
|
+
strict: true,
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
...opts,
|
|
119
|
+
}
|
|
120
|
+
})()
|
|
121
|
+
|
|
122
|
+
const finalOptions = merge(
|
|
123
|
+
{
|
|
124
|
+
filename: id,
|
|
125
|
+
swcrc: false,
|
|
126
|
+
configFile: false,
|
|
127
|
+
...transformOptions,
|
|
128
|
+
},
|
|
129
|
+
swcOptions || {}
|
|
130
|
+
) satisfies SWCOptions
|
|
131
|
+
|
|
132
|
+
const result: Output = await (async () => {
|
|
133
|
+
try {
|
|
134
|
+
debug?.(`transformSWC ${id} using options:\n${JSON.stringify(finalOptions, null, 2)}`)
|
|
135
|
+
|
|
136
|
+
return await transform(code, finalOptions)
|
|
137
|
+
} catch (e: any) {
|
|
138
|
+
const message: string = e.message
|
|
139
|
+
const fileStartIndex = message.indexOf('╭─[')
|
|
140
|
+
if (fileStartIndex !== -1) {
|
|
141
|
+
const match = message.slice(fileStartIndex).match(/:(\d+):(\d+)]/)
|
|
142
|
+
if (match) {
|
|
143
|
+
e.line = match[1]
|
|
144
|
+
e.column = match[2]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
throw e
|
|
148
|
+
}
|
|
149
|
+
})()
|
|
150
|
+
|
|
151
|
+
if (enableNativeCSS) {
|
|
152
|
+
if (result.code.includes(`createInteropElement`)) {
|
|
153
|
+
result.code = `import { createInteropElement } from 'react-native-css-interop/jsx-dev-runtime'\n${result.code}`
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const shouldHMR = refresh && refreshContentRE.test(result.code)
|
|
158
|
+
|
|
159
|
+
// fix for node_modules that ship tsx but don't use type-specific imports
|
|
160
|
+
if (
|
|
161
|
+
options.fixNonTypeSpecificImports ||
|
|
162
|
+
(id.includes('node_modules') && parser.syntax === 'typescript')
|
|
163
|
+
) {
|
|
164
|
+
// we need to keep fake objects for type exports
|
|
165
|
+
const typeExportsMatch = code.match(/^\s*export\s+type\s+([^\s]+)/gi)
|
|
166
|
+
if (typeExportsMatch) {
|
|
167
|
+
for (const typeExport of Array.from(typeExportsMatch)) {
|
|
168
|
+
const [_export, _type, name] = typeExport.split(/\s+/)
|
|
169
|
+
// FIXME: support `export { ... } from '...'`
|
|
170
|
+
if (name.startsWith('{')) continue
|
|
171
|
+
|
|
172
|
+
// FIXME: support `export type Type<T> = ...`
|
|
173
|
+
if (name.includes('<')) continue
|
|
174
|
+
|
|
175
|
+
// basic sanity check it isn't exported already
|
|
176
|
+
const alreadyExported = new RegExp(`export (const|let|class|function) ${name}\\s+`).test(
|
|
177
|
+
result.code
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if (!alreadyExported) {
|
|
181
|
+
const fakeExport = `export let ${name} = {};`
|
|
182
|
+
console.info(
|
|
183
|
+
` ⚠️ Fixing non-type-specifc import in node_module, this should be fixed upstream: ${fakeExport} in ${id}`
|
|
184
|
+
)
|
|
185
|
+
result.code += `\n${fakeExport}\n`
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!result || options.noHMR || !shouldHMR) {
|
|
192
|
+
return result
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
wrapSourceInRefreshRuntime(id, result, options, shouldHMR)
|
|
196
|
+
|
|
197
|
+
// TODO bring back?
|
|
198
|
+
// if (result.map) {
|
|
199
|
+
// const sourceMap: SourceMapPayload = JSON.parse(result.map)
|
|
200
|
+
// sourceMap.mappings = ';;;;;;;;' + sourceMap.mappings
|
|
201
|
+
// return { code: result.code, map: sourceMap }
|
|
202
|
+
// }
|
|
203
|
+
|
|
204
|
+
return { code: result.code }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const SWC_ENV = {
|
|
208
|
+
targets: {
|
|
209
|
+
node: '4',
|
|
210
|
+
},
|
|
211
|
+
// debug: true,
|
|
212
|
+
include: [],
|
|
213
|
+
// this breaks the uniswap app for any file with a ...spread
|
|
214
|
+
exclude: [
|
|
215
|
+
'transform-spread',
|
|
216
|
+
'transform-destructuring',
|
|
217
|
+
'transform-object-rest-spread',
|
|
218
|
+
// `transform-async-to-generator` is relying on `transform-destructuring`.
|
|
219
|
+
// If we exclude `transform-destructuring` but not `transform-async-to-generator`, the SWC binary will panic
|
|
220
|
+
// with error: `called `Option::unwrap()` on a `None` value`.
|
|
221
|
+
// See: https://github.com/swc-project/swc/blob/v1.7.14/crates/swc_ecma_compat_es2015/src/generator.rs#L703-L705
|
|
222
|
+
'transform-async-to-generator',
|
|
223
|
+
'transform-regenerator', // Similar to above
|
|
224
|
+
],
|
|
225
|
+
} satisfies SWCOptions['env']
|
|
226
|
+
|
|
227
|
+
const refreshContentRE = /\$Refresh(?:Reg|Sig)\$\(/
|
|
228
|
+
|
|
229
|
+
function shouldSourceMap() {
|
|
230
|
+
return process.env.VXRN_ENABLE_SOURCE_MAP === '1'
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function wrapSourceInRefreshRuntime(
|
|
234
|
+
id: string,
|
|
235
|
+
result: Output,
|
|
236
|
+
options: Options,
|
|
237
|
+
shouldHMR: boolean
|
|
238
|
+
) {
|
|
239
|
+
if (options.environment === 'client' || options.environment === 'ssr') {
|
|
240
|
+
return wrapSourceInRefreshRuntimeWeb(id, result, shouldHMR)
|
|
241
|
+
}
|
|
242
|
+
return wrapSourceInRefreshRuntimeNative(id, result, options, shouldHMR)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function wrapSourceInRefreshRuntimeWeb(id: string, result: Output, shouldHMR: boolean) {
|
|
246
|
+
const sourceMap: SourceMapPayload = JSON.parse(result.map!)
|
|
247
|
+
sourceMap.mappings = ';;' + sourceMap.mappings
|
|
248
|
+
|
|
249
|
+
result.code = `import * as RefreshRuntime from "${runtimePublicPath}";
|
|
250
|
+
|
|
251
|
+
${result.code}`
|
|
252
|
+
|
|
253
|
+
if (shouldHMR) {
|
|
254
|
+
sourceMap.mappings = ';;;;;;' + sourceMap.mappings
|
|
255
|
+
result.code = `if (!window.$RefreshReg$) throw new Error("React refresh preamble was not loaded. Something is wrong.");
|
|
256
|
+
const prevRefreshReg = window.$RefreshReg$;
|
|
257
|
+
const prevRefreshSig = window.$RefreshSig$;
|
|
258
|
+
window.$RefreshReg$ = RefreshRuntime.getRefreshReg("${id}");
|
|
259
|
+
window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
|
|
260
|
+
|
|
261
|
+
${result.code}
|
|
262
|
+
|
|
263
|
+
window.$RefreshReg$ = prevRefreshReg;
|
|
264
|
+
window.$RefreshSig$ = prevRefreshSig;
|
|
265
|
+
`
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
result.code += `
|
|
269
|
+
RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
|
|
270
|
+
RefreshRuntime.registerExportsForReactRefresh("${id}", currentExports);
|
|
271
|
+
import.meta.hot.accept((nextExports) => {
|
|
272
|
+
if (!nextExports) return;
|
|
273
|
+
const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate("${id}", currentExports, nextExports);
|
|
274
|
+
if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
`
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function wrapSourceInRefreshRuntimeNative(
|
|
281
|
+
id: string,
|
|
282
|
+
result: Output,
|
|
283
|
+
options: Options,
|
|
284
|
+
shouldHMR: boolean
|
|
285
|
+
) {
|
|
286
|
+
const prefixCode =
|
|
287
|
+
options.mode === 'build'
|
|
288
|
+
? `
|
|
289
|
+
import 'react-native'
|
|
290
|
+
import 'react'
|
|
291
|
+
import '@vxrn/vite-native-client'
|
|
292
|
+
`
|
|
293
|
+
: ``
|
|
294
|
+
|
|
295
|
+
if (options.production) {
|
|
296
|
+
return `
|
|
297
|
+
${prefixCode}
|
|
298
|
+
module.url = '${id}'
|
|
299
|
+
${result.code}
|
|
300
|
+
`
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (shouldHMR) {
|
|
304
|
+
result.code = `const RefreshRuntime = __cachedModules["react-refresh/cjs/react-refresh-runtime.development"];
|
|
305
|
+
const prevRefreshReg = globalThis.$RefreshReg$;
|
|
306
|
+
const prevRefreshSig = globalThis.$RefreshSig$ || (() => {
|
|
307
|
+
console.info("no react refresh setup!")
|
|
308
|
+
return (x) => x
|
|
309
|
+
});
|
|
310
|
+
globalThis.$RefreshReg$ = (type, id) => RefreshRuntime.register(type, "${id}" + " " + id);
|
|
311
|
+
globalThis.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
|
|
312
|
+
|
|
313
|
+
${prefixCode}
|
|
314
|
+
|
|
315
|
+
module.url = '${id}'
|
|
316
|
+
module.hot = createHotContext(module.url)
|
|
317
|
+
|
|
318
|
+
${result.code}
|
|
319
|
+
|
|
320
|
+
if (module.hot) {
|
|
321
|
+
globalThis.$RefreshReg$ = prevRefreshReg;
|
|
322
|
+
globalThis.$RefreshSig$ = prevRefreshSig;
|
|
323
|
+
globalThis['lastHmrExports'] = JSON.stringify(Object.keys(exports))
|
|
324
|
+
}
|
|
325
|
+
`
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
result.code = `${result.code}
|
|
329
|
+
|
|
330
|
+
if (module.hot) {
|
|
331
|
+
if (module.hot.accept) {
|
|
332
|
+
module.hot.accept((nextExports) => {
|
|
333
|
+
RefreshRuntime.performReactRefresh()
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}`
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function getParser(id: string, forceJSX = false) {
|
|
340
|
+
if (id.endsWith('one-entry-native')) {
|
|
341
|
+
return parsers['.tsx']
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const extension = extname(id)
|
|
345
|
+
let parser: ParserConfig = !extension ? parsers['.js'] : parsers[extension]
|
|
346
|
+
|
|
347
|
+
if (extension === '.js') {
|
|
348
|
+
if (forceJSX) {
|
|
349
|
+
parser = parsers['.jsx']
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (id.includes('expo-modules-core')) {
|
|
353
|
+
parser = parsers['.jsx']
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return parser
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export const transformSWCStripJSX = async (id: string, code: string) => {
|
|
361
|
+
const parser = getParser(id)
|
|
362
|
+
if (!parser) return
|
|
363
|
+
return await transform(code, {
|
|
364
|
+
filename: id,
|
|
365
|
+
swcrc: false,
|
|
366
|
+
configFile: false,
|
|
367
|
+
sourceMaps: shouldSourceMap(),
|
|
368
|
+
jsc: {
|
|
369
|
+
target: 'es2019',
|
|
370
|
+
parser,
|
|
371
|
+
transform: {
|
|
372
|
+
useDefineForClassFields: true,
|
|
373
|
+
react: {
|
|
374
|
+
development: true,
|
|
375
|
+
runtime: 'automatic',
|
|
376
|
+
refresh: false,
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
})
|
|
381
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Options as SWCOptions } from '@swc/core'
|
|
2
|
+
|
|
3
|
+
export type Environment = 'ios' | 'android' | 'ssr' | 'client'
|
|
4
|
+
|
|
5
|
+
export type Options = {
|
|
6
|
+
environment: Environment
|
|
7
|
+
mode: 'serve' | 'serve-cjs' | 'build'
|
|
8
|
+
forceJSX?: boolean
|
|
9
|
+
noHMR?: boolean
|
|
10
|
+
production?: boolean
|
|
11
|
+
fixNonTypeSpecificImports?: boolean
|
|
12
|
+
transform?: GetTransform
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type GetTransformProps = {
|
|
16
|
+
id: string
|
|
17
|
+
code: string
|
|
18
|
+
development: boolean
|
|
19
|
+
environment: Environment
|
|
20
|
+
reactForRNVersion: '18' | '19'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type GetTransform = (props: GetTransformProps) => GetTransformResponse
|
|
24
|
+
|
|
25
|
+
export type GetTransformResponse = boolean | 'babel' | 'swc' | TransformOptions
|
|
26
|
+
|
|
27
|
+
export type TransformOptions = BabelTransformOptions | SWCTransformOptions
|
|
28
|
+
|
|
29
|
+
export type SWCTransformOptions = {
|
|
30
|
+
transform: 'swc'
|
|
31
|
+
} & SWCOptions
|
|
32
|
+
|
|
33
|
+
export type BabelTransformOptions = {
|
|
34
|
+
transform: 'babel'
|
|
35
|
+
excludeDefaultPlugins?: boolean
|
|
36
|
+
} & babel.TransformOptions
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Environment } from './types';
|
|
2
|
+
type Conf = {
|
|
3
|
+
enableReanimated?: boolean;
|
|
4
|
+
enableCompiler?: boolean | Environment[];
|
|
5
|
+
enableNativeCSS?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare const configuration: Conf;
|
|
8
|
+
export declare function configureVXRNCompilerPlugin(_: Conf): void;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=configure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C,KAAK,IAAI,GAAG;IACV,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,cAAc,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,CAAA;IACxC,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,IAI3B,CAAA;AAED,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,IAAI,QAElD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ParserConfig } from '@swc/core';
|
|
2
|
+
export declare const debug: (((...args: any[]) => any) & {
|
|
3
|
+
namespace: string;
|
|
4
|
+
}) | undefined;
|
|
5
|
+
export declare const runtimePublicPath = "/@react-refresh";
|
|
6
|
+
export declare const asyncGeneratorRegex: RegExp;
|
|
7
|
+
export declare const parsers: Record<string, ParserConfig>;
|
|
8
|
+
export declare const validParsers: Set<string>;
|
|
9
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAI7C,eAAO,MAAQ,KAAK;;cAA2C,CAAA;AAE/D,eAAO,MAAM,iBAAiB,oBAAoB,CAAA;AAElD,eAAO,MAAM,mBAAmB,QAA0C,CAAA;AAE1E,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAQhD,CAAA;AAED,eAAO,MAAM,YAAY,aAA6C,CAAA"}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from https://github.com/vitejs/vite-plugin-react-swc/blob/main/src/index.ts
|
|
3
|
+
* to work on both native and web, and with reanimated and other babel fallbacks
|
|
4
|
+
*/
|
|
5
|
+
import type { PluginOption } from 'vite';
|
|
6
|
+
import type { Options } from './types';
|
|
7
|
+
export * from './configure';
|
|
8
|
+
export * from './transformBabel';
|
|
9
|
+
export * from './transformSWC';
|
|
10
|
+
export type { GetTransform } from './types';
|
|
11
|
+
export declare function createVXRNCompilerPlugin(optionsIn?: Partial<Options>): Promise<PluginOption[]>;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,MAAM,CAAA;AAIpD,OAAO,KAAK,EAAkC,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtE,cAAc,aAAa,CAAA;AAC3B,cAAc,kBAAkB,CAAA;AAChC,cAAc,gBAAgB,CAAA;AAC9B,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,wBAAsB,wBAAwB,CAC5C,SAAS,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAC3B,OAAO,CAAC,YAAY,EAAE,CAAC,CA+IzB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import babel from '@babel/core';
|
|
2
|
+
import type { GetTransformProps, GetTransformResponse } from './types';
|
|
3
|
+
type Props = GetTransformProps & {
|
|
4
|
+
userSetting?: GetTransformResponse;
|
|
5
|
+
};
|
|
6
|
+
export declare function getBabelOptions(props: Props): babel.TransformOptions | null;
|
|
7
|
+
/**
|
|
8
|
+
* Transform input to mostly ES5 compatible code, keep ESM syntax, and transform generators.
|
|
9
|
+
*/
|
|
10
|
+
export declare function transformBabel(id: string, code: string, options: babel.TransformOptions): Promise<string>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=transformBabel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformBabel.d.ts","sourceRoot":"","sources":["../src/transformBabel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,aAAa,CAAA;AAI/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAEtE,KAAK,KAAK,GAAG,iBAAiB,GAAG;IAC/B,WAAW,CAAC,EAAE,oBAAoB,CAAA;CACnC,CAAA;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,gBAAgB,GAAG,IAAI,CAqB3E;AA0BD;;GAEG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,gBAAgB,mBA8B7F"}
|