@vxrn/compiler 1.1.546 → 1.1.547

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/src/index.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  import { resolvePath } from '@vxrn/utils'
7
- import { createHash } from 'node:crypto'
8
7
  import { readFileSync } from 'node:fs'
9
8
  import { readFile } from 'node:fs/promises'
10
9
  import { extname, join, sep } from 'node:path'
@@ -22,16 +21,158 @@ export * from './transformBabel'
22
21
  export * from './transformSWC'
23
22
  export type { GetTransform } from './types'
24
23
 
25
- const getCacheId = (environment: string, id: string) => `${environment}${id}`
26
- const getCacheHash = (code: string) => createHash('sha1').update(code).digest('base64')
24
+ // Shared Babel transform logic
25
+ async function performBabelTransform({
26
+ id,
27
+ code,
28
+ environment,
29
+ production,
30
+ reactForRNVersion,
31
+ optionsIn,
32
+ }: {
33
+ id: string
34
+ code: string
35
+ environment: Environment
36
+ production: boolean
37
+ reactForRNVersion: '18' | '19'
38
+ optionsIn?: Partial<Options>
39
+ }) {
40
+ const transformProps: GetTransformProps = {
41
+ id,
42
+ code,
43
+ development: !production,
44
+ environment,
45
+ reactForRNVersion,
46
+ }
47
+
48
+ const userTransform = optionsIn?.transform?.(transformProps)
49
+
50
+ if (userTransform === false) {
51
+ return null
52
+ }
27
53
 
28
- export const clearCompilerCache = () => {
29
- memoryCache = {}
30
- cacheSize = 0
54
+ const isPreProcess = id.startsWith(`vxrn-swc-preprocess:`)
55
+
56
+ if (!isPreProcess && userTransform !== 'swc') {
57
+ const babelOptions = getBabelOptions({
58
+ ...transformProps,
59
+ userSetting: userTransform,
60
+ })
61
+
62
+ if (babelOptions) {
63
+ const babelOut = await transformBabel(id, code, babelOptions)
64
+ if (babelOut?.code) {
65
+ debug?.(`[babel] ${id}`)
66
+ return { code: babelOut.code, map: babelOut.map }
67
+ }
68
+ }
69
+ }
70
+
71
+ return null
31
72
  }
32
73
 
33
- let memoryCache: Record<string, { hash: string; out: { code: string; map?: any } }> = {}
34
- let cacheSize = 0
74
+ // Full transform (Babel + SWC) for Vite transform hook
75
+ async function performFullTransform({
76
+ codeIn,
77
+ _id,
78
+ environment,
79
+ production,
80
+ reactForRNVersion,
81
+ optionsIn,
82
+ mode,
83
+ }: {
84
+ codeIn: string
85
+ _id: string
86
+ environment: Environment
87
+ production: boolean
88
+ reactForRNVersion: '18' | '19'
89
+ optionsIn?: Partial<Options>
90
+ mode: 'serve' | 'build'
91
+ }) {
92
+ const shouldDebug = process.env.NODE_ENV === 'development' && codeIn.startsWith('// debug')
93
+
94
+ if (shouldDebug) {
95
+ console.info(`[one] ${_id} input:`)
96
+ console.info(codeIn)
97
+ }
98
+
99
+ let id = _id.split('?')[0]
100
+
101
+ const extension = extname(id)
102
+
103
+ if (extension === '.css') {
104
+ return
105
+ }
106
+
107
+ if (!validParsers.has(extension)) {
108
+ return
109
+ }
110
+
111
+ // Skip node_modules to avoid double-processing files already handled by optimizeDeps
112
+ if (id.includes('node_modules')) {
113
+ debug?.(`[skip node_modules] ${id}`)
114
+ return
115
+ }
116
+
117
+ const isPreProcess = id.startsWith(`vxrn-swc-preprocess:`)
118
+ if (isPreProcess) {
119
+ id = id.replace(`vxrn-swc-preprocess:`, '')
120
+ }
121
+
122
+ if (id.includes(`virtual:`)) {
123
+ return
124
+ }
125
+
126
+ let code = codeIn
127
+ let out: {
128
+ code: string
129
+ map: any
130
+ } | null = null
131
+
132
+ // Try Babel first
133
+ const babelResult = await performBabelTransform({
134
+ id,
135
+ code,
136
+ environment,
137
+ production,
138
+ reactForRNVersion,
139
+ optionsIn,
140
+ })
141
+
142
+ if (babelResult) {
143
+ out = babelResult
144
+ code = babelResult.code
145
+ }
146
+
147
+ // Always run SWC for class transforms + react refresh
148
+ const swcOptions = {
149
+ environment,
150
+ mode: optionsIn?.mode || mode,
151
+ production,
152
+ ...optionsIn,
153
+ } satisfies Options
154
+
155
+ const swcOut = await transformSWC(id, code, {
156
+ es5: true,
157
+ noHMR: isPreProcess || environment === 'ssr',
158
+ ...swcOptions,
159
+ })
160
+
161
+ if (swcOut) {
162
+ debug?.(`[swc] ${id}`)
163
+ out = {
164
+ code: swcOut.code,
165
+ map: swcOut.map,
166
+ }
167
+ }
168
+
169
+ if (shouldDebug) {
170
+ console.info(`swcOptions`, swcOptions)
171
+ console.info(`final output:`, out?.code)
172
+ }
173
+
174
+ return out
175
+ }
35
176
 
36
177
  export async function createVXRNCompilerPlugin(
37
178
  optionsIn?: Partial<Options>
@@ -141,21 +282,72 @@ ${rootJS.code}
141
282
  enforce: 'pre',
142
283
 
143
284
  config: () => {
144
- const config = {
145
- esbuild: false,
146
- optimizeDeps: {
147
- noDiscovery: true,
148
- },
149
-
150
- define: {
151
- 'process.env.NATIVEWIND_OS': 'native',
152
- },
153
- } satisfies UserConfig
285
+ const createEnvironmentConfig = (environment: Environment) => {
286
+ return {
287
+ optimizeDeps: {
288
+ esbuildOptions: {
289
+ plugins: [
290
+ {
291
+ name: `transform-before-optimize-deps-${environment}`,
292
+ setup(build) {
293
+ build.onLoad(
294
+ { filter: /node_modules\/.*\.(tsx?|jsx?|mjs|cjs)$/ },
295
+ async (args) => {
296
+ const production = process.env.NODE_ENV === 'production'
297
+ const code = await readFile(args.path, 'utf-8')
298
+
299
+ debug?.(`[esbuild optimizeDeps] ${args.path}`)
300
+
301
+ const result = await performBabelTransform({
302
+ id: args.path,
303
+ code,
304
+ environment,
305
+ production,
306
+ reactForRNVersion,
307
+ optionsIn,
308
+ })
309
+
310
+ if (!result) {
311
+ return null
312
+ }
313
+
314
+ // Determine loader based on file extension
315
+ const ext = extname(args.path)
316
+ const loader =
317
+ ext === '.tsx'
318
+ ? 'tsx'
319
+ : ext === '.ts'
320
+ ? 'ts'
321
+ : ext === '.jsx'
322
+ ? 'jsx'
323
+ : 'js'
324
+
325
+ return {
326
+ contents: result.code,
327
+ loader,
328
+ }
329
+ }
330
+ )
331
+ },
332
+ },
333
+ ],
334
+ },
335
+ },
336
+
337
+ define: {
338
+ 'process.env.NATIVEWIND_OS': JSON.stringify(
339
+ environment === 'ios' || environment === 'android' ? 'native' : 'web'
340
+ ),
341
+ },
342
+ } satisfies UserConfig
343
+ }
154
344
 
155
345
  return {
156
346
  environments: {
157
- ios: config,
158
- android: config,
347
+ ios: createEnvironmentConfig('ios'),
348
+ android: createEnvironmentConfig('android'),
349
+ client: createEnvironmentConfig('client'),
350
+ ssr: createEnvironmentConfig('ssr'),
159
351
  },
160
352
  }
161
353
  },
@@ -190,119 +382,15 @@ ${rootJS.code}
190
382
  return code
191
383
  }
192
384
 
193
- const shouldDebug = process.env.NODE_ENV === 'development' && codeIn.startsWith('// debug')
194
-
195
- if (shouldDebug) {
196
- console.info(`[one] ${_id} input:`)
197
- console.info(codeIn)
198
- }
199
-
200
- const cacheId = getCacheId(environment, _id)
201
- const cacheHash = getCacheHash(code)
202
- const cached = memoryCache[cacheId]
203
-
204
- if (cached?.hash === cacheHash) {
205
- debug?.(`Using cache ${_id} ${cacheId}`)
206
- return cached.out
207
- }
208
-
209
- let id = _id.split('?')[0]
210
-
211
- const extension = extname(id)
212
-
213
- if (extension === '.css') {
214
- // handled in one:compiler-css-to-js
215
- return
216
- }
217
-
218
- if (!validParsers.has(extension)) {
219
- return
220
- }
221
-
222
- // pre process = hmr just are removing jsx but leaving imports as esm
223
- const isPreProcess = id.startsWith(`vxrn-swc-preprocess:`)
224
- if (isPreProcess) {
225
- id = id.replace(`vxrn-swc-preprocess:`, '')
226
- }
227
-
228
- if (id.includes(`virtual:`)) {
229
- return
230
- }
231
-
232
- const transformProps: GetTransformProps = {
233
- id,
234
- code,
235
- development: !production,
385
+ return performFullTransform({
386
+ codeIn,
387
+ _id,
236
388
  environment,
237
- reactForRNVersion,
238
- }
239
-
240
- const userTransform = optionsIn?.transform?.(transformProps)
241
-
242
- if (userTransform === false) {
243
- return
244
- }
245
-
246
- let out: {
247
- code: string
248
- map: any
249
- } | null = null
250
-
251
- if (!isPreProcess && userTransform !== 'swc') {
252
- const babelOptions = getBabelOptions({
253
- ...transformProps,
254
- userSetting: userTransform,
255
- })
256
-
257
- if (babelOptions) {
258
- const babelOut = await transformBabel(id, code, babelOptions)
259
- if (babelOut?.code) {
260
- debug?.(`[${id}] transformed with babel options: ${JSON.stringify(babelOptions)}`)
261
- // TODO we may want to just avoid SWC after babel it likely is faster
262
- // we'd need to have metro or metro-like preset
263
- out = { code: babelOut.code, map: babelOut.map }
264
- }
265
- }
266
- }
267
-
268
- // on native we always go to swc for now to ensure class transforms + react refesh
269
- // we could make the babel plugin support those if we want to avoid
270
- const swcOptions = {
271
- environment,
272
- mode: optionsIn?.mode || config.command,
273
389
  production,
274
- ...optionsIn,
275
- } satisfies Options
276
-
277
- const swcOut = await transformSWC(id, out?.code || code, {
278
- es5: true,
279
- // dont need refresh for ssr, but do for client
280
- noHMR: isPreProcess || environment === 'ssr',
281
- ...swcOptions,
390
+ reactForRNVersion,
391
+ optionsIn,
392
+ mode: config.command,
282
393
  })
283
-
284
- if (swcOut) {
285
- out = {
286
- code: swcOut?.code,
287
- map: swcOut?.map,
288
- }
289
- }
290
-
291
- if (shouldDebug) {
292
- console.info(`swcOptions`, swcOptions)
293
- console.info(`final output:`, out?.code)
294
- }
295
-
296
- if (out) {
297
- cacheSize += out?.code.length
298
- // ~100Mb cache for recent compiler files
299
- if (cacheSize > 52_428_800) {
300
- clearCompilerCache()
301
- }
302
- memoryCache[cacheId] = { out, hash: cacheHash }
303
- }
304
-
305
- return out
306
394
  },
307
395
  },
308
396
  ]
