grg-kit-cli 0.2.0 ā 0.3.1
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 +97 -167
- package/bin/grg.js +25 -32
- package/commands/add.js +59 -101
- package/commands/init.js +150 -19
- package/commands/list.js +26 -58
- package/commands/llm-prompts.js +706 -0
- package/config/resources.js +15 -11
- package/package.json +6 -7
- package/scripts/README.md +4 -4
- package/scripts/generate-resources.js +14 -14
- package/commands/interactive.js +0 -223
- package/commands/metadata.js +0 -122
package/commands/init.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
const fs = require('fs').promises;
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const { exec } = require('child_process');
|
|
4
|
+
const { promisify } = require('util');
|
|
3
5
|
const degit = require('degit');
|
|
4
6
|
const chalk = require('chalk');
|
|
5
7
|
const ora = require('ora');
|
|
6
8
|
const { RESOURCES, REPO } = require('../config/resources');
|
|
7
9
|
|
|
10
|
+
const execAsync = promisify(exec);
|
|
11
|
+
|
|
8
12
|
/**
|
|
9
13
|
* Init command - initializes GRG Kit in the project
|
|
10
|
-
* Creates
|
|
14
|
+
* Creates Angular project, installs Tailwind CSS v4, runs spartan-ng ui, downloads theme
|
|
11
15
|
*/
|
|
12
|
-
async function init(options) {
|
|
16
|
+
async function init(projectName, options) {
|
|
13
17
|
const themeName = options.theme || 'grg-theme';
|
|
14
18
|
const theme = RESOURCES.themes.find(t => t.name === themeName);
|
|
15
19
|
|
|
@@ -21,37 +25,161 @@ async function init(options) {
|
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
console.log(chalk.bold.cyan('\nš Initializing GRG Kit\n'));
|
|
28
|
+
console.log(chalk.gray(` Project: ${projectName}`));
|
|
29
|
+
console.log(chalk.gray(` Theme: ${theme.title}\n`));
|
|
30
|
+
|
|
31
|
+
const spinner = ora();
|
|
24
32
|
|
|
25
|
-
// Step 1: Create
|
|
26
|
-
|
|
33
|
+
// Step 1: Create Angular project
|
|
34
|
+
spinner.start(`Creating Angular project "${projectName}"...`);
|
|
27
35
|
try {
|
|
28
|
-
await
|
|
29
|
-
spinner.succeed(chalk.green(
|
|
36
|
+
await execAsync(`ng new ${projectName} --style=css`, { stdio: 'pipe' });
|
|
37
|
+
spinner.succeed(chalk.green(`ā Created Angular project "${projectName}"`));
|
|
30
38
|
} catch (error) {
|
|
31
|
-
spinner.fail(chalk.red('Failed to create
|
|
39
|
+
spinner.fail(chalk.red('Failed to create Angular project'));
|
|
32
40
|
console.error(chalk.red(error.message));
|
|
33
41
|
process.exit(1);
|
|
34
42
|
}
|
|
35
43
|
|
|
36
|
-
//
|
|
37
|
-
|
|
44
|
+
// Change to project directory for remaining steps
|
|
45
|
+
process.chdir(projectName);
|
|
46
|
+
|
|
47
|
+
// Step 2: Install Tailwind CSS v4
|
|
48
|
+
spinner.start('Installing Tailwind CSS v4...');
|
|
38
49
|
try {
|
|
39
|
-
|
|
50
|
+
await execAsync('pnpm install tailwindcss @tailwindcss/postcss postcss', { stdio: 'pipe' });
|
|
51
|
+
spinner.succeed(chalk.green('ā Tailwind CSS v4 installed'));
|
|
52
|
+
} catch (error) {
|
|
53
|
+
spinner.warn(chalk.yellow('Tailwind CSS installation skipped (may already be installed)'));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Step 3: Create .postcssrc.json
|
|
57
|
+
spinner.start('Creating PostCSS configuration...');
|
|
58
|
+
try {
|
|
59
|
+
const postcssConfig = {
|
|
60
|
+
plugins: {
|
|
61
|
+
'@tailwindcss/postcss': {}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
await fs.writeFile('.postcssrc.json', JSON.stringify(postcssConfig, null, 2) + '\n');
|
|
65
|
+
spinner.succeed(chalk.green('ā Created .postcssrc.json'));
|
|
66
|
+
} catch (error) {
|
|
67
|
+
spinner.warn(chalk.yellow('Could not create .postcssrc.json (may already exist)'));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Step 4: Install Spartan-NG CLI
|
|
71
|
+
spinner.start('Installing Spartan-NG CLI...');
|
|
72
|
+
try {
|
|
73
|
+
await execAsync('pnpm install -D @spartan-ng/cli', { stdio: 'pipe' });
|
|
74
|
+
spinner.succeed(chalk.green('ā Spartan-NG CLI installed'));
|
|
75
|
+
} catch (error) {
|
|
76
|
+
spinner.warn(chalk.yellow('Spartan-NG CLI installation skipped'));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Step 5: Create components.json config file
|
|
80
|
+
spinner.start('Creating Spartan-NG configuration...');
|
|
81
|
+
try {
|
|
82
|
+
const componentsConfig = {
|
|
83
|
+
componentsPath: 'libs/ui',
|
|
84
|
+
importAlias: '@spartan-ng/helm'
|
|
85
|
+
};
|
|
86
|
+
await fs.writeFile('components.json', JSON.stringify(componentsConfig, null, 2) + '\n');
|
|
87
|
+
spinner.succeed(chalk.green('ā Created components.json'));
|
|
88
|
+
} catch (error) {
|
|
89
|
+
spinner.warn(chalk.yellow('Could not create components.json'));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Step 6: Update tsconfig.json with paths for Spartan-NG
|
|
93
|
+
spinner.start('Configuring TypeScript paths...');
|
|
94
|
+
try {
|
|
95
|
+
const tsconfigPath = 'tsconfig.json';
|
|
96
|
+
let tsconfigContent = await fs.readFile(tsconfigPath, 'utf-8');
|
|
97
|
+
|
|
98
|
+
// Strip comments from tsconfig (Angular generates tsconfig with comments)
|
|
99
|
+
tsconfigContent = tsconfigContent
|
|
100
|
+
.replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
|
|
101
|
+
.replace(/\/\/.*/g, ''); // Remove line comments
|
|
102
|
+
|
|
103
|
+
const tsconfig = JSON.parse(tsconfigContent);
|
|
104
|
+
|
|
105
|
+
// Add baseUrl and paths for @spartan-ng/helm
|
|
106
|
+
tsconfig.compilerOptions = tsconfig.compilerOptions || {};
|
|
107
|
+
tsconfig.compilerOptions.baseUrl = '.';
|
|
108
|
+
tsconfig.compilerOptions.paths = {
|
|
109
|
+
'@spartan-ng/helm/*': ['./libs/ui/*/src/index.ts']
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
await fs.writeFile(tsconfigPath, JSON.stringify(tsconfig, null, 2) + '\n');
|
|
113
|
+
spinner.succeed(chalk.green('ā Configured TypeScript paths'));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
spinner.warn(chalk.yellow('Could not update tsconfig.json'));
|
|
116
|
+
console.error(chalk.gray(error.message));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Step 7: Run Spartan-NG UI generator (install all components)
|
|
120
|
+
spinner.start('Installing all Spartan-NG UI components...');
|
|
121
|
+
try {
|
|
122
|
+
const { spawn } = require('child_process');
|
|
123
|
+
await new Promise((resolve, reject) => {
|
|
124
|
+
const child = spawn('pnpm', ['ng', 'g', '@spartan-ng/cli:ui', 'all', '--defaults'], {
|
|
125
|
+
stdio: 'inherit',
|
|
126
|
+
shell: true
|
|
127
|
+
});
|
|
128
|
+
child.on('close', (code) => {
|
|
129
|
+
if (code === 0) resolve();
|
|
130
|
+
else reject(new Error(`Process exited with code ${code}`));
|
|
131
|
+
});
|
|
132
|
+
child.on('error', reject);
|
|
133
|
+
});
|
|
134
|
+
spinner.succeed(chalk.green('ā All Spartan-NG UI components installed'));
|
|
135
|
+
} catch (error) {
|
|
136
|
+
spinner.fail(chalk.red('Failed to run Spartan-NG UI generator'));
|
|
137
|
+
console.error(chalk.red(error.message));
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Step 8: Download Spartan-NG examples
|
|
142
|
+
spinner.start('Downloading Spartan-NG examples...');
|
|
143
|
+
try {
|
|
144
|
+
const examplesEmitter = degit(`${REPO}/templates/spartan-examples`, {
|
|
40
145
|
cache: false,
|
|
41
146
|
force: true,
|
|
42
147
|
verbose: false,
|
|
43
148
|
});
|
|
149
|
+
await examplesEmitter.clone('libs/examples');
|
|
150
|
+
spinner.succeed(chalk.green('ā Downloaded Spartan-NG examples to libs/examples'));
|
|
151
|
+
} catch (error) {
|
|
152
|
+
spinner.warn(chalk.yellow('Could not download Spartan-NG examples'));
|
|
153
|
+
console.error(chalk.gray(error.message));
|
|
154
|
+
}
|
|
44
155
|
|
|
156
|
+
// Step 9: Create themes directory
|
|
157
|
+
spinner.start('Creating themes directory...');
|
|
158
|
+
try {
|
|
159
|
+
await fs.mkdir('src/themes', { recursive: true });
|
|
160
|
+
spinner.succeed(chalk.green('ā Created themes directory'));
|
|
161
|
+
} catch (error) {
|
|
162
|
+
spinner.warn(chalk.yellow('Themes directory may already exist'));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Step 10: Download theme
|
|
166
|
+
spinner.start(`Downloading ${theme.title} theme...`);
|
|
167
|
+
try {
|
|
168
|
+
const emitter = degit(`${REPO}/${theme.path}`, {
|
|
169
|
+
cache: false,
|
|
170
|
+
force: true,
|
|
171
|
+
verbose: false,
|
|
172
|
+
});
|
|
45
173
|
await emitter.clone(theme.defaultOutput);
|
|
46
|
-
spinner.succeed(chalk.green(`ā Downloaded ${theme.title}`));
|
|
174
|
+
spinner.succeed(chalk.green(`ā Downloaded ${theme.title} theme`));
|
|
47
175
|
} catch (error) {
|
|
48
176
|
spinner.fail(chalk.red('Failed to download theme'));
|
|
49
177
|
console.error(chalk.red(error.message));
|
|
50
178
|
process.exit(1);
|
|
51
179
|
}
|
|
52
180
|
|
|
53
|
-
// Step
|
|
54
|
-
spinner.start('
|
|
181
|
+
// Step 11: Update styles.css
|
|
182
|
+
spinner.start('Updating src/styles.css...');
|
|
55
183
|
try {
|
|
56
184
|
const stylesPath = 'src/styles.css';
|
|
57
185
|
let stylesContent = '';
|
|
@@ -66,7 +194,6 @@ async function init(options) {
|
|
|
66
194
|
const themeImport = `@import './themes/${theme.file}';`;
|
|
67
195
|
|
|
68
196
|
if (!stylesContent.includes(themeImport)) {
|
|
69
|
-
// Add required imports if not present
|
|
70
197
|
const requiredImports = [
|
|
71
198
|
'@import "@angular/cdk/overlay-prebuilt.css";',
|
|
72
199
|
'@import "tailwindcss";',
|
|
@@ -89,13 +216,17 @@ async function init(options) {
|
|
|
89
216
|
|
|
90
217
|
// Success message
|
|
91
218
|
console.log(chalk.bold.green('\n⨠GRG Kit initialized successfully!\n'));
|
|
92
|
-
|
|
93
|
-
console.log(chalk.
|
|
219
|
+
|
|
220
|
+
console.log(chalk.bold('Installed:'));
|
|
221
|
+
console.log(chalk.gray(' Tailwind CSS:'), chalk.cyan('v4 with PostCSS'));
|
|
222
|
+
console.log(chalk.gray(' Spartan-NG:'), chalk.cyan('All UI components in libs/ui'));
|
|
223
|
+
console.log(chalk.gray(' Examples:'), chalk.cyan('56+ component examples in libs/examples'));
|
|
224
|
+
console.log(chalk.gray(' Theme:'), chalk.cyan(theme.title));
|
|
94
225
|
|
|
95
226
|
console.log(chalk.yellow('\nNext steps:'));
|
|
96
|
-
console.log(chalk.gray(' 1.
|
|
97
|
-
console.log(chalk.gray(' 2.
|
|
98
|
-
console.log(chalk.gray(' 3. Add
|
|
227
|
+
console.log(chalk.gray(' 1.'), chalk.cyan(`cd ${projectName}`));
|
|
228
|
+
console.log(chalk.gray(' 2. Run'), chalk.cyan('grg list blocks'), chalk.gray('to see available blocks'));
|
|
229
|
+
console.log(chalk.gray(' 3. Add blocks with'), chalk.cyan('grg add block --auth'));
|
|
99
230
|
console.log();
|
|
100
231
|
}
|
|
101
232
|
|
package/commands/list.js
CHANGED
|
@@ -2,91 +2,59 @@ const chalk = require('chalk');
|
|
|
2
2
|
const { RESOURCES } = require('../config/resources');
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* List command - displays available
|
|
5
|
+
* List command - displays available blocks and themes
|
|
6
6
|
* Usage: grg list [category]
|
|
7
7
|
*/
|
|
8
8
|
async function list(category) {
|
|
9
9
|
if (!category) {
|
|
10
|
-
// Show
|
|
10
|
+
// Show overview
|
|
11
11
|
console.log(chalk.bold.cyan('\nš¦ GRG Kit Resources\n'));
|
|
12
12
|
|
|
13
|
+
console.log(chalk.bold('Blocks') + chalk.gray(` (${RESOURCES.blocks.length} available)`));
|
|
14
|
+
console.log(chalk.gray(' Add with: grg add block --<name>'));
|
|
15
|
+
console.log(chalk.gray(' Run: grg list blocks\n'));
|
|
16
|
+
|
|
13
17
|
console.log(chalk.bold('Themes') + chalk.gray(` (${RESOURCES.themes.length} available)`));
|
|
18
|
+
console.log(chalk.gray(' Set with: grg init --theme <name>'));
|
|
14
19
|
console.log(chalk.gray(' Run: grg list themes\n'));
|
|
15
20
|
|
|
16
|
-
console.log(chalk.
|
|
17
|
-
console.log(
|
|
18
|
-
|
|
19
|
-
console.log(chalk.bold('Layouts') + chalk.gray(` (${RESOURCES.layouts.length} available)`));
|
|
20
|
-
console.log(chalk.gray(' Run: grg list layouts\n'));
|
|
21
|
-
|
|
22
|
-
console.log(chalk.bold('Spartan-NG Examples') + chalk.gray(` (${RESOURCES.examples.components.length}+ available)`));
|
|
23
|
-
console.log(chalk.gray(' Run: grg list examples\n'));
|
|
21
|
+
console.log(chalk.gray('Components and spartan-ng examples are installed automatically with'), chalk.cyan('grg init'));
|
|
22
|
+
console.log();
|
|
24
23
|
|
|
25
24
|
return;
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
switch (category) {
|
|
28
|
+
case 'blocks':
|
|
29
|
+
console.log(chalk.bold.cyan('\nš§± Available Blocks\n'));
|
|
30
|
+
RESOURCES.blocks.forEach(block => {
|
|
31
|
+
console.log(chalk.bold(` ${block.name}`));
|
|
32
|
+
console.log(chalk.gray(` ${block.description}`));
|
|
33
|
+
console.log(chalk.yellow(` grg add block --${block.name}`));
|
|
34
|
+
if (block.tags && block.tags.length > 0) {
|
|
35
|
+
console.log(chalk.gray(` Tags: ${block.tags.join(', ')}`));
|
|
36
|
+
}
|
|
37
|
+
console.log();
|
|
38
|
+
});
|
|
39
|
+
break;
|
|
40
|
+
|
|
29
41
|
case 'themes':
|
|
30
42
|
console.log(chalk.bold.cyan('\nšØ Available Themes\n'));
|
|
43
|
+
console.log(chalk.gray(' Use with: grg init --theme <name>\n'));
|
|
31
44
|
RESOURCES.themes.forEach(theme => {
|
|
32
45
|
console.log(chalk.bold(` ${theme.name}`));
|
|
33
46
|
console.log(chalk.gray(` ${theme.description}`));
|
|
34
|
-
console.log(chalk.yellow(` grg
|
|
35
|
-
if (theme.tags.length > 0) {
|
|
47
|
+
console.log(chalk.yellow(` grg init --theme ${theme.name}`));
|
|
48
|
+
if (theme.tags && theme.tags.length > 0) {
|
|
36
49
|
console.log(chalk.gray(` Tags: ${theme.tags.join(', ')}`));
|
|
37
50
|
}
|
|
38
51
|
console.log();
|
|
39
52
|
});
|
|
40
53
|
break;
|
|
41
54
|
|
|
42
|
-
case 'components':
|
|
43
|
-
console.log(chalk.bold.cyan('\nš§© Available Components\n'));
|
|
44
|
-
RESOURCES.components.forEach(component => {
|
|
45
|
-
console.log(chalk.bold(` ${component.name}`));
|
|
46
|
-
console.log(chalk.gray(` ${component.description}`));
|
|
47
|
-
console.log(chalk.yellow(` grg add component:${component.name}`));
|
|
48
|
-
if (component.tags.length > 0) {
|
|
49
|
-
console.log(chalk.gray(` Tags: ${component.tags.join(', ')}`));
|
|
50
|
-
}
|
|
51
|
-
console.log();
|
|
52
|
-
});
|
|
53
|
-
break;
|
|
54
|
-
|
|
55
|
-
case 'layouts':
|
|
56
|
-
console.log(chalk.bold.cyan('\nš Available Layouts\n'));
|
|
57
|
-
RESOURCES.layouts.forEach(layout => {
|
|
58
|
-
console.log(chalk.bold(` ${layout.name}`));
|
|
59
|
-
console.log(chalk.gray(` ${layout.description}`));
|
|
60
|
-
console.log(chalk.yellow(` grg add layout:${layout.name}`));
|
|
61
|
-
if (layout.tags.length > 0) {
|
|
62
|
-
console.log(chalk.gray(` Tags: ${layout.tags.join(', ')}`));
|
|
63
|
-
}
|
|
64
|
-
console.log();
|
|
65
|
-
});
|
|
66
|
-
break;
|
|
67
|
-
|
|
68
|
-
case 'examples':
|
|
69
|
-
console.log(chalk.bold.cyan('\nš Available Spartan-NG Examples\n'));
|
|
70
|
-
|
|
71
|
-
// Show "all" option first
|
|
72
|
-
console.log(chalk.bold(` all`) + chalk.gray(' (recommended)'));
|
|
73
|
-
console.log(chalk.gray(` ${RESOURCES.examples.all.description}`));
|
|
74
|
-
console.log(chalk.yellow(` grg add examples:all`));
|
|
75
|
-
console.log();
|
|
76
|
-
|
|
77
|
-
console.log(chalk.gray(' Or add individual component examples:\n'));
|
|
78
|
-
|
|
79
|
-
RESOURCES.examples.components.forEach(example => {
|
|
80
|
-
console.log(chalk.bold(` ${example.name}`));
|
|
81
|
-
console.log(chalk.gray(` ${example.description}`));
|
|
82
|
-
console.log(chalk.yellow(` grg add examples:${example.name}`));
|
|
83
|
-
console.log();
|
|
84
|
-
});
|
|
85
|
-
break;
|
|
86
|
-
|
|
87
55
|
default:
|
|
88
56
|
console.error(chalk.red(`Error: Unknown category "${category}"`));
|
|
89
|
-
console.log(chalk.yellow('Valid categories:
|
|
57
|
+
console.log(chalk.yellow('Valid categories: blocks, themes'));
|
|
90
58
|
process.exit(1);
|
|
91
59
|
}
|
|
92
60
|
}
|