@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 CHANGED
@@ -25,16 +25,63 @@ All work happens **at build time**.
25
25
 
26
26
  ---
27
27
 
28
- ## Installation
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 --save-dev @ngcorex/cli
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
- * Loads `ngcorex.config.ts`
49
- * Generates CSS output
50
- * Writes the output file
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
- * Watches `ngcorex.config.ts`
61
- * Rebuilds on change
62
- * Does not exit on errors
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
- * Runs the full pipeline
73
- * Does NOT write any files
74
- * Useful for testing configuration
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: 4px;
128
- --spacing-2: 8px;
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.0",
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",