Skip to content

Commit b61ddd1

Browse files
authored
Merge pull request #306 from LeetCode-OpenSource/emotion-11-ts-4.1
feat: support emotion@11 and [email protected]
2 parents 57cf5f6 + 4650a11 commit b61ddd1

25 files changed

+1631
-3466
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,4 @@ typings/
7373
.serverless
7474

7575
lib/
76+
.vscode

README.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ module.exports = {
108108
module: {
109109
rules: [
110110
{
111-
test: /\.(jsx|tsx|js|ts)$/,
111+
test: /\.(j|t)sx?$/,
112112
loader: 'ts-loader',
113113
options: {
114114
transpileOnly: true,
@@ -117,12 +117,19 @@ module.exports = {
117117
sourcemap: true,
118118
autoLabel: true,
119119
labelFormat: '[local]',
120-
autoInject: true, //if the jsxFactory is set, should we auto insert the import statement
120+
// if the jsxFactory is set, should we auto insert the import statement
121+
autoInject: true,
122+
// set for react@17 new jsx runtime
123+
// only effect if `autoInject` is true
124+
// set it in createEmotionPlugin options rather than in `tsconfig.json` will generate more optimized codes:
125+
// import { jsx } from 'react/jsx-runtime' for files not using emotion
126+
// import { jsx } from '@emotion/react/jsx-runtime' for files using emotion
127+
jsxImportSource: "@emotion/react",
121128
})],
122129
}),
123130
compilerOptions: {
124-
// set jsx pragma to jsx or alias which is from the @emotion/core package to enable css property in jsx component
125-
jsxFactory: "jsx"
131+
// set jsx pragma to jsx or alias which is from the @emotion/react package to enable css property in jsx component
132+
jsxFactory: "jsx",
126133
}
127134
},
128135
exclude: /node_modules/,

package.json

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,53 +11,51 @@
1111
"build": "rm -rf lib && tsc -p tsconfig.json --outDir lib --diagnostics",
1212
"dev": "rm -rf lib && tsc -p tsconfig.json --outDir lib --diagnostics -w",
1313
"lint": "tslint -c tslint.json -p tsconfig.json",
14-
"start": "webpack-dev-server --progress --color",
14+
"start": "webpack serve --progress --color",
1515
"test": "jest --no-cache --ci"
1616
},
1717
"devDependencies": {
18-
"@emotion/core": "^10.0.22",
19-
"@emotion/styled": "^10.0.23",
18+
"@emotion/react": "^11.1.1",
19+
"@emotion/styled": "^11.0.0",
2020
"@types/convert-source-map": "^1.5.1",
21-
"@types/find-root": "^1.1.1",
22-
"@types/jest": "^26.0.0",
23-
"@types/lodash": "^4.14.146",
24-
"@types/react-dom": "^16.9.4",
25-
"emotion": "^11.0.0",
26-
"html-webpack-plugin": "^4.0.1",
27-
"husky": "^4.0.0",
28-
"jest": "^26.6.0",
21+
"@types/find-root": "^1.1.2",
22+
"@types/jest": "^26.0.15",
23+
"@types/lodash": "^4.14.165",
24+
"@types/react-dom": "^17.0.0",
25+
"html-webpack-plugin": "^4.5.0",
26+
"husky": "^4.3.0",
27+
"jest": "^26.6.3",
2928
"jest-specific-snapshot": "^4.0.0",
30-
"lint-staged": "^10.0.1",
31-
"mini-css-extract-plugin": "^1.0.0",
32-
"prettier": "^2.0.1",
29+
"lint-staged": "^10.5.1",
30+
"mini-css-extract-plugin": "^1.3.1",
31+
"prettier": "^2.2.0",
3332
"prop-types": "^15.7.2",
3433
"react": "^17.0.1",
3534
"react-dom": "^17.0.1",
36-
"ts-jest": "^26.4.1",
37-
"ts-loader": "^8.0.0",
35+
"ts-jest": "^26.4.4",
36+
"ts-loader": "^8.0.11",
3837
"tslint": "^5.20.1",
3938
"tslint-config-prettier": "^1.18.0",
4039
"tslint-eslint-rules": "^5.4.0",
41-
"typescript": "^3.7.2",
42-
"webpack": "^5.0.0",
43-
"webpack-cli": "^4.0.0",
44-
"webpack-dev-server": "^3.9.0"
40+
"typescript": "^4.1.2",
41+
"webpack": "^5.6.0",
42+
"webpack-cli": "^4.2.0"
4543
},
4644
"dependencies": {
4745
"@emotion/hash": "^0.8.0",
4846
"convert-source-map": "^1.7.0",
4947
"find-root": "^1.1.0",
50-
"lodash": "^4.17.15",
48+
"lodash": "^4.17.20",
5149
"source-map": "^0.7.3",
52-
"tslib": "^2.0.0"
50+
"tslib": "^2.0.3"
5351
},
5452
"files": [
5553
"lib/**"
5654
],
5755
"jest": {
5856
"globals": {
5957
"ts-jest": {
60-
"tsConfig": "tests/tsconfig.json"
58+
"tsconfig": "tests/tsconfig.json"
6159
}
6260
},
6361
"transform": {
@@ -94,4 +92,4 @@
9492
"pre-commit": "lint-staged"
9593
}
9694
}
97-
}
95+
}

