@sb-codex/create-sb-app 0.0.2 → 0.0.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.
- package/README.md +22 -17
- package/dist/index.js +13 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,46 +1,51 @@
|
|
|
1
1
|
# @sb-codex/create-sb-app
|
|
2
2
|
|
|
3
|
-
Scaffold a new multi-tenant SaaS project from the [sb-codex starter](https://github.com/SB-SLIM/react-app-starter).
|
|
3
|
+
Scaffold a new multi-tenant SaaS project from the [sb-codex starter](https://github.com/SB-SLIM/react-app-starter).
|
|
4
|
+
|
|
5
|
+
It generates an **apps-only** project: you get `apps/` (admin, server, web, e2e) and the `@sb-codex/*` plugins are pulled from **npm** at their published versions — there is **no `packages/` folder** to maintain. The CLI also injects each plugin's peer dependencies into the consuming apps so the project installs cleanly.
|
|
4
6
|
|
|
5
7
|
## Usage
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
pnpm create @sb-codex/sb-app my-saas
|
|
9
|
-
# or: npm create @sb-codex/sb-app my-saas
|
|
10
|
+
pnpm create @sb-codex/sb-app@latest my-saas
|
|
11
|
+
# or: npm create @sb-codex/sb-app@latest my-saas
|
|
10
12
|
```
|
|
11
13
|
|
|
14
|
+
> The `@latest` tag forces the newest version and avoids npx serving a cached older release.
|
|
15
|
+
|
|
12
16
|
Prompts:
|
|
13
17
|
|
|
14
18
|
- **Project directory** — where to create the project
|
|
15
19
|
- **Project name** — root `package.json` name
|
|
16
|
-
- **Production domain** — replaces the reference domain
|
|
20
|
+
- **Production domain** — replaces the reference domain. **Defaults to `localhost`** for local dev (e.g. `hub.localhost`)
|
|
17
21
|
- **git init** — start with fresh git history
|
|
18
22
|
|
|
19
23
|
Then:
|
|
20
24
|
|
|
21
25
|
```bash
|
|
22
26
|
cd my-saas
|
|
23
|
-
pnpm install
|
|
27
|
+
pnpm install # plugins resolved from npm — no local build
|
|
24
28
|
pnpm dev
|
|
25
29
|
```
|
|
26
30
|
|
|
27
|
-
## What it
|
|
31
|
+
## What it does
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
| `SB-SLIM/react-app-starter` | `your-gh-org/<name>` |
|
|
34
|
-
| `152.53.187.54` | `YOUR_VPS_IP` |
|
|
35
|
-
| Notion page id | `YOUR_NOTION_PAGE_ID` |
|
|
33
|
+
- Pulls `apps/` from the starter; rewrites each `@sb-codex/*` dependency from `workspace:^` to the **published npm version** and injects that plugin's peer dependencies
|
|
34
|
+
- Removes the `packages/` source, trims `pnpm-workspace.yaml` to `apps/*`, strips `@sb-codex/*` from `pnpm.overrides`, drops the changeset config
|
|
35
|
+
- Copies `.env.example` → `.env`
|
|
36
|
+
- Replaces project-specific tokens:
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
| Token | Replaced with |
|
|
39
|
+
| ----------------------------------- | --------------------------------- |
|
|
40
|
+
| `slimbouchoucha.tn` | your domain (default `localhost`) |
|
|
41
|
+
| `ghcr.io/sb-slim/react-app-starter` | `ghcr.io/your-gh-org/<name>` |
|
|
42
|
+
| `SB-SLIM/react-app-starter` | `your-gh-org/<name>` |
|
|
43
|
+
| Notion page id | `YOUR_NOTION_PAGE_ID` |
|
|
38
44
|
|
|
39
|
-
##
|
|
45
|
+
## Non-interactive flags
|
|
40
46
|
|
|
41
47
|
```bash
|
|
42
|
-
|
|
43
|
-
node packages/create-sb-app/dist/index.js ../my-saas
|
|
48
|
+
npx @sb-codex/create-sb-app my-saas --name my-saas --domain localhost --no-git
|
|
44
49
|
```
|
|
45
50
|
|
|
46
51
|
---
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
rmSync as rmSync2,
|
|
10
10
|
copyFileSync
|
|
11
11
|
} from "fs";
|
|
12
|
-
import { join as join2, extname
|
|
12
|
+
import { join as join2, extname } from "path";
|
|
13
13
|
import { execSync } from "child_process";
|
|
14
14
|
import {
|
|
15
15
|
intro,
|
|
@@ -44,8 +44,6 @@ function buildReplacements(answers) {
|
|
|
44
44
|
},
|
|
45
45
|
// Remaining bare repo name (URLs, image names)
|
|
46
46
|
{ find: /react-app-starter/g, replace: name },
|
|
47
|
-
// VPS IP
|
|
48
|
-
{ find: /152\.53\.187\.54/g, replace: "YOUR_VPS_IP" },
|
|
49
47
|
// Notion page id (mirror of architecture docs — project specific)
|
|
50
48
|
{ find: NOTION_PAGE_ID_DASHED, replace: "YOUR_NOTION_PAGE_ID" },
|
|
51
49
|
{ find: NOTION_PAGE_ID_COMPACT, replace: "YOUR_NOTION_PAGE_ID" }
|
|
@@ -134,7 +132,8 @@ function applyAppsOnly(targetDir) {
|
|
|
134
132
|
}
|
|
135
133
|
}
|
|
136
134
|
}
|
|
137
|
-
if (existsSync(packagesDir))
|
|
135
|
+
if (existsSync(packagesDir))
|
|
136
|
+
rmSync(packagesDir, { recursive: true, force: true });
|
|
138
137
|
const wsPath = join(targetDir, "pnpm-workspace.yaml");
|
|
139
138
|
if (existsSync(wsPath)) {
|
|
140
139
|
const kept = readFileSync(wsPath, "utf8").split(/\r?\n/).filter((line) => !/['"]packages\/\*['"]/.test(line)).join("\n");
|
|
@@ -152,7 +151,8 @@ function applyAppsOnly(targetDir) {
|
|
|
152
151
|
writeJson(rootPj, pkg);
|
|
153
152
|
}
|
|
154
153
|
const changesetDir = join(targetDir, ".changeset");
|
|
155
|
-
if (existsSync(changesetDir))
|
|
154
|
+
if (existsSync(changesetDir))
|
|
155
|
+
rmSync(changesetDir, { recursive: true, force: true });
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
// src/index.ts
|
|
@@ -204,43 +204,32 @@ async function main() {
|
|
|
204
204
|
intro(pc.bgCyan(pc.black(" create-sb-app ")));
|
|
205
205
|
const flags = parseFlags(process.argv.slice(2));
|
|
206
206
|
const positional = process.argv.slice(2).find((a) => !a.startsWith("--"));
|
|
207
|
-
let target = positional ?? (typeof flags.
|
|
207
|
+
let target = positional ?? (typeof flags.name === "string" ? flags.name : void 0);
|
|
208
208
|
if (!target) {
|
|
209
209
|
const answer = await text({
|
|
210
|
-
message: "Project
|
|
210
|
+
message: "Project name?",
|
|
211
211
|
placeholder: "my-saas",
|
|
212
|
-
validate: (v) => v.length === 0 ? "
|
|
212
|
+
validate: (v) => slugify(v).length === 0 ? "Invalid name" : void 0
|
|
213
213
|
});
|
|
214
214
|
if (isCancel(answer)) bail("Cancelled.");
|
|
215
215
|
target = answer;
|
|
216
216
|
}
|
|
217
|
+
const name = slugify(target);
|
|
217
218
|
const targetDir = join2(process.cwd(), target);
|
|
218
219
|
if (existsSync2(targetDir) && readdirSync2(targetDir).length > 0) {
|
|
219
220
|
bail(`Directory "${target}" already exists and is not empty.`);
|
|
220
221
|
}
|
|
221
|
-
let name;
|
|
222
|
-
if (typeof flags.name === "string") {
|
|
223
|
-
name = flags.name;
|
|
224
|
-
} else {
|
|
225
|
-
const nameAnswer = await text({
|
|
226
|
-
message: "Project name (package name)?",
|
|
227
|
-
initialValue: slugify(basename(target)),
|
|
228
|
-
validate: (v) => slugify(v).length === 0 ? "Invalid name" : void 0
|
|
229
|
-
});
|
|
230
|
-
if (isCancel(nameAnswer)) bail("Cancelled.");
|
|
231
|
-
name = nameAnswer;
|
|
232
|
-
}
|
|
233
222
|
let domain;
|
|
234
223
|
if (typeof flags.domain === "string") {
|
|
235
224
|
domain = flags.domain;
|
|
236
225
|
} else {
|
|
237
226
|
const domainAnswer = await text({
|
|
238
|
-
message: "Production domain?",
|
|
239
|
-
placeholder: "
|
|
240
|
-
defaultValue: "
|
|
227
|
+
message: "Production domain? (defaults to localhost for local dev)",
|
|
228
|
+
placeholder: "localhost",
|
|
229
|
+
defaultValue: "localhost"
|
|
241
230
|
});
|
|
242
231
|
if (isCancel(domainAnswer)) bail("Cancelled.");
|
|
243
|
-
domain = domainAnswer || "
|
|
232
|
+
domain = domainAnswer || "localhost";
|
|
244
233
|
}
|
|
245
234
|
let doGit;
|
|
246
235
|
if (flags.git === true || flags["no-git"] === true) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@sb-codex/create-sb-app",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"description": "Scaffold a new multi-tenant SaaS project from the sb-codex starter.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|