diff --git a/packages/vite/package.json b/packages/vite/package.json index 935a1dffa13461..673aa089b3380a 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -136,6 +136,7 @@ "less": "*", "sass": "*", "stylus": "*", + "sugarss": "*", "terser": "^5.4.0" }, "peerDependenciesMeta": { @@ -148,6 +149,9 @@ "less": { "optional": true }, + "sugarss": { + "optional": true + }, "terser": { "optional": true } diff --git a/packages/vite/src/node/optimizer/scan.ts b/packages/vite/src/node/optimizer/scan.ts index a3b799dc91542b..e600e7ff69f96f 100644 --- a/packages/vite/src/node/optimizer/scan.ts +++ b/packages/vite/src/node/optimizer/scan.ts @@ -427,7 +427,8 @@ function esbuildScanPlugin( // css & json & wasm build.onResolve( { - filter: /\.(css|less|sass|scss|styl|stylus|pcss|postcss|json|wasm)$/ + filter: + /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss|json|wasm)$/ }, externalUnlessEntry ) diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index bcc5359d17ca93..cacde725fcc91f 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -104,7 +104,7 @@ export interface CSSModulesOptions { | null } -const cssLangs = `\\.(css|less|sass|scss|styl|stylus|pcss|postcss)($|\\?)` +const cssLangs = `\\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)($|\\?)` const cssLangRE = new RegExp(cssLangs) const cssModuleRE = new RegExp(`\\.module${cssLangs}`) const directRequestRE = /(\?|&)direct\b/ @@ -127,7 +127,13 @@ const enum PreprocessLang { const enum PureCssLang { css = 'css' } -type CssLang = keyof typeof PureCssLang | keyof typeof PreprocessLang +const enum PostCssDialectLang { + sss = 'sugarss' +} +type CssLang = + | keyof typeof PureCssLang + | keyof typeof PreprocessLang + | keyof typeof PostCssDialectLang export const isCSSRequest = (request: string): boolean => cssLangRE.test(request) @@ -150,10 +156,10 @@ export const removedPureCssFilesCache = new WeakMap< export const cssEntryFilesCache = new WeakMap>() -const postcssConfigCache = new WeakMap< - ResolvedConfig, - PostCSSConfigResult | null ->() +const postcssConfigCache: Record< + string, + WeakMap +> = {} function encodePublicUrlsInCSS(config: ResolvedConfig) { return config.command === 'build' @@ -752,8 +758,8 @@ async function compileCSS( // crawl them in order to register watch dependencies. const needInlineImport = code.includes('@import') const hasUrl = cssUrlRE.test(code) || cssImageSetRE.test(code) - const postcssConfig = await resolvePostcssConfig(config) const lang = id.match(cssLangRE)?.[1] as CssLang | undefined + const postcssConfig = await resolvePostcssConfig(config, getCssDialect(lang)) // 1. plain css that needs no processing if ( @@ -827,6 +833,15 @@ async function compileCSS( // 3. postcss const postcssOptions = (postcssConfig && postcssConfig.options) || {} + + // for sugarss change parser + if (lang === 'sss') { + postcssOptions.parser = loadPreprocessor( + PostCssDialectLang.sss, + config.root + ) + } + const postcssPlugins = postcssConfig && postcssConfig.plugins ? postcssConfig.plugins.slice() : [] @@ -1050,9 +1065,15 @@ interface PostCSSConfigResult { } async function resolvePostcssConfig( - config: ResolvedConfig + config: ResolvedConfig, + dialect = 'css' ): Promise { - let result = postcssConfigCache.get(config) + postcssConfigCache[dialect] ??= new WeakMap< + ResolvedConfig, + PostCSSConfigResult | null + >() + + let result = postcssConfigCache[dialect].get(config) if (result !== undefined) { return result } @@ -1089,7 +1110,7 @@ async function resolvePostcssConfig( } } - postcssConfigCache.set(config, result) + postcssConfigCache[dialect].set(config, result) return result } @@ -1385,7 +1406,9 @@ export interface StylePreprocessorResults { deps: string[] } -const loadedPreprocessors: Partial> = {} +const loadedPreprocessors: Partial< + Record +> = {} // TODO: use dynamic import const _require = createRequire(import.meta.url) @@ -1397,7 +1420,14 @@ function loadPreprocessor( lang: PreprocessLang.stylus, root: string ): typeof Stylus -function loadPreprocessor(lang: PreprocessLang, root: string): any { +function loadPreprocessor( + lang: PostCssDialectLang.sss, + root: string +): PostCSS.Parser +function loadPreprocessor( + lang: PreprocessLang | PostCssDialectLang, + root: string +): any { if (lang in loadedPreprocessors) { return loadedPreprocessors[lang] } @@ -1809,3 +1839,7 @@ const preProcessors = Object.freeze({ function isPreProcessor(lang: any): lang is PreprocessLang { return lang && lang in preProcessors } + +function getCssDialect(lang: CssLang | undefined): string { + return lang === 'sss' ? 'sss' : 'css' +} diff --git a/playground/css/__tests__/css.spec.ts b/playground/css/__tests__/css.spec.ts index 689e9c925f644a..833063f61c7497 100644 --- a/playground/css/__tests__/css.spec.ts +++ b/playground/css/__tests__/css.spec.ts @@ -457,6 +457,28 @@ test.runIf(isBuild)('warning can be suppressed by esbuild.logOverride', () => { }) }) +test('sugarss', async () => { + const imported = await page.$('.sugarss') + const atImport = await page.$('.sugarss-at-import') + const atImportAlias = await page.$('.sugarss-at-import-alias') + + expect(await getColor(imported)).toBe('blue') + expect(await getColor(atImport)).toBe('darkslateblue') + expect(await getBg(atImport)).toMatch(isBuild ? /base64/ : '/nested/icon.png') + expect(await getColor(atImportAlias)).toBe('darkslateblue') + expect(await getBg(atImportAlias)).toMatch( + isBuild ? /base64/ : '/nested/icon.png' + ) + + editFile('sugarss.sss', (code) => code.replace('color: blue', 'color: coral')) + await untilUpdated(() => getColor(imported), 'coral') + + editFile('nested/nested.sss', (code) => + code.replace('color: darkslateblue', 'color: blue') + ) + await untilUpdated(() => getColor(atImport), 'blue') +}) + // NOTE: the match inline snapshot should generate by build mode test('async css order', async () => { await withRetry(async () => { diff --git a/playground/css/index.html b/playground/css/index.html index 61d0c2edce5bb8..799cfebf7adbe3 100644 --- a/playground/css/index.html +++ b/playground/css/index.html @@ -73,6 +73,17 @@

CSS

Imported Stylus string:


 
+  

SugarSS: This should be blue

+

+ @import from SugarSS: This should be darkslateblue and have bg image +

+

+ @import from SugarSS: This should be darkslateblue and have bg image which + url contains alias +

+

Imported SugarSS string:

+

+
   

CSS modules: this should be turquoise

Imported CSS module:


diff --git a/playground/css/main.js b/playground/css/main.js
index 4e1ed37754968f..68299638b78369 100644
--- a/playground/css/main.js
+++ b/playground/css/main.js
@@ -3,6 +3,9 @@ import './minify.css'
 import css from './imported.css'
 text('.imported-css', css)
 
+import sugarss from './sugarss.sss'
+text('.imported-sugarss', sugarss)
+
 import sass from './sass.scss'
 text('.imported-sass', sass)
 
diff --git a/playground/css/nested/nested.sss b/playground/css/nested/nested.sss
new file mode 100644
index 00000000000000..2de4c96564a100
--- /dev/null
+++ b/playground/css/nested/nested.sss
@@ -0,0 +1,8 @@
+.sugarss-at-import
+  color: darkslateblue
+  background: url(./icon.png) 10px no-repeat
+
+
+.sugarss-at-import-alias
+  color: darkslateblue
+  background: url(@/nested/icon.png) 10px no-repeat
diff --git a/playground/css/package.json b/playground/css/package.json
index a0d2bb41a00edf..5081b5462c2fa4 100644
--- a/playground/css/package.json
+++ b/playground/css/package.json
@@ -18,6 +18,7 @@
     "less": "^4.1.3",
     "postcss-nested": "^5.0.6",
     "sass": "^1.55.0",
-    "stylus": "^0.59.0"
+    "stylus": "^0.59.0",
+    "sugarss": "^4.0.1"
   }
 }
