11import { createUnplugin } from 'unplugin'
22import {
3- JSX_TSX_REG , NAME ,
4- SUPPORT_FILE_REG ,
3+ NAME ,
54 log ,
6- setTArray ,
75 transformSymbol ,
86} from '@unplugin-vue-cssvars/utils'
97import { createFilter } from '@rollup/pluginutils'
10- import { parse } from '@vue/compiler-sfc'
11- import chalk from 'chalk'
128import MagicString from 'magic-string'
139import { preProcessCSS } from './runtime/pre-process-css'
14- import { getVBindVariableListByPath } from './runtime/process-css'
1510import { initOption } from './option'
16- import { getVariable , matchVariable , parserCompiledSfc } from './parser'
1711import {
18- injectCSSOnServer ,
19- injectCSSVars ,
20- injectCssOnBuild ,
21- } from './inject'
22- import { viteHMR , webpackHMR } from './hmr/hmr'
23- import type { MagicStringBase } from 'magic-string-ast'
24- import type { HmrContext , ResolvedConfig } from 'vite'
12+ transformPostVite ,
13+ transformPreVite ,
14+ vitePlugin ,
15+ } from './runtime/vite'
16+ import {
17+ transformPostWebpack ,
18+ transformPreWebpack ,
19+ webpackPlugin ,
20+ } from './runtime/webpack'
2521import type { TMatchVariable } from './parser'
26- import type { Options } from './types'
27- // TODO refactor
22+ import type { IVueCSSVarsCtx , Options } from './types'
23+
2824const unplugin = createUnplugin < Options > (
2925 ( options : Options = { } , meta ) : any => {
30- const framework = meta . framework
3126 const userOptions = initOption ( options )
3227 const filter = createFilter (
3328 userOptions . include ,
3429 userOptions . exclude ,
3530 )
36- // 预处理 css 文件
37- const CSSFileModuleMap = preProcessCSS ( userOptions , userOptions . alias )
38- const vbindVariableList = new Map < string , TMatchVariable > ( )
39- let isScriptSetup = false
31+
4032 if ( userOptions . server === undefined ) {
4133 log ( 'warning' , 'The server of option is not set, you need to specify whether you are using the development server or building the project' )
42- log ( 'warning' , 'The server of option is not set, you need to specify whether you are using the development server or building the project' )
43- console . warn ( chalk . yellowBright . bold ( `[${ NAME } ] The server of option is not set, you need to specify whether you are using the development server or building the project` ) )
44- console . warn ( chalk . yellowBright . bold ( `[${ NAME } ] See: https://github.com/baiwusanyu-c/unplugin-vue-cssvars/blob/master/README.md#option` ) )
34+ log ( 'warning' , 'See: https://github.com/baiwusanyu-c/unplugin-vue-cssvars/blob/master/README.md#option' )
4535 }
46- let isServer = ! ! userOptions . server
47- let isHMR = false
48-
49- function handleVBindVariable (
50- code : string ,
51- id : string ,
52- mgcStr ?: MagicStringBase ,
53- ) {
54- const { descriptor } = parse ( code )
55- const lang = descriptor ?. script ?. lang ?? 'js'
56- // ⭐TODO: 只支持 .vue ? jsx, tsx, js, ts ?
57- if ( ! JSX_TSX_REG . test ( `.${ lang } ` ) ) {
58- isScriptSetup = ! ! descriptor . scriptSetup
59- const {
60- vbindVariableListByPath,
61- injectCSSContent,
62- } = getVBindVariableListByPath ( descriptor , id , CSSFileModuleMap , isServer , userOptions . alias )
63-
64- const variableName = getVariable ( descriptor )
65- vbindVariableList . set ( id , matchVariable ( vbindVariableListByPath , variableName ) )
6636
67- // vite、rollup、esbuild 打包生效
68- if ( mgcStr && ! isServer && framework !== 'webpack' && framework !== 'rspack' ) {
69- mgcStr = injectCssOnBuild ( mgcStr , injectCSSContent , descriptor )
70- return mgcStr
71- }
72- }
73- }
37+ const context = {
38+ CSSFileModuleMap : preProcessCSS ( userOptions , userOptions . alias ) ,
39+ vbindVariableList : new Map < string , TMatchVariable > ( ) ,
40+ isServer : ! ! userOptions . server ,
41+ isHMR : false ,
42+ userOptions,
43+ framework : meta . framework ,
44+ isScriptSetup : false ,
45+ } as IVueCSSVarsCtx
7446
7547 return [
7648 {
@@ -80,25 +52,23 @@ const unplugin = createUnplugin<Options>(
8052 return filter ( id )
8153 } ,
8254 async transform ( code : string , id : string ) {
83- let transId = transformSymbol ( id )
55+ const transId = transformSymbol ( id )
8456 let mgcStr = new MagicString ( code )
8557 try {
86- // ⭐TODO: 只支持 .vue ? jsx, tsx, js, ts ?
87- // webpack 时 使用 id.includes('?vue&type=style') 判断
88- // webpack dev 和 build 都回进入这里
89-
90- if ( transId . endsWith ( '.vue' ) ) {
91- const res = handleVBindVariable ( code , transId , mgcStr )
92- if ( res )
93- mgcStr = res
94- }
95-
96- if ( ( transId . includes ( '?vue&type=style' ) || transId . includes ( '?vue&type=script' ) )
97- && isHMR && framework === 'webpack' ) {
98- transId = transId . split ( '?vue' ) [ 0 ]
99- const res = handleVBindVariable ( code , transId , mgcStr )
100- if ( res )
101- mgcStr = res
58+ if ( context . framework !== 'webpack'
59+ && context . framework !== 'rspack' ) {
60+ mgcStr = transformPreVite (
61+ transId ,
62+ code ,
63+ mgcStr ,
64+ context ,
65+ )
66+ } else {
67+ transformPreWebpack (
68+ transId ,
69+ code ,
70+ context ,
71+ )
10272 }
10373
10474 return {
@@ -115,89 +85,11 @@ const unplugin = createUnplugin<Options>(
11585 this . error ( `[${ NAME } ] ${ err } ` )
11686 }
11787 } ,
118- vite : {
119- // Vite plugin
120- configResolved ( config : ResolvedConfig ) {
121- if ( userOptions . server !== undefined )
122- isServer = userOptions . server
123- else
124- isServer = config . command === 'serve'
125- } ,
126- handleHotUpdate ( hmr : HmrContext ) {
127- if ( SUPPORT_FILE_REG . test ( hmr . file ) ) {
128- isHMR = true
129- viteHMR (
130- CSSFileModuleMap ,
131- userOptions ,
132- transformSymbol ( hmr . file ) ,
133- hmr . server ,
134- )
135- }
136- } ,
137- } ,
138-
139- // TODO unit test
88+ // handle hmr with vite
89+ vite : vitePlugin ( context ) ,
90+ // handle hmr with webpack
14091 webpack ( compiler ) {
141- // mark webpack hmr
142- let modifiedFile = ''
143- compiler . hooks . watchRun . tapAsync ( NAME , ( compilation1 , watchRunCallBack ) => {
144- if ( compilation1 . modifiedFiles ) {
145- modifiedFile = transformSymbol ( setTArray ( compilation1 . modifiedFiles ) [ 0 ] as string )
146- if ( SUPPORT_FILE_REG . test ( modifiedFile ) ) {
147- isHMR = true
148- webpackHMR (
149- CSSFileModuleMap ,
150- userOptions ,
151- modifiedFile ,
152- )
153- }
154- }
155- watchRunCallBack ( )
156- } )
157-
158- compiler . hooks . compilation . tap ( NAME , ( compilation ) => {
159- compilation . hooks . finishModules . tapAsync ( NAME , async ( modules , callback ) => {
160- if ( isHMR ) {
161- const needRebuildModules = new Map < string , any > ( )
162- for ( const value of modules ) {
163- const resource = transformSymbol ( value . resource )
164- if ( resource . includes ( '?vue&type=script' ) ) {
165- const sfcPathKey = resource . split ( '?vue' ) [ 0 ]
166- if ( CSSFileModuleMap . get ( modifiedFile ) . sfcPath . has ( sfcPathKey ) )
167- needRebuildModules . set ( sfcPathKey , value )
168- }
169- }
170- if ( needRebuildModules . size > 0 ) {
171- const promises = [ ]
172- for ( const [ key ] of needRebuildModules ) {
173- // 创建一个 Promise 对象,表示异步操作
174- const promise = new Promise ( ( resolve , reject ) => {
175- compilation . rebuildModule ( needRebuildModules . get ( key ) , ( e ) => {
176- if ( e )
177- reject ( e )
178- else
179- resolve ( )
180- } )
181- } )
182- promises . push ( promise )
183- }
184- Promise . all ( promises )
185- . then ( ( ) => {
186- callback ( )
187- // hmr end
188- isHMR = false
189- } )
190- . catch ( ( e ) => {
191- log ( 'error' , e )
192- } )
193- } else {
194- callback ( )
195- }
196- } else {
197- callback ( )
198- }
199- } )
200- } )
92+ webpackPlugin ( context , compiler )
20193 } ,
20294 } ,
20395
@@ -208,57 +100,21 @@ const unplugin = createUnplugin<Options>(
208100 return filter ( id )
209101 } ,
210102 async transform ( code : string , id : string ) {
211- console . log ( id )
212- let transId = transformSymbol ( id )
103+ const transId = transformSymbol ( id )
213104 let mgcStr = new MagicString ( code )
214- // ⭐TODO: 只支持 .vue ? jsx, tsx, js, ts ?
215105 try {
216- function injectCSSVarsFn ( idKey : string ) {
217- const parseRes = parserCompiledSfc ( code )
218- const injectRes = injectCSSVars ( vbindVariableList . get ( idKey ) , isScriptSetup , parseRes , mgcStr )
219- mgcStr = injectRes . mgcStr
220- injectRes . vbindVariableList && vbindVariableList . set ( transId , injectRes . vbindVariableList )
221- // TODO vite hmr close ? isHMR -> false
222- }
223-
224106 // transform in dev
225107 // 'vite' | 'rollup' | 'esbuild'
226- if ( isServer ) {
227- if ( framework === 'vite'
228- || framework === 'rollup'
229- || framework === 'esbuild' ) {
230- // inject cssvars to sfc code
231- if ( transId . endsWith ( '.vue' ) )
232- injectCSSVarsFn ( transId )
233- // inject css code
234- if ( transId . includes ( '?vue&type=style' ) ) {
235- mgcStr = injectCSSOnServer (
236- mgcStr ,
237- vbindVariableList . get ( transId . split ( '?vue' ) [ 0 ] ) ,
238- isHMR ,
239- )
240- }
241- }
108+ if ( context . isServer ) {
109+ if ( context . framework === 'vite'
110+ || context . framework === 'rollup'
111+ || context . framework === 'esbuild' )
112+ mgcStr = transformPostVite ( transId , code , mgcStr , context )
242113 }
243114
244115 // webpack dev 和 build 都回进入这里
245- if ( framework === 'webpack' ) {
246- if ( transId . includes ( '?vue&type=script' ) ) {
247- transId = transId . split ( '?vue' ) [ 0 ]
248- injectCSSVarsFn ( transId )
249- }
250-
251- const cssFMM = CSSFileModuleMap . get ( transId )
252- if ( cssFMM && cssFMM . sfcPath && cssFMM . sfcPath . size > 0 ) {
253- const sfcPathIdList = setTArray ( cssFMM . sfcPath )
254- sfcPathIdList . forEach ( ( v ) => {
255- mgcStr = injectCSSOnServer (
256- mgcStr ,
257- vbindVariableList . get ( v ) ,
258- isHMR )
259- } )
260- }
261- }
116+ if ( context . framework === 'webpack' )
117+ mgcStr = transformPostWebpack ( transId , code , mgcStr , context )
262118
263119 return {
264120 code : mgcStr . toString ( ) ,
0 commit comments