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
package/README.md
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# @apexcss/cli
|
|
2
|
+
|
|
3
|
+
ApexCSS CLI - A powerful build tool for with automatic framework detection and seamless integration.
|
|
4
|
+
|
|
5
|
+
## What is ApexCSS CLI?
|
|
6
|
+
|
|
7
|
+
ApexCSS CLI helps you build and customize your own CSS utility framework. It provides:
|
|
8
|
+
|
|
9
|
+
- **Automatic Framework Detection** - Detects your project setup (React, Vue, Next.js, etc.) and configures accordingly
|
|
10
|
+
- **Layered CSS Generation** - Build only what you need (base, utilities, themes)
|
|
11
|
+
- **Watch Mode** - Automatically rebuild on configuration changes
|
|
12
|
+
- **Smart Configuration** - TypeScript/JavaScript config files with validation
|
|
13
|
+
- **Framework Integration** - Automatically adds CSS imports to your framework's entry file
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @apexcss/cli
|
|
19
|
+
# or use without installing
|
|
20
|
+
npx @apexcss/cli <command>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# 1. Initialize with automatic framework detection
|
|
27
|
+
apexcss init
|
|
28
|
+
|
|
29
|
+
# 2. Build your CSS
|
|
30
|
+
apexcss build
|
|
31
|
+
|
|
32
|
+
# 3. During development, watch for changes
|
|
33
|
+
apexcss watch
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Automatic Framework Detection
|
|
37
|
+
|
|
38
|
+
ApexCSS CLI automatically detects your project framework and configures the integration:
|
|
39
|
+
|
|
40
|
+
| Framework | Detection Method | Auto-Import |
|
|
41
|
+
|-----------|-----------------|-------------|
|
|
42
|
+
| Next.js | `next` in dependencies | ✅ Added to globals.css |
|
|
43
|
+
| React | `react` in dependencies | ✅ Added to main entry file |
|
|
44
|
+
| Vue | `vue` in dependencies | ✅ Added to main.ts/js |
|
|
45
|
+
| Angular | `@angular/core` in dependencies | ✅ Added to styles.css |
|
|
46
|
+
| Svelte | `svelte` in dependencies | ✅ Added to main entry file |
|
|
47
|
+
| Astro | `astro` in dependencies | ✅ Added to Layout.astro |
|
|
48
|
+
| Nuxt | `nuxt` in dependencies | ✅ Instructions for nuxt.config.ts |
|
|
49
|
+
| Vanilla | Default fallback | ✅ Added to main.js/ts |
|
|
50
|
+
|
|
51
|
+
Run `apexcss doctor` to see what was detected in your project.
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
### Initialize Configuration
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Interactive mode with prompts
|
|
59
|
+
apexcss init
|
|
60
|
+
|
|
61
|
+
# Skip interactive prompts
|
|
62
|
+
apexcss init --interactive=false
|
|
63
|
+
|
|
64
|
+
# Specify framework explicitly
|
|
65
|
+
apexcss init --framework=react
|
|
66
|
+
|
|
67
|
+
# Custom output directory
|
|
68
|
+
apexcss init --output=./src/styles
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Build CSS
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Build complete CSS (base + utilities + themes)
|
|
75
|
+
apexcss build
|
|
76
|
+
|
|
77
|
+
# Build specific layers only
|
|
78
|
+
apexcss build --layer base
|
|
79
|
+
apexcss build --layer utilities
|
|
80
|
+
apexcss build --layer themes
|
|
81
|
+
apexcss build --layer base,themes
|
|
82
|
+
|
|
83
|
+
# Production build with minification
|
|
84
|
+
apexcss build --minify
|
|
85
|
+
|
|
86
|
+
# Generate source maps
|
|
87
|
+
apexcss build --sourcemap
|
|
88
|
+
|
|
89
|
+
# Output as SCSS instead of CSS
|
|
90
|
+
apexcss build --format=scss
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Watch Mode
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Watch config file for changes and auto-rebuild
|
|
97
|
+
apexcss watch
|
|
98
|
+
|
|
99
|
+
# Watch with custom config path
|
|
100
|
+
apexcss watch --config=./custom.config.js
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Diagnostics
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Run system diagnostics
|
|
107
|
+
apexcss doctor
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## CLI Options
|
|
111
|
+
|
|
112
|
+
| Option | Description | Default |
|
|
113
|
+
|--------|-------------|---------|
|
|
114
|
+
| `-c, --config <path>` | Config file path | `./apex.config.js` |
|
|
115
|
+
| `-o, --output <dir>` | Output directory | `./dist/` |
|
|
116
|
+
| `-l, --layer <layers>` | Build specific layers | `all` |
|
|
117
|
+
| `--minify` | Minify output CSS | `false` |
|
|
118
|
+
| `--sourcemap` | Generate source maps | `false` |
|
|
119
|
+
| `--format <format>` | Output format | `css` |
|
|
120
|
+
| `--framework` | Specify framework | `auto-detect` |
|
|
121
|
+
| `--interactive` | Interactive prompts | `true` |
|
|
122
|
+
| `--import` | Add imports to entry files | `true` |
|
|
123
|
+
|
|
124
|
+
## Configuration
|
|
125
|
+
|
|
126
|
+
Create an `apex.config.js` file in your project root:
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
export default {
|
|
130
|
+
// Feature toggles - enable/disable utility categories
|
|
131
|
+
features: {
|
|
132
|
+
display: true,
|
|
133
|
+
flexbox: true,
|
|
134
|
+
grid: true,
|
|
135
|
+
positioning: true,
|
|
136
|
+
visibility: true,
|
|
137
|
+
spacing: true,
|
|
138
|
+
typography: true,
|
|
139
|
+
colors: true,
|
|
140
|
+
backgrounds: true,
|
|
141
|
+
borders: true,
|
|
142
|
+
shadows: true,
|
|
143
|
+
opacity: true,
|
|
144
|
+
transitions: true,
|
|
145
|
+
transforms: true,
|
|
146
|
+
animations: true
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// Breakpoints
|
|
150
|
+
breakpoints: {
|
|
151
|
+
sm: '320px',
|
|
152
|
+
md: '768px',
|
|
153
|
+
lg: '1024px',
|
|
154
|
+
xl: '1280px'
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
// Custom colors using OKLCH color space
|
|
158
|
+
colors: {
|
|
159
|
+
primary: {
|
|
160
|
+
hue: 250,
|
|
161
|
+
chroma: 0.2,
|
|
162
|
+
lightnessScale: {
|
|
163
|
+
50: 96, 100: 90, 200: 85, 300: 78, 400: 70,
|
|
164
|
+
500: 65, 600: 55, 700: 45, 800: 35, 900: 25, 950: 18
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
secondary: {
|
|
168
|
+
hue: 180,
|
|
169
|
+
chroma: 0.15,
|
|
170
|
+
lightnessScale: {
|
|
171
|
+
50: 96, 100: 90, 200: 85, 300: 78, 400: 70,
|
|
172
|
+
500: 65, 600: 55, 700: 45, 800: 35, 900: 25, 950: 18
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// Spacing scale
|
|
178
|
+
spacing: {
|
|
179
|
+
'0': '0px',
|
|
180
|
+
'1': '0.25rem',
|
|
181
|
+
'2': '0.5rem',
|
|
182
|
+
'4': '1rem',
|
|
183
|
+
'8': '2rem',
|
|
184
|
+
'16': '4rem'
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Peer Dependencies
|
|
190
|
+
|
|
191
|
+
This CLI requires `apexcss` to be installed in your project:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npm install apexcss
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Optional peer dependencies:
|
|
198
|
+
- `vite` - For building CSS (recommended)
|
|
199
|
+
- `sass` - For SCSS support
|
|
200
|
+
|
|
201
|
+
## Development
|
|
202
|
+
|
|
203
|
+
### Setup
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Clone the repository
|
|
207
|
+
git clone https://github.com/chris-briddock/apex-cli.git
|
|
208
|
+
cd apex-cli
|
|
209
|
+
|
|
210
|
+
# Install dependencies
|
|
211
|
+
npm install
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Testing
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Run all tests
|
|
218
|
+
npm test
|
|
219
|
+
|
|
220
|
+
# Run tests with coverage (LCOV report)
|
|
221
|
+
npm run test:coverage
|
|
222
|
+
|
|
223
|
+
# View coverage as text in terminal
|
|
224
|
+
npm run test:coverage:text
|
|
225
|
+
|
|
226
|
+
# Generate HTML coverage report
|
|
227
|
+
npm run test:coverage:html
|
|
228
|
+
# Then open coverage/index.html in your browser
|
|
229
|
+
|
|
230
|
+
# Run tests in watch mode
|
|
231
|
+
npm run test:watch
|
|
232
|
+
|
|
233
|
+
# Run unit tests only
|
|
234
|
+
npm run test:unit
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Code Quality
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Run ESLint
|
|
241
|
+
npm run lint
|
|
242
|
+
|
|
243
|
+
# Fix auto-fixable linting issues
|
|
244
|
+
npm run lint:fix
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Coverage Summary
|
|
248
|
+
|
|
249
|
+
Current test coverage:
|
|
250
|
+
|
|
251
|
+
| Module | Statements | Branches | Functions | Lines |
|
|
252
|
+
|--------|-----------|----------|-----------|-------|
|
|
253
|
+
| cli/utils/config-loader.js | 99% | 95% | 100% | 99% |
|
|
254
|
+
| cli/utils/logger.js | 100% | 100% | 100% | 100% |
|
|
255
|
+
| cli/utils/framework-detector.js | 95% | 94% | 100% | 95% |
|
|
256
|
+
| cli/commands/doctor.js | 82% | 79% | 100% | 82% |
|
|
257
|
+
| cli/commands/watch.js | 67% | 100% | 83% | 67% |
|
|
258
|
+
| cli/commands/build.js | 54% | 100% | 67% | 54% |
|
|
259
|
+
| cli/commands/init.js | 41% | 96% | 50% | 41% |
|
|
260
|
+
|
|
261
|
+
**Total: 76% statements, 92% branches, 86% functions**
|
|
262
|
+
|
|
263
|
+
## How It Works
|
|
264
|
+
|
|
265
|
+
1. **Initialization** (`apexcss init`):
|
|
266
|
+
- Detects your project framework from package.json
|
|
267
|
+
- Creates a starter config file (apex.config.js)
|
|
268
|
+
- Optionally adds CSS import to your framework's entry file
|
|
269
|
+
- Sets up .gitignore for output directory
|
|
270
|
+
|
|
271
|
+
2. **Build Process** (`apexcss build`):
|
|
272
|
+
- Reads your configuration
|
|
273
|
+
- Generates SCSS based on enabled features
|
|
274
|
+
- Uses Vite to compile CSS (if available)
|
|
275
|
+
- Outputs minified CSS (with optional source maps)
|
|
276
|
+
|
|
277
|
+
3. **Watch Mode** (`apexcss watch`):
|
|
278
|
+
- Monitors your config file for changes
|
|
279
|
+
- Automatically rebuilds on change
|
|
280
|
+
- Handles concurrent changes gracefully
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
MIT
|
package/bin/apexcss.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ApexCSS CLI
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* apexcss init Initialize configuration
|
|
8
|
+
* apexcss build Build custom CSS
|
|
9
|
+
* apexcss watch Watch for changes
|
|
10
|
+
* apexcss doctor Check system setup
|
|
11
|
+
*
|
|
12
|
+
* Options:
|
|
13
|
+
* -c, --config <path> Config file path (default: ./apex.config.js)
|
|
14
|
+
* -o, --output <dir> Output directory (default: ./src/apexcss/)
|
|
15
|
+
* --minify Minify output CSS
|
|
16
|
+
* --sourcemap Generate source maps
|
|
17
|
+
* -v, --version Show version
|
|
18
|
+
* -h, --help Show help
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { cli } from '../cli/index.js';
|
|
22
|
+
|
|
23
|
+
cli(process.argv);
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build command - Generate custom CSS from configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { writeFileSync, existsSync, mkdirSync, cpSync, rmSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { resolve, dirname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { loadConfig } from '../utils/config-loader.js';
|
|
9
|
+
import { logger } from '../utils/logger.js';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
|
|
14
|
+
// Import the config builder
|
|
15
|
+
import { generateSCSS } from '../utils/config-builder.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Valid layer names
|
|
19
|
+
*/
|
|
20
|
+
const VALID_LAYERS = ['base', 'utilities', 'themes'];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse layers option and return array of layers to build
|
|
24
|
+
* @param {string} layersOption - Comma-separated layer names or 'all'
|
|
25
|
+
* @returns {string[]} - Array of layer names
|
|
26
|
+
*/
|
|
27
|
+
export function parseLayers(layersOption) {
|
|
28
|
+
if (!layersOption || layersOption === 'all') {
|
|
29
|
+
return ['base', 'utilities', 'themes'];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const requested = layersOption.split(',').map(l => l.trim().toLowerCase());
|
|
33
|
+
const invalid = requested.filter(l => !VALID_LAYERS.includes(l));
|
|
34
|
+
|
|
35
|
+
if (invalid.length > 0) {
|
|
36
|
+
throw new Error(`Invalid layer(s): ${invalid.join(', ')}. Valid options: ${VALID_LAYERS.join(', ')}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return requested;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generate entry SCSS content based on selected layers
|
|
44
|
+
* @param {string[]} layers - Array of layer names to include
|
|
45
|
+
* @returns {string} - SCSS content for entry file
|
|
46
|
+
*/
|
|
47
|
+
export function generateLayerEntry(layers) {
|
|
48
|
+
const lines = [
|
|
49
|
+
'// ============================================================================',
|
|
50
|
+
'// ApexCSS - Layered Build Entry Point',
|
|
51
|
+
'// ============================================================================',
|
|
52
|
+
'// Auto-generated based on --layer option',
|
|
53
|
+
'// ============================================================================',
|
|
54
|
+
''
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
// Always include config
|
|
58
|
+
lines.push('@use \'config\';');
|
|
59
|
+
|
|
60
|
+
// Include selected layers
|
|
61
|
+
for (const layer of layers) {
|
|
62
|
+
lines.push(`@use '${layer}';`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
lines.push('', '// ============================================================================', '// End of Entry Point', '// ============================================================================', '');
|
|
66
|
+
|
|
67
|
+
return lines.join('\n');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get source directories needed for selected layers
|
|
72
|
+
* @param {string[]} layers - Array of layer names
|
|
73
|
+
* @returns {string[]} - Array of source directory names
|
|
74
|
+
*/
|
|
75
|
+
export function getSourceEntriesForLayers(layers) {
|
|
76
|
+
const entries = new Set(['config']);
|
|
77
|
+
|
|
78
|
+
for (const layer of layers) {
|
|
79
|
+
entries.add(layer);
|
|
80
|
+
switch (layer) {
|
|
81
|
+
case 'utilities':
|
|
82
|
+
entries.add('mixins');
|
|
83
|
+
entries.add('plugins');
|
|
84
|
+
break;
|
|
85
|
+
case 'base':
|
|
86
|
+
entries.add('mixins');
|
|
87
|
+
break;
|
|
88
|
+
case 'themes':
|
|
89
|
+
entries.add('mixins');
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return Array.from(entries);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Setup build environment
|
|
99
|
+
* @param {string} outputDir - Output directory path
|
|
100
|
+
* @returns {string} - Temp directory path
|
|
101
|
+
*/
|
|
102
|
+
export function setupBuildEnvironment(outputDir) {
|
|
103
|
+
const tempDir = resolve(outputDir, '.apexcss-build');
|
|
104
|
+
if (existsSync(tempDir)) {
|
|
105
|
+
rmSync(tempDir, { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
mkdirSync(tempDir, { recursive: true });
|
|
108
|
+
return tempDir;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Determine source directory
|
|
113
|
+
* @param {string} cwd - Current working directory
|
|
114
|
+
* @returns {string} - Source directory path
|
|
115
|
+
*/
|
|
116
|
+
export function determineSourceDir(cwd) {
|
|
117
|
+
// Look for apexcss source in user's node_modules
|
|
118
|
+
const nodeModulesSrcDir = resolve(cwd, 'node_modules', 'apexcss', 'src');
|
|
119
|
+
|
|
120
|
+
if (!existsSync(nodeModulesSrcDir)) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
'Could not find ApexCSS source files. ' +
|
|
123
|
+
'Please ensure apexcss is installed: npm install apexcss'
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return nodeModulesSrcDir;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Write configuration files to temp directory
|
|
132
|
+
* @param {string} tempDir - Temp directory path
|
|
133
|
+
* @param {string} scssContent - SCSS content to write
|
|
134
|
+
*/
|
|
135
|
+
export function writeConfigFiles(tempDir, scssContent) {
|
|
136
|
+
const configDir = resolve(tempDir, 'config');
|
|
137
|
+
mkdirSync(configDir, { recursive: true });
|
|
138
|
+
writeFileSync(resolve(configDir, '_custom-config.scss'), scssContent);
|
|
139
|
+
writeFileSync(
|
|
140
|
+
resolve(configDir, '_index.scss'),
|
|
141
|
+
'// Auto-generated config entry\n@forward \'custom-config\';\n'
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Find generated CSS file in temp directory
|
|
147
|
+
* @param {string} tempDir - Temp directory path
|
|
148
|
+
* @returns {string | undefined} - CSS file path or undefined
|
|
149
|
+
*/
|
|
150
|
+
export function findGeneratedCss(tempDir) {
|
|
151
|
+
const candidates = ['apex.css', 'style.css'];
|
|
152
|
+
for (const file of candidates) {
|
|
153
|
+
const fullPath = resolve(tempDir, file);
|
|
154
|
+
if (existsSync(fullPath)) {
|
|
155
|
+
return fullPath;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Copy source map if generated
|
|
163
|
+
* @param {string} tempDir - Temp directory path
|
|
164
|
+
* @param {string} outputDir - Output directory path
|
|
165
|
+
*/
|
|
166
|
+
export function copySourceMap(tempDir, outputDir) {
|
|
167
|
+
const mapPath = resolve(tempDir, 'apex.css.map');
|
|
168
|
+
if (existsSync(mapPath)) {
|
|
169
|
+
const mapContent = readFileSync(mapPath, 'utf-8');
|
|
170
|
+
writeFileSync(resolve(outputDir, 'apex.css.map'), mapContent);
|
|
171
|
+
logger.success('Source map generated');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Build CSS from configuration
|
|
177
|
+
* @param {object} options - Command options
|
|
178
|
+
* @returns {Promise<void>}
|
|
179
|
+
*/
|
|
180
|
+
export async function buildCommand(options) {
|
|
181
|
+
const cwd = process.cwd();
|
|
182
|
+
const startTime = Date.now();
|
|
183
|
+
|
|
184
|
+
logger.header('Building ApexCSS');
|
|
185
|
+
logger.newline();
|
|
186
|
+
|
|
187
|
+
// Load configuration
|
|
188
|
+
logger.info('Loading configuration...');
|
|
189
|
+
const config = loadConfig(options.configPath);
|
|
190
|
+
|
|
191
|
+
// Resolve output directory
|
|
192
|
+
const outputDir = resolve(cwd, options.outputDir);
|
|
193
|
+
|
|
194
|
+
// Ensure output directory exists
|
|
195
|
+
if (!existsSync(outputDir)) {
|
|
196
|
+
mkdirSync(outputDir, { recursive: true });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Generate SCSS from config
|
|
200
|
+
logger.info('Generating SCSS configuration...');
|
|
201
|
+
const scssContent = generateSCSS(config);
|
|
202
|
+
|
|
203
|
+
// Determine source directory
|
|
204
|
+
const sourceDir = determineSourceDir(cwd);
|
|
205
|
+
|
|
206
|
+
if (!existsSync(sourceDir)) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
'Could not find ApexCSS source files. ' +
|
|
209
|
+
'Please ensure apexcss is installed: npm install apexcss'
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Parse layers option
|
|
214
|
+
const layers = parseLayers(options.layers);
|
|
215
|
+
logger.info(`Building layers: ${layers.join(', ')}`);
|
|
216
|
+
|
|
217
|
+
// Setup build environment
|
|
218
|
+
const tempDir = setupBuildEnvironment(outputDir);
|
|
219
|
+
|
|
220
|
+
// Copy source files to temp directory based on selected layers
|
|
221
|
+
logger.info('Preparing build environment...');
|
|
222
|
+
copySourceFiles(sourceDir, tempDir, layers);
|
|
223
|
+
|
|
224
|
+
// Write custom config
|
|
225
|
+
writeConfigFiles(tempDir, scssContent);
|
|
226
|
+
|
|
227
|
+
// Generate layer-specific entry file
|
|
228
|
+
const entryContent = generateLayerEntry(layers);
|
|
229
|
+
writeFileSync(resolve(tempDir, 'apex-entry.scss'), entryContent);
|
|
230
|
+
|
|
231
|
+
// Build using Vite programmatically
|
|
232
|
+
logger.info('Compiling CSS...');
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
await runViteBuild(tempDir, options, outputDir, layers, scssContent);
|
|
236
|
+
|
|
237
|
+
const duration = Date.now() - startTime;
|
|
238
|
+
logger.newline();
|
|
239
|
+
logger.success(`Build completed in ${duration}ms`);
|
|
240
|
+
|
|
241
|
+
} catch (error) {
|
|
242
|
+
// Clean up temp directory on error
|
|
243
|
+
if (existsSync(tempDir)) {
|
|
244
|
+
rmSync(tempDir, { recursive: true });
|
|
245
|
+
}
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Run Vite build
|
|
252
|
+
* @param {string} tempDir - Temp directory path
|
|
253
|
+
* @param {object} options - Build options
|
|
254
|
+
* @param {string} outputDir - Output directory path
|
|
255
|
+
* @param {string[]} layers - Array of layer names
|
|
256
|
+
* @param {string} scssContent - SCSS content
|
|
257
|
+
* @returns {Promise<void>}
|
|
258
|
+
*/
|
|
259
|
+
async function runViteBuild(tempDir, options, outputDir, layers, scssContent) {
|
|
260
|
+
const { build } = await import('vite');
|
|
261
|
+
const cwd = process.cwd();
|
|
262
|
+
|
|
263
|
+
// Look for postcss config in user's project
|
|
264
|
+
const userPostcssConfig = resolve(cwd, 'postcss.config.js');
|
|
265
|
+
const apexcssPostcssConfig = resolve(cwd, 'node_modules', 'apexcss', 'postcss.config.js');
|
|
266
|
+
|
|
267
|
+
let postcssConfig;
|
|
268
|
+
if (existsSync(userPostcssConfig)) {
|
|
269
|
+
postcssConfig = userPostcssConfig;
|
|
270
|
+
} else if (existsSync(apexcssPostcssConfig)) {
|
|
271
|
+
postcssConfig = apexcssPostcssConfig;
|
|
272
|
+
} else {
|
|
273
|
+
postcssConfig = undefined;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
await build({
|
|
277
|
+
configFile: false,
|
|
278
|
+
css: {
|
|
279
|
+
postcss: postcssConfig
|
|
280
|
+
},
|
|
281
|
+
build: {
|
|
282
|
+
cssCodeSplit: false,
|
|
283
|
+
sourcemap: options.sourcemap,
|
|
284
|
+
minify: options.minify ? 'esbuild' : false,
|
|
285
|
+
outDir: tempDir,
|
|
286
|
+
rollupOptions: {
|
|
287
|
+
input: {
|
|
288
|
+
apex: resolve(tempDir, 'apex-entry.scss')
|
|
289
|
+
},
|
|
290
|
+
output: {
|
|
291
|
+
entryFileNames: '[name].js',
|
|
292
|
+
assetFileNames: () => '[name][extname]'
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Get output filenames based on layers
|
|
299
|
+
const { filename, description } = getOutputFilenames(layers);
|
|
300
|
+
|
|
301
|
+
// Copy CSS from temp to output
|
|
302
|
+
const generatedCssPath = findGeneratedCss(tempDir);
|
|
303
|
+
const finalCssPath = resolve(outputDir, `${filename}.css`);
|
|
304
|
+
|
|
305
|
+
if (!generatedCssPath) {
|
|
306
|
+
throw new Error('CSS file was not generated');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const cssContent = readFileSync(generatedCssPath, 'utf-8');
|
|
310
|
+
writeFileSync(finalCssPath, cssContent);
|
|
311
|
+
|
|
312
|
+
// Calculate file size
|
|
313
|
+
const contentBytes = Buffer.byteLength(cssContent, 'utf8');
|
|
314
|
+
const sizeKB = (contentBytes / 1024).toFixed(2);
|
|
315
|
+
|
|
316
|
+
const filePath = logger.path(`${filename}.css`);
|
|
317
|
+
logger.success(`Built: ${filePath} (${sizeKB} KB) [${description}]`);
|
|
318
|
+
|
|
319
|
+
// Copy source map if generated
|
|
320
|
+
if (options.sourcemap) {
|
|
321
|
+
const mapSource = resolve(tempDir, 'apex.css.map');
|
|
322
|
+
const mapDest = resolve(outputDir, `${filename}.css.map`);
|
|
323
|
+
if (existsSync(mapSource)) {
|
|
324
|
+
const mapContent = readFileSync(mapSource, 'utf-8');
|
|
325
|
+
writeFileSync(mapDest, mapContent);
|
|
326
|
+
logger.success('Source map generated');
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Output SCSS if requested
|
|
331
|
+
if (options.format === 'scss' || options.format === 'both') {
|
|
332
|
+
writeFileSync(resolve(outputDir, `${filename}.scss`), scssContent);
|
|
333
|
+
const scssPath = logger.path(`${filename}.scss`);
|
|
334
|
+
logger.success(`Generated: ${scssPath}`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Clean up temp directory
|
|
338
|
+
rmSync(tempDir, { recursive: true });
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Copy source files to temp directory
|
|
343
|
+
* @param {string} sourceDir - Source directory
|
|
344
|
+
* @param {string} tempDir - Temp directory
|
|
345
|
+
* @param {string[]} layers - Array of layer names to include
|
|
346
|
+
*/
|
|
347
|
+
function copySourceFiles(sourceDir, tempDir, layers) {
|
|
348
|
+
const entries = getSourceEntriesForLayers(layers);
|
|
349
|
+
|
|
350
|
+
for (const entry of entries) {
|
|
351
|
+
const sourcePath = resolve(sourceDir, entry);
|
|
352
|
+
const destPath = resolve(tempDir, entry);
|
|
353
|
+
|
|
354
|
+
if (existsSync(sourcePath)) {
|
|
355
|
+
cpSync(sourcePath, destPath, { recursive: true });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Generate filenames based on selected layers
|
|
362
|
+
* @param {string[]} layers - Array of layer names
|
|
363
|
+
* @returns {object} - Object with filename and description
|
|
364
|
+
*/
|
|
365
|
+
function getOutputFilenames(layers) {
|
|
366
|
+
if (layers.length === 3) {
|
|
367
|
+
return { filename: 'apex', description: 'complete framework' };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (layers.length === 1) {
|
|
371
|
+
return { filename: layers[0], description: `${layers[0]} layer` };
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const layerSuffix = layers.join('-');
|
|
375
|
+
return { filename: layerSuffix, description: `${layers.join(' + ')} layers` };
|
|
376
|
+
}
|