Skip to content

Commit 31549ab

Browse files
committed
Fix second half of type errors. Only macro errors remaining
1 parent 7bad187 commit 31549ab

File tree

13 files changed

+247
-127
lines changed

13 files changed

+247
-127
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
"build:lib": "vite build --config vite.lib.config.js --mode development",
99
"build:types": "tsc --declaration --emitDeclarationOnly -p tsconfig.dist.json --skipLibCheck && tsc-alias -p tsconfig.json && mv dist/index.d.ts dist/lib.umd.d.ts",
1010
"check:types": "tsc --noEmit -p tsconfig.json; if [ $? -eq 0 ]; then echo 8J+OiSBUeXBlcyBhcmUgZ29vZCEKCg== | base64 -d; else exit 1; fi",
11-
"confgen": "npx confgen@latest @lib --name BabelPluginMacros typescript vite dist:lib",
1211
"fix:format": "prettier --write src",
1312
"lint": "kcd-scripts lint",
1413
"setup": "npm install && npm run validate -s",

src/__tests__/fixtures/config/configurable.macro.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ import {createMacro} from '../../..'
44
export const configName = 'configurableMacro'
55
export const realMacro = jest.fn()
66

7-
export default createMacro(realMacro, ({configName} = {}))
7+
export default createMacro(realMacro, {configName})

src/__tests__/fixtures/emotion.macro.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,25 @@
22
// const printAST = require('ast-pretty-print')
33
import {createMacro} from '../../'
44

