create-basalt 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ TargetNotEmptyError,
4
+ createProject,
5
+ detectPackageManager
6
+ } from "./chunk-RFS7HGZ6.js";
7
+
8
+ // src/cli.ts
9
+ import { spawn } from "child_process";
10
+ import { stdin, stdout } from "process";
11
+ import { createInterface } from "readline/promises";
12
+ var USAGE = `Usage: npm create basalt <name> [options]
13
+
14
+ Options:
15
+ --dir=<path> Target directory (default: ./<name>)
16
+ --no-tenancy Skip multi-tenancy
17
+ --no-auth Skip authentication
18
+ --billing Include subscriptions/billing
19
+ --ui Scaffold a web/ frontend (React + shadcn + SDK)
20
+ --cli Scaffold the 'basalt' CLI (code generators + commands)
21
+ --install Install dependencies after scaffolding
22
+ --git Initialize a git repository with a first commit
23
+ --pm=<manager> Package manager: pnpm | npm | yarn | bun (default: auto-detect)
24
+ -y, --yes Skip prompts and accept defaults
25
+ -h, --help Show this help
26
+
27
+ Run with no name in a terminal to be prompted interactively.
28
+ `;
29
+ function parseArgs(argv) {
30
+ const flags2 = {
31
+ tenancy: true,
32
+ auth: true,
33
+ billing: false,
34
+ ui: false,
35
+ cli: false,
36
+ install: false,
37
+ git: false,
38
+ yes: false
39
+ };
40
+ for (const token of argv) {
41
+ if (token === "--no-tenancy") flags2.tenancy = false;
42
+ else if (token === "--no-auth") flags2.auth = false;
43
+ else if (token === "--billing") flags2.billing = true;
44
+ else if (token === "--ui") flags2.ui = true;
45
+ else if (token === "--cli") flags2.cli = true;
46
+ else if (token === "--install") flags2.install = true;
47
+ else if (token === "--git") flags2.git = true;
48
+ else if (token === "-y" || token === "--yes") flags2.yes = true;
49
+ else if (token.startsWith("--dir=")) flags2.dir = token.slice("--dir=".length);
50
+ else if (token.startsWith("--pm=")) flags2.pm = token.slice("--pm=".length);
51
+ else if (token === "--help" || token === "-h") {
52
+ stdout.write(USAGE);
53
+ process.exit(0);
54
+ } else if (!token.startsWith("--") && flags2.name === void 0) flags2.name = token;
55
+ }
56
+ return flags2;
57
+ }
58
+ function run(command, args, cwd) {
59
+ return new Promise((resolve) => {
60
+ const child = spawn(command, args, {
61
+ cwd,
62
+ stdio: "inherit",
63
+ shell: process.platform === "win32"
64
+ });
65
+ child.on("error", () => resolve(false));
66
+ child.on("close", (code) => resolve(code === 0));
67
+ });
68
+ }
69
+ function cancel() {
70
+ stdout.write("\nCancelled.\n");
71
+ process.exit(130);
72
+ }
73
+ async function promptMissing(flags2) {
74
+ if (flags2.yes || flags2.name !== void 0 || !stdin.isTTY) return flags2;
75
+ const rl = createInterface({ input: stdin, output: stdout });
76
+ rl.on("SIGINT", cancel);
77
+ try {
78
+ const ask = async (question, fallback) => {
79
+ const answer = (await rl.question(`${question} `)).trim();
80
+ return answer || fallback;
81
+ };
82
+ const confirm = async (question, fallback) => {
83
+ const answer = (await rl.question(`${question} (${fallback ? "Y/n" : "y/N"}) `)).trim().toLowerCase();
84
+ return answer ? answer.startsWith("y") : fallback;
85
+ };
86
+ flags2.name = await ask("Project name:", "my-saas");
87
+ flags2.tenancy = await confirm("Multi-tenancy?", true);
88
+ flags2.auth = await confirm("Authentication?", true);
89
+ flags2.billing = await confirm("Subscriptions / billing?", false);
90
+ flags2.ui = await confirm("Web UI (React + shadcn)?", false);
91
+ flags2.cli = await confirm("'basalt' CLI (code generators)?", false);
92
+ flags2.install = await confirm("Install dependencies now?", false);
93
+ flags2.git = await confirm("Initialize a git repository?", false);
94
+ } catch (error) {
95
+ if (error.code === "ABORT_ERR") cancel();
96
+ throw error;
97
+ } finally {
98
+ rl.close();
99
+ }
100
+ return flags2;
101
+ }
102
+ var flags = await promptMissing(parseArgs(process.argv.slice(2)));
103
+ if (!flags.name) {
104
+ stdout.write(USAGE);
105
+ process.exit(1);
106
+ }
107
+ var pm = flags.pm ?? detectPackageManager();
108
+ if (flags.ui && pm !== "pnpm") {
109
+ console.log(`Note: --ui projects are pnpm workspaces \u2014 using pnpm instead of ${pm}.`);
110
+ pm = "pnpm";
111
+ }
112
+ try {
113
+ const result = await createProject({
114
+ name: flags.name,
115
+ ...flags.dir ? { dir: flags.dir } : {},
116
+ tenancy: flags.tenancy,
117
+ auth: flags.auth,
118
+ billing: flags.billing,
119
+ ui: flags.ui,
120
+ cli: flags.cli
121
+ });
122
+ console.log(`
123
+ Created ${result.options.name} in ${result.dir}
124
+ `);
125
+ for (const file of result.files) console.log(` ${file}`);
126
+ if (flags.git) {
127
+ console.log("\nInitializing git repository\u2026");
128
+ const ok = await run("git", ["init", "-q"], result.dir) && await run("git", ["add", "-A"], result.dir) && await run(
129
+ "git",
130
+ ["commit", "-q", "-m", "Initial commit from create-basalt"],
131
+ result.dir
132
+ );
133
+ console.log(
134
+ ok ? " git repository initialized." : " (skipped \u2014 git unavailable or already a repo)"
135
+ );
136
+ }
137
+ if (flags.install) {
138
+ console.log(`
139
+ Installing dependencies with ${pm}\u2026`);
140
+ const ok = await run(pm, ["install"], result.dir);
141
+ if (!ok) console.log(` (install failed \u2014 run "${pm} install" yourself)`);
142
+ }
143
+ const steps = [`cd ${result.dir}`];
144
+ if (!flags.install) steps.push(`${pm} install`);
145
+ steps.push(`${pm} run dev${flags.ui ? " # API on :3000" : ""}`);
146
+ if (flags.ui) steps.push(`pnpm --filter ${result.options.name}-web dev # UI on :5180`);
147
+ console.log(`
148
+ Next steps:
149
+ ${steps.map((s) => ` ${s}`).join("\n")}
150
+ `);
151
+ } catch (error) {
152
+ if (error instanceof TargetNotEmptyError) {
153
+ console.error(error.message);
154
+ process.exit(1);
155
+ }
156
+ throw error;
157
+ }
@@ -0,0 +1,41 @@
1
+ interface ProjectOptions {
2
+ name: string;
3
+ tenancy: boolean;
4
+ auth: boolean;
5
+ billing: boolean;
6
+ /** Scaffold a web/ frontend (React + shadcn + SDK). */
7
+ ui: boolean;
8
+ /** Scaffold the `basalt` CLI entrypoint (code generators + built-in commands). */
9
+ cli: boolean;
10
+ }
11
+
12
+ interface CreateProjectInput {
13
+ name: string;
14
+ /** Target directory. Default: ./<name> under cwd. */
15
+ dir?: string;
16
+ tenancy?: boolean;
17
+ auth?: boolean;
18
+ billing?: boolean;
19
+ /** Scaffold a web/ frontend (React + shadcn + SDK). Default: false. */
20
+ ui?: boolean;
21
+ /** Scaffold the `basalt` CLI entrypoint (code generators + commands). Default: false. */
22
+ cli?: boolean;
23
+ }
24
+ interface CreateProjectResult {
25
+ dir: string;
26
+ files: string[];
27
+ options: ProjectOptions;
28
+ }
29
+ declare class TargetNotEmptyError extends Error {
30
+ constructor(dir: string);
31
+ }
32
+ type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';
33
+ /**
34
+ * Detects the package manager that invoked `npm create` / `npx`, via the
35
+ * `npm_config_user_agent` env var npm/pnpm/yarn/bun all set. Falls back to npm.
36
+ */
37
+ declare function detectPackageManager(userAgent?: string | undefined): PackageManager;
38
+ /** Generates a ready-to-run Basalt app. Does not install dependencies. */
39
+ declare function createProject(input: CreateProjectInput): Promise<CreateProjectResult>;
40
+
41
+ export { type CreateProjectInput, type CreateProjectResult, type PackageManager, type ProjectOptions, TargetNotEmptyError, createProject, detectPackageManager };
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ import {
2
+ TargetNotEmptyError,
3
+ createProject,
4
+ detectPackageManager
5
+ } from "./chunk-RFS7HGZ6.js";
6
+ export {
7
+ TargetNotEmptyError,
8
+ createProject,
9
+ detectPackageManager
10
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "create-basalt",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold a production-shaped Basalt SaaS app: npm create basalt my-saas [--billing] [--ui] [--cli] [--install] [--git]",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "create-basalt": "./dist/cli.js"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "devDependencies": {
20
+ "@types/node": "^22.15.0",
21
+ "tsup": "^8.4.0",
22
+ "typescript": "^5.8.0",
23
+ "vitest": "^3.1.0",
24
+ "@basaltkit/tsconfig": "^0.24.0"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/Zebedeu/basalt.git",
32
+ "directory": "packages/create-app"
33
+ },
34
+ "homepage": "https://github.com/Zebedeu/basalt/tree/main/packages/create-app#readme",
35
+ "bugs": "https://github.com/Zebedeu/basalt/issues",
36
+ "keywords": [
37
+ "basalt",
38
+ "typescript",
39
+ "saas",
40
+ "scaffolding",
41
+ "create",
42
+ "starter",
43
+ "boilerplate"
44
+ ],
45
+ "scripts": {
46
+ "build": "tsup src/index.ts src/cli.ts --format esm --dts --clean",
47
+ "test": "vitest run",
48
+ "typecheck": "tsc --noEmit"
49
+ }
50
+ }