@ngcorex/cli 0.1.0 → 0.1.2
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 +64 -46
- package/dist/commands/run-build.js +127 -2
- package/package.json +15 -2
package/README.md
CHANGED
|
@@ -25,16 +25,63 @@ All work happens **at build time**.
|
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## Getting Started
|
|
29
|
+
|
|
30
|
+
### Installation
|
|
29
31
|
|
|
30
32
|
Install the CLI as a dev dependency:
|
|
31
33
|
|
|
32
34
|
```bash
|
|
33
|
-
npm install
|
|
34
|
-
|
|
35
|
+
npm install -D @ngcorex/cli
|
|
36
|
+
```
|
|
35
37
|
|
|
36
38
|
> The CLI depends on `@ngcorex/css`, which will be installed automatically.
|
|
37
39
|
|
|
40
|
+
### Create tokens.json
|
|
41
|
+
|
|
42
|
+
Create a `tokens.json` file at your project root:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"spacing": {
|
|
47
|
+
"1": "1rem",
|
|
48
|
+
"2": "2rem"
|
|
49
|
+
},
|
|
50
|
+
"colors": {
|
|
51
|
+
"gray": {
|
|
52
|
+
"100": "#f3f4f6",
|
|
53
|
+
"900": "#111827"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If `tokens.json` is present, it is used automatically.
|
|
60
|
+
|
|
61
|
+
## Configuration File
|
|
62
|
+
|
|
63
|
+
The CLI expects a file named:
|
|
64
|
+
|
|
65
|
+
### Create ngcorex.config.ts
|
|
66
|
+
|
|
67
|
+
Create a `ngcorex.config.ts` file at your project root:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { defineNgCorexConfig } from '@ngcorex/css';
|
|
71
|
+
|
|
72
|
+
export default defineNgCorexConfig({
|
|
73
|
+
output: {
|
|
74
|
+
file: 'src/styles/ngcorex.css'
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Important Rules
|
|
80
|
+
|
|
81
|
+
- The config file **must import from npm packages only**
|
|
82
|
+
- Relative imports are **not allowed**
|
|
83
|
+
- The config is transpiled internally using esbuild
|
|
84
|
+
|
|
38
85
|
---
|
|
39
86
|
|
|
40
87
|
## Commands
|
|
@@ -45,9 +92,9 @@ npm install --save-dev @ngcorex/cli
|
|
|
45
92
|
npx ngcorex build
|
|
46
93
|
```
|
|
47
94
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
95
|
+
- Loads `ngcorex.config.ts`
|
|
96
|
+
- Generates CSS output
|
|
97
|
+
- Writes the output file
|
|
51
98
|
|
|
52
99
|
---
|
|
53
100
|
|
|
@@ -57,9 +104,9 @@ npx ngcorex build
|
|
|
57
104
|
npx ngcorex build --watch
|
|
58
105
|
```
|
|
59
106
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
107
|
+
- Watches `ngcorex.config.ts`
|
|
108
|
+
- Rebuilds on change
|
|
109
|
+
- Does not exit on errors
|
|
63
110
|
|
|
64
111
|
---
|
|
65
112
|
|
|
@@ -69,9 +116,9 @@ npx ngcorex build --watch
|
|
|
69
116
|
npx ngcorex build --dry-run
|
|
70
117
|
```
|
|
71
118
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
119
|
+
- Runs the full pipeline
|
|
120
|
+
- Does NOT write any files
|
|
121
|
+
- Useful for testing configuration
|
|
75
122
|
|
|
76
123
|
---
|
|
77
124
|
|
|
@@ -85,37 +132,6 @@ Prints the CLI version.
|
|
|
85
132
|
|
|
86
133
|
---
|
|
87
134
|
|
|
88
|
-
## Configuration File
|
|
89
|
-
|
|
90
|
-
The CLI expects a file named:
|
|
91
|
-
|
|
92
|
-
```
|
|
93
|
-
ngcorex.config.ts
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Example
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
import { defineNgCorexConfig } from '@ngcorex/css';
|
|
100
|
-
|
|
101
|
-
export default defineNgCorexConfig({
|
|
102
|
-
tokens: {
|
|
103
|
-
spacing: {
|
|
104
|
-
1: 4,
|
|
105
|
-
2: 8
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### Important Rules
|
|
112
|
-
|
|
113
|
-
* The config file **must import from npm packages only**
|
|
114
|
-
* Relative imports are **not allowed**
|
|
115
|
-
* The config is transpiled internally using esbuild
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
135
|
## Output
|
|
120
136
|
|
|
121
137
|
The CLI generates CSS variables based on your tokens and constraints.
|
|
@@ -124,8 +140,10 @@ Example output:
|
|
|
124
140
|
|
|
125
141
|
```css
|
|
126
142
|
:root {
|
|
127
|
-
--spacing-1:
|
|
128
|
-
--spacing-2:
|
|
143
|
+
--nx-spacing-1: 1rem;
|
|
144
|
+
--nx-spacing-2: 2rem;
|
|
145
|
+
--nx-color-gray-100: #f3f4f6;
|
|
146
|
+
--nx-color-gray-900: #111827;
|
|
129
147
|
}
|
|
130
148
|
```
|
|
131
149
|
|
|
@@ -133,4 +151,4 @@ Example output:
|
|
|
133
151
|
|
|
134
152
|
## License
|
|
135
153
|
|
|
136
|
-
MIT
|
|
154
|
+
MIT
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
1
3
|
import { resolveConfigPath } from '../config/resolve-path.js';
|
|
2
4
|
import { loadConfig } from '../config/load-config.js';
|
|
3
5
|
import { writeCss } from '../output/write-css.js';
|
|
@@ -7,10 +9,133 @@ export async function runBuild(options = {}) {
|
|
|
7
9
|
const configPath = resolveConfigPath();
|
|
8
10
|
const config = await loadConfig(configPath);
|
|
9
11
|
console.log('✔ Loaded ngcorex.config.ts');
|
|
10
|
-
const css = buildCssFromConfig(config);
|
|
11
|
-
console.log('✔ Generated CSS');
|
|
12
12
|
const outputFile = config.output?.file ?? 'src/styles/ngcorex.css';
|
|
13
13
|
const outputPath = resolve(process.cwd(), outputFile);
|
|
14
|
+
const tokensPath = path.resolve(process.cwd(), 'tokens.json');
|
|
15
|
+
let fileTokens = null;
|
|
16
|
+
// Safe JSON parsing with friendly errors
|
|
17
|
+
if (fs.existsSync(tokensPath)) {
|
|
18
|
+
const raw = fs.readFileSync(tokensPath, 'utf-8');
|
|
19
|
+
try {
|
|
20
|
+
fileTokens = JSON.parse(raw);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error('');
|
|
24
|
+
console.error('❌ Invalid tokens.json');
|
|
25
|
+
console.error('Failed to parse JSON.');
|
|
26
|
+
if (error instanceof SyntaxError) {
|
|
27
|
+
console.error(error.message);
|
|
28
|
+
}
|
|
29
|
+
console.error('');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Validate top-level token shape
|
|
34
|
+
if (fileTokens !== null) {
|
|
35
|
+
if (typeof fileTokens !== 'object' || Array.isArray(fileTokens)) {
|
|
36
|
+
console.error('');
|
|
37
|
+
console.error('❌ Invalid tokens.json');
|
|
38
|
+
console.error('The file must export a JSON object at the top level.');
|
|
39
|
+
console.error('');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Validate known token categories
|
|
44
|
+
if (fileTokens !== null) {
|
|
45
|
+
const tokens = fileTokens;
|
|
46
|
+
if ('spacing' in tokens) {
|
|
47
|
+
if (typeof tokens.spacing !== 'object' ||
|
|
48
|
+
tokens.spacing === null ||
|
|
49
|
+
Array.isArray(tokens.spacing)) {
|
|
50
|
+
console.error('');
|
|
51
|
+
console.error('❌ Invalid tokens.json');
|
|
52
|
+
console.error('The "spacing" token must be an object.');
|
|
53
|
+
console.error('');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if ('colors' in tokens) {
|
|
58
|
+
if (typeof tokens.colors !== 'object' ||
|
|
59
|
+
tokens.colors === null ||
|
|
60
|
+
Array.isArray(tokens.colors)) {
|
|
61
|
+
console.error('');
|
|
62
|
+
console.error('❌ Invalid tokens.json');
|
|
63
|
+
console.error('The "colors" token must be an object.');
|
|
64
|
+
console.error('');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Validate spacing token values
|
|
70
|
+
if (fileTokens !== null) {
|
|
71
|
+
const tokens = fileTokens;
|
|
72
|
+
if (tokens.spacing) {
|
|
73
|
+
const spacing = tokens.spacing;
|
|
74
|
+
for (const [key, value] of Object.entries(spacing)) {
|
|
75
|
+
if (typeof value !== 'number' && typeof value !== 'string') {
|
|
76
|
+
console.error('');
|
|
77
|
+
console.error('❌ Invalid tokens.json');
|
|
78
|
+
console.error(`Invalid spacing value for key "${key}". Expected number or string.`);
|
|
79
|
+
console.error('');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Validate color token structure & values
|
|
86
|
+
if (fileTokens !== null) {
|
|
87
|
+
const tokens = fileTokens;
|
|
88
|
+
if (tokens.colors) {
|
|
89
|
+
const colors = tokens.colors;
|
|
90
|
+
for (const [colorName, shades] of Object.entries(colors)) {
|
|
91
|
+
if (typeof shades !== 'object' ||
|
|
92
|
+
shades === null ||
|
|
93
|
+
Array.isArray(shades)) {
|
|
94
|
+
console.error('');
|
|
95
|
+
console.error('❌ Invalid tokens.json');
|
|
96
|
+
console.error(`Color "${colorName}" must be an object of shade values.`);
|
|
97
|
+
console.error('');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
for (const [shade, value] of Object.entries(shades)) {
|
|
101
|
+
// shade keys must be numeric
|
|
102
|
+
if (!/^\d+$/.test(shade)) {
|
|
103
|
+
console.error('');
|
|
104
|
+
console.error('❌ Invalid tokens.json');
|
|
105
|
+
console.error(`Invalid shade key "${shade}" in color "${colorName}". Shade keys must be numeric.`);
|
|
106
|
+
console.error('');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
// value must be a string
|
|
110
|
+
if (typeof value !== 'string') {
|
|
111
|
+
console.error('');
|
|
112
|
+
console.error('❌ Invalid tokens.json');
|
|
113
|
+
console.error(`Invalid value for ${colorName}.${shade}. Expected a color string.`);
|
|
114
|
+
console.error('');
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
// very light color format validation (delegate strictness to engine)
|
|
118
|
+
if (!value.startsWith('#') &&
|
|
119
|
+
!value.startsWith('rgb(') &&
|
|
120
|
+
!value.startsWith('rgba(')) {
|
|
121
|
+
console.error('');
|
|
122
|
+
console.error('❌ Invalid tokens.json');
|
|
123
|
+
console.error(`Invalid color format for ${colorName}.${shade}: "${value}".`);
|
|
124
|
+
console.error('');
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const effectiveConfig = fileTokens
|
|
132
|
+
? {
|
|
133
|
+
...config,
|
|
134
|
+
tokens: fileTokens
|
|
135
|
+
}
|
|
136
|
+
: config;
|
|
137
|
+
const css = buildCssFromConfig(effectiveConfig);
|
|
138
|
+
console.log('✔ Generated CSS');
|
|
14
139
|
writeCss(outputPath, css, { dryRun: options.dryRun });
|
|
15
140
|
if (!options.dryRun) {
|
|
16
141
|
console.log(`✔ Output written to ${outputFile}`);
|
package/package.json
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ngcorex/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "CLI for ngCorex - Angular-native design token engine",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"design-tokens",
|
|
7
|
+
"css-cli",
|
|
8
|
+
"build-tool",
|
|
9
|
+
"css-build",
|
|
10
|
+
"token-cli",
|
|
11
|
+
"developer-tooling",
|
|
12
|
+
"frontend-tooling",
|
|
13
|
+
"ngcorex",
|
|
14
|
+
"typescript",
|
|
15
|
+
"angular"
|
|
16
|
+
],
|
|
5
17
|
"license": "MIT",
|
|
6
18
|
"type": "module",
|
|
7
19
|
"bin": {
|
|
8
20
|
"ngcorex": "dist/bin.js"
|
|
9
21
|
},
|
|
10
22
|
"dependencies": {
|
|
11
|
-
"@ngcorex/css": "^0.1.0"
|
|
23
|
+
"@ngcorex/css": "^0.1.0",
|
|
24
|
+
"esbuild": "^0.20.0"
|
|
12
25
|
},
|
|
13
26
|
"files": [
|
|
14
27
|
"dist",
|