@fastkit/plugboy-vanilla-extract-plugin 4.0.0-next.1 β 4.0.0-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -0
- package/dist/css.d.mts +21 -1
- package/dist/css.mjs +61 -10
- package/dist/css.mjs.map +1 -1
- package/dist/plugboy-vanilla-extract-plugin.d.mts +5 -5
- package/dist/plugboy-vanilla-extract-plugin.mjs +100 -207
- package/dist/plugboy-vanilla-extract-plugin.mjs.map +1 -1
- package/package.json +7 -7
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# @fastkit/plugboy-vanilla-extract-plugin
|
|
2
|
+
|
|
3
|
+
π English | [ζ₯ζ¬θͺ](https://github.com/dadajam4/fastkit/blob/main/packages/plugboy-vanilla-extract-plugin/README-ja.md)
|
|
4
|
+
|
|
5
|
+
A plugin that integrates [Vanilla Extract](https://vanilla-extract.style/) into [Plugboy](https://github.com/dadajam4/fastkit/blob/main/packages/plugboy/README.md) builds. It bundles the CSS extracted from `.css.ts` files into a single stylesheet per package, ships a Vite plugin for development, and provides helpers for working with cascade layers.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Single CSS output**: Combines the styles extracted from a package's `.css.ts` files into one `dist/<package>.css`.
|
|
10
|
+
- **Automatic merge with plain CSS**: Merges tsdown's output for plain `.css` / `.scss` with the Vanilla Extract output into a single file (preventing the style loss caused by a file-name collision between the two).
|
|
11
|
+
- **Vite integration**: Ships a Vite plugin for use in dev servers, Storybook, and similar environments.
|
|
12
|
+
- **Layer helpers**: `@fastkit/plugboy-vanilla-extract-plugin/css` exposes utilities for defining cascade layers in a type-safe way.
|
|
13
|
+
|
|
14
|
+
> [!NOTE]
|
|
15
|
+
> Preserving external `@import`s (e.g. `@import url('material-symbols/rounded.css') layer(...)`) and ordering `@layer` declarations are handled by [Plugboy](https://github.com/dadajam4/fastkit/blob/main/packages/plugboy/README.md) itself, and apply to the CSS this plugin combines as well.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -D @fastkit/plugboy-vanilla-extract-plugin
|
|
21
|
+
# or
|
|
22
|
+
pnpm add -D @fastkit/plugboy-vanilla-extract-plugin
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> [!NOTE]
|
|
26
|
+
> Requires `@fastkit/plugboy` (and `vite`, when using the Vite integration) as peer dependencies.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### 1. Register in the build
|
|
31
|
+
|
|
32
|
+
Add it to the `plugins` of `plugboy.project.ts` (project-wide) or a per-workspace `plugboy.workspace.ts`. It activates automatically for packages that contain `.css.ts` files.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { defineProjectConfig } from '@fastkit/plugboy';
|
|
36
|
+
import { createVanillaExtractPlugin } from '@fastkit/plugboy-vanilla-extract-plugin';
|
|
37
|
+
|
|
38
|
+
export default defineProjectConfig({
|
|
39
|
+
plugins: [
|
|
40
|
+
createVanillaExtractPlugin({
|
|
41
|
+
// Identifier format for class names etc. ('short' recommended for production)
|
|
42
|
+
identifiers: 'short',
|
|
43
|
+
}),
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
On build, the package styles are combined into `dist/<package>.css`.
|
|
49
|
+
|
|
50
|
+
### 2. Use with Vite (dev / Storybook, etc.)
|
|
51
|
+
|
|
52
|
+
For environments that resolve Vanilla Extract without a Plugboy build (Vite dev server, Storybook, etc.), use the Vite plugin.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { defineConfig } from 'vite';
|
|
56
|
+
import { ViteVanillaExtractPlugin } from '@fastkit/plugboy-vanilla-extract-plugin';
|
|
57
|
+
|
|
58
|
+
export default defineConfig({
|
|
59
|
+
plugins: [
|
|
60
|
+
ViteVanillaExtractPlugin({
|
|
61
|
+
identifiers: 'debug',
|
|
62
|
+
}),
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Cascade layer helpers (`/css`)
|
|
68
|
+
|
|
69
|
+
`@fastkit/plugboy-vanilla-extract-plugin/css` lets you define nestable cascade layers in a type-safe way.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { defineLayerStyle } from '@fastkit/plugboy-vanilla-extract-plugin/css';
|
|
73
|
+
|
|
74
|
+
export const framework = defineLayerStyle({ globalName: 'my-ui' });
|
|
75
|
+
|
|
76
|
+
export const base = framework.defineNestedLayer({ globalName: 'base' });
|
|
77
|
+
export const component = framework.defineNestedLayer({ globalName: 'component' });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Options
|
|
81
|
+
|
|
82
|
+
The main options accepted by `createVanillaExtractPlugin(options)` / `ViteVanillaExtractPlugin(options)`.
|
|
83
|
+
|
|
84
|
+
| Option | Type | Description |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| `identifiers` | `'short' \| 'debug' \| ((meta) => string)` | Format of generated identifiers such as class names. Use `'short'` for production builds and `'debug'` while debugging. |
|
|
87
|
+
| `esbuildOptions` | `EsbuildOptions` | Options forwarded to esbuild when compiling `.css.ts` files. |
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
[MIT](https://github.com/dadajam4/fastkit/blob/main/LICENSE)
|
package/dist/css.d.mts
CHANGED
|
@@ -69,7 +69,27 @@ interface DefineLayerGlobalOptions<CustomRules extends CustomStyleRules | null =
|
|
|
69
69
|
}
|
|
70
70
|
type DefineLayerOptions<CustomRules extends CustomStyleRules | null = null> = DefineLayerScopedOptions<CustomRules> | DefineLayerGlobalOptions<CustomRules>;
|
|
71
71
|
type DefineNestableLayerOptions<CustomRules extends CustomStyleRules | null = null> = DefineLayerOptions<CustomRules> & DefineLayerParentOptions;
|
|
72
|
+
/**
|
|
73
|
+
* Re-construct a {@link LayerStyle} from a name that was already resolved at
|
|
74
|
+
* build time, WITHOUT re-running `layer()` / `globalLayer()`.
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* This is the runtime counterpart used by the function serializer (see
|
|
78
|
+
* {@link defineLayerStyle}). Because the `layerName` is passed verbatim, the
|
|
79
|
+
* re-constructed instance points at the exact layer that was emitted into CSS at
|
|
80
|
+
* build time β even for scoped layers whose name is a non-deterministic hash that
|
|
81
|
+
* could never be reproduced by calling `layer()` again.
|
|
82
|
+
*
|
|
83
|
+
* The re-constructed instance carries only deterministic state (`layerName` /
|
|
84
|
+
* `parentLayerName`). It has no `hooks` and none of the additions made via
|
|
85
|
+
* `extend()`; it is a reference handle to an already-built layer, not a fresh
|
|
86
|
+
* style-defining entry point.
|
|
87
|
+
*
|
|
88
|
+
* @internal Not part of the public API; exported only so the serializer's
|
|
89
|
+
* `importName` can resolve it at runtime.
|
|
90
|
+
*/
|
|
91
|
+
declare function defineLayerStyleFromResolvedName<CustomRules extends CustomStyleRules | null = null>(layerName: string, parentLayerName: string | null): LayerStyle<CustomRules>;
|
|
72
92
|
declare function defineLayerStyle<CustomRules extends CustomStyleRules | null = null>(globalNameOrOptions?: string | DefineNestableLayerOptions<CustomRules>): LayerStyle<CustomRules>;
|
|
73
93
|
//#endregion
|
|
74
|
-
export { DefineLayerBaseOptions, DefineLayerGlobalOptions, DefineLayerOptions, DefineLayerParentOptions, DefineLayerScopedOptions, DefineNestableLayerOptions, LayerStyle, defineLayerStyle };
|
|
94
|
+
export { DefineLayerBaseOptions, DefineLayerGlobalOptions, DefineLayerOptions, DefineLayerParentOptions, DefineLayerScopedOptions, DefineNestableLayerOptions, LayerStyle, defineLayerStyle, defineLayerStyleFromResolvedName };
|
|
75
95
|
//# sourceMappingURL=css.d.mts.map
|
package/dist/css.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createThemeContract, globalLayer, globalStyle, layer, style } from "@vanilla-extract/css";
|
|
2
|
-
|
|
2
|
+
import { addFunctionSerializer } from "@vanilla-extract/css/functionSerializer";
|
|
3
3
|
//#region src/css/utils.ts
|
|
4
4
|
function get(obj, path) {
|
|
5
5
|
let result = obj;
|
|
@@ -27,7 +27,6 @@ function assignVars(varContract, tokens) {
|
|
|
27
27
|
});
|
|
28
28
|
return varSetters;
|
|
29
29
|
}
|
|
30
|
-
|
|
31
30
|
//#endregion
|
|
32
31
|
//#region src/css/theme.ts
|
|
33
32
|
function createGlobalTheme$1(layerName, selector, arg2, arg3) {
|
|
@@ -37,7 +36,6 @@ function createGlobalTheme$1(layerName, selector, arg2, arg3) {
|
|
|
37
36
|
globalStyle(selector, { "@layer": { [layerName]: { vars: assignVars(themeVars, tokens) } } });
|
|
38
37
|
if (shouldCreateVars) return themeVars;
|
|
39
38
|
}
|
|
40
|
-
|
|
41
39
|
//#endregion
|
|
42
40
|
//#region src/css/layer.ts
|
|
43
41
|
function isGlobalOptions(options) {
|
|
@@ -48,10 +46,20 @@ function normalizeToObject(source) {
|
|
|
48
46
|
if (typeof source === "string") return { globalName: source };
|
|
49
47
|
return source;
|
|
50
48
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Assemble a {@link LayerStyle} object for an already-resolved `layerName`.
|
|
51
|
+
*
|
|
52
|
+
* @remarks
|
|
53
|
+
* This contains the whole object-building logic shared by {@link defineLayerStyle}
|
|
54
|
+
* (build-time, where `layerName` was just produced by `layer()`/`globalLayer()`)
|
|
55
|
+
* and {@link defineLayerStyleFromResolvedName} (runtime re-construction, where
|
|
56
|
+
* `layerName` is the deterministic value carried over from build time). It must
|
|
57
|
+
* NOT call any `@vanilla-extract/css` side-effecting API (no `layer()` /
|
|
58
|
+
* `globalLayer()`), so that re-construction never regenerates a hash-based name.
|
|
59
|
+
*
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
function buildLayerStyle(layerName, parentLayerName, hooks) {
|
|
55
63
|
const layerStyle = function layerStyle(rule, debugId) {
|
|
56
64
|
const rules = Array.isArray(rule) ? rule : [rule];
|
|
57
65
|
const layerAppliedRules = rules.map((_rule) => {
|
|
@@ -66,7 +74,7 @@ function defineLayerStyle(globalNameOrOptions) {
|
|
|
66
74
|
return style(layerAppliedRules, debugId);
|
|
67
75
|
};
|
|
68
76
|
layerStyle.layerName = layerName;
|
|
69
|
-
layerStyle.parentLayerName =
|
|
77
|
+
layerStyle.parentLayerName = parentLayerName;
|
|
70
78
|
layerStyle.style = layerStyle;
|
|
71
79
|
layerStyle.hooks = hooks;
|
|
72
80
|
layerStyle.global = function layerGlobalStyle(selector, rule) {
|
|
@@ -102,7 +110,50 @@ function defineLayerStyle(globalNameOrOptions) {
|
|
|
102
110
|
};
|
|
103
111
|
return layerStyle;
|
|
104
112
|
}
|
|
105
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Re-construct a {@link LayerStyle} from a name that was already resolved at
|
|
115
|
+
* build time, WITHOUT re-running `layer()` / `globalLayer()`.
|
|
116
|
+
*
|
|
117
|
+
* @remarks
|
|
118
|
+
* This is the runtime counterpart used by the function serializer (see
|
|
119
|
+
* {@link defineLayerStyle}). Because the `layerName` is passed verbatim, the
|
|
120
|
+
* re-constructed instance points at the exact layer that was emitted into CSS at
|
|
121
|
+
* build time β even for scoped layers whose name is a non-deterministic hash that
|
|
122
|
+
* could never be reproduced by calling `layer()` again.
|
|
123
|
+
*
|
|
124
|
+
* The re-constructed instance carries only deterministic state (`layerName` /
|
|
125
|
+
* `parentLayerName`). It has no `hooks` and none of the additions made via
|
|
126
|
+
* `extend()`; it is a reference handle to an already-built layer, not a fresh
|
|
127
|
+
* style-defining entry point.
|
|
128
|
+
*
|
|
129
|
+
* @internal Not part of the public API; exported only so the serializer's
|
|
130
|
+
* `importName` can resolve it at runtime.
|
|
131
|
+
*/
|
|
132
|
+
function defineLayerStyleFromResolvedName(layerName, parentLayerName) {
|
|
133
|
+
return buildLayerStyle(layerName, parentLayerName, {});
|
|
134
|
+
}
|
|
135
|
+
function defineLayerStyle(globalNameOrOptions) {
|
|
136
|
+
const options = normalizeToObject(globalNameOrOptions);
|
|
137
|
+
const { parent, hooks = {} } = options;
|
|
138
|
+
const isGlobal = isGlobalOptions(options);
|
|
139
|
+
const layerName = isGlobal ? globalLayer({ parent }, options.globalName) : layer({ parent }, options.debugId);
|
|
140
|
+
const layerStyle = buildLayerStyle(layerName, parent || null, hooks);
|
|
141
|
+
if (isGlobal) addFunctionSerializer(layerStyle, {
|
|
142
|
+
importPath: "@fastkit/plugboy-vanilla-extract-plugin/css",
|
|
143
|
+
importName: "defineLayerStyle",
|
|
144
|
+
args: [{
|
|
145
|
+
globalName: options.globalName,
|
|
146
|
+
...parent ? { parent } : {}
|
|
147
|
+
}]
|
|
148
|
+
});
|
|
149
|
+
else addFunctionSerializer(layerStyle, {
|
|
150
|
+
importPath: "@fastkit/plugboy-vanilla-extract-plugin/css",
|
|
151
|
+
importName: "defineLayerStyleFromResolvedName",
|
|
152
|
+
args: [layerName, parent || null]
|
|
153
|
+
});
|
|
154
|
+
return layerStyle;
|
|
155
|
+
}
|
|
106
156
|
//#endregion
|
|
107
|
-
export { defineLayerStyle };
|
|
157
|
+
export { defineLayerStyle, defineLayerStyleFromResolvedName };
|
|
158
|
+
|
|
108
159
|
//# sourceMappingURL=css.mjs.map
|
package/dist/css.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css.mjs","names":["createGlobalTheme","createGlobalTheme"],"sources":["../src/css/utils.ts","../src/css/theme.ts","../src/css/layer.ts"],"sourcesContent":["/* eslint-disable no-console */\nimport { Contract, MapLeafNodes, CSSVarFunction } from './types';\n\ntype Primitive = string | number | null | undefined;\n\ntype Walkable = {\n [Key in string | number]: Primitive | Walkable;\n};\n\nexport function get(obj: any, path: Array<string>) {\n let result = obj;\n\n for (const key of path) {\n if (!(key in result)) {\n throw new Error(`Path ${path.join(' -> ')} does not exist in object`);\n }\n result = result[key];\n }\n\n return result;\n}\nexport function walkObject<T extends Walkable, MapTo>(\n obj: T,\n fn: (value: Primitive, path: Array<string>) => MapTo,\n path: Array<string> = [],\n): MapLeafNodes<T, MapTo> {\n const clone = obj.constructor();\n\n for (const key in obj) {\n const value = obj[key];\n const currentPath = [...path, key];\n\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n value == null\n ) {\n clone[key] = fn(value as Primitive, currentPath);\n } else if (typeof value === 'object' && !Array.isArray(value)) {\n clone[key] = walkObject(value as Walkable, fn, currentPath);\n } else {\n console.warn(\n `Skipping invalid key \"${currentPath.join(\n '.',\n )}\". Should be a string, number, null or object. Received: \"${\n Array.isArray(value) ? 'Array' : typeof value\n }\"`,\n );\n }\n }\n\n return clone;\n}\n\nexport function assignVars<VarContract extends Contract>(\n varContract: VarContract,\n tokens: MapLeafNodes<VarContract, string>,\n): Record<CSSVarFunction, string> {\n const varSetters: { [cssVarName: string]: string } = {};\n // const { valid, diffString } = validateContract(varContract, tokens);\n\n // if (!valid) {\n // throw new Error(`Tokens don't match contract.\\n${diffString}`);\n // }\n\n walkObject(tokens, (value, path) => {\n varSetters[get(varContract, path)] = String(value);\n });\n\n return varSetters;\n}\n","import { createThemeContract, globalStyle } from '@vanilla-extract/css';\nimport { Tokens, ThemeVars, Contract, MapLeafNodes } from './types';\nimport { assignVars } from './utils';\n\nexport function createGlobalTheme<ThemeTokens extends Tokens>(\n layerName: string,\n selector: string,\n tokens: ThemeTokens,\n): ThemeVars<ThemeTokens>;\nexport function createGlobalTheme<ThemeContract extends Contract>(\n layerName: string,\n selector: string,\n themeContract: ThemeContract,\n tokens: MapLeafNodes<ThemeContract, string>,\n): void;\nexport function createGlobalTheme(\n layerName: string,\n selector: string,\n arg2: any,\n arg3?: any,\n): any {\n const shouldCreateVars = Boolean(!arg3);\n\n const themeVars = shouldCreateVars\n ? createThemeContract(arg2)\n : (arg2 as ThemeVars<any>);\n\n const tokens = shouldCreateVars ? arg2 : arg3;\n\n globalStyle(selector, {\n '@layer': {\n [layerName]: {\n vars: assignVars(themeVars, tokens),\n },\n },\n });\n\n // appendCss(\n // {\n // type: 'global',\n // selector: selector,\n // rule: { vars: assignVars(themeVars, tokens) },\n // },\n // getFileScope(),\n // );\n\n if (shouldCreateVars) {\n return themeVars;\n }\n}\n","import {\n style,\n layer,\n globalLayer,\n StyleRule,\n globalStyle,\n GlobalStyleRule,\n createGlobalTheme as _createGlobalTheme,\n} from '@vanilla-extract/css';\nimport { createGlobalTheme } from './theme';\n\ntype CustomStyleRules = Record<string, any>;\n\ntype _LayerStyleRules = NonNullable<StyleRule['@layer']>[string];\n\ntype LayerStyleRules<CustomRules extends CustomStyleRules | null = null> =\n CustomRules extends null ? _LayerStyleRules : _LayerStyleRules & CustomRules;\n\ntype ClassNames = string | ClassNames[];\n\ntype ComplexLayerStyleRule<CustomRules extends CustomStyleRules | null = null> =\n LayerStyleRules<CustomRules> | (LayerStyleRules<CustomRules> | ClassNames)[];\n\ntype _LayerGlobalStyleRules = NonNullable<GlobalStyleRule['@layer']>[string];\n\ntype LayerGlobalStyleRules<CustomRules extends CustomStyleRules | null = null> =\n CustomRules extends null\n ? _LayerGlobalStyleRules\n : _LayerGlobalStyleRules & CustomRules;\n\ntype AnyStyleRule<CustomRules extends CustomStyleRules | null = null> =\n | LayerStyleRules<CustomRules>\n | LayerGlobalStyleRules<CustomRules>;\n\ntype LayerStyleHooks<CustomRules extends CustomStyleRules | null = null> = {\n style?: (rule: ComplexLayerStyleRule<CustomRules>, debugId?: string) => void;\n global?: (selector: string, rule: LayerGlobalStyleRules<CustomRules>) => void;\n anyStyle?: (style: AnyStyleRule<CustomRules>) => void;\n};\n\nexport interface LayerStyle<\n CustomRules extends CustomStyleRules | null = null,\n> {\n layerName: string;\n parentLayerName: string | null;\n /**\n * @see {@link style}\n */\n (rule: ComplexLayerStyleRule<CustomRules>, debugId?: string): string;\n\n /**\n * @see {@link style}\n */\n style(rule: ComplexLayerStyleRule<CustomRules>, debugId?: string): string;\n\n /**\n * @see {@link globalStyle}\n */\n global(selector: string, rule: LayerGlobalStyleRules<CustomRules>): void;\n\n /**\n * @see {@link _createGlobalTheme}\n */\n globalTheme: typeof _createGlobalTheme;\n\n defineNestedLayer(\n globalNameOrNestedOptions?: string | DefineLayerOptions<CustomRules>,\n ): LayerStyle<CustomRules>;\n\n /**\n * Add global CSS variable with layer\n *\n * @remarks The vanilla-extract API is buggy when handling layered css variables.\n *\n * @param selector - selector\n * @param vars - variables\n */\n pushGlobalVars(selector: string, vars: Record<string, string>): void;\n\n /**\n * Output variables accumulated by `pushGlobalVars`.\n *\n * @remarks The vanilla-extract API is buggy when handling layered css variables.\n */\n dumpGlobalVars(): void;\n hooks: LayerStyleHooks<CustomRules>;\n}\n\nexport interface DefineLayerParentOptions {\n parent?: string;\n}\n\nexport interface DefineLayerBaseOptions<\n CustomRules extends CustomStyleRules | null = null,\n> {\n hooks?: LayerStyleHooks<CustomRules>;\n}\n\nexport interface DefineLayerScopedOptions<\n CustomRules extends CustomStyleRules | null = null,\n> extends DefineLayerBaseOptions<CustomRules> {\n /** Debug ID */\n debugId?: string;\n globalName?: never;\n}\n\nexport interface DefineLayerGlobalOptions<\n CustomRules extends CustomStyleRules | null = null,\n> extends DefineLayerBaseOptions<CustomRules> {\n debugId?: never;\n /** Parent layer name */\n globalName: string;\n}\n\nexport type DefineLayerOptions<\n CustomRules extends CustomStyleRules | null = null,\n> =\n | DefineLayerScopedOptions<CustomRules>\n | DefineLayerGlobalOptions<CustomRules>;\n\nexport type DefineNestableLayerOptions<\n CustomRules extends CustomStyleRules | null = null,\n> = DefineLayerOptions<CustomRules> & DefineLayerParentOptions;\n\nfunction isGlobalOptions(\n options: DefineLayerOptions<any>,\n): options is DefineLayerGlobalOptions {\n return 'globalName' in options;\n}\n\nfunction normalizeToObject<T extends DefineLayerOptions<any>>(\n source?: string | T,\n): T {\n if (!source) return {} as T;\n if (typeof source === 'string') return { globalName: source } as unknown as T;\n return source;\n}\n\nexport function defineLayerStyle<\n CustomRules extends CustomStyleRules | null = null,\n>(\n globalNameOrOptions?: string | DefineNestableLayerOptions<CustomRules>,\n): LayerStyle<CustomRules> {\n const options = normalizeToObject(globalNameOrOptions);\n const { parent, hooks = {} } = options;\n\n const layerName = isGlobalOptions(options)\n ? globalLayer({ parent }, options.globalName)\n : layer({ parent }, options.debugId);\n\n const layerStyle = function layerStyle(rule, debugId) {\n const rules = Array.isArray(rule) ? rule : [rule];\n const layerAppliedRules = rules.map((_rule) => {\n if (typeof _rule === 'string' || Array.isArray(_rule)) return _rule;\n return {\n '@layer': {\n [layerName]: _rule,\n },\n };\n });\n if (hooks.anyStyle) {\n for (const _rule of rules) {\n if (typeof _rule === 'string' || Array.isArray(_rule)) continue;\n hooks.anyStyle(_rule);\n }\n }\n hooks.style && hooks.style(rule, debugId);\n return style(layerAppliedRules, debugId);\n } as LayerStyle<CustomRules>;\n\n layerStyle.layerName = layerName;\n layerStyle.parentLayerName = parent || null;\n layerStyle.style = layerStyle;\n layerStyle.hooks = hooks;\n\n layerStyle.global = function layerGlobalStyle(\n selector: string,\n rule: LayerGlobalStyleRules<CustomRules>,\n ) {\n hooks.anyStyle && hooks.anyStyle(rule);\n hooks.global && hooks.global(selector, rule);\n\n return globalStyle(selector, {\n '@layer': {\n [layerName]: rule,\n },\n });\n };\n\n layerStyle.globalTheme = (...args: any) =>\n (createGlobalTheme as any)(layerName, ...args);\n\n let _varQueues: [string, Record<string, string>][] = [];\n\n layerStyle.pushGlobalVars = function pushGlobalVars(\n selector: string,\n vars: Record<string, string>,\n ) {\n let queue = _varQueues.find((q) => q[0] === selector);\n if (!queue) {\n queue = [selector, {}];\n _varQueues.push(queue);\n }\n Object.assign(queue[1], vars);\n };\n\n layerStyle.dumpGlobalVars = function dumpGlobalVars() {\n for (const [selector, vars] of _varQueues) {\n layerStyle.global(selector, {\n vars,\n } as any);\n }\n _varQueues = [];\n };\n\n layerStyle.defineNestedLayer = function defineNestedLayer(\n globalNameOrNestedOptions?: string | DefineLayerOptions<CustomRules>,\n ) {\n const _options = normalizeToObject(globalNameOrNestedOptions);\n const nestedHooks = _options.hooks;\n\n return defineLayerStyle({\n ..._options,\n hooks: {\n ...hooks,\n ...nestedHooks,\n },\n parent: layerName,\n });\n };\n\n return layerStyle;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"css.mjs","names":["createGlobalTheme","createGlobalTheme"],"sources":["../src/css/utils.ts","../src/css/theme.ts","../src/css/layer.ts"],"sourcesContent":["/* eslint-disable no-console */\nimport { Contract, MapLeafNodes, CSSVarFunction } from './types';\n\ntype Primitive = string | number | null | undefined;\n\ntype Walkable = {\n [Key in string | number]: Primitive | Walkable;\n};\n\nexport function get(obj: any, path: Array<string>) {\n let result = obj;\n\n for (const key of path) {\n if (!(key in result)) {\n throw new Error(`Path ${path.join(' -> ')} does not exist in object`);\n }\n result = result[key];\n }\n\n return result;\n}\nexport function walkObject<T extends Walkable, MapTo>(\n obj: T,\n fn: (value: Primitive, path: Array<string>) => MapTo,\n path: Array<string> = [],\n): MapLeafNodes<T, MapTo> {\n const clone = obj.constructor();\n\n for (const key in obj) {\n const value = obj[key];\n const currentPath = [...path, key];\n\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n value == null\n ) {\n clone[key] = fn(value as Primitive, currentPath);\n } else if (typeof value === 'object' && !Array.isArray(value)) {\n clone[key] = walkObject(value as Walkable, fn, currentPath);\n } else {\n console.warn(\n `Skipping invalid key \"${currentPath.join(\n '.',\n )}\". Should be a string, number, null or object. Received: \"${\n Array.isArray(value) ? 'Array' : typeof value\n }\"`,\n );\n }\n }\n\n return clone;\n}\n\nexport function assignVars<VarContract extends Contract>(\n varContract: VarContract,\n tokens: MapLeafNodes<VarContract, string>,\n): Record<CSSVarFunction, string> {\n const varSetters: { [cssVarName: string]: string } = {};\n // const { valid, diffString } = validateContract(varContract, tokens);\n\n // if (!valid) {\n // throw new Error(`Tokens don't match contract.\\n${diffString}`);\n // }\n\n walkObject(tokens, (value, path) => {\n varSetters[get(varContract, path)] = String(value);\n });\n\n return varSetters;\n}\n","import { createThemeContract, globalStyle } from '@vanilla-extract/css';\nimport { Tokens, ThemeVars, Contract, MapLeafNodes } from './types';\nimport { assignVars } from './utils';\n\nexport function createGlobalTheme<ThemeTokens extends Tokens>(\n layerName: string,\n selector: string,\n tokens: ThemeTokens,\n): ThemeVars<ThemeTokens>;\nexport function createGlobalTheme<ThemeContract extends Contract>(\n layerName: string,\n selector: string,\n themeContract: ThemeContract,\n tokens: MapLeafNodes<ThemeContract, string>,\n): void;\nexport function createGlobalTheme(\n layerName: string,\n selector: string,\n arg2: any,\n arg3?: any,\n): any {\n const shouldCreateVars = Boolean(!arg3);\n\n const themeVars = shouldCreateVars\n ? createThemeContract(arg2)\n : (arg2 as ThemeVars<any>);\n\n const tokens = shouldCreateVars ? arg2 : arg3;\n\n globalStyle(selector, {\n '@layer': {\n [layerName]: {\n vars: assignVars(themeVars, tokens),\n },\n },\n });\n\n // appendCss(\n // {\n // type: 'global',\n // selector: selector,\n // rule: { vars: assignVars(themeVars, tokens) },\n // },\n // getFileScope(),\n // );\n\n if (shouldCreateVars) {\n return themeVars;\n }\n}\n","import {\n style,\n layer,\n globalLayer,\n StyleRule,\n globalStyle,\n GlobalStyleRule,\n createGlobalTheme as _createGlobalTheme,\n} from '@vanilla-extract/css';\nimport { addFunctionSerializer } from '@vanilla-extract/css/functionSerializer';\nimport { createGlobalTheme } from './theme';\n\ntype CustomStyleRules = Record<string, any>;\n\ntype _LayerStyleRules = NonNullable<StyleRule['@layer']>[string];\n\ntype LayerStyleRules<CustomRules extends CustomStyleRules | null = null> =\n CustomRules extends null ? _LayerStyleRules : _LayerStyleRules & CustomRules;\n\ntype ClassNames = string | ClassNames[];\n\ntype ComplexLayerStyleRule<CustomRules extends CustomStyleRules | null = null> =\n | LayerStyleRules<CustomRules>\n | (LayerStyleRules<CustomRules> | ClassNames)[];\n\ntype _LayerGlobalStyleRules = NonNullable<GlobalStyleRule['@layer']>[string];\n\ntype LayerGlobalStyleRules<CustomRules extends CustomStyleRules | null = null> =\n CustomRules extends null\n ? _LayerGlobalStyleRules\n : _LayerGlobalStyleRules & CustomRules;\n\ntype AnyStyleRule<CustomRules extends CustomStyleRules | null = null> =\n | LayerStyleRules<CustomRules>\n | LayerGlobalStyleRules<CustomRules>;\n\ntype LayerStyleHooks<CustomRules extends CustomStyleRules | null = null> = {\n style?: (rule: ComplexLayerStyleRule<CustomRules>, debugId?: string) => void;\n global?: (selector: string, rule: LayerGlobalStyleRules<CustomRules>) => void;\n anyStyle?: (style: AnyStyleRule<CustomRules>) => void;\n};\n\nexport interface LayerStyle<\n CustomRules extends CustomStyleRules | null = null,\n> {\n layerName: string;\n parentLayerName: string | null;\n /**\n * @see {@link style}\n */\n (rule: ComplexLayerStyleRule<CustomRules>, debugId?: string): string;\n\n /**\n * @see {@link style}\n */\n style(rule: ComplexLayerStyleRule<CustomRules>, debugId?: string): string;\n\n /**\n * @see {@link globalStyle}\n */\n global(selector: string, rule: LayerGlobalStyleRules<CustomRules>): void;\n\n /**\n * @see {@link _createGlobalTheme}\n */\n globalTheme: typeof _createGlobalTheme;\n\n defineNestedLayer(\n globalNameOrNestedOptions?: string | DefineLayerOptions<CustomRules>,\n ): LayerStyle<CustomRules>;\n\n /**\n * Add global CSS variable with layer\n *\n * @remarks The vanilla-extract API is buggy when handling layered css variables.\n *\n * @param selector - selector\n * @param vars - variables\n */\n pushGlobalVars(selector: string, vars: Record<string, string>): void;\n\n /**\n * Output variables accumulated by `pushGlobalVars`.\n *\n * @remarks The vanilla-extract API is buggy when handling layered css variables.\n */\n dumpGlobalVars(): void;\n hooks: LayerStyleHooks<CustomRules>;\n}\n\nexport interface DefineLayerParentOptions {\n parent?: string;\n}\n\nexport interface DefineLayerBaseOptions<\n CustomRules extends CustomStyleRules | null = null,\n> {\n hooks?: LayerStyleHooks<CustomRules>;\n}\n\nexport interface DefineLayerScopedOptions<\n CustomRules extends CustomStyleRules | null = null,\n> extends DefineLayerBaseOptions<CustomRules> {\n /** Debug ID */\n debugId?: string;\n globalName?: never;\n}\n\nexport interface DefineLayerGlobalOptions<\n CustomRules extends CustomStyleRules | null = null,\n> extends DefineLayerBaseOptions<CustomRules> {\n debugId?: never;\n /** Parent layer name */\n globalName: string;\n}\n\nexport type DefineLayerOptions<\n CustomRules extends CustomStyleRules | null = null,\n> =\n | DefineLayerScopedOptions<CustomRules>\n | DefineLayerGlobalOptions<CustomRules>;\n\nexport type DefineNestableLayerOptions<\n CustomRules extends CustomStyleRules | null = null,\n> = DefineLayerOptions<CustomRules> & DefineLayerParentOptions;\n\nfunction isGlobalOptions(\n options: DefineLayerOptions<any>,\n): options is DefineLayerGlobalOptions {\n return 'globalName' in options;\n}\n\nfunction normalizeToObject<T extends DefineLayerOptions<any>>(\n source?: string | T,\n): T {\n if (!source) return {} as T;\n if (typeof source === 'string') return { globalName: source } as unknown as T;\n return source;\n}\n\n/**\n * Assemble a {@link LayerStyle} object for an already-resolved `layerName`.\n *\n * @remarks\n * This contains the whole object-building logic shared by {@link defineLayerStyle}\n * (build-time, where `layerName` was just produced by `layer()`/`globalLayer()`)\n * and {@link defineLayerStyleFromResolvedName} (runtime re-construction, where\n * `layerName` is the deterministic value carried over from build time). It must\n * NOT call any `@vanilla-extract/css` side-effecting API (no `layer()` /\n * `globalLayer()`), so that re-construction never regenerates a hash-based name.\n *\n * @internal\n */\nfunction buildLayerStyle<CustomRules extends CustomStyleRules | null = null>(\n layerName: string,\n parentLayerName: string | null,\n hooks: LayerStyleHooks<CustomRules>,\n): LayerStyle<CustomRules> {\n const layerStyle = function layerStyle(rule, debugId) {\n const rules = Array.isArray(rule) ? rule : [rule];\n const layerAppliedRules = rules.map((_rule) => {\n if (typeof _rule === 'string' || Array.isArray(_rule)) return _rule;\n return {\n '@layer': {\n [layerName]: _rule,\n },\n };\n });\n if (hooks.anyStyle) {\n for (const _rule of rules) {\n if (typeof _rule === 'string' || Array.isArray(_rule)) continue;\n hooks.anyStyle(_rule);\n }\n }\n hooks.style && hooks.style(rule, debugId);\n return style(layerAppliedRules, debugId);\n } as LayerStyle<CustomRules>;\n\n layerStyle.layerName = layerName;\n layerStyle.parentLayerName = parentLayerName;\n layerStyle.style = layerStyle;\n layerStyle.hooks = hooks;\n\n layerStyle.global = function layerGlobalStyle(\n selector: string,\n rule: LayerGlobalStyleRules<CustomRules>,\n ) {\n hooks.anyStyle && hooks.anyStyle(rule);\n hooks.global && hooks.global(selector, rule);\n\n return globalStyle(selector, {\n '@layer': {\n [layerName]: rule,\n },\n });\n };\n\n layerStyle.globalTheme = (...args: any) =>\n (createGlobalTheme as any)(layerName, ...args);\n\n let _varQueues: [string, Record<string, string>][] = [];\n\n layerStyle.pushGlobalVars = function pushGlobalVars(\n selector: string,\n vars: Record<string, string>,\n ) {\n let queue = _varQueues.find((q) => q[0] === selector);\n if (!queue) {\n queue = [selector, {}];\n _varQueues.push(queue);\n }\n Object.assign(queue[1], vars);\n };\n\n layerStyle.dumpGlobalVars = function dumpGlobalVars() {\n for (const [selector, vars] of _varQueues) {\n layerStyle.global(selector, {\n vars,\n } as any);\n }\n _varQueues = [];\n };\n\n layerStyle.defineNestedLayer = function defineNestedLayer(\n globalNameOrNestedOptions?: string | DefineLayerOptions<CustomRules>,\n ) {\n const _options = normalizeToObject(globalNameOrNestedOptions);\n const nestedHooks = _options.hooks;\n\n return defineLayerStyle({\n ..._options,\n hooks: {\n ...hooks,\n ...nestedHooks,\n },\n parent: layerName,\n });\n };\n\n return layerStyle;\n}\n\n/**\n * Re-construct a {@link LayerStyle} from a name that was already resolved at\n * build time, WITHOUT re-running `layer()` / `globalLayer()`.\n *\n * @remarks\n * This is the runtime counterpart used by the function serializer (see\n * {@link defineLayerStyle}). Because the `layerName` is passed verbatim, the\n * re-constructed instance points at the exact layer that was emitted into CSS at\n * build time β even for scoped layers whose name is a non-deterministic hash that\n * could never be reproduced by calling `layer()` again.\n *\n * The re-constructed instance carries only deterministic state (`layerName` /\n * `parentLayerName`). It has no `hooks` and none of the additions made via\n * `extend()`; it is a reference handle to an already-built layer, not a fresh\n * style-defining entry point.\n *\n * @internal Not part of the public API; exported only so the serializer's\n * `importName` can resolve it at runtime.\n */\nexport function defineLayerStyleFromResolvedName<\n CustomRules extends CustomStyleRules | null = null,\n>(layerName: string, parentLayerName: string | null): LayerStyle<CustomRules> {\n return buildLayerStyle<CustomRules>(layerName, parentLayerName, {});\n}\n\nexport function defineLayerStyle<\n CustomRules extends CustomStyleRules | null = null,\n>(\n globalNameOrOptions?: string | DefineNestableLayerOptions<CustomRules>,\n): LayerStyle<CustomRules> {\n const options = normalizeToObject(globalNameOrOptions);\n const { parent, hooks = {} } = options;\n\n const isGlobal = isGlobalOptions(options);\n\n const layerName = isGlobal\n ? globalLayer({ parent }, options.globalName)\n : layer({ parent }, options.debugId);\n\n const layerStyle = buildLayerStyle<CustomRules>(\n layerName,\n parent || null,\n hooks,\n );\n\n // Attach vanilla-extract's official function serializer so that a `LayerStyle`\n // can survive being exported from a `.css.ts` module. The `.css.ts` build is\n // delegated to `@vanilla-extract/rollup-plugin`, whose `serializeVanillaModule`\n // rejects plain function exports. `addFunctionSerializer` registers a\n // descriptor (`__function_serializer__`) so the function is emitted as\n // re-construction code instead of throwing.\n //\n // Two re-construction paths, chosen so each reproduces the SAME `layerName`:\n //\n // - global: re-run `defineLayerStyle({ globalName, parent })`. `globalLayer()`\n // derives a deterministic name from `globalName`/`parent`, so re-running it at\n // runtime yields the identical layer. Kept verbatim from the original\n // implementation, so global behavior (including the runtime `globalLayer()`\n // call) is unchanged.\n // - scoped: call `defineLayerStyleFromResolvedName(layerName, parentLayerName)`,\n // which does NOT re-run `layer()`. A scoped layer's name is a hash that cannot\n // be reproduced by calling `layer()` again, so the build-time name is carried\n // over as a plain string instead.\n //\n // What re-construction restores (BOTH paths): only the deterministic state β\n // `layerName` / `parentLayerName`. `hooks` (and anything added via `extend()`:\n // extra `anyStyle`, custom methods, etc.) are intentionally NOT serialized,\n // because they hold functions and the serializer args must be plain serializable\n // values. This does not change build behavior: `hooks` run at `.css.ts`\n // evaluation time when `.style()` / `.global()` are called, and the CSS they emit\n // is already baked in before serialization (which only reads\n // `__function_serializer__` and writes re-construction code). The only caveat is\n // purely runtime: an instance re-constructed in a consumer is a *reference\n // handle* to the already-built layer β calling its `.style()` / `.global()` /\n // extended methods there runs without the original hooks. If a runtime-hooked\n // layer is ever needed, keep that instance build-time only, or add a separately\n // serializable descriptor for it.\n if (isGlobal) {\n addFunctionSerializer(layerStyle, {\n importPath: '@fastkit/plugboy-vanilla-extract-plugin/css',\n importName: 'defineLayerStyle',\n args: [\n {\n globalName: options.globalName,\n ...(parent ? { parent } : {}),\n },\n ],\n });\n } else {\n addFunctionSerializer(layerStyle, {\n importPath: '@fastkit/plugboy-vanilla-extract-plugin/css',\n importName: 'defineLayerStyleFromResolvedName',\n args: [layerName, parent || null],\n });\n }\n\n return layerStyle;\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Plugin } from "@fastkit/plugboy";
|
|
2
|
-
import { vanillaExtractPlugin } from "@vanilla-extract/
|
|
3
|
-
import { vanillaExtractPlugin as vanillaExtractPlugin$1 } from "@vanilla-extract/
|
|
2
|
+
import { vanillaExtractPlugin } from "@vanilla-extract/rollup-plugin";
|
|
3
|
+
import { vanillaExtractPlugin as vanillaExtractPlugin$1 } from "@vanilla-extract/vite-plugin";
|
|
4
4
|
import { Plugin as Plugin$1 } from "vite";
|
|
5
5
|
|
|
6
6
|
//#region src/types.d.ts
|
|
7
|
-
type VanillaExtractPluginOptions = Omit<NonNullable<Parameters<typeof vanillaExtractPlugin
|
|
8
|
-
|
|
7
|
+
type VanillaExtractPluginOptions = Omit<NonNullable<Parameters<typeof vanillaExtractPlugin>[0]>, 'extract'>;
|
|
8
|
+
type PluginOptions = Pick<VanillaExtractPluginOptions, 'identifiers' | 'esbuildOptions'>;
|
|
9
9
|
declare const PLUGIN_NAME = "plugboy-vanilla-extract";
|
|
10
10
|
interface VanillaExtractPlugin extends Plugin {
|
|
11
11
|
name: typeof PLUGIN_NAME;
|
|
@@ -21,7 +21,7 @@ declare module '@fastkit/plugboy' {
|
|
|
21
21
|
declare function createVanillaExtractPlugin(options?: PluginOptions): Promise<VanillaExtractPlugin>;
|
|
22
22
|
//#endregion
|
|
23
23
|
//#region src/vite.d.ts
|
|
24
|
-
type VanillaExtractVitePluginOptions = NonNullable<Parameters<typeof vanillaExtractPlugin>[0]>;
|
|
24
|
+
type VanillaExtractVitePluginOptions = NonNullable<Parameters<typeof vanillaExtractPlugin$1>[0]>;
|
|
25
25
|
interface ViteVanillaExtractPluginOptions extends VanillaExtractVitePluginOptions {}
|
|
26
26
|
declare function ViteVanillaExtractPlugin(options?: ViteVanillaExtractPluginOptions): Promise<Plugin$1[]>;
|
|
27
27
|
//#endregion
|
|
@@ -1,235 +1,128 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import { definePlugin, findFile, findProjectPlugin } from "@fastkit/plugboy";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import MagicString, { Bundle } from "magic-string";
|
|
6
|
-
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
|
|
7
|
-
|
|
8
|
-
//#region rolldown:runtime
|
|
9
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
10
|
-
|
|
11
|
-
//#endregion
|
|
4
|
+
import { vanillaExtractPlugin } from "@vanilla-extract/rollup-plugin";
|
|
5
|
+
import { vanillaExtractPlugin as vanillaExtractPlugin$1 } from "@vanilla-extract/vite-plugin";
|
|
12
6
|
//#region src/types.ts
|
|
13
7
|
const PLUGIN_NAME = "plugboy-vanilla-extract";
|
|
14
|
-
|
|
15
|
-
//#endregion
|
|
16
|
-
//#region src/_origin/lib.ts
|
|
17
|
-
/** Generate a CSS bundle from Rollup context */
|
|
18
|
-
function generateCssBundle(plugin) {
|
|
19
|
-
const cssBundle = new Bundle();
|
|
20
|
-
const extractedCssIds = /* @__PURE__ */ new Set();
|
|
21
|
-
const cssFiles = {};
|
|
22
|
-
for (const id of plugin.getModuleIds()) if (cssFileFilter.test(id)) cssFiles[id] = buildImportChain(id, plugin);
|
|
23
|
-
for (const id of sortModules(cssFiles)) {
|
|
24
|
-
const { importedIds } = plugin.getModuleInfo(id) ?? {};
|
|
25
|
-
for (const importedId of importedIds ?? []) {
|
|
26
|
-
const resolution = plugin.getModuleInfo(importedId);
|
|
27
|
-
if (resolution?.meta.css && !extractedCssIds.has(resolution.id)) {
|
|
28
|
-
extractedCssIds.add(resolution.id);
|
|
29
|
-
cssBundle.addSource({
|
|
30
|
-
filename: resolution.id,
|
|
31
|
-
content: new MagicString(resolution.meta.css)
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return {
|
|
37
|
-
bundle: cssBundle,
|
|
38
|
-
extractedCssIds
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
/** Trace a file back through its importers, building an ordered list */
|
|
42
|
-
function buildImportChain(id, plugin) {
|
|
43
|
-
let mod = plugin.getModuleInfo(id);
|
|
44
|
-
if (!mod) return [];
|
|
45
|
-
/** [id, order] */
|
|
46
|
-
const chain = [[id, -1]];
|
|
47
|
-
while (!mod.isEntry) {
|
|
48
|
-
const { id: currentId, importers } = mod;
|
|
49
|
-
const lastImporterId = importers.at(-1);
|
|
50
|
-
if (!lastImporterId) break;
|
|
51
|
-
if (chain.some(([id]) => id === lastImporterId)) {
|
|
52
|
-
plugin.warn(`Circular import detected. Canβt determine ideal import order of module.\n${chain.reverse().join("\n β ")}`);
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
mod = plugin.getModuleInfo(lastImporterId);
|
|
56
|
-
if (!mod) break;
|
|
57
|
-
chain.push([lastImporterId, mod.importedIds.indexOf(currentId)]);
|
|
58
|
-
}
|
|
59
|
-
return chain.reverse();
|
|
60
|
-
}
|
|
61
|
-
/** Compare import chains to determine a flat ordering for modules */
|
|
62
|
-
function sortModules(modules) {
|
|
63
|
-
const sortedModules = Object.entries(modules);
|
|
64
|
-
sortedModules.sort(([_idA, chainA], [_idB, chainB]) => {
|
|
65
|
-
const shorterChain = Math.min(chainA.length, chainB.length);
|
|
66
|
-
for (let i = 0; i < shorterChain; i++) {
|
|
67
|
-
const [moduleA, orderA] = chainA[i];
|
|
68
|
-
const [moduleB, orderB] = chainB[i];
|
|
69
|
-
if (moduleA === moduleB && orderA === orderB) continue;
|
|
70
|
-
if (orderA !== orderB) return orderA - orderB;
|
|
71
|
-
}
|
|
72
|
-
return 0;
|
|
73
|
-
});
|
|
74
|
-
return sortedModules.map(([id]) => id);
|
|
75
|
-
}
|
|
76
|
-
const SIDE_EFFECT_IMPORT_RE = /^\s*import\s+['"]([^'"]+)['"]\s*;?\s*/gm;
|
|
77
|
-
/** Remove specific side effect imports from JS */
|
|
78
|
-
function stripSideEffectImportsMatching(code, sources) {
|
|
79
|
-
const matches = code.matchAll(SIDE_EFFECT_IMPORT_RE);
|
|
80
|
-
if (!matches) return code;
|
|
81
|
-
let output = code;
|
|
82
|
-
for (const match of matches) {
|
|
83
|
-
if (!match[1] || !sources.includes(match[1])) continue;
|
|
84
|
-
output = output.replace(match[0], "");
|
|
85
|
-
}
|
|
86
|
-
return output;
|
|
87
|
-
}
|
|
88
|
-
async function tryGetPackageName(cwd) {
|
|
89
|
-
try {
|
|
90
|
-
return __require(posix.join(cwd, "package.json"))?.name || null;
|
|
91
|
-
} catch {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
//#endregion
|
|
97
|
-
//#region src/_origin/index.ts
|
|
98
|
-
const { relative, normalize, dirname } = posix;
|
|
99
|
-
function vanillaExtractPlugin$1({ identifiers, cwd = process.cwd(), esbuildOptions, extract = false, unstable_injectFilescopes = false } = {}) {
|
|
100
|
-
if (extract === true) extract = {};
|
|
101
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
102
|
-
let extractedCssIds = /* @__PURE__ */ new Set();
|
|
103
|
-
return {
|
|
104
|
-
name: "vanilla-extract",
|
|
105
|
-
buildStart() {
|
|
106
|
-
extractedCssIds = /* @__PURE__ */ new Set();
|
|
107
|
-
},
|
|
108
|
-
async transform(code, id) {
|
|
109
|
-
if (!cssFileFilter.test(id)) return null;
|
|
110
|
-
const identOption = identifiers ?? (isProduction ? "short" : "debug");
|
|
111
|
-
const [filePath] = id.split("?");
|
|
112
|
-
if (unstable_injectFilescopes) return {
|
|
113
|
-
code: await transform({
|
|
114
|
-
source: code,
|
|
115
|
-
filePath: id,
|
|
116
|
-
rootPath: cwd,
|
|
117
|
-
packageName: await tryGetPackageName(cwd) ?? "",
|
|
118
|
-
identOption
|
|
119
|
-
}),
|
|
120
|
-
map: { mappings: "" }
|
|
121
|
-
};
|
|
122
|
-
const { source, watchFiles } = await compile({
|
|
123
|
-
filePath,
|
|
124
|
-
cwd,
|
|
125
|
-
esbuildOptions,
|
|
126
|
-
identOption
|
|
127
|
-
});
|
|
128
|
-
for (const file of watchFiles) this.addWatchFile(file);
|
|
129
|
-
return {
|
|
130
|
-
code: await processVanillaFile({
|
|
131
|
-
source,
|
|
132
|
-
filePath,
|
|
133
|
-
identOption
|
|
134
|
-
}),
|
|
135
|
-
map: { mappings: "" }
|
|
136
|
-
};
|
|
137
|
-
},
|
|
138
|
-
async resolveId(id) {
|
|
139
|
-
if (!virtualCssFileFilter.test(id)) return null;
|
|
140
|
-
const { fileName, source } = await getSourceFromVirtualCssFile(id);
|
|
141
|
-
return {
|
|
142
|
-
id: fileName,
|
|
143
|
-
external: true,
|
|
144
|
-
meta: { css: source }
|
|
145
|
-
};
|
|
146
|
-
},
|
|
147
|
-
renderChunk(code, chunkInfo) {
|
|
148
|
-
const chunkPath = dirname(chunkInfo.fileName);
|
|
149
|
-
return {
|
|
150
|
-
code: chunkInfo.imports.reduce((codeResult, importPath) => {
|
|
151
|
-
const moduleInfo = this.getModuleInfo(importPath);
|
|
152
|
-
if (!moduleInfo?.meta.css || extract) return codeResult;
|
|
153
|
-
const assetId = this.emitFile({
|
|
154
|
-
type: "asset",
|
|
155
|
-
name: moduleInfo.id,
|
|
156
|
-
source: moduleInfo.meta.css
|
|
157
|
-
});
|
|
158
|
-
const relativeAssetPath = `./${normalize(relative(chunkPath, this.getFileName(assetId)))}`;
|
|
159
|
-
return codeResult.replace(importPath, relativeAssetPath);
|
|
160
|
-
}, code),
|
|
161
|
-
map: null
|
|
162
|
-
};
|
|
163
|
-
},
|
|
164
|
-
async generateBundle(_options, bundle) {
|
|
165
|
-
if (!extract) return;
|
|
166
|
-
for (const chunk of Object.values(bundle)) {
|
|
167
|
-
if (chunk.type !== "chunk" || !chunk.isEntry) continue;
|
|
168
|
-
const jsFileName = chunk.fileName;
|
|
169
|
-
if (/\.d\.(ts|mts|cts)$/.test(jsFileName)) continue;
|
|
170
|
-
const extractName = extract.name || "[name].css";
|
|
171
|
-
const name = jsFileName.replace(/\.(js|mjs)$/, "");
|
|
172
|
-
const cssFileName = typeof extractName === "function" ? extractName(chunk) : extractName.replace("[name]", name);
|
|
173
|
-
const { bundle: cssBundle, extractedCssIds: extractedIds } = generateCssBundle(this);
|
|
174
|
-
extractedCssIds = extractedIds;
|
|
175
|
-
this.emitFile({
|
|
176
|
-
type: "asset",
|
|
177
|
-
fileName: cssFileName,
|
|
178
|
-
source: cssBundle.toString()
|
|
179
|
-
});
|
|
180
|
-
if (extract.sourcemap) {
|
|
181
|
-
const sourcemapName = `${cssFileName}.map`;
|
|
182
|
-
this.emitFile({
|
|
183
|
-
type: "asset",
|
|
184
|
-
name: sourcemapName,
|
|
185
|
-
originalFileName: sourcemapName,
|
|
186
|
-
source: cssBundle.generateMap({
|
|
187
|
-
file: name,
|
|
188
|
-
includeContent: true
|
|
189
|
-
}).toString()
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
await Promise.all(Object.entries(bundle).map(async ([id, chunk]) => {
|
|
194
|
-
if (chunk.type === "chunk" && (id.endsWith(".js") || id.endsWith(".mjs")) && chunk.imports.some((specifier) => extractedCssIds.has(specifier))) chunk.code = await stripSideEffectImportsMatching(chunk.code, [...extractedCssIds]);
|
|
195
|
-
}));
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
8
|
//#endregion
|
|
201
9
|
//#region src/plugin.ts
|
|
10
|
+
/**
|
|
11
|
+
* Temporary file name for the CSS that tsdown's own CSS pipeline emits.
|
|
12
|
+
*
|
|
13
|
+
* A package can have two independent sources of CSS:
|
|
14
|
+
* - tsdown's built-in CSS handling, for plain `.css` / `.scss` imports.
|
|
15
|
+
* - `@vanilla-extract/rollup-plugin`, for `.css.ts` files (extracted into a
|
|
16
|
+
* single bundle named after the package).
|
|
17
|
+
*
|
|
18
|
+
* If both are pointed at the same final file name they collide
|
|
19
|
+
* (`FILE_NAME_CONFLICT` β one silently overwrites the other, dropping all of
|
|
20
|
+
* the vanilla-extract component CSS). To avoid that we route tsdown's CSS to
|
|
21
|
+
* this temporary name and merge it into the vanilla-extract bundle in
|
|
22
|
+
* `writeBundle`.
|
|
23
|
+
*/
|
|
24
|
+
const TSDOWN_CSS_FILE_NAME = "__ve-tsdown__.css";
|
|
202
25
|
async function createVanillaExtractPlugin(options = {}) {
|
|
203
26
|
return definePlugin({
|
|
204
27
|
name: PLUGIN_NAME,
|
|
205
28
|
_options: options,
|
|
206
|
-
hooks: { async setupWorkspace(ctx) {
|
|
29
|
+
hooks: { async setupWorkspace(ctx, getWorkspace) {
|
|
30
|
+
const entryIds = Object.keys(ctx.config.entries);
|
|
31
|
+
const cssFileName = `${entryIds.includes(".") ? ctx.dir.basename : entryIds[0] ?? ctx.dir.basename}.css`;
|
|
207
32
|
ctx.mergeExternals(/@vanilla-extract/);
|
|
208
33
|
ctx.meta.hasVanillaExtract = !!await findFile(ctx.dirs.src.value, /\.css\.ts$/);
|
|
34
|
+
ctx.css = {
|
|
35
|
+
splitting: false,
|
|
36
|
+
fileName: ctx.meta.hasVanillaExtract ? TSDOWN_CSS_FILE_NAME : cssFileName
|
|
37
|
+
};
|
|
209
38
|
if (ctx.meta.hasVanillaExtract) {
|
|
210
|
-
const originalPlugin = vanillaExtractPlugin
|
|
39
|
+
const originalPlugin = vanillaExtractPlugin({
|
|
211
40
|
...options,
|
|
212
|
-
extract:
|
|
41
|
+
extract: {
|
|
42
|
+
name: cssFileName,
|
|
43
|
+
sourcemap: false
|
|
44
|
+
}
|
|
213
45
|
});
|
|
214
|
-
ctx.config.dts ??= {};
|
|
215
|
-
ctx.config.dts.inline = true;
|
|
216
|
-
ctx.dts.inline = true;
|
|
217
46
|
ctx.plugins.push(originalPlugin);
|
|
47
|
+
ctx.plugins.push({
|
|
48
|
+
name: `${PLUGIN_NAME}:rename-css`,
|
|
49
|
+
outputOptions(opts) {
|
|
50
|
+
const original = opts.assetFileNames;
|
|
51
|
+
opts.assetFileNames = (assetInfo) => {
|
|
52
|
+
if (assetInfo.names.includes(cssFileName)) return cssFileName;
|
|
53
|
+
if (typeof original === "function") return original(assetInfo);
|
|
54
|
+
return original ?? "assets/[name]-[hash][extname]";
|
|
55
|
+
};
|
|
56
|
+
return opts;
|
|
57
|
+
},
|
|
58
|
+
generateBundle(_options, bundle) {
|
|
59
|
+
const entryCss = [];
|
|
60
|
+
for (const chunk of Object.values(bundle)) {
|
|
61
|
+
if (chunk.type !== "chunk" || !chunk.isEntry || !chunk.fileName.endsWith(".mjs")) continue;
|
|
62
|
+
const cssChunks = [];
|
|
63
|
+
for (const importId of chunk.imports) {
|
|
64
|
+
const css = this.getModuleInfo(importId)?.meta?.css;
|
|
65
|
+
if (typeof css === "string") cssChunks.push(css);
|
|
66
|
+
}
|
|
67
|
+
if (cssChunks.length) entryCss.push({
|
|
68
|
+
name: chunk.name,
|
|
69
|
+
source: cssChunks.join("\n")
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (entryCss.length <= 1) return;
|
|
73
|
+
const reused = /* @__PURE__ */ new Set();
|
|
74
|
+
for (const { name, source } of entryCss) {
|
|
75
|
+
const fileName = `${name}.css`;
|
|
76
|
+
const existing = bundle[fileName];
|
|
77
|
+
if (existing && existing.type === "asset") {
|
|
78
|
+
existing.source = source;
|
|
79
|
+
reused.add(fileName);
|
|
80
|
+
} else this.emitFile({
|
|
81
|
+
type: "asset",
|
|
82
|
+
fileName,
|
|
83
|
+
source
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const combined = bundle[cssFileName];
|
|
87
|
+
if (combined && combined.type === "asset" && !reused.has(cssFileName)) delete bundle[cssFileName];
|
|
88
|
+
},
|
|
89
|
+
async writeBundle(outputOptions, bundle) {
|
|
90
|
+
const tmp = bundle[TSDOWN_CSS_FILE_NAME];
|
|
91
|
+
if (!tmp) return;
|
|
92
|
+
const tmpCss = tmp.type === "asset" ? tmp.source.toString() : "";
|
|
93
|
+
const dir = outputOptions.dir ?? ".";
|
|
94
|
+
const tmpPath = path.join(dir, TSDOWN_CSS_FILE_NAME);
|
|
95
|
+
const targetPath = path.join(dir, cssFileName);
|
|
96
|
+
let targetCss = "";
|
|
97
|
+
try {
|
|
98
|
+
targetCss = await fs.readFile(targetPath, "utf8");
|
|
99
|
+
} catch {}
|
|
100
|
+
const merged = tmpCss ? targetCss ? `${tmpCss}\n${targetCss}` : tmpCss : targetCss;
|
|
101
|
+
if (merged) await fs.writeFile(targetPath, merged);
|
|
102
|
+
await fs.rm(tmpPath, { force: true });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
218
105
|
}
|
|
219
106
|
} }
|
|
220
107
|
});
|
|
221
108
|
}
|
|
222
|
-
|
|
223
109
|
//#endregion
|
|
224
110
|
//#region src/vite.ts
|
|
225
111
|
async function ViteVanillaExtractPlugin(options = {}) {
|
|
226
|
-
const { identifiers: baseIdentifiers } = (await findProjectPlugin(
|
|
227
|
-
return vanillaExtractPlugin({
|
|
112
|
+
const { identifiers: baseIdentifiers } = (await findProjectPlugin("plugboy-vanilla-extract"))?._options || {};
|
|
113
|
+
return [...vanillaExtractPlugin$1({
|
|
228
114
|
identifiers: baseIdentifiers,
|
|
229
115
|
...options
|
|
230
|
-
})
|
|
116
|
+
}), {
|
|
117
|
+
name: "vanilla-extract-fix-file-scope",
|
|
118
|
+
config(viteConfig) {
|
|
119
|
+
viteConfig.resolve ??= {};
|
|
120
|
+
viteConfig.resolve.dedupe ??= [];
|
|
121
|
+
viteConfig.resolve.dedupe.push("@vanilla-extract/css", "@vanilla-extract/css/fileScope");
|
|
122
|
+
}
|
|
123
|
+
}];
|
|
231
124
|
}
|
|
232
|
-
|
|
233
125
|
//#endregion
|
|
234
126
|
export { PLUGIN_NAME, ViteVanillaExtractPlugin, createVanillaExtractPlugin };
|
|
127
|
+
|
|
235
128
|
//# sourceMappingURL=plugboy-vanilla-extract-plugin.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugboy-vanilla-extract-plugin.mjs","names":["MagicStringBundle","vanillaExtractPlugin","vanillaExtractPlugin"],"sources":["../src/types.ts","../src/_origin/lib.ts","../src/_origin/index.ts","../src/plugin.ts","../src/vite.ts"],"sourcesContent":["import { Plugin } from '@fastkit/plugboy';\nimport type { vanillaExtractPlugin as rollupPlugin } from '@vanilla-extract/rollup-plugin';\n\ntype VanillaExtractPluginOptions = Omit<\n NonNullable<Parameters<typeof rollupPlugin>[0]>,\n 'extract'\n>;\n\nexport interface PluginOptions\n extends Pick<VanillaExtractPluginOptions, 'identifiers' | 'esbuildOptions'> {}\n\nexport const PLUGIN_NAME = 'plugboy-vanilla-extract';\n\nexport interface VanillaExtractPlugin extends Plugin {\n name: typeof PLUGIN_NAME;\n _options: PluginOptions;\n}\n","import { cssFileFilter } from '@vanilla-extract/integration';\nimport MagicString, { Bundle as MagicStringBundle } from 'magic-string';\nimport type { ModuleInfo, PluginContext } from 'rolldown';\nimport { posix } from 'node:path';\n\n/** Generate a CSS bundle from Rollup context */\nexport function generateCssBundle(\n plugin: Pick<PluginContext, 'getModuleIds' | 'getModuleInfo' | 'warn'>,\n): {\n bundle: MagicStringBundle;\n extractedCssIds: Set<string>;\n} {\n const cssBundle = new MagicStringBundle();\n const extractedCssIds = new Set<string>();\n\n // 1. identify CSS files to bundle\n const cssFiles: Record<string, ImportChain> = {};\n for (const id of plugin.getModuleIds()) {\n if (cssFileFilter.test(id)) {\n cssFiles[id] = buildImportChain(id, plugin);\n }\n }\n\n // 2. build bundle from import order\n for (const id of sortModules(cssFiles)) {\n const { importedIds } = plugin.getModuleInfo(id) ?? {};\n for (const importedId of importedIds ?? []) {\n const resolution = plugin.getModuleInfo(importedId);\n if (resolution?.meta.css && !extractedCssIds.has(resolution.id)) {\n extractedCssIds.add(resolution.id);\n cssBundle.addSource({\n filename: resolution.id,\n content: new MagicString(resolution.meta.css),\n });\n }\n }\n }\n\n return { bundle: cssBundle, extractedCssIds };\n}\n\n/** [id, order] tuple meant for ordering imports */\nexport type ImportChain = [id: string, order: number][];\n\n/** Trace a file back through its importers, building an ordered list */\nexport function buildImportChain(\n id: string,\n plugin: Pick<PluginContext, 'getModuleInfo' | 'warn'>,\n): ImportChain {\n let mod: ModuleInfo | null = plugin.getModuleInfo(id)!;\n if (!mod) {\n return [];\n }\n /** [id, order] */\n const chain: ImportChain = [[id, -1]];\n // resolve upwards to root entry\n while (!mod.isEntry) {\n const { id: currentId, importers } = mod;\n const lastImporterId = importers.at(-1);\n if (!lastImporterId) {\n break;\n }\n if (chain.some(([id]) => id === lastImporterId)) {\n plugin.warn(\n `Circular import detected. Canβt determine ideal import order of module.\\n${chain\n .reverse()\n .join('\\n β ')}`,\n );\n break;\n }\n mod = plugin.getModuleInfo(lastImporterId);\n if (!mod) {\n break;\n }\n // importedIds preserves the import order within each module\n chain.push([lastImporterId, mod.importedIds.indexOf(currentId)]);\n }\n return chain.reverse();\n}\n\n/** Compare import chains to determine a flat ordering for modules */\nexport function sortModules(modules: Record<string, ImportChain>): string[] {\n const sortedModules = Object.entries(modules);\n\n // 2. sort CSS by import order\n sortedModules.sort(([_idA, chainA], [_idB, chainB]) => {\n const shorterChain = Math.min(chainA.length, chainB.length);\n for (let i = 0; i < shorterChain; i++) {\n const [moduleA, orderA] = chainA[i];\n const [moduleB, orderB] = chainB[i];\n // on same node, continue to next one\n if (moduleA === moduleB && orderA === orderB) {\n continue;\n }\n if (orderA !== orderB) {\n return orderA - orderB;\n }\n }\n return 0;\n });\n\n return sortedModules.map(([id]) => id);\n}\n\nconst SIDE_EFFECT_IMPORT_RE = /^\\s*import\\s+['\"]([^'\"]+)['\"]\\s*;?\\s*/gm;\n\n/** Remove specific side effect imports from JS */\nexport function stripSideEffectImportsMatching(\n code: string,\n sources: string[],\n): string {\n const matches = code.matchAll(SIDE_EFFECT_IMPORT_RE);\n if (!matches) {\n return code;\n }\n let output = code;\n for (const match of matches) {\n if (!match[1] || !sources.includes(match[1])) {\n continue;\n }\n output = output.replace(match[0], '');\n }\n return output;\n}\n\nexport async function tryGetPackageName(cwd: string): Promise<string | null> {\n try {\n const packageJson = require(posix.join(cwd, 'package.json'));\n\n return packageJson?.name || null;\n } catch {\n return null;\n }\n}\n","import type { Plugin, OutputChunk } from 'rolldown';\nimport {\n cssFileFilter,\n processVanillaFile,\n compile,\n type IdentifierOption,\n getSourceFromVirtualCssFile,\n virtualCssFileFilter,\n transform,\n type CompileOptions,\n} from '@vanilla-extract/integration';\nimport { posix } from 'node:path';\nimport {\n generateCssBundle,\n stripSideEffectImportsMatching,\n tryGetPackageName,\n} from './lib';\n\nconst { relative, normalize, dirname } = posix;\n\nexport interface Options {\n /**\n * Different formatting of identifiers (e.g. class names, keyframes, CSS Vars, etc) can be configured by selecting from the following options:\n * - \"short\": 7+ character hash. e.g. hnw5tz3\n * - \"debug\": human readable prefixes representing the owning filename and a potential rule level debug name. e.g. myfile_mystyle_hnw5tz3\n * - custom function: takes an object parameter with `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier.\n * @default \"short\"\n * @example ({ hash }) => `prefix_${hash}`\n */\n identifiers?: IdentifierOption;\n /**\n * Current working directory\n * @default process.cwd()\n */\n cwd?: string;\n /**\n * Options forwarded to esbuild\n * @see https://esbuild.github.io/\n */\n esbuildOptions?: CompileOptions['esbuildOptions'];\n /**\n * Extract .css bundle to a specified filename\n * @default false\n */\n extract?:\n | {\n /**\n * Name of emitted .css file.\n * @default \"bundle.css\"\n */\n name?: string | ((chunk: OutputChunk) => string);\n /**\n * Generate a .css.map file?\n * @default false\n */\n sourcemap?: boolean;\n }\n | boolean;\n\n /**\n * Inject filescopes into Vanilla Extract modules instead of generating CSS.\n * Useful for utility or component libraries that prefer their consumers to\n * process Vanilla Extract files instead of bundling CSS.\n *\n * Only works with `preserveModules: true`.\n *\n * @default false\n */\n unstable_injectFilescopes?: boolean;\n}\n\nexport function vanillaExtractPlugin({\n identifiers,\n cwd = process.cwd(),\n esbuildOptions,\n extract = false,\n unstable_injectFilescopes = false,\n}: Options = {}): Plugin {\n if (extract === true) {\n extract = {};\n }\n const isProduction = process.env.NODE_ENV === 'production';\n\n let extractedCssIds = new Set<string>(); // only for `extract`\n\n return {\n name: 'vanilla-extract',\n\n buildStart() {\n extractedCssIds = new Set(); // refresh every build\n },\n\n // Transform .css.js to .js\n async transform(code, id) {\n if (!cssFileFilter.test(id)) {\n return null;\n }\n\n const identOption = identifiers ?? (isProduction ? 'short' : 'debug');\n const [filePath] = id.split('?');\n\n if (unstable_injectFilescopes) {\n const packageName = await tryGetPackageName(cwd);\n const transformedCode = await transform({\n source: code,\n filePath: id,\n rootPath: cwd,\n packageName: packageName ?? '',\n identOption,\n });\n\n return {\n code: transformedCode,\n map: { mappings: '' },\n };\n }\n\n const { source, watchFiles } = await compile({\n filePath,\n cwd,\n esbuildOptions,\n identOption,\n });\n\n for (const file of watchFiles) {\n this.addWatchFile(file);\n }\n\n const output = await processVanillaFile({\n source,\n filePath,\n identOption,\n });\n return {\n code: output,\n map: { mappings: '' },\n };\n },\n\n // Resolve .css to external module\n async resolveId(id) {\n if (!virtualCssFileFilter.test(id)) {\n return null;\n }\n const { fileName, source } = await getSourceFromVirtualCssFile(id);\n return {\n id: fileName,\n external: true,\n meta: {\n css: source,\n },\n };\n },\n // Emit .css assets and replace .css import paths with relative paths to emitted css files\n renderChunk(code, chunkInfo) {\n const chunkPath = dirname(chunkInfo.fileName);\n const output = chunkInfo.imports.reduce((codeResult, importPath) => {\n const moduleInfo = this.getModuleInfo(importPath);\n if (!moduleInfo?.meta.css || extract) {\n return codeResult;\n }\n\n const assetId = this.emitFile({\n type: 'asset',\n name: moduleInfo.id,\n source: moduleInfo.meta.css,\n });\n const assetPath = this.getFileName(assetId);\n const relativeAssetPath = `./${normalize(\n relative(chunkPath, assetPath),\n )}`;\n return codeResult.replace(importPath, relativeAssetPath);\n }, code);\n\n return {\n code: output,\n map: null,\n };\n },\n\n // Remove side effect imports (if extracting)\n async generateBundle(_options, bundle) {\n if (!extract) {\n return;\n }\n\n for (const chunk of Object.values(bundle)) {\n if (chunk.type !== 'chunk' || !chunk.isEntry) continue;\n\n const jsFileName = chunk.fileName; // index.js / index.mjs\n // Skip DTS files\n if (/\\.d\\.(ts|mts|cts)$/.test(jsFileName)) continue;\n\n const extractName = extract.name || '[name].css';\n const name = jsFileName.replace(/\\.(js|mjs)$/, '');\n const cssFileName =\n typeof extractName === 'function'\n ? extractName(chunk)\n : extractName.replace('[name]', name);\n\n const { bundle: cssBundle, extractedCssIds: extractedIds } =\n generateCssBundle(this);\n extractedCssIds = extractedIds;\n // const name = extract.name || 'bundle.css';\n this.emitFile({\n type: 'asset',\n fileName: cssFileName,\n source: cssBundle.toString(),\n });\n\n if (extract.sourcemap) {\n const sourcemapName = `${cssFileName}.map`;\n this.emitFile({\n type: 'asset',\n name: sourcemapName,\n originalFileName: sourcemapName,\n source: cssBundle\n .generateMap({ file: name, includeContent: true })\n .toString(),\n });\n }\n }\n\n await Promise.all(\n Object.entries(bundle).map(async ([id, chunk]) => {\n if (\n chunk.type === 'chunk' &&\n (id.endsWith('.js') || id.endsWith('.mjs')) &&\n chunk.imports.some((specifier) => extractedCssIds.has(specifier))\n ) {\n chunk.code = await stripSideEffectImportsMatching(chunk.code, [\n ...extractedCssIds,\n ]);\n }\n }),\n );\n },\n };\n}\n","import { definePlugin, findFile } from '@fastkit/plugboy';\nimport { vanillaExtractPlugin } from './_origin';\nimport { VanillaExtractPlugin, PluginOptions, PLUGIN_NAME } from './types';\n\ndeclare module '@fastkit/plugboy' {\n export interface WorkspaceMeta {\n hasVanillaExtract: boolean;\n }\n}\n\nexport async function createVanillaExtractPlugin(options: PluginOptions = {}) {\n return definePlugin<VanillaExtractPlugin>({\n name: PLUGIN_NAME,\n _options: options,\n hooks: {\n async setupWorkspace(ctx) {\n ctx.mergeExternals(/@vanilla-extract/);\n\n ctx.meta.hasVanillaExtract = !!(await findFile(\n ctx.dirs.src.value,\n /\\.css\\.ts$/,\n ));\n\n if (ctx.meta.hasVanillaExtract) {\n const originalPlugin = vanillaExtractPlugin({\n ...options,\n extract: true,\n });\n\n // @TODO\n // rolldown-plugin-dts cannot handle vanilla-extract correctly\n // https://github.com/sxzz/rolldown-plugin-dts/issues/136\n ctx.config.dts ??= {};\n ctx.config.dts.inline = true;\n ctx.dts.inline = true;\n\n ctx.plugins.push(originalPlugin);\n }\n },\n },\n });\n}\n","import { findProjectPlugin } from '@fastkit/plugboy';\nimport { Plugin as VitePlugin } from 'vite';\nimport { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';\nimport { PLUGIN_NAME, VanillaExtractPlugin } from './types';\n\ntype VanillaExtractVitePluginOptions = NonNullable<\n Parameters<typeof vanillaExtractPlugin>[0]\n>;\n\nexport interface ViteVanillaExtractPluginOptions\n extends VanillaExtractVitePluginOptions {}\n\nexport async function ViteVanillaExtractPlugin(\n options: ViteVanillaExtractPluginOptions = {},\n): Promise<VitePlugin[]> {\n const plugin = await findProjectPlugin<VanillaExtractPlugin>(PLUGIN_NAME);\n const { identifiers: baseIdentifiers } = plugin?._options || {};\n\n return vanillaExtractPlugin({\n identifiers: baseIdentifiers,\n ...options,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugboy-vanilla-extract-plugin.mjs","names":["vanillaExtractPlugin"],"sources":["../src/types.ts","../src/plugin.ts","../src/vite.ts"],"sourcesContent":["import { Plugin } from '@fastkit/plugboy';\nimport type { vanillaExtractPlugin as rollupPlugin } from '@vanilla-extract/rollup-plugin';\n\ntype VanillaExtractPluginOptions = Omit<\n NonNullable<Parameters<typeof rollupPlugin>[0]>,\n 'extract'\n>;\n\nexport type PluginOptions = Pick<\n VanillaExtractPluginOptions,\n 'identifiers' | 'esbuildOptions'\n>;\n\nexport const PLUGIN_NAME = 'plugboy-vanilla-extract';\n\nexport interface VanillaExtractPlugin extends Plugin {\n name: typeof PLUGIN_NAME;\n _options: PluginOptions;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { definePlugin, findFile, type Plugin } from '@fastkit/plugboy';\nimport { vanillaExtractPlugin } from '@vanilla-extract/rollup-plugin';\nimport { VanillaExtractPlugin, PluginOptions, PLUGIN_NAME } from './types';\n\ndeclare module '@fastkit/plugboy' {\n export interface WorkspaceMeta {\n hasVanillaExtract: boolean;\n }\n}\n\n/**\n * Temporary file name for the CSS that tsdown's own CSS pipeline emits.\n *\n * A package can have two independent sources of CSS:\n * - tsdown's built-in CSS handling, for plain `.css` / `.scss` imports.\n * - `@vanilla-extract/rollup-plugin`, for `.css.ts` files (extracted into a\n * single bundle named after the package).\n *\n * If both are pointed at the same final file name they collide\n * (`FILE_NAME_CONFLICT` β one silently overwrites the other, dropping all of\n * the vanilla-extract component CSS). To avoid that we route tsdown's CSS to\n * this temporary name and merge it into the vanilla-extract bundle in\n * `writeBundle`.\n */\nconst TSDOWN_CSS_FILE_NAME = '__ve-tsdown__.css';\n\nexport async function createVanillaExtractPlugin(options: PluginOptions = {}) {\n return definePlugin<VanillaExtractPlugin>({\n name: PLUGIN_NAME,\n _options: options,\n hooks: {\n async setupWorkspace(ctx, getWorkspace) {\n // Derive the final CSS file name from the workspace entry, mirroring the\n // way plugboy names the JS output: the main entry (`.`) maps to the\n // package directory name (e.g. `vue-app-layout`), other entries keep\n // their id. Since `splitting: false` produces a single combined CSS for\n // the package, we base it on the main entry (falling back to the first).\n // Result: `dist/vue-app-layout.css` instead of `dist/__ve-tmp__.css`.\n const entryIds = Object.keys(ctx.config.entries);\n const cssBaseName = entryIds.includes('.')\n ? ctx.dir.basename\n : (entryIds[0] ?? ctx.dir.basename);\n const cssFileName = `${cssBaseName}.css`;\n\n ctx.mergeExternals(/@vanilla-extract/);\n\n ctx.meta.hasVanillaExtract = !!(await findFile(\n ctx.dirs.src.value,\n /\\.css\\.ts$/,\n ));\n\n // When vanilla-extract is in play, route tsdown's own CSS to a temporary\n // name so it doesn't collide with the vanilla-extract bundle that also\n // targets `cssFileName`; the two are merged into a single `cssFileName`\n // by the `writeBundle` hook below. Without vanilla-extract there is no\n // second producer, so tsdown emits `cssFileName` directly.\n ctx.css = {\n splitting: false,\n fileName: ctx.meta.hasVanillaExtract\n ? TSDOWN_CSS_FILE_NAME\n : cssFileName,\n };\n\n if (ctx.meta.hasVanillaExtract) {\n const originalPlugin = vanillaExtractPlugin({\n ...options,\n extract: {\n name: cssFileName,\n sourcemap: false,\n },\n });\n\n // `@vanilla-extract/rollup-plugin` returns a rollup `Plugin`, but\n // plugboy's `ctx.plugins` expects a tsdown (rolldown) `Plugin`. The two\n // are structurally almost identical, but hooks like `outputOptions`\n // type `this` as rollup's `PluginContext` vs rolldown's\n // `MinimalPluginContext`, which makes them unassignable (the `this`\n // type is contravariant). rolldown accepts rollup plugins at runtime,\n // so this is harmless β cast to work around the type mismatch.\n ctx.plugins.push(originalPlugin as unknown as Plugin);\n\n // `@vanilla-extract/rollup-plugin` emits the extracted CSS via\n // `emitFile({ type: 'asset', name: cssFileName })`. Because it uses\n // `name` (a hint) rather than `fileName`, rolldown runs it through the\n // default `assetFileNames` pattern (`assets/[name]-[hash][extname]`),\n // producing e.g. `dist/assets/vue-app-layout-dry0z-1l.css`.\n //\n // We can't *rename* an asset in `generateBundle` because rolldown\n // ignores mutations to a bundle entry's `fileName`. Instead, override\n // `assetFileNames` via the `outputOptions` hook so this single CSS\n // asset keeps its derived name verbatim (no hash, no `assets/` dir)\n // while every other asset keeps its original naming.\n ctx.plugins.push({\n name: `${PLUGIN_NAME}:rename-css`,\n outputOptions(opts) {\n const original = opts.assetFileNames;\n opts.assetFileNames = (assetInfo) => {\n if (assetInfo.names.includes(cssFileName)) {\n return cssFileName;\n }\n if (typeof original === 'function') return original(assetInfo);\n return original ?? 'assets/[name]-[hash][extname]';\n };\n return opts;\n },\n // Split vanilla-extract's single bundle into one CSS file per entry.\n //\n // `extract: { name }` mode collects the CSS of EVERY `.css.ts` in the\n // graph into one asset (`cssFileName`), which loses plugboy's per-entry\n // CSS contract: every entry with `css: true` declares a `./<entry>.css`\n // export, but only `cssFileName` is ever produced. We rebuild the\n // per-entry files from data vanilla-extract already exposes:\n // `moduleInfo.meta.css` (its public hand-off for extracted CSS, the\n // same field its own bundler reads) keyed by each entry chunk's\n // `imports`. We never parse vanilla-extract's asset names or re-add the\n // import statements it strips, so the only coupling is `meta.css`.\n //\n // Runs before `plugboy-optimize-css` (appended later in\n // `workspace.plugins`), so each emitted per-entry file still goes\n // through the postcss optimizations.\n //\n // Only splits when more than one entry actually has CSS. With a single\n // CSS entry (the common case) vanilla-extract's bundle is already the\n // correct, fully-ordered output, so it is left untouched and existing\n // single-entry packages are byte-for-byte unaffected.\n generateBundle(_options, bundle) {\n const entryCss: { name: string; source: string }[] = [];\n\n for (const chunk of Object.values(bundle)) {\n if (\n chunk.type !== 'chunk' ||\n !chunk.isEntry ||\n !chunk.fileName.endsWith('.mjs')\n ) {\n continue;\n }\n const cssChunks: string[] = [];\n for (const importId of chunk.imports) {\n const css = this.getModuleInfo(importId)?.meta?.css;\n if (typeof css === 'string') cssChunks.push(css);\n }\n if (cssChunks.length) {\n entryCss.push({\n name: chunk.name,\n source: cssChunks.join('\\n'),\n });\n }\n }\n\n // 0 or 1 CSS entry β vanilla-extract's bundle is already correct.\n if (entryCss.length <= 1) return;\n\n // Replace vanilla-extract's combined bundle with per-entry files.\n // The entry whose file name matches `cssFileName` overwrites the\n // existing asset in place β re-`emitFile`ing the same name would\n // trip rolldown's FILE_NAME_CONFLICT, since deleting the bundle\n // entry does not release the reserved file name. Other entries are\n // emitted as new assets.\n const reused = new Set<string>();\n for (const { name, source } of entryCss) {\n const fileName = `${name}.css`;\n const existing = bundle[fileName];\n if (existing && existing.type === 'asset') {\n existing.source = source;\n reused.add(fileName);\n } else {\n this.emitFile({ type: 'asset', fileName, source });\n }\n }\n\n // Drop vanilla-extract's combined bundle if no entry reused it.\n const combined = bundle[cssFileName];\n if (\n combined &&\n combined.type === 'asset' &&\n !reused.has(cssFileName)\n ) {\n delete bundle[cssFileName];\n }\n },\n // Merge tsdown's own CSS (emitted to `TSDOWN_CSS_FILE_NAME`) into the\n // vanilla-extract bundle so the package ships a single `cssFileName`.\n //\n // This has to happen in `writeBundle`, not `generateBundle`: tsdown\n // emits its CSS in a separate output pass, so `TSDOWN_CSS_FILE_NAME`\n // is absent from the bundle our `generateBundle` sees but present\n // (alongside the vanilla-extract asset) by `writeBundle`. By then both\n // files are already on disk, so we merge on disk rather than mutating\n // bundle sources. tsdown's CSS goes first so its `@layer` declarations\n // / resets are established before the extracted component styles.\n async writeBundle(outputOptions, bundle) {\n // Only the output pass that emitted tsdown's CSS performs the merge\n // (it is the one whose `bundle` contains `TSDOWN_CSS_FILE_NAME`).\n // This also prevents a second output from re-injecting the imports.\n const tmp = bundle[TSDOWN_CSS_FILE_NAME];\n if (!tmp) return;\n\n const tmpCss = tmp.type === 'asset' ? tmp.source.toString() : '';\n const dir = outputOptions.dir ?? '.';\n const tmpPath = path.join(dir, TSDOWN_CSS_FILE_NAME);\n const targetPath = path.join(dir, cssFileName);\n\n let targetCss = '';\n try {\n targetCss = await fs.readFile(targetPath, 'utf8');\n } catch {\n // No vanilla-extract output file (e.g. `.css.ts` produced no\n // rules) β tsdown's CSS becomes the whole `cssFileName`.\n }\n\n const merged = tmpCss\n ? targetCss\n ? `${tmpCss}\\n${targetCss}`\n : tmpCss\n : targetCss;\n\n if (merged) await fs.writeFile(targetPath, merged);\n await fs.rm(tmpPath, { force: true });\n },\n });\n }\n },\n },\n });\n}\n","import { findProjectPlugin } from '@fastkit/plugboy';\nimport { Plugin as VitePlugin } from 'vite';\nimport { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';\nimport { PLUGIN_NAME, VanillaExtractPlugin } from './types';\n\ntype VanillaExtractVitePluginOptions = NonNullable<\n Parameters<typeof vanillaExtractPlugin>[0]\n>;\n\nexport interface ViteVanillaExtractPluginOptions extends VanillaExtractVitePluginOptions {}\n\nexport async function ViteVanillaExtractPlugin(\n options: ViteVanillaExtractPluginOptions = {},\n): Promise<VitePlugin[]> {\n const plugin = await findProjectPlugin<VanillaExtractPlugin>(PLUGIN_NAME);\n const { identifiers: baseIdentifiers } = plugin?._options || {};\n\n return [\n ...vanillaExtractPlugin({\n identifiers: baseIdentifiers,\n ...options,\n }),\n // @MEMO\n // Plugin to prevent file scope mismatches when utilities using vanilla-extract\n // functions are placed in external files\n {\n name: 'vanilla-extract-fix-file-scope',\n config(viteConfig) {\n viteConfig.resolve ??= {};\n viteConfig.resolve.dedupe ??= [];\n viteConfig.resolve.dedupe.push(\n '@vanilla-extract/css',\n '@vanilla-extract/css/fileScope',\n );\n },\n },\n ];\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastkit/plugboy-vanilla-extract-plugin",
|
|
3
|
-
"version": "4.0.0-next.
|
|
3
|
+
"version": "4.0.0-next.11",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"repository": {
|
|
@@ -42,17 +42,17 @@
|
|
|
42
42
|
"dist"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@vanilla-extract/css": "^1.
|
|
46
|
-
"@vanilla-extract/rollup-plugin": "^1.5.
|
|
47
|
-
"@vanilla-extract/vite-plugin": "^5.
|
|
45
|
+
"@vanilla-extract/css": "^1.20.1",
|
|
46
|
+
"@vanilla-extract/rollup-plugin": "^1.5.3",
|
|
47
|
+
"@vanilla-extract/vite-plugin": "^5.2.2"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"vite": "^
|
|
51
|
-
"@fastkit/plugboy": "^1.0.0-next.
|
|
50
|
+
"vite": "^8.0.16",
|
|
51
|
+
"@fastkit/plugboy": "^1.0.0-next.6"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
|
55
|
-
"@fastkit/plugboy": "^1.0.0-next.
|
|
55
|
+
"@fastkit/plugboy": "^1.0.0-next.6"
|
|
56
56
|
},
|
|
57
57
|
"peerDependenciesMeta": {
|
|
58
58
|
"@vanilla-extract/vite-plugin": {
|