chaincss 2.0.7 → 2.1.1

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.
Files changed (159) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/CODE_OF_CONDUCT.md +21 -0
  3. package/CONTRIBUTING.md +28 -0
  4. package/README.md +455 -226
  5. package/demo/demo/node_modules/caniuse-db/fulldata-json/data-2.0.json +1 -0
  6. package/demo/index.html +16 -0
  7. package/demo/package.json +20 -0
  8. package/demo/src/App.tsx +117 -0
  9. package/demo/src/chaincss-barrel.ts +9 -0
  10. package/demo/src/main.tsx +8 -0
  11. package/demo/src/styles.chain.ts +300 -0
  12. package/demo/vite.config.ts +46 -0
  13. package/dist/cli/commands/build.d.ts +0 -1
  14. package/dist/cli/commands/cache.d.ts +1 -0
  15. package/dist/cli/commands/init.d.ts +6 -3
  16. package/dist/cli/commands/timeline.d.ts +0 -1
  17. package/dist/cli/commands/watch.d.ts +0 -1
  18. package/dist/cli/index.d.ts +0 -1
  19. package/dist/cli/index.js +3213 -5296
  20. package/dist/cli/types.d.ts +51 -20
  21. package/dist/cli/utils/config-loader.d.ts +0 -1
  22. package/dist/cli/utils/file-utils.d.ts +27 -3
  23. package/dist/cli/utils/logger.d.ts +0 -1
  24. package/dist/compiler/Chain.d.ts +215 -0
  25. package/dist/compiler/animations.d.ts +76 -0
  26. package/dist/compiler/atomic-optimizer.d.ts +47 -12
  27. package/dist/compiler/breakpoints.d.ts +46 -0
  28. package/dist/compiler/btt.d.ts +36 -60
  29. package/dist/compiler/cache-manager.d.ts +58 -4
  30. package/dist/compiler/commonProps.d.ts +0 -1
  31. package/dist/compiler/content-addressable-cache.d.ts +78 -0
  32. package/dist/compiler/helpers.d.ts +54 -0
  33. package/dist/compiler/index.d.ts +16 -9
  34. package/dist/compiler/index.js +4450 -4316
  35. package/dist/compiler/prefixer.d.ts +17 -1
  36. package/dist/compiler/shorthands.d.ts +28 -0
  37. package/dist/compiler/suggestions.d.ts +43 -0
  38. package/dist/compiler/theme-contract.d.ts +16 -27
  39. package/dist/compiler/token-resolver.d.ts +69 -0
  40. package/dist/compiler/tokens.d.ts +33 -8
  41. package/dist/core/auto-detector.d.ts +34 -0
  42. package/dist/core/common-utils.d.ts +97 -0
  43. package/dist/core/compiler.d.ts +63 -23
  44. package/dist/core/constants.d.ts +137 -36
  45. package/dist/core/smart-chain.d.ts +3 -0
  46. package/dist/core/types.d.ts +122 -15
  47. package/dist/core/utils.d.ts +134 -17
  48. package/dist/index.d.ts +52 -8
  49. package/dist/index.js +7090 -5578
  50. package/dist/plugins/vite.d.ts +7 -5
  51. package/dist/plugins/vite.js +2964 -25641
  52. package/dist/plugins/webpack.d.ts +24 -1
  53. package/dist/plugins/webpack.js +209 -72
  54. package/dist/runtime/Chain.d.ts +32 -0
  55. package/dist/runtime/auto-hooks.d.ts +11 -0
  56. package/dist/runtime/hmr.d.ts +22 -2
  57. package/dist/runtime/index.d.ts +3 -2
  58. package/dist/runtime/index.js +3648 -301
  59. package/dist/runtime/injector.d.ts +39 -72
  60. package/dist/runtime/react.d.ts +17 -12
  61. package/dist/runtime/svelte.d.ts +15 -0
  62. package/dist/runtime/types.d.ts +126 -4
  63. package/dist/runtime/utils.d.ts +0 -1
  64. package/dist/runtime/vue.d.ts +34 -14
  65. package/package.json +59 -66
  66. package/src/cli/commands/build.ts +133 -0
  67. package/src/cli/commands/cache.ts +371 -0
  68. package/src/cli/commands/init.ts +230 -0
  69. package/src/cli/commands/timeline.ts +435 -0
  70. package/src/cli/commands/watch.ts +211 -0
  71. package/src/cli/index.ts +226 -0
  72. package/src/cli/types.ts +100 -0
  73. package/src/cli/utils/config-loader.ts +174 -0
  74. package/src/cli/utils/file-utils.ts +139 -0
  75. package/src/cli/utils/logger.ts +74 -0
  76. package/src/compiler/Chain.ts +831 -0
  77. package/src/compiler/animations.ts +517 -0
  78. package/src/compiler/atomic-optimizer.ts +786 -0
  79. package/src/compiler/breakpoints.ts +347 -0
  80. package/src/compiler/btt.ts +1147 -0
  81. package/src/compiler/cache-manager.ts +446 -0
  82. package/src/compiler/commonProps.ts +18 -0
  83. package/src/compiler/content-addressable-cache.ts +478 -0
  84. package/src/compiler/helpers.ts +407 -0
  85. package/src/compiler/index.ts +72 -0
  86. package/src/compiler/prefixer.ts +720 -0
  87. package/src/compiler/shorthands.ts +558 -0
  88. package/src/compiler/suggestions.ts +436 -0
  89. package/src/compiler/theme-contract.ts +197 -0
  90. package/src/compiler/token-resolver.ts +241 -0
  91. package/src/compiler/tokens.ts +612 -0
  92. package/src/core/auto-detector.ts +187 -0
  93. package/src/core/common-utils.ts +423 -0
  94. package/src/core/compiler.ts +835 -0
  95. package/src/core/constants.ts +424 -0
  96. package/src/core/index.ts +107 -0
  97. package/src/core/smart-chain.ts +163 -0
  98. package/src/core/types.ts +257 -0
  99. package/src/core/utils.ts +598 -0
  100. package/src/index.ts +208 -0
  101. package/src/plugins/vite.d.ts +316 -0
  102. package/src/plugins/vite.ts +424 -0
  103. package/src/plugins/webpack.d.ts +289 -0
  104. package/src/plugins/webpack.ts +416 -0
  105. package/src/runtime/Chain.ts +242 -0
  106. package/src/runtime/auto-hooks.tsx +127 -0
  107. package/src/runtime/auto-vue.ts +72 -0
  108. package/src/runtime/hmr.ts +212 -0
  109. package/src/runtime/index.ts +82 -0
  110. package/src/runtime/injector.ts +273 -0
  111. package/src/runtime/react.tsx +269 -0
  112. package/src/runtime/svelte.ts +15 -0
  113. package/src/runtime/types.ts +256 -0
  114. package/src/runtime/utils.ts +128 -0
  115. package/src/runtime/vite-env.d.ts +120 -0
  116. package/src/runtime/vue.ts +231 -0
  117. package/tsconfig.build.json +41 -0
  118. package/tsconfig.json +25 -0
  119. package/tsconfig.runtimes.json +18 -0
  120. package/dist/cli/cli.cjs +0 -7
  121. package/dist/cli/commands/build.d.ts.map +0 -1
  122. package/dist/cli/commands/compile.d.ts +0 -3
  123. package/dist/cli/commands/compile.d.ts.map +0 -1
  124. package/dist/cli/commands/init.d.ts.map +0 -1
  125. package/dist/cli/commands/timeline.d.ts.map +0 -1
  126. package/dist/cli/commands/watch.d.ts.map +0 -1
  127. package/dist/cli/index.d.ts.map +0 -1
  128. package/dist/cli/types.d.ts.map +0 -1
  129. package/dist/cli/utils/config-loader.d.ts.map +0 -1
  130. package/dist/cli/utils/file-utils.d.ts.map +0 -1
  131. package/dist/cli/utils/logger.d.ts.map +0 -1
  132. package/dist/compiler/atomic-optimizer.d.ts.map +0 -1
  133. package/dist/compiler/btt.d.ts.map +0 -1
  134. package/dist/compiler/cache-manager.d.ts.map +0 -1
  135. package/dist/compiler/commonProps.d.ts.map +0 -1
  136. package/dist/compiler/index.d.ts.map +0 -1
  137. package/dist/compiler/prefixer.d.ts.map +0 -1
  138. package/dist/compiler/theme-contract.d.ts.map +0 -1
  139. package/dist/compiler/tokens.d.ts.map +0 -1
  140. package/dist/compiler/types.d.ts +0 -57
  141. package/dist/compiler/types.d.ts.map +0 -1
  142. package/dist/core/compiler.d.ts.map +0 -1
  143. package/dist/core/constants.d.ts.map +0 -1
  144. package/dist/core/index.d.ts +0 -4
  145. package/dist/core/index.d.ts.map +0 -1
  146. package/dist/core/types.d.ts.map +0 -1
  147. package/dist/core/utils.d.ts.map +0 -1
  148. package/dist/index.d.ts.map +0 -1
  149. package/dist/plugins/vite.d.ts.map +0 -1
  150. package/dist/plugins/webpack.d.ts.map +0 -1
  151. package/dist/runtime/hmr.d.ts.map +0 -1
  152. package/dist/runtime/index.d.ts.map +0 -1
  153. package/dist/runtime/injector.d.ts.map +0 -1
  154. package/dist/runtime/react.d.ts.map +0 -1
  155. package/dist/runtime/react.js +0 -324
  156. package/dist/runtime/types.d.ts.map +0 -1
  157. package/dist/runtime/utils.d.ts.map +0 -1
  158. package/dist/runtime/vue.d.ts.map +0 -1
  159. package/dist/runtime/vue.js +0 -286
