UNPKG

vite-plugin-vue-style-bundler

Version:

`Automatically extract the `css` in `Vue` components and bundle them into the `js` source code, and then automatically insert the style into the head at runtime.

104 lines (98 loc) 3.13 kB
import { injectCodeToSetup, parseStyles } from "./parse"; import { shortHash } from "./utils"; import less from "./less"; import sass from "./sass"; import type { Plugin } from "vite" import path from "path" export interface StyleBundlerOptions { lessOptions?: any; sassOptions?: any; } /** * 在一段CSS代码中为所有规则注入scope属性 * @param css * @param scopeId */ function insertScopeId(css: string, scopeId: string) { const regex = /(?<=\}|^)([^\{\}]+)(?=\{)/gm; // @ts-ignore return css.replace(regex, (match:string, rules:string) => { return rules.split(",").map(r=>{ r=r.trim() if(r.startsWith("@")) return r const i = r.indexOf(":") if(i==-1){ return "\n"+r + `[data-l-${scopeId}]` }else{ return "\n"+r.slice(0,i) + `[data-l-${scopeId}]` + r.slice(i) } }).join(",") }); } function injectScopeId(code: string, scopeId: string) { // @ts-ignore return code.replace(/(<template>)([\s\S]+)(<\/template>)/gm, (matched: string,openTag:string, template: string,closeTag:string) => { return `${openTag}${template.replace(/(?<!=[\'\"\\])\<\w+\s*/gm, (tag: string) => { return `${tag} data-l-${scopeId} ` })}${closeTag}` }) } export default (options?: StyleBundlerOptions):Plugin => { const opts = Object.assign( { lessOptions: {}, sassOptions: {}, }, options ); return { name: "vue-plugin-vue-style-bundler", enforce: "pre", async transform(code: string, id: string) { // 1. 只处理.vue文件 if (!/.vue$/.test(id)) { return; } const fileId = shortHash(id); // 2. 检查是否有style let [newCode, styles] = parseStyles(code); // 3. 获取需要捆绑的样式 if (styles.length > 0) { // 在<script setup>中插入代码 for (const [props, css] of styles) { const styleId = typeof props.bundle == "string" ? props.bundle : fileId; let compiledCss = css; if (props.lang == "less") { try{ compiledCss = await less(css, Object.assign({ syncImport:true, rootPath:path.dirname(id), paths:path.dirname(id) },opts.lessOptions)); }catch(e){ console.error("Error while compile less:",e) throw e } } else if (["sass", "scss"].includes(props.lang as string)) { try{ compiledCss = await sass(css, opts.sassOptions); }catch(e){ console.error("Error while compile sass:",e) throw e } } if(props.scoped){ // 此时template还没有被编译,无法得到scopeId,先用styleId代替 compiledCss = insertScopeId(compiledCss, styleId); // 提取vue中的template内容 newCode = injectScopeId(newCode,styleId) } newCode = injectCodeToSetup(newCode, { styleId, props, css:compiledCss }); } return newCode; } else { return code; } }, } };