@@ -27,6 +27,12 @@ import { formatMessages, transform } from 'esbuild'
27
27
import type { RawSourceMap } from '@ampproject/remapping'
28
28
import { WorkerWithFallback } from 'artichokie'
29
29
import { globSync } from 'tinyglobby'
30
+ import type {
31
+ LessPreprocessorBaseOptions ,
32
+ SassLegacyPreprocessBaseOptions ,
33
+ SassModernPreprocessBaseOptions ,
34
+ StylusPreprocessorBaseOptions ,
35
+ } from 'types/cssPreprocessorOptions'
30
36
import { getCodeWithSourcemap , injectSourcesContent } from '../server/sourcemap'
31
37
import type { EnvironmentModuleNode } from '../server/moduleGraph'
32
38
import {
@@ -111,7 +117,14 @@ export interface CSSOptions {
111
117
* In addition to options specific to each processors, Vite supports `additionalData` option.
112
118
* The `additionalData` option can be used to inject extra code for each style content.
113
119
*/
114
- preprocessorOptions ?: Record < string , any >
120
+ preprocessorOptions ?: {
121
+ scss ?: SassPreprocessorOptions
122
+ sass ?: SassPreprocessorOptions
123
+ less ?: LessPreprocessorOptions
124
+ styl ?: StylusPreprocessorOptions
125
+ stylus ?: StylusPreprocessorOptions
126
+ }
127
+
115
128
/**
116
129
* If this option is set, preprocessors will run in workers when possible.
117
130
* `true` means the number of CPUs minus 1.
@@ -1135,32 +1148,15 @@ async function compileCSSPreprocessors(
1135
1148
const atImportResolvers = getAtImportResolvers (
1136
1149
environment . getTopLevelConfig ( ) ,
1137
1150
)
1138
-
1139
- const preProcessor = workerController [ lang ]
1140
- let opts = ( preprocessorOptions && preprocessorOptions [ lang ] ) || { }
1141
- // support @import from node dependencies by default
1142
- switch ( lang ) {
1143
- case PreprocessLang . scss :
1144
- case PreprocessLang . sass :
1145
- opts = {
1146
- includePaths : [ 'node_modules' ] ,
1147
- alias : config . resolve . alias ,
1148
- ...opts ,
1149
- }
1150
- break
1151
- case PreprocessLang . less :
1152
- case PreprocessLang . styl :
1153
- case PreprocessLang . stylus :
1154
- opts = {
1155
- paths : [ 'node_modules' ] ,
1156
- alias : config . resolve . alias ,
1157
- ...opts ,
1158
- }
1151
+ const opts = {
1152
+ ...( ( preprocessorOptions && preprocessorOptions [ lang ] ) || { } ) ,
1153
+ alias : config . resolve . alias ,
1154
+ // important: set this for relative import resolving
1155
+ filename : cleanUrl ( id ) ,
1156
+ enableSourcemap : devSourcemap ?? false ,
1159
1157
}
1160
- // important: set this for relative import resolving
1161
- opts . filename = cleanUrl ( id )
1162
- opts . enableSourcemap = devSourcemap ?? false
1163
1158
1159
+ const preProcessor = workerController [ lang ]
1164
1160
const preprocessResult = await preProcessor (
1165
1161
environment ,
1166
1162
code ,
@@ -1977,52 +1973,43 @@ type PreprocessorAdditionalData =
1977
1973
| PreprocessorAdditionalDataResult
1978
1974
| Promise < PreprocessorAdditionalDataResult > )
1979
1975
1980
- type StylePreprocessorOptions = {
1981
- [ key : string ] : any
1976
+ type SassPreprocessorOptions = {
1977
+ additionalData ?: PreprocessorAdditionalData
1978
+ } & (
1979
+ | ( { api ?: 'legacy' } & SassLegacyPreprocessBaseOptions )
1980
+ | ( { api : 'modern' | 'modern-compiler' } & SassModernPreprocessBaseOptions )
1981
+ )
1982
+
1983
+ type LessPreprocessorOptions = {
1982
1984
additionalData ?: PreprocessorAdditionalData
1985
+ } & LessPreprocessorBaseOptions
1986
+
1987
+ type StylusPreprocessorOptions = {
1988
+ additionalData ?: PreprocessorAdditionalData
1989
+ } & StylusPreprocessorBaseOptions
1990
+
1991
+ type StylePreprocessorInternalOptions = {
1983
1992
maxWorkers ?: number | true
1984
1993
filename : string
1985
1994
alias : Alias [ ]
1986
1995
enableSourcemap : boolean
1987
1996
}
1988
1997
1989
- type SassStylePreprocessorOptions = StylePreprocessorOptions &
1990
- Omit < Sass . LegacyOptions < 'async' > , 'data' | 'file' | 'outFile' > & {
1991
- api ?: 'legacy' | 'modern' | 'modern-compiler'
1992
- }
1998
+ type SassStylePreprocessorInternalOptions = StylePreprocessorInternalOptions &
1999
+ SassPreprocessorOptions
1993
2000
1994
- type StylusStylePreprocessorOptions = StylePreprocessorOptions & {
1995
- define ?: Record < string , any >
1996
- }
2001
+ type LessStylePreprocessorInternalOptions = StylePreprocessorInternalOptions &
2002
+ LessPreprocessorOptions
1997
2003
1998
- type StylePreprocessor = {
1999
- process : (
2000
- environment : PartialEnvironment ,
2001
- source : string ,
2002
- root : string ,
2003
- options : StylePreprocessorOptions ,
2004
- resolvers : CSSAtImportResolvers ,
2005
- ) => StylePreprocessorResults | Promise < StylePreprocessorResults >
2006
- close : ( ) => void
2007
- }
2004
+ type StylusStylePreprocessorInternalOptions = StylePreprocessorInternalOptions &
2005
+ StylusPreprocessorOptions
2008
2006
2009
- type SassStylePreprocessor = {
2007
+ type StylePreprocessor < Options extends StylePreprocessorInternalOptions > = {
2010
2008
process : (
2011
2009
environment : PartialEnvironment ,
2012
2010
source : string ,
2013
2011
root : string ,
2014
- options : SassStylePreprocessorOptions ,
2015
- resolvers : CSSAtImportResolvers ,
2016
- ) => StylePreprocessorResults | Promise < StylePreprocessorResults >
2017
- close : ( ) => void
2018
- }
2019
-
2020
- type StylusStylePreprocessor = {
2021
- process : (
2022
- environment : PartialEnvironment ,
2023
- source : string ,
2024
- root : string ,
2025
- options : StylusStylePreprocessorOptions ,
2012
+ options : Options ,
2026
2013
resolvers : CSSAtImportResolvers ,
2027
2014
) => StylePreprocessorResults | Promise < StylePreprocessorResults >
2028
2015
close : ( ) => void
@@ -2176,7 +2163,10 @@ const makeScssWorker = (
2176
2163
sassPath : string ,
2177
2164
data : string ,
2178
2165
// additionalData can a function that is not cloneable but it won't be used
2179
- options : SassStylePreprocessorOptions & { additionalData : undefined } ,
2166
+ options : SassStylePreprocessorInternalOptions & {
2167
+ api : 'legacy'
2168
+ additionalData : undefined
2169
+ } ,
2180
2170
) => {
2181
2171
// eslint-disable-next-line no-restricted-globals -- this function runs inside a cjs worker
2182
2172
const sass : typeof Sass = require ( sassPath )
@@ -2204,6 +2194,8 @@ const makeScssWorker = (
2204
2194
}
2205
2195
2206
2196
const finalOptions : Sass . LegacyOptions < 'async' > = {
2197
+ // support @import from node dependencies by default
2198
+ includePaths : [ 'node_modules' ] ,
2207
2199
...options ,
2208
2200
data,
2209
2201
file : options . filename ,
@@ -2287,7 +2279,10 @@ const makeModernScssWorker = (
2287
2279
sassPath : string ,
2288
2280
data : string ,
2289
2281
// additionalData can a function that is not cloneable but it won't be used
2290
- options : SassStylePreprocessorOptions & { additionalData : undefined } ,
2282
+ options : SassStylePreprocessorInternalOptions & {
2283
+ api : 'modern'
2284
+ additionalData : undefined
2285
+ } ,
2291
2286
) => {
2292
2287
// eslint-disable-next-line no-restricted-globals -- this function runs inside a cjs worker
2293
2288
const sass : typeof Sass = require ( sassPath )
@@ -2453,8 +2448,15 @@ type ScssWorkerResult = {
2453
2448
2454
2449
const scssProcessor = (
2455
2450
maxWorkers : number | undefined ,
2456
- ) : SassStylePreprocessor => {
2457
- const workerMap = new Map < unknown , ReturnType < typeof makeScssWorker > > ( )
2451
+ ) : StylePreprocessor < SassStylePreprocessorInternalOptions > => {
2452
+ const workerMap = new Map <
2453
+ unknown ,
2454
+ ReturnType <
2455
+ | typeof makeScssWorker
2456
+ | typeof makeModernScssWorker
2457
+ | typeof makeModernCompilerScssWorker
2458
+ >
2459
+ > ( )
2458
2460
2459
2461
return {
2460
2462
close ( ) {
@@ -2511,6 +2513,7 @@ const scssProcessor = (
2511
2513
const result = await worker . run (
2512
2514
sassPackage . path ,
2513
2515
data ,
2516
+ // @ts -expect-error the correct worker is selected for `options.type`
2514
2517
optionsWithoutAdditionalData ,
2515
2518
)
2516
2519
const deps = result . stats . includedFiles . map ( ( f ) => cleanScssBugUrl ( f ) )
@@ -2706,7 +2709,9 @@ const makeLessWorker = (
2706
2709
lessPath : string ,
2707
2710
content : string ,
2708
2711
// additionalData can a function that is not cloneable but it won't be used
2709
- options : StylePreprocessorOptions & { additionalData : undefined } ,
2712
+ options : LessStylePreprocessorInternalOptions & {
2713
+ additionalData : undefined
2714
+ } ,
2710
2715
) => {
2711
2716
// eslint-disable-next-line no-restricted-globals -- this function runs inside a cjs worker
2712
2717
const nodeLess : typeof Less = require ( lessPath )
@@ -2715,6 +2720,8 @@ const makeLessWorker = (
2715
2720
options . filename ,
2716
2721
)
2717
2722
const result = await nodeLess . render ( content , {
2723
+ // support @import from node dependencies by default
2724
+ paths : [ 'node_modules' ] ,
2718
2725
...options ,
2719
2726
plugins : [ viteResolverPlugin , ...( options . plugins || [ ] ) ] ,
2720
2727
...( options . enableSourcemap
@@ -2734,15 +2741,17 @@ const makeLessWorker = (
2734
2741
shouldUseFake ( _lessPath , _content , options ) {
2735
2742
// plugins are a function and is not serializable
2736
2743
// in that case, fallback to running in main thread
2737
- return options . plugins ? .length > 0
2744
+ return ! ! options . plugins && options . plugins . length > 0
2738
2745
} ,
2739
2746
max : maxWorkers ,
2740
2747
} ,
2741
2748
)
2742
2749
return worker
2743
2750
}
2744
2751
2745
- const lessProcessor = ( maxWorkers : number | undefined ) : StylePreprocessor => {
2752
+ const lessProcessor = (
2753
+ maxWorkers : number | undefined ,
2754
+ ) : StylePreprocessor < LessStylePreprocessorInternalOptions > => {
2746
2755
const workerMap = new Map < unknown , ReturnType < typeof makeLessWorker > > ( )
2747
2756
2748
2757
return {
@@ -2820,12 +2829,18 @@ const makeStylWorker = (maxWorkers: number | undefined) => {
2820
2829
content : string ,
2821
2830
root : string ,
2822
2831
// additionalData can a function that is not cloneable but it won't be used
2823
- options : StylusStylePreprocessorOptions & { additionalData : undefined } ,
2832
+ options : StylusStylePreprocessorInternalOptions & {
2833
+ additionalData : undefined
2834
+ } ,
2824
2835
) => {
2825
2836
// eslint-disable-next-line no-restricted-globals -- this function runs inside a cjs worker
2826
2837
const nodeStylus : typeof Stylus = require ( stylusPath )
2827
2838
2828
- const ref = nodeStylus ( content , options )
2839
+ const ref = nodeStylus ( content , {
2840
+ // support @import from node dependencies by default
2841
+ paths : [ 'node_modules' ] ,
2842
+ ...options ,
2843
+ } )
2829
2844
if ( options . define ) {
2830
2845
for ( const key in options . define ) {
2831
2846
ref . define ( key , options . define [ key ] )
@@ -2864,7 +2879,7 @@ const makeStylWorker = (maxWorkers: number | undefined) => {
2864
2879
2865
2880
const stylProcessor = (
2866
2881
maxWorkers : number | undefined ,
2867
- ) : StylusStylePreprocessor => {
2882
+ ) : StylePreprocessor < StylusStylePreprocessorInternalOptions > => {
2868
2883
const workerMap = new Map < unknown , ReturnType < typeof makeStylWorker > > ( )
2869
2884
2870
2885
return {
@@ -2981,21 +2996,23 @@ const createPreprocessorWorkerController = (maxWorkers: number | undefined) => {
2981
2996
const less = lessProcessor ( maxWorkers )
2982
2997
const styl = stylProcessor ( maxWorkers )
2983
2998
2984
- const sassProcess : StylePreprocessor [ 'process' ] = (
2985
- environment ,
2986
- source ,
2987
- root ,
2988
- options ,
2989
- resolvers ,
2990
- ) => {
2991
- return scss . process (
2992
- environment ,
2993
- source ,
2994
- root ,
2995
- { ...options , indentedSyntax : true , syntax : 'indented' } ,
2996
- resolvers ,
2997
- )
2998
- }
2999
+ const sassProcess : StylePreprocessor < SassStylePreprocessorInternalOptions > [ 'process' ] =
3000
+ ( environment , source , root , options , resolvers ) => {
3001
+ let opts : SassStylePreprocessorInternalOptions
3002
+ if ( options . api === 'modern' || options . api === 'modern-compiler' ) {
3003
+ opts = { ...options , syntax : 'indented' as const }
3004
+ } else {
3005
+ const narrowedOptions =
3006
+ options as SassStylePreprocessorInternalOptions & {
3007
+ api ?: 'legacy'
3008
+ }
3009
+ opts = {
3010
+ ...narrowedOptions ,
3011
+ indentedSyntax : true ,
3012
+ }
3013
+ }
3014
+ return scss . process ( environment , source , root , opts , resolvers )
3015
+ }
2999
3016
3000
3017
const close = ( ) => {
3001
3018
less . close ( )
0 commit comments