@vxrn/compiler 1.25.13 → 1.25.16-1788753284749

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 (124) hide show
  1. package/dist/cjs/cache.cjs +3 -1
  2. package/dist/cjs/configure.cjs +9 -1
  3. package/dist/cjs/index.cjs +82 -4
  4. package/dist/cjs/reactNativeCodegen.cjs +140 -0
  5. package/dist/cjs/reactNativeViewConfig.cjs +489 -0
  6. package/dist/cjs/transformBabel.cjs +15 -20
  7. package/dist/cjs/transformBabel.test.cjs +260 -0
  8. package/dist/cjs/transformHermesLoops.cjs +317 -0
  9. package/dist/cjs/transformHermesLoops.test.cjs +220 -0
  10. package/dist/cjs/transformSWC.cjs +102 -137
  11. package/dist/cjs/transformSWC.test.cjs +74 -0
  12. package/dist/cjs/transformWorklets.cjs +121 -0
  13. package/dist/cjs/transformWorklets.test.cjs +604 -0
  14. package/dist/cjs/worklets/autoworklet.cjs +265 -0
  15. package/dist/cjs/worklets/globals.cjs +71 -0
  16. package/dist/cjs/worklets/hash.cjs +38 -0
  17. package/dist/cjs/worklets/scope.cjs +370 -0
  18. package/dist/cjs/worklets/serialize.cjs +101 -0
  19. package/dist/cjs/worklets/transform.cjs +232 -0
  20. package/dist/cjs/worklets/types.cjs +18 -0
  21. package/dist/esm/cache.mjs +4 -2
  22. package/dist/esm/cache.mjs.map +1 -1
  23. package/dist/esm/configure.mjs +8 -1
  24. package/dist/esm/configure.mjs.map +1 -1
  25. package/dist/esm/index.js +72 -5
  26. package/dist/esm/index.js.map +1 -1
  27. package/dist/esm/index.mjs +72 -5
  28. package/dist/esm/index.mjs.map +1 -1
  29. package/dist/esm/reactNativeCodegen.mjs +104 -0
  30. package/dist/esm/reactNativeCodegen.mjs.map +1 -0
  31. package/dist/esm/reactNativeViewConfig.mjs +460 -0
  32. package/dist/esm/reactNativeViewConfig.mjs.map +1 -0
  33. package/dist/esm/transformBabel.mjs +14 -19
  34. package/dist/esm/transformBabel.mjs.map +1 -1
  35. package/dist/esm/transformBabel.test.mjs +261 -1
  36. package/dist/esm/transformBabel.test.mjs.map +1 -1
  37. package/dist/esm/transformHermesLoops.mjs +281 -0
  38. package/dist/esm/transformHermesLoops.mjs.map +1 -0
  39. package/dist/esm/transformHermesLoops.test.mjs +197 -0
  40. package/dist/esm/transformHermesLoops.test.mjs.map +1 -0
  41. package/dist/esm/transformSWC.mjs +89 -138
  42. package/dist/esm/transformSWC.mjs.map +1 -1
  43. package/dist/esm/transformSWC.test.mjs +75 -0
  44. package/dist/esm/transformSWC.test.mjs.map +1 -0
  45. package/dist/esm/transformWorklets.mjs +81 -0
  46. package/dist/esm/transformWorklets.mjs.map +1 -0
  47. package/dist/esm/transformWorklets.test.mjs +581 -0
  48. package/dist/esm/transformWorklets.test.mjs.map +1 -0
  49. package/dist/esm/worklets/autoworklet.mjs +234 -0
  50. package/dist/esm/worklets/autoworklet.mjs.map +1 -0
  51. package/dist/esm/worklets/globals.mjs +45 -0
  52. package/dist/esm/worklets/globals.mjs.map +1 -0
  53. package/dist/esm/worklets/hash.mjs +13 -0
  54. package/dist/esm/worklets/hash.mjs.map +1 -0
  55. package/dist/esm/worklets/scope.mjs +345 -0
  56. package/dist/esm/worklets/scope.mjs.map +1 -0
  57. package/dist/esm/worklets/serialize.mjs +75 -0
  58. package/dist/esm/worklets/serialize.mjs.map +1 -0
  59. package/dist/esm/worklets/transform.mjs +196 -0
  60. package/dist/esm/worklets/transform.mjs.map +1 -0
  61. package/dist/esm/worklets/types.mjs +2 -0
  62. package/dist/esm/worklets/types.mjs.map +1 -0
  63. package/package.json +10 -6
  64. package/src/cache.ts +9 -2
  65. package/src/configure.ts +12 -0
  66. package/src/index.ts +93 -13
  67. package/src/reactNativeCodegen.ts +181 -0
  68. package/src/reactNativeViewConfig.ts +593 -0
  69. package/src/transformBabel.test.ts +268 -1
  70. package/src/transformBabel.ts +21 -31
  71. package/src/transformHermesLoops.test.ts +282 -0
  72. package/src/transformHermesLoops.ts +433 -0
  73. package/src/transformSWC.test.ts +88 -0
  74. package/src/transformSWC.ts +109 -160
  75. package/src/transformWorklets.test.ts +708 -0
  76. package/src/transformWorklets.ts +160 -0
  77. package/src/types.ts +4 -4
  78. package/src/worklets/autoworklet.ts +391 -0
  79. package/src/worklets/globals.ts +125 -0
  80. package/src/worklets/hash.ts +14 -0
  81. package/src/worklets/scope.ts +401 -0
  82. package/src/worklets/serialize.ts +114 -0
  83. package/src/worklets/transform.ts +282 -0
  84. package/src/worklets/types.ts +26 -0
  85. package/types/cache.d.ts +1 -0
  86. package/types/cache.d.ts.map +1 -1
  87. package/types/configure.d.ts +2 -0
  88. package/types/configure.d.ts.map +1 -1
  89. package/types/index.d.ts +3 -0
  90. package/types/index.d.ts.map +1 -1
  91. package/types/reactNativeCodegen.d.ts +5 -0
  92. package/types/reactNativeCodegen.d.ts.map +1 -0
  93. package/types/reactNativeViewConfig.d.ts +35 -0
  94. package/types/reactNativeViewConfig.d.ts.map +1 -0
  95. package/types/transformBabel.d.ts +3 -3
  96. package/types/transformBabel.d.ts.map +1 -1
  97. package/types/transformHermesLoops.d.ts +5 -0
  98. package/types/transformHermesLoops.d.ts.map +1 -0
  99. package/types/transformHermesLoops.test.d.ts +2 -0
  100. package/types/transformHermesLoops.test.d.ts.map +1 -0
  101. package/types/transformSWC.d.ts +12 -3
  102. package/types/transformSWC.d.ts.map +1 -1
  103. package/types/transformSWC.test.d.ts +2 -0
  104. package/types/transformSWC.test.d.ts.map +1 -0
  105. package/types/transformWorklets.d.ts +22 -0
  106. package/types/transformWorklets.d.ts.map +1 -0
  107. package/types/transformWorklets.test.d.ts +2 -0
  108. package/types/transformWorklets.test.d.ts.map +1 -0
  109. package/types/types.d.ts +4 -4
  110. package/types/types.d.ts.map +1 -1
  111. package/types/worklets/autoworklet.d.ts +14 -0
  112. package/types/worklets/autoworklet.d.ts.map +1 -0
  113. package/types/worklets/globals.d.ts +3 -0
  114. package/types/worklets/globals.d.ts.map +1 -0
  115. package/types/worklets/hash.d.ts +5 -0
  116. package/types/worklets/hash.d.ts.map +1 -0
  117. package/types/worklets/scope.d.ts +7 -0
  118. package/types/worklets/scope.d.ts.map +1 -0
  119. package/types/worklets/serialize.d.ts +12 -0
  120. package/types/worklets/serialize.d.ts.map +1 -0
  121. package/types/worklets/transform.d.ts +6 -0
  122. package/types/worklets/transform.d.ts.map +1 -0
  123. package/types/worklets/types.d.ts +21 -0
  124. package/types/worklets/types.d.ts.map +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vxrn/compiler",