@@ -0,0 +1,720 @@
1
+ // chaincss/src/compiler/prefixer.ts
2
+ // Dynamic imports for optional dependencies
3
+ import type { ProcessOptions, Result } from 'postcss';
4
+
5
+ // Safe import helper — returns null if module not available
6
+ // This prevents Vite from crashing on optional dependencies
7
+ async function safeImport(moduleName: string): Promise<any> {
8
+ try {
9
+ // Use Function constructor to hide from Vite's static analyzer
10
+ const importFn = new Function('path', 'return import(path)');
11
+ return await importFn(moduleName);
12
+ } catch {
13
+ return null;
14
+ }
15
+ }
16
+
17
+
18
+ // Types for optional dependencies
19
+ type PostCSS = any;
20
+ type Browserslist = any;
21
+ type CaniuseData = any;
22
+ type Autoprefixer = any;
23
+
24
+ // Declare variables for optional dependencies (initially null)
25
+ let postcss: PostCSS | null = null;
26
+ let browserslist: Browserslist | null = null;
27
+ let caniuse: any = null;
28
+ let autoprefixer: Autoprefixer | null = null;
29
+
30
+ // Lazy loading flags
31
+ let postcssLoaded = false;
32
+ let browserslistLoaded = false;
33
+ let caniuseLoaded = false;
34
+ let autoprefixerLoaded = false;
35
+ let loadingPromises: Map<string, Promise<any>> = new Map();
36
+
37
+ // Lazy load functions with better error handling
38
+ async function loadPostcss() {
39
+ if (postcss) return postcss;
40
+ if (loadingPromises.has('postcss')) return loadingPromises.get('postcss');
41
+
42
+ const promise = (async () => {
43
+ if (!postcssLoaded) {
44
+ try {
45
+ const module = await import('postcss');
46
+ postcss = module.default || module;
47
+ } catch (err) {
48
+ if (process.env.DEBUG) {
49
+ console.warn('postcss not installed, using lightweight prefixing');
50
+ }
51
+ }
52
+ postcssLoaded = true;
53
+ }
54
+ return postcss;
55
+ })();
56
+
57
+ loadingPromises.set('postcss', promise);
58
+ return promise;
59
+ }
60
+
61
+ async function loadBrowserslist() {
62
+ if (browserslist) return browserslist;
63
+ if (loadingPromises.has('browserslist')) return loadingPromises.get('browserslist');
64
+
65
+ const promise = (async () => {
66
+ if (!browserslistLoaded) {
67
+ try {
68
+ const module = await import(/* @vite-ignore */ 'browserslist');
69
+ browserslist = module.default || module;
70
+ } catch (err) {
71
+ browserslist = null;
72
+ }
73
+ browserslistLoaded = true;
74
+ }
75
+ return browserslist;
76
+ })();
77
+
78
+ loadingPromises.set('browserslist', promise);
79
+ return promise;
80
+ }
81
+
82
+ async function loadCaniuse() {
83
+ if (caniuse) return caniuse;
84
+ if (loadingPromises.has('caniuse')) return loadingPromises.get('caniuse');
85
+
86
+ const promise = (async () => {
87
+ if (!caniuseLoaded) {
88
+ try {
89
+ // @ts-ignore
90
+ const caniuseModule = await safeImport("caniuse-db/fulldata-json/data-2.0.json");
91
+ caniuse = caniuseModule.default || caniuseModule;
92
+ } catch (err) {
93
+ caniuse = null;
94
+ }
95
+ caniuseLoaded = true;
96
+ }
97
+ return caniuse;
98
+ })();
99
+
100
+ loadingPromises.set('caniuse', promise);
101
+ return promise;
102
+ }
103
+
104
+ async function loadAutoprefixer() {
105
+ if (autoprefixer) return autoprefixer;
106
+ if (loadingPromises.has('autoprefixer')) return loadingPromises.get('autoprefixer');
107
+
108
+ const promise = (async () => {
109
+ if (!autoprefixerLoaded) {
110
+ try {
111
+ // @ts-ignore - autoprefixer is optional
112
+ const module = await import('autoprefixer');
113
+ autoprefixer = module.default || module;
114
+ } catch (err) {
115
+ if (process.env.DEBUG) {
116
+ console.warn('autoprefixer not installed');
117
+ }
118
+ }
119
+ autoprefixerLoaded = true;
120
+ }
121
+ return autoprefixer;
122
+ })();
123
+
124
+ loadingPromises.set('autoprefixer', promise);
125
+ return promise;
126
+ }
127
+
128
+ // Types
129
+ export interface PrefixerConfig {
130
+ browsers?: string[];
131
+ enabled?: boolean;
132
+ mode?: 'auto' | 'full' | 'lightweight';
133
+ sourceMap?: boolean;
134
+ sourceMapInline?: boolean;
135
+ remove?: boolean; // Remove outdated prefixes
136
+ add?: boolean; // Add missing prefixes
137
+ verbose?: boolean;
138
+ flexbox?: boolean | 'no-2009'; // Flexbox support
139
+ grid?: boolean | 'autoplace' | 'no-autoplace'; // Grid support
140
+ }
141
+
142
+ export interface PrefixerResult {
143
+ css: string;
144
+ map: string | null;
145
+ warnings?: string[];
146
+ }
147
+
148
+ export interface ProcessOptionsWithPaths {
149
+ from?: string;
150
+ to?: string;
151
+ map?: boolean | object;
152
+ }
153
+
154
+ export interface CaniuseFeature {
155
+ title: string;
156
+ description: string;
157
+ stats: Record<string, Record<string, string>>;
158
+ spec?: string;
159
+ status?: string;
160
+ }
161
+
162
+ // Built-in prefix map for lightweight mode
163
+ const LIGHTWEIGHT_PREFIX_MAP: Record<string, Record<string, string[]>> = {
164
+ // Transform properties
165
+ 'transform': {
166
+ 'webkit': ['-webkit-transform'],
167
+ 'ms': ['-ms-transform']
168
+ },
169
+ 'transform-origin': {
170
+ 'webkit': ['-webkit-transform-origin'],
171
+ 'ms': ['-ms-transform-origin']
172
+ },
173
+ 'transform-style': {
174
+ 'webkit': ['-webkit-transform-style']
175
+ },
176
+ 'perspective': {
177
+ 'webkit': ['-webkit-perspective']
178
+ },
179
+ 'backface-visibility': {
180
+ 'webkit': ['-webkit-backface-visibility']
181
+ },
182
+
183
+ // Transitions & Animations
184
+ 'transition': {
185
+ 'webkit': ['-webkit-transition']
186
+ },
187
+ 'transition-property': {
188
+ 'webkit': ['-webkit-transition-property']
189
+ },
190
+ 'transition-duration': {
191
+ 'webkit': ['-webkit-transition-duration']
192
+ },
193
+ 'transition-timing-function': {
194
+ 'webkit': ['-webkit-transition-timing-function']
195
+ },
196
+ 'animation': {
197
+ 'webkit': ['-webkit-animation']
198
+ },
199
+ 'animation-name': {
200
+ 'webkit': ['-webkit-animation-name']
201
+ },
202
+ 'animation-duration': {
203
+ 'webkit': ['-webkit-animation-duration']
204
+ },
205
+ 'animation-timing-function': {
206
+ 'webkit': ['-webkit-animation-timing-function']
207
+ },
208
+ 'animation-delay': {
209
+ 'webkit': ['-webkit-animation-delay']
210
+ },
211
+ 'animation-iteration-count': {
212
+ 'webkit': ['-webkit-animation-iteration-count']
213
+ },
214
+ 'animation-direction': {
215
+ 'webkit': ['-webkit-animation-direction']
216
+ },
217
+ 'animation-fill-mode': {
218
+ 'webkit': ['-webkit-animation-fill-mode']
219
+ },
220
+
221
+ // Filters
222
+ 'filter': {
223
+ 'webkit': ['-webkit-filter']
224
+ },
225
+ 'backdrop-filter': {
226
+ 'webkit': ['-webkit-backdrop-filter']
227
+ },
228
+
229
+ // Box properties
230
+ 'box-shadow': {
231
+ 'webkit': ['-webkit-box-shadow']
232
+ },
233
+ 'box-sizing': {
234
+ 'webkit': ['-webkit-box-sizing'],
235
+ 'moz': ['-moz-box-sizing']
236
+ },
237
+ 'border-radius': {
238
+ 'webkit': ['-webkit-border-radius'],
239
+ 'moz': ['-moz-border-radius']
240
+ },
241
+
242
+ // User interface
243
+ 'user-select': {
244
+ 'webkit': ['-webkit-user-select'],
245
+ 'moz': ['-moz-user-select'],
246
+ 'ms': ['-ms-user-select']
247
+ },
248
+ 'appearance': {
249
+ 'webkit': ['-webkit-appearance'],
250
+ 'moz': ['-moz-appearance']
251
+ },
252
+
253
+ // Text
254
+ 'text-fill-color': {
255
+ 'webkit': ['-webkit-text-fill-color']
256
+ },
257
+ 'text-stroke': {
258
+ 'webkit': ['-webkit-text-stroke']
259
+ },
260
+ 'text-stroke-color': {
261
+ 'webkit': ['-webkit-text-stroke-color']
262
+ },
263
+ 'text-stroke-width': {
264
+ 'webkit': ['-webkit-text-stroke-width']
265
+ },
266
+ 'background-clip': {
267
+ 'webkit': ['-webkit-background-clip']
268
+ },
269
+
270
+ // Masks
271
+ 'mask-image': {
272
+ 'webkit': ['-webkit-mask-image']
273
+ },
274
+ 'mask-clip': {
275
+ 'webkit': ['-webkit-mask-clip']
276
+ },
277
+ 'mask-composite': {
278
+ 'webkit': ['-webkit-mask-composite']
279
+ },
280
+ 'mask-origin': {
281
+ 'webkit': ['-webkit-mask-origin']
282
+ },
283
+ 'mask-position': {
284
+ 'webkit': ['-webkit-mask-position']
285
+ },
286
+ 'mask-repeat': {
287
+ 'webkit': ['-webkit-mask-repeat']
288
+ },
289
+ 'mask-size': {
290
+ 'webkit': ['-webkit-mask-size']
291
+ }
292
+ };
293
+
294
+ // Special value prefixes for lightweight mode
295
+ const LIGHTWEIGHT_VALUE_PREFIXES: Record<string, Record<string, string[]>> = {
296
+ 'display': {
297
+ 'flex': ['-webkit-flex', '-ms-flexbox'],
298
+ 'inline-flex': ['-webkit-inline-flex', '-ms-inline-flexbox'],
299
+ 'grid': ['-ms-grid'],
300
+ 'inline-grid': ['-ms-inline-grid']
301
+ },
302
+ 'position': {
303
+ 'sticky': ['-webkit-sticky']
304
+ }
305
+ };
306
+
307
+ // Main class
308
+ export class ChainCSSPrefixer {
309
+ config: Required<PrefixerConfig>;
310
+ hasBuiltInDeps: boolean;
311
+ hasAutoprefixer: boolean;
312
+ prefixerMode: 'auto' | 'full' | 'lightweight';
313
+ caniuseData: Record<string, CaniuseFeature> | null;
314
+ commonProperties: string[];
315
+ specialValues: Record<string, string[]>;
316
+ browserPrefixMap: Record<string, string>;
317
+ targetBrowsers: string[] | null;
318
+ private warnings: string[] = [];
319
+
320
+ constructor(config: PrefixerConfig = {}) {
321
+ this.config = {
322
+ browsers: config.browsers || ['> 0.5%', 'last 2 versions', 'not dead'],
323
+ enabled: config.enabled !== false,
324
+ mode: config.mode || 'auto',
325
+ sourceMap: config.sourceMap !== false,
326
+ sourceMapInline: config.sourceMapInline || false,
327
+ remove: config.remove !== false,
328
+ add: config.add !== false,
329
+ verbose: config.verbose || false,
330
+ flexbox: config.flexbox !== false,
331
+ grid: config.grid || 'autoplace'
332
+ };
333
+
334
+ this.hasBuiltInDeps = false;
335
+ this.hasAutoprefixer = false;
336
+ this.prefixerMode = config.mode || 'auto';
337
+ this.caniuseData = null;
338
+ this.commonProperties = this.getCommonProperties();
339
+ this.specialValues = {
340
+ 'display': ['flex', 'inline-flex', 'grid', 'inline-grid'],
341
+ 'background-clip': ['text'],
342
+ 'position': ['sticky']
343
+ };
344
+ this.browserPrefixMap = {
345
+ 'chrome': 'webkit',
346
+ 'safari': 'webkit',
347
+ 'firefox': 'moz',
348
+ 'ie': 'ms',
349
+ 'edge': 'webkit',
350
+ 'ios_saf': 'webkit',
351
+ 'and_chr': 'webkit',
352
+ 'android': 'webkit',
353
+ 'opera': 'webkit',
354
+ 'op_mob': 'webkit',
355
+ 'samsung': 'webkit',
356
+ 'and_ff': 'moz'
357
+ };
358
+ this.targetBrowsers = null;
359
+ }
360
+
361
+ async determineMode(): Promise<'auto' | 'full' | 'lightweight'> {
362
+ if (this.config.mode === 'full') {
363
+ const hasAutoprefixer = !!(await loadAutoprefixer());
364
+ if (!hasAutoprefixer && this.config.verbose) {
365
+ console.warn('⚠️ Full mode requested but autoprefixer not installed. Falling back to lightweight mode.');
366
+ console.warn(' To use full mode: npm install autoprefixer postcss caniuse-db browserslist\n');
367
+ }
368
+ return hasAutoprefixer ? 'full' : 'lightweight';
369
+ }
370
+ if (this.config.mode === 'lightweight') {
371
+ return 'lightweight';
372
+ }
373
+ if (this.config.mode === 'auto') {
374
+ const hasAutoprefixer = !!(await loadAutoprefixer());
375
+ if (this.config.verbose) {
376
+ console.log(`🔧 Prefixer mode: ${hasAutoprefixer ? 'full' : 'lightweight'}`);
377
+ }
378
+ return hasAutoprefixer ? 'full' : 'lightweight';
379
+ }
380
+ return 'lightweight';
381
+ }
382
+
383
+ async process(cssString: string, options: ProcessOptionsWithPaths = {}): Promise<PrefixerResult> {
384
+ this.warnings = [];
385
+
386
+ if (!this.config.enabled) {
387
+ return { css: cssString, map: null, warnings: [] };
388
+ }
389
+
390
+ try {
391
+ const mode = await this.determineMode();
392
+
393
+ if (mode === 'full') {
394
+ return await this.processWithAutoprefixer(cssString, options);
395
+ }
396
+
397
+ return await this.processWithBuiltIn(cssString, options);
398
+ } catch (err) {
399
+ const errorMsg = (err as Error).message;
400
+ this.warnings.push(`Prefixer error: ${errorMsg}`);
401
+ if (this.config.verbose) {
402
+ console.error('Prefixer error:', errorMsg);
403
+ }
404
+ return { css: cssString, map: null, warnings: this.warnings };
405
+ }
406
+ }
407
+
408
+ private async processWithAutoprefixer(
409
+ cssString: string,
410
+ options: ProcessOptionsWithPaths
411
+ ): Promise<PrefixerResult> {
412
+ const autoprefixerModule = await loadAutoprefixer();
413
+ const postcssModule = await loadPostcss();
414
+
415
+ if (!autoprefixerModule || !postcssModule) {
416
+ this.warnings.push('Autoprefixer or PostCSS not available, falling back to lightweight mode');
417
+ return await this.processWithBuiltIn(cssString, options);
418
+ }
419
+
420
+ const from = options.from || 'input.css';
421
+ const to = options.to || 'output.css';
422
+
423
+ try {
424
+ const result = await postcssModule([
425
+ autoprefixerModule({
426
+ overrideBrowserslist: this.config.browsers,
427
+ remove: this.config.remove,
428
+ add: this.config.add,
429
+ flexbox: this.config.flexbox,
430
+ grid: this.config.grid
431
+ })
432
+ ]).process(cssString, {
433
+ from,
434
+ to,
435
+ map: this.config.sourceMap ? {
436
+ inline: this.config.sourceMapInline,
437
+ annotation: false,
438
+ sourcesContent: true
439
+ } : false
440
+ });
441
+
442
+ if (result.warnings && this.config.verbose) {
443
+ result.warnings().forEach((warning: any) => {
444
+ this.warnings.push(warning.toString());
445
+ });
446
+ }
447
+
448
+ return {
449
+ css: result.css,
450
+ map: result.map ? result.map.toString() : null,
451
+ warnings: this.warnings
452
+ };
453
+ } catch (err) {
454
+ this.warnings.push(`Autoprefixer processing error: ${(err as Error).message}`);
455
+ return { css: cssString, map: null, warnings: this.warnings };
456
+ }
457
+ }
458
+
459
+ private async processWithBuiltIn(
460
+ cssString: string,
461
+ options: ProcessOptionsWithPaths
462
+ ): Promise<PrefixerResult> {
463
+ // Use lightweight prefixing
464
+ const prefixed = this.lightweightPrefix(cssString);
465
+
466
+ return {
467
+ css: prefixed,
468
+ map: null,
469
+ warnings: this.warnings
470
+ };
471
+ }
472
+
473
+ private lightweightPrefix(cssString: string): string {
474
+ let result = cssString;
475
+
476
+ // Process declarations
477
+ const declRegex = /([\w-]+)\s*:\s*([^;]+);/g;
478
+ let match;
479
+
480
+ while ((match = declRegex.exec(cssString)) !== null) {
481
+ const [fullMatch, prop, value] = match;
482
+ const trimmedProp = prop.trim();
483
+ const trimmedValue = value.trim();
484
+
485
+ // Check if property needs prefixing
486
+ const prefixes = LIGHTWEIGHT_PREFIX_MAP[trimmedProp];
487
+ if (prefixes && this.config.add) {
488
+ for (const [prefix, prefixedProps] of Object.entries(prefixes)) {
489
+ for (const prefixedProp of prefixedProps) {
490
+ const prefixedDecl = `${prefixedProp}: ${trimmedValue};`;
491
+ result = result.replace(fullMatch, `${prefixedDecl}\n${fullMatch}`);
492
+ }
493
+ }
494
+ }
495
+
496
+ // Check if value needs special prefixing
497
+ const valuePrefixes = LIGHTWEIGHT_VALUE_PREFIXES[trimmedProp];
498
+ if (valuePrefixes && valuePrefixes[trimmedValue] && this.config.add) {
499
+ for (const prefixedValue of valuePrefixes[trimmedValue]) {
500
+ const prefixedDecl = `${trimmedProp}: ${prefixedValue};`;
501
+ result = result.replace(fullMatch, `${prefixedDecl}\n${fullMatch}`);
502
+ }
503
+ }
504
+ }
505
+
506
+ // Handle keyframes with prefixes
507
+ const keyframesRegex = /@keyframes\s+(\w+)\s*\{([^}]+)\}/g;
508
+ while ((match = keyframesRegex.exec(cssString)) !== null) {
509
+ const [fullMatch, name, frames] = match;
510
+ const webkitKeyframes = `@-webkit-keyframes ${name} {${frames}}`;
511
+ result = result.replace(fullMatch, `${webkitKeyframes}\n${fullMatch}`);
512
+ }
513
+
514
+ return result;
515
+ }
516
+
517
+ private createBuiltInPlugin(): (root: any) => void {
518
+ return (root: any) => {
519
+ root.walkDecls((decl: any) => {
520
+ this.processBuiltInDeclaration(decl);
521
+ });
522
+ };
523
+ }
524
+
525
+ private processBuiltInDeclaration(decl: any): void {
526
+ const { prop, value } = decl;
527
+
528
+ if (this.commonProperties.includes(prop) && this.config.add) {
529
+ this.addPrefixesFromCaniuse(decl);
530
+ }
531
+
532
+ if (this.specialValues[prop]?.includes(value) && this.config.add) {
533
+ this.addSpecialValuePrefixes(decl);
534
+ }
535
+
536
+ if (!this.config.remove) return;
537
+
538
+ // Remove outdated prefixed versions
539
+ const unprefixedProp = prop.replace(/^-(webkit|moz|ms|o)-/, '');
540
+ if (unprefixedProp !== prop && this.commonProperties.includes(unprefixedProp)) {
541
+ // Check if we should keep this prefix
542
+ const shouldKeep = this.shouldKeepPrefix(prop, unprefixedProp);
543
+ if (!shouldKeep) {
544
+ decl.remove();
545
+ }
546
+ }
547
+ }
548
+
549
+ private shouldKeepPrefix(prop: string, unprefixed: string): boolean {
550
+ // Check if browser still needs this prefix
551
+ if (!this.targetBrowsers) return true;
552
+
553
+ const prefix = prop.match(/^-(webkit|moz|ms|o)-/)?.[1];
554
+ if (!prefix) return true;
555
+
556
+ // For modern browsers, many prefixes are no longer needed
557
+ const modernBrowsers = ['chrome >= 80', 'firefox >= 80', 'safari >= 13', 'edge >= 80'];
558
+ const needsPrefix = this.targetBrowsers.some(browser => {
559
+ return modernBrowsers.includes(browser);
560
+ });
561
+
562
+ return !needsPrefix;
563
+ }
564
+
565
+ private addPrefixesFromCaniuse(decl: any): void {
566
+ if (!this.caniuseData) return;
567
+
568
+ const feature = this.findFeature(decl.prop);
569
+ if (!feature) return;
570
+
571
+ const prefixes = new Set<string>();
572
+
573
+ this.targetBrowsers?.forEach(browser => {
574
+ const [id, versionStr] = browser.split(' ');
575
+ const version = parseFloat(versionStr.split('-')[0]);
576
+ const stats = feature.stats[id];
577
+
578
+ if (stats) {
579
+ const versions = Object.keys(stats)
580
+ .map(v => parseFloat(v.split('-')[0]))
581
+ .filter(v => !isNaN(v))
582
+ .sort((a, b) => a - b);
583
+
584
+ const closestVersion = versions.find(v => v <= version) || versions[0];
585
+
586
+ if (closestVersion) {
587
+ const support = stats[closestVersion.toString()];
588
+ if (support && support.includes('x')) {
589
+ const prefix = this.browserPrefixMap[id.split('-')[0]];
590
+ if (prefix) prefixes.add(prefix);
591
+ }
592
+ }
593
+ }
594
+ });
595
+
596
+ prefixes.forEach(prefix => {
597
+ decl.cloneBefore({
598
+ prop: `-${prefix}-${decl.prop}`,
599
+ value: decl.value
600
+ });
601
+ });
602
+ }
603
+
604
+ private addSpecialValuePrefixes(decl: any): void {
605
+ const { prop, value } = decl;
606
+
607
+ if (prop === 'display') {
608
+ if (value === 'flex' || value === 'inline-flex') {
609
+ decl.cloneBefore({ prop: 'display', value: `-webkit-${value}` });
610
+ decl.cloneBefore({
611
+ prop: 'display',
612
+ value: value === 'flex' ? '-ms-flexbox' : '-ms-inline-flexbox'
613
+ });
614
+ }
615
+ if (value === 'grid' || value === 'inline-grid') {
616
+ decl.cloneBefore({
617
+ prop: 'display',
618
+ value: value === 'grid' ? '-ms-grid' : '-ms-inline-grid'
619
+ });
620
+ }
621
+ }
622
+
623
+ if (prop === 'background-clip' && value === 'text') {
624
+ decl.cloneBefore({ prop: '-webkit-background-clip', value: 'text' });
625
+ }
626
+
627
+ if (prop === 'position' && value === 'sticky') {
628
+ decl.cloneBefore({ prop: 'position', value: '-webkit-sticky' });
629
+ }
630
+ }
631
+
632
+ private findFeature(property: string): CaniuseFeature | null {
633
+ if (!this.caniuseData) return null;
634
+
635
+ const featureMap: Record<string, string> = {
636
+ 'transform': 'transforms2d',
637
+ 'transform-origin': 'transforms2d',
638
+ 'transform-style': 'transforms3d',
639
+ 'perspective': 'transforms3d',
640
+ 'backface-visibility': 'transforms3d',
641
+ 'transition': 'css-transitions',
642
+ 'animation': 'css-animation',
643
+ 'backdrop-filter': 'backdrop-filter',
644
+ 'filter': 'css-filters',
645
+ 'user-select': 'user-select-none',
646
+ 'appearance': 'css-appearance',
647
+ 'mask-image': 'css-masks',
648
+ 'box-shadow': 'css-boxshadow',
649
+ 'border-radius': 'border-radius',
650
+ 'text-fill-color': 'text-stroke',
651
+ 'text-stroke': 'text-stroke',
652
+ 'background-clip': 'background-img-opts',
653
+ 'flex': 'flexbox',
654
+ 'flex-grow': 'flexbox',
655
+ 'flex-shrink': 'flexbox',
656
+ 'flex-basis': 'flexbox',
657
+ 'justify-content': 'flexbox',
658
+ 'align-items': 'flexbox',
659
+ 'grid': 'css-grid',
660
+ 'grid-template': 'css-grid',
661
+ 'grid-column': 'css-grid',
662
+ 'grid-row': 'css-grid'
663
+ };
664
+
665
+ const featureId = featureMap[property];
666
+ return featureId ? (this.caniuseData[featureId] as CaniuseFeature) : null;
667
+ }
668
+
669
+ private getCommonProperties(): string[] {
670
+ return [
671
+ 'transform', 'transform-origin', 'transform-style',
672
+ 'transition', 'transition-property', 'transition-duration', 'transition-timing-function',
673
+ 'animation', 'animation-name', 'animation-duration', 'animation-timing-function',
674
+ 'animation-delay', 'animation-iteration-count', 'animation-direction',
675
+ 'animation-fill-mode', 'animation-play-state',
676
+ 'backdrop-filter', 'filter',
677
+ 'user-select', 'appearance',
678
+ 'text-fill-color', 'text-stroke', 'text-stroke-color', 'text-stroke-width',
679
+ 'background-clip',
680
+ 'mask-image', 'mask-clip', 'mask-composite', 'mask-origin',
681
+ 'mask-position', 'mask-repeat', 'mask-size',
682
+ 'box-shadow', 'border-radius', 'box-sizing',
683
+ 'display', 'flex', 'flex-grow', 'flex-shrink', 'flex-basis',
684
+ 'justify-content', 'align-items', 'align-self', 'align-content',
685
+ 'grid', 'grid-template', 'grid-column', 'grid-row', 'gap',
686
+ 'column-gap', 'row-gap'
687
+ ];
688
+ }
689
+
690
+ // Utility method to check if a browser needs a specific prefix
691
+ needsPrefix(property: string, browser: string, version: number): boolean {
692
+ const feature = this.findFeature(property);
693
+ if (!feature) return false;
694
+
695
+ const stats = feature.stats[browser];
696
+ if (!stats) return false;
697
+
698
+ const support = stats[version.toString()];
699
+ return support ? support.includes('x') : false;
700
+ }
701
+
702
+ // Get all available prefixes for a property
703
+ getAvailablePrefixes(property: string): string[] {
704
+ const prefixes = LIGHTWEIGHT_PREFIX_MAP[property];
705
+ if (!prefixes) return [];
706
+
707
+ return Object.keys(prefixes);
708
+ }
709
+
710
+ // Reset the prefixer state
711
+ reset(): void {
712
+ this.warnings = [];
713
+ this.targetBrowsers = null;
714
+ this.hasAutoprefixer = false;
715
+ this.hasBuiltInDeps = false;
716
+ }
717
+ }
718
+
719
+ // ESM Export
720
+ export { ChainCSSPrefixer as default };