stylebot-harmony/scripts/plugins/babel-inline-css-vars.ts
2023-08-03 20:09:32 -04:00

81 lines
2.7 KiB
TypeScript

import hash from "@emotion/hash"
import type { types } from "@babel/core"
import { kebabCase } from "lodash"
import type { BabelPlugin } from "./esbuild-babel"
export const inlineCSSVariables =
(): BabelPlugin<{ styles: { id: string; light: string; dark: string }[] }> =>
({ types: t }) => ({
name: "inline CSS variables",
visitor: {
Program: {
enter(_, state) {
state.styles = []
},
exit(path, { styles }) {
if (!styles.length) return
const css =
`body.light {${styles.map(s => `--${s.id}:${s.light}`).join(";")}}\n` +
`body.dark {${styles.map(s => `--${s.id}:${s.dark}`).join(";")}}`
path.node.body.unshift(
t.importDeclaration([], t.stringLiteral(`data:text/css,${encodeURI(css)}`)),
)
},
},
TaggedTemplateExpression(path, state) {
function join(exp: types.Node): string[] | undefined {
if (t.isIdentifier(exp)) return [exp.name]
if (!t.isMemberExpression(exp) || !t.isIdentifier(exp.property)) return
const prev = t.isIdentifier(exp.object) ? [exp.object.name] : join(exp.object)
return prev ? [...prev, exp.property.name] : undefined
}
const { expressions: exps } = path.node.quasi
for (const [i, exp] of exps.entries()) {
if (t.isIdentifier(exp)) {
if (exp.name === "DARK_MODE") {
exps[i] = t.stringLiteral("body.dark &")
} else if (exp.name === "LIGHT_MODE") {
exps[i] = t.stringLiteral("body.light &")
}
continue
}
if (
t.isCallExpression(exp) &&
t.isIdentifier(exp.callee, { name: "color" }) &&
exp.arguments.length === 2 &&
t.isStringLiteral(exp.arguments[0]) &&
t.isStringLiteral(exp.arguments[1])
) {
const [light, dark] = (exp.arguments as babel.types.StringLiteral[]).map(
arg => arg.value,
)
const id = hash(`${light}-${dark}`)
state.styles.push({ id, light, dark })
exps[i] = t.stringLiteral(`var(--${id})`)
continue
}
let ids: string[] | undefined
if (
t.isMemberExpression(exp) &&
t.isIdentifier(exp.property) &&
(ids = join(exp))
) {
const rest = ids.slice(1).join(".")
if (ids[0] === "vars") {
exps[i] = t.stringLiteral(`var(--${kebabCase(rest)})`)
}
if (ids[0] === "token") {
exps[i] = t.stringLiteral(`var(--color-${kebabCase(rest)})`)
}
}
}
},
},
})