@suluk/platform 0.1.9 → 0.1.10
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/package.json +1 -1
- package/src/generate.ts +6 -2
- package/src/index.ts +1 -1
- package/src/plan.ts +12 -0
- package/test/plan.test.ts +11 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suluk/platform",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "The platform generator (C051): write one `definePlatform` manifest → it plans the shadcn-registry adds, generates the wired Hono entry, and merges each module's provision fragment into a single provision.config. The manifest compiles to a shadcn-add list + a C047 provision.config; the generator runs the adds + `@suluk/provision`. Turns the Suluk backend registry into a one-command platform. CANDIDATE tooling.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
package/src/generate.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* testable. Stops short of `provision apply` — that's a live infra op the operator triggers.
|
|
6
6
|
*/
|
|
7
7
|
import type { PlatformManifest } from "./manifest";
|
|
8
|
-
import { planPlatform, mergePackageJson, mergeWranglerToml, type PlatformPlan } from "./plan";
|
|
8
|
+
import { planPlatform, mergePackageJson, mergeWranglerToml, mergeGitignore, type PlatformPlan } from "./plan";
|
|
9
9
|
|
|
10
10
|
export interface GenerateOptions {
|
|
11
11
|
/** run a command — the CLI spawns `bunx shadcn add <ref>`; a test records. */
|
|
@@ -43,10 +43,14 @@ export async function generatePlatform(manifest: PlatformManifest, opts: Generat
|
|
|
43
43
|
log("▸ writing wrangler.toml");
|
|
44
44
|
await opts.write("wrangler.toml", mergeWranglerToml(plan.wranglerToml, await read("wrangler.toml")));
|
|
45
45
|
written.push("wrangler.toml");
|
|
46
|
+
// .gitignore MERGES (append missing entries) — critically ensures .env/.env.temp are ignored even if the app already had
|
|
47
|
+
// a minimal .gitignore, so secrets are never committed. .env.example + the env-check are always (re)written (no values).
|
|
48
|
+
log("▸ writing .gitignore");
|
|
49
|
+
await opts.write(".gitignore", mergeGitignore(plan.gitignore, await read(".gitignore")));
|
|
50
|
+
written.push(".gitignore");
|
|
46
51
|
for (const [file, content, always] of [
|
|
47
52
|
["tsconfig.json", plan.tsconfig, false],
|
|
48
53
|
["components.json", plan.componentsJson, false],
|
|
49
|
-
[".gitignore", plan.gitignore, false],
|
|
50
54
|
[".env.example", plan.envExample, true], // a checked-in template (no values) — keep it current
|
|
51
55
|
["scripts/env-check.ts", plan.envCheck, true], // the .env.temp lifecycle preflight
|
|
52
56
|
] as const) {
|
package/src/index.ts
CHANGED
|
@@ -7,5 +7,5 @@
|
|
|
7
7
|
export { definePlatform, type PlatformManifest } from "./manifest";
|
|
8
8
|
export { CATALOG, orderServices, collectEnv, resolveVersion, BASE_DEPS, ECOSYSTEM_VERSIONS, DEV_DEPS, type CatalogEntry, type Mount, type EnvVar } from "./catalog";
|
|
9
9
|
export { mergeProvision } from "./merge";
|
|
10
|
-
export { planPlatform, buildPackageJson, mergePackageJson, mergeWranglerToml, type PlatformPlan } from "./plan";
|
|
10
|
+
export { planPlatform, buildPackageJson, mergePackageJson, mergeWranglerToml, mergeGitignore, type PlatformPlan } from "./plan";
|
|
11
11
|
export { generatePlatform, type GenerateOptions, type GenerateResult } from "./generate";
|
package/src/plan.ts
CHANGED
|
@@ -137,6 +137,18 @@ function buildGitignore(): string {
|
|
|
137
137
|
return ["node_modules/", ".env", ".env.temp", ".dev.vars", ".wrangler/", "dist/", ""].join("\n");
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
/** Merge the generated .gitignore into an existing one — APPEND any missing entries (never skip-if-present, so an app's
|
|
141
|
+
* minimal .gitignore can't leave `.env`/`.env.temp` UNIGNORED and risk committing secrets). Dedup, preserve app entries. */
|
|
142
|
+
export function mergeGitignore(generated: string, existing: string | null): string {
|
|
143
|
+
if (!existing) return generated;
|
|
144
|
+
const norm = (s: string) => s.trim().replace(/\/$/, "");
|
|
145
|
+
const have = new Set(existing.split("\n").map(norm).filter(Boolean));
|
|
146
|
+
const add = generated.split("\n").filter((l) => l.trim() && !have.has(norm(l)));
|
|
147
|
+
if (!add.length) return existing.endsWith("\n") ? existing : existing + "\n";
|
|
148
|
+
const base = existing.replace(/\n*$/, "");
|
|
149
|
+
return `${base}\n${add.join("\n")}\n`;
|
|
150
|
+
}
|
|
151
|
+
|
|
140
152
|
/** The `.env.temp` lifecycle preflight (run via `predev` / `bun run check`): if every REQUIRED secret is present (in `.env`
|
|
141
153
|
* or the process env), delete `.env.temp`; else write `.env.temp` from `.env.example` + report the missing keys + fail. */
|
|
142
154
|
function buildEnvCheckScript(env: EnvVar[]): string {
|
package/test/plan.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { test, expect, describe } from "bun:test";
|
|
2
|
-
import { definePlatform, planPlatform, mergeProvision, generatePlatform, buildPackageJson, mergePackageJson, mergeWranglerToml } from "../src/index";
|
|
2
|
+
import { definePlatform, planPlatform, mergeProvision, generatePlatform, buildPackageJson, mergePackageJson, mergeWranglerToml, mergeGitignore } from "../src/index";
|
|
3
3
|
import type { InstanceSpec } from "@suluk/provision";
|
|
4
4
|
|
|
5
5
|
/** C051 — the platform generator: manifest → plan (adds + wired entry + merged provision), the provision merge, and the
|
|
@@ -162,11 +162,11 @@ describe("generatePlatform — the orchestration (with recorders)", () => {
|
|
|
162
162
|
expect(ran).toEqual(plannedAdds()); // exactly the planned adds, in order
|
|
163
163
|
expect(ran.length).toBe(6); // app+auth+credits+keys+billing+logs
|
|
164
164
|
// config is written BEFORE the shadcn adds; the glue after. env-example + env-check + wrangler + gitignore included.
|
|
165
|
-
expect(wrote).toEqual(["package.json", "wrangler.toml", "
|
|
165
|
+
expect(wrote).toEqual(["package.json", "wrangler.toml", ".gitignore", "tsconfig.json", "components.json", ".env.example", "scripts/env-check.ts", "src/index.ts", "provision.config.ts"]);
|
|
166
166
|
expect(res.added.length).toBe(6);
|
|
167
167
|
});
|
|
168
168
|
|
|
169
|
-
test("leaves an existing tsconfig/components.json
|
|
169
|
+
test("leaves an existing tsconfig/components.json untouched; always (re)writes package.json/.gitignore/.env.example", async () => {
|
|
170
170
|
const wrote: string[] = [];
|
|
171
171
|
await generatePlatform(manifest, {
|
|
172
172
|
run: async () => {},
|
|
@@ -174,10 +174,17 @@ describe("generatePlatform — the orchestration (with recorders)", () => {
|
|
|
174
174
|
read: async (p) => (p === "package.json" ? '{"name":"x","dependencies":{"my-lib":"^1.0.0"}}' : "existing"),
|
|
175
175
|
});
|
|
176
176
|
expect(wrote).toContain("package.json"); // merged + rewritten
|
|
177
|
+
expect(wrote).toContain(".gitignore"); // MERGED (never skip — must ensure .env is ignored)
|
|
177
178
|
expect(wrote).toContain(".env.example"); // template — always current
|
|
178
179
|
expect(wrote).toContain("scripts/env-check.ts");
|
|
179
180
|
expect(wrote).not.toContain("tsconfig.json"); // present → left as-is
|
|
180
|
-
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("mergeGitignore appends missing entries so .env is always ignored", () => {
|
|
184
|
+
const merged = mergeGitignore("node_modules/\n.env\n.env.temp\n", "node_modules\n");
|
|
185
|
+
expect(merged).toContain(".env");
|
|
186
|
+
expect(merged).toContain(".env.temp");
|
|
187
|
+
expect(merged.match(/node_modules/g)?.length).toBe(1); // deduped (node_modules vs node_modules/)
|
|
181
188
|
});
|
|
182
189
|
});
|
|
183
190
|
|