@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.
Files changed (98) hide show
  1. package/LICENSE +45 -0
  2. package/dist/cjs/configure.cjs +34 -0
  3. package/dist/cjs/configure.js +29 -0
  4. package/dist/cjs/configure.js.map +6 -0
  5. package/dist/cjs/configure.native.js +35 -0
  6. package/dist/cjs/configure.native.js.map +6 -0
  7. package/dist/cjs/constants.cjs +65 -0
  8. package/dist/cjs/constants.js +34 -0
  9. package/dist/cjs/constants.js.map +6 -0
  10. package/dist/cjs/constants.native.js +65 -0
  11. package/dist/cjs/constants.native.js.map +6 -0
  12. package/dist/cjs/index.cjs +124 -0
  13. package/dist/cjs/index.js +113 -0
  14. package/dist/cjs/index.js.map +6 -0
  15. package/dist/cjs/index.native.js +123 -0
  16. package/dist/cjs/index.native.js.map +6 -0
  17. package/dist/cjs/refresh-runtime.cjs +295 -0
  18. package/dist/cjs/refresh-runtime.js +296 -0
  19. package/dist/cjs/refresh-runtime.js.map +6 -0
  20. package/dist/cjs/refresh-runtime.native.js +316 -0
  21. package/dist/cjs/refresh-runtime.native.js.map +6 -0
  22. package/dist/cjs/transformBabel.cjs +111 -0
  23. package/dist/cjs/transformBabel.js +116 -0
  24. package/dist/cjs/transformBabel.js.map +6 -0
  25. package/dist/cjs/transformBabel.native.js +152 -0
  26. package/dist/cjs/transformBabel.native.js.map +6 -0
  27. package/dist/cjs/transformSWC.cjs +261 -0
  28. package/dist/cjs/transformSWC.js +256 -0
  29. package/dist/cjs/transformSWC.js.map +6 -0
  30. package/dist/cjs/transformSWC.native.js +275 -0
  31. package/dist/cjs/transformSWC.native.js.map +6 -0
  32. package/dist/cjs/types.cjs +16 -0
  33. package/dist/cjs/types.js +14 -0
  34. package/dist/cjs/types.js.map +6 -0
  35. package/dist/cjs/types.native.js +15 -0
  36. package/dist/cjs/types.native.js.map +6 -0
  37. package/dist/esm/configure.js +13 -0
  38. package/dist/esm/configure.js.map +6 -0
  39. package/dist/esm/configure.mjs +10 -0
  40. package/dist/esm/configure.mjs.map +1 -0
  41. package/dist/esm/configure.native.js +13 -0
  42. package/dist/esm/configure.native.js.map +6 -0
  43. package/dist/esm/constants.js +18 -0
  44. package/dist/esm/constants.js.map +6 -0
  45. package/dist/esm/constants.mjs +38 -0
  46. package/dist/esm/constants.mjs.map +1 -0
  47. package/dist/esm/constants.native.js +41 -0
  48. package/dist/esm/constants.native.js.map +6 -0
  49. package/dist/esm/index.js +102 -0
  50. package/dist/esm/index.js.map +6 -0
  51. package/dist/esm/index.mjs +99 -0
  52. package/dist/esm/index.mjs.map +1 -0
  53. package/dist/esm/index.native.js +104 -0
  54. package/dist/esm/index.native.js.map +6 -0
  55. package/dist/esm/refresh-runtime.js +280 -0
  56. package/dist/esm/refresh-runtime.js.map +6 -0
  57. package/dist/esm/refresh-runtime.mjs +266 -0
  58. package/dist/esm/refresh-runtime.mjs.map +1 -0
  59. package/dist/esm/refresh-runtime.native.js +290 -0
  60. package/dist/esm/refresh-runtime.native.js.map +6 -0
  61. package/dist/esm/transformBabel.js +95 -0
  62. package/dist/esm/transformBabel.js.map +6 -0
  63. package/dist/esm/transformBabel.mjs +76 -0
  64. package/dist/esm/transformBabel.mjs.map +1 -0
  65. package/dist/esm/transformBabel.native.js +125 -0
  66. package/dist/esm/transformBabel.native.js.map +6 -0
  67. package/dist/esm/transformSWC.js +246 -0
  68. package/dist/esm/transformSWC.js.map +6 -0
  69. package/dist/esm/transformSWC.mjs +237 -0
  70. package/dist/esm/transformSWC.mjs.map +1 -0
  71. package/dist/esm/transformSWC.native.js +257 -0
  72. package/dist/esm/transformSWC.native.js.map +6 -0
  73. package/dist/esm/types.js +1 -0
  74. package/dist/esm/types.js.map +6 -0
  75. package/dist/esm/types.mjs +2 -0
  76. package/dist/esm/types.mjs.map +1 -0
  77. package/dist/esm/types.native.js +1 -0
  78. package/dist/esm/types.native.js.map +6 -0
  79. package/package.json +54 -0
  80. package/src/configure.ts +17 -0
  81. package/src/constants.ts +21 -0
  82. package/src/index.ts +166 -0
  83. package/src/refresh-runtime.js +619 -0
  84. package/src/transformBabel.ts +218 -0
  85. package/src/transformSWC.ts +381 -0
  86. package/src/types.ts +36 -0
  87. package/types/configure.d.ts +10 -0
  88. package/types/configure.d.ts.map +1 -0
  89. package/types/constants.d.ts +9 -0
  90. package/types/constants.d.ts.map +1 -0
  91. package/types/index.d.ts +12 -0
  92. package/types/index.d.ts.map +1 -0
  93. package/types/transformBabel.d.ts +12 -0
  94. package/types/transformBabel.d.ts.map +1 -0
  95. package/types/transformSWC.d.ts +7 -0
  96. package/types/transformSWC.d.ts.map +1 -0
  97. package/types/types.d.ts +29 -0
  98. 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"}
@@ -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"}