@@ -26,7 +26,6 @@ export function getBabelOptions(props: Props): babel.TransformOptions | null {
26
26
  }
27
27
 
28
28
  const getOptions = (props: Props, force = false): babel.TransformOptions | null => {
29
- const presets: string[] = []
30
29
  let plugins: babel.PluginItem[] = []
31
30
 
32
31
  if (force || shouldBabelGenerators(props)) {
@@ -48,7 +47,7 @@ const getOptions = (props: Props, force = false): babel.TransformOptions | null
48
47
  }
49
48
 
50
49
  if (enableNativewind || shouldBabelReanimated(props)) {
51
- debug?.(`Using babel reanimated on file`)
50
+ debug?.(`Using babel reanimated on file ${props.id}`)
52
51
  plugins.push(
53
52
  // TODO make this configurable
54
53
  process.env.VXRN_WORKLET_PLUGIN
@@ -67,8 +66,8 @@ const getOptions = (props: Props, force = false): babel.TransformOptions | null
67
66
  plugins.push('@react-native/babel-plugin-codegen')
68
67
  }
69
68
 
70
- if (plugins.length || presets.length) {
71
- return { plugins, presets }
69
+ if (plugins.length) {
70
+ return { plugins }
72
71
  }
73
72
 
74
73
  return null
@@ -79,43 +78,39 @@ const getOptions = (props: Props, force = false): babel.TransformOptions | null
79
78
  */
80
79
  export async function transformBabel(id: string, code: string, options: babel.TransformOptions) {
81
80
  const compilerPlugin = options.plugins?.find((x) => x && x[0] === 'babel-plugin-react-compiler')
81
+ const extension = extname(id)
82
+ const isTSX = extension === '.tsx'
83
+ const isTS = isTSX || extension === '.ts'
84
+ const babelOptions = {
85
+ filename: id,
86
+ compact: false,
87
+ babelrc: false,
88
+ configFile: false,
89
+ sourceMaps: false,
90
+ minified: false,
91
+ ...options,
92
+ presets: [
93
+ isTS
94
+ ? [
95
+ '@babel/preset-typescript',
96
+ {
97
+ isTSX,
98
+ allExtensions: isTSX,
99
+ },
100
+ ]
101
+ : '',
102
+ ...(options.presets || []),
103
+ ].filter(Boolean),
104
+ }
82
105
 
83
106
  try {
84
- const extension = extname(id)
85
- const isTSX = extension === '.tsx'
86
- const isTS = isTSX || extension === '.ts'
87
-
88
107
  const out = await new Promise<babel.BabelFileResult>((res, rej) => {
89
- babel.transform(
90
- code,
91
- {
92
- filename: id,
93
- compact: false,
94
- babelrc: false,
95
- configFile: false,
96
- sourceMaps: false,
97
- minified: false,
98
- ...options,
99
- presets: [
100
- isTS
101
- ? [
102
- '@babel/preset-typescript',
103
- {
104
- isTSX,
105
- allExtensions: isTSX,
106
- },
107
- ]
108
- : '',
109
- ...(options.presets || []),
110
- ].filter(Boolean),
111
- },
112
- (err: any, result) => {
113
- if (!result || err) {
114
- return rej(err || 'no res')
115
- }
116
- res(result!)
108
+ babel.transform(code, babelOptions, (err: any, result) => {
109
+ if (!result || err) {
110
+ return rej(err || 'no res')
117
111
  }
118
- )
112
+ res(result!)
113
+ })
119
114
  })
120
115
 
121
116
  if (
@@ -130,7 +125,7 @@ export async function transformBabel(id: string, code: string, options: babel.Tr
130
125
 
131
126
  return out
132
127
  } catch (err) {
133
- console.error(`[vxrn:compiler] babel transform error`, err)
128
+ console.error(`[vxrn:compiler] babel transform error`, err, `with options`, babelOptions)
134
129
  console.error('code', code)
135
130
  console.error('id', id)
136
131
  }
@@ -188,7 +183,7 @@ const shouldBabelReactCompiler = (props: Props) => {
188
183
  return false
189
184
  }
190
185
  }
191
- if (!/.*(.tsx?)$/.test(props.id)) return false
186
+ if (!/(\.tsx?)$/.test(props.id)) return false
192
187
  // disable node modules for now...
193
188
  if (props.id.includes('node_modules')) return false
194
189
  // we had OG image generation on the server using react, this fixes a bug because compiler breaks @vercel/og
package/types/index.d.ts CHANGED
@@ -8,6 +8,5 @@ export * from './configure';
8
8
  export * from './transformBabel';
9
9
  export * from './transformSWC';
10
10
  export type { GetTransform } from './types';
11
- export declare const clearCompilerCache: () => void;
12
11
  export declare function createVXRNCompilerPlugin(optionsIn?: Partial<Options>): Promise<PluginOption[]>;
13
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,MAAM,CAAA;AAKpE,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;AAK3C,eAAO,MAAM,kBAAkB,YAG9B,CAAA;AAKD,wBAAsB,wBAAwB,CAC5C,SAAS,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAC3B,OAAO,CAAC,YAAY,EAAE,CAAC,CA+QzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,KAAK,EAAE,YAAY,EAA8B,MAAM,MAAM,CAAA;AAKpE,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;AA2J3C,wBAAsB,wBAAwB,CAC5C,SAAS,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAC3B,OAAO,CAAC,YAAY,EAAE,CAAC,CA0NzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"transformBabel.d.ts","sourceRoot":"","sources":["../src/transformBabel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,aAAa,CAAA;AAK/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,CAc3E;AAmDD;;GAEG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,gBAAgB,8CAyD7F"}
1
+ {"version":3,"file":"transformBabel.d.ts","sourceRoot":"","sources":["../src/transformBabel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,aAAa,CAAA;AAK/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,CAc3E;AAkDD;;GAEG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,gBAAgB,8CAqD7F"}