apexcss-cli 0.1.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/README.md +284 -0
- package/bin/apexcss.js +23 -0
- package/cli/commands/build.js +376 -0
- package/cli/commands/doctor.js +286 -0
- package/cli/commands/init.js +339 -0
- package/cli/commands/watch.js +150 -0
- package/cli/index.js +129 -0
- package/cli/utils/config-builder.js +1934 -0
- package/cli/utils/config-loader.js +963 -0
- package/cli/utils/framework-detector.js +189 -0
- package/cli/utils/logger.js +121 -0
- package/package.json +72 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watch command - Watch for config changes and rebuild automatically
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { watchFile } from 'node:fs';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import { buildCommand } from './build.js';
|
|
8
|
+
import { loadConfig } from '../utils/config-loader.js';
|
|
9
|
+
import { logger } from '../utils/logger.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build state manager for watch mode
|
|
13
|
+
*/
|
|
14
|
+
export class WatchBuildState {
|
|
15
|
+
isBuilding = false;
|
|
16
|
+
pendingBuild = false;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if a build can start
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
canStartBuild() {
|
|
23
|
+
if (this.isBuilding) {
|
|
24
|
+
this.pendingBuild = true;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Mark build as started
|
|
32
|
+
*/
|
|
33
|
+
startBuild() {
|
|
34
|
+
this.isBuilding = true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Mark build as completed
|
|
39
|
+
* @returns {boolean} - True if another build is pending
|
|
40
|
+
*/
|
|
41
|
+
finishBuild() {
|
|
42
|
+
this.isBuilding = false;
|
|
43
|
+
const hasPending = this.pendingBuild;
|
|
44
|
+
this.pendingBuild = false;
|
|
45
|
+
return hasPending;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Perform a single build in watch mode
|
|
51
|
+
* @param {object} options - Build options
|
|
52
|
+
* @param {WatchBuildState} state - Build state manager
|
|
53
|
+
* @param {object} [deps] - Dependencies (for testing)
|
|
54
|
+
* @returns {Promise<boolean>} - True if build succeeded
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* Perform a single build in watch mode
|
|
58
|
+
* @param {object} options - Build options
|
|
59
|
+
* @param {WatchBuildState} state - Build state manager
|
|
60
|
+
* @param {object} [deps] - Dependencies (for testing)
|
|
61
|
+
* @returns {Promise<boolean>} - True if build succeeded
|
|
62
|
+
*/
|
|
63
|
+
export async function performWatchBuild(options, state, deps) {
|
|
64
|
+
const dependencies = deps || { loadConfig, buildCommand, logger };
|
|
65
|
+
if (!state.canStartBuild()) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
state.startBuild();
|
|
70
|
+
dependencies.logger.info('Config changed, rebuilding...');
|
|
71
|
+
dependencies.logger.newline();
|
|
72
|
+
|
|
73
|
+
let success = false;
|
|
74
|
+
try {
|
|
75
|
+
// Reload config to validate it
|
|
76
|
+
dependencies.loadConfig(options.configPath);
|
|
77
|
+
|
|
78
|
+
// Build
|
|
79
|
+
await dependencies.buildCommand(options);
|
|
80
|
+
success = true;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
dependencies.logger.error(`Build failed: ${error.message}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
dependencies.logger.newline();
|
|
86
|
+
dependencies.logger.info('Waiting for changes... (Press Ctrl+C to stop)');
|
|
87
|
+
dependencies.logger.newline();
|
|
88
|
+
|
|
89
|
+
const hasPending = state.finishBuild();
|
|
90
|
+
|
|
91
|
+
// If another change occurred during build, rebuild
|
|
92
|
+
if (hasPending) {
|
|
93
|
+
return performWatchBuild(options, state, dependencies);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return success;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Watch for changes and rebuild
|
|
101
|
+
* @param {object} options - Command options
|
|
102
|
+
*/
|
|
103
|
+
export async function watchCommand(options) {
|
|
104
|
+
const cwd = process.cwd();
|
|
105
|
+
const configPath = resolve(cwd, options.configPath);
|
|
106
|
+
|
|
107
|
+
logger.header('ApexCSS Watch Mode');
|
|
108
|
+
logger.newline();
|
|
109
|
+
logger.info(`Watching: ${logger.path(options.configPath)}`);
|
|
110
|
+
logger.info(`Output: ${logger.path(options.outputDir)}`);
|
|
111
|
+
logger.newline();
|
|
112
|
+
|
|
113
|
+
// Perform initial build
|
|
114
|
+
try {
|
|
115
|
+
await buildCommand(options);
|
|
116
|
+
logger.newline();
|
|
117
|
+
logger.info('Waiting for changes... (Press Ctrl+C to stop)');
|
|
118
|
+
logger.newline();
|
|
119
|
+
} catch (error) {
|
|
120
|
+
logger.error(`Initial build failed: ${error.message}`);
|
|
121
|
+
logger.info('Continuing to watch for changes...');
|
|
122
|
+
logger.newline();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Watch the config file
|
|
126
|
+
const state = new WatchBuildState();
|
|
127
|
+
|
|
128
|
+
// Use fs.watchFile for polling (works across all platforms)
|
|
129
|
+
watchFile(configPath, { interval: 500 }, async (curr, prev) => {
|
|
130
|
+
if (curr.mtime !== prev.mtime) {
|
|
131
|
+
await performWatchBuild(options, state);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Keep the process alive
|
|
136
|
+
process.stdin.resume();
|
|
137
|
+
|
|
138
|
+
// Handle graceful shutdown
|
|
139
|
+
process.on('SIGINT', () => {
|
|
140
|
+
logger.newline();
|
|
141
|
+
logger.info('Stopping watch mode...');
|
|
142
|
+
process.exit(0);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
process.on('SIGTERM', () => {
|
|
146
|
+
logger.newline();
|
|
147
|
+
logger.info('Stopping watch mode...');
|
|
148
|
+
process.exit(0);
|
|
149
|
+
});
|
|
150
|
+
}
|
package/cli/index.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { initCommand } from './commands/init.js';
|
|
7
|
+
import { buildCommand } from './commands/build.js';
|
|
8
|
+
import { watchCommand } from './commands/watch.js';
|
|
9
|
+
import { doctorCommand } from './commands/doctor.js';
|
|
10
|
+
import { logger } from './utils/logger.js';
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
|
|
15
|
+
// Read package.json for version
|
|
16
|
+
let version = '0.0.0';
|
|
17
|
+
const packageJsonPath = join(__dirname, '..', 'package.json');
|
|
18
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
19
|
+
version = packageJson.version;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Main CLI entry point
|
|
23
|
+
* @param {string[]} args - Command line arguments
|
|
24
|
+
*/
|
|
25
|
+
export function cli(args) {
|
|
26
|
+
const program = new Command();
|
|
27
|
+
|
|
28
|
+
program
|
|
29
|
+
.name('apexcss')
|
|
30
|
+
.description('ApexCSS CLI - Build and customize your CSS framework')
|
|
31
|
+
.version(version, '-v, --version');
|
|
32
|
+
|
|
33
|
+
// Global options
|
|
34
|
+
program
|
|
35
|
+
.option('-c, --config <path>', 'path to config file', './apex.config.js')
|
|
36
|
+
.option('-o, --output <dir>', 'output directory', './dist/')
|
|
37
|
+
.option('--minify', 'minify output CSS', false)
|
|
38
|
+
.option('--sourcemap', 'generate source maps', false);
|
|
39
|
+
|
|
40
|
+
// Init command
|
|
41
|
+
program
|
|
42
|
+
.command('init')
|
|
43
|
+
.description('Initialize ApexCSS configuration in your project')
|
|
44
|
+
.option('-f, --framework <name>', 'specify framework (react, vue, angular, svelte, astro, next, nuxt, vanilla, astro)')
|
|
45
|
+
.option('--no-interactive', 'skip interactive prompts')
|
|
46
|
+
.option('--no-import', 'skip adding imports to entry files')
|
|
47
|
+
.action(async (options) => {
|
|
48
|
+
try {
|
|
49
|
+
await initCommand({
|
|
50
|
+
configPath: program.opts().config,
|
|
51
|
+
outputDir: program.opts().output,
|
|
52
|
+
framework: options.framework,
|
|
53
|
+
interactive: options.interactive,
|
|
54
|
+
addImport: options.import
|
|
55
|
+
});
|
|
56
|
+
} catch (error) {
|
|
57
|
+
logger.error(error.message);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Build command
|
|
63
|
+
program
|
|
64
|
+
.command('build')
|
|
65
|
+
.description('Build custom CSS from configuration')
|
|
66
|
+
.option('--format <format>', 'output format (css, scss, both)', 'css')
|
|
67
|
+
.option('-l, --layer <layers>', 'build specific layers (base, utilities, themes, all). Comma-separated for multiple', 'all')
|
|
68
|
+
.action(async (options) => {
|
|
69
|
+
try {
|
|
70
|
+
await buildCommand({
|
|
71
|
+
configPath: program.opts().config,
|
|
72
|
+
outputDir: program.opts().output,
|
|
73
|
+
minify: program.opts().minify,
|
|
74
|
+
sourcemap: program.opts().sourcemap,
|
|
75
|
+
format: options.format,
|
|
76
|
+
layers: options.layer
|
|
77
|
+
});
|
|
78
|
+
} catch (error) {
|
|
79
|
+
logger.error(error.message);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Watch command
|
|
85
|
+
program
|
|
86
|
+
.command('watch')
|
|
87
|
+
.description('Watch for config changes and rebuild automatically')
|
|
88
|
+
.action(async () => {
|
|
89
|
+
try {
|
|
90
|
+
await watchCommand({
|
|
91
|
+
configPath: program.opts().config,
|
|
92
|
+
outputDir: program.opts().output,
|
|
93
|
+
minify: program.opts().minify,
|
|
94
|
+
sourcemap: program.opts().sourcemap
|
|
95
|
+
});
|
|
96
|
+
} catch (error) {
|
|
97
|
+
logger.error(error.message);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Doctor command
|
|
103
|
+
program
|
|
104
|
+
.command('doctor')
|
|
105
|
+
.description('Check system setup and diagnose issues')
|
|
106
|
+
.action(async () => {
|
|
107
|
+
try {
|
|
108
|
+
await doctorCommand();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logger.error(error.message);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Handle unknown commands
|
|
116
|
+
program.on('command:*', (operands) => {
|
|
117
|
+
logger.error(`Unknown command: ${operands[0]}`);
|
|
118
|
+
logger.info('Run "apexcss --help" for available commands');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Parse arguments
|
|
123
|
+
program.parse(args);
|
|
124
|
+
|
|
125
|
+
// Show help if no command provided
|
|
126
|
+
if (!program.args.length) {
|
|
127
|
+
program.help();
|
|
128
|
+
}
|
|
129
|
+
}
|