81 lines
2.7 KiB
TypeScript
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)})`)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
})
|