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.
@@ -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
+ }