Skip to content

Commit

Permalink
Fix second half of type errors. Only macro errors remaining
Browse files Browse the repository at this point in the history
  • Loading branch information
erikpukinskis committed Feb 14, 2023
1 parent 7bad187 commit 121785b
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 126 deletions.
2 changes: 1 addition & 1 deletion src/__tests__/fixtures/config/configurable.macro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ import {createMacro} from '../../..'
export const configName = 'configurableMacro'
export const realMacro = jest.fn()

export default createMacro(realMacro, ({configName} = {}))
export default createMacro(realMacro, {configName})
23 changes: 14 additions & 9 deletions src/__tests__/fixtures/emotion.macro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@
// const printAST = require('ast-pretty-print')
import {createMacro} from '../../'

module.exports = createMacro(emotionMacro)

function emotionMacro({references, babel}) {
module.exports = createMacro(function emotionMacro({references, babel}) {
const {types: t} = babel
references.css.forEach(cssRef => {
if (cssRef.parentPath.type === 'TaggedTemplateExpression') {
cssRef.parentPath.replaceWith(
t.stringLiteral(cssRef.parentPath.get('quasi').evaluate().value.trim()),
)
if (cssRef.parentPath?.type === 'TaggedTemplateExpression') {
const path = cssRef.parentPath.get('quasi')
if (Array.isArray(path)) {
throw new Error("Don't know how to handle this situation")
}
const str = path.evaluate().value.trim()

cssRef.parentPath.replaceWith(t.stringLiteral(str))
}
})
references.styled.forEach(styledRef => {
if (styledRef.parentPath.parentPath.type === 'TaggedTemplateExpression') {
if (styledRef.parentPath?.parentPath?.type === 'TaggedTemplateExpression') {
const quasi = styledRef.parentPath.parentPath.get('quasi')
if (Array.isArray(quasi)) {
throw new Error('Not expecting array')
}
const val = quasi.evaluate().value.trim()
const replacement = t.templateLiteral(
[t.templateElement({raw: val, cooked: val})],
Expand All @@ -24,4 +29,4 @@ function emotionMacro({references, babel}) {
quasi.replaceWith(replacement)
}
})
}
})
8 changes: 3 additions & 5 deletions src/__tests__/fixtures/error-thrower.macro.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// const printAST = require('ast-pretty-print')
const {createMacro} = require('../../')
import {createMacro} from '../../'

module.exports = createMacro(evalMacro)

function evalMacro() {
module.exports = createMacro(function evalMacro() {
throw new Error('very unhelpful')
}
})
112 changes: 82 additions & 30 deletions src/__tests__/fixtures/eval.macro.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,106 @@
const {parse} = require('@babel/parser')
import {parse} from '@babel/parser'
import {Node, NodePath} from '@babel/traverse'
import {Expression, Statement, VariableDeclaration} from '@babel/types'
// const printAST = require('ast-pretty-print')
const {createMacro} = require('../../')
import {createMacro} from '../../'

module.exports = createMacro(evalMacro)

function evalMacro({references, state}) {
export default createMacro(function evalMacro({references, state}) {
references.default.forEach(referencePath => {
if (referencePath.parentPath.type === 'TaggedTemplateExpression') {
asTag(referencePath.parentPath.get('quasi'), state)
} else if (referencePath.parentPath.type === 'CallExpression') {
asFunction(referencePath.parentPath.get('arguments'), state)
} else if (referencePath.parentPath.type === 'JSXOpeningElement') {
asJSX(
{
attributes: referencePath.parentPath.get('attributes'),
children: referencePath.parentPath.parentPath.get('children'),
},
state,
)
if (referencePath.parentPath?.type === 'TaggedTemplateExpression') {
asTag(referencePath.parentPath?.get('quasi'))
} else if (referencePath.parentPath?.type === 'CallExpression') {
const args = referencePath.parentPath?.get('arguments')
if (!Array.isArray(args)) {
throw new Error('Was expecting array')
}
asFunction(args)
} else if (referencePath.parentPath?.type === 'JSXOpeningElement') {
asJSX({
attributes: referencePath.parentPath?.get('attributes'),
children: referencePath.parentPath?.parentPath?.get('children'),
})
} else {
// TODO: throw a helpful error message
}
})
}
})

function asTag(quasiPath: NodePath | NodePath[]) {
if (Array.isArray(quasiPath)) {
throw new Error("Don't know how to handle arrays")
}

const parentQuasi = quasiPath.parentPath?.get('quasi')

function asTag(quasiPath) {
const value = quasiPath.parentPath.get('quasi').evaluate().value
quasiPath.parentPath.replaceWith(evalToAST(value))
if (!parentQuasi) {
throw new Error('No quasi path on parent')
}

if (Array.isArray(parentQuasi)) {
throw new Error("Don't know how to handle arrays")
}
const value = parentQuasi.evaluate().value
quasiPath.parentPath?.replaceWith(evalToAST(value))
}

function asFunction(argumentsPaths) {
function asFunction(argumentsPaths: NodePath[]) {
const value = argumentsPaths[0].evaluate().value
argumentsPaths[0].parentPath.replaceWith(evalToAST(value))
argumentsPaths[0].parentPath?.replaceWith(evalToAST(value))
}

type NodeWithValue = Node & {
value: any
}

function isNodeWithValue(node: Node): node is NodeWithValue {
return Object.prototype.hasOwnProperty.call(node, 'value')
}

// eslint-disable-next-line no-unused-vars
function asJSX({attributes, children}) {
function asJSX({
attributes,
children,
}: {
attributes: NodePath | NodePath[]
children: NodePath | NodePath[] | undefined
}) {
// It's a shame you cannot use evaluate() with JSX
const value = children[0].node.value
children[0].parentPath.replaceWith(evalToAST(value))
if (!Array.isArray(children)) {
throw new Error("Don't know how to handle single children")
}
const firstChild = children[0]
if (!isNodeWithValue(firstChild.node)) {
throw new Error("Don't know to handle nodes without values")
}
const value = firstChild.node.value
firstChild.parentPath?.replaceWith(evalToAST(value))
}

function evalToAST(value) {
let x
function evalToAST(value: Expression | null | undefined): Expression {
let x: Record<string, unknown> = {}
// eslint-disable-next-line
eval(`x = ${value}`)
return thingToAST(x)
}

function thingToAST(object) {
function isVariableDeclaration(
statement: Statement,
): statement is VariableDeclaration {
return statement.type === 'VariableDeclaration'
}

function thingToAST(object: Record<string, unknown>) {
const fileNode = parse(`var x = ${JSON.stringify(object)}`)
return fileNode.program.body[0].declarations[0].init
const firstStatement = fileNode.program.body[0]

if (!isVariableDeclaration(firstStatement)) {
throw new Error('Only know how to handle VariableDeclarations')
}

const initDeclaration = firstStatement.declarations[0].init

if (!initDeclaration) {
throw new Error('Was expecting expression')
}
return initDeclaration
}
32 changes: 25 additions & 7 deletions src/__tests__/fixtures/jsx-id-prefix.macro.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
// adds "prefix-" to each `id` attribute
const {createMacro} = require('../../')
import {createMacro} from '../../'
import {
JSXElement,
JSXExpressionContainer,
JSXFragment,
StringLiteral,
} from '@babel/types'

module.exports = createMacro(wrapWidget)

function wrapWidget({references, babel}) {
module.exports = createMacro(function wrapWidget({references, babel}) {
const {types: t} = babel
references.default.forEach(wrap => {
wrap.parentPath.traverse({
wrap.parentPath?.traverse({
JSXAttribute(path) {
const name = path.get('name')
if (t.isJSXIdentifier(name) && name.node.name === 'id') {
const value = path.get('value')
if (t.isStringLiteral(value))
value.replaceWith(t.stringLiteral(`macro-${value.node.value}`))
if (isStringLiteral(value.node)) {
value.replaceWith(t.stringLiteral(`macro-${value.node?.value}`))
}
}
},
})
})
})

function isStringLiteral(
node:
| JSXElement
| JSXExpressionContainer
| JSXFragment
| StringLiteral
| null
| undefined,
): node is StringLiteral {
if (!node) return false
return node.type === 'StringLiteral'
}
16 changes: 10 additions & 6 deletions src/__tests__/fixtures/jsx-id-prefix.plugin.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
// babel-plugin adding `plugin-` prefix to each "id" JSX attribute
module.exports = main
import {NodePath} from '@babel/core'
import {BabelType} from 'babel-plugin-tester'

function main({types: t}) {
export default function main({types: t}: BabelType) {
return {
visitor: {
// intentionally traversing from Program,
// if it matches JSXAttribute here the issue won't be reproduced
Program(progPath) {
Program(progPath: NodePath) {
progPath.traverse({
JSXAttribute(path) {
JSXAttribute(path: NodePath) {
const name = path.get('name')
if (t.isJSXIdentifier(name) && name.node.name === 'id') {
if (t.isJSXIdentifier(name) && name.name === 'id') { /// DANGER! CODE CHANGE!
const value = path.get('value')
if (Array.isArray(value)) {
throw new Error("Value path is an array. Don't know how to handle this")
}
if (t.isStringLiteral(value))
value.replaceWith(t.stringLiteral(`plugin-${value.node.value}`))
value.replaceWith(t.stringLiteral(`plugin-${value.value}`)) /// DANGER! CODE CHANGE!
}
},
})
Expand Down
8 changes: 3 additions & 5 deletions src/__tests__/fixtures/keep-imports.macro.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
const {createMacro} = require('../../')
import {createMacro} from '../../'

module.exports = createMacro(keepImportMacro)

function keepImportMacro() {
export default createMacro(function keepImportMacro() {
return {keepImports: true}
}
})
8 changes: 3 additions & 5 deletions src/__tests__/fixtures/macro-error-thrower.macro.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// const printAST = require('ast-pretty-print')
const {createMacro, MacroError} = require('../../')
import {createMacro, MacroError} from '../../'

module.exports = createMacro(evalMacro)

function evalMacro() {
export default createMacro(function evalMacro() {
throw new MacroError('very helpful')
}
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import myEval from '../eval.macro'

const result = myEval`+('4' + '2')`
const result = myEval`+('4' + '2')` as number

declare global {
var result: number;
}

global.result = result
15 changes: 6 additions & 9 deletions src/__tests__/fixtures/primitive-config/configurable.macro.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
const {createMacro} = require('../../..')
import { createMacro } from '../../..'

const configName = 'configurableMacro'
const realMacro = jest.fn()
module.exports = createMacro(realMacro, {configName})
// for testing purposes only
Object.assign(module.exports, {
realMacro,
configName,
})
// exports for testing purposes only:
export const configName = 'configurableMacro'
export const realMacro = jest.fn()

export default createMacro(realMacro, { configName })
Loading

0 comments on commit 121785b

Please sign in to comment.