stackkit-cli 0.4.2 → 0.4.4

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.
Files changed (137) hide show
  1. package/README.md +17 -10
  2. package/bin/stackkit.js +10 -1
  3. package/dist/commands/add.js +164 -25
  4. package/dist/commands/init.d.ts +1 -1
  5. package/dist/commands/init.js +34 -29
  6. package/dist/commands/list.js +12 -12
  7. package/dist/index.js +25 -23
  8. package/dist/types/index.d.ts +28 -16
  9. package/dist/utils/code-inject.d.ts +1 -1
  10. package/dist/utils/code-inject.js +6 -6
  11. package/dist/utils/detect.d.ts +1 -1
  12. package/dist/utils/detect.js +48 -44
  13. package/dist/utils/env-editor.js +20 -20
  14. package/dist/utils/files.js +4 -4
  15. package/dist/utils/json-editor.d.ts +3 -3
  16. package/dist/utils/json-editor.js +10 -14
  17. package/dist/utils/logger.d.ts +1 -1
  18. package/dist/utils/logger.js +8 -8
  19. package/dist/utils/package-manager.d.ts +2 -2
  20. package/dist/utils/package-manager.js +33 -26
  21. package/modules/auth/better-auth/files/api/auth/[...all]/route.ts +4 -0
  22. package/modules/auth/better-auth/files/lib/auth.ts +13 -0
  23. package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +63 -0
  24. package/modules/auth/better-auth/module.json +54 -0
  25. package/modules/auth/{clerk-express/files/lib → clerk/files/express}/auth.ts +1 -1
  26. package/modules/auth/{clerk-nextjs/files/lib → clerk/files/nextjs}/auth-provider.tsx +1 -1
  27. package/modules/auth/clerk/files/nextjs/middleware.ts +9 -0
  28. package/modules/auth/{clerk-react/files/lib → clerk/files/react}/auth-provider.tsx +2 -2
  29. package/modules/auth/clerk/module.json +115 -0
  30. package/modules/database/mongoose-mongodb/files/lib/db.ts +45 -7
  31. package/modules/database/mongoose-mongodb/files/models/User.ts +39 -0
  32. package/modules/database/mongoose-mongodb/module.json +59 -7
  33. package/modules/database/prisma/files/lib/prisma.ts +6 -0
  34. package/modules/database/prisma/files/prisma/schema.prisma +8 -0
  35. package/modules/database/prisma/files/prisma.config.ts +12 -0
  36. package/modules/database/prisma/module.json +140 -0
  37. package/package.json +1 -1
  38. package/templates/express/.env.example +3 -0
  39. package/templates/express/eslint.config.cjs +42 -0
  40. package/templates/express/package.json +33 -0
  41. package/templates/express/src/app.ts +51 -0
  42. package/templates/express/src/config/env.ts +12 -0
  43. package/templates/express/src/features/auth/auth.controller.ts +48 -0
  44. package/templates/express/src/features/auth/auth.route.ts +10 -0
  45. package/templates/express/src/features/auth/auth.service.ts +21 -0
  46. package/templates/express/src/middlewares/error.middleware.ts +18 -0
  47. package/templates/{bases/express-base → express}/src/server.ts +3 -3
  48. package/templates/express/template.json +40 -0
  49. package/templates/express/tsconfig.json +30 -0
  50. package/templates/{bases/nextjs-base → nextjs}/app/layout.tsx +1 -5
  51. package/templates/nextjs/app/page.tsx +57 -0
  52. package/templates/{bases/nextjs-base → nextjs}/package.json +2 -1
  53. package/templates/{bases/nextjs-base → nextjs}/template.json +13 -1
  54. package/templates/react-vite/.env.example +2 -0
  55. package/templates/react-vite/README.md +85 -0
  56. package/templates/react-vite/eslint.config.js +23 -0
  57. package/templates/{bases/react-vite-base → react-vite}/index.html +1 -0
  58. package/templates/{bases/react-vite-base → react-vite}/package.json +16 -2
  59. package/templates/react-vite/src/api/client.ts +47 -0
  60. package/templates/react-vite/src/api/services/user.service.ts +18 -0
  61. package/templates/react-vite/src/components/ErrorBoundary.tsx +51 -0
  62. package/templates/react-vite/src/components/Layout.tsx +13 -0
  63. package/templates/react-vite/src/components/Loading.tsx +8 -0
  64. package/templates/react-vite/src/components/SEO.tsx +49 -0
  65. package/templates/react-vite/src/config/constants.ts +5 -0
  66. package/templates/react-vite/src/hooks/index.ts +64 -0
  67. package/templates/react-vite/src/index.css +1 -0
  68. package/templates/react-vite/src/lib/queryClient.ts +12 -0
  69. package/templates/react-vite/src/main.tsx +22 -0
  70. package/templates/react-vite/src/pages/About.tsx +78 -0
  71. package/templates/react-vite/src/pages/Home.tsx +49 -0
  72. package/templates/react-vite/src/pages/NotFound.tsx +24 -0
  73. package/templates/react-vite/src/pages/UserProfile.tsx +40 -0
  74. package/templates/react-vite/src/router.tsx +33 -0
  75. package/templates/react-vite/src/types/api.d.ts +20 -0
  76. package/templates/react-vite/src/types/user.d.ts +6 -0
  77. package/templates/react-vite/src/utils/helpers.ts +51 -0
  78. package/templates/react-vite/src/utils/storage.ts +35 -0
  79. package/templates/react-vite/src/vite-env.d.ts +11 -0
  80. package/templates/react-vite/template.json +46 -0
  81. package/templates/react-vite/tsconfig.json +4 -0
  82. package/templates/react-vite/vite.config.ts +13 -0
  83. package/modules/auth/better-auth-express/files/lib/auth.ts +0 -16
  84. package/modules/auth/better-auth-express/files/routes/auth.ts +0 -12
  85. package/modules/auth/better-auth-express/module.json +0 -38
  86. package/modules/auth/better-auth-nextjs/files/api/auth/[...all]/route.ts +0 -5
  87. package/modules/auth/better-auth-nextjs/files/lib/auth.ts +0 -26
  88. package/modules/auth/better-auth-nextjs/module.json +0 -41
  89. package/modules/auth/better-auth-react/files/lib/auth-client.ts +0 -9
  90. package/modules/auth/better-auth-react/module.json +0 -26
  91. package/modules/auth/clerk-express/module.json +0 -20
  92. package/modules/auth/clerk-nextjs/files/middleware.ts +0 -9
  93. package/modules/auth/clerk-nextjs/module.json +0 -28
  94. package/modules/auth/clerk-react/module.json +0 -19
  95. package/modules/database/drizzle-postgresql/files/drizzle.config.ts +0 -10
  96. package/modules/database/drizzle-postgresql/files/lib/db.ts +0 -7
  97. package/modules/database/drizzle-postgresql/files/lib/schema.ts +0 -8
  98. package/modules/database/drizzle-postgresql/module.json +0 -35
  99. package/modules/database/prisma-mongodb/files/lib/db.ts +0 -9
  100. package/modules/database/prisma-mongodb/files/prisma/schema.prisma +0 -17
  101. package/modules/database/prisma-mongodb/module.json +0 -36
  102. package/modules/database/prisma-postgresql/files/lib/db.ts +0 -9
  103. package/modules/database/prisma-postgresql/files/prisma/schema.prisma +0 -17
  104. package/modules/database/prisma-postgresql/module.json +0 -36
  105. package/templates/bases/express-base/.env.example +0 -2
  106. package/templates/bases/express-base/package.json +0 -23
  107. package/templates/bases/express-base/src/app.ts +0 -34
  108. package/templates/bases/express-base/src/config/env.ts +0 -14
  109. package/templates/bases/express-base/src/middlewares/error.middleware.ts +0 -12
  110. package/templates/bases/express-base/template.json +0 -7
  111. package/templates/bases/express-base/tsconfig.json +0 -14
  112. package/templates/bases/nextjs-base/app/page.tsx +0 -65
  113. package/templates/bases/react-vite-base/README.md +0 -73
  114. package/templates/bases/react-vite-base/eslint.config.js +0 -23
  115. package/templates/bases/react-vite-base/src/App.css +0 -42
  116. package/templates/bases/react-vite-base/src/App.tsx +0 -35
  117. package/templates/bases/react-vite-base/src/index.css +0 -68
  118. package/templates/bases/react-vite-base/src/main.tsx +0 -10
  119. package/templates/bases/react-vite-base/template.json +0 -19
  120. package/templates/bases/react-vite-base/tsconfig.json +0 -7
  121. package/templates/bases/react-vite-base/vite.config.ts +0 -7
  122. /package/templates/{bases/nextjs-base → nextjs}/README.md +0 -0
  123. /package/templates/{bases/nextjs-base → nextjs}/app/favicon.ico +0 -0
  124. /package/templates/{bases/nextjs-base → nextjs}/app/globals.css +0 -0
  125. /package/templates/{bases/nextjs-base → nextjs}/eslint.config.mjs +0 -0
  126. /package/templates/{bases/nextjs-base → nextjs}/next.config.ts +0 -0
  127. /package/templates/{bases/nextjs-base → nextjs}/postcss.config.mjs +0 -0
  128. /package/templates/{bases/nextjs-base → nextjs}/public/file.svg +0 -0
  129. /package/templates/{bases/nextjs-base → nextjs}/public/globe.svg +0 -0
  130. /package/templates/{bases/nextjs-base → nextjs}/public/next.svg +0 -0
  131. /package/templates/{bases/nextjs-base → nextjs}/public/vercel.svg +0 -0
  132. /package/templates/{bases/nextjs-base → nextjs}/public/window.svg +0 -0
  133. /package/templates/{bases/nextjs-base → nextjs}/tsconfig.json +0 -0
  134. /package/templates/{bases/react-vite-base → react-vite}/public/vite.svg +0 -0
  135. /package/templates/{bases/react-vite-base → react-vite}/src/assets/react.svg +0 -0
  136. /package/templates/{bases/react-vite-base → react-vite}/tsconfig.app.json +0 -0
  137. /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
