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,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework detection utility
|
|
3
|
+
* Detects which framework the user's project is using
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Framework definitions with their detection criteria
|
|
11
|
+
*/
|
|
12
|
+
export const FRAMEWORKS = {
|
|
13
|
+
next: {
|
|
14
|
+
name: 'Next.js',
|
|
15
|
+
detect: (pkg) => pkg.dependencies?.next || pkg.devDependencies?.next,
|
|
16
|
+
entryFiles: ['src/app/layout.tsx', 'src/app/layout.jsx', 'app/layout.tsx', 'app/layout.jsx'],
|
|
17
|
+
importStatement: 'import \'apexcss\';\n',
|
|
18
|
+
cssConfig: '// Import in layout.tsx:\nimport \'apexcss/base\';\nimport \'apexcss/utilities\';\nimport \'apexcss/themes\';\n',
|
|
19
|
+
configFile: 'next.config.js'
|
|
20
|
+
},
|
|
21
|
+
nuxt: {
|
|
22
|
+
name: 'Nuxt',
|
|
23
|
+
detect: (pkg) => pkg.dependencies?.nuxt || pkg.devDependencies?.nuxt,
|
|
24
|
+
entryFiles: ['nuxt.config.ts', 'nuxt.config.js'],
|
|
25
|
+
cssConfig: 'css: [\'~/apexcss/apex.css\']',
|
|
26
|
+
importStatement: null // Nuxt uses CSS config in nuxt.config
|
|
27
|
+
},
|
|
28
|
+
react: {
|
|
29
|
+
name: 'React',
|
|
30
|
+
detect: (pkg) => pkg.dependencies?.react && !pkg.dependencies?.next,
|
|
31
|
+
entryFiles: ['src/index.css', 'src/main.css', 'src/styles.css'],
|
|
32
|
+
importStatement: '@import \'apexcss\';\n',
|
|
33
|
+
fallbackFile: 'src/index.css'
|
|
34
|
+
},
|
|
35
|
+
vue: {
|
|
36
|
+
name: 'Vue',
|
|
37
|
+
detect: (pkg) => pkg.dependencies?.vue && !pkg.dependencies?.nuxt,
|
|
38
|
+
entryFiles: ['src/style.css', 'src/styles.css', 'src/main.css'],
|
|
39
|
+
importStatement: '@import \'apexcss\';\n',
|
|
40
|
+
fallbackFile: 'src/style.css'
|
|
41
|
+
},
|
|
42
|
+
angular: {
|
|
43
|
+
name: 'Angular',
|
|
44
|
+
detect: (pkg) => pkg.dependencies?.['@angular/core'],
|
|
45
|
+
entryFiles: ['src/styles.css', 'src/styles.scss', 'angular.json'],
|
|
46
|
+
importStatement: '@import \'apexcss\';\n',
|
|
47
|
+
configFile: 'angular.json'
|
|
48
|
+
},
|
|
49
|
+
svelte: {
|
|
50
|
+
name: 'Svelte',
|
|
51
|
+
detect: (pkg) => pkg.dependencies?.svelte || pkg.devDependencies?.svelte,
|
|
52
|
+
entryFiles: ['src/app.css', 'src/style.css', 'src/styles.css'],
|
|
53
|
+
importStatement: '@import \'apexcss\';\n',
|
|
54
|
+
fallbackFile: 'src/app.css'
|
|
55
|
+
},
|
|
56
|
+
astro: {
|
|
57
|
+
name: 'Astro',
|
|
58
|
+
detect: (pkg) => pkg.dependencies?.astro || pkg.devDependencies?.astro,
|
|
59
|
+
entryFiles: ['src/styles/global.css', 'src/style.css', 'src/layouts/Layout.astro'],
|
|
60
|
+
importStatement: '@import \'apexcss\';\n',
|
|
61
|
+
fallbackFile: 'src/styles/global.css'
|
|
62
|
+
},
|
|
63
|
+
vanilla: {
|
|
64
|
+
name: 'Vanilla/Vite',
|
|
65
|
+
detect: () => true, // Fallback
|
|
66
|
+
entryFiles: ['src/style.css', 'src/styles.css', 'style.css', 'styles.css'],
|
|
67
|
+
importStatement: '@import \'apexcss\';\n',
|
|
68
|
+
fallbackFile: 'src/style.css'
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Detect the framework being used in the current project
|
|
74
|
+
* @param {string} cwd - Current working directory
|
|
75
|
+
* @returns {object} - Detected framework info
|
|
76
|
+
*/
|
|
77
|
+
export function detectFramework(cwd = process.cwd()) {
|
|
78
|
+
// Read package.json
|
|
79
|
+
const packageJsonPath = resolve(cwd, 'package.json');
|
|
80
|
+
|
|
81
|
+
if (!existsSync(packageJsonPath)) {
|
|
82
|
+
return {
|
|
83
|
+
id: 'vanilla',
|
|
84
|
+
...FRAMEWORKS.vanilla,
|
|
85
|
+
detected: false,
|
|
86
|
+
hasPackageJson: false
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let pkg;
|
|
91
|
+
try {
|
|
92
|
+
pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
93
|
+
} catch {
|
|
94
|
+
return {
|
|
95
|
+
id: 'vanilla',
|
|
96
|
+
...FRAMEWORKS.vanilla,
|
|
97
|
+
detected: false,
|
|
98
|
+
hasPackageJson: true,
|
|
99
|
+
parseError: true
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check each framework in order (more specific first)
|
|
104
|
+
const frameworkOrder = ['next', 'nuxt', 'angular', 'svelte', 'astro', 'react', 'vue', 'vanilla'];
|
|
105
|
+
|
|
106
|
+
for (const frameworkId of frameworkOrder) {
|
|
107
|
+
const framework = FRAMEWORKS[frameworkId];
|
|
108
|
+
if (framework.detect(pkg)) {
|
|
109
|
+
// Find the actual entry file that exists
|
|
110
|
+
const existingEntry = framework.entryFiles.find(file =>
|
|
111
|
+
existsSync(resolve(cwd, file))
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
id: frameworkId,
|
|
116
|
+
...framework,
|
|
117
|
+
detected: frameworkId !== 'vanilla',
|
|
118
|
+
hasPackageJson: true,
|
|
119
|
+
entryFile: existingEntry || framework.fallbackFile,
|
|
120
|
+
packageJson: pkg
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Should not reach here due to vanilla fallback
|
|
126
|
+
return {
|
|
127
|
+
id: 'vanilla',
|
|
128
|
+
...FRAMEWORKS.vanilla,
|
|
129
|
+
detected: false,
|
|
130
|
+
hasPackageJson: true
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get all available frameworks for selection
|
|
136
|
+
* @returns {Array} - List of framework options
|
|
137
|
+
*/
|
|
138
|
+
export function getAvailableFrameworks() {
|
|
139
|
+
return [
|
|
140
|
+
{ id: 'next', name: 'Next.js' },
|
|
141
|
+
{ id: 'nuxt', name: 'Nuxt' },
|
|
142
|
+
{ id: 'react', name: 'React (Vite)' },
|
|
143
|
+
{ id: 'vue', name: 'Vue (Vite)' },
|
|
144
|
+
{ id: 'angular', name: 'Angular' },
|
|
145
|
+
{ id: 'svelte', name: 'Svelte (Vite)' },
|
|
146
|
+
{ id: 'astro', name: 'Astro' },
|
|
147
|
+
{ id: 'vanilla', name: 'Vanilla/Vite' }
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get framework-specific output directory recommendation
|
|
153
|
+
* @param {string} frameworkId - Framework identifier
|
|
154
|
+
* @returns {string} - Recommended output directory
|
|
155
|
+
*/
|
|
156
|
+
export function getRecommendedOutputDir(frameworkId) {
|
|
157
|
+
const recommendations = {
|
|
158
|
+
next: './dist/',
|
|
159
|
+
nuxt: './dist/',
|
|
160
|
+
react: './dist/',
|
|
161
|
+
vue: './dist/',
|
|
162
|
+
angular: './dist/',
|
|
163
|
+
svelte: './dist/',
|
|
164
|
+
astro: './dist/',
|
|
165
|
+
vanilla: './dist/'
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
return recommendations[frameworkId] || './dist/';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Check if a framework uses a specific configuration approach
|
|
173
|
+
* @param {string} frameworkId - Framework identifier
|
|
174
|
+
* @returns {object} - Configuration approach
|
|
175
|
+
*/
|
|
176
|
+
export function getFrameworkConfigApproach(frameworkId) {
|
|
177
|
+
const approaches = {
|
|
178
|
+
next: { type: 'import', supportsCSSImport: true },
|
|
179
|
+
nuxt: { type: 'config', configKey: 'css' },
|
|
180
|
+
react: { type: 'import', supportsCSSImport: true },
|
|
181
|
+
vue: { type: 'import', supportsCSSImport: true },
|
|
182
|
+
angular: { type: 'styles', supportsGlobalStyles: true },
|
|
183
|
+
svelte: { type: 'import', supportsCSSImport: true },
|
|
184
|
+
astro: { type: 'import', supportsCSSImport: true },
|
|
185
|
+
vanilla: { type: 'import', supportsCSSImport: true }
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
return approaches[frameworkId] || { type: 'import' };
|
|
189
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility for consistent CLI output
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Simple ANSI color codes
|
|
6
|
+
const colors = {
|
|
7
|
+
reset: '\x1b[0m',
|
|
8
|
+
bright: '\x1b[1m',
|
|
9
|
+
dim: '\x1b[2m',
|
|
10
|
+
red: '\x1b[31m',
|
|
11
|
+
green: '\x1b[32m',
|
|
12
|
+
yellow: '\x1b[33m',
|
|
13
|
+
blue: '\x1b[34m',
|
|
14
|
+
magenta: '\x1b[35m',
|
|
15
|
+
cyan: '\x1b[36m'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Format a message with color
|
|
20
|
+
* @param {string} text
|
|
21
|
+
* @param {string} color
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Format a message with color
|
|
26
|
+
* @param {string} text - Text to colorize
|
|
27
|
+
* @param {string} color - Color name
|
|
28
|
+
* @returns {string} - Colorized text
|
|
29
|
+
*/
|
|
30
|
+
function colorize(text, color) {
|
|
31
|
+
return `${colors[color]}${text}${colors.reset}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const logger = {
|
|
35
|
+
/**
|
|
36
|
+
* Info message
|
|
37
|
+
* @param {string} message
|
|
38
|
+
*/
|
|
39
|
+
info(message) {
|
|
40
|
+
console.log(`${colorize('[apexcss]', 'cyan')} ${message}`);
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Success message
|
|
45
|
+
* @param {string} message
|
|
46
|
+
*/
|
|
47
|
+
success(message) {
|
|
48
|
+
console.log(`${colorize('[apexcss]', 'green')} ${colorize('✔', 'green')} ${message}`);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Warning message
|
|
53
|
+
* @param {string} message
|
|
54
|
+
*/
|
|
55
|
+
warn(message) {
|
|
56
|
+
console.warn(`${colorize('[apexcss]', 'yellow')} ${colorize('⚠', 'yellow')} ${message}`);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Error message
|
|
61
|
+
* @param {string} message
|
|
62
|
+
*/
|
|
63
|
+
error(message) {
|
|
64
|
+
console.error(`${colorize('[apexcss]', 'red')} ${colorize('✖', 'red')} ${message}`);
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Debug message (only shown with DEBUG env var)
|
|
69
|
+
* @param {string} message
|
|
70
|
+
*/
|
|
71
|
+
debug(message) {
|
|
72
|
+
if (process.env.DEBUG) {
|
|
73
|
+
console.log(`${colorize('[apexcss]', 'dim')} ${colorize('[debug]', 'magenta')} ${message}`);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* New line
|
|
79
|
+
*/
|
|
80
|
+
newline() {
|
|
81
|
+
console.log();
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Section header
|
|
86
|
+
* @param {string} title
|
|
87
|
+
*/
|
|
88
|
+
header(title) {
|
|
89
|
+
this.newline();
|
|
90
|
+
console.log(colorize(title, 'bright'));
|
|
91
|
+
console.log(colorize('═'.repeat(title.length), 'dim'));
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* List items
|
|
96
|
+
* @param {string[]} items
|
|
97
|
+
*/
|
|
98
|
+
list(items) {
|
|
99
|
+
items.forEach(item => {
|
|
100
|
+
console.log(` ${colorize('•', 'cyan')} ${item}`);
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Format file path
|
|
106
|
+
* @param {string} filepath
|
|
107
|
+
* @returns {string}
|
|
108
|
+
*/
|
|
109
|
+
path(filepath) {
|
|
110
|
+
return colorize(filepath, 'cyan');
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Format command
|
|
115
|
+
* @param {string} command
|
|
116
|
+
* @returns {string}
|
|
117
|
+
*/
|
|
118
|
+
cmd(command) {
|
|
119
|
+
return colorize(command, 'yellow');
|
|
120
|
+
}
|
|
121
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "apexcss-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ApexCSS CLI - Build and customize your CSS framework",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "cli/index.js",
|
|
7
|
+
"types": "./package.json",
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"cli/",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public",
|
|
16
|
+
"registry": "https://registry.npmjs.org/"
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"apexcss": "bin/apexcss.js"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "node tests/run-tests.js",
|
|
23
|
+
"test:unit": "node --test tests/unit/**/*.test.js",
|
|
24
|
+
"test:watch": "node --test --watch tests/unit/**/*.test.js",
|
|
25
|
+
"test:coverage": "c8 node tests/run-tests.js",
|
|
26
|
+
"test:coverage:text": "c8 --reporter=text --reporter=text-summary node tests/run-tests.js",
|
|
27
|
+
"test:coverage:html": "c8 --reporter=html node tests/run-tests.js",
|
|
28
|
+
"lint": "eslint cli/ bin/ tests/",
|
|
29
|
+
"lint:fix": "eslint cli/ bin/ tests/ --fix",
|
|
30
|
+
"doctor": "node bin/apexcss.js doctor",
|
|
31
|
+
"version": "node bin/apexcss.js --version",
|
|
32
|
+
"prepublishOnly": "echo \"Preparing for publish...\""
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"css",
|
|
36
|
+
"framework",
|
|
37
|
+
"cli",
|
|
38
|
+
"apexcss",
|
|
39
|
+
"utility-first",
|
|
40
|
+
"sass",
|
|
41
|
+
"scss",
|
|
42
|
+
"build-tool"
|
|
43
|
+
],
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/chris-briddock/apex-cli.git"
|
|
47
|
+
},
|
|
48
|
+
"author": "",
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"c8": "^10.1.3",
|
|
52
|
+
"eslint": "^9.16.0",
|
|
53
|
+
"eslint-plugin-jsdoc": "^50.6.1",
|
|
54
|
+
"globals": "^15.14.0"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"chalk": "^5.3.0",
|
|
58
|
+
"commander": "^12.0.0"
|
|
59
|
+
},
|
|
60
|
+
"optionalDependencies": {
|
|
61
|
+
"chokidar": "^3.6.0",
|
|
62
|
+
"inquirer": "^9.2.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"apexcss": ">=0.3.0",
|
|
66
|
+
"sass": ">=1.90.0",
|
|
67
|
+
"vite": ">=7.0.0"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=18.0.0"
|
|
71
|
+
}
|
|
72
|
+
}
|