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.
- package/CHANGELOG.md +30 -0
- package/CODE_OF_CONDUCT.md +21 -0
- package/CONTRIBUTING.md +28 -0
- package/README.md +455 -226
- package/demo/demo/node_modules/caniuse-db/fulldata-json/data-2.0.json +1 -0
- package/demo/index.html +16 -0
- package/demo/package.json +20 -0
- package/demo/src/App.tsx +117 -0
- package/demo/src/chaincss-barrel.ts +9 -0
- package/demo/src/main.tsx +8 -0
- package/demo/src/styles.chain.ts +300 -0
- package/demo/vite.config.ts +46 -0
- package/dist/cli/commands/build.d.ts +0 -1
- package/dist/cli/commands/cache.d.ts +1 -0
- package/dist/cli/commands/init.d.ts +6 -3
- package/dist/cli/commands/timeline.d.ts +0 -1
- package/dist/cli/commands/watch.d.ts +0 -1
- package/dist/cli/index.d.ts +0 -1
- package/dist/cli/index.js +3213 -5296
- package/dist/cli/types.d.ts +51 -20
- package/dist/cli/utils/config-loader.d.ts +0 -1
- package/dist/cli/utils/file-utils.d.ts +27 -3
- package/dist/cli/utils/logger.d.ts +0 -1
- package/dist/compiler/Chain.d.ts +215 -0
- package/dist/compiler/animations.d.ts +76 -0
- package/dist/compiler/atomic-optimizer.d.ts +47 -12
- package/dist/compiler/breakpoints.d.ts +46 -0
- package/dist/compiler/btt.d.ts +36 -60
- package/dist/compiler/cache-manager.d.ts +58 -4
- package/dist/compiler/commonProps.d.ts +0 -1
- package/dist/compiler/content-addressable-cache.d.ts +78 -0
- package/dist/compiler/helpers.d.ts +54 -0
- package/dist/compiler/index.d.ts +16 -9
- package/dist/compiler/index.js +4450 -4316
- package/dist/compiler/prefixer.d.ts +17 -1
- package/dist/compiler/shorthands.d.ts +28 -0
- package/dist/compiler/suggestions.d.ts +43 -0
- package/dist/compiler/theme-contract.d.ts +16 -27
- package/dist/compiler/token-resolver.d.ts +69 -0
- package/dist/compiler/tokens.d.ts +33 -8
- package/dist/core/auto-detector.d.ts +34 -0
- package/dist/core/common-utils.d.ts +97 -0
- package/dist/core/compiler.d.ts +63 -23
- package/dist/core/constants.d.ts +137 -36
- package/dist/core/smart-chain.d.ts +3 -0
- package/dist/core/types.d.ts +122 -15
- package/dist/core/utils.d.ts +134 -17
- package/dist/index.d.ts +52 -8
- package/dist/index.js +7090 -5578
- package/dist/plugins/vite.d.ts +7 -5
- package/dist/plugins/vite.js +2964 -25641
- package/dist/plugins/webpack.d.ts +24 -1
- package/dist/plugins/webpack.js +209 -72
- package/dist/runtime/Chain.d.ts +32 -0
- package/dist/runtime/auto-hooks.d.ts +11 -0
- package/dist/runtime/hmr.d.ts +22 -2
- package/dist/runtime/index.d.ts +3 -2
- package/dist/runtime/index.js +3648 -301
- package/dist/runtime/injector.d.ts +39 -72
- package/dist/runtime/react.d.ts +17 -12
- package/dist/runtime/svelte.d.ts +15 -0
- package/dist/runtime/types.d.ts +126 -4
- package/dist/runtime/utils.d.ts +0 -1
- package/dist/runtime/vue.d.ts +34 -14
- package/package.json +59 -66
- package/src/cli/commands/build.ts +133 -0
- package/src/cli/commands/cache.ts +371 -0
- package/src/cli/commands/init.ts +230 -0
- package/src/cli/commands/timeline.ts +435 -0
- package/src/cli/commands/watch.ts +211 -0
- package/src/cli/index.ts +226 -0
- package/src/cli/types.ts +100 -0
- package/src/cli/utils/config-loader.ts +174 -0
- package/src/cli/utils/file-utils.ts +139 -0
- package/src/cli/utils/logger.ts +74 -0
- package/src/compiler/Chain.ts +831 -0
- package/src/compiler/animations.ts +517 -0
- package/src/compiler/atomic-optimizer.ts +786 -0
- package/src/compiler/breakpoints.ts +347 -0
- package/src/compiler/btt.ts +1147 -0
- package/src/compiler/cache-manager.ts +446 -0
- package/src/compiler/commonProps.ts +18 -0
- package/src/compiler/content-addressable-cache.ts +478 -0
- package/src/compiler/helpers.ts +407 -0
- package/src/compiler/index.ts +72 -0
- package/src/compiler/prefixer.ts +720 -0
- package/src/compiler/shorthands.ts +558 -0
- package/src/compiler/suggestions.ts +436 -0
- package/src/compiler/theme-contract.ts +197 -0
- package/src/compiler/token-resolver.ts +241 -0
- package/src/compiler/tokens.ts +612 -0
- package/src/core/auto-detector.ts +187 -0
- package/src/core/common-utils.ts +423 -0
- package/src/core/compiler.ts +835 -0
- package/src/core/constants.ts +424 -0
- package/src/core/index.ts +107 -0
- package/src/core/smart-chain.ts +163 -0
- package/src/core/types.ts +257 -0
- package/src/core/utils.ts +598 -0
- package/src/index.ts +208 -0
- package/src/plugins/vite.d.ts +316 -0
- package/src/plugins/vite.ts +424 -0
- package/src/plugins/webpack.d.ts +289 -0
- package/src/plugins/webpack.ts +416 -0
- package/src/runtime/Chain.ts +242 -0
- package/src/runtime/auto-hooks.tsx +127 -0
- package/src/runtime/auto-vue.ts +72 -0
- package/src/runtime/hmr.ts +212 -0
- package/src/runtime/index.ts +82 -0
- package/src/runtime/injector.ts +273 -0
- package/src/runtime/react.tsx +269 -0
- package/src/runtime/svelte.ts +15 -0
- package/src/runtime/types.ts +256 -0
- package/src/runtime/utils.ts +128 -0
- package/src/runtime/vite-env.d.ts +120 -0
- package/src/runtime/vue.ts +231 -0
- package/tsconfig.build.json +41 -0
- package/tsconfig.json +25 -0
- package/tsconfig.runtimes.json +18 -0
- package/dist/cli/cli.cjs +0 -7
- package/dist/cli/commands/build.d.ts.map +0 -1
- package/dist/cli/commands/compile.d.ts +0 -3
- package/dist/cli/commands/compile.d.ts.map +0 -1
- package/dist/cli/commands/init.d.ts.map +0 -1
- package/dist/cli/commands/timeline.d.ts.map +0 -1
- package/dist/cli/commands/watch.d.ts.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/types.d.ts.map +0 -1
- package/dist/cli/utils/config-loader.d.ts.map +0 -1
- package/dist/cli/utils/file-utils.d.ts.map +0 -1
- package/dist/cli/utils/logger.d.ts.map +0 -1
- package/dist/compiler/atomic-optimizer.d.ts.map +0 -1
- package/dist/compiler/btt.d.ts.map +0 -1
- package/dist/compiler/cache-manager.d.ts.map +0 -1
- package/dist/compiler/commonProps.d.ts.map +0 -1
- package/dist/compiler/index.d.ts.map +0 -1
- package/dist/compiler/prefixer.d.ts.map +0 -1
- package/dist/compiler/theme-contract.d.ts.map +0 -1
- package/dist/compiler/tokens.d.ts.map +0 -1
- package/dist/compiler/types.d.ts +0 -57
- package/dist/compiler/types.d.ts.map +0 -1
- package/dist/core/compiler.d.ts.map +0 -1
- package/dist/core/constants.d.ts.map +0 -1
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/utils.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/plugins/vite.d.ts.map +0 -1
- package/dist/plugins/webpack.d.ts.map +0 -1
- package/dist/runtime/hmr.d.ts.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/injector.d.ts.map +0 -1
- package/dist/runtime/react.d.ts.map +0 -1
- package/dist/runtime/react.js +0 -324
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/utils.d.ts.map +0 -1
- package/dist/runtime/vue.d.ts.map +0 -1
- package/dist/runtime/vue.js +0 -286
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// src/cli/commands/watch.ts (with fixes for logger)
|
|
2
|
+
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import { ChainCSSCompiler } from '../../core/compiler.js';
|
|
7
|
+
import { createLogger } from '../utils/logger.js';
|
|
8
|
+
import { loadConfig } from '../utils/config-loader.js';
|
|
9
|
+
import { findInputFiles, ensureDirectory, getOutputPath } from '../utils/file-utils.js';
|
|
10
|
+
import type { BuildOptions } from '../types.js';
|
|
11
|
+
|
|
12
|
+
export interface WatchOptions extends BuildOptions {
|
|
13
|
+
debounce?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function watchCommand(options: WatchOptions): Promise<void> {
|
|
17
|
+
const logger = createLogger(options.verbose);
|
|
18
|
+
|
|
19
|
+
logger.header('ChainCSS Watch Mode');
|
|
20
|
+
|
|
21
|
+
// Load configuration
|
|
22
|
+
const config = await loadConfig(options.config);
|
|
23
|
+
const inputs = config.inputs || ['src/**/*.chain.{js,ts}', 'src/**/*.chain.{jsx,tsx}'];
|
|
24
|
+
|
|
25
|
+
// Determine output directory
|
|
26
|
+
let outputDir = 'dist/styles';
|
|
27
|
+
if (typeof config.output === 'object' && config.output.cssFile) {
|
|
28
|
+
outputDir = path.dirname(config.output.cssFile);
|
|
29
|
+
} else if (typeof config.output === 'string') {
|
|
30
|
+
outputDir = config.output;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (inputs.length === 0) {
|
|
34
|
+
logger.error('No input patterns found in configuration');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
logger.info(`Watching files matching: ${inputs.join(', ')}`);
|
|
39
|
+
|
|
40
|
+
// Find initial files
|
|
41
|
+
const initialFiles = findInputFiles(inputs);
|
|
42
|
+
logger.success(`Found ${initialFiles.length} file(s) to watch`);
|
|
43
|
+
|
|
44
|
+
// Ensure output directory exists
|
|
45
|
+
ensureDirectory(outputDir);
|
|
46
|
+
|
|
47
|
+
// Initialize compiler
|
|
48
|
+
const compiler = new ChainCSSCompiler({
|
|
49
|
+
tokens: config.tokens,
|
|
50
|
+
atomic: {
|
|
51
|
+
enabled: config.atomic?.enabled !== false,
|
|
52
|
+
threshold: config.atomic?.threshold || 2,
|
|
53
|
+
naming: config.atomic?.naming || (process.env.NODE_ENV === 'production' ? 'hash' : 'readable'),
|
|
54
|
+
minify: config.atomic?.minify !== false,
|
|
55
|
+
mode: config.atomic?.mode || 'hybrid',
|
|
56
|
+
verbose: options.verbose || config.verbose || false
|
|
57
|
+
},
|
|
58
|
+
prefixer: {
|
|
59
|
+
enabled: config.prefixer?.enabled !== false,
|
|
60
|
+
browsers: config.prefixer?.browsers
|
|
61
|
+
},
|
|
62
|
+
output: {
|
|
63
|
+
minify: config.output?.minify !== false
|
|
64
|
+
},
|
|
65
|
+
verbose: options.verbose || config.verbose || false,
|
|
66
|
+
breakpoints: config.breakpoints,
|
|
67
|
+
debug: config.debug || false,
|
|
68
|
+
timeline: config.timeline || false
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Initial compilation
|
|
72
|
+
logger.step('Initial compilation...');
|
|
73
|
+
let compiledCount = 0;
|
|
74
|
+
|
|
75
|
+
for (let i = 0; i < initialFiles.length; i++) {
|
|
76
|
+
const file = initialFiles[i];
|
|
77
|
+
const relativePath = path.relative(process.cwd(), file);
|
|
78
|
+
logger.progress(i + 1, initialFiles.length, `Compiling ${relativePath}...`);
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
await compiler.compile(file, outputDir);
|
|
82
|
+
compiledCount++;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
logger.error(`Failed to compile ${relativePath}: ${(error as Error).message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
logger.progress(initialFiles.length, initialFiles.length, 'Initial build complete!');
|
|
89
|
+
logger.success(`Compiled ${compiledCount} file(s)`);
|
|
90
|
+
|
|
91
|
+
// Setup file watcher
|
|
92
|
+
logger.divider();
|
|
93
|
+
logger.info(`👀 Watching for changes... (press Ctrl+C to stop)\n`);
|
|
94
|
+
|
|
95
|
+
const chokidar = await import('chokidar');
|
|
96
|
+
const debounceDelay = options.debounce || 100;
|
|
97
|
+
|
|
98
|
+
// Create watcher
|
|
99
|
+
const watcher = chokidar.watch(inputs, {
|
|
100
|
+
ignored: ['**/node_modules/**', '**/dist/**', '**/.chaincss-cache/**', '**/*.css', '**/*.d.ts'],
|
|
101
|
+
persistent: true,
|
|
102
|
+
ignoreInitial: true
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Debounce function to avoid multiple rapid recompilations
|
|
106
|
+
let debounceTimer: NodeJS.Timeout | null = null;
|
|
107
|
+
let pendingFiles = new Set<string>();
|
|
108
|
+
|
|
109
|
+
function scheduleRecompile() {
|
|
110
|
+
if (debounceTimer) {
|
|
111
|
+
clearTimeout(debounceTimer);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
debounceTimer = setTimeout(async () => {
|
|
115
|
+
const filesToCompile = Array.from(pendingFiles);
|
|
116
|
+
pendingFiles.clear();
|
|
117
|
+
|
|
118
|
+
logger.divider();
|
|
119
|
+
logger.info(`📦 Recompiling ${filesToCompile.length} changed file(s)...`);
|
|
120
|
+
|
|
121
|
+
let successCount = 0;
|
|
122
|
+
let failCount = 0;
|
|
123
|
+
|
|
124
|
+
for (const file of filesToCompile) {
|
|
125
|
+
const relativePath = path.relative(process.cwd(), file);
|
|
126
|
+
try {
|
|
127
|
+
await compiler.compile(file, outputDir);
|
|
128
|
+
logger.success(` ✓ ${relativePath}`);
|
|
129
|
+
successCount++;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
logger.error(` ✗ ${relativePath}: ${(error as Error).message}`);
|
|
132
|
+
failCount++;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (successCount > 0) {
|
|
137
|
+
logger.success(`✅ Recompiled ${successCount} file(s) successfully`);
|
|
138
|
+
}
|
|
139
|
+
if (failCount > 0) {
|
|
140
|
+
logger.error(`❌ Failed to recompile ${failCount} file(s)`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
debounceTimer = null;
|
|
144
|
+
}, debounceDelay);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Handle file changes
|
|
148
|
+
watcher.on('change', (filePath: string) => {
|
|
149
|
+
const ext = path.extname(filePath);
|
|
150
|
+
if (ext === '.js' || ext === '.ts' || ext === '.jsx' || ext === '.tsx') {
|
|
151
|
+
const relativePath = path.relative(process.cwd(), filePath);
|
|
152
|
+
if (options.verbose) {
|
|
153
|
+
logger.info(`File changed: ${relativePath}`);
|
|
154
|
+
}
|
|
155
|
+
pendingFiles.add(filePath);
|
|
156
|
+
scheduleRecompile();
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Handle new files
|
|
161
|
+
watcher.on('add', (filePath: string) => {
|
|
162
|
+
const ext = path.extname(filePath);
|
|
163
|
+
if (ext === '.js' || ext === '.ts' || ext === '.jsx' || ext === '.tsx') {
|
|
164
|
+
const relativePath = path.relative(process.cwd(), filePath);
|
|
165
|
+
logger.info(`📄 New file detected: ${relativePath}`);
|
|
166
|
+
pendingFiles.add(filePath);
|
|
167
|
+
scheduleRecompile();
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Handle deleted files
|
|
172
|
+
watcher.on('unlink', (filePath: string) => {
|
|
173
|
+
const ext = path.extname(filePath);
|
|
174
|
+
if (ext === '.js' || ext === '.ts' || ext === '.jsx' || ext === '.tsx') {
|
|
175
|
+
const relativePath = path.relative(process.cwd(), filePath);
|
|
176
|
+
logger.warn(`🗑️ File deleted: ${relativePath}`);
|
|
177
|
+
|
|
178
|
+
// Remove corresponding output files
|
|
179
|
+
const baseName = path.basename(filePath, ext);
|
|
180
|
+
const outputBase = path.join(outputDir, baseName);
|
|
181
|
+
const cssFile = `${outputBase}.css`;
|
|
182
|
+
const classFile = `${outputBase}.class.js`;
|
|
183
|
+
|
|
184
|
+
if (fs.existsSync(cssFile)) fs.unlinkSync(cssFile);
|
|
185
|
+
if (fs.existsSync(classFile)) fs.unlinkSync(classFile);
|
|
186
|
+
|
|
187
|
+
logger.info(` Removed output files for ${relativePath}`);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Handle watcher errors
|
|
192
|
+
watcher.on('error', (error: unknown) => {
|
|
193
|
+
logger.error(`Watcher error: ${error instanceof Error ? error.message : String(error)}`);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Handle process termination
|
|
197
|
+
const cleanup = () => {
|
|
198
|
+
logger.info('\n👋 Shutting down watcher...');
|
|
199
|
+
watcher.close();
|
|
200
|
+
if (debounceTimer) {
|
|
201
|
+
clearTimeout(debounceTimer);
|
|
202
|
+
}
|
|
203
|
+
process.exit(0);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
process.on('SIGINT', cleanup);
|
|
207
|
+
process.on('SIGTERM', cleanup);
|
|
208
|
+
|
|
209
|
+
// Keep the process alive
|
|
210
|
+
await new Promise(() => {});
|
|
211
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// src/cli/index.ts
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { glob } from 'glob';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { loadConfig } from './utils/config-loader.js';
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Path Resolution
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
|
|
17
|
+
const findPackageJson = (startDir: string): string => {
|
|
18
|
+
let currentDir = startDir;
|
|
19
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
20
|
+
const pkgPath = path.join(currentDir, 'package.json');
|
|
21
|
+
if (existsSync(pkgPath)) return pkgPath;
|
|
22
|
+
currentDir = path.dirname(currentDir);
|
|
23
|
+
}
|
|
24
|
+
throw new Error('Could not find package.json');
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const packageJsonPath = findPackageJson(__dirname);
|
|
28
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
29
|
+
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// CLI Setup
|
|
32
|
+
// ============================================================================
|
|
33
|
+
|
|
34
|
+
const program = new Command();
|
|
35
|
+
|
|
36
|
+
program
|
|
37
|
+
.name('chaincss')
|
|
38
|
+
.description('ChainCSS - Zero-runtime CSS-in-JS Compiler')
|
|
39
|
+
.version(packageJson.version, '-V, --version')
|
|
40
|
+
.usage('[command] [options]')
|
|
41
|
+
.helpOption('-h, --help', 'Display help for command');
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Dynamic Import for Compiler (avoids bundling top-level await)
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
const getCompiler = async (config?: any) => {
|
|
48
|
+
const { ChainCSSCompiler } = await import('../core/compiler.js');
|
|
49
|
+
return new ChainCSSCompiler(config);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// Error Handling
|
|
54
|
+
// ============================================================================
|
|
55
|
+
|
|
56
|
+
const handleError = (error: unknown, command: string): void => {
|
|
57
|
+
console.error(chalk.red(`\n❌ Error running "${command}":`));
|
|
58
|
+
if (error instanceof Error) {
|
|
59
|
+
console.error(chalk.red(` ${error.message}`));
|
|
60
|
+
if (process.env.DEBUG) console.error(error.stack);
|
|
61
|
+
} else {
|
|
62
|
+
console.error(chalk.red(` ${String(error)}`));
|
|
63
|
+
}
|
|
64
|
+
process.exit(1);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Init Command
|
|
69
|
+
// ============================================================================
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// Init Command (Generates the NEW Object-based config)
|
|
73
|
+
// ============================================================================
|
|
74
|
+
program
|
|
75
|
+
.command('init')
|
|
76
|
+
.description('Initialize ChainCSS configuration file')
|
|
77
|
+
.option('-f, --force', 'Overwrite existing config file')
|
|
78
|
+
.action(async (options) => {
|
|
79
|
+
try {
|
|
80
|
+
const configPath = 'chaincss.config.js';
|
|
81
|
+
if (existsSync(configPath) && !options.force) {
|
|
82
|
+
console.log(chalk.yellow('Config file already exists. Use --force to overwrite.'));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const config = `export default {
|
|
87
|
+
inputs: ['src/**/*.chain.{js,ts}', 'src/**/*.tsx'],
|
|
88
|
+
output: {
|
|
89
|
+
cssFile: 'global.css',
|
|
90
|
+
classMapFile: 'style',
|
|
91
|
+
minify: false,
|
|
92
|
+
generateGlobalCSS: true
|
|
93
|
+
},
|
|
94
|
+
atomic: {
|
|
95
|
+
enabled: true,
|
|
96
|
+
naming: 'hash',
|
|
97
|
+
mode: 'hybrid'
|
|
98
|
+
},
|
|
99
|
+
verbose: true
|
|
100
|
+
};`;
|
|
101
|
+
writeFileSync(configPath, config);
|
|
102
|
+
console.log(chalk.green('✓ Created chaincss.config.js with Object-based output.'));
|
|
103
|
+
} catch (error) {
|
|
104
|
+
handleError(error, 'init');
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Build Command
|
|
110
|
+
// ============================================================================
|
|
111
|
+
program
|
|
112
|
+
.command('build')
|
|
113
|
+
.option('-v, --verbose', 'Verbose output')
|
|
114
|
+
.action(async (opts) => {
|
|
115
|
+
try {
|
|
116
|
+
const config = await loadConfig();
|
|
117
|
+
const compiler = await getCompiler(config);
|
|
118
|
+
|
|
119
|
+
console.log(chalk.blue('🚀 Starting ChainCSS Build...'));
|
|
120
|
+
|
|
121
|
+
const patterns = config.inputs || ['src/**/*.chain.{js,ts}', 'src/**/*.tsx'];
|
|
122
|
+
const files = await glob(patterns);
|
|
123
|
+
|
|
124
|
+
// The compiler handles the logic we wrote earlier:
|
|
125
|
+
// 1. Component CSS in src/components/<Name>/style/
|
|
126
|
+
// 2. Global CSS in public/
|
|
127
|
+
// 3. Manifest in src/manifest/
|
|
128
|
+
await compiler.compileComponents(files);
|
|
129
|
+
|
|
130
|
+
const stats = compiler.getStats();
|
|
131
|
+
console.log(chalk.green(`\n✅ Build Complete!`));
|
|
132
|
+
console.log(chalk.cyan(`📊 Atomic Rules: ${stats.atomicStyles}`));
|
|
133
|
+
} catch (error) {
|
|
134
|
+
handleError(error, 'build');
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// Watch Command
|
|
140
|
+
// ============================================================================
|
|
141
|
+
program
|
|
142
|
+
.command('watch')
|
|
143
|
+
.description('Watch and automatically recompile styles')
|
|
144
|
+
.action(async () => {
|
|
145
|
+
try {
|
|
146
|
+
const config = await loadConfig();
|
|
147
|
+
const compiler = await getCompiler(config);
|
|
148
|
+
const chokidar = await import('chokidar');
|
|
149
|
+
|
|
150
|
+
const patterns = config.inputs || ['src/**/*.chain.{js,ts}', 'src/**/*.tsx'];
|
|
151
|
+
const watcher = chokidar.watch(patterns, { ignored: '**/node_modules/**' });
|
|
152
|
+
|
|
153
|
+
console.log(chalk.blue('📡 Watching for changes...'));
|
|
154
|
+
|
|
155
|
+
watcher.on('change', async (filePath) => {
|
|
156
|
+
console.log(chalk.yellow(`\r🔄 Change detected: ${path.basename(filePath)}`));
|
|
157
|
+
const files = await glob(patterns);
|
|
158
|
+
await compiler.compileComponents(files);
|
|
159
|
+
});
|
|
160
|
+
} catch (error) {
|
|
161
|
+
handleError(error, 'watch');
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// ============================================================================
|
|
166
|
+
// Timeline
|
|
167
|
+
// ============================================================================
|
|
168
|
+
|
|
169
|
+
program
|
|
170
|
+
.command('timeline')
|
|
171
|
+
.description('Manage style timeline')
|
|
172
|
+
.argument('<action>', 'Action: list, diff, export, clear')
|
|
173
|
+
.option('-s, --snapshot1 <id>', 'First snapshot ID or selector for diff')
|
|
174
|
+
.option('--snapshot2 <id>', 'Second snapshot ID or selector for diff')
|
|
175
|
+
.option('-o, --output <path>', 'Output file for export')
|
|
176
|
+
.action(async (action, options) => {
|
|
177
|
+
const { timelineCommand } = await import('./commands/timeline.js');
|
|
178
|
+
await timelineCommand(action, options);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// Help and Examples
|
|
183
|
+
// ============================================================================
|
|
184
|
+
|
|
185
|
+
program.on('--help', () => {
|
|
186
|
+
console.log('');
|
|
187
|
+
console.log(chalk.cyan('Examples:'));
|
|
188
|
+
console.log(chalk.gray(' # Initialize a new project'));
|
|
189
|
+
console.log(' $ chaincss init');
|
|
190
|
+
console.log('');
|
|
191
|
+
console.log(chalk.gray(' # Build all styles'));
|
|
192
|
+
console.log(' $ chaincss build -c "src/**/*.chain.js"');
|
|
193
|
+
console.log('');
|
|
194
|
+
console.log(chalk.gray(' # Watch for changes'));
|
|
195
|
+
console.log(' $ chaincss watch -c "src/**/*.chain.js"');
|
|
196
|
+
console.log('');
|
|
197
|
+
console.log(chalk.cyan('Documentation:'));
|
|
198
|
+
console.log(' https://github.com/melcanz08/chaincss');
|
|
199
|
+
console.log('');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// Cache
|
|
204
|
+
// ============================================================================
|
|
205
|
+
|
|
206
|
+
program
|
|
207
|
+
.command('cache')
|
|
208
|
+
.description('Manage persistent cache')
|
|
209
|
+
.argument('<action>', 'Action: clear, stats, prune')
|
|
210
|
+
.option('-v, --verbose', 'Verbose output')
|
|
211
|
+
.action(async (action, options) => {
|
|
212
|
+
const { cacheCommand } = await import('./commands/cache.js');
|
|
213
|
+
await cacheCommand(action, options);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// ============================================================================
|
|
217
|
+
// Parse Arguments
|
|
218
|
+
// ============================================================================
|
|
219
|
+
|
|
220
|
+
if (process.argv.length === 2) {
|
|
221
|
+
program.outputHelp();
|
|
222
|
+
process.exit(0);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
program.parse(process.argv);
|
|
226
|
+
|
package/src/cli/types.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// src/cli/types.ts
|
|
2
|
+
|
|
3
|
+
import type { ChainCSSConfig as CoreChainCSSConfig } from '../core/types.js';
|
|
4
|
+
|
|
5
|
+
// Re-export core config
|
|
6
|
+
export type ChainCSSConfig = CoreChainCSSConfig;
|
|
7
|
+
|
|
8
|
+
// CLI-specific options
|
|
9
|
+
export interface CLIOptions {
|
|
10
|
+
input?: string;
|
|
11
|
+
output?: string;
|
|
12
|
+
watch?: boolean;
|
|
13
|
+
atomic?: boolean;
|
|
14
|
+
minify?: boolean;
|
|
15
|
+
prefix?: boolean;
|
|
16
|
+
sourceMap?: boolean;
|
|
17
|
+
verbose?: boolean;
|
|
18
|
+
config?: string;
|
|
19
|
+
help?: boolean;
|
|
20
|
+
version?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CompileOptions {
|
|
24
|
+
input: string;
|
|
25
|
+
output: string;
|
|
26
|
+
watch?: boolean;
|
|
27
|
+
atomic?: boolean;
|
|
28
|
+
minify?: boolean;
|
|
29
|
+
prefix?: boolean;
|
|
30
|
+
sourceMap?: boolean;
|
|
31
|
+
verbose?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface BuildOptions {
|
|
35
|
+
config?: string;
|
|
36
|
+
watch?: boolean;
|
|
37
|
+
verbose?: boolean;
|
|
38
|
+
atomic?: boolean;
|
|
39
|
+
minify?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface WatchOptions {
|
|
43
|
+
config?: string;
|
|
44
|
+
verbose?: boolean;
|
|
45
|
+
atomic?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface CacheOptions {
|
|
49
|
+
action: 'clear' | 'stats' | 'prune' | 'list' | 'inspect' | 'delete' | 'validate' | 'backup';
|
|
50
|
+
key?: string;
|
|
51
|
+
force?: boolean;
|
|
52
|
+
maxAge?: number;
|
|
53
|
+
maxSize?: number;
|
|
54
|
+
output?: string;
|
|
55
|
+
verbose?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface TimelineOptions {
|
|
59
|
+
action: 'list' | 'diff' | 'changes' | 'stats' | 'export' | 'clear' | 'watch';
|
|
60
|
+
snapshot1?: string;
|
|
61
|
+
snapshot2?: string;
|
|
62
|
+
output?: string;
|
|
63
|
+
verbose?: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface InitOptions {
|
|
67
|
+
force?: boolean;
|
|
68
|
+
template?: 'full' | 'minimal';
|
|
69
|
+
typescript?: boolean;
|
|
70
|
+
framework?: 'react' | 'vue' | 'svelte' | 'solid';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Utility type for command handlers
|
|
74
|
+
export type CommandHandler<T = any> = (options: T) => Promise<void> | void;
|
|
75
|
+
|
|
76
|
+
// CLI command definition
|
|
77
|
+
export interface CLICommand {
|
|
78
|
+
name: string;
|
|
79
|
+
description: string;
|
|
80
|
+
options?: Array<{
|
|
81
|
+
flags: string;
|
|
82
|
+
description: string;
|
|
83
|
+
defaultValue?: any;
|
|
84
|
+
}>;
|
|
85
|
+
handler: CommandHandler;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Build result type
|
|
89
|
+
export interface BuildResult {
|
|
90
|
+
success: boolean;
|
|
91
|
+
compiledFiles: number;
|
|
92
|
+
duration: number;
|
|
93
|
+
errors: Error[];
|
|
94
|
+
warnings: string[];
|
|
95
|
+
stats?: {
|
|
96
|
+
totalStyles: number;
|
|
97
|
+
atomicStyles: number;
|
|
98
|
+
cssSize: number;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// src/cli/utils/config-loader.ts
|
|
2
|
+
/**
|
|
3
|
+
* ChainCSS Configuration Loader
|
|
4
|
+
* @module config-loader
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { createLogger } from './logger.js';
|
|
10
|
+
import type { ChainCSSConfig } from '../types.js';
|
|
11
|
+
import { DEFAULT_CONFIG as CORE_DEFAULTS } from '../../core/constants.js';
|
|
12
|
+
|
|
13
|
+
export async function loadConfig(configPath?: string): Promise<ChainCSSConfig> {
|
|
14
|
+
const logger = createLogger(false);
|
|
15
|
+
|
|
16
|
+
const possiblePaths = configPath
|
|
17
|
+
? [configPath]
|
|
18
|
+
: [
|
|
19
|
+
path.join(process.cwd(), 'chaincss.config.js'),
|
|
20
|
+
path.join(process.cwd(), 'chaincss.config.ts'),
|
|
21
|
+
path.join(process.cwd(), '.chaincssrc.js'),
|
|
22
|
+
path.join(process.cwd(), 'chaincss.json')
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
for (const configFile of possiblePaths) {
|
|
26
|
+
if (fs.existsSync(configFile)) {
|
|
27
|
+
try {
|
|
28
|
+
logger.debug(`Loading config from ${configFile}`);
|
|
29
|
+
|
|
30
|
+
const configModule = await import(`file://${configFile}`);
|
|
31
|
+
const userConfig = configModule.default || configModule;
|
|
32
|
+
|
|
33
|
+
// 2. MERGE WITH CORE DEFAULTS INSTEAD OF LOCAL DEFAULTS
|
|
34
|
+
return mergeConfig(CORE_DEFAULTS, userConfig);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
logger.warn(`Failed to load config from ${configFile}:`, error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return CORE_DEFAULTS;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function mergeConfig(defaults: any, user: any): ChainCSSConfig {
|
|
45
|
+
return {
|
|
46
|
+
...defaults,
|
|
47
|
+
...user,
|
|
48
|
+
tokens: {
|
|
49
|
+
...defaults.tokens,
|
|
50
|
+
...user.tokens
|
|
51
|
+
},
|
|
52
|
+
atomic: {
|
|
53
|
+
...defaults.atomic,
|
|
54
|
+
...user.atomic
|
|
55
|
+
},
|
|
56
|
+
prefixer: {
|
|
57
|
+
...defaults.prefixer,
|
|
58
|
+
...user.prefixer
|
|
59
|
+
},
|
|
60
|
+
output: {
|
|
61
|
+
...defaults.output,
|
|
62
|
+
...user.output
|
|
63
|
+
},
|
|
64
|
+
breakpoints: {
|
|
65
|
+
...defaults.breakpoints,
|
|
66
|
+
...user.breakpoints
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function saveConfigTemplate(outputPath: string = 'chaincss.config.js', full: boolean = false): void {
|
|
72
|
+
let template = '';
|
|
73
|
+
|
|
74
|
+
if (full) {
|
|
75
|
+
template = `/**
|
|
76
|
+
* ChainCSS Configuration
|
|
77
|
+
* @type {import('chaincss').ChainCSSConfig}
|
|
78
|
+
*
|
|
79
|
+
* Documentation: https://chaincss.dev/docs/configuration
|
|
80
|
+
*/
|
|
81
|
+
export default {
|
|
82
|
+
// ========== INPUT/OUTPUT ==========
|
|
83
|
+
inputs: ['src/**/*.chain.js', 'src/**/*.chain.ts'],
|
|
84
|
+
output: 'dist/styles',
|
|
85
|
+
|
|
86
|
+
// ========== TOKENS ==========
|
|
87
|
+
tokens: {
|
|
88
|
+
enabled: true,
|
|
89
|
+
prefix: 'chain'
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
// ========== ATOMIC CSS ==========
|
|
93
|
+
atomic: {
|
|
94
|
+
enabled: true,
|
|
95
|
+
threshold: 3,
|
|
96
|
+
naming: 'readable',
|
|
97
|
+
minify: true,
|
|
98
|
+
mode: 'hybrid',
|
|
99
|
+
outputStrategy: 'component-first',
|
|
100
|
+
verbose: false
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
// ========== AUTOPREFIXER ==========
|
|
104
|
+
prefixer: {
|
|
105
|
+
enabled: true,
|
|
106
|
+
mode: 'auto',
|
|
107
|
+
browsers: ['> 0.5%', 'last 2 versions', 'not dead'],
|
|
108
|
+
sourceMap: true
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
// ========== OUTPUT OPTIONS ==========
|
|
112
|
+
output: {
|
|
113
|
+
cssFile: 'styles.css',
|
|
114
|
+
classMapFile: 'class-map.json',
|
|
115
|
+
typesFile: 'classes.d.ts',
|
|
116
|
+
minify: true,
|
|
117
|
+
generateGlobalCSS: true
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// ========== RESPONSIVE BREAKPOINTS ==========
|
|
121
|
+
breakpoints: {
|
|
122
|
+
sm: '(max-width: 640px)',
|
|
123
|
+
md: '(min-width: 641px) and (max-width: 768px)',
|
|
124
|
+
lg: '(min-width: 769px) and (max-width: 1024px)',
|
|
125
|
+
xl: '(min-width: 1025px)',
|
|
126
|
+
'2xl': '(min-width: 1280px)',
|
|
127
|
+
mobile: '(max-width: 768px)',
|
|
128
|
+
tablet: '(min-width: 769px) and (max-width: 1024px)',
|
|
129
|
+
desktop: '(min-width: 1025px)'
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// ========== DEBUG & TIMELINE ==========
|
|
133
|
+
debug: false,
|
|
134
|
+
sourceComments: true,
|
|
135
|
+
timeline: false,
|
|
136
|
+
|
|
137
|
+
// ========== FRAMEWORK ==========
|
|
138
|
+
framework: 'auto',
|
|
139
|
+
|
|
140
|
+
// ========== GENERAL ==========
|
|
141
|
+
namespace: 'chain',
|
|
142
|
+
watch: false,
|
|
143
|
+
verbose: false
|
|
144
|
+
};
|
|
145
|
+
`;
|
|
146
|
+
} else {
|
|
147
|
+
template = `/**
|
|
148
|
+
* ChainCSS Configuration
|
|
149
|
+
* @type {import('chaincss').ChainCSSConfig}
|
|
150
|
+
*/
|
|
151
|
+
export default {
|
|
152
|
+
inputs: ['src/**/*.chain.js', 'src/**/*.chain.ts'],
|
|
153
|
+
output: 'dist/styles',
|
|
154
|
+
atomic: {
|
|
155
|
+
enabled: true,
|
|
156
|
+
naming: 'readable',
|
|
157
|
+
minify: true
|
|
158
|
+
},
|
|
159
|
+
prefixer: {
|
|
160
|
+
enabled: true
|
|
161
|
+
},
|
|
162
|
+
breakpoints: {
|
|
163
|
+
mobile: '(max-width: 768px)',
|
|
164
|
+
tablet: '(min-width: 769px) and (max-width: 1024px)',
|
|
165
|
+
desktop: '(min-width: 1025px)'
|
|
166
|
+
},
|
|
167
|
+
debug: false,
|
|
168
|
+
verbose: false
|
|
169
|
+
};
|
|
170
|
+
`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fs.writeFileSync(outputPath, template, 'utf8');
|
|
174
|
+
}
|