- ## Available Commands
18
+ ## Features
19
19
 
20
- ### `add <module>`
21
- Add authentication or database module to your project. Interactive wizard guides you through options.
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
- ### `list`
24
- List all available modules.
25
+ ## Available Modules
25
26
 
26
- ## Documentation
27
+ ### Authentication
28
+
29
+ - Better Auth (Next.js, Express, React)
30
+ - Clerk (Next.js, Express, React)
27
31
 
28
- See [main repository](https://github.com/tariqul420/stackkit) for full documentation.
32
+ ### Database
29
33
 
30
- ## License
34
+ - Prisma with PostgreSQL or MongoDB (Next.js, Express)
35
+ - Mongoose with MongoDB (Next.js, Express)
36
+
37
+ ## Documentation
31
38
 
32
- MIT
39
+ Full documentation: [stackkit.dev](https://stackkit.dev) | [GitHub](https://github.com/tariqul420/stackkit)
package/bin/stackkit.js CHANGED
@@ -1,2 +1,11 @@
1
1
  #!/usr/bin/env node
2
- require('../dist/index.js');
2
+
3
+ try {
4
+ // eslint-disable-next-line
5
+ require("ts-node/register");
6
+ // eslint-disable-next-line
7
+ require("../src/index.ts");
8
+ } catch {
9
+ // eslint-disable-next-line
10
+ require("../dist/index.js");
11
+ }
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.addCommand = addCommand;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
+ const child_process_1 = require("child_process");
8
9
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
10
  const inquirer_1 = __importDefault(require("inquirer"));
10
11
  const path_1 = __importDefault(require("path"));
@@ -17,43 +18,136 @@ async function addCommand(module, options) {
17
18
  try {
18
19
  const projectRoot = process.cwd();
19
20
  // Detect project info
20
- const spinner = logger_1.logger.startSpinner('Detecting project...');
21
+ const spinner = logger_1.logger.startSpinner("Detecting project...");
21
22
  const projectInfo = await (0, detect_1.detectProjectInfo)(projectRoot);
22
23
  spinner.succeed(`Detected ${projectInfo.framework} (${projectInfo.router} router, ${projectInfo.language})`);
23
24
  // Load module metadata
24
- const modulesDir = path_1.default.join(__dirname, '..', '..', 'modules');
25
+ const modulesDir = path_1.default.join(__dirname, "..", "..", "modules");
25
26
  const moduleMetadata = await loadModuleMetadata(modulesDir, module, options.provider);
26
27
  if (!moduleMetadata) {
27
28
  logger_1.logger.error(`Module "${module}" not found`);
28
29
  process.exit(1);
29
30
  }
31
+ // For database modules, ensure provider is selected
32
+ let selectedProvider = options.provider;
33
+ if (moduleMetadata.category === "database" && !selectedProvider) {
34
+ if (typeof moduleMetadata.dependencies === "object" &&
35
+ "providers" in moduleMetadata.dependencies) {
36
+ const providers = Object.keys(moduleMetadata.dependencies.providers || {});
37
+ if (providers.length > 0) {
38
+ const { provider } = await inquirer_1.default.prompt([
39
+ {
40
+ type: "list",
41
+ name: "provider",
42
+ message: "Select database provider:",
43
+ choices: providers.map((p) => ({ name: p, value: p })),
44
+ },
45
+ ]);
46
+ selectedProvider = provider;
47
+ }
48
+ }
49
+ }
50
+ // Merge dependencies based on provider
51
+ const mergedDeps = {};
52
+ const mergedDevDeps = {};
53
+ if (typeof moduleMetadata.dependencies === "object" &&
54
+ !("common" in moduleMetadata.dependencies)) {
55
+ // Already flat
56
+ Object.assign(mergedDeps, moduleMetadata.dependencies);
57
+ }
58
+ else if (typeof moduleMetadata.dependencies === "object" &&
59
+ "common" in moduleMetadata.dependencies) {
60
+ Object.assign(mergedDeps, moduleMetadata.dependencies.common);
61
+ if (selectedProvider &&
62
+ typeof moduleMetadata.dependencies === "object" &&
63
+ "providers" in moduleMetadata.dependencies &&
64
+ typeof moduleMetadata.dependencies.providers === "object" &&
65
+ selectedProvider in moduleMetadata.dependencies.providers) {
66
+ Object.assign(mergedDeps, moduleMetadata.dependencies.providers[selectedProvider]);
67
+ }
68
+ }
69
+ if (moduleMetadata.devDependencies) {
70
+ if (typeof moduleMetadata.devDependencies === "object" &&
71
+ !("common" in moduleMetadata.devDependencies)) {
72
+ Object.assign(mergedDevDeps, moduleMetadata.devDependencies);
73
+ }
74
+ else if (typeof moduleMetadata.devDependencies === "object" &&
75
+ "common" in moduleMetadata.devDependencies) {
76
+ Object.assign(mergedDevDeps, moduleMetadata.devDependencies.common);
77
+ if (selectedProvider &&
78
+ typeof moduleMetadata.devDependencies === "object" &&
79
+ "providers" in moduleMetadata.devDependencies &&
80
+ typeof moduleMetadata.devDependencies.providers === "object" &&
81
+ selectedProvider in moduleMetadata.devDependencies.providers) {
82
+ Object.assign(mergedDevDeps, moduleMetadata.devDependencies.providers[selectedProvider]);
83
+ }
84
+ }
85
+ }
86
+ // Update metadata with merged deps
87
+ moduleMetadata.dependencies = mergedDeps;
88
+ moduleMetadata.devDependencies = mergedDevDeps;
89
+ // Set variables for replacements
90
+ const variables = {};
91
+ if (selectedProvider) {
92
+ variables.provider = selectedProvider;
93
+ if (selectedProvider === "postgresql") {
94
+ variables.connectionString = "postgresql://user:password@localhost:5432/mydb?schema=public";
95
+ }
96
+ else if (selectedProvider === "mongodb") {
97
+ variables.connectionString = "mongodb://localhost:27017/mydb";
98
+ }
99
+ else if (selectedProvider === "mysql") {
100
+ variables.connectionString = "mysql://user:password@localhost:3306/mydb";
101
+ }
102
+ else if (selectedProvider === "sqlite") {
103
+ variables.connectionString = "file:./dev.db";
104
+ }
105
+ }
30
106
  // Check if framework is supported
31
107
  if (!moduleMetadata.supportedFrameworks.includes(projectInfo.framework)) {
32
- logger_1.logger.error(`Module "${module}" does not support ${projectInfo.framework}. Supported: ${moduleMetadata.supportedFrameworks.join(', ')}`);
108
+ logger_1.logger.error(`Module "${module}" does not support ${projectInfo.framework}. Supported: ${moduleMetadata.supportedFrameworks.join(", ")}`);
33
109
  process.exit(1);
34
110
  }
35
111
  // Check for conflicts
36
- if (module === 'auth' && projectInfo.hasAuth && !options.force) {
37
- logger_1.logger.warn('Auth library already detected in this project');
112
+ if (module === "auth" && projectInfo.hasAuth && !options.force) {
113
+ logger_1.logger.warn("Auth library already detected in this project");
38
114
  const { proceed } = await inquirer_1.default.prompt([
39
115
  {
40
- type: 'confirm',
41
- name: 'proceed',
42
- message: 'Continue anyway? (use --force to skip this prompt)',
116
+ type: "confirm",
117
+ name: "proceed",
118
+ message: "Continue anyway? (use --force to skip this prompt)",
43
119
  default: false,
44
120
  },
45
121
  ]);
46
122
  if (!proceed) {
47
- logger_1.logger.info('Cancelled');
123
+ logger_1.logger.info("Cancelled");
48
124
  process.exit(0);
49
125
  }
50
126
  }
51
127
  if (options.dryRun) {
52
- logger_1.logger.warn('Dry run mode - no changes will be made');
128
+ logger_1.logger.warn("Dry run mode - no changes will be made");
53
129
  logger_1.logger.newLine();
54
130
  }
55
131
  // Apply module patches
56
132
  await applyModulePatches(projectRoot, projectInfo, moduleMetadata, modulesDir, module, options);
133
+ // Apply framework patches
134
+ if (moduleMetadata.frameworkPatches && !options.dryRun) {
135
+ await applyFrameworkPatches(projectRoot, moduleMetadata.frameworkPatches, projectInfo.framework);
136
+ }
137
+ // Run post-install commands
138
+ if (moduleMetadata.postInstall && moduleMetadata.postInstall.length > 0 && !options.dryRun) {
139
+ const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
140
+ try {
141
+ for (const command of moduleMetadata.postInstall) {
142
+ (0, child_process_1.execSync)(command, { cwd: projectRoot, stdio: "pipe" });
143
+ }
144
+ postInstallSpinner.succeed("Post-install commands completed");
145
+ }
146
+ catch (error) {
147
+ postInstallSpinner.fail("Failed to run post-install commands");
148
+ throw error;
149
+ }
150
+ }
57
151
  // Add dependencies
58
152
  if (Object.keys(moduleMetadata.dependencies).length > 0 && options.install !== false) {
59
153
  const deps = Object.entries(moduleMetadata.dependencies).map(([name, version]) => `${name}@${version}`);
@@ -61,7 +155,7 @@ async function addCommand(module, options) {
61
155
  await (0, package_manager_1.addDependencies)(projectRoot, projectInfo.packageManager, deps, false);
62
156
  }
63
157
  else {
64
- logger_1.logger.info(`Would add dependencies: ${deps.join(', ')}`);
158
+ logger_1.logger.info(`Would add dependencies: ${deps.join(", ")}`);
65
159
  }
66
160
  }
67
161
  // Add dev dependencies
@@ -73,16 +167,21 @@ async function addCommand(module, options) {
73
167
  await (0, package_manager_1.addDependencies)(projectRoot, projectInfo.packageManager, devDeps, true);
74
168
  }
75
169
  else {
76
- logger_1.logger.info(`Would add dev dependencies: ${devDeps.join(', ')}`);
170
+ logger_1.logger.info(`Would add dev dependencies: ${devDeps.join(", ")}`);
77
171
  }
78
172
  }
79
173
  // Add environment variables
80
174
  if (moduleMetadata.envVars.length > 0) {
175
+ // Replace variables in envVars
176
+ const processedEnvVars = moduleMetadata.envVars.map((envVar) => ({
177
+ ...envVar,
178
+ value: envVar.value?.replace(/\{\{(\w+)\}\}/g, (match, key) => variables[key] || match),
179
+ }));
81
180
  if (!options.dryRun) {
82
- await (0, env_editor_1.addEnvVariables)(projectRoot, moduleMetadata.envVars, { force: options.force });
181
+ await (0, env_editor_1.addEnvVariables)(projectRoot, processedEnvVars, { force: options.force });
83
182
  }
84
183
  else {
85
- logger_1.logger.log(` ${chalk_1.default.dim('~')} .env.example`);
184
+ logger_1.logger.log(` ${chalk_1.default.dim("~")} .env.example`);
86
185
  }
87
186
  }
88
187
  logger_1.logger.newLine();
@@ -90,7 +189,7 @@ async function addCommand(module, options) {
90
189
  logger_1.logger.newLine();
91
190
  // Print next steps
92
191
  if (moduleMetadata.envVars.some((v) => v.required)) {
93
- logger_1.logger.log('Next: Fill in environment variables in .env');
192
+ logger_1.logger.log("Next: Fill in environment variables in .env");
94
193
  }
95
194
  logger_1.logger.newLine();
96
195
  }
@@ -120,7 +219,7 @@ async function loadModuleMetadata(modulesDir, moduleName, provider) {
120
219
  const moduleStat = await fs_extra_1.default.stat(modulePath);
121
220
  if (!moduleStat.isDirectory())
122
221
  continue;
123
- const metadataPath = path_1.default.join(modulePath, 'module.json');
222
+ const metadataPath = path_1.default.join(modulePath, "module.json");
124
223
  if (await fs_extra_1.default.pathExists(metadataPath)) {
125
224
  const metadata = await fs_extra_1.default.readJSON(metadataPath);
126
225
  // If provider is specified, match by directory name (exact match)
@@ -136,13 +235,15 @@ async function loadModuleMetadata(modulesDir, moduleName, provider) {
136
235
  }
137
236
  return null;
138
237
  }
238
+ // (removed duplicate import)
139
239
  async function applyModulePatches(projectRoot, projectInfo, moduleMetadata, modulesDir, moduleName, options) {
240
+ // Find the module path
140
241
  const moduleBasePath = await findModulePath(modulesDir, moduleName, options.provider);
141
242
  if (!moduleBasePath) {
142
- throw new Error('Module files not found');
243
+ throw new Error("Module files not found");
143
244
  }
144
245
  for (const patch of moduleMetadata.patches) {
145
- if (patch.type === 'create-file') {
246
+ if (patch.type === "create-file") {
146
247
  const filePatch = patch;
147
248
  // Check conditions
148
249
  if (filePatch.condition) {
@@ -153,18 +254,18 @@ async function applyModulePatches(projectRoot, projectInfo, moduleMetadata, modu
153
254
  continue; // Skip this patch
154
255
  }
155
256
  }
156
- const sourceFile = path_1.default.join(moduleBasePath, 'files', filePatch.source);
257
+ const sourceFile = path_1.default.join(moduleBasePath, "files", filePatch.source);
157
258
  let destFile = path_1.default.join(projectRoot, filePatch.destination);
158
259
  // Replace placeholders in destination
159
260
  destFile = destFile
160
- .replace('{{router}}', (0, detect_1.getRouterBasePath)(projectInfo))
161
- .replace('{{lib}}', (0, detect_1.getLibPath)(projectInfo));
261
+ .replace("{{router}}", (0, detect_1.getRouterBasePath)(projectInfo))
262
+ .replace("{{lib}}", (0, detect_1.getLibPath)(projectInfo));
162
263
  if (!options.dryRun) {
163
264
  if (await (0, files_1.fileExists)(sourceFile)) {
164
- const content = await fs_extra_1.default.readFile(sourceFile, 'utf-8');
265
+ const content = await fs_extra_1.default.readFile(sourceFile, "utf-8");
165
266
  await (0, files_1.createFile)(destFile, content, { force: options.force });
166
267
  const relativePath = path_1.default.relative(projectRoot, destFile);
167
- logger_1.logger.log(` ${chalk_1.default.green('+')} ${relativePath}`);
268
+ logger_1.logger.log(` ${chalk_1.default.green("+")} ${relativePath}`);
168
269
  }
169
270
  else {
170
271
  logger_1.logger.warn(`Source file not found: ${filePatch.source}`);
@@ -172,7 +273,7 @@ async function applyModulePatches(projectRoot, projectInfo, moduleMetadata, modu
172
273
  }
173
274
  else {
174
275
  const relativePath = path_1.default.relative(projectRoot, destFile);
175
- logger_1.logger.log(` ${chalk_1.default.dim('+')} ${relativePath}`);
276
+ logger_1.logger.log(` ${chalk_1.default.dim("+")} ${relativePath}`);
176
277
  }
177
278
  }
178
279
  }
@@ -190,7 +291,7 @@ async function findModulePath(modulesDir, moduleName, provider) {
190
291
  const moduleStat = await fs_extra_1.default.stat(modulePath);
191
292
  if (!moduleStat.isDirectory())
192
293
  continue;
193
- const metadataPath = path_1.default.join(modulePath, 'module.json');
294
+ const metadataPath = path_1.default.join(modulePath, "module.json");
194
295
  if (await fs_extra_1.default.pathExists(metadataPath)) {
195
296
  const metadata = await fs_extra_1.default.readJSON(metadataPath);
196
297
  // If provider is specified, match by directory name (exact match)
@@ -206,3 +307,41 @@ async function findModulePath(modulesDir, moduleName, provider) {
206
307
  }
207
308
  return null;
208
309
  }
310
+ async function applyFrameworkPatches(projectRoot, patches, framework) {
311
+ const frameworkKey = framework;
312
+ const frameworkPatches = patches[frameworkKey];
313
+ if (!frameworkPatches)
314
+ return;
315
+ for (const [filename, patchConfig] of Object.entries(frameworkPatches)) {
316
+ const filePath = path_1.default.join(projectRoot, filename);
317
+ if (await fs_extra_1.default.pathExists(filePath)) {
318
+ const fileContent = await fs_extra_1.default.readJson(filePath);
319
+ if (patchConfig.merge) {
320
+ const merged = deepMerge(fileContent, patchConfig.merge);
321
+ await fs_extra_1.default.writeJson(filePath, merged, { spaces: 2 });
322
+ const relativePath = path_1.default.relative(projectRoot, filePath);
323
+ logger_1.logger.log(` ${chalk_1.default.blue("~")} ${relativePath}`);
324
+ }
325
+ }
326
+ }
327
+ }
328
+ function deepMerge(target, source) {
329
+ const output = { ...target };
330
+ for (const key in source) {
331
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
332
+ if (target[key]) {
333
+ output[key] = deepMerge(target[key], source[key]);
334
+ }
335
+ else {
336
+ output[key] = source[key];
337
+ }
338
+ }
339
+ else if (Array.isArray(source[key])) {
340
+ output[key] = Array.from(new Set([...(target[key] || []), ...source[key]]));
341
+ }
342
+ else {
343
+ output[key] = source[key];
344
+ }
345
+ }
346
+ return output;
347
+ }
@@ -1,4 +1,4 @@
1
- import { PackageManager } from '../utils/package-manager';
1
+ import { PackageManager } from "../utils/package-manager";
2
2
  interface InitOptions {
3
3
  template?: string;
4
4
  pm?: PackageManager;
@@ -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 && !['npm', 'yarn', 'pnpm'].includes(options.pm)) {
19
- logger_1.logger.error(`Invalid package manager: ${options.pm}. Use npm, yarn, or pnpm.`);
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, '..', '..', 'templates');
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('No templates found');
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 || 'my-app',
33
+ projectName: projectName || "my-app",
34
34
  template: options.template || templates[0].name,
35
- packageManager: options.pm || 'pnpm',
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: 'input',
44
- name: 'projectName',
45
- message: 'Project name:',
46
- default: projectName || 'my-app',
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] || 'Invalid package name';
51
+ return validation.errors?.[0] || "Invalid package name";
52
52
  }
53
53
  return true;
54
54
  },
55
55
  },
56
56
  {
57
- type: 'list',
58
- name: 'template',
59
- message: 'Select a template:',
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: 'list',
68
- name: 'packageManager',
69
- message: 'Select a package manager:',
70
- choices: ['pnpm', 'npm', 'yarn'],
71
- default: 'pnpm',
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: 'confirm',
76
- name: 'install',
77
- message: 'Install dependencies?',
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: 'confirm',
83
- name: 'git',
84
- message: 'Initialize git repository?',
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('Please choose a different name or remove the existing directory.');
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('cd')} ${answers.projectName}`);
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 === 'npm' ? 'run ' : ''}dev`);
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, 'template.json');
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);
@@ -15,28 +15,28 @@ async function listCommand(options) {
15
15
  logger_1.logger.newLine();
16
16
  // List templates
17
17
  if (showTemplates) {
18
- const templatesDir = path_1.default.join(__dirname, '..', '..', 'templates', 'bases');
18
+ const templatesDir = path_1.default.join(__dirname, "..", "..", "templates");
19
19
  const templates = await getAvailableTemplates(templatesDir);
20
- logger_1.logger.log(chalk_1.default.bold.cyan('▸ TEMPLATES') + chalk_1.default.gray(` (${templates.length})`));
20
+ logger_1.logger.log(chalk_1.default.bold.cyan("▸ TEMPLATES") + chalk_1.default.gray(` (${templates.length})`));
21
21
  logger_1.logger.newLine();
22
22
  if (templates.length === 0) {
23
- logger_1.logger.log(chalk_1.default.dim(' No templates available'));
23
+ logger_1.logger.log(chalk_1.default.dim(" No templates available"));
24
24
  }
25
25
  else {
26
- templates.forEach((template, index) => {
27
- logger_1.logger.log(` ${chalk_1.default.cyan('')} ${template.displayName}`);
26
+ templates.forEach((template) => {
27
+ logger_1.logger.log(` ${chalk_1.default.cyan("")} ${template.displayName}`);
28
28
  });
29
29
  }
30
30
  logger_1.logger.newLine();
31
31
  }
32
32
  // List modules
33
33
  if (showModules) {
34
- const modulesDir = path_1.default.join(__dirname, '..', '..', 'modules');
34
+ const modulesDir = path_1.default.join(__dirname, "..", "..", "modules");
35
35
  const modules = await getAvailableModules(modulesDir);
36
- logger_1.logger.log(chalk_1.default.bold.cyan('▸ MODULES') + chalk_1.default.gray(` (${modules.length})`));
36
+ logger_1.logger.log(chalk_1.default.bold.cyan("▸ MODULES") + chalk_1.default.gray(` (${modules.length})`));
37
37
  if (modules.length === 0) {
38
38
  logger_1.logger.newLine();
39
- logger_1.logger.log(chalk_1.default.dim(' No modules available'));
39
+ logger_1.logger.log(chalk_1.default.dim(" No modules available"));
40
40
  logger_1.logger.newLine();
41
41
  }
42
42
  else {
@@ -50,9 +50,9 @@ async function listCommand(options) {
50
50
  }, {});
51
51
  for (const [category, mods] of Object.entries(grouped)) {
52
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})`)}`);
53
+ logger_1.logger.log(` ${chalk_1.default.yellow("")} ${chalk_1.default.bold.yellow(category.toUpperCase())} ${chalk_1.default.dim(`(${mods.length})`)}`);
54
54
  mods.forEach((mod) => {
55
- logger_1.logger.log(` ${chalk_1.default.cyan('')} ${mod.displayName}`);
55
+ logger_1.logger.log(` ${chalk_1.default.cyan("")} ${mod.displayName}`);
56
56
  });
57
57
  }
58
58
  logger_1.logger.newLine();
@@ -71,7 +71,7 @@ async function getAvailableTemplates(templatesDir) {
71
71
  const templateDirs = await fs_extra_1.default.readdir(templatesDir);
72
72
  const templates = [];
73
73
  for (const dir of templateDirs) {
74
- const metadataPath = path_1.default.join(templatesDir, dir, 'template.json');
74
+ const metadataPath = path_1.default.join(templatesDir, dir, "template.json");
75
75
  if (await fs_extra_1.default.pathExists(metadataPath)) {
76
76
  const metadata = await fs_extra_1.default.readJSON(metadataPath);
77
77
  templates.push(metadata);
@@ -92,7 +92,7 @@ async function getAvailableModules(modulesDir) {
92
92
  continue;
93
93
  const moduleDirs = await fs_extra_1.default.readdir(categoryPath);
94
94
  for (const moduleDir of moduleDirs) {
95
- const metadataPath = path_1.default.join(categoryPath, moduleDir, 'module.json');
95
+ const metadataPath = path_1.default.join(categoryPath, moduleDir, "module.json");
96
96
  if (await fs_extra_1.default.pathExists(metadataPath)) {
97
97
  const metadata = await fs_extra_1.default.readJSON(metadataPath);
98
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('stackkit')
15
- .description('Production-ready project generator and module CLI')
16
- .version('0.3.2');
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('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)')
23
- .option('--no-install', 'Skip installing dependencies')
24
- .option('--no-git', 'Skip git initialization')
25
- .option('-y, --yes', 'Skip prompts and use defaults')
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('list')
30
- .description('List available templates and modules')
31
- .option('-t, --templates', 'List only templates')
32
- .option('-m, --modules', 'List only modules')
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('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')
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('command:*', () => {
45
- console.error(chalk_1.default.red(`\nInvalid command: ${program.args.join(' ')}\n`));
46
- console.log(chalk_1.default.yellow('Run stackkit --help for a list of available commands.\n'));
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();