stackkit 0.2.3 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/add.js +2 -16
- package/dist/cli/create.js +1 -3
- package/dist/cli/doctor.js +0 -4
- package/dist/index.js +2 -2
- package/dist/lib/conversion/js-conversion.js +27 -11
- package/dist/lib/discovery/installed-detection.js +3 -3
- package/dist/lib/discovery/module-discovery.d.ts +1 -1
- package/dist/lib/discovery/module-discovery.js +22 -6
- package/dist/lib/generation/code-generator.js +3 -3
- package/package.json +8 -8
- package/templates/nextjs/next-env.d.ts +6 -0
- package/templates/react/dist/assets/index-D4AHT4dU.js +193 -0
- package/templates/react/dist/assets/index-rpwj5ZOX.css +1 -0
- package/templates/react/dist/index.html +14 -0
- package/templates/react/dist/vite.svg +1 -0
- package/templates/express/.env.example +0 -2
- package/templates/nextjs/.env.example +0 -1
- package/templates/react/.env.example +0 -1
- package/templates/react/.prettierignore +0 -4
- package/templates/react/.prettierrc +0 -9
package/dist/cli/add.js
CHANGED
|
@@ -31,10 +31,7 @@ async function addCommand(module, options) {
|
|
|
31
31
|
await addModuleToProject(projectRoot, refreshedProjectInfo, config, options);
|
|
32
32
|
logger_1.logger.newLine();
|
|
33
33
|
if (config.preAdded && config.preAdded.length > 0) {
|
|
34
|
-
const addedNames = [
|
|
35
|
-
...config.preAdded.map((p) => p.displayName),
|
|
36
|
-
config.displayName,
|
|
37
|
-
].map((s) => chalk_1.default.bold(s));
|
|
34
|
+
const addedNames = [...config.preAdded.map((p) => p.displayName), config.displayName].map((s) => chalk_1.default.bold(s));
|
|
38
35
|
logger_1.logger.success(`Added ${addedNames.join(" and ")}`);
|
|
39
36
|
}
|
|
40
37
|
else {
|
|
@@ -121,7 +118,7 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
|
|
|
121
118
|
const discovered = await (0, module_discovery_1.discoverModules)(modulesDir);
|
|
122
119
|
// Prefer discovered framework, then projectInfo.framework; leave empty if unknown
|
|
123
120
|
const defaultFramework = (discovered.frameworks && discovered.frameworks[0]?.name) || projectInfo?.framework || "";
|
|
124
|
-
const compatibleAuths = (0, module_discovery_1.getCompatibleAuthOptions)(discovered.auth || [], projectInfo?.framework || defaultFramework, projectInfo?.hasPrisma ? "prisma" : "none");
|
|
121
|
+
const compatibleAuths = (0, module_discovery_1.getCompatibleAuthOptions)(discovered.auth || [], projectInfo?.framework || defaultFramework, projectInfo?.hasPrisma ? "prisma" : "none", discovered.frameworks);
|
|
125
122
|
const categories = [
|
|
126
123
|
{ name: "Database", value: "database" },
|
|
127
124
|
];
|
|
@@ -171,7 +168,6 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
|
|
|
171
168
|
}
|
|
172
169
|
else if (category === "auth") {
|
|
173
170
|
let preAddedForReturn;
|
|
174
|
-
// If no database detected, require the user to select/add a database first
|
|
175
171
|
if (!projectInfo?.hasDatabase) {
|
|
176
172
|
logger_1.logger.warn("No database detected in the project. Authentication requires a database.");
|
|
177
173
|
const dbChoices = (0, module_discovery_1.getDatabaseChoices)(discovered.databases || [], projectInfo?.framework || defaultFramework);
|
|
@@ -188,7 +184,6 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
|
|
|
188
184
|
logger_1.logger.info("Cancelled — authentication requires a database");
|
|
189
185
|
process.exit(0);
|
|
190
186
|
}
|
|
191
|
-
// Build a database AddConfig and add it immediately, then refresh projectInfo
|
|
192
187
|
let dbConfig;
|
|
193
188
|
if (selectedDb.startsWith("prisma-")) {
|
|
194
189
|
const provider = selectedDb.split("-")[1];
|
|
@@ -210,13 +205,9 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
|
|
|
210
205
|
metadata: meta,
|
|
211
206
|
};
|
|
212
207
|
}
|
|
213
|
-
// Add the database first (suppress its top-level success message)
|
|
214
208
|
await addModuleToProject(projectRoot, projectInfo || (await (0, detect_1.detectProjectInfo)(projectRoot)), dbConfig, options);
|
|
215
|
-
// Refresh project info after database install and record pre-added
|
|
216
209
|
projectInfo = await (0, detect_1.detectProjectInfo)(projectRoot);
|
|
217
|
-
// attach preAdded so caller can summarize chained additions
|
|
218
210
|
dbConfig.preAdded = dbConfig.preAdded || [];
|
|
219
|
-
// store for returning later
|
|
220
211
|
preAddedForReturn = dbConfig;
|
|
221
212
|
}
|
|
222
213
|
const dbString = projectInfo?.hasPrisma ? "prisma" : "none";
|
|
@@ -249,7 +240,6 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
|
|
|
249
240
|
displayName: metadata.displayName || selectedAuth,
|
|
250
241
|
metadata,
|
|
251
242
|
};
|
|
252
|
-
// If we added a DB earlier in this flow, include it for grouped messaging
|
|
253
243
|
if (typeof preAddedForReturn !== "undefined" && preAddedForReturn) {
|
|
254
244
|
result.preAdded = [preAddedForReturn];
|
|
255
245
|
}
|
|
@@ -257,7 +247,6 @@ async function getInteractiveConfig(modulesDir, projectInfo, options) {
|
|
|
257
247
|
}
|
|
258
248
|
throw new Error("Invalid selection");
|
|
259
249
|
}
|
|
260
|
-
/* removed unused getProviderConfig — discovery-based flows handle providers */
|
|
261
250
|
async function addModuleToProject(projectRoot, projectInfo, config, options) {
|
|
262
251
|
const moduleMetadata = config.metadata;
|
|
263
252
|
const selectedProvider = config.provider;
|
|
@@ -329,14 +318,11 @@ async function addModuleToProject(projectRoot, projectInfo, config, options) {
|
|
|
329
318
|
}
|
|
330
319
|
}
|
|
331
320
|
const selectedModules = { framework: projectInfo.framework };
|
|
332
|
-
// Populate database context from detected project state so generator conditions work
|
|
333
321
|
try {
|
|
334
322
|
const pkg = await fs_extra_1.default.readJson(path_1.default.join(projectRoot, "package.json"));
|
|
335
323
|
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
336
324
|
if (projectInfo.hasPrisma) {
|
|
337
325
|
selectedModules.database = "prisma";
|
|
338
|
-
// Parse provider specifically from the `datasource` block to avoid
|
|
339
|
-
// capturing the generator block (which uses provider = "prisma-client-js").
|
|
340
326
|
const prismaSchema = path_1.default.join(projectRoot, "prisma", "schema.prisma");
|
|
341
327
|
if (await fs_extra_1.default.pathExists(prismaSchema)) {
|
|
342
328
|
const content = await fs_extra_1.default.readFile(prismaSchema, "utf-8");
|
package/dist/cli/create.js
CHANGED
|
@@ -205,8 +205,6 @@ async function getProjectConfig(projectName, options) {
|
|
|
205
205
|
return [{ name: "None", value: "none" }];
|
|
206
206
|
},
|
|
207
207
|
},
|
|
208
|
-
// If a prisma-* choice is selected above, `prismaProvider` will be derived from it,
|
|
209
|
-
// otherwise prompt for provider when `prisma` is selected directly.
|
|
210
208
|
{
|
|
211
209
|
type: "list",
|
|
212
210
|
name: "prismaProvider",
|
|
@@ -222,7 +220,7 @@ async function getProjectConfig(projectName, options) {
|
|
|
222
220
|
name: "auth",
|
|
223
221
|
message: "Select authentication:",
|
|
224
222
|
when: (answers) => answers.database !== "none" || answers.framework === "react",
|
|
225
|
-
choices: (answers) => (0, module_discovery_1.getCompatibleAuthOptions)(discoveredModules.auth, answers.framework, answers.database || "none"),
|
|
223
|
+
choices: (answers) => (0, module_discovery_1.getCompatibleAuthOptions)(discoveredModules.auth, answers.framework, answers.database || "none", discoveredModules.frameworks),
|
|
226
224
|
},
|
|
227
225
|
{
|
|
228
226
|
type: "list",
|
package/dist/cli/doctor.js
CHANGED
|
@@ -296,9 +296,6 @@ async function checkAuthRoutesExist(projectRoot) {
|
|
|
296
296
|
catch {
|
|
297
297
|
// ignore discovery errors
|
|
298
298
|
}
|
|
299
|
-
// Fallback to known common paths if generators don't provide any
|
|
300
|
-
// If generators provided candidate routes, check them. Otherwise
|
|
301
|
-
// do not assume any hard-coded route locations.
|
|
302
299
|
if (candidates.size === 0)
|
|
303
300
|
return false;
|
|
304
301
|
for (const routePath of candidates) {
|
|
@@ -442,7 +439,6 @@ async function checkDependencies(packageJson) {
|
|
|
442
439
|
// Assume up to date if using flexible versioning
|
|
443
440
|
}
|
|
444
441
|
else {
|
|
445
|
-
// Assume outdated if using exact versions (simplified check)
|
|
446
442
|
outdated.push(name);
|
|
447
443
|
}
|
|
448
444
|
}
|
package/dist/index.js
CHANGED
|
@@ -78,7 +78,7 @@ function buildOptionHints() {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
catch {
|
|
81
|
-
|
|
81
|
+
// ignore malformed module.json
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -93,7 +93,7 @@ function buildOptionHints() {
|
|
|
93
93
|
auths.push(m.name);
|
|
94
94
|
}
|
|
95
95
|
catch {
|
|
96
|
-
|
|
96
|
+
// ignore malformed module.json
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -7,6 +7,7 @@ exports.convertToJavaScript = convertToJavaScript;
|
|
|
7
7
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const logger_1 = require("../ui/logger");
|
|
10
11
|
const package_root_1 = require("../utils/package-root");
|
|
11
12
|
const baseDirs = {
|
|
12
13
|
express: "./src",
|
|
@@ -41,6 +42,32 @@ async function convertToJavaScript(targetDir, framework) {
|
|
|
41
42
|
};
|
|
42
43
|
await removeDtsFiles(targetDir);
|
|
43
44
|
const babel = require("@babel/core");
|
|
45
|
+
const checkModule = (name) => {
|
|
46
|
+
try {
|
|
47
|
+
require.resolve(name);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const hasRecast = checkModule("recast");
|
|
55
|
+
const hasBabelParser = checkModule("@babel/parser");
|
|
56
|
+
const hasTransformTypescript = checkModule("@babel/plugin-transform-typescript");
|
|
57
|
+
if (!hasRecast || !hasBabelParser || !hasTransformTypescript) {
|
|
58
|
+
const packageRoot = (0, package_root_1.getPackageRoot)();
|
|
59
|
+
const missing = [];
|
|
60
|
+
if (!hasRecast)
|
|
61
|
+
missing.push("recast");
|
|
62
|
+
if (!hasBabelParser)
|
|
63
|
+
missing.push("@babel/parser");
|
|
64
|
+
if (!hasTransformTypescript)
|
|
65
|
+
missing.push("@babel/plugin-transform-typescript");
|
|
66
|
+
logger_1.logger.warn(`Optional tooling missing: ${missing.join(", ")}. Generated JavaScript may contain transformed JSX.`);
|
|
67
|
+
if (process.env.STACKKIT_VERBOSE === "1" || process.env.DEBUG) {
|
|
68
|
+
logger_1.logger.info(`To ensure generated JavaScript preserves JSX, add these to the CLI package dependencies or run 'pnpm install' in ${packageRoot}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
44
71
|
const transpileAllTsFiles = async (dir) => {
|
|
45
72
|
const entries = await fs_extra_1.default.readdir(dir, { withFileTypes: true });
|
|
46
73
|
for (const entry of entries) {
|
|
@@ -73,20 +100,10 @@ async function convertToJavaScript(targetDir, framework) {
|
|
|
73
100
|
},
|
|
74
101
|
],
|
|
75
102
|
];
|
|
76
|
-
if (isTsx) {
|
|
77
|
-
presets.push([
|
|
78
|
-
require.resolve("@babel/preset-react"),
|
|
79
|
-
{
|
|
80
|
-
runtime: "automatic",
|
|
81
|
-
},
|
|
82
|
-
]);
|
|
83
|
-
}
|
|
84
|
-
// Use recast + Babel AST transform (same approach as transform.tools)
|
|
85
103
|
try {
|
|
86
104
|
const recast = require("recast");
|
|
87
105
|
const { transformFromAstSync } = require("@babel/core");
|
|
88
106
|
const transformTypescript = require("@babel/plugin-transform-typescript");
|
|
89
|
-
// getBabelOptions may be exported as default or directly
|
|
90
107
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
108
|
let getBabelOptions = require("recast/parsers/_babel_options");
|
|
92
109
|
if (getBabelOptions && getBabelOptions.default)
|
|
@@ -96,7 +113,6 @@ async function convertToJavaScript(targetDir, framework) {
|
|
|
96
113
|
parser: {
|
|
97
114
|
parse: (source, options) => {
|
|
98
115
|
const babelOptions = getBabelOptions(options || {});
|
|
99
|
-
// ensure typescript and jsx handling
|
|
100
116
|
if (isTsx) {
|
|
101
117
|
babelOptions.plugins.push("typescript", "jsx");
|
|
102
118
|
}
|
|
@@ -44,7 +44,7 @@ async function collectModulePkgNames(modulePath) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
catch {
|
|
47
|
-
// ignore
|
|
47
|
+
// ignore malformed module.json
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
return Array.from(new Set(pkgNames));
|
|
@@ -70,7 +70,7 @@ async function detectAuthModules(packageJson) {
|
|
|
70
70
|
moduleName = m.name;
|
|
71
71
|
}
|
|
72
72
|
catch {
|
|
73
|
-
|
|
73
|
+
// ignore malformed module.json
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
for (const pkg of pkgNames) {
|
|
@@ -111,7 +111,7 @@ async function detectDatabaseModules(packageJson) {
|
|
|
111
111
|
moduleName = m.name;
|
|
112
112
|
}
|
|
113
113
|
catch {
|
|
114
|
-
|
|
114
|
+
// ignore malformed module.json
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
for (const pkg of pkgNames) {
|
|
@@ -41,7 +41,7 @@ export declare function getValidAuthOptions(authModules: ModuleMetadata[]): stri
|
|
|
41
41
|
/**
|
|
42
42
|
* Get compatible auth options for given framework and database
|
|
43
43
|
*/
|
|
44
|
-
export declare function getCompatibleAuthOptions(authModules: ModuleMetadata[], framework: string, database: string): Array<{
|
|
44
|
+
export declare function getCompatibleAuthOptions(authModules: ModuleMetadata[], framework: string, database: string, frameworksMeta?: ModuleMetadata[]): Array<{
|
|
45
45
|
name: string;
|
|
46
46
|
value: string;
|
|
47
47
|
}>;
|
|
@@ -52,7 +52,7 @@ async function discoverModules(modulesDir) {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
catch {
|
|
55
|
-
//
|
|
55
|
+
// ignore invalid template
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
}
|
|
@@ -83,7 +83,7 @@ async function discoverModules(modulesDir) {
|
|
|
83
83
|
discovered.databases.push(metadata);
|
|
84
84
|
}
|
|
85
85
|
catch {
|
|
86
|
-
//
|
|
86
|
+
// ignore invalid module
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -105,7 +105,7 @@ async function discoverModules(modulesDir) {
|
|
|
105
105
|
discovered.auth.push(metadata);
|
|
106
106
|
}
|
|
107
107
|
catch {
|
|
108
|
-
//
|
|
108
|
+
// ignore invalid module
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -149,7 +149,7 @@ function getValidAuthOptions(authModules) {
|
|
|
149
149
|
/**
|
|
150
150
|
* Get compatible auth options for given framework and database
|
|
151
151
|
*/
|
|
152
|
-
function getCompatibleAuthOptions(authModules, framework, database) {
|
|
152
|
+
function getCompatibleAuthOptions(authModules, framework, database, frameworksMeta) {
|
|
153
153
|
const compatible = [];
|
|
154
154
|
for (const auth of authModules) {
|
|
155
155
|
// Check if auth supports the framework
|
|
@@ -159,10 +159,26 @@ function getCompatibleAuthOptions(authModules, framework, database) {
|
|
|
159
159
|
// Normalize database option (handle prisma-<provider> values)
|
|
160
160
|
const parsedDb = (0, shared_1.parseDatabaseOption)(database || "").database;
|
|
161
161
|
// If module provides explicit compatibility matrix, use it
|
|
162
|
+
let dbCompatible = true;
|
|
162
163
|
if (auth.compatibility && auth.compatibility.databases) {
|
|
163
|
-
|
|
164
|
-
continue;
|
|
164
|
+
dbCompatible = auth.compatibility.databases.includes(parsedDb);
|
|
165
165
|
}
|
|
166
|
+
// If the framework template explicitly lists this auth as compatible,
|
|
167
|
+
// allow it even if the auth module's database compatibility would normally
|
|
168
|
+
// exclude the current database selection (covers cases like React where
|
|
169
|
+
// the framework can support auth without a DB).
|
|
170
|
+
let explicitlyAllowedByFramework = false;
|
|
171
|
+
if (frameworksMeta && Array.isArray(frameworksMeta)) {
|
|
172
|
+
const fw = frameworksMeta.find((f) => f.name === framework);
|
|
173
|
+
if (fw && fw.compatibility) {
|
|
174
|
+
const authList = fw.compatibility.auth;
|
|
175
|
+
if (Array.isArray(authList) && authList.includes(auth.name)) {
|
|
176
|
+
explicitlyAllowedByFramework = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (!dbCompatible && !explicitlyAllowedByFramework)
|
|
181
|
+
continue;
|
|
166
182
|
compatible.push({
|
|
167
183
|
name: auth.displayName,
|
|
168
184
|
value: auth.name,
|
|
@@ -62,7 +62,7 @@ class AdvancedCodeGenerator {
|
|
|
62
62
|
this.generators.set(`${type}:${moduleName}`, config);
|
|
63
63
|
}
|
|
64
64
|
catch {
|
|
65
|
-
//
|
|
65
|
+
// ignore invalid generator files
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
}
|
|
@@ -396,7 +396,7 @@ class AdvancedCodeGenerator {
|
|
|
396
396
|
}
|
|
397
397
|
}
|
|
398
398
|
catch {
|
|
399
|
-
// ignore
|
|
399
|
+
// ignore (not critical)
|
|
400
400
|
}
|
|
401
401
|
// Ensure gitignore is present in target even if template authors
|
|
402
402
|
// renamed it to avoid npm/package issues (e.g. 'gitignore' or '_gitignore')
|
|
@@ -453,7 +453,7 @@ class AdvancedCodeGenerator {
|
|
|
453
453
|
}
|
|
454
454
|
}
|
|
455
455
|
catch {
|
|
456
|
-
// ignore
|
|
456
|
+
// ignore
|
|
457
457
|
}
|
|
458
458
|
try {
|
|
459
459
|
const templateJsonPath2 = path.join(templatePath, "template.json");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stackkit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Production-ready CLI to create and extend JavaScript or TypeScript apps with modular stacks.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -67,17 +67,17 @@
|
|
|
67
67
|
"@babel/core": "^7.28.5",
|
|
68
68
|
"@babel/preset-env": "^7.28.5",
|
|
69
69
|
"@babel/preset-typescript": "^7.28.5",
|
|
70
|
-
"@babel/preset-react": "^7.28.5"
|
|
70
|
+
"@babel/preset-react": "^7.28.5",
|
|
71
|
+
"recast": "^0.20.5",
|
|
72
|
+
"@babel/parser": "^7.28.5",
|
|
73
|
+
"@babel/plugin-transform-typescript": "^7.28.5",
|
|
74
|
+
"@babel/plugin-transform-react-jsx": "^7.27.1"
|
|
71
75
|
},
|
|
72
76
|
"devDependencies": {
|
|
73
77
|
"@types/fs-extra": "^11.0.4",
|
|
74
78
|
"@types/inquirer": "^9.0.7",
|
|
75
79
|
"@types/node": "^25.0.8",
|
|
76
80
|
"@types/validate-npm-package-name": "^4.0.2",
|
|
77
|
-
"typescript": "^5.3.3"
|
|
78
|
-
"recast": "^0.20.5",
|
|
79
|
-
"@babel/plugin-transform-typescript": "^7.28.5",
|
|
80
|
-
"@babel/parser": "^7.28.5",
|
|
81
|
-
"@babel/plugin-transform-react-jsx": "^7.27.1"
|
|
81
|
+
"typescript": "^5.3.3"
|
|
82
82
|
}
|
|
83
|
-
}
|
|
83
|
+
}
|