@sylphx/silk-vite-plugin 2.0.2 ā 2.2.0
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/dist/index.d.ts +35 -49
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +91 -180
- package/dist/index.old.d.ts +109 -0
- package/dist/index.old.d.ts.map +1 -0
- package/dist/index.old.js +279 -0
- package/package.json +3 -9
package/dist/index.d.ts
CHANGED
|
@@ -1,64 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @sylphx/silk-vite-plugin
|
|
3
|
-
*
|
|
3
|
+
* Zero-codegen Vite plugin using virtual CSS modules
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Architecture:
|
|
6
|
+
* 1. Scan source files for css() calls
|
|
7
|
+
* 2. Generate CSS via scanAndGenerate()
|
|
8
|
+
* 3. Create virtual 'silk.css' module
|
|
9
|
+
* 4. CSS flows through Vite's CSS pipeline (PostCSS, optimization, bundling)
|
|
6
10
|
*/
|
|
7
|
-
|
|
11
|
+
import type { Plugin } from 'vite';
|
|
12
|
+
import { type GenerateOptions } from '@sylphx/silk/codegen';
|
|
13
|
+
export interface SilkVitePluginOptions extends GenerateOptions {
|
|
8
14
|
/**
|
|
9
|
-
*
|
|
10
|
-
* @default
|
|
15
|
+
* Source directory to scan for css() calls
|
|
16
|
+
* @default './src'
|
|
11
17
|
*/
|
|
12
|
-
|
|
18
|
+
srcDir?: string;
|
|
13
19
|
/**
|
|
14
|
-
*
|
|
15
|
-
* @default 11
|
|
16
|
-
*/
|
|
17
|
-
brotliQuality?: number;
|
|
18
|
-
/**
|
|
19
|
-
* Enable gzip compression (.css.gz)
|
|
20
|
-
* @default true
|
|
21
|
-
*/
|
|
22
|
-
gzip?: boolean;
|
|
23
|
-
/**
|
|
24
|
-
* Gzip level (0-9)
|
|
25
|
-
* @default 9
|
|
26
|
-
*/
|
|
27
|
-
gzipLevel?: number;
|
|
28
|
-
}
|
|
29
|
-
export interface SilkPluginOptions {
|
|
30
|
-
/**
|
|
31
|
-
* Output CSS file path
|
|
20
|
+
* Virtual module ID (what users import)
|
|
32
21
|
* @default 'silk.css'
|
|
33
22
|
*/
|
|
34
|
-
|
|
23
|
+
virtualModuleId?: string;
|
|
35
24
|
/**
|
|
36
|
-
*
|
|
37
|
-
* @default
|
|
25
|
+
* Enable debug logging
|
|
26
|
+
* @default false
|
|
38
27
|
*/
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Pre-compression options
|
|
42
|
-
*/
|
|
43
|
-
compression?: CompressionOptions;
|
|
44
|
-
/**
|
|
45
|
-
* Babel plugin options
|
|
46
|
-
*/
|
|
47
|
-
babelOptions?: {
|
|
48
|
-
production?: boolean;
|
|
49
|
-
classPrefix?: string;
|
|
50
|
-
importSources?: string[];
|
|
51
|
-
tokens?: Record<string, any>;
|
|
52
|
-
breakpoints?: Record<string, string>;
|
|
53
|
-
};
|
|
28
|
+
debug?: boolean;
|
|
54
29
|
}
|
|
55
30
|
/**
|
|
56
|
-
* Silk
|
|
31
|
+
* Silk Vite plugin - Zero-codegen with virtual CSS module
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* // vite.config.ts
|
|
36
|
+
* import silk from '@sylphx/silk-vite-plugin'
|
|
37
|
+
*
|
|
38
|
+
* export default {
|
|
39
|
+
* plugins: [silk()]
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // app.tsx
|
|
45
|
+
* import 'silk.css' // Virtual module ā Vite CSS pipeline
|
|
46
|
+
* ```
|
|
57
47
|
*/
|
|
58
|
-
export
|
|
59
|
-
export
|
|
60
|
-
export declare const webpack: (options: SilkPluginOptions) => WebpackPluginInstance;
|
|
61
|
-
export declare const rollup: (options: SilkPluginOptions) => import("rollup").Plugin<any> | import("rollup").Plugin<any>[];
|
|
62
|
-
export declare const esbuild: (options: SilkPluginOptions) => import("unplugin").EsbuildPlugin;
|
|
63
|
-
export default vite;
|
|
48
|
+
export default function silkPlugin(options?: SilkVitePluginOptions): Plugin;
|
|
49
|
+
export { silkPlugin };
|
|
64
50
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE7E,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAID;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,OAAO,GAAE,qBAA0B,GAAG,MAAM,CAiG9E;AAGD,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,202 +1,113 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @sylphx/silk-vite-plugin
|
|
3
|
-
*
|
|
3
|
+
* Zero-codegen Vite plugin using virtual CSS modules
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Architecture:
|
|
6
|
+
* 1. Scan source files for css() calls
|
|
7
|
+
* 2. Generate CSS via scanAndGenerate()
|
|
8
|
+
* 3. Create virtual 'silk.css' module
|
|
9
|
+
* 4. CSS flows through Vite's CSS pipeline (PostCSS, optimization, bundling)
|
|
6
10
|
*/
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
import { gzipSync, brotliCompressSync, constants } from 'node:zlib';
|
|
10
|
-
// @ts-ignore - Babel preset types not needed
|
|
11
|
-
import presetReact from '@babel/preset-react';
|
|
12
|
-
// @ts-ignore - Babel preset types not needed
|
|
13
|
-
import presetTypeScript from '@babel/preset-typescript';
|
|
14
|
-
// Global CSS registry
|
|
15
|
-
const cssRules = new Map();
|
|
11
|
+
import { scanAndGenerate } from '@sylphx/silk/codegen';
|
|
12
|
+
const VIRTUAL_MODULE_PREFIX = '\0virtual:';
|
|
16
13
|
/**
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
34
|
-
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Silk unplugin instance
|
|
14
|
+
* Silk Vite plugin - Zero-codegen with virtual CSS module
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // vite.config.ts
|
|
19
|
+
* import silk from '@sylphx/silk-vite-plugin'
|
|
20
|
+
*
|
|
21
|
+
* export default {
|
|
22
|
+
* plugins: [silk()]
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* ```typescript
|
|
27
|
+
* // app.tsx
|
|
28
|
+
* import 'silk.css' // Virtual module ā Vite CSS pipeline
|
|
29
|
+
* ```
|
|
38
30
|
*/
|
|
39
|
-
export
|
|
40
|
-
const {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
gzip: compression.gzip ?? true,
|
|
45
|
-
gzipLevel: compression.gzipLevel ?? 9,
|
|
46
|
-
};
|
|
47
|
-
let isProduction = false;
|
|
31
|
+
export default function silkPlugin(options = {}) {
|
|
32
|
+
const { srcDir = './src', virtualModuleId = 'silk.css', debug = false, ...generateOptions } = options;
|
|
33
|
+
const resolvedVirtualModuleId = VIRTUAL_MODULE_PREFIX + virtualModuleId;
|
|
34
|
+
let generatedCSS = null;
|
|
35
|
+
let isDevMode = true;
|
|
48
36
|
return {
|
|
49
|
-
name: '
|
|
50
|
-
enforce: 'pre',
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
37
|
+
name: 'vite-plugin-silk',
|
|
38
|
+
enforce: 'pre',
|
|
39
|
+
configResolved(config) {
|
|
40
|
+
isDevMode = config.command === 'serve';
|
|
41
|
+
if (debug) {
|
|
42
|
+
console.log(`[Silk] Mode: ${isDevMode ? 'development' : 'production'}`);
|
|
43
|
+
console.log(`[Silk] Scanning: ${srcDir}`);
|
|
56
44
|
}
|
|
57
|
-
return /\.[jt]sx?$/.test(id);
|
|
58
45
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// Skip if no silk imports
|
|
62
|
-
if (!code.includes('@sylphx/silk')) {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
46
|
+
async buildStart() {
|
|
47
|
+
// Generate CSS on build start
|
|
65
48
|
try {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const babelPluginSilk = (await import('@sylphx/babel-plugin-silk')).default;
|
|
69
|
-
// Set production mode if not explicitly set
|
|
70
|
-
const babelOpts = {
|
|
71
|
-
...babelOptions,
|
|
72
|
-
production: babelOptions.production ?? isProduction,
|
|
73
|
-
};
|
|
74
|
-
// Transform with Babel plugin
|
|
75
|
-
const result = transformSync(code, {
|
|
76
|
-
filename: id,
|
|
77
|
-
presets: [
|
|
78
|
-
[presetReact, { runtime: 'automatic' }],
|
|
79
|
-
[presetTypeScript, { isTSX: true, allExtensions: true }],
|
|
80
|
-
],
|
|
81
|
-
plugins: [[babelPluginSilk, babelOpts]],
|
|
82
|
-
sourceMaps: true,
|
|
83
|
-
configFile: false,
|
|
84
|
-
babelrc: false,
|
|
85
|
-
});
|
|
86
|
-
if (!result || !result.code) {
|
|
87
|
-
return null;
|
|
49
|
+
if (debug) {
|
|
50
|
+
console.log('[Silk] Generating CSS...');
|
|
88
51
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (!isProduction && metadata.silk.cssRules.length > 0) {
|
|
97
|
-
console.log(`[Silk] Compiled ${metadata.silk.cssRules.length} CSS rules from ${path.basename(id)}`);
|
|
98
|
-
}
|
|
52
|
+
generatedCSS = await scanAndGenerate(srcDir, {
|
|
53
|
+
...generateOptions,
|
|
54
|
+
minify: generateOptions.minify ?? !isDevMode,
|
|
55
|
+
debug
|
|
56
|
+
});
|
|
57
|
+
if (debug) {
|
|
58
|
+
console.log(`[Silk] Generated ${generatedCSS.length} bytes of CSS`);
|
|
99
59
|
}
|
|
100
|
-
return {
|
|
101
|
-
code: result.code,
|
|
102
|
-
map: result.map || undefined,
|
|
103
|
-
};
|
|
104
60
|
}
|
|
105
61
|
catch (error) {
|
|
106
|
-
console.error(
|
|
107
|
-
|
|
62
|
+
console.error('[Silk] CSS generation failed:', error);
|
|
63
|
+
generatedCSS = '/* Silk CSS generation failed */';
|
|
108
64
|
}
|
|
109
65
|
},
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (
|
|
113
|
-
return;
|
|
114
|
-
// Generate CSS
|
|
115
|
-
let css = Array.from(cssRules.values()).join('\n');
|
|
116
|
-
if (shouldMinify ?? isProduction) {
|
|
117
|
-
css = minifyCSS(css);
|
|
66
|
+
resolveId(id) {
|
|
67
|
+
// Resolve virtual module
|
|
68
|
+
if (id === virtualModuleId) {
|
|
69
|
+
return resolvedVirtualModuleId;
|
|
118
70
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (isProduction) {
|
|
127
|
-
const cssBuffer = Buffer.from(css, 'utf-8');
|
|
128
|
-
const originalSize = cssBuffer.length;
|
|
129
|
-
// Brotli compression
|
|
130
|
-
if (compressionConfig.brotli) {
|
|
131
|
-
try {
|
|
132
|
-
const compressed = brotliCompressSync(cssBuffer, {
|
|
133
|
-
params: {
|
|
134
|
-
[constants.BROTLI_PARAM_QUALITY]: compressionConfig.brotliQuality,
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
this.emitFile({
|
|
138
|
-
type: 'asset',
|
|
139
|
-
fileName: `${outputFile}.br`,
|
|
140
|
-
source: compressed,
|
|
141
|
-
});
|
|
142
|
-
const savings = ((1 - compressed.length / originalSize) * 100).toFixed(1);
|
|
143
|
-
console.log(`[Silk] Brotli: ${formatBytes(compressed.length)} (-${savings}%)`);
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
console.warn('[Silk] Brotli compression failed:', error);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
// Gzip compression
|
|
150
|
-
if (compressionConfig.gzip) {
|
|
151
|
-
try {
|
|
152
|
-
const compressed = gzipSync(cssBuffer, {
|
|
153
|
-
level: compressionConfig.gzipLevel,
|
|
154
|
-
});
|
|
155
|
-
this.emitFile({
|
|
156
|
-
type: 'asset',
|
|
157
|
-
fileName: `${outputFile}.gz`,
|
|
158
|
-
source: compressed,
|
|
159
|
-
});
|
|
160
|
-
const savings = ((1 - compressed.length / originalSize) * 100).toFixed(1);
|
|
161
|
-
console.log(`[Silk] Gzip: ${formatBytes(compressed.length)} (-${savings}%)`);
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
console.warn('[Silk] Gzip compression failed:', error);
|
|
165
|
-
}
|
|
71
|
+
return null;
|
|
72
|
+
},
|
|
73
|
+
load(id) {
|
|
74
|
+
// Load virtual module
|
|
75
|
+
if (id === resolvedVirtualModuleId) {
|
|
76
|
+
if (!generatedCSS) {
|
|
77
|
+
return '/* No CSS generated */';
|
|
166
78
|
}
|
|
167
|
-
|
|
168
|
-
console.log(`\nš¦ Silk CSS Bundle:`);
|
|
169
|
-
console.log(` Original: ${formatBytes(originalSize)} (${outputFile})`);
|
|
170
|
-
console.log(` Rules: ${cssRules.size} atomic classes\n`);
|
|
79
|
+
return generatedCSS;
|
|
171
80
|
}
|
|
81
|
+
return null;
|
|
172
82
|
},
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
compiler.hooks.emit.tapPromise('SilkPlugin', async (compilation) => {
|
|
182
|
-
if (cssRules.size === 0)
|
|
183
|
-
return;
|
|
184
|
-
let css = Array.from(cssRules.values()).join('\n');
|
|
185
|
-
if (shouldMinify ?? true) {
|
|
186
|
-
css = minifyCSS(css);
|
|
83
|
+
async handleHotUpdate({ file, server }) {
|
|
84
|
+
// Watch mode: regenerate CSS on file changes
|
|
85
|
+
if (!isDevMode)
|
|
86
|
+
return;
|
|
87
|
+
// Check if changed file is in srcDir and matches our patterns
|
|
88
|
+
if (file.includes(srcDir) && /\.[jt]sx?$/.test(file)) {
|
|
89
|
+
if (debug) {
|
|
90
|
+
console.log(`[Silk] File changed: ${file}, regenerating CSS...`);
|
|
187
91
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
92
|
+
try {
|
|
93
|
+
generatedCSS = await scanAndGenerate(srcDir, {
|
|
94
|
+
...generateOptions,
|
|
95
|
+
minify: false,
|
|
96
|
+
debug
|
|
97
|
+
});
|
|
98
|
+
// Trigger HMR for the virtual module
|
|
99
|
+
const module = server.moduleGraph.getModuleById(resolvedVirtualModuleId);
|
|
100
|
+
if (module) {
|
|
101
|
+
server.moduleGraph.invalidateModule(module);
|
|
102
|
+
return [module];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error('[Silk] CSS regeneration failed:', error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
194
110
|
};
|
|
195
|
-
}
|
|
196
|
-
//
|
|
197
|
-
export
|
|
198
|
-
export const webpack = unpluginSilk.webpack;
|
|
199
|
-
export const rollup = unpluginSilk.rollup;
|
|
200
|
-
export const esbuild = unpluginSilk.esbuild;
|
|
201
|
-
// Default export for Vite
|
|
202
|
-
export default vite;
|
|
111
|
+
}
|
|
112
|
+
// Named exports for clarity
|
|
113
|
+
export { silkPlugin };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sylphx/silk-vite-plugin
|
|
3
|
+
* Universal plugin for zero-runtime Silk CSS-in-TypeScript
|
|
4
|
+
*
|
|
5
|
+
* Uses unplugin for cross-bundler compatibility
|
|
6
|
+
*/
|
|
7
|
+
export interface CompressionOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Enable Brotli compression (.css.br)
|
|
10
|
+
* @default true
|
|
11
|
+
*/
|
|
12
|
+
brotli?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Brotli quality (0-11)
|
|
15
|
+
* @default 11
|
|
16
|
+
*/
|
|
17
|
+
brotliQuality?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Enable gzip compression (.css.gz)
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
gzip?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Gzip level (0-9)
|
|
25
|
+
* @default 9
|
|
26
|
+
*/
|
|
27
|
+
gzipLevel?: number;
|
|
28
|
+
}
|
|
29
|
+
export interface PostCssOptions {
|
|
30
|
+
/**
|
|
31
|
+
* Enable PostCSS processing
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
enable?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* PostCSS plugins (e.g., autoprefixer, cssnano)
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* import autoprefixer from 'autoprefixer'
|
|
40
|
+
*
|
|
41
|
+
* postCss: {
|
|
42
|
+
* enable: true,
|
|
43
|
+
* plugins: [
|
|
44
|
+
* autoprefixer({ overrideBrowserslist: ['> 1%', 'last 2 versions'] })
|
|
45
|
+
* ]
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
plugins?: any[];
|
|
50
|
+
/**
|
|
51
|
+
* Generate source maps
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
sourceMaps?: boolean;
|
|
55
|
+
}
|
|
56
|
+
export interface SilkPluginOptions {
|
|
57
|
+
/**
|
|
58
|
+
* Output CSS file path
|
|
59
|
+
* @default 'silk.css'
|
|
60
|
+
*/
|
|
61
|
+
outputFile?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Minify CSS output
|
|
64
|
+
* @default true in production
|
|
65
|
+
*/
|
|
66
|
+
minify?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Pre-compression options
|
|
69
|
+
*/
|
|
70
|
+
compression?: CompressionOptions;
|
|
71
|
+
/**
|
|
72
|
+
* PostCSS processing (optional)
|
|
73
|
+
* Add autoprefixer for legacy browser support or custom PostCSS plugins
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* import autoprefixer from 'autoprefixer'
|
|
78
|
+
*
|
|
79
|
+
* {
|
|
80
|
+
* postCss: {
|
|
81
|
+
* enable: true,
|
|
82
|
+
* plugins: [autoprefixer()],
|
|
83
|
+
* sourceMaps: true
|
|
84
|
+
* }
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
postCss?: PostCssOptions;
|
|
89
|
+
/**
|
|
90
|
+
* Babel plugin options
|
|
91
|
+
*/
|
|
92
|
+
babelOptions?: {
|
|
93
|
+
production?: boolean;
|
|
94
|
+
classPrefix?: string;
|
|
95
|
+
importSources?: string[];
|
|
96
|
+
tokens?: Record<string, any>;
|
|
97
|
+
breakpoints?: Record<string, string>;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Silk unplugin instance
|
|
102
|
+
*/
|
|
103
|
+
export declare const unpluginSilk: import("unplugin").UnpluginInstance<SilkPluginOptions, boolean>;
|
|
104
|
+
export declare const vite: (options: SilkPluginOptions) => import("vite").Plugin<any> | import("vite").Plugin<any>[];
|
|
105
|
+
export declare const webpack: (options: SilkPluginOptions) => import("unplugin").WebpackPluginInstance;
|
|
106
|
+
export declare const rollup: (options: SilkPluginOptions) => import("rollup").Plugin<any> | import("rollup").Plugin<any>[];
|
|
107
|
+
export declare const esbuild: (options: SilkPluginOptions) => import("unplugin").EsbuildPlugin;
|
|
108
|
+
export default vite;
|
|
109
|
+
//# sourceMappingURL=index.old.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.old.d.ts","sourceRoot":"","sources":["../src/index.old.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IAEd;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,EAAE,GAAG,EAAE,CAAA;IAEf;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB;;OAEG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAA;IAEhC;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,EAAE,cAAc,CAAA;IAExB;;OAEG;IACH,YAAY,CAAC,EAAE;QACb,UAAU,CAAC,EAAE,OAAO,CAAA;QACpB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;QACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KACrC,CAAA;CACF;AA0DD;;GAEG;AACH,eAAO,MAAM,YAAY,iEAkPvB,CAAA;AAGF,eAAO,MAAM,IAAI,2FAAoB,CAAA;AACrC,eAAO,MAAM,OAAO,0EAAuB,CAAA;AAC3C,eAAO,MAAM,MAAM,+FAAsB,CAAA;AACzC,eAAO,MAAM,OAAO,kEAAuB,CAAA;AAG3C,eAAe,IAAI,CAAA"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sylphx/silk-vite-plugin
|
|
3
|
+
* Universal plugin for zero-runtime Silk CSS-in-TypeScript
|
|
4
|
+
*
|
|
5
|
+
* Uses unplugin for cross-bundler compatibility
|
|
6
|
+
*/
|
|
7
|
+
import { createUnplugin } from 'unplugin';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { gzipSync, brotliCompressSync, constants } from 'node:zlib';
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
11
|
+
// @ts-ignore - Babel preset types not needed
|
|
12
|
+
import presetReact from '@babel/preset-react';
|
|
13
|
+
// @ts-ignore - Babel preset types not needed
|
|
14
|
+
import presetTypeScript from '@babel/preset-typescript';
|
|
15
|
+
// Global CSS registry
|
|
16
|
+
const cssRules = new Map();
|
|
17
|
+
/**
|
|
18
|
+
* Minify CSS
|
|
19
|
+
*/
|
|
20
|
+
function minifyCSS(css) {
|
|
21
|
+
return css
|
|
22
|
+
.replace(/\s+/g, ' ')
|
|
23
|
+
.replace(/\s*([{}:;,])\s*/g, '$1')
|
|
24
|
+
.replace(/;}/g, '}')
|
|
25
|
+
.trim();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Format bytes
|
|
29
|
+
*/
|
|
30
|
+
function formatBytes(bytes) {
|
|
31
|
+
if (bytes < 1024)
|
|
32
|
+
return `${bytes}B`;
|
|
33
|
+
if (bytes < 1024 * 1024)
|
|
34
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
35
|
+
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Process CSS with PostCSS (optional)
|
|
39
|
+
*/
|
|
40
|
+
async function processWithPostCss(css, postCssOptions) {
|
|
41
|
+
if (!postCssOptions?.enable || !postCssOptions?.plugins?.length) {
|
|
42
|
+
return { css };
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
// Dynamic import to avoid forcing PostCSS as a dependency
|
|
46
|
+
const postcss = await import('postcss').then(m => m.default || m);
|
|
47
|
+
const result = await postcss(postCssOptions.plugins).process(css, {
|
|
48
|
+
from: undefined,
|
|
49
|
+
map: postCssOptions.sourceMaps ? { inline: false } : false
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
css: result.css,
|
|
53
|
+
map: result.map?.toString()
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (error?.code === 'ERR_MODULE_NOT_FOUND') {
|
|
58
|
+
console.warn('[Silk] PostCSS enabled but not installed. Run: npm install postcss');
|
|
59
|
+
return { css };
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Silk unplugin instance
|
|
66
|
+
*/
|
|
67
|
+
export const unpluginSilk = createUnplugin((options = {}) => {
|
|
68
|
+
const { outputFile = 'silk.css', minify: shouldMinify, compression = {}, babelOptions = {}, postCss, } = options;
|
|
69
|
+
const compressionConfig = {
|
|
70
|
+
brotli: compression.brotli ?? true,
|
|
71
|
+
brotliQuality: compression.brotliQuality ?? 11,
|
|
72
|
+
gzip: compression.gzip ?? true,
|
|
73
|
+
gzipLevel: compression.gzipLevel ?? 9,
|
|
74
|
+
};
|
|
75
|
+
let isProduction = false;
|
|
76
|
+
return {
|
|
77
|
+
name: 'unplugin-silk',
|
|
78
|
+
enforce: 'pre', // Run before other plugins
|
|
79
|
+
// Only transform TypeScript/JSX files
|
|
80
|
+
transformInclude(id) {
|
|
81
|
+
// Skip node_modules and virtual modules
|
|
82
|
+
if (id.includes('node_modules') || id.includes('\0')) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return /\.[jt]sx?$/.test(id);
|
|
86
|
+
},
|
|
87
|
+
// Transform code with Babel
|
|
88
|
+
async transform(code, id) {
|
|
89
|
+
// Skip if no silk imports
|
|
90
|
+
if (!code.includes('@sylphx/silk')) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
// Dynamic import to avoid bundling
|
|
95
|
+
const { transformSync } = await import('@babel/core');
|
|
96
|
+
const babelPluginSilk = (await import('@sylphx/babel-plugin-silk')).default;
|
|
97
|
+
// Set production mode if not explicitly set
|
|
98
|
+
const babelOpts = {
|
|
99
|
+
...babelOptions,
|
|
100
|
+
production: babelOptions.production ?? isProduction,
|
|
101
|
+
};
|
|
102
|
+
// Transform with Babel plugin
|
|
103
|
+
const result = transformSync(code, {
|
|
104
|
+
filename: id,
|
|
105
|
+
presets: [
|
|
106
|
+
[presetReact, { runtime: 'automatic' }],
|
|
107
|
+
[presetTypeScript, { isTSX: true, allExtensions: true }],
|
|
108
|
+
],
|
|
109
|
+
plugins: [[babelPluginSilk, babelOpts]],
|
|
110
|
+
sourceMaps: true,
|
|
111
|
+
configFile: false,
|
|
112
|
+
babelrc: false,
|
|
113
|
+
});
|
|
114
|
+
if (!result || !result.code) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
// Extract CSS from metadata
|
|
118
|
+
const metadata = result.metadata;
|
|
119
|
+
if (metadata?.silk?.cssRules) {
|
|
120
|
+
for (const [className, rule] of metadata.silk.cssRules) {
|
|
121
|
+
cssRules.set(className, rule);
|
|
122
|
+
}
|
|
123
|
+
// Log in dev mode
|
|
124
|
+
if (!isProduction && metadata.silk.cssRules.length > 0) {
|
|
125
|
+
console.log(`[Silk] Compiled ${metadata.silk.cssRules.length} CSS rules from ${path.basename(id)}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
code: result.code,
|
|
130
|
+
map: result.map || undefined,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error(`[Silk] Transform error in ${id}:`, error);
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
// Universal hook - works for all bundlers
|
|
139
|
+
async generateBundle() {
|
|
140
|
+
if (cssRules.size === 0)
|
|
141
|
+
return;
|
|
142
|
+
// Generate CSS
|
|
143
|
+
let css = Array.from(cssRules.values()).join('\n');
|
|
144
|
+
if (shouldMinify ?? isProduction) {
|
|
145
|
+
css = minifyCSS(css);
|
|
146
|
+
}
|
|
147
|
+
// Emit main CSS file
|
|
148
|
+
this.emitFile({
|
|
149
|
+
type: 'asset',
|
|
150
|
+
fileName: outputFile,
|
|
151
|
+
source: css,
|
|
152
|
+
});
|
|
153
|
+
// Generate compressed versions
|
|
154
|
+
if (isProduction) {
|
|
155
|
+
const cssBuffer = Buffer.from(css, 'utf-8');
|
|
156
|
+
const originalSize = cssBuffer.length;
|
|
157
|
+
// Brotli compression
|
|
158
|
+
if (compressionConfig.brotli) {
|
|
159
|
+
try {
|
|
160
|
+
const compressed = brotliCompressSync(cssBuffer, {
|
|
161
|
+
params: {
|
|
162
|
+
[constants.BROTLI_PARAM_QUALITY]: compressionConfig.brotliQuality,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
this.emitFile({
|
|
166
|
+
type: 'asset',
|
|
167
|
+
fileName: `${outputFile}.br`,
|
|
168
|
+
source: compressed,
|
|
169
|
+
});
|
|
170
|
+
const savings = ((1 - compressed.length / originalSize) * 100).toFixed(1);
|
|
171
|
+
console.log(`[Silk] Brotli: ${formatBytes(compressed.length)} (-${savings}%)`);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
console.warn('[Silk] Brotli compression failed:', error);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Gzip compression
|
|
178
|
+
if (compressionConfig.gzip) {
|
|
179
|
+
try {
|
|
180
|
+
const compressed = gzipSync(cssBuffer, {
|
|
181
|
+
level: compressionConfig.gzipLevel,
|
|
182
|
+
});
|
|
183
|
+
this.emitFile({
|
|
184
|
+
type: 'asset',
|
|
185
|
+
fileName: `${outputFile}.gz`,
|
|
186
|
+
source: compressed,
|
|
187
|
+
});
|
|
188
|
+
const savings = ((1 - compressed.length / originalSize) * 100).toFixed(1);
|
|
189
|
+
console.log(`[Silk] Gzip: ${formatBytes(compressed.length)} (-${savings}%)`);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
console.warn('[Silk] Gzip compression failed:', error);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Summary
|
|
196
|
+
console.log(`\nš¦ Silk CSS Bundle:`);
|
|
197
|
+
console.log(` Original: ${formatBytes(originalSize)} (${outputFile})`);
|
|
198
|
+
console.log(` Rules: ${cssRules.size} atomic classes\n`);
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
// Vite-specific hooks
|
|
202
|
+
vite: {
|
|
203
|
+
configResolved(config) {
|
|
204
|
+
isProduction = config.command === 'build' && config.mode === 'production';
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
// Webpack-specific hooks
|
|
208
|
+
webpack(compiler) {
|
|
209
|
+
compiler.hooks.emit.tapPromise('SilkPlugin', async (compilation) => {
|
|
210
|
+
if (cssRules.size === 0)
|
|
211
|
+
return;
|
|
212
|
+
let css = Array.from(cssRules.values()).join('\n');
|
|
213
|
+
let sourceMap;
|
|
214
|
+
// Minify CSS
|
|
215
|
+
if (shouldMinify ?? true) {
|
|
216
|
+
css = minifyCSS(css);
|
|
217
|
+
}
|
|
218
|
+
// PostCSS processing (optional)
|
|
219
|
+
if (postCss?.enable) {
|
|
220
|
+
const result = await processWithPostCss(css, postCss);
|
|
221
|
+
css = result.css;
|
|
222
|
+
sourceMap = result.map;
|
|
223
|
+
}
|
|
224
|
+
// Generate content hash for cache busting
|
|
225
|
+
const hash = createHash('md5').update(css).digest('hex').slice(0, 8);
|
|
226
|
+
const baseName = outputFile.replace('.css', '');
|
|
227
|
+
const hashedFileName = `${baseName}.${hash}.css`;
|
|
228
|
+
// Emit to both locations for Next.js compatibility
|
|
229
|
+
compilation.assets[outputFile] = {
|
|
230
|
+
source: () => css,
|
|
231
|
+
size: () => css.length,
|
|
232
|
+
};
|
|
233
|
+
// Emit with content hash to static/css directory (production)
|
|
234
|
+
const staticCssPath = `static/css/${hashedFileName}`;
|
|
235
|
+
compilation.assets[staticCssPath] = {
|
|
236
|
+
source: () => css,
|
|
237
|
+
size: () => css.length,
|
|
238
|
+
};
|
|
239
|
+
// Also emit non-hashed version for backwards compatibility
|
|
240
|
+
const staticCssPathLegacy = `static/css/${outputFile}`;
|
|
241
|
+
compilation.assets[staticCssPathLegacy] = {
|
|
242
|
+
source: () => css,
|
|
243
|
+
size: () => css.length,
|
|
244
|
+
};
|
|
245
|
+
// Emit source map if PostCSS generated one
|
|
246
|
+
if (sourceMap) {
|
|
247
|
+
compilation.assets[`${staticCssPath}.map`] = {
|
|
248
|
+
source: () => sourceMap,
|
|
249
|
+
size: () => sourceMap.length,
|
|
250
|
+
};
|
|
251
|
+
compilation.assets[`${outputFile}.map`] = {
|
|
252
|
+
source: () => sourceMap,
|
|
253
|
+
size: () => sourceMap.length,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
console.log(`[Silk] Emitted CSS:`);
|
|
257
|
+
console.log(` ⢠${outputFile} (${css.length} bytes)`);
|
|
258
|
+
console.log(` ⢠${staticCssPath} (content-hashed, cacheable)`);
|
|
259
|
+
console.log(` ⢠${staticCssPathLegacy} (legacy, no hash)`);
|
|
260
|
+
if (postCss?.enable) {
|
|
261
|
+
console.log(` ⢠PostCSS: ${postCss.plugins?.length || 0} plugins applied`);
|
|
262
|
+
}
|
|
263
|
+
if (sourceMap) {
|
|
264
|
+
console.log(` ⢠Source map generated`);
|
|
265
|
+
}
|
|
266
|
+
// Store the hashed filename for potential use in HTML injection
|
|
267
|
+
;
|
|
268
|
+
compilation.__silkCssFileName = hashedFileName;
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
});
|
|
273
|
+
// Export for different bundlers
|
|
274
|
+
export const vite = unpluginSilk.vite;
|
|
275
|
+
export const webpack = unpluginSilk.webpack;
|
|
276
|
+
export const rollup = unpluginSilk.rollup;
|
|
277
|
+
export const esbuild = unpluginSilk.esbuild;
|
|
278
|
+
// Default export for Vite
|
|
279
|
+
export default vite;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylphx/silk-vite-plugin",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Vite plugin for Silk - Build-time CSS extraction with production optimizations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"silk",
|
|
@@ -40,20 +40,14 @@
|
|
|
40
40
|
"prepublishOnly": "bun run build"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@
|
|
44
|
-
"@babel/preset-react": "^7.23.0",
|
|
45
|
-
"@babel/preset-typescript": "^7.23.0",
|
|
46
|
-
"@sylphx/babel-plugin-silk": "^2.0.0",
|
|
47
|
-
"@sylphx/silk": "^2.0.0",
|
|
48
|
-
"unplugin": "^2.3.10"
|
|
43
|
+
"@sylphx/silk": "workspace:*"
|
|
49
44
|
},
|
|
50
45
|
"devDependencies": {
|
|
51
|
-
"@types/babel__core": "^7.20.5",
|
|
52
46
|
"typescript": "^5.3.0",
|
|
53
47
|
"vite": "^5.0.0"
|
|
54
48
|
},
|
|
55
49
|
"peerDependencies": {
|
|
56
|
-
"vite": "^4.0.0 || ^5.0.0"
|
|
50
|
+
"vite": "^4.0.0 || ^5.0.0 || ^6.0.0"
|
|
57
51
|
},
|
|
58
52
|
"publishConfig": {
|
|
59
53
|
"access": "public"
|