3
- "version": "1.25.13",
3
+ "version": "1.25.16-1788753284749",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -35,16 +35,20 @@
35
35
  "@babel/plugin-transform-flow-strip-types": "^7.27.1",
36
36
  "@babel/plugin-transform-react-jsx": "^7.27.1",
37
37
  "@babel/plugin-transform-regenerator": "^7.28.4",
38
- "@react-native/babel-plugin-codegen": "^0.86.2",
39
- "@vxrn/utils": "1.25.13",
40
- "@vxrn/vite-native-client": "1.25.13",
38
+ "@jridgewell/remapping": "^2.3.0",
39
+ "@vxrn/utils": "1.25.16-1788753284749",
40
+ "@vxrn/vite-native-client": "1.25.16-1788753284749",
41
41
  "babel-plugin-react-compiler": "^1.0.0",
42
42
  "babel-plugin-syntax-hermes-parser": "^0.32.0",
43
+ "hermes-parser": "^0.32.0",
44
+ "magic-string": "^0.30.21",
45
+ "oxc-parser": "^0.112.0",
46
+ "oxc-transform": "^0.112.0",
43
47
  "oxc-transform-react": "^0.145.0",
44
- "react-native-css-interop": "^0.2.6",
45
- "ts-deepmerge": "^7.0.2"
48
+ "react-native-css-interop": "^0.2.6"
46
49
  },
