Skip to content

Commit 8e30b7c

Browse files
committed
fix: fixes jsx opening/closing created elements blowing up with const/let
1 parent e0828de commit 8e30b7c

File tree

11 files changed

+111
-54
lines changed

11 files changed

+111
-54
lines changed

examples/class-names-dynamic-object.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ export default {
55
title: 'class names dynamic object',
66
};
77

8-
export var objectLiteral = () => {
9-
var [color, setColor] = useState('blue');
8+
export const objectLiteral = () => {
9+
const [color, setColor] = useState('blue');
1010

1111
return (
1212
<div>

examples/class-names-static-object.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export default {
55
title: 'class names static object',
66
};
77

8-
export var objectLiteral = () => (
8+
export const objectLiteral = () => (
99
<ClassNames>
1010
{({ css }) => <div className={css({ fontSize: '30px' })}>hello world</div>}
1111
</ClassNames>

examples/css-prop-dynamic-object.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ export default {
66
title: 'css prop dynamic object',
77
};
88

9-
export var dynamicCssProp = () => {
10-
// works with var, but not let or const ????
11-
var [color, setColor] = useState('red');
9+
export const dynamicCssProp = () => {
10+
// works with const, but not let or const ????
11+
const [color, setColor] = useState('red');
1212

1313
return (
1414
<div

examples/css-prop-static-object.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22
import { jsx } from '../src';
33
import { hover } from './mixins/mixins';
44

5-
var inlineMixinFunc = () => ({
5+
const inlineMixinFunc = () => ({
66
color: 'red',
77
});
88

9-
var inlineMixinObj = {
9+
const inlineMixinObj = {
1010
color: 'green',
1111
};
1212

1313
export default {
1414
title: 'css prop static object',
1515
};
1616

17-
export var objectLiteral = () => {
17+
export const objectLiteral = () => {
1818
return <div css={{ display: 'flex', fontSize: '50px', color: 'blue' }}>Hello, world!</div>;
1919
};
2020

21-
export var objectLiteralSpreadFromFunc = () => {
21+
export const objectLiteralSpreadFromFunc = () => {
2222
return (
2323
<div
2424
css={{
@@ -32,7 +32,7 @@ export var objectLiteralSpreadFromFunc = () => {
3232
);
3333
};
3434

35-
export var objectLiteralSpreadFromObj = () => {
35+
export const objectLiteralSpreadFromObj = () => {
3636
return (
3737
<div
3838
css={{
@@ -46,7 +46,7 @@ export var objectLiteralSpreadFromObj = () => {
4646
);
4747
};
4848

49-
export var objectLiteralLocalObj = () => {
49+
export const objectLiteralLocalObj = () => {
5050
return (
5151
<div
5252
css={{
@@ -60,7 +60,7 @@ export var objectLiteralLocalObj = () => {
6060
);
6161
};
6262

63-
export var objectLiteralImportedObj = () => {
63+
export const objectLiteralImportedObj = () => {
6464
return (
6565
<div
6666
css={{

examples/styled-dynamic-object.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export default {
55
title: 'styled component dynamic object',
66
};
77

8-
var Highlight = styled.div<{ primary: string }>({
8+
const Highlight = styled.div<{ primary: string }>({
99
fontSize: '20px',
1010
color: props => props.primary,
1111
margin: '20px',
@@ -14,8 +14,8 @@ var Highlight = styled.div<{ primary: string }>({
1414
},
1515
});
1616

17-
export var objectLiteral = () => {
18-
var [color, setColor] = useState('blue');
17+
export const objectLiteral = () => {
18+
const [color, setColor] = useState('blue');
1919

2020
return (
2121
<>

examples/styled-static-object.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ export default {
55
title: 'styled component static object',
66
};
77

8-
var Thing = styled.div({
8+
const Thing = styled.div({
99
fontSize: '20px',
1010
color: 'red',
1111
});
1212

13-
export var objectLiteral = () => <Thing>hello world</Thing>;
13+
export const objectLiteral = () => <Thing>hello world</Thing>;

src/ts-transformer/__tests__/index.test.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,40 @@
1-
import { Transformer } from 'ts-transformer-testing-library';
2-
import { rawTransformers } from '../index';
1+
import * as ts from 'typescript';
2+
import rootTransformer from '../index';
33
import pkg from '../../../package.json';
44

5-
const transformer = new Transformer()
6-
.addTransformers(rawTransformers)
7-
.addMock({ name: pkg.name, content: `export const jsx: any = () => null` })
8-
.setFilePath('/index.tsx');
9-
105
describe('root transformer', () => {
116
it('should not blow up when transforming with const', () => {
7+
const transformer = rootTransformer({} as ts.Program, {});
8+
129
expect(() => {
13-
transformer.transform(
10+
ts.transpileModule(
1411
`
1512
/** @jsx jsx */
1613
import { jsx } from '${pkg.name}';
1714
const MyComponent = () => <div css={{ fontSize: '20px' }}>hello world</div>
18-
`
15+
`,
16+
{
17+
transformers: { before: transformer },
18+
compilerOptions: { module: ts.ModuleKind.ESNext, jsx: ts.JsxEmit.React },
19+
}
1920
);
2021
}).not.toThrow();
2122
});
2223

2324
it('should not blow up when transforming with var', () => {
25+
const transformer = rootTransformer({} as ts.Program, {});
26+
2427
expect(() => {
25-
transformer.transform(
28+
ts.transpileModule(
2629
`
2730
/** @jsx jsx */
2831
import { jsx } from '${pkg.name}';
2932
var MyComponent = () => <div css={{ fontSize: '20px' }}>hello world</div>
30-
`
33+
`,
34+
{
35+
transformers: { before: transformer },
36+
compilerOptions: { module: ts.ModuleKind.ESNext, jsx: ts.JsxEmit.React },
37+
}
3138
);
3239
}).not.toThrow();
3340
});

src/ts-transformer/class-names/visitors/visit-class-names-jsx-element.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export const visitClassNamesJsxElement = (
9999
selector: '',
100100
css,
101101
cssVariables,
102+
originalNode: classNamesNode,
102103
children: children as any,
103104
});
104105
};

src/ts-transformer/css-prop/visitors/visit-jsx-element-with-css-prop.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,31 @@ export const visitJsxElementWithCssProp = (
9797

9898
// Create the style element that will precede the node that had the css prop.
9999
const styleNode = ts.createJsxElement(
100-
ts.createJsxOpeningElement(ts.createIdentifier('style'), [], ts.createJsxAttributes([])),
100+
// We use setOriginalNode() here to work around createJsx not working without the original node.
101+
// See: https://github.com/microsoft/TypeScript/issues/35686
102+
ts.setOriginalNode(
103+
ts.createJsxOpeningElement(ts.createIdentifier('style'), [], ts.createJsxAttributes([])),
104+
node
105+
),
101106
[ts.createJsxText(compiledCss)],
102-
ts.createJsxClosingElement(ts.createIdentifier('style'))
107+
// We use setOriginalNode() here to work around createJsx not working without the original node.
108+
// See: https://github.com/microsoft/TypeScript/issues/35686
109+
ts.setOriginalNode(ts.createJsxClosingElement(ts.createIdentifier('style')), node)
103110
);
104111

105112
// Create a new fragment that will wrap both the style and the node we found initially.
106113
const newFragmentParent = ts.createJsxFragment(
107-
ts.createJsxOpeningFragment(),
114+
// We use setOriginalNode() here to work around createJsx not working without the original node.
115+
// See: https://github.com/microsoft/TypeScript/issues/35686
116+
ts.setOriginalNode(ts.createJsxOpeningFragment(), node),
108117
[
109118
// important that the style goes before the node
110119
styleNode,
111120
nodeToTransform,
112121
],
113-
ts.createJsxJsxClosingFragment()
122+
// We use setOriginalNode() here to work around createJsx not working without the original node.
123+
// See: https://github.com/microsoft/TypeScript/issues/35686
124+
ts.setOriginalNode(ts.createJsxJsxClosingFragment(), node)
114125
);
115126

116127
logger.log('returning fragment with style and parsed jsx element with css prop');

src/ts-transformer/styled-component/visitors/visit-styled-component.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@ export const visitObjectStyledComponent = (
1818

1919
const result = objectLiteralToCssString(objectLiteral, {}, context);
2020

21-
const newElement = createJsxElement(tagNode, {
22-
...result,
23-
children: ts.createJsxExpression(undefined, ts.createIdentifier('props.children')),
24-
});
21+
const newElement = createJsxElement(
22+
tagNode,
23+
{
24+
...result,
25+
originalNode: node,
26+
children: ts.createJsxExpression(undefined, ts.createIdentifier('props.children')),
27+
},
28+
node
29+
);
2530

2631
return ts.createArrowFunction(
2732
undefined,

src/ts-transformer/utils/create-jsx-element.tsx

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,31 @@ import { CssVariableExpressions } from '../types';
77
interface JsxElementOpts {
88
css: string;
99
cssVariables: CssVariableExpressions[];
10+
originalNode: ts.Node;
1011
selector?: string;
1112
children?: ts.JsxChild;
1213
}
1314

1415
export const createStyleFragment = ({
1516
selector = `.${nextClassName()}`,
17+
originalNode,
1618
...opts
1719
}: JsxElementOpts) => {
1820
const compiledCss = stylis(selector, opts.css);
1921

2022
// Create the style element that will precede the node that had the css prop.
2123
const styleNode = ts.createJsxElement(
22-
ts.createJsxOpeningElement(ts.createIdentifier('style'), [], ts.createJsxAttributes([])),
24+
// We use setOriginalNode() here to work around createJsx not working without the original node.
25+
// See: https://github.com/microsoft/TypeScript/issues/35686
26+
ts.setOriginalNode(
27+
ts.createJsxOpeningElement(ts.createIdentifier('style'), [], ts.createJsxAttributes([])),
28+
originalNode
29+
),
2330
// should this be text or an jsx expression?
2431
[ts.createJsxText(compiledCss)],
25-
ts.createJsxClosingElement(ts.createIdentifier('style'))
32+
// We use setOriginalNode() here to work around createJsx not working without the original node.
33+
// See: https://github.com/microsoft/TypeScript/issues/35686
34+
ts.setOriginalNode(ts.createJsxClosingElement(ts.createIdentifier('style')), originalNode)
2635
);
2736

2837
const children: ts.JsxChild[] = [
@@ -32,37 +41,57 @@ export const createStyleFragment = ({
3241

3342
// Create a new fragment that will wrap both the style and the node we found initially.
3443
const newFragmentParent = ts.createJsxFragment(
35-
ts.createJsxOpeningFragment(),
44+
// We use setOriginalNode() here to work around createJsx not working without the original node.
45+
// See: https://github.com/microsoft/TypeScript/issues/35686
46+
ts.setOriginalNode(ts.createJsxOpeningFragment(), originalNode),
3647
children.concat(opts.children ? opts.children : []),
37-
ts.createJsxJsxClosingFragment()
48+
// We use setOriginalNode() here to work around createJsx not working without the original node.
49+
// See: https://github.com/microsoft/TypeScript/issues/35686
50+
ts.setOriginalNode(ts.createJsxJsxClosingFragment(), originalNode)
3851
);
3952

4053
return newFragmentParent;
4154
};
4255

43-
export const createJsxElement = (tagNode: string, opts: JsxElementOpts) => {
56+
export const createJsxElement = (tagNode: string, opts: JsxElementOpts, originalNode: ts.Node) => {
4457
const className = nextClassName();
4558
const compiledCss = stylis(`.${className}`, opts.css);
4659

4760
// Create the style element that will precede the node that had the css prop.
4861
const styleNode = ts.createJsxElement(
49-
ts.createJsxOpeningElement(ts.createIdentifier('style'), [], ts.createJsxAttributes([])),
62+
// We use setOriginalNode() here to work around createJsx not working without the original node.
63+
// See: https://github.com/microsoft/TypeScript/issues/35686
64+
ts.setOriginalNode(
65+
ts.createJsxOpeningElement(ts.createIdentifier('style'), [], ts.createJsxAttributes([])),
66+
originalNode
67+
),
5068
// should this be text or an jsx expression?
5169
[ts.createJsxText(compiledCss)],
52-
ts.createJsxClosingElement(ts.createIdentifier('style'))
70+
// We use setOriginalNode() here to work around createJsx not working without the original node.
71+
// See: https://github.com/microsoft/TypeScript/issues/35686
72+
ts.setOriginalNode(ts.createJsxClosingElement(ts.createIdentifier('style')), originalNode)
5373
);
5474

5575
const elementNode = ts.createJsxElement(
56-
// todo: we need to set types and shit depending on props used
57-
ts.createJsxOpeningElement(
58-
ts.createIdentifier(tagNode),
59-
[],
60-
ts.createJsxAttributes([
61-
ts.createJsxAttribute(ts.createIdentifier('className'), ts.createStringLiteral(className)),
62-
])
76+
// We use setOriginalNode() here to work around createJsx not working without the original node.
77+
// See: https://github.com/microsoft/TypeScript/issues/35686
78+
ts.setOriginalNode(
79+
ts.createJsxOpeningElement(
80+
ts.createIdentifier(tagNode),
81+
[],
82+
ts.createJsxAttributes([
83+
ts.createJsxAttribute(
84+
ts.createIdentifier('className'),
85+
ts.createStringLiteral(className)
86+
),
87+
])
88+
),
89+
originalNode
6390
),
6491
opts.children ? [opts.children] : [],
65-
ts.createJsxClosingElement(ts.createIdentifier(tagNode))
92+
// We use setOriginalNode() here to work around createJsx not working without the original node.
93+
// See: https://github.com/microsoft/TypeScript/issues/35686
94+
ts.setOriginalNode(ts.createJsxClosingElement(ts.createIdentifier(tagNode)), originalNode)
6695
);
6796

6897
if (opts.cssVariables.length) {
@@ -89,13 +118,17 @@ export const createJsxElement = (tagNode: string, opts: JsxElementOpts) => {
89118

90119
// Create a new fragment that will wrap both the style and the node we found initially.
91120
const newFragmentParent = ts.createJsxFragment(
92-
ts.createJsxOpeningFragment(),
121+
// We use setOriginalNode() here to work around createJsx not working without the original node.
122+
// See: https://github.com/microsoft/TypeScript/issues/35686
123+
ts.setOriginalNode(ts.createJsxOpeningFragment(), originalNode),
93124
[
94125
// important that the style goes before the node
95126
styleNode,
96127
elementNode,
97128
],
98-
ts.createJsxJsxClosingFragment()
129+
// We use setOriginalNode() here to work around createJsx not working without the original node.
130+
// See: https://github.com/microsoft/TypeScript/issues/35686
131+
ts.setOriginalNode(ts.createJsxJsxClosingFragment(), originalNode)
99132
);
100133

101134
return newFragmentParent;

0 commit comments

Comments
 (0)