Skip to content

Commit 8b5c8b6

Browse files
author
LongYinan
committed
fix: auto import logic when jsx: react-jsx/react-jsxdev
1 parent 258f20b commit 8b5c8b6

File tree

4 files changed

+106
-27
lines changed

4 files changed

+106
-27
lines changed

src/index.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const defaultOptions: Options = {
4848
autoLabel: true,
4949
labelFormat: '[local]',
5050
autoInject: true,
51+
jsxImportSource: '@emotion/react',
5152
}
5253

5354
const getPackageRootPath = memoize((filename: string) => findRoot(filename))
@@ -157,19 +158,21 @@ export const createEmotionPlugin = (pluginOptions?: Options) => {
157158
}
158159

159160
const transformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
160-
let importCalls: ImportInfo[] = []
161+
const importCalls: ImportInfo[] = []
161162
const compilerOptions = context.getCompilerOptions()
162163
let sourcemapGenerator: SourceMapGenerator
163164
let emotionTargetClassNameCount = 0
164165
let sourceFile: ts.SourceFile
165166
let inserted = false
166167
const visitor: ts.Visitor = (node) => {
167168
if (ts.isImportDeclaration(node)) {
168-
importCalls = importCalls.concat(getImportCalls(node, compilerOptions))
169+
importCalls.push(...getImportCalls(node, compilerOptions))
169170
// insert import { jsx [as jsxFactory] } from '@emotion/react' behind the react import declaration
170171
if (
171172
!inserted &&
172173
options.autoInject &&
174+
// only for jsx: 'react'
175+
compilerOptions.jsx === ts.JsxEmit.React &&
173176
(<ts.StringLiteral>node.moduleSpecifier).text === 'react'
174177
) {
175178
inserted = true
@@ -369,6 +372,21 @@ export const createEmotionPlugin = (pluginOptions?: Options) => {
369372
}
370373
}
371374

375+
if (
376+
ts.isJsxAttribute(node) &&
377+
node.name.text === 'css' &&
378+
(compilerOptions.jsx === ts.JsxEmit.ReactJSX ||
379+
compilerOptions.jsx === ts.JsxEmit.ReactJSXDev) &&
380+
!importCalls.find((info) => {
381+
return (
382+
info.moduleName === '@emotion/react' &&
383+
info.exportedNames.includes('jsx')
384+
)
385+
})
386+
) {
387+
inserted = true
388+
}
389+
372390
return ts.visitEachChild(node, visitor, context)
373391
}
374392
return (node) => {
@@ -388,7 +406,7 @@ export const createEmotionPlugin = (pluginOptions?: Options) => {
388406
},
389407
})
390408
}
391-
importCalls = []
409+
importCalls.length = 0
392410
inserted = false
393411
emotionTargetClassNameCount = 0
394412
return distNode
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`auto insert jsx when css attribute used 1`] = `
4+
5+
File: auto-import-test.tsx
6+
TypeScript before transform:
7+
8+
export const Button = () => {
9+
return <div css={{ '.btn-text': { color: 'red' } }} />
10+
}
11+
12+
13+
14+
↓ ↓ ↓ ↓ ↓ ↓
15+
16+
TypeScript after transform:
17+
import { jsx as _jsx } from "@emotion/react/jsx-runtime";
18+
export const Button = () => {
19+
return _jsx("div", { css: { '.btn-text': { color: 'red' } } }, void 0);
20+
};
21+
22+
23+
`;

tests/unit.spec.ts

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -64,29 +64,7 @@ fixtures
6464
)
6565
.forEach((filename) => {
6666
const sourceCode = fs.readFileSync(join(baseDir, filename), 'utf-8')
67-
68-
function transform(options?: Options): TransformBaseline {
69-
const emotion = createEmotionPlugin(options)
70-
const sourceFile = ts.createSourceFile(
71-
join(baseDir, `${filename}`),
72-
sourceCode,
73-
ts.ScriptTarget.ESNext,
74-
true,
75-
)
76-
const [transformed] = ts.transform(
77-
sourceFile,
78-
[emotion],
79-
compilerOptions,
80-
).transformed
81-
return {
82-
transformed: printer.printFile(transformed),
83-
source: printer.printFile(sourceFile),
84-
filename,
85-
type: 'transform-baseline',
86-
content: sourceCode,
87-
}
88-
}
89-
67+
const transform = transformFactory(filename, sourceCode)
9068
const pathToSnap = join(
9169
process.cwd(),
9270
'tests',
@@ -130,3 +108,63 @@ fixtures
130108
expect(result).toMatchSpecificSnapshot(pathToSnap)
131109
})
132110
})
111+
112+
it('auto insert jsx when css attribute used', () => {
113+
const sourceCode = `
114+
export const Button = () => {
115+
return <div css={{ '.btn-text': { color: 'red' } }} />
116+
}
117+
`
118+
const { outputText } = ts.transpileModule(sourceCode, {
119+
compilerOptions: {
120+
target: ts.ScriptTarget.ES2018,
121+
jsx: ts.JsxEmit.ReactJSX,
122+
module: ts.ModuleKind.ESNext,
123+
},
124+
transformers: {
125+
before: [
126+
createEmotionPlugin({
127+
autoInject: true,
128+
jsxImportSource: '@emotion/react',
129+
}),
130+
],
131+
},
132+
})
133+
expect({
134+
transformed: outputText,
135+
source: sourceCode,
136+
filename: 'auto-import-test.tsx',
137+
type: 'transform-baseline',
138+
content: sourceCode,
139+
}).toMatchSpecificSnapshot(
140+
join(process.cwd(), 'tests', '__snapshots__', `auto-import-test.shot`),
141+
)
142+
})
143+
144+
function transformFactory(
145+
filename: string,
146+
sourceCode: string,
147+
additionalCompilerOptions: ts.CompilerOptions = {},
148+
): (options?: Options) => TransformBaseline {
149+
return (options?: Options) => {
150+
const emotion = createEmotionPlugin(options)
151+
const sourceFile = ts.createSourceFile(
152+
join(baseDir, filename),
153+
sourceCode,
154+
ts.ScriptTarget.ESNext,
155+
true,
156+
)
157+
const [transformed] = ts.transform(sourceFile, [emotion], {
158+
...compilerOptions,
159+
...{ jsx: ts.JsxEmit.React, jsxImportSource: undefined },
160+
...additionalCompilerOptions,
161+
}).transformed
162+
return {
163+
transformed: printer.printFile(transformed),
164+
source: printer.printFile(sourceFile),
165+
filename,
166+
type: 'transform-baseline',
167+
content: sourceCode,
168+
}
169+
}
170+
}

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"compilerOptions": {
3-
"jsx": "react-jsxdev",
3+
"jsx": "react-jsx",
44
"jsxImportSource": "@emotion/react",
55
"allowSyntheticDefaultImports": true,
66
"declaration": true,

0 commit comments

Comments
 (0)