47
50
  "devDependencies": {
51
+ "@jridgewell/trace-mapping": "^0.3.25",
48
52
  "@tamagui/build": "2.6.2",
49
53
  "depcheck": "^1.4.7",
50
54
  "rolldown": "^1.2.4",
package/src/cache.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createHash } from 'node:crypto'
2
2
  import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
3
3
  import { join } from 'node:path'
4
- import { configuration } from './configure'
4
+ import { configuration, isNativeWorkletsEnabled } from './configure'
5
5
 
6
6
  /**
7
7
  * Fast file-based cache for babel transforms
@@ -23,9 +23,15 @@ interface CacheStats {
23
23
  hits: 0
24
24
  misses: 0
25
25
  writes: 0
26
+ errors: 0
26
27
  }
27
28
 
28
- const stats: CacheStats = { hits: 0, misses: 0, writes: 0 }
29
+ const stats: CacheStats = {
30
+ hits: 0,
31
+ misses: 0,
32
+ writes: 0,
33
+ errors: 0,
34
+ }
29
35
 
30
36
  function getCacheDir(): string {
31
37
  // Use .vxrn cache directory
@@ -43,6 +49,7 @@ function getConfigFingerprint(): string {
43
49
  JSON.stringify({
44
50
  compiler: configuration.enableCompiler,
45
51
  reanimated: configuration.enableReanimated,
52
+ nativeWorklets: isNativeWorkletsEnabled(),
46
53
  nativewind: configuration.enableNativewind,
47
54
  nativeCSS: configuration.enableNativeCSS,
48
55
  // bump when the transform engine changes, so entries written by a
package/src/configure.ts CHANGED
@@ -18,6 +18,7 @@ type CompilerConfig =
18
18
  type Conf = {
19
19
  enableNativewind?: boolean
20
20
  enableReanimated?: boolean
21
+ enableNativeWorklets?: boolean
21
22
  enableCompiler?: CompilerConfig
22
23
  enableNativeCSS?: boolean
23
24
  }
@@ -25,10 +26,21 @@ type Conf = {
25
26
  export const configuration: Conf = {
26
27
  enableNativewind: false,
27
28
  enableReanimated: false,
29
+ enableNativeWorklets: true,
28
30
  enableCompiler: false,
29
31
  enableNativeCSS: false,
30
32
  }
31
33
 
34
+ export function isNativeWorkletsEnabled() {
35
+ if (
36
+ process.env.VXRN_NATIVE_WORKLETS === 'false' ||
37
+ process.env.VXRN_NATIVE_WORKLETS === '0'
38
+ ) {
39
+ return false
40
+ }
41
+ return configuration.enableNativeWorklets !== false
42
+ }
43
+
32
44
  export function configureVXRNCompilerPlugin(_: Conf) {
33
45
  Object.assign(configuration, _)
34
46
  }
package/src/index.ts CHANGED
@@ -12,19 +12,24 @@ import { resolvePath } from '@vxrn/utils'
12
12
  import { cssToReactNativeRuntime } from 'react-native-css-interop/css-to-rn/index.js'
13
13
  import type { OutputChunk } from 'rolldown'
14
14
  import type { PluginOption, ResolvedConfig, UserConfig } from 'vite'
15
- import { configuration } from './configure'
15
+ import { configuration, isNativeWorkletsEnabled } from './configure'
16
16
  import { debug, runtimePublicPath, validParsers } from './constants'
17
17
  import {
18
18
  getBabelOptions,
19
19
  transformBabel,
20
20
  transformOxcReactCompiler,
21
21
  } from './transformBabel'
22
+ import { shouldSourceMap } from './transformSWC'
23
+ import { shouldTransformWorklets, transformWorklets } from './transformWorklets'
22
24
  import type { Environment, GetTransformProps, Options } from './types'
23
25
  import { getCachedTransform, logCacheStats, setCachedTransform } from './cache'
24
26
 
25
27
  export * from './configure'
26
28
  export * from './transformBabel'
27
29
  export * from './transformSWC'
30
+ export * from './transformWorklets'
31
+ export * from './reactNativeCodegen'
32
+ export * from './transformHermesLoops'
28
33
  export type { GetTransform } from './types'
29
34
 
30
35
  // Performance tracking
@@ -148,7 +153,7 @@ async function performBabelTransform({
148
153
  return null
149
154
  }
150
155
 
151
- if (userTransform !== 'swc') {
156
+ if (userTransform !== 'swc' && userTransform !== 'oxc') {
152
157
  const babelOptions = getBabelOptions({
153
158
  ...transformProps,
154
159
  userSetting: userTransform,
@@ -175,20 +180,95 @@ async function performBabelTransform({
175
180
  return cached
176
181
  }
177
182
 
178
- // when the react compiler is the only transform this file needs (the
179
- // common case on web) run oxc's rust port instead of babel. files that
180
- // also need reanimated, nativewind, RN codegen or generator lowering
181
- // still go through babel, which is the only implementation of those.
182
- const compilerOnly = hasCompilerPlugin && babelOptions.plugins?.length === 1
183
- const compilerTarget = compilerOnly
184
- ? ((babelOptions.plugins![0] as any[])[1]?.target ?? '19')
185
- : '19'
183
+ // React compiler through oxc's rust port instead of babel.
184
+ let compilerOut: { code: string; map?: any } | null = null
185
+ const compilerPluginIndex =
186
+ babelOptions.plugins?.findIndex(
187
+ (x) => Array.isArray(x) && x[0] === 'babel-plugin-react-compiler'
188
+ ) ?? -1
189
+
190
+ if (compilerPluginIndex !== -1) {
191
+ const compilerTarget =
192
+ (babelOptions.plugins![compilerPluginIndex] as any[])[1]?.target ?? '19'
193
+ compilerOut = await transformOxcReactCompiler(
194
+ id,
195
+ code,
196
+ compilerTarget,
197
+ shouldSourceMap()
198
+ )
199
+ babelOptions.plugins!.splice(compilerPluginIndex, 1)
200
+ }
201
+
202
+ let curCode = compilerOut ? compilerOut.code : code
203
+
204
+ // Native Worklets through custom native/Oxc worklet transformer instead of babel.
205
+ let workletsOut: { code: string; map?: any } | null = null
206
+ const isWorkletPlugin = (entry: any) => {
207
+ if (!entry) return false
208
+ const name =
209
+ typeof entry === 'string' ? entry : Array.isArray(entry) ? entry[0] : null
210
+ return (
211
+ typeof name === 'string' &&
212
+ (name.includes('react-native-worklets') ||
213
+ name.includes('react-native-reanimated'))
214
+ )
215
+ }
216
+ const workletPluginIndex = babelOptions.plugins?.findIndex(isWorkletPlugin) ?? -1
217
+ let workletPluginOptions: any = undefined
218
+ if (workletPluginIndex !== -1) {
219
+ const entry = babelOptions.plugins![workletPluginIndex]
220
+ if (Array.isArray(entry) && entry[1] && typeof entry[1] === 'object') {
221
+ workletPluginOptions = entry[1]
222
+ }
223
+ }
224
+
225
+ if (
226
+ isNativeWorkletsEnabled() &&
227
+ (workletPluginIndex !== -1 || shouldTransformWorklets({ id, code: curCode }))
228
+ ) {
229
+ workletsOut = await transformWorklets(id, curCode, shouldSourceMap(), {
230
+ projectRoot,
231
+ ...workletPluginOptions,
232
+ })
233
+ if (workletPluginIndex !== -1) {
234
+ babelOptions.plugins!.splice(workletPluginIndex, 1)
235
+ }
236
+ curCode = workletsOut.code
237
+ }
186
238
 
187
239
  // Cache miss - do the transform
188
240
  const startTime = Date.now()
189
- const babelOut = compilerOnly
190
- ? await transformOxcReactCompiler(id, code, compilerTarget)
191
- : await transformBabel(id, code, babelOptions)
241
+ let babelOut: { code?: string | null; map?: any } | null = null
242
+
243
+ if (babelOptions.plugins?.length === 0) {
244
+ let finalMap: any = undefined
245
+ if (shouldSourceMap()) {
246
+ const intermediateMaps = [compilerOut?.map, workletsOut?.map].filter(Boolean)
247
+ if (intermediateMaps.length === 1) {
248
+ finalMap = intermediateMaps[0]
249
+ } else if (intermediateMaps.length > 1) {
250
+ const remapping = (await import('@jridgewell/remapping')).default
251
+ finalMap = remapping(intermediateMaps.slice().reverse(), () => null)
252
+ }
253
+ }
254
+ babelOut = { code: curCode, map: finalMap }
255
+ } else {
256
+ const babelOptsWithMap =
257
+ shouldSourceMap() && (workletsOut?.map || compilerOut?.map)
258
+ ? { ...babelOptions, sourceMaps: true }
259
+ : babelOptions
260
+ babelOut = await transformBabel(id, curCode, babelOptsWithMap)
261
+ if (shouldSourceMap() && babelOut?.map) {
262
+ const priorMaps = [compilerOut?.map, workletsOut?.map].filter(Boolean)
263
+ if (priorMaps.length > 0) {
264
+ const remapping = (await import('@jridgewell/remapping')).default
265
+ babelOut.map = remapping(
266
+ [babelOut.map, ...priorMaps.slice().reverse()],
267
+ () => null
268
+ )
269
+ }
270
+ }
271
+ }
192
272
  const babelTime = Date.now() - startTime
193
273
 
194
274
  if (babelOut?.code) {
@@ -0,0 +1,181 @@
1
+ import MagicString from 'magic-string'
2
+ import {
3
+ generateViewConfig,
4
+ parseSpec,
5
+ renderViewConfigModule,
6
+ } from './reactNativeViewConfig'
7
+
8
+ // vite's normalizePath, inlined so this stays usable from the metro transformer
9
+ // worker, which must not pull vite into a worker process.
10
+ function normalizePath(id: string) {
11
+ return id.replace(/\\/g, '/')
12
+ }
13
+
14
+ const CODEGEN_FILE_RE = /(?:NativeComponent\.[cm]?[jt]sx?$|[/\\]specs?[/\\])/
15
+ const CODEGEN_CALL_RE = /codegenNativeComponent\s*[<(]/
16
+
17
+ // oxc reports positions as start/end, hermes-parser (the flow specs) as range
18
+ const startOf = (node: any): number => node.start ?? node.range[0]
19
+ const endOf = (node: any): number => node.end ?? node.range[1]
20
+
21
+ function unwrapExpression(node: any): any {
22
+ while (node) {
23
+ if (
24
+ node.type === 'TSAsExpression' ||
25
+ node.type === 'TSSatisfiesExpression' ||
26
+ node.type === 'TSTypeAssertion' ||
27
+ node.type === 'TypeCastExpression' ||
28
+ node.type === 'AsExpression' ||
29
+ node.type === 'ParenthesizedExpression'
30
+ ) {
31
+ node = node.expression
32
+ } else if (
33
+ node.type === 'SequenceExpression' &&
34
+ Array.isArray(node.expressions) &&
35
+ node.expressions.length > 0
36
+ ) {
37
+ node = node.expressions[node.expressions.length - 1]
38
+ } else {
39
+ break
40
+ }
41
+ }
42
+ return node
43
+ }
44
+
45
+ function isCodegenDeclaration(declaration: any): boolean {
46
+ if (!declaration) return false
47
+
48
+ if (
49
+ declaration.left &&
50
+ declaration.left.left &&
51
+ declaration.left.left.name === 'codegenNativeComponent'
52
+ ) {
53
+ return true
54
+ }
55
+
56
+ const unwrapped = unwrapExpression(declaration)
57
+ return (
58
+ unwrapped?.type === 'CallExpression' &&
59
+ unwrapped.callee?.type === 'Identifier' &&
60
+ unwrapped.callee.name === 'codegenNativeComponent'
61
+ )
62
+ }
63
+
64
+ function isCodegenNativeCommandsDeclaration(declaration: any): boolean {
65
+ if (!declaration) return false
66
+ const unwrapped = unwrapExpression(declaration)
67
+ return (
68
+ unwrapped?.type === 'CallExpression' &&
69
+ unwrapped.callee?.type === 'Identifier' &&
70
+ unwrapped.callee.name === 'codegenNativeCommands'
71
+ )
72
+ }
73
+
74
+ export function transformReactNativeCodegen(
75
+ code: string,
76
+ id: string,
77
+ projectRoot?: string
78
+ ): { code: string; map: any } | null | undefined {
79
+ const cleanId = normalizePath(id.split('?')[0])
80
+
81
+ if (!CODEGEN_FILE_RE.test(cleanId)) {
82
+ return
83
+ }
84
+
85
+ // the transform runs more than once over the same module, so it must not
86
+ // reject the output it just produced. matching the bare name matches the
87
+ // surviving `import codegenNativeComponent from ...` too; only a remaining
88
+ // CALL means the file still needs generating. a comment marker cannot do
89
+ // this job because oxc drops leading comments on the next pass.
90
+ if (!CODEGEN_CALL_RE.test(code)) {
91
+ return
92
+ }
93
+
94
+ const program = parseSpec(code, cleanId, projectRoot)
95
+ const body = program?.body
96
+ if (!body) {
97
+ return
98
+ }
99
+
100
+ let defaultExport: any = null
101
+ let commandsExport: any = null
102
+
103
+ for (const node of body) {
104
+ if (node.type === 'ExportDefaultDeclaration') {
105
+ if (isCodegenDeclaration(node.declaration)) {
106
+ defaultExport = node
107
+ }
108
+ } else if (node.type === 'ExportNamedDeclaration') {
109
+ if (node.declaration && node.declaration.declarations) {
110
+ const firstDeclaration = node.declaration.declarations[0]
111
+ if (firstDeclaration?.type === 'VariableDeclarator') {
112
+ const isValidCommandsExport = isCodegenNativeCommandsDeclaration(
113
+ firstDeclaration.init
114
+ )
115
+
116
+ if (isValidCommandsExport) {
117
+ if (
118
+ firstDeclaration.id?.type === 'Identifier' &&
119
+ firstDeclaration.id.name !== 'Commands'
120
+ ) {
121
+ throw new Error("Native commands must be exported with the name 'Commands'")
122
+ }
123
+ commandsExport = node
124
+ } else {
125
+ if (
126
+ firstDeclaration.id?.type === 'Identifier' &&
127
+ firstDeclaration.id.name === 'Commands'
128
+ ) {
129
+ throw new Error(
130
+ "'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands."
131
+ )
132
+ }
133
+ }
134
+ }
135
+ } else if (node.specifiers && node.specifiers.length > 0) {
136
+ for (const specifier of node.specifiers) {
137
+ if (
138
+ specifier.type === 'ExportSpecifier' &&
139
+ ((specifier.local?.type === 'Identifier' &&
140
+ specifier.local.name === 'Commands') ||
141
+ (specifier.exported?.type === 'Identifier' &&
142
+ specifier.exported.name === 'Commands'))
143
+ ) {
144
+ throw new Error(
145
+ "'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands."
146
+ )
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ if (!defaultExport) {
154
+ return
155
+ }
156
+
157
+ const generated = generateViewConfig(program)
158
+ if (!generated) {
159
+ return
160
+ }
161
+
162
+ const s = new MagicString(code)
163
+
164
+ if (commandsExport) {
165
+ let removeEnd = endOf(commandsExport)
166
+ if (code[removeEnd] === '\r') removeEnd++
167
+ if (code[removeEnd] === '\n') removeEnd++
168
+ s.remove(startOf(commandsExport), removeEnd)
169
+ }
170
+
171
+ s.overwrite(
172
+ startOf(defaultExport),
173
+ endOf(defaultExport),
174
+ renderViewConfigModule(generated)
175
+ )
176
+
177
+ return {
178
+ code: s.toString(),
179
+ map: s.generateMap({ hires: true, source: cleanId }),
180
+ }
181
+ }