Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
vendor
node_modules
coverage
dist
Expand Down
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"description": "Allows you to build compile-time libraries",
"main": "src/index.js",
"scripts": {
"build": "rm -rf dist/* && yarn build:lib && yarn build:types",
"build:lib": "vite build --config vite.lib.config.js --mode development",
"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",
"check:types": "tsc --noEmit -p tsconfig.json; if [ $? -eq 0 ]; then echo 8J+OiSBUeXBlcyBhcmUgZ29vZCEKCg== | base64 -d; else exit 1; fi",
"fix:format": "prettier --write src",
"lint": "kcd-scripts lint",
"setup": "npm install && npm run validate -s",
"test": "kcd-scripts test",
Expand Down Expand Up @@ -33,11 +38,14 @@
"@babel/parser": "^7.12.7",
"@babel/plugin-transform-modules-commonjs": "^7.16.7",
"@babel/types": "^7.12.7",
"@types/cosmiconfig": "^6.0.0",
"ast-pretty-print": "^2.0.1",
"babel-plugin-tester": "^10.0.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"cpy": "^8.1.1",
"kcd-scripts": "^7.1.0"
"kcd-scripts": "^7.1.0",
"typescript": "^4.9.5",
"vite": "^4.1.1"
},
"eslintConfig": {
"extends": "./node_modules/kcd-scripts/eslint.js"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const {createMacro} = require('../')
import {createMacro} from '..'

test('throws error if it is not transpiled', () => {
const untranspiledMacro = createMacro(() => {})
Expand Down
10 changes: 0 additions & 10 deletions src/__tests__/fixtures/config/configurable.macro.js

This file was deleted.

7 changes: 7 additions & 0 deletions src/__tests__/fixtures/config/configurable.macro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {createMacro} from '../../..'

// exports for testing purposes only
export const configName = 'configurableMacro'
export const realMacro = jest.fn()

export default createMacro(realMacro, {configName})
27 changes: 0 additions & 27 deletions src/__tests__/fixtures/emotion.macro.js

This file was deleted.

32 changes: 32 additions & 0 deletions src/__tests__/fixtures/emotion.macro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// this is a fake version of emotion
// const printAST = require('ast-pretty-print')
import {createMacro} from '../../'

module.exports = createMacro(function emotionMacro({references, babel}) {
const {types: t} = babel
references.css.forEach(cssRef => {
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') {
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})],
[],
)
quasi.replaceWith(replacement)
}
})
})
8 changes: 0 additions & 8 deletions src/__tests__/fixtures/error-thrower.macro.js

This file was deleted.

6 changes: 6 additions & 0 deletions src/__tests__/fixtures/error-thrower.macro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// const printAST = require('ast-pretty-print')
import {createMacro} from '../../'

module.exports = createMacro(function evalMacro() {
throw new Error('very unhelpful')
})
54 changes: 0 additions & 54 deletions src/__tests__/fixtures/eval.macro.js

This file was deleted.

106 changes: 106 additions & 0 deletions src/__tests__/fixtures/eval.macro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {parse} from '@babel/parser'
import {Node, NodePath} from '@babel/traverse'
import {Expression, Statement, VariableDeclaration} from '@babel/types'
// const printAST = require('ast-pretty-print')
import {createMacro} from '../../'

export default createMacro(function evalMacro({references, state}) {
references.default.forEach(referencePath => {
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')

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: NodePath[]) {
const value = argumentsPaths[0].evaluate().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,
}: {
attributes: NodePath | NodePath[]
children: NodePath | NodePath[] | undefined
}) {
// It's a shame you cannot use evaluate() with JSX
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: Expression | null | undefined): Expression {
let x: Record<string, unknown> = {}
// eslint-disable-next-line
eval(`x = ${value}`)
return thingToAST(x)
}

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)}`)
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
}
20 changes: 0 additions & 20 deletions src/__tests__/fixtures/jsx-id-prefix.macro.js

This file was deleted.

38 changes: 38 additions & 0 deletions src/__tests__/fixtures/jsx-id-prefix.macro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// adds "prefix-" to each `id` attribute
import {createMacro} from '../../'
import {
JSXElement,
JSXExpressionContainer,
JSXFragment,
StringLiteral,
} from '@babel/types'

module.exports = createMacro(function wrapWidget({references, babel}) {
const {types: t} = babel
references.default.forEach(wrap => {
wrap.parentPath?.traverse({
JSXAttribute(path) {
const name = path.get('name')
if (t.isJSXIdentifier(name) && name.node.name === 'id') {
const value = path.get('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'
}
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
7 changes: 0 additions & 7 deletions src/__tests__/fixtures/keep-imports.macro.js

This file was deleted.

Loading