Skip to content

Commit ec6bbc4

Browse files
committed
feat(css): export PostCSS config type for type-safe configs
Re-exports the PostCSS user config shape as `PostcssUserConfig` from `vite`, so PostCSS configs can be typed against the same definition Vite loads them with: ```ts import type { PostcssUserConfig } from 'vite' const config: PostcssUserConfig = { plugins: [] } export default config ``` The type mirrors `postcss-load-config`'s `Config`. `postcss-load-config` is bundled into Vite and its declarations use CommonJS `export =` syntax, which `rolldown-plugin-dts` cannot re-export through the bundled `.d.ts`, so the shape is mirrored from `postcss` types instead. A type-level test asserts the mirror stays structurally identical to the upstream `Config`, so it cannot silently drift. Closes #19109
1 parent 869e8ea commit ec6bbc4

3 files changed

Lines changed: 56 additions & 0 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Tests the `PostcssUserConfig` type re-exported from `vite`. It mirrors the
2+
// `Config` type of `postcss-load-config` (which is bundled into Vite and cannot
3+
// be re-exported through the bundled `.d.ts`, see #19109). The `Equal` case
4+
// below locks the two together so the mirror can never silently drift.
5+
import type { Equal, ExpectTrue } from '@type-challenges/utils'
6+
import type { Config as PostcssLoadConfig } from 'postcss-load-config'
7+
import type { PostcssUserConfig } from '../index'
8+
9+
export type cases = [ExpectTrue<Equal<PostcssUserConfig, PostcssLoadConfig>>]
10+
11+
// The array plugin format is accepted.
12+
;({ plugins: [] }) satisfies PostcssUserConfig
13+
14+
// The object plugin format is accepted, alongside the other options.
15+
;({
16+
map: false,
17+
from: 'src/index.css',
18+
plugins: { 'postcss-preset-env': {} },
19+
}) satisfies PostcssUserConfig
20+
21+
;({
22+
// @ts-expect-error --- `map` must be `string | false`, not a number
23+
map: 123,
24+
}) satisfies PostcssUserConfig
25+
26+
export {}

packages/vite/src/node/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export type {
200200
CSSModulesOptions,
201201
PreprocessCSSResult,
202202
ResolvedCSSOptions,
203+
PostcssUserConfig,
203204
SassPreprocessorOptions,
204205
LessPreprocessorOptions,
205206
StylusPreprocessorOptions,

packages/vite/src/node/plugins/css.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,35 @@ import { IIFE_BEGIN_RE, UMD_BEGIN_RE } from './oxc'
111111
const decoder = new TextDecoder()
112112
// const debug = createDebugger('vite:css')
113113

114+
/**
115+
* The shape of a PostCSS config file (e.g. `postcss.config.js`), matching the
116+
* `Config` type of the `postcss-load-config` version that Vite uses to load it.
117+
* Use it to write a type-safe PostCSS config:
118+
*
119+
* ```ts
120+
* import type { PostcssUserConfig } from 'vite'
121+
*
122+
* const config: PostcssUserConfig = { plugins: [] }
123+
* export default config
124+
* ```
125+
*
126+
* `postcss-load-config` is bundled into Vite and its types are declared with
127+
* CommonJS `export =` syntax, so they cannot be re-exported through the bundled
128+
* `.d.ts`. This mirrors its `Config` type instead, and a type-level test in
129+
* `__tests_dts__/postcss.ts` asserts the two stay structurally identical.
130+
*/
131+
export type PostcssUserConfig = {
132+
parser?: string | PostCSS.ProcessOptions['parser'] | false
133+
stringifier?: string | PostCSS.ProcessOptions['stringifier'] | false
134+
syntax?: string | PostCSS.ProcessOptions['syntax'] | false
135+
map?: string | false
136+
from?: string
137+
to?: string
138+
plugins?:
139+
| Array<PostCSS.Transformer | PostCSS.Plugin | PostCSS.Processor | false>
140+
| Record<string, object | false>
141+
}
142+
114143
export interface CSSOptions {
115144
/**
116145
* Using lightningcss is an experimental option to handle CSS modules,

0 commit comments

Comments
 (0)