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/LICENSE +21 -0
- package/README.md +256 -0
- package/dist/chunk-RFS7HGZ6.js +1079 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +157 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +10 -0
- package/package.json +50 -0
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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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
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
|
+
}
|