hatchkit 0.1.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/dist/config.d.ts +131 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +629 -0
- package/dist/config.js.map +1 -0
- package/dist/deploy/coolify.d.ts +4 -0
- package/dist/deploy/coolify.d.ts.map +1 -0
- package/dist/deploy/coolify.js +20 -0
- package/dist/deploy/coolify.js.map +1 -0
- package/dist/deploy/github.d.ts +4 -0
- package/dist/deploy/github.d.ts.map +1 -0
- package/dist/deploy/github.js +39 -0
- package/dist/deploy/github.js.map +1 -0
- package/dist/deploy/gpu.d.ts +4 -0
- package/dist/deploy/gpu.d.ts.map +1 -0
- package/dist/deploy/gpu.js +97 -0
- package/dist/deploy/gpu.js.map +1 -0
- package/dist/deploy/keys.d.ts +9 -0
- package/dist/deploy/keys.d.ts.map +1 -0
- package/dist/deploy/keys.js +73 -0
- package/dist/deploy/keys.js.map +1 -0
- package/dist/deploy/terraform.d.ts +4 -0
- package/dist/deploy/terraform.d.ts.map +1 -0
- package/dist/deploy/terraform.js +55 -0
- package/dist/deploy/terraform.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +599 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +52 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +313 -0
- package/dist/prompts.js.map +1 -0
- package/dist/provision/glitchtip.d.ts +6 -0
- package/dist/provision/glitchtip.d.ts.map +1 -0
- package/dist/provision/glitchtip.js +46 -0
- package/dist/provision/glitchtip.js.map +1 -0
- package/dist/provision/index.d.ts +9 -0
- package/dist/provision/index.d.ts.map +1 -0
- package/dist/provision/index.js +108 -0
- package/dist/provision/index.js.map +1 -0
- package/dist/provision/openpanel.d.ts +8 -0
- package/dist/provision/openpanel.d.ts.map +1 -0
- package/dist/provision/openpanel.js +66 -0
- package/dist/provision/openpanel.js.map +1 -0
- package/dist/provision/resend.d.ts +16 -0
- package/dist/provision/resend.d.ts.map +1 -0
- package/dist/provision/resend.js +43 -0
- package/dist/provision/resend.js.map +1 -0
- package/dist/scaffold/app.d.ts +13 -0
- package/dist/scaffold/app.d.ts.map +1 -0
- package/dist/scaffold/app.js +340 -0
- package/dist/scaffold/app.js.map +1 -0
- package/dist/scaffold/dotenvx.d.ts +30 -0
- package/dist/scaffold/dotenvx.d.ts.map +1 -0
- package/dist/scaffold/dotenvx.js +142 -0
- package/dist/scaffold/dotenvx.js.map +1 -0
- package/dist/scaffold/infra.d.ts +17 -0
- package/dist/scaffold/infra.d.ts.map +1 -0
- package/dist/scaffold/infra.js +200 -0
- package/dist/scaffold/infra.js.map +1 -0
- package/dist/scaffold/manifest.d.ts +50 -0
- package/dist/scaffold/manifest.d.ts.map +1 -0
- package/dist/scaffold/manifest.js +83 -0
- package/dist/scaffold/manifest.js.map +1 -0
- package/dist/scaffold/ml-client.d.ts +20 -0
- package/dist/scaffold/ml-client.d.ts.map +1 -0
- package/dist/scaffold/ml-client.js +38 -0
- package/dist/scaffold/ml-client.js.map +1 -0
- package/dist/scaffold/pkg-json.d.ts +20 -0
- package/dist/scaffold/pkg-json.d.ts.map +1 -0
- package/dist/scaffold/pkg-json.js +113 -0
- package/dist/scaffold/pkg-json.js.map +1 -0
- package/dist/scaffold/starter-files.d.ts +40 -0
- package/dist/scaffold/starter-files.d.ts.map +1 -0
- package/dist/scaffold/starter-files.js +197 -0
- package/dist/scaffold/starter-files.js.map +1 -0
- package/dist/scaffold/update.d.ts +8 -0
- package/dist/scaffold/update.d.ts.map +1 -0
- package/dist/scaffold/update.js +255 -0
- package/dist/scaffold/update.js.map +1 -0
- package/dist/templates/addons/analytics/middleware.ts.hbs +13 -0
- package/dist/templates/addons/analytics/sentry.ts.hbs +16 -0
- package/dist/templates/addons/storage/s3.ts.hbs +40 -0
- package/dist/templates/addons/storage/upload.ts.hbs +23 -0
- package/dist/templates/addons/stripe/checkout.ts.hbs +27 -0
- package/dist/templates/addons/stripe/client.ts.hbs +6 -0
- package/dist/templates/addons/stripe/webhook.ts.hbs +39 -0
- package/dist/templates/addons/websocket/redis.ts.hbs +25 -0
- package/dist/templates/addons/websocket/ws.ts.hbs +32 -0
- package/dist/templates/base/.dockerignore.hbs +7 -0
- package/dist/templates/base/Dockerfile.hbs +18 -0
- package/dist/templates/base/env.example.hbs +60 -0
- package/dist/templates/base/github-actions.yml.hbs +35 -0
- package/dist/templates/base/gitignore.hbs +5 -0
- package/dist/templates/base/package.json.hbs +36 -0
- package/dist/templates/base/src/auth/auth.ts.hbs +13 -0
- package/dist/templates/base/src/auth/routes.ts.hbs +19 -0
- package/dist/templates/base/src/config.ts.hbs +36 -0
- package/dist/templates/base/src/db.ts.hbs +12 -0
- package/dist/templates/base/src/index.ts.hbs +80 -0
- package/dist/templates/base/src/routes/health.ts.hbs +12 -0
- package/dist/templates/base/tsconfig.json.hbs +18 -0
- package/dist/templates/ml-clients/3d-extraction.ts.hbs +20 -0
- package/dist/templates/ml-clients/background-removal.ts.hbs +17 -0
- package/dist/templates/ml-clients/custom-hf.ts.hbs +20 -0
- package/dist/templates/ml-clients/image-recognition.ts.hbs +22 -0
- package/dist/templates/ml-clients/subtitles.ts.hbs +26 -0
- package/dist/utils/coolify-api.d.ts +45 -0
- package/dist/utils/coolify-api.d.ts.map +1 -0
- package/dist/utils/coolify-api.js +72 -0
- package/dist/utils/coolify-api.js.map +1 -0
- package/dist/utils/errors.d.ts +2 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +31 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/exec.d.ts +23 -0
- package/dist/utils/exec.d.ts.map +1 -0
- package/dist/utils/exec.js +47 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/flags.d.ts +17 -0
- package/dist/utils/flags.d.ts.map +1 -0
- package/dist/utils/flags.js +116 -0
- package/dist/utils/flags.js.map +1 -0
- package/dist/utils/hf-api.d.ts +13 -0
- package/dist/utils/hf-api.d.ts.map +1 -0
- package/dist/utils/hf-api.js +30 -0
- package/dist/utils/hf-api.js.map +1 -0
- package/dist/utils/ports.d.ts +29 -0
- package/dist/utils/ports.d.ts.map +1 -0
- package/dist/utils/ports.js +86 -0
- package/dist/utils/ports.js.map +1 -0
- package/dist/utils/secrets.d.ts +25 -0
- package/dist/utils/secrets.d.ts.map +1 -0
- package/dist/utils/secrets.js +51 -0
- package/dist/utils/secrets.js.map +1 -0
- package/dist/utils/template.d.ts +7 -0
- package/dist/utils/template.d.ts.map +1 -0
- package/dist/utils/template.js +35 -0
- package/dist/utils/template.js.map +1 -0
- package/dist/utils/validate.d.ts +16 -0
- package/dist/utils/validate.d.ts.map +1 -0
- package/dist/utils/validate.js +60 -0
- package/dist/utils/validate.js.map +1 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +21 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +48 -0
- package/scripts/copy-templates.mjs +20 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* package.json surgery helpers for scaffoldApp.
|
|
3
|
+
*
|
|
4
|
+
* Every helper here reads the root `package.json` from the scaffolded
|
|
5
|
+
* output directory, mutates one field, and writes back. Kept in one
|
|
6
|
+
* place so the read/parse/write cycle stays consistent and so it's
|
|
7
|
+
* easy to swap for a more atomic approach later (e.g. one read, batched
|
|
8
|
+
* edits, one write).
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
export function stripPackageJsonScripts(outputDir, names) {
|
|
13
|
+
const path = join(outputDir, "package.json");
|
|
14
|
+
if (!existsSync(path))
|
|
15
|
+
return;
|
|
16
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
17
|
+
if (!pkg.scripts)
|
|
18
|
+
return;
|
|
19
|
+
for (const name of names)
|
|
20
|
+
delete pkg.scripts[name];
|
|
21
|
+
writeFileSync(path, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
22
|
+
}
|
|
23
|
+
export function stripPackageJsonDeps(outputDir, names) {
|
|
24
|
+
const path = join(outputDir, "package.json");
|
|
25
|
+
if (!existsSync(path))
|
|
26
|
+
return;
|
|
27
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
28
|
+
for (const name of names) {
|
|
29
|
+
if (pkg.dependencies)
|
|
30
|
+
delete pkg.dependencies[name];
|
|
31
|
+
if (pkg.devDependencies)
|
|
32
|
+
delete pkg.devDependencies[name];
|
|
33
|
+
if (pkg.optionalDependencies)
|
|
34
|
+
delete pkg.optionalDependencies[name];
|
|
35
|
+
}
|
|
36
|
+
writeFileSync(path, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
37
|
+
}
|
|
38
|
+
export function stripPackageJsonBuildBlock(outputDir) {
|
|
39
|
+
const path = join(outputDir, "package.json");
|
|
40
|
+
if (!existsSync(path))
|
|
41
|
+
return;
|
|
42
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
43
|
+
delete pkg.build;
|
|
44
|
+
writeFileSync(path, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
45
|
+
}
|
|
46
|
+
/** Drop the `pnpm typecheck:electron` segment from the root `typecheck`
|
|
47
|
+
* script regardless of where it sits in the `&&` chain. Handles:
|
|
48
|
+
* "A && pnpm typecheck:electron" → "A"
|
|
49
|
+
* "pnpm typecheck:electron && A" → "A"
|
|
50
|
+
* "pnpm typecheck:electron" → <script deleted entirely>
|
|
51
|
+
*/
|
|
52
|
+
export function unchainTypecheckScript(outputDir) {
|
|
53
|
+
const path = join(outputDir, "package.json");
|
|
54
|
+
if (!existsSync(path))
|
|
55
|
+
return;
|
|
56
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
57
|
+
if (!pkg.scripts?.typecheck)
|
|
58
|
+
return;
|
|
59
|
+
const ELECTRON_SEG = /\s*pnpm\s+typecheck:electron\s*/;
|
|
60
|
+
let script = pkg.scripts.typecheck;
|
|
61
|
+
script = script
|
|
62
|
+
.replace(new RegExp(`&&${ELECTRON_SEG.source}`), "")
|
|
63
|
+
.replace(new RegExp(`${ELECTRON_SEG.source}&&`), "")
|
|
64
|
+
.replace(ELECTRON_SEG, "")
|
|
65
|
+
.trim();
|
|
66
|
+
if (script) {
|
|
67
|
+
pkg.scripts.typecheck = script;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
delete pkg.scripts.typecheck;
|
|
71
|
+
}
|
|
72
|
+
writeFileSync(path, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
73
|
+
}
|
|
74
|
+
/** Set a specific script entry in the root `package.json`. Creates
|
|
75
|
+
* `scripts` if missing. */
|
|
76
|
+
export function setPackageJsonScript(outputDir, name, value) {
|
|
77
|
+
const path = join(outputDir, "package.json");
|
|
78
|
+
if (!existsSync(path))
|
|
79
|
+
return;
|
|
80
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
81
|
+
pkg.scripts = pkg.scripts ?? {};
|
|
82
|
+
pkg.scripts[name] = value;
|
|
83
|
+
writeFileSync(path, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
84
|
+
}
|
|
85
|
+
/** Read the `name` field from a workspace package's package.json. */
|
|
86
|
+
export function readPackageName(pkgDir) {
|
|
87
|
+
const path = join(pkgDir, "package.json");
|
|
88
|
+
if (!existsSync(path))
|
|
89
|
+
return undefined;
|
|
90
|
+
try {
|
|
91
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
92
|
+
return typeof pkg.name === "string" ? pkg.name : undefined;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Return every workspace package's `name` field from its package.json.
|
|
99
|
+
* Used to populate Next's `transpilePackages` dynamically instead of
|
|
100
|
+
* hardcoding `@starter/*` names. */
|
|
101
|
+
export function readWorkspacePackageNames(outputDir) {
|
|
102
|
+
const packagesDir = join(outputDir, "packages");
|
|
103
|
+
if (!existsSync(packagesDir))
|
|
104
|
+
return [];
|
|
105
|
+
const names = [];
|
|
106
|
+
for (const entry of readdirSync(packagesDir)) {
|
|
107
|
+
const name = readPackageName(join(packagesDir, entry));
|
|
108
|
+
if (name)
|
|
109
|
+
names.push(name);
|
|
110
|
+
}
|
|
111
|
+
return names;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=pkg-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkg-json.js","sourceRoot":"","sources":["../../src/scaffold/pkg-json.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ProjectConfig } from "../prompts.js";
|
|
2
|
+
import type { ProjectPorts } from "../utils/ports.js";
|
|
3
|
+
/** Literal string replacement across a file. Safe for any content —
|
|
4
|
+
* `replaceAll` is literal (no regex interpretation). */
|
|
5
|
+
export declare function replaceInFile(filePath: string, search: string, replace: string): void;
|
|
6
|
+
/** Rewrite a file via a pure transform. No-op if the file is missing. */
|
|
7
|
+
export declare function rewriteFile(path: string, fn: (content: string) => string): void;
|
|
8
|
+
export declare function removeIfExists(path: string): void;
|
|
9
|
+
/** Rewrite a server or client .env.example to use production URLs for
|
|
10
|
+
* the project's domain. Swaps in TRUSTED_ORIGINS for native clients
|
|
11
|
+
* when desktop or mobile is selected. Idempotent on repeat runs. */
|
|
12
|
+
export declare function updateEnvExample(outputDir: string, relPath: string, config: ProjectConfig): void;
|
|
13
|
+
/** Strip the MobileBridgeLoader import + mount from the client layout
|
|
14
|
+
* when mobile isn't selected. Leaves the rest of the layout alone. */
|
|
15
|
+
export declare function stripMobileBridgeFromLayout(outputDir: string): void;
|
|
16
|
+
/** Overwrite `packages/client/next.config.ts` with a known-good
|
|
17
|
+
* static-export config. `transpilePackages` is populated from the
|
|
18
|
+
* actual workspace package names so a starter rename doesn't break
|
|
19
|
+
* the exported client build. */
|
|
20
|
+
export declare function flipNextConfigToStaticExport(outputDir: string): void;
|
|
21
|
+
/** Rewrite every file in the starter that hard-codes the old default
|
|
22
|
+
* ports (3000/5000) so each scaffolded project has its own coherent
|
|
23
|
+
* port set. Targets covered:
|
|
24
|
+
* • packages/server/.env.development (PORT + URLs)
|
|
25
|
+
* • packages/server/.env.example (PORT line added)
|
|
26
|
+
* • packages/client/.env.development (PORT + API + WS URLs)
|
|
27
|
+
* • packages/client/.env.example (PORT line added)
|
|
28
|
+
* • packages/server/Dockerfile (ENV PORT, EXPOSE)
|
|
29
|
+
* • packages/client/Dockerfile (ENV PORT, EXPOSE)
|
|
30
|
+
* • docker-compose.yml (server PORT env)
|
|
31
|
+
* • scripts/dev.mjs (fixed-mode defaults)
|
|
32
|
+
* • electron/main.ts (DEV_URL fallback)
|
|
33
|
+
* • scripts/android-dev.sh + ios-dev.sh (NEXT_PORT default)
|
|
34
|
+
* • package.json dev:desktop script (Next port + wait-on)
|
|
35
|
+
*/
|
|
36
|
+
export declare function applyPorts(outputDir: string, ports: ProjectPorts, opts: {
|
|
37
|
+
wantsDesktop: boolean;
|
|
38
|
+
wantsMobile: boolean;
|
|
39
|
+
}): void;
|
|
40
|
+
//# sourceMappingURL=starter-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starter-files.d.ts","sourceRoot":"","sources":["../../src/scaffold/starter-files.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD;yDACyD;AACzD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAIrF;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAI/E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIjD;AAED;;qEAEqE;AACrE,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAoChG;AAED;uEACuE;AACvE,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAUnE;AAED;;;iCAGiC;AACjC,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAmBpE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE;IAAE,YAAY,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,GACpD,IAAI,CAyGN"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Per-file customization helpers for scaffoldApp.
|
|
3
|
+
*
|
|
4
|
+
* These edits run after the starter has been copied into the output
|
|
5
|
+
* directory. Each helper is a pure function over one file (or a small
|
|
6
|
+
* group of files), so they're easy to unit-test in isolation.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { readPackageName, readWorkspacePackageNames, setPackageJsonScript } from "./pkg-json.js";
|
|
11
|
+
/** Literal string replacement across a file. Safe for any content —
|
|
12
|
+
* `replaceAll` is literal (no regex interpretation). */
|
|
13
|
+
export function replaceInFile(filePath, search, replace) {
|
|
14
|
+
if (!existsSync(filePath))
|
|
15
|
+
return;
|
|
16
|
+
const content = readFileSync(filePath, "utf-8");
|
|
17
|
+
writeFileSync(filePath, content.replaceAll(search, replace), "utf-8");
|
|
18
|
+
}
|
|
19
|
+
/** Rewrite a file via a pure transform. No-op if the file is missing. */
|
|
20
|
+
export function rewriteFile(path, fn) {
|
|
21
|
+
if (!existsSync(path))
|
|
22
|
+
return;
|
|
23
|
+
const content = readFileSync(path, "utf-8");
|
|
24
|
+
writeFileSync(path, fn(content), "utf-8");
|
|
25
|
+
}
|
|
26
|
+
export function removeIfExists(path) {
|
|
27
|
+
if (existsSync(path)) {
|
|
28
|
+
rmSync(path, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Rewrite a server or client .env.example to use production URLs for
|
|
32
|
+
* the project's domain. Swaps in TRUSTED_ORIGINS for native clients
|
|
33
|
+
* when desktop or mobile is selected. Idempotent on repeat runs. */
|
|
34
|
+
export function updateEnvExample(outputDir, relPath, config) {
|
|
35
|
+
const path = join(outputDir, relPath);
|
|
36
|
+
if (!existsSync(path))
|
|
37
|
+
return;
|
|
38
|
+
const { domain } = config;
|
|
39
|
+
const appUrl = `https://${domain}`;
|
|
40
|
+
const apiUrl = `https://api.${domain}`;
|
|
41
|
+
const wsUrl = `wss://api.${domain}`;
|
|
42
|
+
let content = readFileSync(path, "utf-8");
|
|
43
|
+
// Targeted line-level rewrites. Each regex anchors on `KEY=` so stray
|
|
44
|
+
// "localhost" mentions inside comments / other keys aren't affected.
|
|
45
|
+
const rewrites = [
|
|
46
|
+
[/^FRONTEND_URL=.*$/m, `FRONTEND_URL=${appUrl}`],
|
|
47
|
+
[/^BETTER_AUTH_URL=.*$/m, `BETTER_AUTH_URL=${apiUrl}`],
|
|
48
|
+
[/^NEXT_PUBLIC_API_URL=.*$/m, `NEXT_PUBLIC_API_URL=${apiUrl}`],
|
|
49
|
+
[/^NEXT_PUBLIC_WS_URL=.*$/m, `NEXT_PUBLIC_WS_URL=${wsUrl}`],
|
|
50
|
+
];
|
|
51
|
+
for (const [re, replacement] of rewrites) {
|
|
52
|
+
content = content.replace(re, replacement);
|
|
53
|
+
}
|
|
54
|
+
// Pre-populate TRUSTED_ORIGINS for native clients so the commented
|
|
55
|
+
// guidance in the starter's .env.example is replaced with a working
|
|
56
|
+
// default the user only has to uncomment.
|
|
57
|
+
const wantsDesktop = config.features.includes("desktop");
|
|
58
|
+
const wantsMobile = config.features.includes("mobile");
|
|
59
|
+
if ((wantsDesktop || wantsMobile) && /^#\s*TRUSTED_ORIGINS=/m.test(content)) {
|
|
60
|
+
const origins = [];
|
|
61
|
+
if (wantsMobile)
|
|
62
|
+
origins.push("capacitor://localhost", "https://localhost");
|
|
63
|
+
if (wantsDesktop)
|
|
64
|
+
origins.push("app://-");
|
|
65
|
+
content = content.replace(/^#\s*TRUSTED_ORIGINS=.*$/m, `TRUSTED_ORIGINS=${origins.join(",")}`);
|
|
66
|
+
}
|
|
67
|
+
writeFileSync(path, content, "utf-8");
|
|
68
|
+
}
|
|
69
|
+
/** Strip the MobileBridgeLoader import + mount from the client layout
|
|
70
|
+
* when mobile isn't selected. Leaves the rest of the layout alone. */
|
|
71
|
+
export function stripMobileBridgeFromLayout(outputDir) {
|
|
72
|
+
const path = join(outputDir, "packages/client/src/app/layout.tsx");
|
|
73
|
+
if (!existsSync(path))
|
|
74
|
+
return;
|
|
75
|
+
let content = readFileSync(path, "utf-8");
|
|
76
|
+
content = content.replace(/import\s*\{\s*MobileBridgeLoader\s*\}\s*from\s*["']@\/mobile\/MobileBridgeLoader["'];\n/, "");
|
|
77
|
+
content = content.replace(/\s*<MobileBridgeLoader\s*\/>\n/, "\n");
|
|
78
|
+
writeFileSync(path, content, "utf-8");
|
|
79
|
+
}
|
|
80
|
+
/** Overwrite `packages/client/next.config.ts` with a known-good
|
|
81
|
+
* static-export config. `transpilePackages` is populated from the
|
|
82
|
+
* actual workspace package names so a starter rename doesn't break
|
|
83
|
+
* the exported client build. */
|
|
84
|
+
export function flipNextConfigToStaticExport(outputDir) {
|
|
85
|
+
const path = join(outputDir, "packages/client/next.config.ts");
|
|
86
|
+
if (!existsSync(path))
|
|
87
|
+
return;
|
|
88
|
+
const clientName = readPackageName(join(outputDir, "packages/client"));
|
|
89
|
+
const transpile = readWorkspacePackageNames(outputDir).filter((n) => n !== clientName);
|
|
90
|
+
const transpileList = transpile.map((n) => `"${n}"`).join(", ");
|
|
91
|
+
const staticExportConfig = `import type { NextConfig } from "next";
|
|
92
|
+
|
|
93
|
+
const nextConfig: NextConfig = {
|
|
94
|
+
output: "export",
|
|
95
|
+
assetPrefix: "./",
|
|
96
|
+
trailingSlash: true,
|
|
97
|
+
images: { unoptimized: true },
|
|
98
|
+
transpilePackages: [${transpileList}],
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default nextConfig;
|
|
102
|
+
`;
|
|
103
|
+
writeFileSync(path, staticExportConfig, "utf-8");
|
|
104
|
+
}
|
|
105
|
+
/** Rewrite every file in the starter that hard-codes the old default
|
|
106
|
+
* ports (3000/5000) so each scaffolded project has its own coherent
|
|
107
|
+
* port set. Targets covered:
|
|
108
|
+
* • packages/server/.env.development (PORT + URLs)
|
|
109
|
+
* • packages/server/.env.example (PORT line added)
|
|
110
|
+
* • packages/client/.env.development (PORT + API + WS URLs)
|
|
111
|
+
* • packages/client/.env.example (PORT line added)
|
|
112
|
+
* • packages/server/Dockerfile (ENV PORT, EXPOSE)
|
|
113
|
+
* • packages/client/Dockerfile (ENV PORT, EXPOSE)
|
|
114
|
+
* • docker-compose.yml (server PORT env)
|
|
115
|
+
* • scripts/dev.mjs (fixed-mode defaults)
|
|
116
|
+
* • electron/main.ts (DEV_URL fallback)
|
|
117
|
+
* • scripts/android-dev.sh + ios-dev.sh (NEXT_PORT default)
|
|
118
|
+
* • package.json dev:desktop script (Next port + wait-on)
|
|
119
|
+
*/
|
|
120
|
+
export function applyPorts(outputDir, ports, opts) {
|
|
121
|
+
const { server, client, nativeHmr } = ports;
|
|
122
|
+
// .env.development (server) — set PORT + update URLs pointing at localhost:5000
|
|
123
|
+
rewriteFile(join(outputDir, "packages/server/.env.development"), (c) => {
|
|
124
|
+
let out = c;
|
|
125
|
+
if (/^PORT=/m.test(out)) {
|
|
126
|
+
out = out.replace(/^PORT=.*$/m, `PORT=${server}`);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
out = `PORT=${server}\n${out}`;
|
|
130
|
+
}
|
|
131
|
+
out = out.replace(/localhost:5000/g, `localhost:${server}`);
|
|
132
|
+
return out;
|
|
133
|
+
});
|
|
134
|
+
// .env.development (client) — set PORT + API/WS URLs
|
|
135
|
+
rewriteFile(join(outputDir, "packages/client/.env.development"), (c) => {
|
|
136
|
+
let out = c;
|
|
137
|
+
if (/^PORT=/m.test(out)) {
|
|
138
|
+
out = out.replace(/^PORT=.*$/m, `PORT=${client}`);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
out = `PORT=${client}\n${out}`;
|
|
142
|
+
}
|
|
143
|
+
out = out.replace(/^NEXT_PUBLIC_API_URL=.*$/m, `NEXT_PUBLIC_API_URL=http://localhost:${server}`);
|
|
144
|
+
out = out.replace(/^NEXT_PUBLIC_WS_URL=.*$/m, `NEXT_PUBLIC_WS_URL=ws://localhost:${server}`);
|
|
145
|
+
return out;
|
|
146
|
+
});
|
|
147
|
+
// .env.example files: add PORT line if missing, else overwrite.
|
|
148
|
+
for (const [rel, port] of [
|
|
149
|
+
["packages/server/.env.example", server],
|
|
150
|
+
["packages/client/.env.example", client],
|
|
151
|
+
]) {
|
|
152
|
+
rewriteFile(join(outputDir, rel), (c) => {
|
|
153
|
+
if (/^PORT=/m.test(c))
|
|
154
|
+
return c.replace(/^PORT=.*$/m, `PORT=${port}`);
|
|
155
|
+
return `PORT=${port}\n${c}`;
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
// Server + client Dockerfiles: port-bearing lines.
|
|
159
|
+
rewriteFile(join(outputDir, "packages/server/Dockerfile"), (c) => c
|
|
160
|
+
.replace(/ENV PORT=\d+/g, `ENV PORT=${server}`)
|
|
161
|
+
.replace(/EXPOSE \d+/g, `EXPOSE ${server}`)
|
|
162
|
+
.replace(/\|\|'\d+'\)/g, `||'${server}')`));
|
|
163
|
+
rewriteFile(join(outputDir, "packages/client/Dockerfile"), (c) => c
|
|
164
|
+
.replace(/ENV PORT=\d+/g, `ENV PORT=${client}`)
|
|
165
|
+
.replace(/EXPOSE \d+/g, `EXPOSE ${client}`)
|
|
166
|
+
.replace(/\|\|'\d+'\)/g, `||'${client}')`));
|
|
167
|
+
// docker-compose.yml: server PORT first (appears first in file),
|
|
168
|
+
// then the client PORT if present.
|
|
169
|
+
rewriteFile(join(outputDir, "docker-compose.yml"), (c) => {
|
|
170
|
+
let out = c.replace(/PORT:\s*"3000"/, `PORT: "${server}"`);
|
|
171
|
+
out = out.replace(/PORT:\s*"3000"/, `PORT: "${client}"`);
|
|
172
|
+
return out;
|
|
173
|
+
});
|
|
174
|
+
// scripts/dev.mjs: bump fixed-mode defaults + the comment header
|
|
175
|
+
// that documents the default ports.
|
|
176
|
+
rewriteFile(join(outputDir, "scripts/dev.mjs"), (c) => c
|
|
177
|
+
.replace(/clientPort = 3000/g, `clientPort = ${client}`)
|
|
178
|
+
.replace(/apiPort = 5000/g, `apiPort = ${server}`)
|
|
179
|
+
.replace(/client 3000, docs 4000, server 5000/g, `client ${client}, docs 4000, server ${server}`));
|
|
180
|
+
// Native HMR port — only wired when desktop or mobile is selected.
|
|
181
|
+
if (nativeHmr === undefined)
|
|
182
|
+
return;
|
|
183
|
+
rewriteFile(join(outputDir, "electron/main.ts"), (c) => c.replace(/const DEV_URL = process\.env\.ELECTRON_DEV_URL \|\| "http:\/\/localhost:\d+"/, `const DEV_URL = process.env.ELECTRON_DEV_URL || "http://localhost:${nativeHmr}"`));
|
|
184
|
+
for (const script of ["scripts/android-dev.sh", "scripts/ios-dev.sh"]) {
|
|
185
|
+
rewriteFile(join(outputDir, script), (c) => c.replace(/NEXT_PORT="\$\{NEXT_PORT:-\d+\}"/, `NEXT_PORT="\${NEXT_PORT:-${nativeHmr}}"`));
|
|
186
|
+
}
|
|
187
|
+
// Root package.json: dev:desktop needs PORT + wait-on retargeted at
|
|
188
|
+
// nativeHmr so web-dev and desktop-dev don't stomp each other.
|
|
189
|
+
if (opts.wantsDesktop) {
|
|
190
|
+
const clientPkgName = readPackageName(join(outputDir, "packages/client")) ?? "@starter/client";
|
|
191
|
+
setPackageJsonScript(outputDir, "dev:desktop", `concurrently -k -n next,electron -c blue,magenta ` +
|
|
192
|
+
`"PORT=${nativeHmr} pnpm --filter ${clientPkgName} dev" ` +
|
|
193
|
+
`"wait-on http://localhost:${nativeHmr} && pnpm electron:compile && ` +
|
|
194
|
+
`ELECTRON_DEV_URL=http://localhost:${nativeHmr} electron electron/main.js"`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=starter-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starter-files.js","sourceRoot":"","sources":["../../src/scaffold/starter-files.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,eAAe,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEjG;yDACyD;AACzD,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,MAAc,EAAE,OAAe;IAC7E,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO;IAClC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChD,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;AACxE,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,EAA+B;IACvE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO;IAC9B,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5C,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED;;qEAEqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,SAAiB,EAAE,OAAe,EAAE,MAAqB;IACxF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO;IAE9B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC1B,MAAM,MAAM,GAAG,WAAW,MAAM,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,eAAe,MAAM,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,aAAa,MAAM,EAAE,CAAC;IAEpC,IAAI,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAE1C,sEAAsE;IACtE,qEAAqE;IACrE,MAAM,QAAQ,GAA4B;QACxC,CAAC,oBAAoB,EAAE,gBAAgB,MAAM,EAAE,CAAC;QAChD,CAAC,uBAAuB,EAAE,mBAAmB,MAAM,EAAE,CAAC;QACtD,CAAC,2BAA2B,EAAE,uBAAuB,MAAM,EAAE,CAAC;QAC9D,CAAC,0BAA0B,EAAE,sBAAsB,KAAK,EAAE,CAAC;KAC5D,CAAC;IACF,KAAK,MAAM,CAAC,EAAE,EAAE,WAAW,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IAC7C,CAAC;IAED,mEAAmE;IACnE,oEAAoE;IACpE,0CAA0C;IAC1C,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACvD,IAAI,CAAC,YAAY,IAAI,WAAW,CAAC,IAAI,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5E,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,IAAI,WAAW;YAAE,OAAO,CAAC,IAAI,CAAC,uBAAuB,EAAE,mBAAmB,CAAC,CAAC;QAC5E,IAAI,YAAY;YAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1C,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,2BAA2B,EAAE,mBAAmB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACjG,CAAC;IAED,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACxC,CAAC;AAED;uEACuE;AACvE,MAAM,UAAU,2BAA2B,CAAC,SAAiB;IAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,oCAAoC,CAAC,CAAC;IACnE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO;IAC9B,IAAI,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1C,OAAO,GAAG,OAAO,CAAC,OAAO,CACvB,yFAAyF,EACzF,EAAE,CACH,CAAC;IACF,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,gCAAgC,EAAE,IAAI,CAAC,CAAC;IAClE,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACxC,CAAC;AAED;;;iCAGiC;AACjC,MAAM,UAAU,4BAA4B,CAAC,SAAiB;IAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,gCAAgC,CAAC,CAAC;IAC/D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO;IAC9B,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACvE,MAAM,SAAS,GAAG,yBAAyB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC;IACvF,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,MAAM,kBAAkB,GAAG;;;;;;;wBAOL,aAAa;;;;CAIpC,CAAC;IACA,aAAa,CAAC,IAAI,EAAE,kBAAkB,EAAE,OAAO,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,UAAU,CACxB,SAAiB,EACjB,KAAmB,EACnB,IAAqD;IAErD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAE5C,gFAAgF;IAChF,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,kCAAkC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;QACrE,IAAI,GAAG,GAAG,CAAC,CAAC;QACZ,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,QAAQ,MAAM,EAAE,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,QAAQ,MAAM,KAAK,GAAG,EAAE,CAAC;QACjC,CAAC;QACD,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,EAAE,aAAa,MAAM,EAAE,CAAC,CAAC;QAC5D,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,qDAAqD;IACrD,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,kCAAkC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;QACrE,IAAI,GAAG,GAAG,CAAC,CAAC;QACZ,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,QAAQ,MAAM,EAAE,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACN,GAAG,GAAG,QAAQ,MAAM,KAAK,GAAG,EAAE,CAAC;QACjC,CAAC;QACD,GAAG,GAAG,GAAG,CAAC,OAAO,CACf,2BAA2B,EAC3B,wCAAwC,MAAM,EAAE,CACjD,CAAC;QACF,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,0BAA0B,EAAE,qCAAqC,MAAM,EAAE,CAAC,CAAC;QAC7F,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,gEAAgE;IAChE,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI;QACxB,CAAC,8BAA8B,EAAE,MAAM,CAAU;QACjD,CAAC,8BAA8B,EAAE,MAAM,CAAU;KAClD,EAAE,CAAC;QACF,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;YACtC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;gBAAE,OAAO,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC;YACtE,OAAO,QAAQ,IAAI,KAAK,CAAC,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,mDAAmD;IACnD,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,4BAA4B,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAC/D,CAAC;SACE,OAAO,CAAC,eAAe,EAAE,YAAY,MAAM,EAAE,CAAC;SAC9C,OAAO,CAAC,aAAa,EAAE,UAAU,MAAM,EAAE,CAAC;SAC1C,OAAO,CAAC,cAAc,EAAE,MAAM,MAAM,IAAI,CAAC,CAC7C,CAAC;IACF,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,4BAA4B,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAC/D,CAAC;SACE,OAAO,CAAC,eAAe,EAAE,YAAY,MAAM,EAAE,CAAC;SAC9C,OAAO,CAAC,aAAa,EAAE,UAAU,MAAM,EAAE,CAAC;SAC1C,OAAO,CAAC,cAAc,EAAE,MAAM,MAAM,IAAI,CAAC,CAC7C,CAAC;IAEF,iEAAiE;IACjE,mCAAmC;IACnC,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;QACvD,IAAI,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,UAAU,MAAM,GAAG,CAAC,CAAC;QAC3D,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,UAAU,MAAM,GAAG,CAAC,CAAC;QACzD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,iEAAiE;IACjE,oCAAoC;IACpC,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CACpD,CAAC;SACE,OAAO,CAAC,oBAAoB,EAAE,gBAAgB,MAAM,EAAE,CAAC;SACvD,OAAO,CAAC,iBAAiB,EAAE,aAAa,MAAM,EAAE,CAAC;SACjD,OAAO,CACN,sCAAsC,EACtC,UAAU,MAAM,uBAAuB,MAAM,EAAE,CAChD,CACJ,CAAC;IAEF,mEAAmE;IACnE,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO;IAEpC,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CACrD,CAAC,CAAC,OAAO,CACP,8EAA8E,EAC9E,qEAAqE,SAAS,GAAG,CAClF,CACF,CAAC;IAEF,KAAK,MAAM,MAAM,IAAI,CAAC,wBAAwB,EAAE,oBAAoB,CAAC,EAAE,CAAC;QACtE,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CACzC,CAAC,CAAC,OAAO,CAAC,kCAAkC,EAAE,4BAA4B,SAAS,IAAI,CAAC,CACzF,CAAC;IACJ,CAAC;IAED,oEAAoE;IACpE,+DAA+D;IAC/D,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC,IAAI,iBAAiB,CAAC;QAC/F,oBAAoB,CAClB,SAAS,EACT,aAAa,EACb,mDAAmD;YACjD,SAAS,SAAS,kBAAkB,aAAa,QAAQ;YACzD,6BAA6B,SAAS,+BAA+B;YACrE,qCAAqC,SAAS,6BAA6B,CAC9E,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Feature } from "../prompts.js";
|
|
2
|
+
export interface UpdateResult {
|
|
3
|
+
added: Feature[];
|
|
4
|
+
skipped: Feature[];
|
|
5
|
+
removed: Feature[];
|
|
6
|
+
}
|
|
7
|
+
export declare function runUpdate(projectDir: string): Promise<UpdateResult>;
|
|
8
|
+
//# sourceMappingURL=update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/scaffold/update.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAoB7C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAiGzE"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* `hatchkit update` — add feature scaffolding to an already-scaffolded
|
|
3
|
+
* project.
|
|
4
|
+
*
|
|
5
|
+
* Scope of this MVP:
|
|
6
|
+
* • Read the .hatchkit.json manifest in cwd.
|
|
7
|
+
* • Prompt for a new feature set (defaulting to the current one).
|
|
8
|
+
* • REFUSE to remove features — that risks deleting user code built
|
|
9
|
+
* on top of them. Removal stays a manual operation.
|
|
10
|
+
* • For each newly-added feature, copy the starter's feature files
|
|
11
|
+
* into the project and merge package.json edits.
|
|
12
|
+
* • Refresh the manifest.
|
|
13
|
+
*
|
|
14
|
+
* Currently supported additions: `desktop`, `mobile`.
|
|
15
|
+
* `websocket` / `stripe` / `analytics` / `s3` additions are flagged
|
|
16
|
+
* as "manual" — the scaffold-time strip for those is coarse-grained
|
|
17
|
+
* and re-adding them cleanly would need per-feature merge logic that
|
|
18
|
+
* doesn't exist yet. Users can cherry-pick files from the starter.
|
|
19
|
+
*/
|
|
20
|
+
import { cpSync, existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { join, resolve } from "node:path";
|
|
22
|
+
import { checkbox, confirm } from "@inquirer/prompts";
|
|
23
|
+
import chalk from "chalk";
|
|
24
|
+
import { addUsedPorts, getUsedPorts } from "../config.js";
|
|
25
|
+
import { PORT_RANGES, pickPort } from "../utils/ports.js";
|
|
26
|
+
import { getCliVersion } from "../utils/version.js";
|
|
27
|
+
import { MANIFEST_FILENAME, readManifest, writeManifest, } from "./manifest.js";
|
|
28
|
+
import { setPackageJsonScript } from "./pkg-json.js";
|
|
29
|
+
import { applyPorts, rewriteFile } from "./starter-files.js";
|
|
30
|
+
// Same derivation as scaffold/app.ts — STARTER_ROOT lives next to the
|
|
31
|
+
// monorepo root, two hops up from this file's compiled location.
|
|
32
|
+
const MONOREPO_ROOT = resolve(join(import.meta.dirname, "..", "..", ".."));
|
|
33
|
+
const STARTER_ROOT = join(MONOREPO_ROOT, "starter");
|
|
34
|
+
/** Features that `update` knows how to layer onto an existing project. */
|
|
35
|
+
const SUPPORTED_ADDITIONS = ["desktop", "mobile"];
|
|
36
|
+
export async function runUpdate(projectDir) {
|
|
37
|
+
const manifest = readManifest(projectDir);
|
|
38
|
+
if (!manifest) {
|
|
39
|
+
throw new Error(`No ${MANIFEST_FILENAME} found in ${projectDir}. This directory wasn't scaffolded by hatchkit, or the manifest was deleted.`);
|
|
40
|
+
}
|
|
41
|
+
if (!existsSync(STARTER_ROOT)) {
|
|
42
|
+
throw new Error(`Starter template not found at ${STARTER_ROOT}. Run 'git submodule update --init' in the monorepo root.`);
|
|
43
|
+
}
|
|
44
|
+
console.log(chalk.bold(`\n ── Update: ${manifest.name} ─────────────────────────────\n`));
|
|
45
|
+
console.log(chalk.dim(` Current features: ${manifest.features.join(", ") || "(none)"}`));
|
|
46
|
+
console.log(chalk.dim(` Supported additions: ${SUPPORTED_ADDITIONS.join(", ")}`));
|
|
47
|
+
const allOptions = ["websocket", "stripe", "analytics", "s3", "desktop", "mobile"];
|
|
48
|
+
const desired = await checkbox({
|
|
49
|
+
message: "Desired feature set (current pre-selected):",
|
|
50
|
+
choices: allOptions.map((f) => ({
|
|
51
|
+
name: SUPPORTED_ADDITIONS.includes(f) || manifest.features.includes(f)
|
|
52
|
+
? f
|
|
53
|
+
: `${f} (manual add only — use the starter repo directly)`,
|
|
54
|
+
value: f,
|
|
55
|
+
checked: manifest.features.includes(f),
|
|
56
|
+
disabled: !manifest.features.includes(f) && !SUPPORTED_ADDITIONS.includes(f),
|
|
57
|
+
})),
|
|
58
|
+
});
|
|
59
|
+
const current = new Set(manifest.features);
|
|
60
|
+
const next = new Set(desired);
|
|
61
|
+
const added = [...next].filter((f) => !current.has(f));
|
|
62
|
+
const removed = [...current].filter((f) => !next.has(f));
|
|
63
|
+
if (removed.length > 0) {
|
|
64
|
+
console.log(chalk.yellow(`\n Refusing to remove features: ${removed.join(", ")}. Removing features risks deleting user code. Remove manually + update the manifest.`));
|
|
65
|
+
}
|
|
66
|
+
if (added.length === 0) {
|
|
67
|
+
console.log(chalk.dim("\n No new features to add."));
|
|
68
|
+
return { added: [], skipped: [], removed };
|
|
69
|
+
}
|
|
70
|
+
const ok = await confirm({
|
|
71
|
+
message: `Add ${added.join(", ")} to ${manifest.name}?`,
|
|
72
|
+
default: true,
|
|
73
|
+
});
|
|
74
|
+
if (!ok)
|
|
75
|
+
return { added: [], skipped: added, removed };
|
|
76
|
+
const resolvedStarter = realpathSync(STARTER_ROOT);
|
|
77
|
+
const updatedFeatures = new Set(manifest.features);
|
|
78
|
+
let updatedPorts = manifest.ports;
|
|
79
|
+
for (const feature of added) {
|
|
80
|
+
if (feature === "desktop") {
|
|
81
|
+
await addDesktop(projectDir, resolvedStarter, manifest);
|
|
82
|
+
updatedFeatures.add("desktop");
|
|
83
|
+
}
|
|
84
|
+
else if (feature === "mobile") {
|
|
85
|
+
await addMobile(projectDir, resolvedStarter, manifest);
|
|
86
|
+
updatedFeatures.add("mobile");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Pick a nativeHmr port if the project didn't have one and now needs one.
|
|
90
|
+
const needsNative = updatedFeatures.has("desktop") || updatedFeatures.has("mobile");
|
|
91
|
+
if (needsNative && updatedPorts.nativeHmr === undefined) {
|
|
92
|
+
const used = new Set(getUsedPorts());
|
|
93
|
+
const nativeHmr = await pickPort(PORT_RANGES.nativeHmr[0], PORT_RANGES.nativeHmr[1], used);
|
|
94
|
+
addUsedPorts([nativeHmr]);
|
|
95
|
+
updatedPorts = { ...updatedPorts, nativeHmr };
|
|
96
|
+
// Wire the new port into the freshly-added native scaffolding.
|
|
97
|
+
applyPorts(projectDir, updatedPorts, {
|
|
98
|
+
wantsDesktop: updatedFeatures.has("desktop"),
|
|
99
|
+
wantsMobile: updatedFeatures.has("mobile"),
|
|
100
|
+
});
|
|
101
|
+
console.log(chalk.dim(` Assigned native HMR port: ${nativeHmr}`));
|
|
102
|
+
}
|
|
103
|
+
// Refresh the manifest so subsequent `update` runs see the new state.
|
|
104
|
+
const updatedManifest = {
|
|
105
|
+
...manifest,
|
|
106
|
+
version: manifest.version,
|
|
107
|
+
cliVersion: getCliVersion(),
|
|
108
|
+
scaffoldedAt: manifest.scaffoldedAt, // preserve original date
|
|
109
|
+
features: [...updatedFeatures],
|
|
110
|
+
ports: updatedPorts,
|
|
111
|
+
};
|
|
112
|
+
writeManifest(projectDir, updatedManifest);
|
|
113
|
+
return { added, skipped: [], removed };
|
|
114
|
+
}
|
|
115
|
+
/** Copy desktop scaffolding from the starter + apply project-name
|
|
116
|
+
* substitutions. Assumes the feature isn't already present. */
|
|
117
|
+
async function addDesktop(projectDir, resolvedStarter, manifest) {
|
|
118
|
+
console.log(chalk.dim("\n Adding desktop (Electron)..."));
|
|
119
|
+
copyFromStarter(resolvedStarter, projectDir, "electron");
|
|
120
|
+
copyFromStarter(resolvedStarter, projectDir, "build");
|
|
121
|
+
copyFromStarter(resolvedStarter, projectDir, "packages/client/src/types/electron.d.ts");
|
|
122
|
+
copyFromStarter(resolvedStarter, projectDir, ".github/workflows/desktop-release.yml");
|
|
123
|
+
// Merge package.json: pick up desktop scripts + build block + deps.
|
|
124
|
+
const starterPkg = readJson(join(resolvedStarter, "package.json"));
|
|
125
|
+
const projectPkgPath = join(projectDir, "package.json");
|
|
126
|
+
const projectPkg = readJson(projectPkgPath);
|
|
127
|
+
const bundleId = manifest.name.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
128
|
+
const DESKTOP_SCRIPTS = [
|
|
129
|
+
"dev:desktop",
|
|
130
|
+
"dev:electron",
|
|
131
|
+
"build:desktop",
|
|
132
|
+
"electron:compile",
|
|
133
|
+
"electron:build",
|
|
134
|
+
"electron:preview",
|
|
135
|
+
"typecheck:electron",
|
|
136
|
+
"icons:desktop",
|
|
137
|
+
"itch:push:mac",
|
|
138
|
+
"itch:push:win",
|
|
139
|
+
"itch:push:linux",
|
|
140
|
+
];
|
|
141
|
+
const DESKTOP_DEPS = ["electron", "electron-builder", "electron-icon-builder", "wait-on"];
|
|
142
|
+
projectPkg.scripts = projectPkg.scripts ?? {};
|
|
143
|
+
for (const name of DESKTOP_SCRIPTS) {
|
|
144
|
+
if (starterPkg.scripts?.[name])
|
|
145
|
+
projectPkg.scripts[name] = starterPkg.scripts[name];
|
|
146
|
+
}
|
|
147
|
+
projectPkg.devDependencies = projectPkg.devDependencies ?? {};
|
|
148
|
+
for (const name of DESKTOP_DEPS) {
|
|
149
|
+
if (starterPkg.devDependencies?.[name]) {
|
|
150
|
+
projectPkg.devDependencies[name] = starterPkg.devDependencies[name];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// electron-builder `build` block — only adopt it if the project
|
|
154
|
+
// doesn't already have one the user may have edited.
|
|
155
|
+
if (!projectPkg.build && starterPkg.build) {
|
|
156
|
+
projectPkg.build = JSON.parse(JSON.stringify(starterPkg.build)
|
|
157
|
+
.replaceAll("{{bundleId}}", bundleId)
|
|
158
|
+
.replaceAll("{{projectName}}", manifest.name));
|
|
159
|
+
}
|
|
160
|
+
writeFileSync(projectPkgPath, JSON.stringify(projectPkg, null, 2) + "\n", "utf-8");
|
|
161
|
+
// Chain electron typecheck into the root `typecheck` if present
|
|
162
|
+
// and not already chained.
|
|
163
|
+
if (projectPkg.scripts.typecheck &&
|
|
164
|
+
!projectPkg.scripts.typecheck.includes("typecheck:electron")) {
|
|
165
|
+
setPackageJsonScript(projectDir, "typecheck", `${projectPkg.scripts.typecheck} && pnpm typecheck:electron`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Copy mobile (Capacitor) scaffolding + wire MobileBridgeLoader
|
|
169
|
+
* into the client layout. Assumes the feature isn't already present. */
|
|
170
|
+
async function addMobile(projectDir, resolvedStarter, manifest) {
|
|
171
|
+
console.log(chalk.dim("\n Adding mobile (Capacitor)..."));
|
|
172
|
+
copyFromStarter(resolvedStarter, projectDir, "capacitor.config.ts");
|
|
173
|
+
copyFromStarter(resolvedStarter, projectDir, "packages/client/src/mobile");
|
|
174
|
+
copyFromStarter(resolvedStarter, projectDir, "resources");
|
|
175
|
+
copyFromStarter(resolvedStarter, projectDir, "scripts/android-dev.sh");
|
|
176
|
+
copyFromStarter(resolvedStarter, projectDir, "scripts/android-env.sh");
|
|
177
|
+
copyFromStarter(resolvedStarter, projectDir, "scripts/ios-dev.sh");
|
|
178
|
+
copyFromStarter(resolvedStarter, projectDir, ".github/workflows/mobile-release.yml");
|
|
179
|
+
// Substitute project identifiers into capacitor.config.ts.
|
|
180
|
+
const capPath = join(projectDir, "capacitor.config.ts");
|
|
181
|
+
const bundleId = manifest.name.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
182
|
+
rewriteFile(capPath, (c) => c.replaceAll("{{projectName}}", manifest.name).replaceAll("{{bundleId}}", bundleId));
|
|
183
|
+
// Merge package.json scripts + deps.
|
|
184
|
+
const starterPkg = readJson(join(resolvedStarter, "package.json"));
|
|
185
|
+
const projectPkgPath = join(projectDir, "package.json");
|
|
186
|
+
const projectPkg = readJson(projectPkgPath);
|
|
187
|
+
const MOBILE_SCRIPTS = [
|
|
188
|
+
"dev:android",
|
|
189
|
+
"dev:ios",
|
|
190
|
+
"build:mobile",
|
|
191
|
+
"cap:add:ios",
|
|
192
|
+
"cap:add:android",
|
|
193
|
+
"cap:sync",
|
|
194
|
+
"cap:run:ios",
|
|
195
|
+
"cap:run:android",
|
|
196
|
+
"build:ios:release",
|
|
197
|
+
"build:android:release",
|
|
198
|
+
"build:android:apk",
|
|
199
|
+
"mobile:assets",
|
|
200
|
+
];
|
|
201
|
+
const MOBILE_DEPS = [
|
|
202
|
+
"@capacitor/core",
|
|
203
|
+
"@capacitor/cli",
|
|
204
|
+
"@capacitor/ios",
|
|
205
|
+
"@capacitor/android",
|
|
206
|
+
"@capacitor/splash-screen",
|
|
207
|
+
"@capacitor/status-bar",
|
|
208
|
+
"@capacitor/screen-orientation",
|
|
209
|
+
"@capacitor/preferences",
|
|
210
|
+
"@capacitor/app",
|
|
211
|
+
"@capacitor/assets",
|
|
212
|
+
];
|
|
213
|
+
projectPkg.scripts = projectPkg.scripts ?? {};
|
|
214
|
+
for (const name of MOBILE_SCRIPTS) {
|
|
215
|
+
if (starterPkg.scripts?.[name])
|
|
216
|
+
projectPkg.scripts[name] = starterPkg.scripts[name];
|
|
217
|
+
}
|
|
218
|
+
projectPkg.dependencies = projectPkg.dependencies ?? {};
|
|
219
|
+
projectPkg.devDependencies = projectPkg.devDependencies ?? {};
|
|
220
|
+
for (const name of MOBILE_DEPS) {
|
|
221
|
+
if (starterPkg.dependencies?.[name]) {
|
|
222
|
+
projectPkg.dependencies[name] = starterPkg.dependencies[name];
|
|
223
|
+
}
|
|
224
|
+
else if (starterPkg.devDependencies?.[name]) {
|
|
225
|
+
projectPkg.devDependencies[name] = starterPkg.devDependencies[name];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
writeFileSync(projectPkgPath, JSON.stringify(projectPkg, null, 2) + "\n", "utf-8");
|
|
229
|
+
// Wire MobileBridgeLoader into the client layout if not already there.
|
|
230
|
+
const layoutPath = join(projectDir, "packages/client/src/app/layout.tsx");
|
|
231
|
+
if (existsSync(layoutPath)) {
|
|
232
|
+
let content = readFileSync(layoutPath, "utf-8");
|
|
233
|
+
if (!content.includes("MobileBridgeLoader")) {
|
|
234
|
+
// Insert the import + mount in well-known positions.
|
|
235
|
+
content = content.replace(/(import[^\n]+"@\/styles\/globals\.css";\n)/, `$1import { MobileBridgeLoader } from "@/mobile/MobileBridgeLoader";\n`);
|
|
236
|
+
content = content.replace(/(<body[^>]*>)\s*/, `$1\n <MobileBridgeLoader />\n `);
|
|
237
|
+
writeFileSync(layoutPath, content, "utf-8");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function copyFromStarter(starter, outputDir, rel) {
|
|
242
|
+
const src = join(starter, rel);
|
|
243
|
+
const dst = join(outputDir, rel);
|
|
244
|
+
if (!existsSync(src))
|
|
245
|
+
return;
|
|
246
|
+
if (existsSync(dst)) {
|
|
247
|
+
// Already present — skip to avoid clobbering user edits.
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
cpSync(src, dst, { recursive: true });
|
|
251
|
+
}
|
|
252
|
+
function readJson(path) {
|
|
253
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=update.js.map
|