src/index.ts

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,19 @@ export interface Options {
2626
labelFormat?: string
2727
autoInject?: boolean
2828
customModules?: ModuleConfig[]
29+
jsxFactory?: string
30+
jsxImportSource?: string
2931
}
3032

3133
const defaultEmotionModules: ModuleConfig[] = [
32-
{
33-
moduleName: 'emotion',
34-
exportedNames: ['css', 'keyframes', 'injectGlobal', 'cx', 'merge'],
35-
},
3634
{
3735
moduleName: '@emotion/styled',
3836
exportedNames: ['styled'],
3937
hasDefaultExport: true,
4038
styledName: 'styled',
4139
},
4240
{
43-
moduleName: '@emotion/core',
41+
moduleName: '@emotion/react',
4442
exportedNames: ['css'],
4543
},
4644
]
@@ -56,20 +54,24 @@ const getPackageRootPath = memoize((filename: string) => findRoot(filename))
5654
const hashArray = (arr: Array<string>) => hashString(arr.join(''))
5755

5856
const createImportJSXAst = memoize((propertyName: string | undefined) => {
59-
const importClause = ts.createImportClause(
57+
const importClause = ts.factory.createImportClause(
58+
false,
6059
undefined,
61-
ts.createNamedImports([
60+
ts.factory.createNamedImports([
6261
propertyName
63-
? ts.createImportSpecifier(
64-
ts.createIdentifier('jsx'),
65-
ts.createIdentifier(propertyName),
62+
? ts.factory.createImportSpecifier(
63+
ts.factory.createIdentifier('jsx'),
64+
ts.factory.createIdentifier(propertyName),
6665
)
67-
: ts.createImportSpecifier(undefined, ts.createIdentifier('jsx')),
66+
: ts.factory.createImportSpecifier(
67+
undefined,
68+
ts.factory.createIdentifier('jsx'),
69+
),
6870
]),
6971
)
70-
const moduleSpecifier = ts.createStringLiteral('@emotion/core')
72+
const moduleSpecifier = ts.factory.createStringLiteral('@emotion/react')
7173

72-
return ts.createImportDeclaration(
74+
return ts.factory.createImportDeclaration(
7375
undefined,
7476
undefined,
7577
importClause,
@@ -162,20 +164,21 @@ export const createEmotionPlugin = (pluginOptions?: Options) => {
162164
let sourceFile: ts.SourceFile
163165
let inserted = false
164166
const visitor: ts.Visitor = (node) => {
165-
if (ts.isSourceFile(node)) {
166-
inserted = false
167-
return ts.visitEachChild(node, visitor, context)
168-
}
169167
if (ts.isImportDeclaration(node)) {
170168
importCalls = importCalls.concat(getImportCalls(node, compilerOptions))
171-
// insert import { jsx [as jsxFactory] } from '@emotion/core' behind the react import declaration
169+
// insert import { jsx [as jsxFactory] } from '@emotion/react' behind the react import declaration
172170
if (
173171
!inserted &&
174172
options.autoInject &&
175173
(<ts.StringLiteral>node.moduleSpecifier).text === 'react'
176174
) {
177175
inserted = true
178-
return [createImportJSXAst(compilerOptions.jsxFactory), node]
176+
return [
177+
createImportJSXAst(
178+
options?.jsxFactory ?? compilerOptions.jsxFactory,
179+
),
180+
node,
181+
]
179182
}
180183
return node
181184
}
@@ -208,10 +211,10 @@ export const createEmotionPlugin = (pluginOptions?: Options) => {
208211
)
209212
})
210213
if (info) {
211-
expression = ts.createCall(
214+
expression = ts.factory.createCallExpression(
212215
expression.expression,
213216
[],
214-
[ts.createStringLiteral(expression.name.text)],
217+
[ts.factory.createStringLiteral(expression.name.text)],
215218
)
216219
}
217220
}
@@ -248,28 +251,28 @@ export const createEmotionPlugin = (pluginOptions?: Options) => {
248251
stuffToHash,
249252
)}${positionInFile}`
250253
const [el, opts] = exp.arguments
251-
const targetAssignment = ts.createPropertyAssignment(
252-
ts.createIdentifier('target'),
253-
ts.createStringLiteral(stableClassName),
254+
const targetAssignment = ts.factory.createPropertyAssignment(
255+
ts.factory.createIdentifier('target'),
256+
ts.factory.createStringLiteral(stableClassName),
254257
)
255258
const args = [el]
256259
args.push(
257-
ts.createObjectLiteral(
260+
ts.factory.createObjectLiteralExpression(
258261
opts && ts.isObjectLiteralExpression(opts)
259262
? opts.properties.concat(targetAssignment)
260263
: [targetAssignment],
261264
true,
262265
),
263266
)
264267

265-
const updatedCall = ts.updateCall(
268+
const updatedCall = ts.factory.updateCallExpression(
266269
exp,
267270
exp.expression,
268271
exp.typeArguments,
269272
args,
270273
)
271274

272-
return ts.updateCall(
275+
return ts.factory.updateCallExpression(
273276
transformedNode,
274277
updatedCall,
275278
transformedNode.typeArguments,
@@ -300,12 +303,12 @@ export const createEmotionPlugin = (pluginOptions?: Options) => {
300303
if (localNameNode && ts.isIdentifier(localNameNode)) {
301304
const local = localNameNode.text
302305
const fileName = basename(rawPath, extname(rawPath))
303-
transformedNode = ts.updateCall(
306+
transformedNode = ts.factory.updateCallExpression(
304307
transformedNode,
305308
transformedNode.expression,
306309
transformedNode.typeArguments,
307310
transformedNode.arguments.concat([
308-
ts.createStringLiteral(
311+
ts.factory.createStringLiteral(
309312
`label:${options
310313
.labelFormat!.replace('[local]', local)
311314
.replace('[filename]', fileName)};`,
@@ -345,12 +348,12 @@ export const createEmotionPlugin = (pluginOptions?: Options) => {
345348
const comment = convert
346349
.fromObject(sourcemapGenerator)
347350
.toComment({ multiline: true })
348-
transformedNode = ts.updateCall(
351+
transformedNode = ts.factory.updateCallExpression(
349352
transformedNode,
350353
transformedNode.expression,
351354
transformedNode.typeArguments,
352355
transformedNode.arguments.concat([
353-
ts.createStringLiteral(comment),
356+
ts.factory.createStringLiteral(comment),
354357
]),
355358
)
356359
}
@@ -375,7 +378,19 @@ export const createEmotionPlugin = (pluginOptions?: Options) => {
375378
sourceRoot: '',
376379
})
377380
const distNode = ts.visitNode(node, visitor)
381+
if (inserted && options.jsxImportSource && distNode.statements.length) {
382+
// fIXME
383+
// typeScript private API https://github.com/microsoft/TypeScript/pull/39199/files#diff-1516c8349f7a625a2e4a2aa60f6bbe84e4b1a499128e8705d3087d893e01d367R5974
384+
// @ts-expect-error
385+
distNode.pragmas.set('jsximportsource', {
386+
arguments: {
387+
factory: options.jsxImportSource,
388+
},
389+
})
390+
}
378391
importCalls = []
392+
inserted = false
393+
emotionTargetClassNameCount = 0
379394
return distNode
380395
}
381396
}

tests/__snapshots__/component-as-selector.tsx.shot

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ TypeScript before transform:
6161
↓ ↓ ↓ ↓ ↓ ↓
6262

6363
TypeScript after transform:
64-
import { jsx } from "@emotion/core";
64+
import { jsx } from "@emotion/react";
6565
import React from 'react';
6666
import ReactDOM from 'react-dom';
6767
import styled from '@emotion/styled';
@@ -170,7 +170,7 @@ TypeScript before transform:
170170
↓ ↓ ↓ ↓ ↓ ↓
171171

172172
TypeScript after transform:
173-
import { jsx } from "@emotion/core";
173+
import { jsx } from "@emotion/react";
174174
import React from 'react';
175175
import ReactDOM from 'react-dom';
176176
import styled from '@emotion/styled';
@@ -279,7 +279,7 @@ TypeScript before transform:
279279
↓ ↓ ↓ ↓ ↓ ↓
280280

281281
TypeScript after transform:
282-
import { jsx } from "@emotion/core";
282+
import { jsx } from "@emotion/react";
283283
import React from 'react';
284284
import ReactDOM from 'react-dom';
285285
import styled from '@emotion/styled';
@@ -388,7 +388,7 @@ TypeScript before transform:
388388
↓ ↓ ↓ ↓ ↓ ↓
389389

390390
TypeScript after transform:
391-
import { jsx } from "@emotion/core";
391+
import { jsx } from "@emotion/react";
392392
import React from 'react';
393393
import ReactDOM from 'react-dom';
394394
import styled from '@emotion/styled';
@@ -497,7 +497,7 @@ TypeScript before transform:
497497
↓ ↓ ↓ ↓ ↓ ↓
498498

499499
TypeScript after transform:
500-
import { jsx } from "@emotion/core";
500+
import { jsx } from "@emotion/react";
501501
import React from 'react';
502502
import ReactDOM from 'react-dom';
503503
import styled from '@emotion/styled';

0 commit comments

Comments
 (0)