@vitejs/plugin-react 2.1.0 → 2.2.0
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/dist/chunks/babel-restore-jsx.cjs +1 -1
- package/dist/index.cjs +53 -23
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +44 -8
- package/package.json +6 -7
- package/src/babel.d.ts +0 -4
- package/src/fast-refresh.ts +0 -117
- package/src/index.ts +0 -473
- package/src/jsx-runtime/babel-import-to-require.ts +0 -35
- package/src/jsx-runtime/babel-restore-jsx.spec.ts +0 -132
- package/src/jsx-runtime/babel-restore-jsx.ts +0 -232
- package/src/jsx-runtime/restore-jsx.spec.ts +0 -147
- package/src/jsx-runtime/restore-jsx.ts +0 -74
package/src/index.ts
DELETED
@@ -1,473 +0,0 @@
|
|
1
|
-
import path from 'node:path'
|
2
|
-
import type { ParserOptions, TransformOptions, types as t } from '@babel/core'
|
3
|
-
import * as babel from '@babel/core'
|
4
|
-
import { createFilter, normalizePath } from 'vite'
|
5
|
-
import type { Plugin, PluginOption, ResolvedConfig } from 'vite'
|
6
|
-
import MagicString from 'magic-string'
|
7
|
-
import type { SourceMap } from 'magic-string'
|
8
|
-
import {
|
9
|
-
addRefreshWrapper,
|
10
|
-
isRefreshBoundary,
|
11
|
-
preambleCode,
|
12
|
-
runtimeCode,
|
13
|
-
runtimePublicPath
|
14
|
-
} from './fast-refresh'
|
15
|
-
import { babelImportToRequire } from './jsx-runtime/babel-import-to-require'
|
16
|
-
import { restoreJSX } from './jsx-runtime/restore-jsx'
|
17
|
-
|
18
|
-
export interface Options {
|
19
|
-
include?: string | RegExp | Array<string | RegExp>
|
20
|
-
exclude?: string | RegExp | Array<string | RegExp>
|
21
|
-
/**
|
22
|
-
* Enable `react-refresh` integration. Vite disables this in prod env or build mode.
|
23
|
-
* @default true
|
24
|
-
*/
|
25
|
-
fastRefresh?: boolean
|
26
|
-
/**
|
27
|
-
* Set this to `"automatic"` to use [vite-react-jsx](https://github.com/alloc/vite-react-jsx).
|
28
|
-
* @default "automatic"
|
29
|
-
*/
|
30
|
-
jsxRuntime?: 'classic' | 'automatic'
|
31
|
-
/**
|
32
|
-
* Control where the JSX factory is imported from.
|
33
|
-
* This option is ignored when `jsxRuntime` is not `"automatic"`.
|
34
|
-
* @default "react"
|
35
|
-
*/
|
36
|
-
jsxImportSource?: string
|
37
|
-
/**
|
38
|
-
* Set this to `true` to annotate the JSX factory with `\/* @__PURE__ *\/`.
|
39
|
-
* This option is ignored when `jsxRuntime` is not `"automatic"`.
|
40
|
-
* @default true
|
41
|
-
*/
|
42
|
-
jsxPure?: boolean
|
43
|
-
/**
|
44
|
-
* Babel configuration applied in both dev and prod.
|
45
|
-
*/
|
46
|
-
babel?:
|
47
|
-
| BabelOptions
|
48
|
-
| ((id: string, options: { ssr?: boolean }) => BabelOptions)
|
49
|
-
}
|
50
|
-
|
51
|
-
export type BabelOptions = Omit<
|
52
|
-
TransformOptions,
|
53
|
-
| 'ast'
|
54
|
-
| 'filename'
|
55
|
-
| 'root'
|
56
|
-
| 'sourceFileName'
|
57
|
-
| 'sourceMaps'
|
58
|
-
| 'inputSourceMap'
|
59
|
-
>
|
60
|
-
|
61
|
-
/**
|
62
|
-
* The object type used by the `options` passed to plugins with
|
63
|
-
* an `api.reactBabel` method.
|
64
|
-
*/
|
65
|
-
export interface ReactBabelOptions extends BabelOptions {
|
66
|
-
plugins: Extract<BabelOptions['plugins'], any[]>
|
67
|
-
presets: Extract<BabelOptions['presets'], any[]>
|
68
|
-
overrides: Extract<BabelOptions['overrides'], any[]>
|
69
|
-
parserOpts: ParserOptions & {
|
70
|
-
plugins: Extract<ParserOptions['plugins'], any[]>
|
71
|
-
}
|
72
|
-
}
|
73
|
-
|
74
|
-
type ReactBabelHook = (
|
75
|
-
babelConfig: ReactBabelOptions,
|
76
|
-
context: ReactBabelHookContext,
|
77
|
-
config: ResolvedConfig
|
78
|
-
) => void
|
79
|
-
|
80
|
-
type ReactBabelHookContext = { ssr: boolean; id: string }
|
81
|
-
|
82
|
-
declare module 'vite' {
|
83
|
-
export interface Plugin {
|
84
|
-
api?: {
|
85
|
-
/**
|
86
|
-
* Manipulate the Babel options of `@vitejs/plugin-react`
|
87
|
-
*/
|
88
|
-
reactBabel?: ReactBabelHook
|
89
|
-
}
|
90
|
-
}
|
91
|
-
}
|
92
|
-
|
93
|
-
const prependReactImportCode = "import React from 'react'; "
|
94
|
-
|
95
|
-
export default function viteReact(opts: Options = {}): PluginOption[] {
|
96
|
-
// Provide default values for Rollup compat.
|
97
|
-
let devBase = '/'
|
98
|
-
let resolvedCacheDir: string
|
99
|
-
let filter = createFilter(opts.include, opts.exclude)
|
100
|
-
let needHiresSourcemap = false
|
101
|
-
let isProduction = true
|
102
|
-
let projectRoot = process.cwd()
|
103
|
-
let skipFastRefresh = opts.fastRefresh === false
|
104
|
-
let skipReactImport = false
|
105
|
-
let runPluginOverrides = (
|
106
|
-
options: ReactBabelOptions,
|
107
|
-
context: ReactBabelHookContext
|
108
|
-
) => false
|
109
|
-
let staticBabelOptions: ReactBabelOptions | undefined
|
110
|
-
|
111
|
-
const useAutomaticRuntime = opts.jsxRuntime !== 'classic'
|
112
|
-
|
113
|
-
// Support patterns like:
|
114
|
-
// - import * as React from 'react';
|
115
|
-
// - import React from 'react';
|
116
|
-
// - import React, {useEffect} from 'react';
|
117
|
-
const importReactRE = /(^|\n)import\s+(\*\s+as\s+)?React(,|\s+)/
|
118
|
-
|
119
|
-
// Any extension, including compound ones like '.bs.js'
|
120
|
-
const fileExtensionRE = /\.[^\/\s\?]+$/
|
121
|
-
|
122
|
-
const viteBabel: Plugin = {
|
123
|
-
name: 'vite:react-babel',
|
124
|
-
enforce: 'pre',
|
125
|
-
config() {
|
126
|
-
if (opts.jsxRuntime === 'classic') {
|
127
|
-
return {
|
128
|
-
esbuild: {
|
129
|
-
logOverride: {
|
130
|
-
'this-is-undefined-in-esm': 'silent'
|
131
|
-
}
|
132
|
-
}
|
133
|
-
}
|
134
|
-
}
|
135
|
-
},
|
136
|
-
configResolved(config) {
|
137
|
-
devBase = config.base
|
138
|
-
projectRoot = config.root
|
139
|
-
resolvedCacheDir = normalizePath(path.resolve(config.cacheDir))
|
140
|
-
filter = createFilter(opts.include, opts.exclude, {
|
141
|
-
resolve: projectRoot
|
142
|
-
})
|
143
|
-
needHiresSourcemap =
|
144
|
-
config.command === 'build' && !!config.build.sourcemap
|
145
|
-
isProduction = config.isProduction
|
146
|
-
skipFastRefresh ||= isProduction || config.command === 'build'
|
147
|
-
|
148
|
-
const jsxInject = config.esbuild && config.esbuild.jsxInject
|
149
|
-
if (jsxInject && importReactRE.test(jsxInject)) {
|
150
|
-
skipReactImport = true
|
151
|
-
config.logger.warn(
|
152
|
-
'[@vitejs/plugin-react] This plugin imports React for you automatically,' +
|
153
|
-
' so you can stop using `esbuild.jsxInject` for that purpose.'
|
154
|
-
)
|
155
|
-
}
|
156
|
-
|
157
|
-
config.plugins.forEach((plugin) => {
|
158
|
-
const hasConflict =
|
159
|
-
plugin.name === 'react-refresh' ||
|
160
|
-
(plugin !== viteReactJsx && plugin.name === 'vite:react-jsx')
|
161
|
-
|
162
|
-
if (hasConflict)
|
163
|
-
return config.logger.warn(
|
164
|
-
`[@vitejs/plugin-react] You should stop using "${plugin.name}" ` +
|
165
|
-
`since this plugin conflicts with it.`
|
166
|
-
)
|
167
|
-
})
|
168
|
-
|
169
|
-
runPluginOverrides = (babelOptions, context) => {
|
170
|
-
const hooks = config.plugins
|
171
|
-
.map((plugin) => plugin.api?.reactBabel)
|
172
|
-
.filter(Boolean) as ReactBabelHook[]
|
173
|
-
|
174
|
-
if (hooks.length > 0) {
|
175
|
-
return (runPluginOverrides = (babelOptions, context) => {
|
176
|
-
hooks.forEach((hook) => hook(babelOptions, context, config))
|
177
|
-
return true
|
178
|
-
})(babelOptions, context)
|
179
|
-
}
|
180
|
-
runPluginOverrides = () => false
|
181
|
-
return false
|
182
|
-
}
|
183
|
-
},
|
184
|
-
async transform(code, id, options) {
|
185
|
-
const ssr = options?.ssr === true
|
186
|
-
// File extension could be mocked/overridden in querystring.
|
187
|
-
const [filepath, querystring = ''] = id.split('?')
|
188
|
-
const [extension = ''] =
|
189
|
-
querystring.match(fileExtensionRE) ||
|
190
|
-
filepath.match(fileExtensionRE) ||
|
191
|
-
[]
|
192
|
-
|
193
|
-
if (/\.(mjs|[tj]sx?)$/.test(extension)) {
|
194
|
-
const isJSX = extension.endsWith('x')
|
195
|
-
const isNodeModules = id.includes('/node_modules/')
|
196
|
-
const isProjectFile =
|
197
|
-
!isNodeModules && (id[0] === '\0' || id.startsWith(projectRoot + '/'))
|
198
|
-
|
199
|
-
let babelOptions = staticBabelOptions
|
200
|
-
if (typeof opts.babel === 'function') {
|
201
|
-
const rawOptions = opts.babel(id, { ssr })
|
202
|
-
babelOptions = createBabelOptions(rawOptions)
|
203
|
-
runPluginOverrides(babelOptions, { ssr, id: id })
|
204
|
-
} else if (!babelOptions) {
|
205
|
-
babelOptions = createBabelOptions(opts.babel)
|
206
|
-
if (!runPluginOverrides(babelOptions, { ssr, id: id })) {
|
207
|
-
staticBabelOptions = babelOptions
|
208
|
-
}
|
209
|
-
}
|
210
|
-
|
211
|
-
const plugins = isProjectFile ? [...babelOptions.plugins] : []
|
212
|
-
|
213
|
-
let useFastRefresh = false
|
214
|
-
if (!skipFastRefresh && !ssr && !isNodeModules) {
|
215
|
-
// Modules with .js or .ts extension must import React.
|
216
|
-
const isReactModule = isJSX || importReactRE.test(code)
|
217
|
-
if (isReactModule && filter(id)) {
|
218
|
-
useFastRefresh = true
|
219
|
-
plugins.push([
|
220
|
-
await loadPlugin('react-refresh/babel'),
|
221
|
-
{ skipEnvCheck: true }
|
222
|
-
])
|
223
|
-
}
|
224
|
-
}
|
225
|
-
|
226
|
-
let ast: t.File | null | undefined
|
227
|
-
let prependReactImport = false
|
228
|
-
if (!isProjectFile || isJSX) {
|
229
|
-
if (useAutomaticRuntime) {
|
230
|
-
// By reverse-compiling "React.createElement" calls into JSX,
|
231
|
-
// React elements provided by dependencies will also use the
|
232
|
-
// automatic runtime!
|
233
|
-
// Avoid parsing the optimized react-dom since it will never
|
234
|
-
// contain compiled JSX and it's a pretty big file (800kb).
|
235
|
-
const isOptimizedReactDom =
|
236
|
-
id.startsWith(resolvedCacheDir) && id.includes('/react-dom.js')
|
237
|
-
const [restoredAst, isCommonJS] =
|
238
|
-
!isProjectFile && !isJSX && !isOptimizedReactDom
|
239
|
-
? await restoreJSX(babel, code, id)
|
240
|
-
: [null, false]
|
241
|
-
|
242
|
-
if (isJSX || (ast = restoredAst)) {
|
243
|
-
plugins.push([
|
244
|
-
await loadPlugin(
|
245
|
-
'@babel/plugin-transform-react-jsx' +
|
246
|
-
(isProduction ? '' : '-development')
|
247
|
-
),
|
248
|
-
{
|
249
|
-
runtime: 'automatic',
|
250
|
-
importSource: opts.jsxImportSource,
|
251
|
-
pure: opts.jsxPure !== false
|
252
|
-
}
|
253
|
-
])
|
254
|
-
|
255
|
-
// Avoid inserting `import` statements into CJS modules.
|
256
|
-
if (isCommonJS) {
|
257
|
-
plugins.push(babelImportToRequire)
|
258
|
-
}
|
259
|
-
}
|
260
|
-
} else if (isProjectFile) {
|
261
|
-
// These plugins are only needed for the classic runtime.
|
262
|
-
if (!isProduction) {
|
263
|
-
plugins.push(
|
264
|
-
await loadPlugin('@babel/plugin-transform-react-jsx-self'),
|
265
|
-
await loadPlugin('@babel/plugin-transform-react-jsx-source')
|
266
|
-
)
|
267
|
-
}
|
268
|
-
|
269
|
-
// Even if the automatic JSX runtime is not used, we can still
|
270
|
-
// inject the React import for .jsx and .tsx modules.
|
271
|
-
if (!skipReactImport && !importReactRE.test(code)) {
|
272
|
-
prependReactImport = true
|
273
|
-
}
|
274
|
-
}
|
275
|
-
}
|
276
|
-
|
277
|
-
let inputMap: SourceMap | undefined
|
278
|
-
if (prependReactImport) {
|
279
|
-
if (needHiresSourcemap) {
|
280
|
-
const s = new MagicString(code)
|
281
|
-
s.prepend(prependReactImportCode)
|
282
|
-
code = s.toString()
|
283
|
-
inputMap = s.generateMap({ hires: true, source: id })
|
284
|
-
} else {
|
285
|
-
code = prependReactImportCode + code
|
286
|
-
}
|
287
|
-
}
|
288
|
-
|
289
|
-
// Plugins defined through this Vite plugin are only applied
|
290
|
-
// to modules within the project root, but "babel.config.js"
|
291
|
-
// files can define plugins that need to be applied to every
|
292
|
-
// module, including node_modules and linked packages.
|
293
|
-
const shouldSkip =
|
294
|
-
!plugins.length &&
|
295
|
-
!babelOptions.configFile &&
|
296
|
-
!(isProjectFile && babelOptions.babelrc)
|
297
|
-
|
298
|
-
// Avoid parsing if no plugins exist.
|
299
|
-
if (shouldSkip) {
|
300
|
-
return {
|
301
|
-
code,
|
302
|
-
map: inputMap ?? null
|
303
|
-
}
|
304
|
-
}
|
305
|
-
|
306
|
-
const parserPlugins: typeof babelOptions.parserOpts.plugins = [
|
307
|
-
...babelOptions.parserOpts.plugins,
|
308
|
-
'importMeta',
|
309
|
-
// This plugin is applied before esbuild transforms the code,
|
310
|
-
// so we need to enable some stage 3 syntax that is supported in
|
311
|
-
// TypeScript and some environments already.
|
312
|
-
'topLevelAwait',
|
313
|
-
'classProperties',
|
314
|
-
'classPrivateProperties',
|
315
|
-
'classPrivateMethods'
|
316
|
-
]
|
317
|
-
|
318
|
-
if (!extension.endsWith('.ts')) {
|
319
|
-
parserPlugins.push('jsx')
|
320
|
-
}
|
321
|
-
|
322
|
-
if (/\.tsx?$/.test(extension)) {
|
323
|
-
parserPlugins.push('typescript')
|
324
|
-
}
|
325
|
-
|
326
|
-
const transformAsync = ast
|
327
|
-
? babel.transformFromAstAsync.bind(babel, ast, code)
|
328
|
-
: babel.transformAsync.bind(babel, code)
|
329
|
-
|
330
|
-
const isReasonReact = extension.endsWith('.bs.js')
|
331
|
-
const result = await transformAsync({
|
332
|
-
...babelOptions,
|
333
|
-
ast: !isReasonReact,
|
334
|
-
root: projectRoot,
|
335
|
-
filename: id,
|
336
|
-
sourceFileName: filepath,
|
337
|
-
parserOpts: {
|
338
|
-
...babelOptions.parserOpts,
|
339
|
-
sourceType: 'module',
|
340
|
-
allowAwaitOutsideFunction: true,
|
341
|
-
plugins: parserPlugins
|
342
|
-
},
|
343
|
-
generatorOpts: {
|
344
|
-
...babelOptions.generatorOpts,
|
345
|
-
decoratorsBeforeExport: true
|
346
|
-
},
|
347
|
-
plugins,
|
348
|
-
sourceMaps: true,
|
349
|
-
// Vite handles sourcemap flattening
|
350
|
-
inputSourceMap: inputMap ?? (false as any)
|
351
|
-
})
|
352
|
-
|
353
|
-
if (result) {
|
354
|
-
let code = result.code!
|
355
|
-
if (useFastRefresh && /\$RefreshReg\$\(/.test(code)) {
|
356
|
-
const accept = isReasonReact || isRefreshBoundary(result.ast!)
|
357
|
-
code = addRefreshWrapper(code, id, accept)
|
358
|
-
}
|
359
|
-
return {
|
360
|
-
code,
|
361
|
-
map: result.map
|
362
|
-
}
|
363
|
-
}
|
364
|
-
}
|
365
|
-
}
|
366
|
-
}
|
367
|
-
|
368
|
-
const viteReactRefresh: Plugin = {
|
369
|
-
name: 'vite:react-refresh',
|
370
|
-
enforce: 'pre',
|
371
|
-
config: () => ({
|
372
|
-
resolve: {
|
373
|
-
dedupe: ['react', 'react-dom']
|
374
|
-
}
|
375
|
-
}),
|
376
|
-
resolveId(id) {
|
377
|
-
if (id === runtimePublicPath) {
|
378
|
-
return id
|
379
|
-
}
|
380
|
-
},
|
381
|
-
load(id) {
|
382
|
-
if (id === runtimePublicPath) {
|
383
|
-
return runtimeCode
|
384
|
-
}
|
385
|
-
},
|
386
|
-
transformIndexHtml() {
|
387
|
-
if (!skipFastRefresh)
|
388
|
-
return [
|
389
|
-
{
|
390
|
-
tag: 'script',
|
391
|
-
attrs: { type: 'module' },
|
392
|
-
children: preambleCode.replace(`__BASE__`, devBase)
|
393
|
-
}
|
394
|
-
]
|
395
|
-
}
|
396
|
-
}
|
397
|
-
|
398
|
-
const reactJsxRuntimeId = 'react/jsx-runtime'
|
399
|
-
const reactJsxDevRuntimeId = 'react/jsx-dev-runtime'
|
400
|
-
const virtualReactJsxRuntimeId = '\0' + reactJsxRuntimeId
|
401
|
-
const virtualReactJsxDevRuntimeId = '\0' + reactJsxDevRuntimeId
|
402
|
-
// Adapted from https://github.com/alloc/vite-react-jsx
|
403
|
-
const viteReactJsx: Plugin = {
|
404
|
-
name: 'vite:react-jsx',
|
405
|
-
enforce: 'pre',
|
406
|
-
config() {
|
407
|
-
return {
|
408
|
-
optimizeDeps: {
|
409
|
-
// We can't add `react-dom` because the dependency is `react-dom/client`
|
410
|
-
// for React 18 while it's `react-dom` for React 17. We'd need to detect
|
411
|
-
// what React version the user has installed.
|
412
|
-
include: [reactJsxRuntimeId, reactJsxDevRuntimeId, 'react']
|
413
|
-
}
|
414
|
-
}
|
415
|
-
},
|
416
|
-
resolveId(id, importer) {
|
417
|
-
// Resolve runtime to a virtual path to be interoped.
|
418
|
-
// Since the interop code re-imports `id`, we need to prevent re-resolving
|
419
|
-
// to the virtual id if the importer is already the virtual id.
|
420
|
-
if (id === reactJsxRuntimeId && importer !== virtualReactJsxRuntimeId) {
|
421
|
-
return virtualReactJsxRuntimeId
|
422
|
-
}
|
423
|
-
if (
|
424
|
-
id === reactJsxDevRuntimeId &&
|
425
|
-
importer !== virtualReactJsxDevRuntimeId
|
426
|
-
) {
|
427
|
-
return virtualReactJsxDevRuntimeId
|
428
|
-
}
|
429
|
-
},
|
430
|
-
load(id) {
|
431
|
-
// Apply manual interop
|
432
|
-
if (id === virtualReactJsxRuntimeId) {
|
433
|
-
return [
|
434
|
-
`import * as jsxRuntime from ${JSON.stringify(reactJsxRuntimeId)}`,
|
435
|
-
`export const Fragment = jsxRuntime.Fragment`,
|
436
|
-
`export const jsx = jsxRuntime.jsx`,
|
437
|
-
`export const jsxs = jsxRuntime.jsxs`
|
438
|
-
].join('\n')
|
439
|
-
}
|
440
|
-
if (id === virtualReactJsxDevRuntimeId) {
|
441
|
-
return [
|
442
|
-
`import * as jsxRuntime from ${JSON.stringify(reactJsxDevRuntimeId)}`,
|
443
|
-
`export const Fragment = jsxRuntime.Fragment`,
|
444
|
-
`export const jsxDEV = jsxRuntime.jsxDEV`
|
445
|
-
].join('\n')
|
446
|
-
}
|
447
|
-
}
|
448
|
-
}
|
449
|
-
|
450
|
-
return [viteBabel, viteReactRefresh, useAutomaticRuntime && viteReactJsx]
|
451
|
-
}
|
452
|
-
|
453
|
-
viteReact.preambleCode = preambleCode
|
454
|
-
|
455
|
-
function loadPlugin(path: string): Promise<any> {
|
456
|
-
return import(path).then((module) => module.default || module)
|
457
|
-
}
|
458
|
-
|
459
|
-
function createBabelOptions(rawOptions?: BabelOptions) {
|
460
|
-
const babelOptions = {
|
461
|
-
babelrc: false,
|
462
|
-
configFile: false,
|
463
|
-
...rawOptions
|
464
|
-
} as ReactBabelOptions
|
465
|
-
|
466
|
-
babelOptions.plugins ||= []
|
467
|
-
babelOptions.presets ||= []
|
468
|
-
babelOptions.overrides ||= []
|
469
|
-
babelOptions.parserOpts ||= {} as any
|
470
|
-
babelOptions.parserOpts.plugins ||= []
|
471
|
-
|
472
|
-
return babelOptions
|
473
|
-
}
|
@@ -1,35 +0,0 @@
|
|
1
|
-
import type * as babelCore from '@babel/core'
|
2
|
-
|
3
|
-
/**
|
4
|
-
* Replace this:
|
5
|
-
*
|
6
|
-
* import { jsx as _jsx } from "react/jsx-runtime"
|
7
|
-
*
|
8
|
-
* with this:
|
9
|
-
*
|
10
|
-
* var _jsx = require("react/jsx-runtime").jsx
|
11
|
-
*/
|
12
|
-
export function babelImportToRequire({ types: t }: typeof babelCore): {
|
13
|
-
visitor: babelCore.Visitor
|
14
|
-
} {
|
15
|
-
return {
|
16
|
-
visitor: {
|
17
|
-
ImportDeclaration(path) {
|
18
|
-
const decl = path.node
|
19
|
-
const spec = decl.specifiers[0] as babelCore.types.ImportSpecifier
|
20
|
-
|
21
|
-
path.replaceWith(
|
22
|
-
t.variableDeclaration('var', [
|
23
|
-
t.variableDeclarator(
|
24
|
-
spec.local,
|
25
|
-
t.memberExpression(
|
26
|
-
t.callExpression(t.identifier('require'), [decl.source]),
|
27
|
-
spec.imported
|
28
|
-
)
|
29
|
-
)
|
30
|
-
])
|
31
|
-
)
|
32
|
-
}
|
33
|
-
}
|
34
|
-
}
|
35
|
-
}
|
@@ -1,132 +0,0 @@
|
|
1
|
-
import * as babel from '@babel/core'
|
2
|
-
import { describe, expect, it } from 'vitest'
|
3
|
-
import babelRestoreJSX from './babel-restore-jsx'
|
4
|
-
|
5
|
-
function jsx(code: string) {
|
6
|
-
return babel.transform(code, {
|
7
|
-
parserOpts: { plugins: ['jsx'] },
|
8
|
-
plugins: [babelRestoreJSX]
|
9
|
-
})?.code
|
10
|
-
}
|
11
|
-
|
12
|
-
// Tests adapted from: https://github.com/flying-sheep/babel-plugin-transform-react-createelement-to-jsx/blob/63137b6/test/index.js
|
13
|
-
describe('babel-restore-jsx', () => {
|
14
|
-
it('should convert 1-argument calls', () => {
|
15
|
-
expect(jsx('React.createElement("h1")')).toMatchInlineSnapshot(`"<h1 />;"`)
|
16
|
-
expect(jsx('React.createElement(Foo)')).toMatchInlineSnapshot(`"<Foo />;"`)
|
17
|
-
expect(jsx('React.createElement(Foo.Bar)')).toMatchInlineSnapshot(
|
18
|
-
`"<Foo.Bar />;"`
|
19
|
-
)
|
20
|
-
expect(jsx('React.createElement(Foo.Bar.Baz)')).toMatchInlineSnapshot(
|
21
|
-
`"<Foo.Bar.Baz />;"`
|
22
|
-
)
|
23
|
-
})
|
24
|
-
|
25
|
-
it('should convert effective 1-argument calls (with null or undefined)', () => {
|
26
|
-
expect(jsx('React.createElement("h1", null)')).toMatchInlineSnapshot(
|
27
|
-
`"<h1 />;"`
|
28
|
-
)
|
29
|
-
expect(jsx('React.createElement("h2", null, null)')).toMatchInlineSnapshot(
|
30
|
-
`"<h2 />;"`
|
31
|
-
)
|
32
|
-
expect(jsx('React.createElement("h3", undefined)')).toMatchInlineSnapshot(
|
33
|
-
`"<h3 />;"`
|
34
|
-
)
|
35
|
-
})
|
36
|
-
|
37
|
-
it('should handle props without children', () => {
|
38
|
-
expect(jsx('React.createElement("h1", {hi: there})')).toMatchInlineSnapshot(
|
39
|
-
`"<h1 hi={there} />;"`
|
40
|
-
)
|
41
|
-
expect(
|
42
|
-
jsx('React.createElement("h2", {"hi": there})')
|
43
|
-
).toMatchInlineSnapshot(`"<h2 hi={there} />;"`)
|
44
|
-
expect(
|
45
|
-
jsx('React.createElement("h3", {hi: "there"})')
|
46
|
-
).toMatchInlineSnapshot(`"<h3 hi=\\"there\\" />;"`)
|
47
|
-
})
|
48
|
-
|
49
|
-
it('should handle spread props', () => {
|
50
|
-
expect(jsx('React.createElement("h1", props)')).toMatchInlineSnapshot(
|
51
|
-
`"<h1 {...props} />;"`
|
52
|
-
)
|
53
|
-
expect(jsx('React.createElement("h1", getProps())')).toMatchInlineSnapshot(
|
54
|
-
`"<h1 {...getProps()} />;"`
|
55
|
-
)
|
56
|
-
})
|
57
|
-
|
58
|
-
it('should handle mixed props', () => {
|
59
|
-
expect(
|
60
|
-
jsx('React.createElement("h1", _extends({ hi: "there" }, props))')
|
61
|
-
).toMatchInlineSnapshot(`"<h1 hi=\\"there\\" {...props} />;"`)
|
62
|
-
expect(
|
63
|
-
jsx('React.createElement("h1", _extends({}, props, { hi: "there" }))')
|
64
|
-
).toMatchInlineSnapshot(`"<h1 {...props} hi=\\"there\\" />;"`)
|
65
|
-
expect(
|
66
|
-
jsx('React.createElement("h1", { ...props, hi: "there" })')
|
67
|
-
).toMatchInlineSnapshot(`"<h1 {...props} hi=\\"there\\" />;"`)
|
68
|
-
})
|
69
|
-
|
70
|
-
it('should handle props and ignore “null”/“undefined” children', () => {
|
71
|
-
expect(
|
72
|
-
jsx('React.createElement("h1", {hi: there}, null, undefined)')
|
73
|
-
).toMatchInlineSnapshot(`"<h1 hi={there} />;"`)
|
74
|
-
})
|
75
|
-
|
76
|
-
it('should ignore “null”/“undefined” props and handle children', () => {
|
77
|
-
expect(
|
78
|
-
jsx('React.createElement("h1", null, "Header")')
|
79
|
-
).toMatchInlineSnapshot(`"<h1>Header</h1>;"`)
|
80
|
-
//this can be created from e.g. '<h2>Header{"harhar"}</h2>', but i think there’s no downside to merging it
|
81
|
-
expect(
|
82
|
-
jsx('React.createElement("h2", null, "Header", "harhar")')
|
83
|
-
).toMatchInlineSnapshot(`"<h2>Headerharhar</h2>;"`)
|
84
|
-
expect(
|
85
|
-
jsx('React.createElement("h3", null, React.createElement("i"))')
|
86
|
-
).toMatchInlineSnapshot(`"<h3><i /></h3>;"`)
|
87
|
-
expect(
|
88
|
-
jsx('React.createElement("h4", null, "a", React.createElement("b"), "c")')
|
89
|
-
).toMatchInlineSnapshot(`"<h4>a<b />c</h4>;"`)
|
90
|
-
})
|
91
|
-
|
92
|
-
it('should handle props and children', () => {
|
93
|
-
//we extensively tested props and children separately, so only sth. basic
|
94
|
-
expect(
|
95
|
-
jsx('React.createElement("h1", {hi: there}, "Header")')
|
96
|
-
).toMatchInlineSnapshot(`"<h1 hi={there}>Header</h1>;"`)
|
97
|
-
})
|
98
|
-
|
99
|
-
it('should ignore intermingled “null”/“undefined” children', () => {
|
100
|
-
expect(
|
101
|
-
jsx('React.createElement("h1", null, null, "Header", undefined)')
|
102
|
-
).toMatchInlineSnapshot(`"<h1>Header</h1>;"`)
|
103
|
-
})
|
104
|
-
|
105
|
-
it('should handle children in nested expressions', () => {
|
106
|
-
expect(
|
107
|
-
jsx(
|
108
|
-
'React.createElement("h1", null, foo ? React.createElement("p") : null)'
|
109
|
-
)
|
110
|
-
).toMatchInlineSnapshot(`"<h1>{foo ? <p /> : null}</h1>;"`)
|
111
|
-
})
|
112
|
-
|
113
|
-
it('should handle lowercase component names', () => {
|
114
|
-
expect(jsx('React.createElement(aaa)')).toMatchInlineSnapshot(
|
115
|
-
`"React.createElement(aaa);"`
|
116
|
-
)
|
117
|
-
})
|
118
|
-
|
119
|
-
it('should not handle contains __self prop', () => {
|
120
|
-
expect(
|
121
|
-
jsx('React.createElement(Provider, { __self: this })')
|
122
|
-
).toMatchInlineSnapshot('"<Provider />;"')
|
123
|
-
})
|
124
|
-
|
125
|
-
it('should not handle contains __source prop', () => {
|
126
|
-
expect(
|
127
|
-
jsx(
|
128
|
-
'React.createElement(Provider, { __source: { fileName: _jsxFileName, lineNumber: 133 }})'
|
129
|
-
)
|
130
|
-
).toMatchInlineSnapshot('"<Provider />;"')
|
131
|
-
})
|
132
|
-
})
|