@wild-framework/cli 0.1.1 → 0.2.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.map +1 -1
- package/dist/cli.js +109 -5
- package/dist/cli.js.map +1 -1
- package/dist/client/codegen.d.ts +32 -0
- package/dist/client/codegen.d.ts.map +1 -0
- package/dist/client/codegen.js +94 -0
- package/dist/client/codegen.js.map +1 -0
- package/dist/client/command.d.ts +29 -0
- package/dist/client/command.d.ts.map +1 -0
- package/dist/client/command.js +95 -0
- package/dist/client/command.js.map +1 -0
- package/dist/client/contract.d.ts +86 -0
- package/dist/client/contract.d.ts.map +1 -0
- package/dist/client/contract.js +12 -0
- package/dist/client/contract.js.map +1 -0
- package/dist/client/core.d.ts +80 -0
- package/dist/client/core.d.ts.map +1 -0
- package/dist/client/core.js +52 -0
- package/dist/client/core.js.map +1 -0
- package/dist/client/fs-writer.d.ts +13 -0
- package/dist/client/fs-writer.d.ts.map +1 -0
- package/dist/client/fs-writer.js +24 -0
- package/dist/client/fs-writer.js.map +1 -0
- package/dist/client/inspector.d.ts +19 -0
- package/dist/client/inspector.d.ts.map +1 -0
- package/dist/client/inspector.js +71 -0
- package/dist/client/inspector.js.map +1 -0
- package/dist/client/spec.d.ts +13 -0
- package/dist/client/spec.d.ts.map +1 -0
- package/dist/client/spec.js +235 -0
- package/dist/client/spec.js.map +1 -0
- package/dist/client/targets/index.d.ts +9 -0
- package/dist/client/targets/index.d.ts.map +1 -0
- package/dist/client/targets/index.js +11 -0
- package/dist/client/targets/index.js.map +1 -0
- package/dist/client/targets/react.d.ts +20 -0
- package/dist/client/targets/react.d.ts.map +1 -0
- package/dist/client/targets/react.js +519 -0
- package/dist/client/targets/react.js.map +1 -0
- package/dist/commands/ci.d.ts +5 -0
- package/dist/commands/ci.d.ts.map +1 -0
- package/dist/commands/ci.js +254 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/db.d.ts +13 -0
- package/dist/commands/db.d.ts.map +1 -0
- package/dist/commands/db.js +104 -0
- package/dist/commands/db.js.map +1 -0
- package/dist/commands/docker.d.ts +9 -0
- package/dist/commands/docker.d.ts.map +1 -0
- package/dist/commands/docker.js +175 -0
- package/dist/commands/docker.js.map +1 -0
- package/dist/commands/graph.d.ts +9 -0
- package/dist/commands/graph.d.ts.map +1 -0
- package/dist/commands/graph.js +36 -0
- package/dist/commands/graph.js.map +1 -0
- package/dist/commands/make.d.ts +1 -1
- package/dist/commands/make.d.ts.map +1 -1
- package/dist/commands/make.js +51 -0
- package/dist/commands/make.js.map +1 -1
- package/dist/commands/new.d.ts +24 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +374 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/resource.d.ts +33 -0
- package/dist/commands/resource.d.ts.map +1 -0
- package/dist/commands/resource.js +291 -0
- package/dist/commands/resource.js.map +1 -0
- package/dist/frontend/codegen.d.ts +27 -0
- package/dist/frontend/codegen.d.ts.map +1 -0
- package/dist/frontend/codegen.js +97 -0
- package/dist/frontend/codegen.js.map +1 -0
- package/dist/frontend/command.d.ts +22 -0
- package/dist/frontend/command.d.ts.map +1 -0
- package/dist/frontend/command.js +57 -0
- package/dist/frontend/command.js.map +1 -0
- package/dist/frontend/react.d.ts +14 -0
- package/dist/frontend/react.d.ts.map +1 -0
- package/dist/frontend/react.js +156 -0
- package/dist/frontend/react.js.map +1 -0
- package/dist/frontend/scaffold.d.ts +20 -0
- package/dist/frontend/scaffold.d.ts.map +1 -0
- package/dist/frontend/scaffold.js +157 -0
- package/dist/frontend/scaffold.js.map +1 -0
- package/dist/frontend/spec.d.ts +33 -0
- package/dist/frontend/spec.d.ts.map +1 -0
- package/dist/frontend/spec.js +74 -0
- package/dist/frontend/spec.js.map +1 -0
- package/dist/templates/resource-slice.d.ts +87 -0
- package/dist/templates/resource-slice.d.ts.map +1 -0
- package/dist/templates/resource-slice.js +513 -0
- package/dist/templates/resource-slice.js.map +1 -0
- package/package.json +8 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { functionName, hookName, pathParamKeys, jsonSchemaToTs } from "./codegen.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generate the TypeScript types file from the spec's component schemas.
|
|
4
|
+
*/
|
|
5
|
+
export function generateTypes(spec) {
|
|
6
|
+
const lines = [];
|
|
7
|
+
lines.push("/**");
|
|
8
|
+
lines.push(` * Generated types for ${spec.title} v${spec.version}.`);
|
|
9
|
+
lines.push(" * Do not edit by hand — regenerate with `wild frontend`.");
|
|
10
|
+
lines.push(" */");
|
|
11
|
+
lines.push("");
|
|
12
|
+
for (const [name, schema] of Object.entries(spec.schemas)) {
|
|
13
|
+
lines.push(`export interface ${name} ${jsonSchemaToTs(schema, spec.schemas)}`);
|
|
14
|
+
lines.push("");
|
|
15
|
+
}
|
|
16
|
+
// Types for operations with request bodies but no named schema
|
|
17
|
+
const anonymous = spec.operations.filter((op) => op.hasRequestBody && !op.requestSchemaName);
|
|
18
|
+
if (anonymous.length > 0) {
|
|
19
|
+
lines.push("/** Request body payloads. */");
|
|
20
|
+
for (const op of anonymous) {
|
|
21
|
+
const fn = functionName(op);
|
|
22
|
+
lines.push(`export type ${fn}Body = unknown;`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return lines.join("\n");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate the typed fetch client (api.ts).
|
|
29
|
+
*/
|
|
30
|
+
export function generateApiClient(spec, baseUrl) {
|
|
31
|
+
const lines = [];
|
|
32
|
+
// api.ts lives in src/lib/, types.ts in src/ — import from ../types.js
|
|
33
|
+
lines.push(`import type { ${Object.keys(spec.schemas).join(", ")} } from "../types.js";`);
|
|
34
|
+
lines.push("");
|
|
35
|
+
lines.push(`/** Generated API client for ${spec.title} v${spec.version}. */`);
|
|
36
|
+
lines.push(`export const baseUrl = ${JSON.stringify(baseUrl)};`);
|
|
37
|
+
lines.push("");
|
|
38
|
+
lines.push(`async function request<T>(path: string, init?: RequestInit): Promise<T> {`);
|
|
39
|
+
lines.push(` const res = await fetch(baseUrl + path, {`);
|
|
40
|
+
lines.push(` ...init,`);
|
|
41
|
+
lines.push(` headers: { "Content-Type": "application/json", ...init?.headers },`);
|
|
42
|
+
lines.push(` });`);
|
|
43
|
+
lines.push(` if (!res.ok) {`);
|
|
44
|
+
lines.push(` throw new Error(\`\${res.status} \${await res.text()}\`);`);
|
|
45
|
+
lines.push(` }`);
|
|
46
|
+
lines.push(` return res.json() as Promise<T>;`);
|
|
47
|
+
lines.push(`}`);
|
|
48
|
+
lines.push("");
|
|
49
|
+
for (const op of spec.operations) {
|
|
50
|
+
lines.push(...operationClient(op));
|
|
51
|
+
lines.push("");
|
|
52
|
+
}
|
|
53
|
+
return lines.join("\n");
|
|
54
|
+
}
|
|
55
|
+
function operationClient(op) {
|
|
56
|
+
const fn = functionName(op);
|
|
57
|
+
const paramKeys = pathParamKeys(op.path);
|
|
58
|
+
const returnType = "unknown";
|
|
59
|
+
const out = [];
|
|
60
|
+
const args = [];
|
|
61
|
+
const usesBody = op.hasRequestBody;
|
|
62
|
+
const usesPath = paramKeys.length > 0;
|
|
63
|
+
if (usesPath) {
|
|
64
|
+
args.push(`params: { ${paramKeys.map((k) => `${k}: string | number`).join("; ")} }`);
|
|
65
|
+
}
|
|
66
|
+
if (usesBody) {
|
|
67
|
+
const bodyType = op.requestSchemaName ?? `${fn}Body`;
|
|
68
|
+
args.push(`body: ${bodyType}`);
|
|
69
|
+
}
|
|
70
|
+
out.push(`export function ${fn}(${args.join(", ")}): Promise<${returnType}> {`);
|
|
71
|
+
let url = `\`${op.path}\``;
|
|
72
|
+
if (paramKeys.length > 0) {
|
|
73
|
+
const interp = op.path.replace(/\{([^}]+)\}/g, (_, k) => `\${encodeURIComponent(String(params.${k}))}`);
|
|
74
|
+
url = `\`${interp}\``;
|
|
75
|
+
}
|
|
76
|
+
const init = [];
|
|
77
|
+
if (op.method !== "get") {
|
|
78
|
+
init.push(`method: ${JSON.stringify(op.method.toUpperCase())}`);
|
|
79
|
+
if (usesBody)
|
|
80
|
+
init.push(`body: JSON.stringify(body)`);
|
|
81
|
+
}
|
|
82
|
+
const initStr = init.length > 0 ? `{ ${init.join(", ")} }` : "";
|
|
83
|
+
out.push(` return request(${url}${initStr ? `, ${initStr}` : ""});`);
|
|
84
|
+
out.push(`}`);
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Generate TanStack Query hooks (hooks.ts).
|
|
89
|
+
*/
|
|
90
|
+
export function generateHooks(spec) {
|
|
91
|
+
const lines = [];
|
|
92
|
+
lines.push(`import { useQuery, useMutation } from "@tanstack/react-query";`);
|
|
93
|
+
lines.push(`import * as api from "./lib/api.js";`);
|
|
94
|
+
// hooks reference request body types (e.g. CreateOrderRequest) — import them.
|
|
95
|
+
const bodyTypes = new Set();
|
|
96
|
+
for (const op of spec.operations) {
|
|
97
|
+
if (op.hasRequestBody && op.requestSchemaName)
|
|
98
|
+
bodyTypes.add(op.requestSchemaName);
|
|
99
|
+
}
|
|
100
|
+
if (bodyTypes.size > 0) {
|
|
101
|
+
lines.push(`import type { ${[...bodyTypes].join(", ")} } from "./types.js";`);
|
|
102
|
+
}
|
|
103
|
+
lines.push("");
|
|
104
|
+
lines.push(`/** Generated TanStack Query hooks for ${spec.title}. */`);
|
|
105
|
+
lines.push("");
|
|
106
|
+
for (const op of spec.operations) {
|
|
107
|
+
const fn = functionName(op);
|
|
108
|
+
const hook = hookName(fn);
|
|
109
|
+
const paramKeys = pathParamKeys(op.path);
|
|
110
|
+
if (op.method === "get") {
|
|
111
|
+
const argTypes = paramKeys.length > 0
|
|
112
|
+
? `params: { ${paramKeys.map((k) => `${k}: string | number`).join("; ")} }`
|
|
113
|
+
: "";
|
|
114
|
+
const queryKey = `["${fn}", ${paramKeys.length > 0 ? "params" : "undefined"}]`;
|
|
115
|
+
lines.push(`export function ${hook}(${argTypes ? argTypes + ", " : ""}options?: { enabled?: boolean }) {`);
|
|
116
|
+
lines.push(` return useQuery({`);
|
|
117
|
+
lines.push(` queryKey: ${queryKey},`);
|
|
118
|
+
lines.push(` queryFn: () => api.${fn}(${paramKeys.length > 0 ? "params" : ""}),`);
|
|
119
|
+
lines.push(` ...options,`);
|
|
120
|
+
lines.push(` });`);
|
|
121
|
+
lines.push(`}`);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
// Mutations take a single variables argument. With both path params
|
|
125
|
+
// and a body, combine them: mutationFn({ params, body }) — the
|
|
126
|
+
// standard TanStack Query pattern.
|
|
127
|
+
const variablesType = [];
|
|
128
|
+
if (paramKeys.length > 0) {
|
|
129
|
+
variablesType.push(`params: { ${paramKeys.map((k) => `${k}: string | number`).join("; ")} }`);
|
|
130
|
+
}
|
|
131
|
+
if (op.hasRequestBody) {
|
|
132
|
+
const bodyType = op.requestSchemaName ?? `${fn}Body`;
|
|
133
|
+
variablesType.push(`body: ${bodyType}`);
|
|
134
|
+
}
|
|
135
|
+
lines.push(`export function ${hook}() {`);
|
|
136
|
+
lines.push(` return useMutation({`);
|
|
137
|
+
if (variablesType.length > 1) {
|
|
138
|
+
const destructure = variablesType.map((v) => v.split(":")[0]).join(", ");
|
|
139
|
+
const varsType = `{ ${variablesType.join("; ")} }`;
|
|
140
|
+
lines.push(` mutationFn: ({ ${destructure} }: ${varsType}) => api.${fn}(${destructure}),`);
|
|
141
|
+
}
|
|
142
|
+
else if (variablesType.length === 1) {
|
|
143
|
+
const arg = variablesType[0];
|
|
144
|
+
lines.push(` mutationFn: (${arg}) => api.${fn}(${arg.split(":")[0]}),`);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
lines.push(` mutationFn: () => api.${fn}(),`);
|
|
148
|
+
}
|
|
149
|
+
lines.push(` });`);
|
|
150
|
+
lines.push(`}`);
|
|
151
|
+
}
|
|
152
|
+
lines.push("");
|
|
153
|
+
}
|
|
154
|
+
return lines.join("\n");
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/frontend/react.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ParsedSpec } from "./spec.js";
|
|
2
|
+
/**
|
|
3
|
+
* Files that make up a generated React + TanStack + Tailwind frontend.
|
|
4
|
+
*/
|
|
5
|
+
export interface FrontendFile {
|
|
6
|
+
path: string;
|
|
7
|
+
content: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Build the full file set for the React target.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildReactFrontend(spec: ParsedSpec, options: {
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
title: string;
|
|
15
|
+
}): FrontendFile[];
|
|
16
|
+
/**
|
|
17
|
+
* Write generated files to disk, skipping existing ones.
|
|
18
|
+
*/
|
|
19
|
+
export declare function writeFrontend(files: FrontendFile[], root: string): number;
|
|
20
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/frontend/scaffold.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAG5C;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAC9B,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC5C,YAAY,EAAE,CAgJhB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAczE"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { generateTypes, generateApiClient, generateHooks } from "./react.js";
|
|
4
|
+
/**
|
|
5
|
+
* Build the full file set for the React target.
|
|
6
|
+
*/
|
|
7
|
+
export function buildReactFrontend(spec, options) {
|
|
8
|
+
const files = [];
|
|
9
|
+
const add = (path, content) => files.push({ path, content });
|
|
10
|
+
const appName = options.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "app";
|
|
11
|
+
// --- Project root files ---
|
|
12
|
+
add("package.json", `{
|
|
13
|
+
"name": "${appName}-frontend",
|
|
14
|
+
"private": true,
|
|
15
|
+
"type": "module",
|
|
16
|
+
"version": "0.0.0",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"dev": "vite",
|
|
19
|
+
"build": "tsc -b && vite build",
|
|
20
|
+
"preview": "vite preview"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@tanstack/react-query": "^5.0.0",
|
|
24
|
+
"react": "^18.3.1",
|
|
25
|
+
"react-dom": "^18.3.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/react": "^18.3.1",
|
|
29
|
+
"@types/react-dom": "^18.3.1",
|
|
30
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
31
|
+
"autoprefixer": "^10.4.20",
|
|
32
|
+
"postcss": "^8.4.40",
|
|
33
|
+
"tailwindcss": "^3.4.7",
|
|
34
|
+
"typescript": "^5.5.0",
|
|
35
|
+
"vite": "^5.4.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
`);
|
|
39
|
+
add("index.html", `<!DOCTYPE html>
|
|
40
|
+
<html lang="en">
|
|
41
|
+
<head>
|
|
42
|
+
<meta charset="UTF-8" />
|
|
43
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
44
|
+
<title>${options.title} Frontend</title>
|
|
45
|
+
</head>
|
|
46
|
+
<body>
|
|
47
|
+
<div id="root"></div>
|
|
48
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
49
|
+
</body>
|
|
50
|
+
</html>
|
|
51
|
+
`);
|
|
52
|
+
add("vite.config.ts", `import { defineConfig } from "vite";
|
|
53
|
+
import react from "@vitejs/plugin-react";
|
|
54
|
+
|
|
55
|
+
export default defineConfig({
|
|
56
|
+
plugins: [react()],
|
|
57
|
+
});
|
|
58
|
+
`);
|
|
59
|
+
add("tailwind.config.ts", `import type { Config } from "tailwindcss";
|
|
60
|
+
|
|
61
|
+
export default {
|
|
62
|
+
content: ["./index.html", "./src/**/*.{ts,tsx}"],
|
|
63
|
+
theme: {
|
|
64
|
+
extend: {},
|
|
65
|
+
},
|
|
66
|
+
plugins: [],
|
|
67
|
+
} satisfies Config;
|
|
68
|
+
`);
|
|
69
|
+
add("postcss.config.js", `export default {
|
|
70
|
+
plugins: {
|
|
71
|
+
tailwindcss: {},
|
|
72
|
+
autoprefixer: {},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
`);
|
|
76
|
+
add("tsconfig.json", `{
|
|
77
|
+
"compilerOptions": {
|
|
78
|
+
"target": "ES2020",
|
|
79
|
+
"useDefineForClassFields": true,
|
|
80
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
81
|
+
"module": "ESNext",
|
|
82
|
+
"skipLibCheck": true,
|
|
83
|
+
"moduleResolution": "bundler",
|
|
84
|
+
"allowImportingTsExtensions": true,
|
|
85
|
+
"isolatedModules": true,
|
|
86
|
+
"moduleDetection": "force",
|
|
87
|
+
"noEmit": true,
|
|
88
|
+
"jsx": "react-jsx",
|
|
89
|
+
"strict": true,
|
|
90
|
+
"noUnusedLocals": true,
|
|
91
|
+
"noUnusedParameters": true,
|
|
92
|
+
"noFallthroughCasesInSwitch": true
|
|
93
|
+
},
|
|
94
|
+
"include": ["src"]
|
|
95
|
+
}
|
|
96
|
+
`);
|
|
97
|
+
// --- src files ---
|
|
98
|
+
add("src/main.tsx", `import { StrictMode } from "react";
|
|
99
|
+
import { createRoot } from "react-dom/client";
|
|
100
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
101
|
+
import { App } from "./App.js";
|
|
102
|
+
import "./index.css";
|
|
103
|
+
|
|
104
|
+
const queryClient = new QueryClient();
|
|
105
|
+
|
|
106
|
+
createRoot(document.getElementById("root")!).render(
|
|
107
|
+
<StrictMode>
|
|
108
|
+
<QueryClientProvider client={queryClient}>
|
|
109
|
+
<App />
|
|
110
|
+
</QueryClientProvider>
|
|
111
|
+
</StrictMode>,
|
|
112
|
+
);
|
|
113
|
+
`);
|
|
114
|
+
add("src/index.css", `@tailwind base;
|
|
115
|
+
@tailwind components;
|
|
116
|
+
@tailwind utilities;
|
|
117
|
+
`);
|
|
118
|
+
add("src/vite-env.d.ts", `/// <reference types="vite/client" />
|
|
119
|
+
`);
|
|
120
|
+
add("src/App.tsx", `/**
|
|
121
|
+
* Generated app shell.
|
|
122
|
+
* Replace with your own UI — import hooks from "./hooks.js".
|
|
123
|
+
*/
|
|
124
|
+
export function App() {
|
|
125
|
+
return (
|
|
126
|
+
<main className="min-h-screen bg-gray-50 p-8">
|
|
127
|
+
<h1 className="text-2xl font-bold text-gray-900">${options.title} Frontend</h1>
|
|
128
|
+
<p className="mt-2 text-gray-600">Generated with <code>wild frontend</code>.</p>
|
|
129
|
+
</main>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
`);
|
|
133
|
+
// --- Generated lib + hooks ---
|
|
134
|
+
add("src/types.ts", generateTypes(spec));
|
|
135
|
+
add("src/lib/api.ts", generateApiClient(spec, options.baseUrl));
|
|
136
|
+
add("src/hooks.ts", generateHooks(spec));
|
|
137
|
+
return files;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Write generated files to disk, skipping existing ones.
|
|
141
|
+
*/
|
|
142
|
+
export function writeFrontend(files, root) {
|
|
143
|
+
let written = 0;
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
const target = join(root, file.path);
|
|
146
|
+
if (existsSync(target)) {
|
|
147
|
+
console.log(` skip ${file.path}`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
151
|
+
writeFileSync(target, file.content, "utf8");
|
|
152
|
+
console.log(` create ${file.path}`);
|
|
153
|
+
written++;
|
|
154
|
+
}
|
|
155
|
+
return written;
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/frontend/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAU7E;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAC9B,IAAgB,EAChB,OAA2C;IAE3C,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAE7E,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC;IAEzG,6BAA6B;IAC7B,GAAG,CAAC,cAAc,EAAE;aACX,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;CAyBnB,CAAC,CAAC;IAEC,GAAG,CAAC,YAAY,EAAE;;;;;aAKT,OAAO,CAAC,KAAK;;;;;;;CAOzB,CAAC,CAAC;IAEC,GAAG,CAAC,gBAAgB,EAAE;;;;;;CAMzB,CAAC,CAAC;IAEC,GAAG,CAAC,oBAAoB,EAAE;;;;;;;;;CAS7B,CAAC,CAAC;IAEC,GAAG,CAAC,mBAAmB,EAAE;;;;;;CAM5B,CAAC,CAAC;IAEC,GAAG,CAAC,eAAe,EAAE;;;;;;;;;;;;;;;;;;;;CAoBxB,CAAC,CAAC;IAEC,oBAAoB;IACpB,GAAG,CAAC,cAAc,EAAE;;;;;;;;;;;;;;;CAevB,CAAC,CAAC;IAEC,GAAG,CAAC,eAAe,EAAE;;;CAGxB,CAAC,CAAC;IAEC,GAAG,CAAC,mBAAmB,EAAE;CAC5B,CAAC,CAAC;IAEC,GAAG,CAAC,aAAa,EAAE;;;;;;;yDAOkC,OAAO,CAAC,KAAK;;;;;CAKrE,CAAC,CAAC;IAEC,gCAAgC;IAChC,GAAG,CAAC,cAAc,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IACzC,GAAG,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAChE,GAAG,CAAC,cAAc,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IAEzC,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAqB,EAAE,IAAY;IAC7D,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACpC,SAAS;QACb,CAAC;QACD,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACrC,OAAO,EAAE,CAAC;IACd,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single parsed operation from the OpenAPI spec.
|
|
3
|
+
*/
|
|
4
|
+
export interface SpecOperation {
|
|
5
|
+
method: "get" | "post" | "put" | "patch" | "delete";
|
|
6
|
+
path: string;
|
|
7
|
+
operationId?: string;
|
|
8
|
+
/** Component schema name for the request body, if any. */
|
|
9
|
+
requestSchemaName?: string;
|
|
10
|
+
/** Component schema names for path/query params, if any. */
|
|
11
|
+
pathParamNames: string[];
|
|
12
|
+
queryParamNames: string[];
|
|
13
|
+
hasRequestBody: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Normalized OpenAPI spec: the parts the generator needs.
|
|
17
|
+
*/
|
|
18
|
+
export interface ParsedSpec {
|
|
19
|
+
title: string;
|
|
20
|
+
version: string;
|
|
21
|
+
operations: SpecOperation[];
|
|
22
|
+
/** component schema name → JSON schema object */
|
|
23
|
+
schemas: Record<string, Record<string, unknown>>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Load an OpenAPI spec from a file path (local) or a URL.
|
|
27
|
+
*/
|
|
28
|
+
export declare function loadSpec(source: string): Promise<Record<string, unknown>>;
|
|
29
|
+
/**
|
|
30
|
+
* Normalize a raw OpenAPI document into the model the generators consume.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseSpec(spec: Record<string, unknown>): ParsedSpec;
|
|
33
|
+
//# sourceMappingURL=spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../src/frontend/spec.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4DAA4D;IAC5D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAc/E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAsDnE"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Load an OpenAPI spec from a file path (local) or a URL.
|
|
5
|
+
*/
|
|
6
|
+
export async function loadSpec(source) {
|
|
7
|
+
if (/^https?:\/\//.test(source)) {
|
|
8
|
+
const res = await fetch(source);
|
|
9
|
+
if (!res.ok) {
|
|
10
|
+
throw new Error(`Failed to fetch spec from ${source}: HTTP ${res.status}`);
|
|
11
|
+
}
|
|
12
|
+
return (await res.json());
|
|
13
|
+
}
|
|
14
|
+
const filePath = resolve(source);
|
|
15
|
+
if (!existsSync(filePath)) {
|
|
16
|
+
throw new Error(`Spec file not found: ${source}`);
|
|
17
|
+
}
|
|
18
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Normalize a raw OpenAPI document into the model the generators consume.
|
|
22
|
+
*/
|
|
23
|
+
export function parseSpec(spec) {
|
|
24
|
+
const info = (spec.info ?? {});
|
|
25
|
+
const paths = (spec.paths ?? {});
|
|
26
|
+
const components = (spec.components ?? {});
|
|
27
|
+
const schemas = (components.schemas ?? {});
|
|
28
|
+
const operations = [];
|
|
29
|
+
for (const [path, pathItem] of Object.entries(paths)) {
|
|
30
|
+
for (const method of ["get", "post", "put", "patch", "delete"]) {
|
|
31
|
+
const op = pathItem[method];
|
|
32
|
+
if (!op || typeof op !== "object")
|
|
33
|
+
continue;
|
|
34
|
+
const params = (op.parameters ?? []);
|
|
35
|
+
const pathParamNames = [];
|
|
36
|
+
const queryParamNames = [];
|
|
37
|
+
for (const param of params) {
|
|
38
|
+
const inLoc = param.in;
|
|
39
|
+
const name = String(param.name ?? "");
|
|
40
|
+
if (inLoc === "path")
|
|
41
|
+
pathParamNames.push(name);
|
|
42
|
+
if (inLoc === "query")
|
|
43
|
+
queryParamNames.push(name);
|
|
44
|
+
}
|
|
45
|
+
// Request body → component schema name
|
|
46
|
+
let requestSchemaName;
|
|
47
|
+
const requestBody = op.requestBody;
|
|
48
|
+
if (requestBody) {
|
|
49
|
+
const content = requestBody.content;
|
|
50
|
+
const json = content?.["application/json"];
|
|
51
|
+
const schema = json?.schema;
|
|
52
|
+
requestSchemaName = schema?.$ref
|
|
53
|
+
? String(schema.$ref).replace("#/components/schemas/", "")
|
|
54
|
+
: undefined;
|
|
55
|
+
}
|
|
56
|
+
operations.push({
|
|
57
|
+
method,
|
|
58
|
+
path,
|
|
59
|
+
operationId: op.operationId,
|
|
60
|
+
requestSchemaName,
|
|
61
|
+
pathParamNames,
|
|
62
|
+
queryParamNames,
|
|
63
|
+
hasRequestBody: !!requestBody,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
title: String(info.title ?? "API"),
|
|
69
|
+
version: String(info.version ?? "1.0.0"),
|
|
70
|
+
operations,
|
|
71
|
+
schemas,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.js","sourceRoot":"","sources":["../../src/frontend/spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource slice templates — a full vertical feature.
|
|
3
|
+
*
|
|
4
|
+
* `wild resource Product` generates, under `features/products/`:
|
|
5
|
+
* products-provider.ts — DI bindings + routes
|
|
6
|
+
* product-service.ts — business logic (in-memory CRUD)
|
|
7
|
+
* product-controller.ts — thin HTTP handlers
|
|
8
|
+
* create-product-request.ts — Zod validators (create + update)
|
|
9
|
+
* product-resource.ts — response shaping
|
|
10
|
+
* products.test.ts — service test
|
|
11
|
+
* plus a `migrations/<ts>_create_products.sql` + `.down.sql` pair.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Field types. Rich, ACF-inspired set — each maps to a TS type, a Zod
|
|
15
|
+
* validator, and a SQL column.
|
|
16
|
+
*/
|
|
17
|
+
export type FieldType = "string" | "text" | "email" | "url" | "uuid" | "date" | "datetime" | "time" | "int" | "float" | "boolean" | "enum" | "json";
|
|
18
|
+
/**
|
|
19
|
+
* Validation rules for a field — the ACF-style "define fields with rules"
|
|
20
|
+
* model. Everything is optional; sensible defaults apply when absent.
|
|
21
|
+
*/
|
|
22
|
+
export interface FieldRules {
|
|
23
|
+
/** Required in create input. Defaults to true. */
|
|
24
|
+
required?: boolean;
|
|
25
|
+
/** Numeric minimum (int/float). */
|
|
26
|
+
min?: number;
|
|
27
|
+
/** Numeric maximum (int/float). */
|
|
28
|
+
max?: number;
|
|
29
|
+
/** String minimum length (string/text/email/url/uuid). */
|
|
30
|
+
minLength?: number;
|
|
31
|
+
/** String maximum length. */
|
|
32
|
+
maxLength?: number;
|
|
33
|
+
/** Regex pattern (string-like types). */
|
|
34
|
+
pattern?: string;
|
|
35
|
+
/** Allowed values (enum). */
|
|
36
|
+
options?: string[];
|
|
37
|
+
/** Default value (applied in validator + SQL). */
|
|
38
|
+
default?: string | number | boolean;
|
|
39
|
+
/** Make the field an array of the base type (e.g. `tags:string:array=true`). */
|
|
40
|
+
array?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/** A user-declared field on the resource. */
|
|
43
|
+
export interface ResourceField {
|
|
44
|
+
name: string;
|
|
45
|
+
type: FieldType;
|
|
46
|
+
rules?: FieldRules;
|
|
47
|
+
}
|
|
48
|
+
/** Optional add-ons that add more bindings to the slice. */
|
|
49
|
+
export interface ResourceAddons {
|
|
50
|
+
event?: boolean;
|
|
51
|
+
listener?: boolean;
|
|
52
|
+
job?: boolean;
|
|
53
|
+
policy?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** Everything the generator needs to render a slice. */
|
|
56
|
+
export interface ResourceSpec {
|
|
57
|
+
/** PascalCase singular, e.g. "Product". */
|
|
58
|
+
name: string;
|
|
59
|
+
/** kebab plural (feature folder + URL segment), e.g. "products". */
|
|
60
|
+
kebabPlural: string;
|
|
61
|
+
/** kebab singular, e.g. "product". */
|
|
62
|
+
kebabSingular: string;
|
|
63
|
+
/** Fields (always include an `id` implicitly). */
|
|
64
|
+
fields: ResourceField[];
|
|
65
|
+
/** Optional add-ons. */
|
|
66
|
+
addons?: ResourceAddons;
|
|
67
|
+
}
|
|
68
|
+
/** A generated file (path relative to src root or project root). */
|
|
69
|
+
export interface SliceFile {
|
|
70
|
+
path: string;
|
|
71
|
+
content: string;
|
|
72
|
+
}
|
|
73
|
+
/** The TypeScript type for a field, e.g. `string`, `number`, `"a" | "b"`, `string[]`. */
|
|
74
|
+
export declare function tsType(field: ResourceField): string;
|
|
75
|
+
/** The TS interface line for a field, e.g. `email: string;` / `nickname?: string;`. */
|
|
76
|
+
export declare function interfaceLine(field: ResourceField): string;
|
|
77
|
+
/** The full Zod chain for a field, e.g. `z.string().email().optional()`. */
|
|
78
|
+
export declare function zodChain(field: ResourceField): string;
|
|
79
|
+
/** The SQL column definition for a field, e.g. `name TEXT NOT NULL`. */
|
|
80
|
+
export declare function sqlColumn(field: ResourceField): string;
|
|
81
|
+
/**
|
|
82
|
+
* Render every file for a resource slice.
|
|
83
|
+
*
|
|
84
|
+
* @returns files grouped by prefix (`src/…` for feature files, `migrations/…`).
|
|
85
|
+
*/
|
|
86
|
+
export declare function renderResourceSlice(spec: ResourceSpec, migrationPrefix: string): SliceFile[];
|
|
87
|
+
//# sourceMappingURL=resource-slice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-slice.d.ts","sourceRoot":"","sources":["../../src/templates/resource-slice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;GAGG;AACH,MAAM,MAAM,SAAS,GACf,QAAQ,GACR,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,MAAM,GACN,UAAU,GACV,MAAM,GACN,KAAK,GACL,OAAO,GACP,SAAS,GACT,MAAM,GACN,MAAM,CAAC;AAEb;;;GAGG;AACH,MAAM,WAAW,UAAU;IACvB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mCAAmC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACpC,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,6CAA6C;AAC7C,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,UAAU,CAAC;CACtB;AAED,4DAA4D;AAC5D,MAAM,WAAW,cAAc;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wDAAwD;AACxD,MAAM,WAAW,YAAY;IACzB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,wBAAwB;IACxB,MAAM,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,oEAAoE;AACpE,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB;AAWD,yFAAyF;AACzF,wBAAgB,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAOnD;AAmBD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAG1D;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAWrD;AAqCD,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CA8BtD;AAmXD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,GAAG,SAAS,EAAE,CA8B5F"}
|