stackkit-cli 0.1.0
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 +119 -0
- package/bin/stackkit.js +2 -0
- package/dist/commands/add.d.ts +9 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +201 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +153 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +7 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +107 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +65 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/code-inject.d.ts +15 -0
- package/dist/utils/code-inject.d.ts.map +1 -0
- package/dist/utils/code-inject.js +71 -0
- package/dist/utils/code-inject.js.map +1 -0
- package/dist/utils/detect.d.ts +5 -0
- package/dist/utils/detect.d.ts.map +1 -0
- package/dist/utils/detect.js +79 -0
- package/dist/utils/detect.js.map +1 -0
- package/dist/utils/env-editor.d.ts +11 -0
- package/dist/utils/env-editor.d.ts.map +1 -0
- package/dist/utils/env-editor.js +92 -0
- package/dist/utils/env-editor.js.map +1 -0
- package/dist/utils/files.d.ts +7 -0
- package/dist/utils/files.d.ts.map +1 -0
- package/dist/utils/files.js +51 -0
- package/dist/utils/files.js.map +1 -0
- package/dist/utils/json-editor.d.ts +9 -0
- package/dist/utils/json-editor.d.ts.map +1 -0
- package/dist/utils/json-editor.js +50 -0
- package/dist/utils/json-editor.js.map +1 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +58 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/package-manager.d.ts +6 -0
- package/dist/utils/package-manager.d.ts.map +1 -0
- package/dist/utils/package-manager.js +79 -0
- package/dist/utils/package-manager.js.map +1 -0
- package/package.json +51 -0
- package/src/commands/add.ts +261 -0
- package/src/commands/init.ts +182 -0
- package/src/commands/list.ts +124 -0
- package/src/index.ts +53 -0
- package/src/types/index.ts +71 -0
- package/src/utils/code-inject.ts +85 -0
- package/src/utils/detect.ts +89 -0
- package/src/utils/env-editor.ts +127 -0
- package/src/utils/files.ts +59 -0
- package/src/utils/json-editor.ts +64 -0
- package/src/utils/logger.ts +62 -0
- package/src/utils/package-manager.ts +85 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logger = exports.Logger = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
class Logger {
|
|
10
|
+
spinner = null;
|
|
11
|
+
info(message) {
|
|
12
|
+
console.log(chalk_1.default.blue('ℹ'), message);
|
|
13
|
+
}
|
|
14
|
+
success(message) {
|
|
15
|
+
console.log(chalk_1.default.green('✔'), message);
|
|
16
|
+
}
|
|
17
|
+
error(message) {
|
|
18
|
+
console.log(chalk_1.default.red('✖'), message);
|
|
19
|
+
}
|
|
20
|
+
warn(message) {
|
|
21
|
+
console.log(chalk_1.default.yellow('⚠'), message);
|
|
22
|
+
}
|
|
23
|
+
log(message) {
|
|
24
|
+
console.log(message);
|
|
25
|
+
}
|
|
26
|
+
newLine() {
|
|
27
|
+
console.log();
|
|
28
|
+
}
|
|
29
|
+
startSpinner(text) {
|
|
30
|
+
this.spinner = (0, ora_1.default)(text).start();
|
|
31
|
+
return this.spinner;
|
|
32
|
+
}
|
|
33
|
+
stopSpinner(success = true, text) {
|
|
34
|
+
if (this.spinner) {
|
|
35
|
+
if (success) {
|
|
36
|
+
this.spinner.succeed(text);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.spinner.fail(text);
|
|
40
|
+
}
|
|
41
|
+
this.spinner = null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
updateSpinner(text) {
|
|
45
|
+
if (this.spinner) {
|
|
46
|
+
this.spinner.text = text;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
header(text) {
|
|
50
|
+
console.log(chalk_1.default.bold.cyan(text));
|
|
51
|
+
}
|
|
52
|
+
footer() {
|
|
53
|
+
console.log();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.Logger = Logger;
|
|
57
|
+
exports.logger = new Logger();
|
|
58
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA0B;AAC1B,8CAA+B;AAE/B,MAAa,MAAM;IACT,OAAO,GAAe,IAAI,CAAC;IAEnC,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,CAAC,OAAe;QACrB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,OAAe;QACnB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;IAED,OAAO;QACL,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;IAED,YAAY,CAAC,IAAY;QACvB,IAAI,CAAC,OAAO,GAAG,IAAA,aAAG,EAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,WAAW,CAAC,OAAO,GAAG,IAAI,EAAE,IAAa;QACvC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,OAAO,EAAE,CAAC;gBACZ,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;YACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IAED,aAAa,CAAC,IAAY;QACxB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,MAAM,CAAC,IAAY;QACjB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,MAAM;QACJ,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;CACF;AAxDD,wBAwDC;AAEY,QAAA,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type PackageManager = 'npm' | 'yarn' | 'pnpm';
|
|
2
|
+
export declare function detectPackageManager(cwd: string): Promise<PackageManager>;
|
|
3
|
+
export declare function installDependencies(cwd: string, pm: PackageManager, dev?: boolean): Promise<void>;
|
|
4
|
+
export declare function addDependencies(cwd: string, pm: PackageManager, packages: string[], dev?: boolean): Promise<void>;
|
|
5
|
+
export declare function initGit(cwd: string): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=package-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-manager.d.ts","sourceRoot":"","sources":["../../src/utils/package-manager.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAO/E;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,cAAc,EAClB,GAAG,UAAQ,GACV,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAAE,EAClB,GAAG,UAAQ,GACV,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxD"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.detectPackageManager = detectPackageManager;
|
|
7
|
+
exports.installDependencies = installDependencies;
|
|
8
|
+
exports.addDependencies = addDependencies;
|
|
9
|
+
exports.initGit = initGit;
|
|
10
|
+
const detect_package_manager_1 = require("detect-package-manager");
|
|
11
|
+
const execa_1 = __importDefault(require("execa"));
|
|
12
|
+
const logger_1 = require("./logger");
|
|
13
|
+
async function detectPackageManager(cwd) {
|
|
14
|
+
try {
|
|
15
|
+
const pm = await (0, detect_package_manager_1.detect)({ cwd });
|
|
16
|
+
return pm;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return 'npm';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function installDependencies(cwd, pm, dev = false) {
|
|
23
|
+
const spinner = logger_1.logger.startSpinner(`Installing dependencies with ${pm}...`);
|
|
24
|
+
try {
|
|
25
|
+
const args = [];
|
|
26
|
+
if (pm === 'npm') {
|
|
27
|
+
args.push('install');
|
|
28
|
+
}
|
|
29
|
+
else if (pm === 'yarn') {
|
|
30
|
+
args.push('install');
|
|
31
|
+
}
|
|
32
|
+
else if (pm === 'pnpm') {
|
|
33
|
+
args.push('install');
|
|
34
|
+
}
|
|
35
|
+
await (0, execa_1.default)(pm, args, { cwd, stdio: 'pipe' });
|
|
36
|
+
spinner.succeed(`Dependencies installed successfully`);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
spinner.fail(`Failed to install dependencies`);
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function addDependencies(cwd, pm, packages, dev = false) {
|
|
44
|
+
if (packages.length === 0)
|
|
45
|
+
return;
|
|
46
|
+
const spinner = logger_1.logger.startSpinner(`Adding ${dev ? 'dev ' : ''}dependencies: ${packages.join(', ')}...`);
|
|
47
|
+
try {
|
|
48
|
+
const args = [];
|
|
49
|
+
if (pm === 'npm') {
|
|
50
|
+
args.push('install', dev ? '--save-dev' : '--save', ...packages);
|
|
51
|
+
}
|
|
52
|
+
else if (pm === 'yarn') {
|
|
53
|
+
args.push('add', dev ? '--dev' : '', ...packages);
|
|
54
|
+
}
|
|
55
|
+
else if (pm === 'pnpm') {
|
|
56
|
+
args.push('add', dev ? '-D' : '', ...packages);
|
|
57
|
+
}
|
|
58
|
+
await (0, execa_1.default)(pm, args.filter(Boolean), { cwd, stdio: 'pipe' });
|
|
59
|
+
spinner.succeed(`Dependencies added successfully`);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
spinner.fail(`Failed to add dependencies`);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function initGit(cwd) {
|
|
67
|
+
const spinner = logger_1.logger.startSpinner('Initializing git repository...');
|
|
68
|
+
try {
|
|
69
|
+
await (0, execa_1.default)('git', ['init'], { cwd });
|
|
70
|
+
await (0, execa_1.default)('git', ['add', '.'], { cwd });
|
|
71
|
+
await (0, execa_1.default)('git', ['commit', '-m', 'Initial commit from StackKit'], { cwd });
|
|
72
|
+
spinner.succeed('Git repository initialized');
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
spinner.fail('Failed to initialize git repository');
|
|
76
|
+
// Don't throw - git init is optional
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=package-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-manager.js","sourceRoot":"","sources":["../../src/utils/package-manager.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stackkit-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for StackKit - Production-ready project generator and module system",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"stackkit": "./bin/stackkit.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "commonjs",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/tariqul420/stackkit.git",
|
|
13
|
+
"directory": "apps/stackkit-cli"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/tariqul420/stackkit#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/tariqul420/stackkit/issues"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "tsc --watch",
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"clean": "rm -rf dist",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"lint": "eslint src --ext .ts"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"cli",
|
|
28
|
+
"generator",
|
|
29
|
+
"nextjs",
|
|
30
|
+
"template"
|
|
31
|
+
],
|
|
32
|
+
"author": "Your Name",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"commander": "^12.0.0",
|
|
36
|
+
"inquirer": "^9.2.12",
|
|
37
|
+
"chalk": "^4.1.2",
|
|
38
|
+
"ora": "^5.4.1",
|
|
39
|
+
"fs-extra": "^11.2.0",
|
|
40
|
+
"execa": "^5.1.1",
|
|
41
|
+
"validate-npm-package-name": "^5.0.0",
|
|
42
|
+
"detect-package-manager": "^3.0.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/fs-extra": "^11.0.4",
|
|
46
|
+
"@types/inquirer": "^9.0.7",
|
|
47
|
+
"@types/node": "^20.11.0",
|
|
48
|
+
"@types/validate-npm-package-name": "^4.0.2",
|
|
49
|
+
"typescript": "^5.3.3"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,261 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
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
|
+
}
|