Created
October 8, 2024 18:37
-
-
Save kasperskei/d8950a65a0c57d0853531dbf79256340 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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