stackkit-cli 0.4.1 → 0.4.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 +17 -10
- package/bin/stackkit.js +1 -1
- package/dist/commands/add.js +26 -24
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +34 -29
- package/dist/commands/list.js +20 -17
- package/dist/index.js +25 -23
- package/dist/types/index.d.ts +14 -14
- package/dist/utils/code-inject.d.ts +1 -1
- package/dist/utils/code-inject.js +6 -6
- package/dist/utils/detect.d.ts +1 -1
- package/dist/utils/detect.js +48 -44
- package/dist/utils/env-editor.js +20 -20
- package/dist/utils/files.js +4 -4
- package/dist/utils/json-editor.d.ts +3 -3
- package/dist/utils/json-editor.js +10 -14
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/logger.js +8 -8
- package/dist/utils/package-manager.d.ts +2 -2
- package/dist/utils/package-manager.js +33 -26
- package/modules/auth/better-auth-express/adapters/mongoose-mongodb.ts +13 -0
- package/modules/auth/better-auth-express/adapters/prisma-mongodb.ts +15 -0
- package/modules/auth/better-auth-express/adapters/prisma-postgresql.ts +15 -0
- package/modules/auth/better-auth-express/files/lib/auth.ts +1 -1
- package/modules/auth/better-auth-express/files/routes/auth.ts +3 -3
- package/modules/auth/better-auth-express/files/schemas/prisma-mongodb-schema.prisma +72 -0
- package/modules/auth/better-auth-express/files/schemas/prisma-postgresql-schema.prisma +72 -0
- package/modules/auth/better-auth-express/module.json +26 -3
- package/modules/auth/better-auth-nextjs/adapters/mongoose-mongodb.ts +24 -0
- package/modules/auth/better-auth-nextjs/adapters/prisma-mongodb.ts +26 -0
- package/modules/auth/better-auth-nextjs/adapters/prisma-postgresql.ts +26 -0
- package/modules/auth/better-auth-nextjs/files/api/auth/[...all]/route.ts +2 -3
- package/modules/auth/better-auth-nextjs/files/lib/auth.ts +4 -4
- package/modules/auth/better-auth-nextjs/files/schemas/prisma-mongodb-schema.prisma +72 -0
- package/modules/auth/better-auth-nextjs/files/schemas/prisma-postgresql-schema.prisma +72 -0
- package/modules/auth/better-auth-nextjs/module.json +26 -5
- package/modules/auth/better-auth-react/files/lib/auth-client.ts +2 -2
- package/modules/auth/better-auth-react/module.json +7 -5
- package/modules/auth/clerk-express/files/lib/auth.ts +1 -1
- package/modules/auth/clerk-express/module.json +23 -8
- package/modules/auth/clerk-nextjs/files/lib/auth-provider.tsx +1 -1
- package/modules/auth/clerk-nextjs/files/middleware.ts +3 -3
- package/modules/auth/clerk-nextjs/module.json +51 -14
- package/modules/auth/clerk-react/files/lib/auth-provider.tsx +2 -2
- package/modules/auth/clerk-react/module.json +17 -7
- package/modules/database/mongoose-mongodb/files/lib/db.ts +3 -3
- package/modules/database/mongoose-mongodb/module.json +44 -6
- package/modules/database/prisma-mongodb/files/lib/db.ts +2 -2
- package/modules/database/prisma-mongodb/files/prisma/schema.prisma +1 -1
- package/modules/database/prisma-mongodb/module.json +28 -4
- package/modules/database/prisma-postgresql/files/lib/db.ts +2 -2
- package/modules/database/prisma-postgresql/files/prisma/schema.prisma +1 -1
- package/modules/database/prisma-postgresql/module.json +28 -4
- package/package.json +1 -1
- package/templates/express/.env.example +11 -0
- package/templates/express/eslint.config.cjs +42 -0
- package/templates/express/package.json +39 -0
- package/templates/express/src/app.ts +71 -0
- package/templates/express/src/config/env.ts +23 -0
- package/templates/express/src/middlewares/error.middleware.ts +18 -0
- package/templates/{bases/express-base → express}/src/server.ts +2 -2
- package/templates/express/template.json +44 -0
- package/templates/express/tsconfig.json +31 -0
- package/templates/{bases/nextjs-base → nextjs}/app/layout.tsx +1 -5
- package/templates/nextjs/app/page.tsx +57 -0
- package/templates/{bases/nextjs-base → nextjs}/package.json +2 -1
- package/templates/{bases/nextjs-base → nextjs}/template.json +13 -1
- package/templates/react-vite/.env.example +2 -0
- package/templates/react-vite/README.md +85 -0
- package/templates/react-vite/eslint.config.js +23 -0
- package/templates/{bases/react-vite-base → react-vite}/index.html +1 -0
- package/templates/{bases/react-vite-base → react-vite}/package.json +16 -2
- package/templates/react-vite/src/api/client.ts +47 -0
- package/templates/react-vite/src/api/services/user.service.ts +18 -0
- package/templates/react-vite/src/components/ErrorBoundary.tsx +51 -0
- package/templates/react-vite/src/components/Layout.tsx +13 -0
- package/templates/react-vite/src/components/Loading.tsx +8 -0
- package/templates/react-vite/src/components/SEO.tsx +49 -0
- package/templates/react-vite/src/config/constants.ts +5 -0
- package/templates/react-vite/src/hooks/index.ts +64 -0
- package/templates/react-vite/src/index.css +1 -0
- package/templates/react-vite/src/lib/queryClient.ts +12 -0
- package/templates/react-vite/src/main.tsx +22 -0
- package/templates/react-vite/src/pages/About.tsx +78 -0
- package/templates/react-vite/src/pages/Home.tsx +49 -0
- package/templates/react-vite/src/pages/NotFound.tsx +24 -0
- package/templates/react-vite/src/pages/UserProfile.tsx +40 -0
- package/templates/react-vite/src/router.tsx +33 -0
- package/templates/react-vite/src/types/api.d.ts +20 -0
- package/templates/react-vite/src/types/user.d.ts +6 -0
- package/templates/react-vite/src/utils/helpers.ts +51 -0
- package/templates/react-vite/src/utils/storage.ts +35 -0
- package/templates/react-vite/src/vite-env.d.ts +11 -0
- package/templates/react-vite/template.json +46 -0
- package/templates/react-vite/tsconfig.json +4 -0
- package/templates/react-vite/vite.config.ts +13 -0
- package/modules/database/drizzle-postgresql/files/drizzle.config.ts +0 -10
- package/modules/database/drizzle-postgresql/files/lib/db.ts +0 -7
- package/modules/database/drizzle-postgresql/files/lib/schema.ts +0 -8
- package/modules/database/drizzle-postgresql/module.json +0 -34
- package/templates/bases/express-base/.env.example +0 -2
- package/templates/bases/express-base/package.json +0 -23
- package/templates/bases/express-base/src/app.ts +0 -34
- package/templates/bases/express-base/src/config/env.ts +0 -14
- package/templates/bases/express-base/src/middlewares/error.middleware.ts +0 -12
- package/templates/bases/express-base/template.json +0 -7
- package/templates/bases/express-base/tsconfig.json +0 -14
- package/templates/bases/nextjs-base/app/page.tsx +0 -65
- package/templates/bases/react-vite-base/README.md +0 -73
- package/templates/bases/react-vite-base/eslint.config.js +0 -23
- package/templates/bases/react-vite-base/src/App.css +0 -42
- package/templates/bases/react-vite-base/src/App.tsx +0 -35
- package/templates/bases/react-vite-base/src/index.css +0 -68
- package/templates/bases/react-vite-base/src/main.tsx +0 -10
- package/templates/bases/react-vite-base/template.json +0 -19
- package/templates/bases/react-vite-base/tsconfig.json +0 -7
- package/templates/bases/react-vite-base/vite.config.ts +0 -7
- /package/templates/{bases/nextjs-base → nextjs}/README.md +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/app/favicon.ico +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/app/globals.css +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/eslint.config.mjs +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/next.config.ts +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/postcss.config.mjs +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/public/file.svg +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/public/globe.svg +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/public/next.svg +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/public/vercel.svg +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/public/window.svg +0 -0
- /package/templates/{bases/nextjs-base → nextjs}/tsconfig.json +0 -0
- /package/templates/{bases/react-vite-base → react-vite}/public/vite.svg +0 -0
- /package/templates/{bases/react-vite-base → react-vite}/src/assets/react.svg +0 -0
- /package/templates/{bases/react-vite-base → react-vite}/tsconfig.app.json +0 -0
- /package/templates/{bases/react-vite-base → react-vite}/tsconfig.node.json +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# stackkit-cli
|
|
2
2
|
|
|
3
|
-
Add modules to existing projects.
|
|
3
|
+
Add authentication and database modules to existing projects.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
@@ -15,18 +15,25 @@ npx stackkit-cli add database
|
|
|
15
15
|
npx stackkit-cli list
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## Features
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
- **Auto-detects** your framework (Next.js, Express, React)
|
|
21
|
+
- **Shows compatible modules** for your project
|
|
22
|
+
- **Installs dependencies** automatically
|
|
23
|
+
- **Configures everything** - files, env vars, and setup
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
List all available modules.
|
|
25
|
+
## Available Modules
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
### Authentication
|
|
28
|
+
|
|
29
|
+
- Better Auth (Next.js, Express, React)
|
|
30
|
+
- Clerk (Next.js, Express, React)
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
### Database
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
- Prisma with PostgreSQL or MongoDB (Next.js, Express)
|
|
35
|
+
- Mongoose with MongoDB (Next.js, Express)
|
|
36
|
+
|
|
37
|
+
## Documentation
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
Full documentation: [stackkit.dev](https://stackkit.dev) | [GitHub](https://github.com/tariqul420/stackkit)
|
package/bin/stackkit.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
require(
|
|
2
|
+
require("../dist/index.js");
|
package/dist/commands/add.js
CHANGED
|
@@ -17,11 +17,11 @@ async function addCommand(module, options) {
|
|
|
17
17
|
try {
|
|
18
18
|
const projectRoot = process.cwd();
|
|
19
19
|
// Detect project info
|
|
20
|
-
const spinner = logger_1.logger.startSpinner(
|
|
20
|
+
const spinner = logger_1.logger.startSpinner("Detecting project...");
|
|
21
21
|
const projectInfo = await (0, detect_1.detectProjectInfo)(projectRoot);
|
|
22
22
|
spinner.succeed(`Detected ${projectInfo.framework} (${projectInfo.router} router, ${projectInfo.language})`);
|
|
23
23
|
// Load module metadata
|
|
24
|
-
const modulesDir = path_1.default.join(__dirname,
|
|
24
|
+
const modulesDir = path_1.default.join(__dirname, "..", "..", "modules");
|
|
25
25
|
const moduleMetadata = await loadModuleMetadata(modulesDir, module, options.provider);
|
|
26
26
|
if (!moduleMetadata) {
|
|
27
27
|
logger_1.logger.error(`Module "${module}" not found`);
|
|
@@ -29,27 +29,27 @@ async function addCommand(module, options) {
|
|
|
29
29
|
}
|
|
30
30
|
// Check if framework is supported
|
|
31
31
|
if (!moduleMetadata.supportedFrameworks.includes(projectInfo.framework)) {
|
|
32
|
-
logger_1.logger.error(`Module "${module}" does not support ${projectInfo.framework}. Supported: ${moduleMetadata.supportedFrameworks.join(
|
|
32
|
+
logger_1.logger.error(`Module "${module}" does not support ${projectInfo.framework}. Supported: ${moduleMetadata.supportedFrameworks.join(", ")}`);
|
|
33
33
|
process.exit(1);
|
|
34
34
|
}
|
|
35
35
|
// Check for conflicts
|
|
36
|
-
if (module ===
|
|
37
|
-
logger_1.logger.warn(
|
|
36
|
+
if (module === "auth" && projectInfo.hasAuth && !options.force) {
|
|
37
|
+
logger_1.logger.warn("Auth library already detected in this project");
|
|
38
38
|
const { proceed } = await inquirer_1.default.prompt([
|
|
39
39
|
{
|
|
40
|
-
type:
|
|
41
|
-
name:
|
|
42
|
-
message:
|
|
40
|
+
type: "confirm",
|
|
41
|
+
name: "proceed",
|
|
42
|
+
message: "Continue anyway? (use --force to skip this prompt)",
|
|
43
43
|
default: false,
|
|
44
44
|
},
|
|
45
45
|
]);
|
|
46
46
|
if (!proceed) {
|
|
47
|
-
logger_1.logger.info(
|
|
47
|
+
logger_1.logger.info("Cancelled");
|
|
48
48
|
process.exit(0);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
if (options.dryRun) {
|
|
52
|
-
logger_1.logger.warn(
|
|
52
|
+
logger_1.logger.warn("Dry run mode - no changes will be made");
|
|
53
53
|
logger_1.logger.newLine();
|
|
54
54
|
}
|
|
55
55
|
// Apply module patches
|
|
@@ -61,7 +61,7 @@ async function addCommand(module, options) {
|
|
|
61
61
|
await (0, package_manager_1.addDependencies)(projectRoot, projectInfo.packageManager, deps, false);
|
|
62
62
|
}
|
|
63
63
|
else {
|
|
64
|
-
logger_1.logger.info(`Would add dependencies: ${deps.join(
|
|
64
|
+
logger_1.logger.info(`Would add dependencies: ${deps.join(", ")}`);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
// Add dev dependencies
|
|
@@ -73,7 +73,7 @@ async function addCommand(module, options) {
|
|
|
73
73
|
await (0, package_manager_1.addDependencies)(projectRoot, projectInfo.packageManager, devDeps, true);
|
|
74
74
|
}
|
|
75
75
|
else {
|
|
76
|
-
logger_1.logger.info(`Would add dev dependencies: ${devDeps.join(
|
|
76
|
+
logger_1.logger.info(`Would add dev dependencies: ${devDeps.join(", ")}`);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
// Add environment variables
|
|
@@ -82,7 +82,7 @@ async function addCommand(module, options) {
|
|
|
82
82
|
await (0, env_editor_1.addEnvVariables)(projectRoot, moduleMetadata.envVars, { force: options.force });
|
|
83
83
|
}
|
|
84
84
|
else {
|
|
85
|
-
logger_1.logger.log(` ${chalk_1.default.dim(
|
|
85
|
+
logger_1.logger.log(` ${chalk_1.default.dim("~")} .env.example`);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
logger_1.logger.newLine();
|
|
@@ -90,7 +90,7 @@ async function addCommand(module, options) {
|
|
|
90
90
|
logger_1.logger.newLine();
|
|
91
91
|
// Print next steps
|
|
92
92
|
if (moduleMetadata.envVars.some((v) => v.required)) {
|
|
93
|
-
logger_1.logger.log(
|
|
93
|
+
logger_1.logger.log("Next: Fill in environment variables in .env");
|
|
94
94
|
}
|
|
95
95
|
logger_1.logger.newLine();
|
|
96
96
|
}
|
|
@@ -120,7 +120,7 @@ async function loadModuleMetadata(modulesDir, moduleName, provider) {
|
|
|
120
120
|
const moduleStat = await fs_extra_1.default.stat(modulePath);
|
|
121
121
|
if (!moduleStat.isDirectory())
|
|
122
122
|
continue;
|
|
123
|
-
const metadataPath = path_1.default.join(modulePath,
|
|
123
|
+
const metadataPath = path_1.default.join(modulePath, "module.json");
|
|
124
124
|
if (await fs_extra_1.default.pathExists(metadataPath)) {
|
|
125
125
|
const metadata = await fs_extra_1.default.readJSON(metadataPath);
|
|
126
126
|
// If provider is specified, match by directory name (exact match)
|
|
@@ -136,13 +136,15 @@ async function loadModuleMetadata(modulesDir, moduleName, provider) {
|
|
|
136
136
|
}
|
|
137
137
|
return null;
|
|
138
138
|
}
|
|
139
|
+
// (removed duplicate import)
|
|
139
140
|
async function applyModulePatches(projectRoot, projectInfo, moduleMetadata, modulesDir, moduleName, options) {
|
|
141
|
+
// Find the module path
|
|
140
142
|
const moduleBasePath = await findModulePath(modulesDir, moduleName, options.provider);
|
|
141
143
|
if (!moduleBasePath) {
|
|
142
|
-
throw new Error(
|
|
144
|
+
throw new Error("Module files not found");
|
|
143
145
|
}
|
|
144
146
|
for (const patch of moduleMetadata.patches) {
|
|
145
|
-
if (patch.type ===
|
|
147
|
+
if (patch.type === "create-file") {
|
|
146
148
|
const filePatch = patch;
|
|
147
149
|
// Check conditions
|
|
148
150
|
if (filePatch.condition) {
|
|
@@ -153,18 +155,18 @@ async function applyModulePatches(projectRoot, projectInfo, moduleMetadata, modu
|
|
|
153
155
|
continue; // Skip this patch
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
|
-
const sourceFile = path_1.default.join(moduleBasePath,
|
|
158
|
+
const sourceFile = path_1.default.join(moduleBasePath, "files", filePatch.source);
|
|
157
159
|
let destFile = path_1.default.join(projectRoot, filePatch.destination);
|
|
158
160
|
// Replace placeholders in destination
|
|
159
161
|
destFile = destFile
|
|
160
|
-
.replace(
|
|
161
|
-
.replace(
|
|
162
|
+
.replace("{{router}}", (0, detect_1.getRouterBasePath)(projectInfo))
|
|
163
|
+
.replace("{{lib}}", (0, detect_1.getLibPath)(projectInfo));
|
|
162
164
|
if (!options.dryRun) {
|
|
163
165
|
if (await (0, files_1.fileExists)(sourceFile)) {
|
|
164
|
-
const content = await fs_extra_1.default.readFile(sourceFile,
|
|
166
|
+
const content = await fs_extra_1.default.readFile(sourceFile, "utf-8");
|
|
165
167
|
await (0, files_1.createFile)(destFile, content, { force: options.force });
|
|
166
168
|
const relativePath = path_1.default.relative(projectRoot, destFile);
|
|
167
|
-
logger_1.logger.log(` ${chalk_1.default.green(
|
|
169
|
+
logger_1.logger.log(` ${chalk_1.default.green("+")} ${relativePath}`);
|
|
168
170
|
}
|
|
169
171
|
else {
|
|
170
172
|
logger_1.logger.warn(`Source file not found: ${filePatch.source}`);
|
|
@@ -172,7 +174,7 @@ async function applyModulePatches(projectRoot, projectInfo, moduleMetadata, modu
|
|
|
172
174
|
}
|
|
173
175
|
else {
|
|
174
176
|
const relativePath = path_1.default.relative(projectRoot, destFile);
|
|
175
|
-
logger_1.logger.log(` ${chalk_1.default.dim(
|
|
177
|
+
logger_1.logger.log(` ${chalk_1.default.dim("+")} ${relativePath}`);
|
|
176
178
|
}
|
|
177
179
|
}
|
|
178
180
|
}
|
|
@@ -190,7 +192,7 @@ async function findModulePath(modulesDir, moduleName, provider) {
|
|
|
190
192
|
const moduleStat = await fs_extra_1.default.stat(modulePath);
|
|
191
193
|
if (!moduleStat.isDirectory())
|
|
192
194
|
continue;
|
|
193
|
-
const metadataPath = path_1.default.join(modulePath,
|
|
195
|
+
const metadataPath = path_1.default.join(modulePath, "module.json");
|
|
194
196
|
if (await fs_extra_1.default.pathExists(metadataPath)) {
|
|
195
197
|
const metadata = await fs_extra_1.default.readJSON(metadataPath);
|
|
196
198
|
// If provider is specified, match by directory name (exact match)
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -15,24 +15,24 @@ const package_manager_1 = require("../utils/package-manager");
|
|
|
15
15
|
async function initCommand(projectName, options) {
|
|
16
16
|
try {
|
|
17
17
|
// Validate package manager option
|
|
18
|
-
if (options.pm && ![
|
|
19
|
-
logger_1.logger.error(`Invalid package manager: ${options.pm}. Use npm, yarn, or
|
|
18
|
+
if (options.pm && !["npm", "yarn", "pnpm", "bun"].includes(options.pm)) {
|
|
19
|
+
logger_1.logger.error(`Invalid package manager: ${options.pm}. Use npm, yarn, pnpm, or bun.`);
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
22
22
|
// Get available templates
|
|
23
|
-
const templatesDir = path_1.default.join(__dirname,
|
|
23
|
+
const templatesDir = path_1.default.join(__dirname, "..", "..", "templates");
|
|
24
24
|
const templates = await getAvailableTemplates(templatesDir);
|
|
25
25
|
if (templates.length === 0) {
|
|
26
|
-
logger_1.logger.error(
|
|
26
|
+
logger_1.logger.error("No templates found");
|
|
27
27
|
process.exit(1);
|
|
28
28
|
}
|
|
29
29
|
// Prompt for project details if not using --yes
|
|
30
30
|
let answers;
|
|
31
31
|
if (options.yes) {
|
|
32
32
|
answers = {
|
|
33
|
-
projectName: projectName ||
|
|
33
|
+
projectName: projectName || "my-app",
|
|
34
34
|
template: options.template || templates[0].name,
|
|
35
|
-
packageManager: options.pm ||
|
|
35
|
+
packageManager: options.pm || "pnpm",
|
|
36
36
|
install: options.install !== false,
|
|
37
37
|
git: options.git !== false,
|
|
38
38
|
};
|
|
@@ -40,23 +40,23 @@ async function initCommand(projectName, options) {
|
|
|
40
40
|
else {
|
|
41
41
|
const prompted = await inquirer_1.default.prompt([
|
|
42
42
|
{
|
|
43
|
-
type:
|
|
44
|
-
name:
|
|
45
|
-
message:
|
|
46
|
-
default: projectName ||
|
|
43
|
+
type: "input",
|
|
44
|
+
name: "projectName",
|
|
45
|
+
message: "Project name:",
|
|
46
|
+
default: projectName || "my-app",
|
|
47
47
|
when: !projectName,
|
|
48
48
|
validate: (input) => {
|
|
49
49
|
const validation = (0, validate_npm_package_name_1.default)(input);
|
|
50
50
|
if (!validation.validForNewPackages) {
|
|
51
|
-
return validation.errors?.[0] ||
|
|
51
|
+
return validation.errors?.[0] || "Invalid package name";
|
|
52
52
|
}
|
|
53
53
|
return true;
|
|
54
54
|
},
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
|
-
type:
|
|
58
|
-
name:
|
|
59
|
-
message:
|
|
57
|
+
type: "list",
|
|
58
|
+
name: "template",
|
|
59
|
+
message: "Select a template:",
|
|
60
60
|
choices: templates.map((t) => ({
|
|
61
61
|
name: t.displayName,
|
|
62
62
|
value: t.name,
|
|
@@ -64,24 +64,29 @@ async function initCommand(projectName, options) {
|
|
|
64
64
|
when: !options.template,
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
|
-
type:
|
|
68
|
-
name:
|
|
69
|
-
message:
|
|
70
|
-
choices: [
|
|
71
|
-
|
|
67
|
+
type: "list",
|
|
68
|
+
name: "packageManager",
|
|
69
|
+
message: "Select a package manager:",
|
|
70
|
+
choices: [
|
|
71
|
+
{ name: "pnpm (recommended)", value: "pnpm" },
|
|
72
|
+
{ name: "npm", value: "npm" },
|
|
73
|
+
{ name: "yarn", value: "yarn" },
|
|
74
|
+
{ name: "bun", value: "bun" },
|
|
75
|
+
],
|
|
76
|
+
default: "pnpm",
|
|
72
77
|
when: !options.pm,
|
|
73
78
|
},
|
|
74
79
|
{
|
|
75
|
-
type:
|
|
76
|
-
name:
|
|
77
|
-
message:
|
|
80
|
+
type: "confirm",
|
|
81
|
+
name: "install",
|
|
82
|
+
message: "Install dependencies?",
|
|
78
83
|
default: true,
|
|
79
84
|
when: options.install !== false,
|
|
80
85
|
},
|
|
81
86
|
{
|
|
82
|
-
type:
|
|
83
|
-
name:
|
|
84
|
-
message:
|
|
87
|
+
type: "confirm",
|
|
88
|
+
name: "git",
|
|
89
|
+
message: "Initialize git repository?",
|
|
85
90
|
default: true,
|
|
86
91
|
when: options.git !== false,
|
|
87
92
|
},
|
|
@@ -98,7 +103,7 @@ async function initCommand(projectName, options) {
|
|
|
98
103
|
// Check if directory exists
|
|
99
104
|
if (await fs_extra_1.default.pathExists(targetDir)) {
|
|
100
105
|
logger_1.logger.error(`Directory "${answers.projectName}" already exists`);
|
|
101
|
-
logger_1.logger.info(
|
|
106
|
+
logger_1.logger.info("Please choose a different name or remove the existing directory.");
|
|
102
107
|
process.exit(1);
|
|
103
108
|
}
|
|
104
109
|
// Validate template exists
|
|
@@ -123,11 +128,11 @@ async function initCommand(projectName, options) {
|
|
|
123
128
|
logger_1.logger.success(`Created ${chalk_1.default.bold(answers.projectName)}`);
|
|
124
129
|
logger_1.logger.newLine();
|
|
125
130
|
logger_1.logger.log(`Next steps:`);
|
|
126
|
-
logger_1.logger.log(` ${chalk_1.default.cyan(
|
|
131
|
+
logger_1.logger.log(` ${chalk_1.default.cyan("cd")} ${answers.projectName}`);
|
|
127
132
|
if (!answers.install) {
|
|
128
133
|
logger_1.logger.log(` ${chalk_1.default.cyan(answers.packageManager)} install`);
|
|
129
134
|
}
|
|
130
|
-
logger_1.logger.log(` ${chalk_1.default.cyan(answers.packageManager)} ${answers.packageManager ===
|
|
135
|
+
logger_1.logger.log(` ${chalk_1.default.cyan(answers.packageManager)} ${answers.packageManager === "npm" ? "run " : ""}dev`);
|
|
131
136
|
logger_1.logger.newLine();
|
|
132
137
|
}
|
|
133
138
|
catch (error) {
|
|
@@ -142,7 +147,7 @@ async function getAvailableTemplates(templatesDir) {
|
|
|
142
147
|
const templateDirs = await fs_extra_1.default.readdir(templatesDir);
|
|
143
148
|
const templates = [];
|
|
144
149
|
for (const dir of templateDirs) {
|
|
145
|
-
const metadataPath = path_1.default.join(templatesDir, dir,
|
|
150
|
+
const metadataPath = path_1.default.join(templatesDir, dir, "template.json");
|
|
146
151
|
if (await fs_extra_1.default.pathExists(metadataPath)) {
|
|
147
152
|
const metadata = await fs_extra_1.default.readJSON(metadataPath);
|
|
148
153
|
templates.push(metadata);
|
package/dist/commands/list.js
CHANGED
|
@@ -12,30 +12,32 @@ async function listCommand(options) {
|
|
|
12
12
|
const showTemplates = !options.modules || options.templates;
|
|
13
13
|
const showModules = !options.templates || options.modules;
|
|
14
14
|
try {
|
|
15
|
+
logger_1.logger.newLine();
|
|
15
16
|
// List templates
|
|
16
17
|
if (showTemplates) {
|
|
17
|
-
const templatesDir = path_1.default.join(__dirname,
|
|
18
|
+
const templatesDir = path_1.default.join(__dirname, "..", "..", "templates");
|
|
18
19
|
const templates = await getAvailableTemplates(templatesDir);
|
|
19
|
-
logger_1.logger.log(chalk_1.default.bold(
|
|
20
|
+
logger_1.logger.log(chalk_1.default.bold.cyan("▸ TEMPLATES") + chalk_1.default.gray(` (${templates.length})`));
|
|
20
21
|
logger_1.logger.newLine();
|
|
21
22
|
if (templates.length === 0) {
|
|
22
|
-
logger_1.logger.
|
|
23
|
+
logger_1.logger.log(chalk_1.default.dim(" No templates available"));
|
|
23
24
|
}
|
|
24
25
|
else {
|
|
25
|
-
|
|
26
|
-
logger_1.logger.log(` ${chalk_1.default.cyan(
|
|
27
|
-
}
|
|
28
|
-
logger_1.logger.newLine();
|
|
26
|
+
templates.forEach((template) => {
|
|
27
|
+
logger_1.logger.log(` ${chalk_1.default.cyan("•")} ${template.displayName}`);
|
|
28
|
+
});
|
|
29
29
|
}
|
|
30
|
+
logger_1.logger.newLine();
|
|
30
31
|
}
|
|
31
32
|
// List modules
|
|
32
33
|
if (showModules) {
|
|
33
|
-
const modulesDir = path_1.default.join(__dirname,
|
|
34
|
+
const modulesDir = path_1.default.join(__dirname, "..", "..", "modules");
|
|
34
35
|
const modules = await getAvailableModules(modulesDir);
|
|
35
|
-
logger_1.logger.log(chalk_1.default.bold(
|
|
36
|
-
logger_1.logger.newLine();
|
|
36
|
+
logger_1.logger.log(chalk_1.default.bold.cyan("▸ MODULES") + chalk_1.default.gray(` (${modules.length})`));
|
|
37
37
|
if (modules.length === 0) {
|
|
38
|
-
logger_1.logger.
|
|
38
|
+
logger_1.logger.newLine();
|
|
39
|
+
logger_1.logger.log(chalk_1.default.dim(" No modules available"));
|
|
40
|
+
logger_1.logger.newLine();
|
|
39
41
|
}
|
|
40
42
|
else {
|
|
41
43
|
// Group by category
|
|
@@ -47,10 +49,11 @@ async function listCommand(options) {
|
|
|
47
49
|
return acc;
|
|
48
50
|
}, {});
|
|
49
51
|
for (const [category, mods] of Object.entries(grouped)) {
|
|
50
|
-
logger_1.logger.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
logger_1.logger.newLine();
|
|
53
|
+
logger_1.logger.log(` ${chalk_1.default.yellow("→")} ${chalk_1.default.bold.yellow(category.toUpperCase())} ${chalk_1.default.dim(`(${mods.length})`)}`);
|
|
54
|
+
mods.forEach((mod) => {
|
|
55
|
+
logger_1.logger.log(` ${chalk_1.default.cyan("•")} ${mod.displayName}`);
|
|
56
|
+
});
|
|
54
57
|
}
|
|
55
58
|
logger_1.logger.newLine();
|
|
56
59
|
}
|
|
@@ -68,7 +71,7 @@ async function getAvailableTemplates(templatesDir) {
|
|
|
68
71
|
const templateDirs = await fs_extra_1.default.readdir(templatesDir);
|
|
69
72
|
const templates = [];
|
|
70
73
|
for (const dir of templateDirs) {
|
|
71
|
-
const metadataPath = path_1.default.join(templatesDir, dir,
|
|
74
|
+
const metadataPath = path_1.default.join(templatesDir, dir, "template.json");
|
|
72
75
|
if (await fs_extra_1.default.pathExists(metadataPath)) {
|
|
73
76
|
const metadata = await fs_extra_1.default.readJSON(metadataPath);
|
|
74
77
|
templates.push(metadata);
|
|
@@ -89,7 +92,7 @@ async function getAvailableModules(modulesDir) {
|
|
|
89
92
|
continue;
|
|
90
93
|
const moduleDirs = await fs_extra_1.default.readdir(categoryPath);
|
|
91
94
|
for (const moduleDir of moduleDirs) {
|
|
92
|
-
const metadataPath = path_1.default.join(categoryPath, moduleDir,
|
|
95
|
+
const metadataPath = path_1.default.join(categoryPath, moduleDir, "module.json");
|
|
93
96
|
if (await fs_extra_1.default.pathExists(metadataPath)) {
|
|
94
97
|
const metadata = await fs_extra_1.default.readJSON(metadataPath);
|
|
95
98
|
modules.push(metadata);
|
package/dist/index.js
CHANGED
|
@@ -11,39 +11,41 @@ const init_1 = require("./commands/init");
|
|
|
11
11
|
const list_1 = require("./commands/list");
|
|
12
12
|
const program = new commander_1.Command();
|
|
13
13
|
program
|
|
14
|
-
.name(
|
|
15
|
-
.description(
|
|
16
|
-
.version(
|
|
14
|
+
.name("stackkit")
|
|
15
|
+
.description("Production-ready project generator and module CLI")
|
|
16
|
+
.version("0.3.2");
|
|
17
17
|
// Init command
|
|
18
18
|
program
|
|
19
|
-
.command(
|
|
20
|
-
.description(
|
|
21
|
-
.option(
|
|
22
|
-
.option(
|
|
23
|
-
.option(
|
|
24
|
-
.option(
|
|
25
|
-
.option(
|
|
19
|
+
.command("init [project-name]")
|
|
20
|
+
.description("Create a new project from a template")
|
|
21
|
+
.option("-t, --template <template>", "Template to use")
|
|
22
|
+
.option("--pm <pm>", "Package manager to use (npm, yarn, pnpm, bun)")
|
|
23
|
+
.option("--no-install", "Skip installing dependencies")
|
|
24
|
+
.option("--no-git", "Skip git initialization")
|
|
25
|
+
.option("-y, --yes", "Skip prompts and use defaults")
|
|
26
26
|
.action(init_1.initCommand);
|
|
27
27
|
// List command
|
|
28
28
|
program
|
|
29
|
-
.command(
|
|
30
|
-
.description(
|
|
31
|
-
.option(
|
|
32
|
-
.option(
|
|
29
|
+
.command("list")
|
|
30
|
+
.description("List available templates and modules")
|
|
31
|
+
.option("-t, --templates", "List only templates")
|
|
32
|
+
.option("-m, --modules", "List only modules")
|
|
33
33
|
.action(list_1.listCommand);
|
|
34
34
|
// Add command
|
|
35
35
|
program
|
|
36
|
-
.command(
|
|
37
|
-
.description(
|
|
38
|
-
.option(
|
|
39
|
-
.option(
|
|
40
|
-
.option(
|
|
41
|
-
.option(
|
|
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
42
|
.action(add_1.addCommand);
|
|
43
43
|
// Error handling
|
|
44
|
-
program.on(
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
program.on("command:*", () => {
|
|
45
|
+
// Use logger for error and info
|
|
46
|
+
// logger.error and logger.log are not available here since logger is not imported, so fallback to process.stderr
|
|
47
|
+
process.stderr.write(chalk_1.default.red(`\nInvalid command: ${program.args.join(" ")}\n`));
|
|
48
|
+
process.stderr.write(chalk_1.default.yellow("Run stackkit --help for a list of available commands.\n"));
|
|
47
49
|
process.exit(1);
|
|
48
50
|
});
|
|
49
51
|
program.parse();
|
package/dist/types/index.d.ts
CHANGED
|
@@ -3,14 +3,14 @@ export interface TemplateMetadata {
|
|
|
3
3
|
displayName: string;
|
|
4
4
|
description: string;
|
|
5
5
|
tags: string[];
|
|
6
|
-
defaultPackageManager:
|
|
6
|
+
defaultPackageManager: "pnpm" | "npm" | "yarn" | "bun";
|
|
7
7
|
features: string[];
|
|
8
8
|
}
|
|
9
9
|
export interface ModuleMetadata {
|
|
10
10
|
name: string;
|
|
11
11
|
displayName: string;
|
|
12
12
|
description: string;
|
|
13
|
-
category:
|
|
13
|
+
category: "auth" | "database" | "ui" | "other";
|
|
14
14
|
supportedFrameworks: string[];
|
|
15
15
|
dependencies: Record<string, string>;
|
|
16
16
|
devDependencies?: Record<string, string>;
|
|
@@ -24,33 +24,33 @@ export interface EnvVar {
|
|
|
24
24
|
required: boolean;
|
|
25
25
|
}
|
|
26
26
|
export interface ModulePatch {
|
|
27
|
-
type:
|
|
27
|
+
type: "create-file" | "modify-json" | "append-env" | "inject-code";
|
|
28
28
|
description: string;
|
|
29
|
-
[key: string]:
|
|
29
|
+
[key: string]: unknown;
|
|
30
30
|
}
|
|
31
31
|
export interface CreateFilePatch extends ModulePatch {
|
|
32
|
-
type:
|
|
32
|
+
type: "create-file";
|
|
33
33
|
source: string;
|
|
34
34
|
destination: string;
|
|
35
35
|
condition?: {
|
|
36
|
-
router?:
|
|
37
|
-
language?:
|
|
36
|
+
router?: "app" | "pages";
|
|
37
|
+
language?: "ts" | "js";
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
export interface ModifyJsonPatch extends ModulePatch {
|
|
41
|
-
type:
|
|
41
|
+
type: "modify-json";
|
|
42
42
|
file: string;
|
|
43
43
|
operations: {
|
|
44
44
|
path: string;
|
|
45
|
-
value:
|
|
45
|
+
value: unknown;
|
|
46
46
|
merge?: boolean;
|
|
47
47
|
}[];
|
|
48
48
|
}
|
|
49
49
|
export interface ProjectInfo {
|
|
50
|
-
framework:
|
|
51
|
-
router:
|
|
52
|
-
language:
|
|
53
|
-
packageManager:
|
|
50
|
+
framework: "nextjs" | "express" | "react" | "react-vite" | "unknown";
|
|
51
|
+
router: "app" | "pages" | "unknown";
|
|
52
|
+
language: "ts" | "js";
|
|
53
|
+
packageManager: "npm" | "yarn" | "pnpm" | "bun";
|
|
54
54
|
hasAuth: boolean;
|
|
55
55
|
hasPrisma: boolean;
|
|
56
56
|
hasDatabase: boolean;
|
|
@@ -61,5 +61,5 @@ export interface CLIOptions {
|
|
|
61
61
|
dryRun?: boolean;
|
|
62
62
|
yes?: boolean;
|
|
63
63
|
noInstall?: boolean;
|
|
64
|
-
pm?:
|
|
64
|
+
pm?: "npm" | "yarn" | "pnpm" | "bun";
|
|
65
65
|
}
|
|
@@ -3,7 +3,7 @@ export interface CodeInjection {
|
|
|
3
3
|
code: string;
|
|
4
4
|
description: string;
|
|
5
5
|
}
|
|
6
|
-
export declare function injectCode(filePath: string, injection: CodeInjection, position:
|
|
6
|
+
export declare function injectCode(filePath: string, injection: CodeInjection, position: "append" | "prepend" | {
|
|
7
7
|
after: string;
|
|
8
8
|
} | {
|
|
9
9
|
before: string;
|
|
@@ -13,7 +13,7 @@ async function injectCode(filePath, injection, position, options = {}) {
|
|
|
13
13
|
if (!(await fs_extra_1.default.pathExists(filePath))) {
|
|
14
14
|
throw new Error(`File not found: ${filePath}`);
|
|
15
15
|
}
|
|
16
|
-
let content = await fs_extra_1.default.readFile(filePath,
|
|
16
|
+
let content = await fs_extra_1.default.readFile(filePath, "utf-8");
|
|
17
17
|
// Check if already injected
|
|
18
18
|
const startMarker = CODE_MARKER_START(injection.id);
|
|
19
19
|
if (content.includes(startMarker) && !options.force) {
|
|
@@ -26,13 +26,13 @@ async function injectCode(filePath, injection, position, options = {}) {
|
|
|
26
26
|
// Prepare the code block with markers
|
|
27
27
|
const markedCode = `\n${startMarker}\n${injection.code}\n${CODE_MARKER_END(injection.id)}\n`;
|
|
28
28
|
// Inject based on position
|
|
29
|
-
if (position ===
|
|
29
|
+
if (position === "append") {
|
|
30
30
|
content += markedCode;
|
|
31
31
|
}
|
|
32
|
-
else if (position ===
|
|
32
|
+
else if (position === "prepend") {
|
|
33
33
|
content = markedCode + content;
|
|
34
34
|
}
|
|
35
|
-
else if (
|
|
35
|
+
else if ("after" in position) {
|
|
36
36
|
const index = content.indexOf(position.after);
|
|
37
37
|
if (index === -1) {
|
|
38
38
|
throw new Error(`Could not find marker: ${position.after}`);
|
|
@@ -40,14 +40,14 @@ async function injectCode(filePath, injection, position, options = {}) {
|
|
|
40
40
|
const insertPos = index + position.after.length;
|
|
41
41
|
content = content.slice(0, insertPos) + markedCode + content.slice(insertPos);
|
|
42
42
|
}
|
|
43
|
-
else if (
|
|
43
|
+
else if ("before" in position) {
|
|
44
44
|
const index = content.indexOf(position.before);
|
|
45
45
|
if (index === -1) {
|
|
46
46
|
throw new Error(`Could not find marker: ${position.before}`);
|
|
47
47
|
}
|
|
48
48
|
content = content.slice(0, index) + markedCode + content.slice(index);
|
|
49
49
|
}
|
|
50
|
-
await fs_extra_1.default.writeFile(filePath, content,
|
|
50
|
+
await fs_extra_1.default.writeFile(filePath, content, "utf-8");
|
|
51
51
|
}
|
|
52
52
|
function removeInjection(content, id) {
|
|
53
53
|
const startMarker = CODE_MARKER_START(id);
|
package/dist/utils/detect.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ProjectInfo } from
|
|
1
|
+
import { ProjectInfo } from "../types";
|
|
2
2
|
export declare function detectProjectInfo(targetDir: string): Promise<ProjectInfo>;
|
|
3
3
|
export declare function getRouterBasePath(projectInfo: ProjectInfo): string;
|
|
4
4
|
export declare function getLibPath(projectInfo: ProjectInfo): string;
|