diff --git a/playground/css/sugarss.sss b/playground/css/sugarss.sss
new file mode 100644
index 00000000000000..cd393b10519041
--- /dev/null
+++ b/playground/css/sugarss.sss
@@ -0,0 +1,4 @@
+@import '@/nested/nested.sss'
+
+.sugarss
+  color: blue
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3eeeda1cd37e39..e2d70f6ebe482d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -399,6 +399,7 @@ importers:
       postcss-nested: ^5.0.6
       sass: ^1.55.0
       stylus: ^0.59.0
+      sugarss: ^4.0.1
     devDependencies:
       css-dep: link:css-dep
       css-js-dep: file:playground/css/css-js-dep
@@ -407,6 +408,7 @@ importers:
       postcss-nested: 5.0.6
       sass: 1.55.0
       stylus: 0.59.0
+      sugarss: 4.0.1
 
   playground/css-codesplit:
     specifiers: {}
@@ -8211,6 +8213,13 @@ packages:
       - supports-color
     dev: true
 
+  /sugarss/4.0.1:
+    resolution: {integrity: sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw==}
+    engines: {node: '>=12.0'}
+    peerDependencies:
+      postcss: ^8.3.3
+    dev: true
+
   /supports-color/5.5.0:
     resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
     engines: {node: '>=4'}