5-
module.exports = createMacro(emotionMacro)
6-
7-
function emotionMacro({references, babel}) {
5+
module.exports = createMacro(function emotionMacro({references, babel}) {
86
const {types: t} = babel
97
references.css.forEach(cssRef => {
10-
if (cssRef.parentPath.type === 'TaggedTemplateExpression') {
11-
cssRef.parentPath.replaceWith(
12-
t.stringLiteral(cssRef.parentPath.get('quasi').evaluate().value.trim()),
13-
)
8+
if (cssRef.parentPath?.type === 'TaggedTemplateExpression') {
9+
const path = cssRef.parentPath.get('quasi')
10+
if (Array.isArray(path)) {
11+
throw new Error("Don't know how to handle this situation")
12+
}
13+
const str = path.evaluate().value.trim()
14+
15+
cssRef.parentPath.replaceWith(t.stringLiteral(str))
1416
}
1517
})
1618
references.styled.forEach(styledRef => {
17-
if (styledRef.parentPath.parentPath.type === 'TaggedTemplateExpression') {
19+
if (styledRef.parentPath?.parentPath?.type === 'TaggedTemplateExpression') {
1820
const quasi = styledRef.parentPath.parentPath.get('quasi')
21+
if (Array.isArray(quasi)) {
22+
throw new Error('Not expecting array')
23+
}
1924
const val = quasi.evaluate().value.trim()
2025
const replacement = t.templateLiteral(
2126
[t.templateElement({raw: val, cooked: val})],
@@ -24,4 +29,4 @@ function emotionMacro({references, babel}) {
2429
quasi.replaceWith(replacement)
2530
}
2631
})
27-
}
32+
})
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// const printAST = require('ast-pretty-print')
2-
const {createMacro} = require('../../')
2+
import {createMacro} from '../../'
33

4-
module.exports = createMacro(evalMacro)
5-
6-
function evalMacro() {
4+
module.exports = createMacro(function evalMacro() {
75
throw new Error('very unhelpful')
8-
}
6+
})
Lines changed: 82 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,106 @@
1-
const {parse} = require('@babel/parser')
1+
import {parse} from '@babel/parser'
2+
import {Node, NodePath} from '@babel/traverse'
3+
import {Expression, Statement, VariableDeclaration} from '@babel/types'
24
// const printAST = require('ast-pretty-print')
3-
const {createMacro} = require('../../')
5+
import {createMacro} from '../../'
46

5-
module.exports = createMacro(evalMacro)
6-
7-
function evalMacro({references, state}) {
7+
export default createMacro(function evalMacro({references, state}) {
88
references.default.forEach(referencePath => {
9-
if (referencePath.parentPath.type === 'TaggedTemplateExpression') {
10-
asTag(referencePath.parentPath.get('quasi'), state)
11-
} else if (referencePath.parentPath.type === 'CallExpression') {
12-
asFunction(referencePath.parentPath.get('arguments'), state)
13-
} else if (referencePath.parentPath.type === 'JSXOpeningElement') {
14-
asJSX(
15-
{
16-
attributes: referencePath.parentPath.get('attributes'),
17-
children: referencePath.parentPath.parentPath.get('children'),
18-
},
19-
state,
20-
)
9+
if (referencePath.parentPath?.type === 'TaggedTemplateExpression') {
10+
asTag(referencePath.parentPath?.get('quasi'))
11+
} else if (referencePath.parentPath?.type === 'CallExpression') {
12+
const args = referencePath.parentPath?.get('arguments')
13+
if (!Array.isArray(args)) {
14+
throw new Error('Was expecting array')
15+
}
16+
asFunction(args)
17+
} else if (referencePath.parentPath?.type === 'JSXOpeningElement') {
18+
asJSX({
19+
attributes: referencePath.parentPath?.get('attributes'),
20+
children: referencePath.parentPath?.parentPath?.get('children'),
21+
})
2122
} else {
2223
// TODO: throw a helpful error message
2324
}
2425
})
25-
}
26+
})
27+
28+
function asTag(quasiPath: NodePath | NodePath[]) {
29+
if (Array.isArray(quasiPath)) {
30+
throw new Error("Don't know how to handle arrays")
31+
}
32+
33+
const parentQuasi = quasiPath.parentPath?.get('quasi')
2634

27-
function asTag(quasiPath) {
28-
const value = quasiPath.parentPath.get('quasi').evaluate().value
29-
quasiPath.parentPath.replaceWith(evalToAST(value))
35+
if (!parentQuasi) {
36+
throw new Error('No quasi path on parent')
37+
}
38+
39+
if (Array.isArray(parentQuasi)) {
40+
throw new Error("Don't know how to handle arrays")
41+
}
42+
const value = parentQuasi.evaluate().value
43+
quasiPath.parentPath?.replaceWith(evalToAST(value))
3044
}
3145

32-
function asFunction(argumentsPaths) {
46+
function asFunction(argumentsPaths: NodePath[]) {
3347
const value = argumentsPaths[0].evaluate().value
34-
argumentsPaths[0].parentPath.replaceWith(evalToAST(value))
48+
argumentsPaths[0].parentPath?.replaceWith(evalToAST(value))
49+
}
50+
51+
type NodeWithValue = Node & {
52+
value: any
53+
}
54+
55+
function isNodeWithValue(node: Node): node is NodeWithValue {
56+
return Object.prototype.hasOwnProperty.call(node, 'value')
3557
}
3658

3759
// eslint-disable-next-line no-unused-vars
38-
function asJSX({attributes, children}) {
60+
function asJSX({
61+
attributes,
62+
children,
63+
}: {
64+
attributes: NodePath | NodePath[]
65+
children: NodePath | NodePath[] | undefined
66+
}) {
3967
// It's a shame you cannot use evaluate() with JSX
40-
const value = children[0].node.value
41-
children[0].parentPath.replaceWith(evalToAST(value))
68+
if (!Array.isArray(children)) {
69+
throw new Error("Don't know how to handle single children")
70+
}
71+
const firstChild = children[0]
72+
if (!isNodeWithValue(firstChild.node)) {
73+
throw new Error("Don't know to handle nodes without values")
74+
}
75+
const value = firstChild.node.value
76+
firstChild.parentPath?.replaceWith(evalToAST(value))
4277
}
4378

44-
function evalToAST(value) {
45-
let x
79+
function evalToAST(value: Expression | null | undefined): Expression {
80+
let x: Record<string, unknown> = {}
4681
// eslint-disable-next-line
4782
eval(`x = ${value}`)
4883
return thingToAST(x)
4984
}
5085

51-
function thingToAST(object) {
86+
function isVariableDeclaration(
87+
statement: Statement,
88+
): statement is VariableDeclaration {
89+
return statement.type === 'VariableDeclaration'
90+
}
91+
92+
function thingToAST(object: Record<string, unknown>) {
5293
const fileNode = parse(`var x = ${JSON.stringify(object)}`)
53-
return fileNode.program.body[0].declarations[0].init
94+
const firstStatement = fileNode.program.body[0]
95+
96+
if (!isVariableDeclaration(firstStatement)) {
97+
throw new Error('Only know how to handle VariableDeclarations')
98+
}
99+
100+
const initDeclaration = firstStatement.declarations[0].init
101+
102+
if (!initDeclaration) {
103+
throw new Error('Was expecting expression')
104+
}
105+
return initDeclaration
54106
}
Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
11
// adds "prefix-" to each `id` attribute
2-
const {createMacro} = require('../../')
2+
import {createMacro} from '../../'
3+
import {
4+
JSXElement,
5+
JSXExpressionContainer,
6+
JSXFragment,
7+
StringLiteral,
8+
} from '@babel/types'
39

4-
module.exports = createMacro(wrapWidget)
5-
6-
function wrapWidget({references, babel}) {
10+
module.exports = createMacro(function wrapWidget({references, babel}) {
711
const {types: t} = babel
812
references.default.forEach(wrap => {
9-
wrap.parentPath.traverse({
13+
wrap.parentPath?.traverse({
1014
JSXAttribute(path) {
1115
const name = path.get('name')
1216
if (t.isJSXIdentifier(name) && name.node.name === 'id') {
1317
const value = path.get('value')
14-
if (t.isStringLiteral(value))
15-
value.replaceWith(t.stringLiteral(`macro-${value.node.value}`))
18+
if (isStringLiteral(value.node)) {
19+
value.replaceWith(t.stringLiteral(`macro-${value.node?.value}`))
20+
}
1621
}
1722
},
1823
})
1924
})
25+
})
26+
27+
function isStringLiteral(
28+
node:
29+
| JSXElement
30+
| JSXExpressionContainer
31+
| JSXFragment
32+
| StringLiteral
33+
| null
34+
| undefined,
35+
): node is StringLiteral {
36+
if (!node) return false
37+
return node.type === 'StringLiteral'
2038
}

src/__tests__/fixtures/jsx-id-prefix.plugin.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
// babel-plugin adding `plugin-` prefix to each "id" JSX attribute
2-
module.exports = main
2+
import {NodePath} from '@babel/core'
3+
import {BabelType} from 'babel-plugin-tester'
34

4-
function main({types: t}) {
5+
export default function main({types: t}: BabelType) {
56
return {
67
visitor: {
78
// intentionally traversing from Program,
89
// if it matches JSXAttribute here the issue won't be reproduced
9-
Program(progPath) {
10+
Program(progPath: NodePath) {
1011
progPath.traverse({
11-
JSXAttribute(path) {
12+
JSXAttribute(path: NodePath) {
1213
const name = path.get('name')
13-
if (t.isJSXIdentifier(name) && name.node.name === 'id') {
14+
if (t.isJSXIdentifier(name) && name.name === 'id') { /// DANGER! CODE CHANGE!
1415
const value = path.get('value')
16+
if (Array.isArray(value)) {
17+
throw new Error("Value path is an array. Don't know how to handle this")
18+
}
1519
if (t.isStringLiteral(value))
16-
value.replaceWith(t.stringLiteral(`plugin-${value.node.value}`))
20+
value.replaceWith(t.stringLiteral(`plugin-${value.value}`)) /// DANGER! CODE CHANGE!
1721
}
1822
},
1923
})
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
const {createMacro} = require('../../')
1+
import {createMacro} from '../../'
22

3-
module.exports = createMacro(keepImportMacro)
4-
5-
function keepImportMacro() {
3+
export default createMacro(function keepImportMacro() {
64
return {keepImports: true}
7-
}
5+
})
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// const printAST = require('ast-pretty-print')
2-
const {createMacro, MacroError} = require('../../')
2+
import {createMacro, MacroError} from '../../'
33

4-
module.exports = createMacro(evalMacro)
5-
6-
function evalMacro() {
4+
export default createMacro(function evalMacro() {
75
throw new MacroError('very helpful')
8-
}
6+
})
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import myEval from '../eval.macro'
22

3-
const result = myEval`+('4' + '2')`
3+
const result: number = myEval`+('4' + '2')`
4+
5+
declare global {
6+
var result: number;
7+
}
48

59
global.result = result

0 commit comments

Comments
 (0)