stackkit-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/dist/commands/add.js +1 -1
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.js +2 -2
- package/dist/commands/list.js.map +1 -1
- package/modules/auth/nextauth/files/app-router/api/auth/[...nextauth]/route.ts +6 -0
- package/modules/auth/nextauth/files/lib/auth.ts +82 -0
- package/modules/auth/nextauth/files/pages-router/api/auth/[...nextauth].ts +4 -0
- package/modules/auth/nextauth/module.json +50 -0
- package/package.json +7 -1
- package/templates/next-prisma-postgres-shadcn/.env.example +5 -0
- package/templates/next-prisma-postgres-shadcn/.eslintrc.json +7 -0
- package/templates/next-prisma-postgres-shadcn/.prettierrc +8 -0
- package/templates/next-prisma-postgres-shadcn/README.md +79 -0
- package/templates/next-prisma-postgres-shadcn/app/api/health/route.ts +25 -0
- package/templates/next-prisma-postgres-shadcn/app/globals.css +1 -0
- package/templates/next-prisma-postgres-shadcn/app/layout.tsx +22 -0
- package/templates/next-prisma-postgres-shadcn/app/page.tsx +29 -0
- package/templates/next-prisma-postgres-shadcn/lib/db.ts +14 -0
- package/templates/next-prisma-postgres-shadcn/lib/env.ts +15 -0
- package/templates/next-prisma-postgres-shadcn/next.config.ts +7 -0
- package/templates/next-prisma-postgres-shadcn/package.json +32 -0
- package/templates/next-prisma-postgres-shadcn/prisma/schema.prisma +20 -0
- package/templates/next-prisma-postgres-shadcn/public/.gitkeep +1 -0
- package/templates/next-prisma-postgres-shadcn/template.json +18 -0
- package/templates/next-prisma-postgres-shadcn/tsconfig.json +32 -0
- package/src/commands/add.ts +0 -261
- package/src/commands/init.ts +0 -182
- package/src/commands/list.ts +0 -124
- package/src/index.ts +0 -53
- package/src/types/index.ts +0 -71
- package/src/utils/code-inject.ts +0 -85
- package/src/utils/detect.ts +0 -89
- package/src/utils/env-editor.ts +0 -127
- package/src/utils/files.ts +0 -59
- package/src/utils/json-editor.ts +0 -64
- package/src/utils/logger.ts +0 -62
- package/src/utils/package-manager.ts +0 -85
- package/tsconfig.json +0 -9
package/src/commands/add.ts
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { CreateFilePatch, ModuleMetadata } from '../types';
|
|
6
|
-
import { detectProjectInfo, getLibPath, getRouterBasePath } from '../utils/detect';
|
|
7
|
-
import { addEnvVariables } from '../utils/env-editor';
|
|
8
|
-
import { createFile, fileExists } from '../utils/files';
|
|
9
|
-
import { logger } from '../utils/logger';
|
|
10
|
-
import { addDependencies } from '../utils/package-manager';
|
|
11
|
-
|
|
12
|
-
interface AddOptions {
|
|
13
|
-
provider?: string;
|
|
14
|
-
force?: boolean;
|
|
15
|
-
dryRun?: boolean;
|
|
16
|
-
install?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function addCommand(module: string, options: AddOptions): Promise<void> {
|
|
20
|
-
try {
|
|
21
|
-
const projectRoot = process.cwd();
|
|
22
|
-
|
|
23
|
-
// Detect project info
|
|
24
|
-
const spinner = logger.startSpinner('Detecting project...');
|
|
25
|
-
const projectInfo = await detectProjectInfo(projectRoot);
|
|
26
|
-
spinner.succeed(
|
|
27
|
-
`Detected ${projectInfo.framework} (${projectInfo.router} router, ${projectInfo.language})`
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
// Load module metadata
|
|
31
|
-
const modulesDir = path.join(__dirname, '..', '..', '..', '..', 'modules');
|
|
32
|
-
const moduleMetadata = await loadModuleMetadata(modulesDir, module, options.provider);
|
|
33
|
-
|
|
34
|
-
if (!moduleMetadata) {
|
|
35
|
-
logger.error(`Module "${module}" not found`);
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Check if framework is supported
|
|
40
|
-
if (!moduleMetadata.supportedFrameworks.includes(projectInfo.framework)) {
|
|
41
|
-
logger.error(
|
|
42
|
-
`Module "${module}" does not support ${projectInfo.framework}. Supported: ${moduleMetadata.supportedFrameworks.join(', ')}`
|
|
43
|
-
);
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Check for conflicts
|
|
48
|
-
if (module === 'auth' && projectInfo.hasAuth && !options.force) {
|
|
49
|
-
logger.warn('Auth library already detected in this project');
|
|
50
|
-
const { proceed } = await inquirer.prompt([
|
|
51
|
-
{
|
|
52
|
-
type: 'confirm',
|
|
53
|
-
name: 'proceed',
|
|
54
|
-
message: 'Continue anyway? (use --force to skip this prompt)',
|
|
55
|
-
default: false,
|
|
56
|
-
},
|
|
57
|
-
]);
|
|
58
|
-
|
|
59
|
-
if (!proceed) {
|
|
60
|
-
logger.info('Cancelled');
|
|
61
|
-
process.exit(0);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (options.dryRun) {
|
|
66
|
-
logger.warn('Dry run mode - no changes will be made');
|
|
67
|
-
logger.newLine();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Apply module patches
|
|
71
|
-
await applyModulePatches(projectRoot, projectInfo, moduleMetadata, modulesDir, module, options);
|
|
72
|
-
|
|
73
|
-
// Add dependencies
|
|
74
|
-
if (Object.keys(moduleMetadata.dependencies).length > 0 && options.install !== false) {
|
|
75
|
-
const deps = Object.entries(moduleMetadata.dependencies).map(
|
|
76
|
-
([name, version]) => `${name}@${version}`
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
if (!options.dryRun) {
|
|
80
|
-
await addDependencies(projectRoot, projectInfo.packageManager, deps, false);
|
|
81
|
-
} else {
|
|
82
|
-
logger.info(`Would add dependencies: ${deps.join(', ')}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Add dev dependencies
|
|
87
|
-
if (
|
|
88
|
-
moduleMetadata.devDependencies &&
|
|
89
|
-
Object.keys(moduleMetadata.devDependencies).length > 0 &&
|
|
90
|
-
options.install !== false
|
|
91
|
-
) {
|
|
92
|
-
const devDeps = Object.entries(moduleMetadata.devDependencies).map(
|
|
93
|
-
([name, version]) => `${name}@${version}`
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
if (!options.dryRun) {
|
|
97
|
-
await addDependencies(projectRoot, projectInfo.packageManager, devDeps, true);
|
|
98
|
-
} else {
|
|
99
|
-
logger.info(`Would add dev dependencies: ${devDeps.join(', ')}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Add environment variables
|
|
104
|
-
if (moduleMetadata.envVars.length > 0) {
|
|
105
|
-
if (!options.dryRun) {
|
|
106
|
-
await addEnvVariables(projectRoot, moduleMetadata.envVars, { force: options.force });
|
|
107
|
-
} else {
|
|
108
|
-
logger.log(` ${chalk.dim('~')} .env.example`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
logger.newLine();
|
|
113
|
-
logger.success(`Added ${chalk.bold(moduleMetadata.displayName)}`);
|
|
114
|
-
logger.newLine();
|
|
115
|
-
|
|
116
|
-
// Print next steps
|
|
117
|
-
if (moduleMetadata.envVars.some((v) => v.required)) {
|
|
118
|
-
logger.log('Next: Fill in environment variables in .env');
|
|
119
|
-
}
|
|
120
|
-
logger.newLine();
|
|
121
|
-
} catch (error) {
|
|
122
|
-
logger.error(`Failed to add module: ${(error as Error).message}`);
|
|
123
|
-
if (error instanceof Error && error.stack) {
|
|
124
|
-
logger.log(chalk.gray(error.stack));
|
|
125
|
-
}
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function loadModuleMetadata(
|
|
131
|
-
modulesDir: string,
|
|
132
|
-
moduleName: string,
|
|
133
|
-
provider?: string
|
|
134
|
-
): Promise<ModuleMetadata | null> {
|
|
135
|
-
if (!(await fs.pathExists(modulesDir))) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Try to find module in any category
|
|
140
|
-
const categories = await fs.readdir(modulesDir);
|
|
141
|
-
|
|
142
|
-
for (const category of categories) {
|
|
143
|
-
const categoryPath = path.join(modulesDir, category);
|
|
144
|
-
const stat = await fs.stat(categoryPath);
|
|
145
|
-
|
|
146
|
-
if (!stat.isDirectory()) continue;
|
|
147
|
-
|
|
148
|
-
// Get all modules in this category
|
|
149
|
-
const moduleDirs = await fs.readdir(categoryPath);
|
|
150
|
-
|
|
151
|
-
for (const moduleDir of moduleDirs) {
|
|
152
|
-
const modulePath = path.join(categoryPath, moduleDir);
|
|
153
|
-
const moduleStat = await fs.stat(modulePath);
|
|
154
|
-
|
|
155
|
-
if (!moduleStat.isDirectory()) continue;
|
|
156
|
-
|
|
157
|
-
const metadataPath = path.join(modulePath, 'module.json');
|
|
158
|
-
|
|
159
|
-
if (await fs.pathExists(metadataPath)) {
|
|
160
|
-
const metadata = await fs.readJSON(metadataPath);
|
|
161
|
-
|
|
162
|
-
// Match by module name or provider
|
|
163
|
-
if (metadata.name === moduleName || (provider && moduleDir === provider)) {
|
|
164
|
-
return metadata;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async function applyModulePatches(
|
|
174
|
-
projectRoot: string,
|
|
175
|
-
projectInfo: any,
|
|
176
|
-
moduleMetadata: ModuleMetadata,
|
|
177
|
-
modulesDir: string,
|
|
178
|
-
moduleName: string,
|
|
179
|
-
options: AddOptions
|
|
180
|
-
): Promise<void> {
|
|
181
|
-
const moduleBasePath = await findModulePath(modulesDir, moduleName, options.provider);
|
|
182
|
-
|
|
183
|
-
if (!moduleBasePath) {
|
|
184
|
-
throw new Error('Module files not found');
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
for (const patch of moduleMetadata.patches) {
|
|
188
|
-
if (patch.type === 'create-file') {
|
|
189
|
-
const filePatch = patch as CreateFilePatch;
|
|
190
|
-
|
|
191
|
-
// Check conditions
|
|
192
|
-
if (filePatch.condition) {
|
|
193
|
-
if (filePatch.condition.router && filePatch.condition.router !== projectInfo.router) {
|
|
194
|
-
continue; // Skip this patch
|
|
195
|
-
}
|
|
196
|
-
if (filePatch.condition.language && filePatch.condition.language !== projectInfo.language) {
|
|
197
|
-
continue; // Skip this patch
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const sourceFile = path.join(moduleBasePath, 'files', filePatch.source);
|
|
202
|
-
let destFile = path.join(projectRoot, filePatch.destination);
|
|
203
|
-
|
|
204
|
-
// Replace placeholders in destination
|
|
205
|
-
destFile = destFile
|
|
206
|
-
.replace('{{router}}', getRouterBasePath(projectInfo))
|
|
207
|
-
.replace('{{lib}}', getLibPath(projectInfo));
|
|
208
|
-
|
|
209
|
-
if (!options.dryRun) {
|
|
210
|
-
if (await fileExists(sourceFile)) {
|
|
211
|
-
const content = await fs.readFile(sourceFile, 'utf-8');
|
|
212
|
-
await createFile(destFile, content, { force: options.force });
|
|
213
|
-
const relativePath = path.relative(projectRoot, destFile);
|
|
214
|
-
logger.log(` ${chalk.green('+')} ${relativePath}`);
|
|
215
|
-
} else {
|
|
216
|
-
logger.warn(`Source file not found: ${filePatch.source}`);
|
|
217
|
-
}
|
|
218
|
-
} else {
|
|
219
|
-
const relativePath = path.relative(projectRoot, destFile);
|
|
220
|
-
logger.log(` ${chalk.dim('+')} ${relativePath}`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async function findModulePath(
|
|
227
|
-
modulesDir: string,
|
|
228
|
-
moduleName: string,
|
|
229
|
-
provider?: string
|
|
230
|
-
): Promise<string | null> {
|
|
231
|
-
const categories = await fs.readdir(modulesDir);
|
|
232
|
-
|
|
233
|
-
for (const category of categories) {
|
|
234
|
-
const categoryPath = path.join(modulesDir, category);
|
|
235
|
-
const stat = await fs.stat(categoryPath);
|
|
236
|
-
|
|
237
|
-
if (!stat.isDirectory()) continue;
|
|
238
|
-
|
|
239
|
-
const moduleDirs = await fs.readdir(categoryPath);
|
|
240
|
-
|
|
241
|
-
for (const moduleDir of moduleDirs) {
|
|
242
|
-
const modulePath = path.join(categoryPath, moduleDir);
|
|
243
|
-
const moduleStat = await fs.stat(modulePath);
|
|
244
|
-
|
|
245
|
-
if (!moduleStat.isDirectory()) continue;
|
|
246
|
-
|
|
247
|
-
const metadataPath = path.join(modulePath, 'module.json');
|
|
248
|
-
|
|
249
|
-
if (await fs.pathExists(metadataPath)) {
|
|
250
|
-
const metadata = await fs.readJSON(metadataPath);
|
|
251
|
-
|
|
252
|
-
// Match by module name or provider
|
|
253
|
-
if (metadata.name === moduleName || (provider && moduleDir === provider)) {
|
|
254
|
-
return modulePath;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return null;
|
|
261
|
-
}
|
package/src/commands/init.ts
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import validateNpmPackageName from 'validate-npm-package-name';
|
|
6
|
-
import { TemplateMetadata } from '../types';
|
|
7
|
-
import { copyTemplate } from '../utils/files';
|
|
8
|
-
import { logger } from '../utils/logger';
|
|
9
|
-
import { initGit, installDependencies, PackageManager } from '../utils/package-manager';
|
|
10
|
-
|
|
11
|
-
interface InitOptions {
|
|
12
|
-
template?: string;
|
|
13
|
-
pm?: PackageManager;
|
|
14
|
-
install?: boolean;
|
|
15
|
-
git?: boolean;
|
|
16
|
-
yes?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function initCommand(
|
|
20
|
-
projectName: string | undefined,
|
|
21
|
-
options: InitOptions
|
|
22
|
-
): Promise<void> {
|
|
23
|
-
try {
|
|
24
|
-
// Validate package manager option
|
|
25
|
-
if (options.pm && !['npm', 'yarn', 'pnpm'].includes(options.pm)) {
|
|
26
|
-
logger.error(`Invalid package manager: ${options.pm}. Use npm, yarn, or pnpm.`);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Get available templates
|
|
31
|
-
const templatesDir = path.join(__dirname, '..', '..', '..', '..', 'templates');
|
|
32
|
-
const templates = await getAvailableTemplates(templatesDir);
|
|
33
|
-
|
|
34
|
-
if (templates.length === 0) {
|
|
35
|
-
logger.error('No templates found');
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Prompt for project details if not using --yes
|
|
40
|
-
let answers: {
|
|
41
|
-
projectName: string;
|
|
42
|
-
template: string;
|
|
43
|
-
packageManager: PackageManager;
|
|
44
|
-
install: boolean;
|
|
45
|
-
git: boolean;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
if (options.yes) {
|
|
49
|
-
answers = {
|
|
50
|
-
projectName: projectName || 'my-app',
|
|
51
|
-
template: options.template || templates[0].name,
|
|
52
|
-
packageManager: options.pm || 'pnpm',
|
|
53
|
-
install: options.install !== false,
|
|
54
|
-
git: options.git !== false,
|
|
55
|
-
};
|
|
56
|
-
} else {
|
|
57
|
-
const prompted = await inquirer.prompt([
|
|
58
|
-
{
|
|
59
|
-
type: 'input',
|
|
60
|
-
name: 'projectName',
|
|
61
|
-
message: 'Project name:',
|
|
62
|
-
default: projectName || 'my-app',
|
|
63
|
-
when: !projectName,
|
|
64
|
-
validate: (input: string) => {
|
|
65
|
-
const validation = validateNpmPackageName(input);
|
|
66
|
-
if (!validation.validForNewPackages) {
|
|
67
|
-
return validation.errors?.[0] || 'Invalid package name';
|
|
68
|
-
}
|
|
69
|
-
return true;
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
type: 'list',
|
|
74
|
-
name: 'template',
|
|
75
|
-
message: 'Select a template:',
|
|
76
|
-
choices: templates.map((t) => ({
|
|
77
|
-
name: `${t.displayName} - ${t.description}`,
|
|
78
|
-
value: t.name,
|
|
79
|
-
})),
|
|
80
|
-
when: !options.template,
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
type: 'list',
|
|
84
|
-
name: 'packageManager',
|
|
85
|
-
message: 'Select a package manager:',
|
|
86
|
-
choices: ['pnpm', 'npm', 'yarn'],
|
|
87
|
-
default: 'pnpm',
|
|
88
|
-
when: !options.pm,
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
type: 'confirm',
|
|
92
|
-
name: 'install',
|
|
93
|
-
message: 'Install dependencies?',
|
|
94
|
-
default: true,
|
|
95
|
-
when: options.install !== false,
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
type: 'confirm',
|
|
99
|
-
name: 'git',
|
|
100
|
-
message: 'Initialize git repository?',
|
|
101
|
-
default: true,
|
|
102
|
-
when: options.git !== false,
|
|
103
|
-
},
|
|
104
|
-
]);
|
|
105
|
-
|
|
106
|
-
answers = {
|
|
107
|
-
projectName: projectName || prompted.projectName,
|
|
108
|
-
template: options.template || prompted.template,
|
|
109
|
-
packageManager: options.pm || prompted.packageManager,
|
|
110
|
-
install: options.install !== false && (prompted.install ?? true),
|
|
111
|
-
git: options.git !== false && (prompted.git ?? true),
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const targetDir = path.join(process.cwd(), answers.projectName);
|
|
116
|
-
|
|
117
|
-
// Check if directory exists
|
|
118
|
-
if (await fs.pathExists(targetDir)) {
|
|
119
|
-
logger.error(`Directory "${answers.projectName}" already exists`);
|
|
120
|
-
logger.info('Please choose a different name or remove the existing directory.');
|
|
121
|
-
process.exit(1);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Validate template exists
|
|
125
|
-
const selectedTemplate = templates.find((t) => t.name === answers.template);
|
|
126
|
-
if (!selectedTemplate) {
|
|
127
|
-
logger.error(`Template "${answers.template}" not found`);
|
|
128
|
-
process.exit(1);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
logger.newLine();
|
|
132
|
-
|
|
133
|
-
// Copy template
|
|
134
|
-
const templatePath = path.join(templatesDir, answers.template);
|
|
135
|
-
await copyTemplate(templatePath, targetDir, answers.projectName);
|
|
136
|
-
|
|
137
|
-
// Install dependencies
|
|
138
|
-
if (answers.install) {
|
|
139
|
-
await installDependencies(targetDir, answers.packageManager);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Initialize git
|
|
143
|
-
if (answers.git) {
|
|
144
|
-
await initGit(targetDir);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
logger.newLine();
|
|
148
|
-
logger.success(`Created ${chalk.bold(answers.projectName)}`);
|
|
149
|
-
logger.newLine();
|
|
150
|
-
logger.log(`Next steps:`);
|
|
151
|
-
logger.log(` ${chalk.cyan('cd')} ${answers.projectName}`);
|
|
152
|
-
if (!answers.install) {
|
|
153
|
-
logger.log(` ${chalk.cyan(answers.packageManager)} install`);
|
|
154
|
-
}
|
|
155
|
-
logger.log(
|
|
156
|
-
` ${chalk.cyan(answers.packageManager)} ${answers.packageManager === 'npm' ? 'run ' : ''}dev`
|
|
157
|
-
);
|
|
158
|
-
logger.newLine();
|
|
159
|
-
} catch (error) {
|
|
160
|
-
logger.error(`Failed to create project: ${(error as Error).message}`);
|
|
161
|
-
process.exit(1);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async function getAvailableTemplates(templatesDir: string): Promise<TemplateMetadata[]> {
|
|
166
|
-
if (!(await fs.pathExists(templatesDir))) {
|
|
167
|
-
return [];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const templateDirs = await fs.readdir(templatesDir);
|
|
171
|
-
const templates: TemplateMetadata[] = [];
|
|
172
|
-
|
|
173
|
-
for (const dir of templateDirs) {
|
|
174
|
-
const metadataPath = path.join(templatesDir, dir, 'template.json');
|
|
175
|
-
if (await fs.pathExists(metadataPath)) {
|
|
176
|
-
const metadata = await fs.readJSON(metadataPath);
|
|
177
|
-
templates.push(metadata);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return templates;
|
|
182
|
-
}
|
package/src/commands/list.ts
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { ModuleMetadata, TemplateMetadata } from '../types';
|
|
5
|
-
import { logger } from '../utils/logger';
|
|
6
|
-
|
|
7
|
-
interface ListOptions {
|
|
8
|
-
templates?: boolean;
|
|
9
|
-
modules?: boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export async function listCommand(options: ListOptions): Promise<void> {
|
|
13
|
-
const showTemplates = !options.modules || options.templates;
|
|
14
|
-
const showModules = !options.templates || options.modules;
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
// List templates
|
|
18
|
-
if (showTemplates) {
|
|
19
|
-
const templatesDir = path.join(__dirname, '..', '..', '..', '..', 'templates');
|
|
20
|
-
const templates = await getAvailableTemplates(templatesDir);
|
|
21
|
-
|
|
22
|
-
logger.log(chalk.bold('📦 Templates'));
|
|
23
|
-
logger.newLine();
|
|
24
|
-
|
|
25
|
-
if (templates.length === 0) {
|
|
26
|
-
logger.warn(' No templates found');
|
|
27
|
-
} else {
|
|
28
|
-
for (const template of templates) {
|
|
29
|
-
logger.log(chalk.bold(` ${template.displayName}`));
|
|
30
|
-
logger.log(chalk.gray(` ${template.description}`));
|
|
31
|
-
logger.log(chalk.blue(` Command: stackkit init --template ${template.name}`));
|
|
32
|
-
logger.log(chalk.gray(` Features: ${template.features.join(', ')}`));
|
|
33
|
-
logger.newLine();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// List modules
|
|
39
|
-
if (showModules) {
|
|
40
|
-
const modulesDir = path.join(__dirname, '..', '..', '..', '..', 'modules');
|
|
41
|
-
const modules = await getAvailableModules(modulesDir);
|
|
42
|
-
|
|
43
|
-
logger.log(chalk.bold('🔧 Modules'));
|
|
44
|
-
logger.newLine();
|
|
45
|
-
|
|
46
|
-
if (modules.length === 0) {
|
|
47
|
-
logger.warn(' No modules found');
|
|
48
|
-
} else {
|
|
49
|
-
// Group by category
|
|
50
|
-
const grouped = modules.reduce(
|
|
51
|
-
(acc, mod) => {
|
|
52
|
-
if (!acc[mod.category]) {
|
|
53
|
-
acc[mod.category] = [];
|
|
54
|
-
}
|
|
55
|
-
acc[mod.category].push(mod);
|
|
56
|
-
return acc;
|
|
57
|
-
},
|
|
58
|
-
{} as Record<string, ModuleMetadata[]>
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
for (const [category, mods] of Object.entries(grouped)) {
|
|
62
|
-
logger.log(chalk.yellow(` ${category.toUpperCase()}:`));
|
|
63
|
-
for (const mod of mods) {
|
|
64
|
-
logger.log(chalk.bold(` ${mod.displayName}`));
|
|
65
|
-
logger.log(chalk.gray(` ${mod.description}`));
|
|
66
|
-
logger.log(chalk.blue(` Command: stackkit add ${mod.name}`));
|
|
67
|
-
logger.log(chalk.gray(` Supports: ${mod.supportedFrameworks.join(', ')}`));
|
|
68
|
-
logger.newLine();
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
} catch (error) {
|
|
74
|
-
logger.error(`Failed to list resources: ${(error as Error).message}`);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async function getAvailableTemplates(templatesDir: string): Promise<TemplateMetadata[]> {
|
|
80
|
-
if (!(await fs.pathExists(templatesDir))) {
|
|
81
|
-
return [];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const templateDirs = await fs.readdir(templatesDir);
|
|
85
|
-
const templates: TemplateMetadata[] = [];
|
|
86
|
-
|
|
87
|
-
for (const dir of templateDirs) {
|
|
88
|
-
const metadataPath = path.join(templatesDir, dir, 'template.json');
|
|
89
|
-
if (await fs.pathExists(metadataPath)) {
|
|
90
|
-
const metadata = await fs.readJSON(metadataPath);
|
|
91
|
-
templates.push(metadata);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return templates;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async function getAvailableModules(modulesDir: string): Promise<ModuleMetadata[]> {
|
|
99
|
-
if (!(await fs.pathExists(modulesDir))) {
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const modules: ModuleMetadata[] = [];
|
|
104
|
-
const categories = await fs.readdir(modulesDir);
|
|
105
|
-
|
|
106
|
-
for (const category of categories) {
|
|
107
|
-
const categoryPath = path.join(modulesDir, category);
|
|
108
|
-
const stat = await fs.stat(categoryPath);
|
|
109
|
-
|
|
110
|
-
if (!stat.isDirectory()) continue;
|
|
111
|
-
|
|
112
|
-
const moduleDirs = await fs.readdir(categoryPath);
|
|
113
|
-
|
|
114
|
-
for (const moduleDir of moduleDirs) {
|
|
115
|
-
const metadataPath = path.join(categoryPath, moduleDir, 'module.json');
|
|
116
|
-
if (await fs.pathExists(metadataPath)) {
|
|
117
|
-
const metadata = await fs.readJSON(metadataPath);
|
|
118
|
-
modules.push(metadata);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return modules;
|
|
124
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { Command } from 'commander';
|
|
4
|
-
import { addCommand } from './commands/add';
|
|
5
|
-
import { initCommand } from './commands/init';
|
|
6
|
-
import { listCommand } from './commands/list';
|
|
7
|
-
|
|
8
|
-
const program = new Command();
|
|
9
|
-
|
|
10
|
-
program
|
|
11
|
-
.name('stackkit')
|
|
12
|
-
.description('Production-ready project generator and module CLI')
|
|
13
|
-
.version('0.1.0');
|
|
14
|
-
|
|
15
|
-
// Init command
|
|
16
|
-
program
|
|
17
|
-
.command('init [project-name]')
|
|
18
|
-
.description('Create a new project from a template')
|
|
19
|
-
.option('-t, --template <template>', 'Template to use')
|
|
20
|
-
.option('--pm <pm>', 'Package manager to use (npm, yarn, pnpm)')
|
|
21
|
-
.option('--no-install', 'Skip installing dependencies')
|
|
22
|
-
.option('--no-git', 'Skip git initialization')
|
|
23
|
-
.option('-y, --yes', 'Skip prompts and use defaults')
|
|
24
|
-
.action(initCommand);
|
|
25
|
-
|
|
26
|
-
// List command
|
|
27
|
-
program
|
|
28
|
-
.command('list')
|
|
29
|
-
.description('List available templates and modules')
|
|
30
|
-
.option('-t, --templates', 'List only templates')
|
|
31
|
-
.option('-m, --modules', 'List only modules')
|
|
32
|
-
.action(listCommand);
|
|
33
|
-
|
|
34
|
-
// Add command
|
|
35
|
-
program
|
|
36
|
-
.command('add <module>')
|
|
37
|
-
.description('Add a module to your existing project')
|
|
38
|
-
.option('--provider <provider>', 'Specific provider/variant to use')
|
|
39
|
-
.option('--force', 'Overwrite existing files')
|
|
40
|
-
.option('--dry-run', 'Show what would be changed without making changes')
|
|
41
|
-
.option('--no-install', 'Skip installing dependencies')
|
|
42
|
-
.action(addCommand);
|
|
43
|
-
|
|
44
|
-
// Error handling
|
|
45
|
-
program.on('command:*', () => {
|
|
46
|
-
console.error(
|
|
47
|
-
chalk.red(`\nInvalid command: ${program.args.join(' ')}\n`)
|
|
48
|
-
);
|
|
49
|
-
console.log(chalk.yellow('Run stackkit --help for a list of available commands.\n'));
|
|
50
|
-
process.exit(1);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
program.parse();
|
package/src/types/index.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
export interface TemplateMetadata {
|
|
2
|
-
name: string;
|
|
3
|
-
displayName: string;
|
|
4
|
-
description: string;
|
|
5
|
-
tags: string[];
|
|
6
|
-
defaultPackageManager: 'pnpm' | 'npm' | 'yarn';
|
|
7
|
-
features: string[];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ModuleMetadata {
|
|
11
|
-
name: string;
|
|
12
|
-
displayName: string;
|
|
13
|
-
description: string;
|
|
14
|
-
category: 'auth' | 'database' | 'ui' | 'other';
|
|
15
|
-
supportedFrameworks: string[];
|
|
16
|
-
dependencies: Record<string, string>;
|
|
17
|
-
devDependencies?: Record<string, string>;
|
|
18
|
-
envVars: EnvVar[];
|
|
19
|
-
patches: ModulePatch[];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface EnvVar {
|
|
23
|
-
key: string;
|
|
24
|
-
value?: string;
|
|
25
|
-
description: string;
|
|
26
|
-
required: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface ModulePatch {
|
|
30
|
-
type: 'create-file' | 'modify-json' | 'append-env' | 'inject-code';
|
|
31
|
-
description: string;
|
|
32
|
-
[key: string]: any;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface CreateFilePatch extends ModulePatch {
|
|
36
|
-
type: 'create-file';
|
|
37
|
-
source: string;
|
|
38
|
-
destination: string;
|
|
39
|
-
condition?: {
|
|
40
|
-
router?: 'app' | 'pages';
|
|
41
|
-
language?: 'ts' | 'js';
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface ModifyJsonPatch extends ModulePatch {
|
|
46
|
-
type: 'modify-json';
|
|
47
|
-
file: string;
|
|
48
|
-
operations: {
|
|
49
|
-
path: string;
|
|
50
|
-
value: any;
|
|
51
|
-
merge?: boolean;
|
|
52
|
-
}[];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface ProjectInfo {
|
|
56
|
-
framework: 'nextjs' | 'unknown';
|
|
57
|
-
router: 'app' | 'pages' | 'unknown';
|
|
58
|
-
language: 'ts' | 'js';
|
|
59
|
-
packageManager: 'npm' | 'yarn' | 'pnpm';
|
|
60
|
-
hasAuth: boolean;
|
|
61
|
-
hasPrisma: boolean;
|
|
62
|
-
rootDir: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface CLIOptions {
|
|
66
|
-
force?: boolean;
|
|
67
|
-
dryRun?: boolean;
|
|
68
|
-
yes?: boolean;
|
|
69
|
-
noInstall?: boolean;
|
|
70
|
-
pm?: 'npm' | 'yarn' | 'pnpm';
|
|
71
|
-
}
|