Skip to content

Instantly share code, notes, and snippets.

@kasperskei
Created October 8, 2024 18:37
Show Gist options
  • Save kasperskei/d8950a65a0c57d0853531dbf79256340 to your computer and use it in GitHub Desktop.
Save kasperskei/d8950a65a0c57d0853531dbf79256340 to your computer and use it in GitHub Desktop.
import {
type Plugin
} from 'vite'
import {
type Node
} from 'estree'
import {
transform,
} from 'lightningcss'
const isNode = (value: unknown): value is Node => {
// @ts-ignore
return typeof value?.type === 'string'
}
const flat = (node: Node): Node[] => {
return Object.values(node)
.flat()
.filter(isNode)
.flatMap((value) => [value, ...flat(value)])
}
const walk = (node: Node, visit: (value: Node, index: number, array: Node[]) => void) => {
flat(node).forEach(visit)
}
const transformCss = (raw: string) => {
const tempLeft = '.temp{'
const tempRight = '}'
const code = tempLeft + raw + tempRight
const style = transform({
filename: 'temp.css',
code: Buffer.from(code),
minify: true,
}).code.toString().slice(tempLeft.length, -tempRight.length)
return style
}
const replace = (source: string, target: string, node: Node): string => {
const left = source.slice(0, node.start)
const right = source.slice(node.end)
const middle = '\'' + target + '\''
return left + middle + right
}
/**
* Оптимизирует CSS код.
* @example
* ```ts
* const style = css`color: black;` // 'color:#000'
* ```
*/
export const cssInJsPlugin = (): Plugin => {
return {
name: 'css-in-js',
transform(code, id, options) {
if (
id.includes('node_modules')
|| !/.*\.(jsx|tsx|js|ts)$/.test(id)
|| !/\bcss\b/.test(code)
) {
return
}
const run = (code: string): string => {
let ast = this.parse(code)
let dirty = false
walk(ast, (node) => {
if (dirty) {
return
}
if (
node.type === 'TaggedTemplateExpression'
&& node.tag.type === 'Identifier'
&& node.tag.name === 'css'
&& node.quasi.expressions.length === 0
&& node.quasi.quasis.length === 1
&& node.quasi.quasis[0]?.type === 'TemplateElement'
) {
const style = transformCss(node.quasi.quasis[0].value.raw)
code = replace(code, style, node)
dirty = true
} else if (
node.type === 'Property'
&& node.key.type === 'Identifier'
&& node.key.name === 'css'
&& node.value.type === 'TemplateLiteral'
&& node.value.expressions.length === 0
&& node.value.quasis.length === 1
&& node.value.quasis[0]?.type === 'TemplateElement'
) {
const style = transformCss(node.value.quasis[0].value.raw)
code = replace(code, style, node.value)
dirty = true
}
})
if (dirty) {
return run(code)
}
return code
}
return run(code)
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment