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