better-ts-stack 1.1.4 → 2.0.1
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/LICENSE +21 -0
- package/README.md +113 -82
- package/dist/args.js +90 -0
- package/dist/builder/buildPlanner.js +71 -0
- package/dist/builder/configGenerator.js +19 -10
- package/dist/builder/dependencyInstaller.js +8 -15
- package/dist/builder/fileProcessor.js +47 -44
- package/dist/builder/gitInitializer.js +21 -35
- package/dist/builder/index.js +33 -91
- package/dist/builder/moduleSelector.js +11 -16
- package/dist/builder/templateContext.js +6 -27
- package/dist/index.js +47 -21
- package/dist/modules/registry.js +4 -6
- package/dist/output/nextSteps.js +10 -20
- package/dist/prompts/index.js +22 -59
- package/dist/prompts/stack.js +85 -71
- package/dist/types/index.js +47 -41
- package/dist/ui/index.js +18 -0
- package/dist/ui/messages.js +124 -0
- package/dist/ui/prompts.js +70 -0
- package/dist/ui/theme.js +23 -0
- package/dist/validators/index.js +15 -40
- package/package.json +71 -66
- package/templates/backend/express/.prettierrc +2 -2
- package/templates/backend/express/config.json +40 -44
- package/templates/backend/express/src/{index.ts → index.ts.hbs} +75 -76
- package/templates/backend/nestjs/.prettierignore +52 -0
- package/templates/backend/nestjs/.prettierrc +16 -0
- package/templates/backend/nestjs/config.json +41 -0
- package/templates/backend/nestjs/eslint.config.mjs +40 -0
- package/templates/backend/nestjs/gitignore +36 -0
- package/templates/backend/nestjs/nest-cli.json +9 -0
- package/templates/backend/nestjs/src/app.module.ts.hbs +34 -0
- package/templates/backend/nestjs/src/health/health.controller.ts +12 -0
- package/templates/backend/nestjs/src/main.ts +23 -0
- package/templates/backend/nestjs/tsconfig.eslint.json +9 -0
- package/templates/backend/nestjs/tsconfig.json +25 -0
- package/templates/frontend/nextjs/app/layout.tsx +13 -13
- package/templates/frontend/nextjs/app/page.tsx.hbs +97 -97
- package/templates/frontend/nextjs/components/ui/button.tsx +51 -51
- package/templates/frontend/nextjs/components/ui/card.tsx +60 -60
- package/templates/frontend/nextjs/components/ui/field.tsx +67 -67
- package/templates/frontend/nextjs/components/ui/input.tsx +18 -18
- package/templates/frontend/nextjs/config.json +37 -38
- package/templates/frontend/nextjs/eslint.config.mjs +11 -11
- package/templates/frontend/nextjs/next.config.ts.hbs +12 -12
- package/templates/frontend/tanstack/config.json +45 -48
- package/templates/modules/auth/express/config.json +20 -26
- package/templates/modules/auth/express/src/controllers/authController.ts.hbs +43 -57
- package/templates/modules/auth/nestjs/config.json +20 -0
- package/templates/modules/auth/nestjs/src/auth/auth.controller.ts +46 -0
- package/templates/modules/auth/nestjs/src/auth/auth.guard.ts +36 -0
- package/templates/modules/auth/nestjs/src/auth/auth.module.ts.hbs +22 -0
- package/templates/modules/auth/nestjs/src/auth/auth.schemas.ts +35 -0
- package/templates/modules/auth/nestjs/src/auth/auth.service.ts +70 -0
- package/templates/modules/auth/nestjs/src/auth/jwt.ts +27 -0
- package/templates/modules/auth/nestjs/src/auth/users.service.ts.hbs +144 -0
- package/templates/modules/auth/nestjs/src/auth/zod-validation.pipe.ts +16 -0
- package/templates/modules/auth/nextjs/config.json +18 -31
- package/templates/modules/auth/tanstack/config.json +2 -16
- package/templates/modules/docker/Dockerfile.hbs +135 -133
- package/templates/modules/docker/config.json +15 -19
- package/templates/modules/drizzle/express/config.json +26 -27
- package/templates/modules/drizzle/nestjs/config.json +26 -0
- package/templates/modules/drizzle/nestjs/drizzle.config.ts +13 -0
- package/templates/modules/drizzle/nestjs/src/database/database.module.ts +30 -0
- package/templates/modules/drizzle/nestjs/src/database/schema.ts +10 -0
- package/templates/modules/drizzle/nextjs/config.json +26 -29
- package/templates/modules/drizzle/tanstack/config.json +5 -5
- package/templates/modules/mongoose/express/config.json +15 -16
- package/templates/modules/mongoose/nestjs/config.json +15 -0
- package/templates/modules/mongoose/nestjs/src/users/user.schema.ts +21 -0
- package/templates/modules/mongoose/nextjs/config.json +15 -16
- package/templates/modules/mongoose/tanstack/config.json +16 -17
- package/templates/modules/prisma/express/config.json +27 -28
- package/templates/modules/prisma/nestjs/config.json +27 -0
- package/templates/modules/prisma/nestjs/prisma/schema.prisma +23 -0
- package/templates/modules/prisma/nestjs/prisma.config.ts +12 -0
- package/templates/modules/prisma/nestjs/src/prisma/prisma.module.ts +11 -0
- package/templates/modules/prisma/nestjs/src/prisma/prisma.service.ts +17 -0
- package/templates/modules/prisma/nextjs/config.json +28 -29
- package/templates/modules/prisma/tanstack/config.json +5 -6
- package/dist/builder/configGenerator.d.ts +0 -7
- package/dist/builder/configGenerator.d.ts.map +0 -1
- package/dist/builder/configGenerator.js.map +0 -1
- package/dist/builder/dependencyInstaller.d.ts +0 -3
- package/dist/builder/dependencyInstaller.d.ts.map +0 -1
- package/dist/builder/dependencyInstaller.js.map +0 -1
- package/dist/builder/fileProcessor.d.ts +0 -6
- package/dist/builder/fileProcessor.d.ts.map +0 -1
- package/dist/builder/fileProcessor.js.map +0 -1
- package/dist/builder/gitInitializer.d.ts +0 -2
- package/dist/builder/gitInitializer.d.ts.map +0 -1
- package/dist/builder/gitInitializer.js.map +0 -1
- package/dist/builder/index.d.ts +0 -3
- package/dist/builder/index.d.ts.map +0 -1
- package/dist/builder/index.js.map +0 -1
- package/dist/builder/moduleSelector.d.ts +0 -9
- package/dist/builder/moduleSelector.d.ts.map +0 -1
- package/dist/builder/moduleSelector.js.map +0 -1
- package/dist/builder/templateContext.d.ts +0 -21
- package/dist/builder/templateContext.d.ts.map +0 -1
- package/dist/builder/templateContext.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/intro.d.ts +0 -2
- package/dist/intro.d.ts.map +0 -1
- package/dist/intro.js +0 -27
- package/dist/intro.js.map +0 -1
- package/dist/modules/registry.d.ts +0 -6
- package/dist/modules/registry.d.ts.map +0 -1
- package/dist/modules/registry.js.map +0 -1
- package/dist/output/nextSteps.d.ts +0 -4
- package/dist/output/nextSteps.d.ts.map +0 -1
- package/dist/output/nextSteps.js.map +0 -1
- package/dist/prompts/index.d.ts +0 -4
- package/dist/prompts/index.d.ts.map +0 -1
- package/dist/prompts/index.js.map +0 -1
- package/dist/prompts/stack.d.ts +0 -3
- package/dist/prompts/stack.d.ts.map +0 -1
- package/dist/prompts/stack.js.map +0 -1
- package/dist/types/index.d.ts +0 -159
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/validators/index.d.ts +0 -5
- package/dist/validators/index.d.ts.map +0 -1
- package/dist/validators/index.js.map +0 -1
- package/templates/backend/express/.eslintrc.js +0 -24
|
@@ -1,52 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isInsideGitRepository = isInsideGitRepository;
|
|
6
4
|
exports.initializeGitRepository = initializeGitRepository;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
//
|
|
10
|
-
function
|
|
5
|
+
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
const ui_1 = require("../ui");
|
|
7
|
+
// True when the directory is already tracked by a git repository above it
|
|
8
|
+
function isInsideGitRepository(cwd) {
|
|
11
9
|
try {
|
|
12
|
-
(0,
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
consola_1.default.warn("Git is not installed or not available in PATH");
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
// Initializes a new git repository in the given directory
|
|
21
|
-
function initGit(cwd) {
|
|
22
|
-
consola_1.default.info("Initializing git repository...");
|
|
23
|
-
try {
|
|
24
|
-
(0, child_process_1.execSync)("git init", {
|
|
10
|
+
const output = (0, node_child_process_1.execSync)("git rev-parse --is-inside-work-tree", {
|
|
25
11
|
cwd,
|
|
26
|
-
stdio: "ignore",
|
|
12
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27
13
|
});
|
|
28
|
-
|
|
29
|
-
return true;
|
|
14
|
+
return output.toString().trim() === "true";
|
|
30
15
|
}
|
|
31
16
|
catch {
|
|
32
|
-
consola_1.default.warn("Failed to initialize git repository");
|
|
33
17
|
return false;
|
|
34
18
|
}
|
|
35
19
|
}
|
|
36
|
-
//
|
|
20
|
+
// Initializes a git repository in the given directory
|
|
37
21
|
function initializeGitRepository(cwd) {
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
22
|
+
// A project created inside a repository (apps/api in a monorepo) must not
|
|
23
|
+
// get a nested repository of its own
|
|
24
|
+
if (isInsideGitRepository(cwd)) {
|
|
25
|
+
(0, ui_1.info)("Already inside a git repository, skipped git init");
|
|
42
26
|
return false;
|
|
43
27
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
28
|
+
try {
|
|
29
|
+
(0, node_child_process_1.execSync)("git init", { cwd, stdio: "ignore" });
|
|
30
|
+
(0, ui_1.success)("Git repository initialized");
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// Not fatal: git may not be installed
|
|
35
|
+
(0, ui_1.warn)('Could not initialize git. Run "git init" yourself.');
|
|
48
36
|
return false;
|
|
49
37
|
}
|
|
50
|
-
return true;
|
|
51
38
|
}
|
|
52
|
-
//# sourceMappingURL=gitInitializer.js.map
|
package/dist/builder/index.js
CHANGED
|
@@ -3,123 +3,65 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.planBuild = void 0;
|
|
6
7
|
exports.build = build;
|
|
7
|
-
const consola_1 = __importDefault(require("consola"));
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
-
const registry_1 = require("../modules/registry");
|
|
10
9
|
const nextSteps_1 = require("../output/nextSteps");
|
|
11
10
|
const types_1 = require("../types");
|
|
12
11
|
const validators_1 = require("../validators");
|
|
12
|
+
const buildPlanner_1 = require("./buildPlanner");
|
|
13
13
|
const configGenerator_1 = require("./configGenerator");
|
|
14
14
|
const dependencyInstaller_1 = require("./dependencyInstaller");
|
|
15
15
|
const fileProcessor_1 = require("./fileProcessor");
|
|
16
16
|
const gitInitializer_1 = require("./gitInitializer");
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
var buildPlanner_2 = require("./buildPlanner");
|
|
18
|
+
Object.defineProperty(exports, "planBuild", { enumerable: true, get: function () { return buildPlanner_2.planBuild; } });
|
|
19
19
|
// Main function to orchestrate the project construction process
|
|
20
20
|
async function build(config, targetDir) {
|
|
21
|
-
|
|
21
|
+
// 1. Validate target directory
|
|
22
|
+
const validationResult = await (0, validators_1.validateDirectoryEmpty)(targetDir);
|
|
23
|
+
if (validationResult)
|
|
24
|
+
throw (0, types_1.buildError)(validationResult, "DIRECTORY_NOT_EMPTY");
|
|
25
|
+
const didDirectoryExist = await fs_extra_1.default.pathExists(targetDir);
|
|
22
26
|
try {
|
|
23
|
-
// 1. Validate target directory
|
|
24
|
-
consola_1.default.info("Validating target directory...");
|
|
25
|
-
const validationResult = await (0, validators_1.validateDirectoryEmpty)(targetDir);
|
|
26
|
-
if (validationResult)
|
|
27
|
-
throw (0, types_1.buildError)(validationResult, "DIRECTORY_NOT_EMPTY");
|
|
28
27
|
// 2. Create target directory
|
|
29
|
-
consola_1.default.info("Creating project directory...");
|
|
30
28
|
await fs_extra_1.default.ensureDir(targetDir).catch((e) => {
|
|
31
29
|
throw (0, types_1.buildError)(e, "DIRECTORY_CREATE_ERROR", "Failed to create directory");
|
|
32
30
|
});
|
|
33
|
-
// 3.
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
// 4.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
const baseModule = await (0, registry_1.getModule)(selection.base);
|
|
41
|
-
modules.push({ ...baseModule, framework: config.framework });
|
|
42
|
-
for (const moduleWithFramework of selection.modules) {
|
|
43
|
-
const module = await (0, registry_1.getModule)(moduleWithFramework.id);
|
|
44
|
-
modules.push({ ...module, framework: moduleWithFramework.framework });
|
|
45
|
-
}
|
|
31
|
+
// 3. Load the selected modules, build the template context, and merge
|
|
32
|
+
// the module configurations
|
|
33
|
+
const { modules, templateContext, mergedConfig } = await (0, buildPlanner_1.loadBuildInputs)(config);
|
|
34
|
+
// 4. Copy module files
|
|
35
|
+
for (const module of modules) {
|
|
36
|
+
await (0, fileProcessor_1.copyModuleFiles)(module.path, targetDir);
|
|
46
37
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
consola_1.default.success(`Selected ${modules.length} modules: ${modules.map((m) => m.config.id).join(", ")}`);
|
|
51
|
-
// 5. Build template context
|
|
52
|
-
consola_1.default.info("Building template context...");
|
|
53
|
-
const templateContext = (0, templateContext_1.buildTemplateContext)(config);
|
|
54
|
-
// 6. Merge configurations
|
|
55
|
-
const mergedConfig = (0, configGenerator_1.mergeConfigurations)(modules.map((m) => m.config), templateContext);
|
|
56
|
-
// 7. Copy module files
|
|
57
|
-
consola_1.default.info("Copying module files...");
|
|
58
|
-
try {
|
|
59
|
-
for (const module of modules) {
|
|
60
|
-
consola_1.default.debug(` - Copying ${module.config.id}...`);
|
|
61
|
-
await (0, fileProcessor_1.copyModuleFiles)(module.path, targetDir);
|
|
62
|
-
}
|
|
63
|
-
consola_1.default.success("Module files copied");
|
|
64
|
-
}
|
|
65
|
-
catch (e) {
|
|
66
|
-
throw (0, types_1.buildError)(e, "FILE_COPY_ERROR", "Failed to copy module files");
|
|
67
|
-
}
|
|
68
|
-
// 8. Process Handlebars templates
|
|
69
|
-
consola_1.default.info("Processing template files...");
|
|
70
|
-
try {
|
|
71
|
-
const allTemplateFiles = modules.flatMap((m) => m.config.templateFiles);
|
|
72
|
-
if (allTemplateFiles.length > 0) {
|
|
73
|
-
await (0, fileProcessor_1.processTemplateFiles)(targetDir, templateContext, allTemplateFiles);
|
|
74
|
-
consola_1.default.success(`Processed ${allTemplateFiles.length} template file(s)`);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
consola_1.default.info(" No template files to process");
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch (e) {
|
|
81
|
-
throw (0, types_1.buildError)(e, "TEMPLATE_PROCESSING_ERROR", "Failed to process template files");
|
|
82
|
-
}
|
|
83
|
-
// 9. Generate package.json
|
|
84
|
-
consola_1.default.info("Generating package.json...");
|
|
38
|
+
// 5. Process Handlebars templates
|
|
39
|
+
await (0, fileProcessor_1.processTemplateFiles)(targetDir, templateContext);
|
|
40
|
+
// 6. Generate package.json
|
|
85
41
|
await (0, configGenerator_1.generatePackageJson)(targetDir, mergedConfig, config);
|
|
86
42
|
if (config.packageManager === "pnpm") {
|
|
87
43
|
await (0, configGenerator_1.generatePnpmWorkspace)(targetDir, mergedConfig.overrides);
|
|
88
44
|
}
|
|
89
|
-
|
|
90
|
-
// 10. Generate environment files
|
|
91
|
-
consola_1.default.info("Generating environment files...");
|
|
45
|
+
// 7. Generate environment files
|
|
92
46
|
await (0, configGenerator_1.generateEnvFile)(targetDir, mergedConfig.envVars);
|
|
93
|
-
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
catch {
|
|
101
|
-
consola_1.default.warn("Continuing despite dependency installation issues");
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
// 12. Initialize git (optional)
|
|
105
|
-
if (config.initGit) {
|
|
106
|
-
try {
|
|
107
|
-
(0, gitInitializer_1.initializeGitRepository)(targetDir);
|
|
108
|
-
}
|
|
109
|
-
catch {
|
|
110
|
-
consola_1.default.warn("Continuing despite git initialization issues");
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// 13. Done!
|
|
114
|
-
consola_1.default.success("Project building completed successfully!");
|
|
47
|
+
// 8. Install dependencies and initialize git (both optional); a failure
|
|
48
|
+
// in either is reported as a warning and does not fail the build
|
|
49
|
+
const depsInstalled = config.installDeps &&
|
|
50
|
+
(0, dependencyInstaller_1.installDependencies)(config.packageManager, targetDir);
|
|
51
|
+
if (config.initGit)
|
|
52
|
+
(0, gitInitializer_1.initializeGitRepository)(targetDir);
|
|
115
53
|
return {
|
|
116
|
-
success: true,
|
|
117
54
|
projectPath: targetDir,
|
|
118
|
-
nextSteps: (0, nextSteps_1.generateNextSteps)(config, depsInstalled),
|
|
55
|
+
nextSteps: (0, nextSteps_1.generateNextSteps)(config, targetDir, depsInstalled),
|
|
119
56
|
};
|
|
120
57
|
}
|
|
121
58
|
catch (error) {
|
|
122
|
-
|
|
59
|
+
// Remove the partial project so a retry is not blocked by a non-empty
|
|
60
|
+
// directory; a directory that already existed is emptied, not deleted
|
|
61
|
+
await (didDirectoryExist ? fs_extra_1.default.emptyDir(targetDir) : fs_extra_1.default.remove(targetDir)).catch(() => undefined);
|
|
62
|
+
// Errors raised by the build steps already say what failed
|
|
63
|
+
throw (0, types_1.isBuildError)(error)
|
|
64
|
+
? error
|
|
65
|
+
: (0, types_1.buildError)(error, "BUILDING_ERROR", "Building failed");
|
|
123
66
|
}
|
|
124
67
|
}
|
|
125
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -3,24 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.selectModules = selectModules;
|
|
4
4
|
// Determines which base and feature modules to include based on user configuration
|
|
5
5
|
function selectModules(config) {
|
|
6
|
-
|
|
7
|
-
const framework = config.framework;
|
|
6
|
+
const { framework } = config;
|
|
8
7
|
// Select base template based on app type
|
|
9
8
|
const base = config.applicationType === "backend"
|
|
10
|
-
? `backend/${
|
|
11
|
-
: `frontend/${
|
|
9
|
+
? `backend/${framework}`
|
|
10
|
+
: `frontend/${framework}`;
|
|
12
11
|
const modules = [];
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (config.useAuth) {
|
|
22
|
-
modules.push({ id: `auth/${framework}`, framework });
|
|
23
|
-
}
|
|
12
|
+
// Database and auth modules have one variant per framework (prisma/express,
|
|
13
|
+
// auth/nextjs, ...); the Docker module is shared
|
|
14
|
+
if (config.database !== "none")
|
|
15
|
+
modules.push(`${config.database}/${framework}`);
|
|
16
|
+
if (config.useDocker)
|
|
17
|
+
modules.push("docker");
|
|
18
|
+
if (config.useAuth)
|
|
19
|
+
modules.push(`auth/${framework}`);
|
|
24
20
|
return { base, modules };
|
|
25
21
|
}
|
|
26
|
-
//# sourceMappingURL=moduleSelector.js.map
|
|
@@ -1,38 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.lowercase = lowercase;
|
|
4
|
-
exports.uppercase = uppercase;
|
|
5
|
-
exports.kebabCase = kebabCase;
|
|
6
3
|
exports.buildTemplateContext = buildTemplateContext;
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
return str.toLowerCase();
|
|
10
|
-
}
|
|
11
|
-
// Converts string to uppercase
|
|
12
|
-
function uppercase(str) {
|
|
13
|
-
return str.toUpperCase();
|
|
14
|
-
}
|
|
15
|
-
// Converts string to kebab-case
|
|
16
|
-
function kebabCase(str) {
|
|
17
|
-
return str
|
|
18
|
-
.replace(/([a-z])([A-Z])/g, "$1-$2") // camelCase to kebab-case
|
|
19
|
-
.replace(/[\s_]+/g, "-") // spaces and underscores to hyphens
|
|
20
|
-
.toLowerCase();
|
|
21
|
-
}
|
|
4
|
+
// Context builder for Handlebars, providing variables and helpers for templates
|
|
5
|
+
const types_1 = require("../types");
|
|
22
6
|
function buildTemplateContext(config) {
|
|
23
|
-
const framework = config.framework;
|
|
24
7
|
return {
|
|
25
8
|
projectName: config.projectName,
|
|
26
9
|
packageManager: config.packageManager,
|
|
27
|
-
database: config.database
|
|
28
|
-
framework,
|
|
29
|
-
port:
|
|
30
|
-
useDocker: config.useDocker
|
|
10
|
+
database: config.database,
|
|
11
|
+
framework: config.framework,
|
|
12
|
+
port: types_1.DEFAULT_PORT,
|
|
13
|
+
useDocker: config.useDocker,
|
|
31
14
|
useAuth: config.useAuth,
|
|
32
15
|
helpers: {
|
|
33
|
-
lowercase,
|
|
34
|
-
uppercase,
|
|
35
|
-
kebabCase,
|
|
36
16
|
eq: (a, b) => a === b,
|
|
37
17
|
runner: () => {
|
|
38
18
|
if (config.packageManager === "bun")
|
|
@@ -42,4 +22,3 @@ function buildTemplateContext(config) {
|
|
|
42
22
|
},
|
|
43
23
|
};
|
|
44
24
|
}
|
|
45
|
-
//# sourceMappingURL=templateContext.js.map
|
package/dist/index.js
CHANGED
|
@@ -4,31 +4,57 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const node_process_1 = require("node:process");
|
|
10
|
+
const args_1 = require("./args");
|
|
11
11
|
const builder_1 = require("./builder");
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
12
|
+
const prompts_1 = require("./prompts");
|
|
13
|
+
const types_1 = require("./types");
|
|
14
|
+
const ui_1 = require("./ui");
|
|
15
|
+
const validators_1 = require("./validators");
|
|
15
16
|
// Main entry point for the CLI tool
|
|
16
17
|
async function main() {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
if (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
(0, prompts_1.outro)("Happy coding!");
|
|
27
|
-
process.exit(0);
|
|
18
|
+
const args = (0, args_1.parseCliArgs)(process.argv.slice(2));
|
|
19
|
+
if (args.help) {
|
|
20
|
+
console.log(args_1.HELP_TEXT);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const { version } = (await fs_extra_1.default.readJson(node_path_1.default.join(__dirname, "../package.json")));
|
|
24
|
+
if (args.version) {
|
|
25
|
+
console.log(version);
|
|
26
|
+
return;
|
|
28
27
|
}
|
|
28
|
+
(0, ui_1.showBanner)(version, !args.yes);
|
|
29
|
+
const projectPath = await (0, prompts_1.collectProjectPath)(args.projectPath, args.yes);
|
|
30
|
+
const targetDir = node_path_1.default.resolve((0, node_process_1.cwd)(), projectPath);
|
|
31
|
+
// Fail before the remaining prompts when the directory cannot be used
|
|
32
|
+
const directoryError = await (0, validators_1.validateDirectoryEmpty)(targetDir);
|
|
33
|
+
if (directoryError)
|
|
34
|
+
throw (0, types_1.buildError)(directoryError, "DIRECTORY_NOT_EMPTY");
|
|
35
|
+
const choices = await (0, prompts_1.collectStackChoices)(args.yes ? { ...types_1.DEFAULT_CHOICES, ...args.choices } : args.choices);
|
|
36
|
+
const config = {
|
|
37
|
+
projectName: node_path_1.default.basename(targetDir),
|
|
38
|
+
applicationType: (0, types_1.getApplicationType)(choices.framework),
|
|
39
|
+
...choices,
|
|
40
|
+
};
|
|
41
|
+
(0, ui_1.showSummary)(config, targetDir);
|
|
42
|
+
if (args.dryRun) {
|
|
43
|
+
(0, ui_1.showPlan)(await (0, builder_1.planBuild)(config, targetDir));
|
|
44
|
+
(0, ui_1.info)("Dry run: no files were written.");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Without a terminal nobody can answer, and every option came from a flag
|
|
48
|
+
if (!args.yes && process.stdin.isTTY)
|
|
49
|
+
await (0, prompts_1.confirmBuild)();
|
|
50
|
+
const result = await (0, builder_1.build)(config, targetDir);
|
|
51
|
+
(0, ui_1.success)("Project created");
|
|
52
|
+
(0, ui_1.showNextSteps)(result.nextSteps);
|
|
29
53
|
}
|
|
30
|
-
main()
|
|
31
|
-
|
|
54
|
+
main()
|
|
55
|
+
.then(() => process.exit(0))
|
|
56
|
+
.catch((err) => {
|
|
57
|
+
// Errors raised by the CLI carry a code and a message meant for the user
|
|
58
|
+
(0, ui_1.error)((0, types_1.isBuildError)(err) ? err.message : String(err));
|
|
32
59
|
process.exit(1);
|
|
33
60
|
});
|
|
34
|
-
//# sourceMappingURL=index.js.map
|
package/dist/modules/registry.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.getModule = getModule;
|
|
7
7
|
// Module registry for accessing base templates and feature modules
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
-
const
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const zod_1 = require("zod");
|
|
11
11
|
const types_1 = require("../types");
|
|
12
12
|
const moduleConfigSchema = zod_1.z.object({
|
|
@@ -19,20 +19,19 @@ const moduleConfigSchema = zod_1.z.object({
|
|
|
19
19
|
overrides: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).default({}),
|
|
20
20
|
scripts: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).default({}),
|
|
21
21
|
envVars: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).default({}),
|
|
22
|
-
templateFiles: zod_1.z.array(zod_1.z.string()).default([]),
|
|
23
22
|
});
|
|
24
23
|
// Determines the absolute filesystem path for a given module ID
|
|
25
24
|
function resolveModulePath(id) {
|
|
26
25
|
// Base templates: backend/express, frontend/nextjs
|
|
27
26
|
if (id.startsWith("backend/") || id.startsWith("frontend/")) {
|
|
28
|
-
return
|
|
27
|
+
return node_path_1.default.join(__dirname, `../../templates/${id}`);
|
|
29
28
|
}
|
|
30
29
|
// Feature modules: auth/express, prisma/nextjs, docker, mongoose, etc.
|
|
31
|
-
return
|
|
30
|
+
return node_path_1.default.join(__dirname, `../../templates/modules/${id}`);
|
|
32
31
|
}
|
|
33
32
|
// Reads and parses a module's config.json file
|
|
34
33
|
async function loadModuleConfig(modulePath) {
|
|
35
|
-
const configPath =
|
|
34
|
+
const configPath = node_path_1.default.join(modulePath, "config.json");
|
|
36
35
|
try {
|
|
37
36
|
const configContent = await fs_extra_1.default.readFile(configPath, "utf-8");
|
|
38
37
|
const rawConfig = JSON.parse(configContent);
|
|
@@ -58,4 +57,3 @@ async function getModule(id) {
|
|
|
58
57
|
path: modulePath,
|
|
59
58
|
};
|
|
60
59
|
}
|
|
61
|
-
//# sourceMappingURL=registry.js.map
|
package/dist/output/nextSteps.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.generateNextSteps = generateNextSteps;
|
|
4
|
-
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_process_1 = require("node:process");
|
|
5
9
|
const MONGODB_AUTH_SETUP_URL = "https://better-ts-stack.abdullahtech.me/docs/modules/database#mongoose-and-mongodb";
|
|
6
10
|
// Node 24 releases before 24.20 report mismatched file stats on Windows, which
|
|
7
11
|
// makes srvx (TanStack's production server) reject every static asset
|
|
@@ -13,11 +17,13 @@ function isNodeBelow([major, minor]) {
|
|
|
13
17
|
return (currentMajor < major || (currentMajor === major && currentMinor < minor));
|
|
14
18
|
}
|
|
15
19
|
// Generates a list of instructions for the user to follow after project creation
|
|
16
|
-
function generateNextSteps(config, depsInstalled) {
|
|
20
|
+
function generateNextSteps(config, targetDir, depsInstalled) {
|
|
17
21
|
const steps = [];
|
|
18
22
|
const isFullstack = config.applicationType === "fullstack";
|
|
19
|
-
// Step 1: cd into project directory
|
|
20
|
-
|
|
23
|
+
// Step 1: cd into project directory, unless it was created in place
|
|
24
|
+
const relativeDir = node_path_1.default.relative((0, node_process_1.cwd)(), targetDir);
|
|
25
|
+
if (relativeDir)
|
|
26
|
+
steps.push(`cd ${relativeDir}`);
|
|
21
27
|
// Step 2: Set environment variables
|
|
22
28
|
if (config.database !== "none") {
|
|
23
29
|
steps.push("Review the generated .env and set your database connection details");
|
|
@@ -64,19 +70,3 @@ function generateNextSteps(config, depsInstalled) {
|
|
|
64
70
|
}
|
|
65
71
|
return steps;
|
|
66
72
|
}
|
|
67
|
-
// Formats the next steps into a success message string
|
|
68
|
-
function displayNextSteps(_projectName, steps) {
|
|
69
|
-
const formattedSteps = steps
|
|
70
|
-
.map((step, index) => ` ${index + 1}. ${step}`)
|
|
71
|
-
.join("\n");
|
|
72
|
-
const message = `
|
|
73
|
-
✨ Project created successfully!
|
|
74
|
-
|
|
75
|
-
Next steps:
|
|
76
|
-
${formattedSteps}
|
|
77
|
-
|
|
78
|
-
Your server will be running at http://localhost:3000
|
|
79
|
-
`;
|
|
80
|
-
return message;
|
|
81
|
-
}
|
|
82
|
-
//# sourceMappingURL=nextSteps.js.map
|
package/dist/prompts/index.js
CHANGED
|
@@ -1,71 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
3
|
+
exports.collectStackChoices = void 0;
|
|
4
|
+
exports.collectProjectPath = collectProjectPath;
|
|
7
5
|
exports.confirmBuild = confirmBuild;
|
|
8
|
-
const prompts_1 = require("@clack/prompts");
|
|
9
|
-
const consola_1 = __importDefault(require("consola"));
|
|
10
6
|
const types_1 = require("../types");
|
|
7
|
+
const ui_1 = require("../ui");
|
|
11
8
|
const validators_1 = require("../validators");
|
|
12
9
|
const stack_1 = require("./stack");
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
(0, prompts_1.cancel)("Operation cancelled.");
|
|
22
|
-
process.exit(0);
|
|
10
|
+
var stack_2 = require("./stack");
|
|
11
|
+
Object.defineProperty(exports, "collectStackChoices", { enumerable: true, get: function () { return stack_2.collectStackChoices; } });
|
|
12
|
+
// Returns the project path from the command line, or asks for it
|
|
13
|
+
async function collectProjectPath(preset, isNonInteractive) {
|
|
14
|
+
if (preset !== undefined) {
|
|
15
|
+
const error = (0, validators_1.validateProjectPath)(preset);
|
|
16
|
+
if (error)
|
|
17
|
+
throw (0, types_1.buildError)(error, "INVALID_ARGUMENT");
|
|
23
18
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
options: types_1.applicationTypeOptions,
|
|
27
|
-
initialValue: "backend",
|
|
28
|
-
});
|
|
29
|
-
if ((0, prompts_1.isCancel)(applicationType)) {
|
|
30
|
-
(0, prompts_1.cancel)("Operation cancelled.");
|
|
31
|
-
process.exit(0);
|
|
19
|
+
else if (isNonInteractive) {
|
|
20
|
+
throw (0, types_1.buildError)("A project path is required with --yes, for example: better-ts-stack my-app --yes", "INVALID_ARGUMENT");
|
|
32
21
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
applicationType,
|
|
39
|
-
...userChoices,
|
|
40
|
-
database,
|
|
41
|
-
};
|
|
42
|
-
return choices;
|
|
22
|
+
return (0, stack_1.ask)(preset, () => (0, ui_1.text)({
|
|
23
|
+
message: "Project name",
|
|
24
|
+
placeholder: "my-awesome-project",
|
|
25
|
+
validate: (value) => (0, validators_1.validateProjectPath)(value ?? ""),
|
|
26
|
+
}));
|
|
43
27
|
}
|
|
44
|
-
//
|
|
45
|
-
async function confirmBuild(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
consola_1.default.box(`Project Name: ${config.projectName}\n` +
|
|
49
|
-
`Target Dir: ${targetDir}\n` +
|
|
50
|
-
`App Type: ${appTypeLabel}\n` +
|
|
51
|
-
`Framework: ${config.framework}\n` +
|
|
52
|
-
`Database: ${config.database}\n` +
|
|
53
|
-
`Auth: ${config.useAuth ? "Yes" : "No"}\n` +
|
|
54
|
-
`Docker: ${config.useDocker ? "Yes" : "No"}\n` +
|
|
55
|
-
`Package Mgr: ${config.packageManager}\n` +
|
|
56
|
-
`Git Init: ${config.initGit ? "Yes" : "No"}\n` +
|
|
57
|
-
`Install Deps: ${config.installDeps ? "Yes" : "No"}`);
|
|
58
|
-
const shouldContinue = await (0, prompts_1.confirm)({
|
|
59
|
-
message: "Looks good? Ready to build?",
|
|
60
|
-
initialValue: true,
|
|
61
|
-
});
|
|
62
|
-
if ((0, prompts_1.isCancel)(shouldContinue)) {
|
|
63
|
-
(0, prompts_1.cancel)("Operation cancelled.");
|
|
64
|
-
process.exit(0);
|
|
65
|
-
}
|
|
66
|
-
if (!shouldContinue) {
|
|
67
|
-
(0, prompts_1.cancel)("Building cancelled by user.");
|
|
28
|
+
// Asks for final confirmation before building; declining ends the session
|
|
29
|
+
async function confirmBuild() {
|
|
30
|
+
const shouldBuild = await (0, stack_1.ask)(undefined, () => (0, ui_1.confirm)({ message: "Create this project?", initialValue: true }));
|
|
31
|
+
if (!shouldBuild) {
|
|
68
32
|
process.exit(0);
|
|
69
33
|
}
|
|
70
34
|
}
|
|
71
|
-
//# sourceMappingURL=index.js.map
|