chaincss 1.13.2 ā 1.13.3
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 +6 -14
- package/browser/index.js +1 -1
- package/browser/rtt.js +39 -9
- package/browser/vue-composables.js +200 -0
- package/node/atomic-optimizer.js +279 -144
- package/node/btt.js +149 -102
- package/node/chaincss.js +197 -44
- package/node/prefixer.js +2 -2
- package/node/strVal.js +37 -51
- package/node/theme-validator.js +4 -4
- package/package.json +13 -3
- package/types.d.ts +51 -3
- package/node/css-properties.json +0 -633
package/node/chaincss.js
CHANGED
|
@@ -4,7 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const Module = require('module');
|
|
5
5
|
const chokidar = require('chokidar');
|
|
6
6
|
const CleanCSS = require('clean-css');
|
|
7
|
-
const { $, run, compile: originalCompile, chain } = require('./btt');
|
|
7
|
+
const { $, run, compile: originalCompile, chain, setAtomicOptimizer, createTokens, responsive, tokens } = require('./btt');
|
|
8
8
|
const ChainCSSPrefixer = require('./prefixer.js');
|
|
9
9
|
const strVal = require('./strVal.js');
|
|
10
10
|
const { AtomicOptimizer } = require('./atomic-optimizer');
|
|
@@ -17,11 +17,21 @@ let atomicOptimizer = null;
|
|
|
17
17
|
let config = {
|
|
18
18
|
atomic: {
|
|
19
19
|
enabled: false,
|
|
20
|
-
threshold:
|
|
21
|
-
naming: 'hash',
|
|
20
|
+
threshold: 2,
|
|
21
|
+
naming: 'hash',
|
|
22
22
|
cache: true,
|
|
23
23
|
cachePath: './.chaincss-cache',
|
|
24
|
-
minify: true
|
|
24
|
+
minify: true,
|
|
25
|
+
mode: 'hybrid',
|
|
26
|
+
alwaysAtomic: [],
|
|
27
|
+
neverAtomic: ['content', 'animation', 'transition', 'keyframes', 'counterIncrement', 'counterReset'],
|
|
28
|
+
frameworkOutput: {
|
|
29
|
+
react: false,
|
|
30
|
+
vue: false,
|
|
31
|
+
vanilla: true
|
|
32
|
+
},
|
|
33
|
+
preserveSelectors: false,
|
|
34
|
+
verbose: false
|
|
25
35
|
},
|
|
26
36
|
prefixer: {
|
|
27
37
|
enabled: true,
|
|
@@ -35,9 +45,14 @@ let config = {
|
|
|
35
45
|
let prefixer = new ChainCSSPrefixer(config.prefixer);
|
|
36
46
|
|
|
37
47
|
function deft_to_userConf(target, source) {
|
|
48
|
+
// Handle arrays specially
|
|
49
|
+
if (Array.isArray(source)) {
|
|
50
|
+
return source; // Return array as-is, don't merge
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
const result = { ...target };
|
|
39
54
|
for (const key in source) {
|
|
40
|
-
if (source[key] instanceof Object && key in target) {
|
|
55
|
+
if (source[key] instanceof Object && !Array.isArray(source[key]) && key in target) {
|
|
41
56
|
result[key] = deft_to_userConf(target[key], source[key]);
|
|
42
57
|
} else {
|
|
43
58
|
result[key] = source[key];
|
|
@@ -50,8 +65,7 @@ const ensureConfigExists = () => {
|
|
|
50
65
|
const configPath = path.join(process.cwd(), 'chaincss.config.cjs');
|
|
51
66
|
const configExists = fs.existsSync(configPath);
|
|
52
67
|
if (!configExists && !process.env.CHAINCSS_SKIP_CONFIG) {
|
|
53
|
-
|
|
54
|
-
fs.writeFileSync(configPath, defaultConfig);
|
|
68
|
+
fs.writeFileSync(configPath, strVal.userConf);
|
|
55
69
|
console.log('-- Successfully created config file: ./chaincss.config.cjs\n');
|
|
56
70
|
}
|
|
57
71
|
};
|
|
@@ -61,7 +75,35 @@ const loadUserConfig = () => {
|
|
|
61
75
|
if (fs.existsSync(configPath)) {
|
|
62
76
|
try {
|
|
63
77
|
const userConfig = require(configPath);
|
|
64
|
-
|
|
78
|
+
|
|
79
|
+
// Deep merge that preserves arrays
|
|
80
|
+
function deepMerge(target, source) {
|
|
81
|
+
const result = { ...target };
|
|
82
|
+
for (const key in source) {
|
|
83
|
+
if (Array.isArray(source[key])) {
|
|
84
|
+
result[key] = [...source[key]]; // Copy array
|
|
85
|
+
} else if (source[key] instanceof Object && key in target) {
|
|
86
|
+
result[key] = deepMerge(target[key], source[key]);
|
|
87
|
+
} else {
|
|
88
|
+
result[key] = source[key];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
config = deepMerge(config, userConfig);
|
|
95
|
+
|
|
96
|
+
// Ensure atomic arrays are arrays
|
|
97
|
+
if (config.atomic) {
|
|
98
|
+
if (!Array.isArray(config.atomic.alwaysAtomic)) {
|
|
99
|
+
config.atomic.alwaysAtomic = [];
|
|
100
|
+
}
|
|
101
|
+
if (!Array.isArray(config.atomic.neverAtomic)) {
|
|
102
|
+
config.atomic.neverAtomic = [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Validate prefixer browsers
|
|
65
107
|
if (config.prefixer) {
|
|
66
108
|
if (typeof config.prefixer.browsers === 'string') {
|
|
67
109
|
config.prefixer.browsers = config.prefixer.browsers.split(',').map(b => b.trim());
|
|
@@ -78,9 +120,14 @@ const loadUserConfig = () => {
|
|
|
78
120
|
|
|
79
121
|
const initAtomicOptimizer = () => {
|
|
80
122
|
if (config.atomic.enabled) {
|
|
123
|
+
//if (config.atomic.verbose) {
|
|
124
|
+
//console.log('--Initializing Atomic Optimizer with config:', JSON.stringify(config.atomic, null, 2));
|
|
125
|
+
//}
|
|
126
|
+
|
|
81
127
|
atomicOptimizer = new AtomicOptimizer(config.atomic);
|
|
82
|
-
|
|
83
|
-
|
|
128
|
+
|
|
129
|
+
// Inject the configured atomic optimizer into btt
|
|
130
|
+
setAtomicOptimizer(atomicOptimizer);
|
|
84
131
|
}
|
|
85
132
|
};
|
|
86
133
|
|
|
@@ -98,7 +145,11 @@ function parseArgs(args) {
|
|
|
98
145
|
prefixerMode: null,
|
|
99
146
|
sourceMap: null,
|
|
100
147
|
sourceMapInline: false,
|
|
101
|
-
atomic: false
|
|
148
|
+
atomic: false,
|
|
149
|
+
atomicMode: null,
|
|
150
|
+
atomicNaming: null,
|
|
151
|
+
atomicVerbose: false,
|
|
152
|
+
preserveSelectors: false
|
|
102
153
|
};
|
|
103
154
|
|
|
104
155
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -120,12 +171,20 @@ function parseArgs(args) {
|
|
|
120
171
|
result.sourceMapInline = true;
|
|
121
172
|
} else if (arg === '--atomic') {
|
|
122
173
|
result.atomic = true;
|
|
174
|
+
} else if (arg === '--atomic-mode' && args[i + 1]) {
|
|
175
|
+
result.atomicMode = args[i + 1];
|
|
176
|
+
i++;
|
|
177
|
+
} else if (arg === '--atomic-naming' && args[i + 1]) {
|
|
178
|
+
result.atomicNaming = args[i + 1];
|
|
179
|
+
i++;
|
|
180
|
+
} else if (arg === '--atomic-verbose') {
|
|
181
|
+
result.atomicVerbose = true;
|
|
182
|
+
} else if (arg === '--preserve-selectors') {
|
|
183
|
+
result.preserveSelectors = true;
|
|
123
184
|
} else if (!result.inputFile) {
|
|
124
185
|
result.inputFile = arg;
|
|
125
186
|
} else if (!result.outputFile) {
|
|
126
187
|
result.outputFile = arg;
|
|
127
|
-
}else if (arg === '--validate-themes') {
|
|
128
|
-
result.validateThemes = true;
|
|
129
188
|
}
|
|
130
189
|
}
|
|
131
190
|
return result;
|
|
@@ -150,14 +209,18 @@ const applyCliOptions = (cliOptions) => {
|
|
|
150
209
|
if (cliOptions.atomic) {
|
|
151
210
|
config.atomic.enabled = true;
|
|
152
211
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
212
|
+
if (cliOptions.atomicMode && ['atomic', 'standard', 'hybrid'].includes(cliOptions.atomicMode)) {
|
|
213
|
+
config.atomic.mode = cliOptions.atomicMode;
|
|
214
|
+
}
|
|
215
|
+
if (cliOptions.atomicNaming && ['hash', 'readable'].includes(cliOptions.atomicNaming)) {
|
|
216
|
+
config.atomic.naming = cliOptions.atomicNaming;
|
|
217
|
+
}
|
|
218
|
+
if (cliOptions.atomicVerbose) {
|
|
219
|
+
config.atomic.verbose = true;
|
|
220
|
+
}
|
|
221
|
+
if (cliOptions.preserveSelectors) {
|
|
222
|
+
config.atomic.preserveSelectors = true;
|
|
223
|
+
}
|
|
161
224
|
};
|
|
162
225
|
|
|
163
226
|
function watch(inputFile, outputFile) {
|
|
@@ -190,7 +253,7 @@ const transpilerModule = {
|
|
|
190
253
|
chain
|
|
191
254
|
};
|
|
192
255
|
|
|
193
|
-
// Native module-based JCSS file processor
|
|
256
|
+
// Native module-based JCSS file processor
|
|
194
257
|
const processJCSSFile = (filePath) => {
|
|
195
258
|
const abs = path.resolve(filePath);
|
|
196
259
|
|
|
@@ -222,6 +285,9 @@ const processJCSSFile = (filePath) => {
|
|
|
222
285
|
'compile',
|
|
223
286
|
'chain',
|
|
224
287
|
'get',
|
|
288
|
+
'createTokens',
|
|
289
|
+
'responsive',
|
|
290
|
+
'tokens',
|
|
225
291
|
content
|
|
226
292
|
);
|
|
227
293
|
compiledCache.set(abs, compiledFn);
|
|
@@ -238,7 +304,10 @@ const processJCSSFile = (filePath) => {
|
|
|
238
304
|
run,
|
|
239
305
|
originalCompile,
|
|
240
306
|
chain,
|
|
241
|
-
get
|
|
307
|
+
get,
|
|
308
|
+
createTokens,
|
|
309
|
+
responsive,
|
|
310
|
+
tokens
|
|
242
311
|
);
|
|
243
312
|
} catch (err) {
|
|
244
313
|
console.error(`Error processing ${abs}:`, err.message);
|
|
@@ -263,6 +332,9 @@ const processScript = (scriptBlock, filename) => {
|
|
|
263
332
|
'compile',
|
|
264
333
|
'chain',
|
|
265
334
|
'get',
|
|
335
|
+
'createTokens',
|
|
336
|
+
'responsive',
|
|
337
|
+
'tokens',
|
|
266
338
|
'__filename',
|
|
267
339
|
'__dirname',
|
|
268
340
|
scriptBlock
|
|
@@ -271,7 +343,7 @@ const processScript = (scriptBlock, filename) => {
|
|
|
271
343
|
}
|
|
272
344
|
|
|
273
345
|
try {
|
|
274
|
-
compiledFn($, run, originalCompile, chain, get, filename, dirname);
|
|
346
|
+
compiledFn($, run, originalCompile, chain, get, createTokens, responsive, tokens, filename, dirname);
|
|
275
347
|
} catch (err) {
|
|
276
348
|
console.error(`Error processing script in ${filename}:`, err.message);
|
|
277
349
|
throw err;
|
|
@@ -289,14 +361,16 @@ const processJavascriptBlocks = (content, inputpath) => {
|
|
|
289
361
|
outputCSS += blocks[i];
|
|
290
362
|
} else {
|
|
291
363
|
const scriptBlock = blocks[i];
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
364
|
+
if (scriptBlock && scriptBlock.trim()) {
|
|
365
|
+
try {
|
|
366
|
+
const blockCSS = processScript(scriptBlock, inputpath);
|
|
367
|
+
if (typeof blockCSS !== 'object' && typeof blockCSS !== 'undefined') {
|
|
368
|
+
outputCSS += blockCSS;
|
|
369
|
+
}
|
|
370
|
+
} catch (err) {
|
|
371
|
+
console.error(`Error processing script block:`, err.stack);
|
|
372
|
+
throw err;
|
|
296
373
|
}
|
|
297
|
-
} catch (err) {
|
|
298
|
-
console.error(`Error processing script block:`, err.stack);
|
|
299
|
-
throw err;
|
|
300
374
|
}
|
|
301
375
|
}
|
|
302
376
|
}
|
|
@@ -351,6 +425,26 @@ const processAndMinifyCss = async (css, inputFile, outputFile) => {
|
|
|
351
425
|
return { css: finalCss, map: finalSourceMap };
|
|
352
426
|
};
|
|
353
427
|
|
|
428
|
+
const writeFrameworkOutput = (outputDir, result) => {
|
|
429
|
+
const frameworkOutputs = [];
|
|
430
|
+
|
|
431
|
+
if (config.atomic.frameworkOutput.react && result.frameworkOutput) {
|
|
432
|
+
const reactPath = path.join(outputDir, 'atomic.react.js');
|
|
433
|
+
fs.writeFileSync(reactPath, result.frameworkOutput, 'utf8');
|
|
434
|
+
frameworkOutputs.push(`React: ${reactPath}`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (config.atomic.frameworkOutput.vue && result.frameworkOutput) {
|
|
438
|
+
const vuePath = path.join(outputDir, 'atomic.vue.js');
|
|
439
|
+
fs.writeFileSync(vuePath, result.frameworkOutput, 'utf8');
|
|
440
|
+
frameworkOutputs.push(`Vue: ${vuePath}`);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (frameworkOutputs.length > 0) {
|
|
444
|
+
console.log(`Framework outputs: ${frameworkOutputs.join(', ')}`);
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
|
|
354
448
|
const processor = async (inputFile, outputFile) => {
|
|
355
449
|
try {
|
|
356
450
|
const input = path.resolve(inputFile);
|
|
@@ -365,6 +459,7 @@ const processor = async (inputFile, outputFile) => {
|
|
|
365
459
|
}
|
|
366
460
|
const stylePath = path.join(outputDir, 'global.css');
|
|
367
461
|
const result = await processAndMinifyCss(processedCSS, input, stylePath);
|
|
462
|
+
|
|
368
463
|
if (result.css) {
|
|
369
464
|
fs.writeFileSync(stylePath, result.css, 'utf8');
|
|
370
465
|
if (result.map) {
|
|
@@ -373,38 +468,81 @@ const processor = async (inputFile, outputFile) => {
|
|
|
373
468
|
}
|
|
374
469
|
|
|
375
470
|
// ========== ATOMIC CLASS MAP GENERATION ==========
|
|
376
|
-
|
|
377
|
-
|
|
471
|
+
// ALWAYS generate atomic files when atomic optimizer is enabled
|
|
472
|
+
if (atomicOptimizer && config.atomic.enabled) {
|
|
473
|
+
|
|
474
|
+
// Get atomic classes from the optimizer
|
|
475
|
+
const atomicClasses = atomicOptimizer.getAllAtomicClasses();
|
|
476
|
+
const atomicClassMap = {};
|
|
477
|
+
|
|
478
|
+
// Build class map from component map
|
|
479
|
+
if (atomicOptimizer.componentMap) {
|
|
480
|
+
for (const [selector, data] of atomicOptimizer.componentMap) {
|
|
481
|
+
if (data.atomicClasses && data.atomicClasses.length > 0) {
|
|
482
|
+
atomicClassMap[selector] = data.atomicClasses.join(' ');
|
|
483
|
+
}
|
|
484
|
+
if (data.hoverAtomicClasses && data.hoverAtomicClasses.length > 0) {
|
|
485
|
+
atomicClassMap[`${selector}:hover`] = data.hoverAtomicClasses.join(' ');
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Write map.json
|
|
378
491
|
const mapJsonPath = path.join(outputDir, 'global.map.json');
|
|
379
492
|
const mapData = {
|
|
380
|
-
version: '
|
|
493
|
+
version: '2.0.0',
|
|
381
494
|
generated: new Date().toISOString(),
|
|
382
495
|
input: inputFile,
|
|
383
496
|
output: stylePath,
|
|
384
497
|
atomicEnabled: true,
|
|
498
|
+
mode: config.atomic.mode,
|
|
499
|
+
naming: config.atomic.naming,
|
|
500
|
+
outputStrategy: config.atomic.outputStrategy,
|
|
385
501
|
threshold: config.atomic.threshold,
|
|
386
|
-
classMap:
|
|
387
|
-
|
|
502
|
+
classMap: atomicClassMap,
|
|
503
|
+
atomicClasses: atomicClasses.map(a => ({ className: a.className, prop: a.prop, value: a.value })),
|
|
504
|
+
stats: chain.atomicStats || atomicOptimizer.getStats()
|
|
388
505
|
};
|
|
389
506
|
fs.writeFileSync(mapJsonPath, JSON.stringify(mapData, null, 2), 'utf8');
|
|
390
|
-
console.log(` Class map: ${mapJsonPath}`);
|
|
391
507
|
|
|
392
|
-
|
|
508
|
+
|
|
509
|
+
// Write JS module
|
|
393
510
|
const jsPath = path.join(outputDir, 'global.classes.js');
|
|
394
|
-
|
|
511
|
+
let jsContent = `/**
|
|
395
512
|
* ChainCSS Atomic Class Map
|
|
396
513
|
* Generated: ${new Date().toISOString()}
|
|
514
|
+
* Mode: ${config.atomic.mode}
|
|
515
|
+
* Output Strategy: ${config.atomic.outputStrategy}
|
|
516
|
+
* Naming: ${config.atomic.naming}
|
|
397
517
|
* Threshold: ${config.atomic.threshold}
|
|
398
518
|
*/
|
|
399
519
|
|
|
400
|
-
export const classMap = ${JSON.stringify(
|
|
520
|
+
export const classMap = ${JSON.stringify(atomicClassMap, null, 2)};
|
|
521
|
+
|
|
522
|
+
export const atomicClasses = ${JSON.stringify(atomicClasses.map(a => a.className), null, 2)};
|
|
401
523
|
|
|
402
524
|
export const getClass = (selector) => classMap[selector] || '';
|
|
403
525
|
|
|
526
|
+
export const getAtomicClass = (prop, value) => {
|
|
527
|
+
const atomic = atomicClasses.find(a => a.prop === prop && a.value === value);
|
|
528
|
+
return atomic ? atomic.className : null;
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
export const getAllClasses = () => Object.values(classMap).join(' ');
|
|
532
|
+
|
|
533
|
+
export const applyClasses = (element, selector) => {
|
|
534
|
+
if (!element) return;
|
|
535
|
+
const classes = getClass(selector);
|
|
536
|
+
if (classes) {
|
|
537
|
+
element.className = classes;
|
|
538
|
+
}
|
|
539
|
+
return classes;
|
|
540
|
+
};
|
|
541
|
+
|
|
404
542
|
export default classMap;
|
|
405
543
|
`;
|
|
544
|
+
|
|
406
545
|
fs.writeFileSync(jsPath, jsContent, 'utf8');
|
|
407
|
-
console.log(` JS module: ${jsPath}`);
|
|
408
546
|
|
|
409
547
|
// Write TypeScript definitions
|
|
410
548
|
const dtsPath = path.join(outputDir, 'global.classes.d.ts');
|
|
@@ -413,13 +551,24 @@ export default classMap;
|
|
|
413
551
|
* Generated: ${new Date().toISOString()}
|
|
414
552
|
*/
|
|
415
553
|
|
|
554
|
+
export interface AtomicClass {
|
|
555
|
+
className: string;
|
|
556
|
+
prop: string;
|
|
557
|
+
value: string;
|
|
558
|
+
}
|
|
559
|
+
|
|
416
560
|
export const classMap: Record<string, string>;
|
|
561
|
+
export const atomicClasses: string[];
|
|
417
562
|
export const getClass: (selector: string) => string;
|
|
563
|
+
export const getAtomicClass: (prop: string, value: string) => string | null;
|
|
564
|
+
export const getAllClasses: () => string;
|
|
565
|
+
export const applyClasses: (element: HTMLElement | null, selector: string) => string | undefined;
|
|
566
|
+
|
|
418
567
|
declare const _default: Record<string, string>;
|
|
419
568
|
export default _default;
|
|
420
569
|
`;
|
|
421
570
|
fs.writeFileSync(dtsPath, dtsContent, 'utf8');
|
|
422
|
-
|
|
571
|
+
|
|
423
572
|
|
|
424
573
|
// Update manifest
|
|
425
574
|
const manifestPath = path.join(outputDir, 'chaincss-manifest.json');
|
|
@@ -431,18 +580,22 @@ export default _default;
|
|
|
431
580
|
}
|
|
432
581
|
|
|
433
582
|
manifest[path.basename(stylePath)] = {
|
|
583
|
+
version: '2.0.0',
|
|
434
584
|
css: path.basename(stylePath),
|
|
435
585
|
map: path.basename(mapJsonPath),
|
|
436
586
|
js: path.basename(jsPath),
|
|
437
587
|
dts: path.basename(dtsPath),
|
|
438
588
|
generated: new Date().toISOString(),
|
|
439
589
|
input: inputFile,
|
|
590
|
+
mode: config.atomic.mode,
|
|
591
|
+
outputStrategy: config.atomic.outputStrategy,
|
|
592
|
+
naming: config.atomic.naming,
|
|
440
593
|
threshold: config.atomic.threshold,
|
|
441
|
-
|
|
594
|
+
atomicClassesCount: atomicClasses.length,
|
|
595
|
+
stats: chain.atomicStats || atomicOptimizer.getStats()
|
|
442
596
|
};
|
|
443
597
|
|
|
444
598
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
445
|
-
console.log(` Manifest: ${manifestPath}`);
|
|
446
599
|
}
|
|
447
600
|
}
|
|
448
601
|
} catch (err) {
|
package/node/prefixer.js
CHANGED
|
@@ -39,8 +39,8 @@ class ChainCSSPrefixer {
|
|
|
39
39
|
}
|
|
40
40
|
determineMode() {
|
|
41
41
|
if (this.config.mode === 'full' && !this.hasAutoprefixer) {
|
|
42
|
-
console.warn('
|
|
43
|
-
console.warn(' To use full mode
|
|
42
|
+
console.warn('Full mode requested but autoprefixer not installed. Falling back to lightweight mode.');
|
|
43
|
+
console.warn(' To use full mode: npm install autoprefixer postcss caniuse-db browserslist\n');
|
|
44
44
|
return 'lightweight';
|
|
45
45
|
}
|
|
46
46
|
if (this.config.mode === 'lightweight') {
|
package/node/strVal.js
CHANGED
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
const strVal = {
|
|
2
|
-
userConf: `//
|
|
2
|
+
userConf: `// ChainCSS Configuration
|
|
3
|
+
// Generated: ${new Date().toISOString()}
|
|
4
|
+
|
|
3
5
|
module.exports = {
|
|
4
6
|
atomic: {
|
|
5
|
-
enabled: true,
|
|
6
|
-
threshold: 3,
|
|
7
|
-
naming: 'hash',
|
|
8
|
-
cache: true,
|
|
7
|
+
enabled: true, // Enable atomic CSS optimization
|
|
8
|
+
threshold: 3, // Minimum usage count for atomic conversion
|
|
9
|
+
naming: 'hash', // 'hash' (c_3b82f6) or 'readable' (bg-blue-500)
|
|
10
|
+
cache: true, // Cache atomic classes between builds
|
|
9
11
|
cachePath: './.chaincss-cache',
|
|
10
|
-
minify: true
|
|
12
|
+
minify: true, // Minify CSS output
|
|
13
|
+
mode: 'hybrid', // 'atomic' | 'standard' | 'hybrid'
|
|
14
|
+
alwaysAtomic: [], // Force these properties to be atomic
|
|
15
|
+
neverAtomic: [ // Never make these properties atomic
|
|
16
|
+
'content', 'animation', 'transition', 'keyframes',
|
|
17
|
+
'counterIncrement', 'counterReset'
|
|
18
|
+
],
|
|
19
|
+
outputStrategy: 'component-first',
|
|
20
|
+
frameworkOutput: {
|
|
21
|
+
react: false, // Generate React hooks
|
|
22
|
+
vue: false, // Generate Vue composables
|
|
23
|
+
vanilla: true // Generate vanilla JS class map
|
|
24
|
+
},
|
|
25
|
+
preserveSelectors: false, // Keep original selector names in comments
|
|
26
|
+
verbose: true // Show detailed atomic optimization stats
|
|
11
27
|
},
|
|
12
28
|
prefixer: {
|
|
13
|
-
mode: 'auto',
|
|
14
|
-
browsers: ['> 0.5%', 'last 2 versions', 'not dead'],
|
|
15
29
|
enabled: true,
|
|
30
|
+
mode: 'auto', // 'auto' | 'always' | 'never'
|
|
31
|
+
browsers: ['> 0.5%', 'last 2 versions', 'not dead'],
|
|
16
32
|
sourceMap: true,
|
|
17
33
|
sourceMapInline: false
|
|
18
34
|
}
|
|
@@ -34,27 +50,14 @@ Options:
|
|
|
34
50
|
|
|
35
51
|
# Atomic CSS Optimization
|
|
36
52
|
--atomic Enable atomic CSS optimization
|
|
37
|
-
--
|
|
38
|
-
--atomic-naming <
|
|
53
|
+
--atomic-mode <mode> Atomic mode: atomic, standard, hybrid (default: hybrid)
|
|
54
|
+
--atomic-naming <scheme> Naming scheme: hash, readable (default: hash)
|
|
55
|
+
--atomic-verbose Show detailed atomic optimization stats
|
|
56
|
+
--preserve-selectors Keep original selector names in comments
|
|
39
57
|
--no-atomic-cache Disable atomic CSS cache
|
|
40
58
|
|
|
41
|
-
# Performance & Debug
|
|
42
|
-
--verbose Enable verbose logging
|
|
43
|
-
--debug Enable debug mode with detailed output
|
|
44
|
-
--tree-shake Remove unused CSS classes (dead code elimination)
|
|
45
|
-
|
|
46
59
|
# Output Control
|
|
47
|
-
--minify Minify CSS output (default: true in production)
|
|
48
60
|
--no-minify Disable CSS minification
|
|
49
|
-
--out-dir <dir> Output directory (default: same as outputFile dir)
|
|
50
|
-
--manifest Generate build manifest file
|
|
51
|
-
|
|
52
|
-
# Configuration
|
|
53
|
-
--config <path> Path to config file (default: chaincss.config.cjs)
|
|
54
|
-
--no-config Ignore config file, use CLI options only
|
|
55
|
-
|
|
56
|
-
# Theme Validation
|
|
57
|
-
--validate-themes Validate theme contracts during build
|
|
58
61
|
|
|
59
62
|
# Help
|
|
60
63
|
--help, -h Show this help message
|
|
@@ -62,44 +65,27 @@ Options:
|
|
|
62
65
|
|
|
63
66
|
Examples:
|
|
64
67
|
# Basic compilation
|
|
65
|
-
chaincss style.jcss
|
|
68
|
+
chaincss style.jcss dist/
|
|
66
69
|
|
|
67
70
|
# Watch mode for development
|
|
68
|
-
chaincss style.jcss
|
|
71
|
+
chaincss style.jcss dist/ --watch
|
|
69
72
|
|
|
70
73
|
# Atomic CSS optimization
|
|
71
|
-
chaincss style.jcss
|
|
74
|
+
chaincss style.jcss dist/ --atomic
|
|
72
75
|
|
|
73
|
-
# With custom
|
|
74
|
-
chaincss style.jcss
|
|
76
|
+
# With custom naming scheme
|
|
77
|
+
chaincss style.jcss dist/ --atomic --atomic-naming readable
|
|
75
78
|
|
|
76
|
-
#
|
|
77
|
-
chaincss style.jcss
|
|
79
|
+
# Verbose output for debugging
|
|
80
|
+
chaincss style.jcss dist/ --atomic --atomic-verbose
|
|
78
81
|
|
|
79
82
|
# Custom browser support
|
|
80
|
-
chaincss style.jcss
|
|
81
|
-
|
|
82
|
-
# Full autoprefixer mode
|
|
83
|
-
chaincss style.jcss style.css --prefixer-mode full
|
|
84
|
-
|
|
85
|
-
# Validate themes during build
|
|
86
|
-
chaincss style.jcss style.css --validate-themes
|
|
87
|
-
|
|
88
|
-
# Debug mode with verbose output
|
|
89
|
-
chaincss style.jcss style.css --debug --verbose
|
|
90
|
-
|
|
91
|
-
# Disable prefixing (for modern browsers only)
|
|
92
|
-
chaincss style.jcss style.css --no-prefix
|
|
93
|
-
|
|
94
|
-
# With custom config file
|
|
95
|
-
chaincss style.jcss style.css --config ./my-chaincss.config.cjs
|
|
83
|
+
chaincss style.jcss dist/ --browsers "> 1%, last 2 versions, not dead"
|
|
96
84
|
|
|
97
85
|
Notes:
|
|
98
86
|
- Atomic CSS optimization reduces CSS size by reusing common style patterns
|
|
99
|
-
- Tree shaking removes unused CSS classes from final bundle
|
|
100
|
-
- Theme validation ensures all themes have required tokens
|
|
101
87
|
- Use --watch during development for instant updates
|
|
102
|
-
- Use --atomic
|
|
88
|
+
- Use --atomic for production builds to optimize CSS bundle size
|
|
103
89
|
`
|
|
104
90
|
}
|
|
105
91
|
|
package/node/theme-validator.js
CHANGED
|
@@ -8,7 +8,7 @@ export function validateThemeFiles(configPath) {
|
|
|
8
8
|
|
|
9
9
|
const { contract, themes } = config;
|
|
10
10
|
|
|
11
|
-
console.log('\
|
|
11
|
+
console.log('\nValidating Theme Contract...\n');
|
|
12
12
|
|
|
13
13
|
const errors = [];
|
|
14
14
|
|
|
@@ -16,9 +16,9 @@ export function validateThemeFiles(configPath) {
|
|
|
16
16
|
const themeName = theme.name || `theme-${index}`;
|
|
17
17
|
try {
|
|
18
18
|
validateTheme(contract, theme.values);
|
|
19
|
-
console.log(
|
|
19
|
+
console.log(`${themeName}: Valid`);
|
|
20
20
|
} catch (err) {
|
|
21
|
-
errors.push(
|
|
21
|
+
errors.push(`${themeName}: ${err.message}`);
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
|
|
@@ -28,5 +28,5 @@ export function validateThemeFiles(configPath) {
|
|
|
28
28
|
process.exit(1);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
console.log('\
|
|
31
|
+
console.log('\nAll themes valid!\n');
|
|
32
32
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chaincss",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.3",
|
|
4
4
|
"description": "Chainable CSS-in-JS with build-time compilation, atomic CSS, and zero-runtime options",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"css",
|
|
@@ -61,11 +61,20 @@
|
|
|
61
61
|
},
|
|
62
62
|
"default": "./browser/react-hooks.js"
|
|
63
63
|
},
|
|
64
|
-
"./
|
|
64
|
+
"./vue": {
|
|
65
|
+
"types": "./types.d.ts",
|
|
66
|
+
"browser": {
|
|
67
|
+
"import": "./browser/vue-composables.js",
|
|
68
|
+
"require": "./browser/vue-composables.js"
|
|
69
|
+
},
|
|
70
|
+
"default": "./browser/vue-composables.js"
|
|
71
|
+
},
|
|
72
|
+
"./browser": {
|
|
65
73
|
"browser": {
|
|
66
74
|
"import": "./browser/*",
|
|
67
75
|
"require": "./browser/*"
|
|
68
|
-
}
|
|
76
|
+
},
|
|
77
|
+
"default": "./browser/index.js"
|
|
69
78
|
},
|
|
70
79
|
"./node/*": {
|
|
71
80
|
"node": {
|
|
@@ -120,6 +129,7 @@
|
|
|
120
129
|
"clean-css": "^5.3.3"
|
|
121
130
|
},
|
|
122
131
|
"peerDependencies": {
|
|
132
|
+
"vue": "^3.0.0",
|
|
123
133
|
"react": ">=16.8.0",
|
|
124
134
|
"autoprefixer": "^10.0.0",
|
|
125
135
|
"postcss": "^8.5.6",
|