@emberkit/cli 0.5.2 → 0.6.1-alpha.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.js +44 -10
- package/dist/commands/create.js +10 -21
- package/dist/templates/action/index.js +12 -0
- package/dist/templates/action.js +12 -0
- package/dist/templates/api/api.js +280 -0
- package/dist/templates/apiRoute/index.js +20 -0
- package/dist/templates/apiRoute.js +20 -0
- package/dist/templates/blog/blog.js +358 -0
- package/dist/templates/component/index.js +14 -0
- package/dist/templates/component.js +14 -0
- package/dist/templates/config/index.js +13 -0
- package/dist/templates/config.js +13 -0
- package/dist/templates/context/index.js +21 -0
- package/dist/templates/context.js +21 -0
- package/dist/templates/dashboard/dashboard.js +406 -0
- package/dist/templates/entry/index.js +15 -0
- package/dist/templates/entry.js +15 -0
- package/dist/templates/errorBoundary/index.js +17 -0
- package/dist/templates/errorBoundary.js +17 -0
- package/dist/templates/form/index.js +59 -0
- package/dist/templates/form.js +59 -0
- package/dist/templates/index.js +0 -260
- package/dist/templates/layout/index.js +16 -0
- package/dist/templates/layout.js +16 -0
- package/dist/templates/layoutRoutes/index.js +9 -0
- package/dist/templates/layoutRoutes.js +9 -0
- package/dist/templates/loader/index.js +10 -0
- package/dist/templates/loader.js +10 -0
- package/dist/templates/minimal/minimal.js +89 -0
- package/dist/templates/project-templates/_shared/base.js +139 -0
- package/dist/templates/project-templates/api/api.js +209 -0
- package/dist/templates/project-templates/blog/blog.js +283 -0
- package/dist/templates/project-templates/dashboard/dashboard.js +331 -0
- package/dist/templates/project-templates/minimal/minimal.js +21 -0
- package/dist/templates/project-templates/saas/saas.js +461 -0
- package/dist/templates/project-templates/starter-kit/starter.js +209 -0
- package/dist/templates/project-templates/starter-kit/with-ui.js +365 -0
- package/dist/templates/projects/starter.js +286 -0
- package/dist/templates/projects/with-ui.js +445 -0
- package/dist/templates/projects.js +419 -191
- package/dist/templates/route/index.js +12 -0
- package/dist/templates/route.js +12 -0
- package/dist/templates/saas/saas.js +539 -0
- package/dist/templates/signal/index.js +25 -0
- package/dist/templates/signal.js +25 -0
- package/dist/utils/generator.js +30 -2
- package/package.json +1 -2
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,9 @@ import inquirer from "inquirer";
|
|
|
2
2
|
import { dev } from "./commands/dev.js";
|
|
3
3
|
import { build } from "./commands/build.js";
|
|
4
4
|
import { preview } from "./commands/preview.js";
|
|
5
|
-
import { create } from "./commands/create.js";
|
|
6
|
-
import {
|
|
5
|
+
import { create, TEMPLATES } from "./commands/create.js";
|
|
6
|
+
import { generate } from "./utils/generator.js";
|
|
7
|
+
import { toKebabCase } from "./templates/index.js";
|
|
7
8
|
export async function runCLI(args) {
|
|
8
9
|
const [command, ...restArgs] = args.slice(2);
|
|
9
10
|
if (!command) {
|
|
@@ -28,7 +29,7 @@ export async function runCLI(args) {
|
|
|
28
29
|
break;
|
|
29
30
|
case "--version":
|
|
30
31
|
case "-v":
|
|
31
|
-
console.log("EmberKit CLI v0.
|
|
32
|
+
console.log("EmberKit CLI v0.6.0");
|
|
32
33
|
break;
|
|
33
34
|
case "--help":
|
|
34
35
|
case "-h":
|
|
@@ -42,7 +43,7 @@ export async function runCLI(args) {
|
|
|
42
43
|
}
|
|
43
44
|
function showHelp() {
|
|
44
45
|
console.log(`
|
|
45
|
-
🔥 EmberKit CLI v0.
|
|
46
|
+
🔥 EmberKit CLI v0.6.0
|
|
46
47
|
|
|
47
48
|
Usage: emberkit <command> [options]
|
|
48
49
|
|
|
@@ -51,15 +52,24 @@ Commands:
|
|
|
51
52
|
dev Start development server
|
|
52
53
|
build Build for production
|
|
53
54
|
preview Preview production build
|
|
54
|
-
generate <type>
|
|
55
|
+
generate <type> <name> Generate a file from a template
|
|
55
56
|
|
|
56
57
|
Options:
|
|
57
58
|
--template, -t <id> Project template to use
|
|
58
59
|
--no-install Skip dependency installation
|
|
60
|
+
--path, -p <path> Output path for generate (overrides default)
|
|
59
61
|
--help, -h Show this help message
|
|
60
62
|
--version, -v Show version number
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
Generate types:
|
|
65
|
+
route Route component (src/routes/)
|
|
66
|
+
component UI component (src/components/)
|
|
67
|
+
layout Layout component (src/layouts/)
|
|
68
|
+
loader Data loader (src/loaders/)
|
|
69
|
+
action Form action (src/actions/)
|
|
70
|
+
api API route handler (src/routes/_api/)
|
|
71
|
+
|
|
72
|
+
Project templates:
|
|
63
73
|
basic Simple starter with Tailwind CSS (default)
|
|
64
74
|
with-ui Starter with EmberKit UI components
|
|
65
75
|
minimal Barebones project, no CSS framework
|
|
@@ -163,12 +173,36 @@ function extractFlagValue(args, longFlag, shortFlag) {
|
|
|
163
173
|
}
|
|
164
174
|
return undefined;
|
|
165
175
|
}
|
|
176
|
+
const GENERATE_TYPES = {
|
|
177
|
+
route: { dir: "src/routes", ext: ".tsx" },
|
|
178
|
+
component: { dir: "src/components", ext: ".tsx" },
|
|
179
|
+
layout: { dir: "src/layouts", ext: ".tsx" },
|
|
180
|
+
error: { dir: "src/routes", ext: ".tsx" },
|
|
181
|
+
loader: { dir: "src/loaders", ext: ".ts" },
|
|
182
|
+
action: { dir: "src/actions", ext: ".ts" },
|
|
183
|
+
api: { dir: "src/routes/_api", ext: ".ts" },
|
|
184
|
+
};
|
|
166
185
|
async function runGenerate(args) {
|
|
167
|
-
const
|
|
186
|
+
const nonFlagArgs = args.filter((a) => !a.startsWith("-"));
|
|
187
|
+
const [type, name] = nonFlagArgs;
|
|
168
188
|
if (!type || !name) {
|
|
169
|
-
console.error(
|
|
189
|
+
console.error(`Usage: emberkit generate <type> <name> [--path <file-path>]\n\nTypes: ${Object.keys(GENERATE_TYPES).join(", ")}`);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
const explicitPath = extractFlagValue(args, "--path", "-p");
|
|
193
|
+
const typeConfig = GENERATE_TYPES[type];
|
|
194
|
+
if (!typeConfig && !explicitPath) {
|
|
195
|
+
console.error(`Unknown type "${type}". Valid types: ${Object.keys(GENERATE_TYPES).join(", ")}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
const filePath = explicitPath ??
|
|
199
|
+
`${typeConfig.dir}/${toKebabCase(name)}${typeConfig.ext}`;
|
|
200
|
+
const result = await generate({ name, path: filePath, template: type });
|
|
201
|
+
if (result.success) {
|
|
202
|
+
console.log(`✓ Generated ${type}: ${result.path}`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
console.error(`✗ ${result.error}`);
|
|
170
206
|
process.exit(1);
|
|
171
207
|
}
|
|
172
|
-
console.log(`🎨 Generating ${type}: ${name}`);
|
|
173
|
-
console.log("(Not yet implemented)");
|
|
174
208
|
}
|
package/dist/commands/create.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
2
|
-
import { resolve, join } from "path";
|
|
2
|
+
import { resolve, join, dirname } from "path";
|
|
3
3
|
import { execSync } from "child_process";
|
|
4
4
|
import { getPackageManager, getInstallCommand } from "../utils/filesystem.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
5
|
+
import { formatTemplate, toKebabCase } from "../templates/index.js";
|
|
6
|
+
import { starterFiles } from "../templates/project-templates/starter-kit/starter.js";
|
|
7
|
+
import { withUiTemplate } from "../templates/project-templates/starter-kit/with-ui.js";
|
|
8
|
+
import { minimalTemplate } from "../templates/project-templates/minimal/minimal.js";
|
|
9
|
+
import { blogTemplate } from "../templates/project-templates/blog/blog.js";
|
|
10
|
+
import { saasTemplate } from "../templates/project-templates/saas/saas.js";
|
|
11
|
+
import { dashboardTemplate } from "../templates/project-templates/dashboard/dashboard.js";
|
|
12
|
+
import { apiTemplate } from "../templates/project-templates/api/api.js";
|
|
11
13
|
const RESET = "\x1b[0m";
|
|
12
14
|
const BOLD = "\x1b[1m";
|
|
13
15
|
const DIM = "\x1b[2m";
|
|
@@ -67,19 +69,6 @@ function printInfo(message) {
|
|
|
67
69
|
const info = BRIGHT_BLUE + "›" + RESET;
|
|
68
70
|
console.log(` ${info} ${DIM + message + RESET}`);
|
|
69
71
|
}
|
|
70
|
-
function formatTemplate(template, vars) {
|
|
71
|
-
let result = template;
|
|
72
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
73
|
-
result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value);
|
|
74
|
-
}
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
function toKebabCase(str) {
|
|
78
|
-
return str
|
|
79
|
-
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
80
|
-
.replace(/[\s_]+/g, "-")
|
|
81
|
-
.toLowerCase();
|
|
82
|
-
}
|
|
83
72
|
function getNpmPackageName(name) {
|
|
84
73
|
const kebab = toKebabCase(name);
|
|
85
74
|
return kebab.startsWith("@") ? kebab : kebab.replace(/^emberkit-/, "");
|
|
@@ -115,7 +104,7 @@ export async function create(options) {
|
|
|
115
104
|
const templateFiles = template.files;
|
|
116
105
|
for (const [filePath, content] of Object.entries(templateFiles)) {
|
|
117
106
|
const fullPath = join(targetDir, filePath);
|
|
118
|
-
const dir =
|
|
107
|
+
const dir = dirname(fullPath);
|
|
119
108
|
if (!existsSync(dir)) {
|
|
120
109
|
mkdirSync(dir, { recursive: true });
|
|
121
110
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const actionTemplate = `import type { ActionFunction, LoaderResult } from '@emberkit/core';
|
|
2
|
+
|
|
3
|
+
export const action: ActionFunction = async ({ params, request }) => {
|
|
4
|
+
const formData = await request.formData();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
data: {
|
|
8
|
+
success: true,
|
|
9
|
+
},
|
|
10
|
+
} as LoaderResult<unknown>;
|
|
11
|
+
};
|
|
12
|
+
`;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const actionTemplate = `import type { ActionFunction, LoaderResult } from '@emberkit/core';
|
|
2
|
+
|
|
3
|
+
export const action: ActionFunction = async ({ params, request }) => {
|
|
4
|
+
const formData = await request.formData();
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
data: {
|
|
8
|
+
success: true,
|
|
9
|
+
},
|
|
10
|
+
} as LoaderResult<unknown>;
|
|
11
|
+
};
|
|
12
|
+
`;
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
export const apiTemplate = {
|
|
2
|
+
"package.json": `{
|
|
3
|
+
"name": "{{name}}",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "emberkit dev",
|
|
9
|
+
"build": "emberkit build",
|
|
10
|
+
"preview": "emberkit preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@emberkit/core": "^0.2.4"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@emberkit/cli": "^0.2.4",
|
|
17
|
+
"typescript": "^5.7.0",
|
|
18
|
+
"vite": "^6.0.0"
|
|
19
|
+
}
|
|
20
|
+
}`,
|
|
21
|
+
"tsconfig.json": `{
|
|
22
|
+
"compilerOptions": {
|
|
23
|
+
"target": "ES2022",
|
|
24
|
+
"module": "ESNext",
|
|
25
|
+
"moduleResolution": "bundler",
|
|
26
|
+
"jsx": "react-jsx",
|
|
27
|
+
"jsxImportSource": "@emberkit/core",
|
|
28
|
+
"strict": true,
|
|
29
|
+
"esModuleInterop": true,
|
|
30
|
+
"skipLibCheck": true,
|
|
31
|
+
"forceConsistentCasingInFileNames": true,
|
|
32
|
+
"resolveJsonModule": true,
|
|
33
|
+
"isolatedModules": true,
|
|
34
|
+
"noEmit": true,
|
|
35
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
36
|
+
"paths": {
|
|
37
|
+
"@/*": ["./src/*"]
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"include": ["src"],
|
|
41
|
+
"exclude": ["node_modules", "dist"]
|
|
42
|
+
}`,
|
|
43
|
+
"vite.config.ts": `import { defineConfig } from 'vite';
|
|
44
|
+
import { emberkitVitePlugin } from '@emberkit/core/vite-plugin';
|
|
45
|
+
|
|
46
|
+
export default defineConfig({
|
|
47
|
+
plugins: [emberkitVitePlugin()],
|
|
48
|
+
server: {
|
|
49
|
+
port: 3000,
|
|
50
|
+
host: 'localhost',
|
|
51
|
+
},
|
|
52
|
+
esbuild: {
|
|
53
|
+
jsxImportSource: '@emberkit/core',
|
|
54
|
+
},
|
|
55
|
+
});`,
|
|
56
|
+
"index.html": `<!DOCTYPE html>
|
|
57
|
+
<html lang="en">
|
|
58
|
+
<head>
|
|
59
|
+
<meta charset="UTF-8">
|
|
60
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
61
|
+
<title>{{name}} API</title>
|
|
62
|
+
</head>
|
|
63
|
+
<body id="app">
|
|
64
|
+
<script type="module" src="/src/index.tsx"></script>
|
|
65
|
+
</body>
|
|
66
|
+
</html>`,
|
|
67
|
+
"src/index.tsx": `import { render } from '@emberkit/core';
|
|
68
|
+
import { routes } from 'virtual:emberkit-routes';
|
|
69
|
+
import App from './routes/index';
|
|
70
|
+
|
|
71
|
+
const root = document.getElementById('app');
|
|
72
|
+
|
|
73
|
+
if (root) {
|
|
74
|
+
try {
|
|
75
|
+
render(App, root, { routes });
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('[entry] Render error:', error);
|
|
78
|
+
}
|
|
79
|
+
}`,
|
|
80
|
+
"src/routes/index.tsx": `import type { RouteComponent } from '@emberkit/core';
|
|
81
|
+
|
|
82
|
+
const ApiHome: RouteComponent = () => {
|
|
83
|
+
const endpoints = [
|
|
84
|
+
{ method: 'GET', path: '/api/users', desc: 'List all users' },
|
|
85
|
+
{ method: 'POST', path: '/api/users', desc: 'Create a new user' },
|
|
86
|
+
{ method: 'GET', path: '/api/users/:id', desc: 'Get a user by ID' },
|
|
87
|
+
{ method: 'PUT', path: '/api/users/:id', desc: 'Update a user' },
|
|
88
|
+
{ method: 'DELETE', path: '/api/users/:id', desc: 'Delete a user' },
|
|
89
|
+
{ method: 'GET', path: '/api/health', desc: 'Health check' },
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
const methodColor = (method: string) => {
|
|
93
|
+
switch (method) {
|
|
94
|
+
case 'GET': return 'bg-green-100 text-green-700';
|
|
95
|
+
case 'POST': return 'bg-blue-100 text-blue-700';
|
|
96
|
+
case 'PUT': return 'bg-yellow-100 text-yellow-700';
|
|
97
|
+
case 'DELETE': return 'bg-red-100 text-red-700';
|
|
98
|
+
default: return 'bg-gray-100 text-gray-700';
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div style={{ fontFamily: 'system-ui, sans-serif', maxWidth: '800px', margin: '2rem auto', padding: '0 1rem' }}>
|
|
104
|
+
<header className="mb-12">
|
|
105
|
+
<h1 className="text-3xl font-bold mb-2">{{name}} API</h1>
|
|
106
|
+
<p className="text-gray-600">RESTful API built with EmberKit</p>
|
|
107
|
+
<div className="mt-4 p-4 bg-gray-100 rounded-lg">
|
|
108
|
+
<code className="text-sm">Base URL: http://localhost:3000</code>
|
|
109
|
+
</div>
|
|
110
|
+
</header>
|
|
111
|
+
|
|
112
|
+
<section>
|
|
113
|
+
<h2 className="text-xl font-semibold mb-4">Endpoints</h2>
|
|
114
|
+
<div className="space-y-3">
|
|
115
|
+
{endpoints.map((ep) => (
|
|
116
|
+
<div key={ep.method + ep.path} className="flex items-center gap-4 p-4 border border-gray-200 rounded-lg">
|
|
117
|
+
<span className={\`px-2 py-1 rounded text-xs font-bold \${methodColor(ep.method)}\`}>
|
|
118
|
+
{ep.method}
|
|
119
|
+
</span>
|
|
120
|
+
<code className="text-sm font-mono">{ep.path}</code>
|
|
121
|
+
<span className="text-gray-500 text-sm ml-auto">{ep.desc}</span>
|
|
122
|
+
</div>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
</section>
|
|
126
|
+
|
|
127
|
+
<section className="mt-12">
|
|
128
|
+
<h2 className="text-xl font-semibold mb-4">Example Response</h2>
|
|
129
|
+
<pre className="p-4 bg-gray-900 text-gray-100 rounded-lg overflow-x-auto text-sm">
|
|
130
|
+
{JSON.stringify({
|
|
131
|
+
data: [
|
|
132
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com' },
|
|
133
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' },
|
|
134
|
+
],
|
|
135
|
+
meta: { page: 1, total: 2 },
|
|
136
|
+
}, null, 2)}
|
|
137
|
+
</pre>
|
|
138
|
+
</section>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export default ApiHome;`,
|
|
144
|
+
"src/routes/_api/users.ts": `import type { LoaderFunction, LoaderResult, ActionFunction } from '@emberkit/core';
|
|
145
|
+
|
|
146
|
+
// In-memory store (replace with database in production)
|
|
147
|
+
const users = [
|
|
148
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com', createdAt: '2026-01-15' },
|
|
149
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', createdAt: '2026-02-20' },
|
|
150
|
+
{ id: 3, name: 'Mike Johnson', email: 'mike@example.com', createdAt: '2026-03-10' },
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
let nextId = 4;
|
|
154
|
+
|
|
155
|
+
export const GET: LoaderFunction = async ({ query }) => {
|
|
156
|
+
const page = parseInt(query.page as string) || 1;
|
|
157
|
+
const limit = parseInt(query.limit as string) || 10;
|
|
158
|
+
const start = (page - 1) * limit;
|
|
159
|
+
const end = start + limit;
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
data: {
|
|
163
|
+
users: users.slice(start, end),
|
|
164
|
+
meta: {
|
|
165
|
+
page,
|
|
166
|
+
limit,
|
|
167
|
+
total: users.length,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
} as LoaderResult<unknown>;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export const POST: ActionFunction = async ({ request }) => {
|
|
174
|
+
const body = await request.json();
|
|
175
|
+
|
|
176
|
+
if (!body.name || !body.email) {
|
|
177
|
+
return {
|
|
178
|
+
error: {
|
|
179
|
+
code: 'VALIDATION_ERROR',
|
|
180
|
+
message: 'Name and email are required',
|
|
181
|
+
status: 400,
|
|
182
|
+
},
|
|
183
|
+
} as LoaderResult<unknown>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const newUser = {
|
|
187
|
+
id: nextId++,
|
|
188
|
+
name: body.name,
|
|
189
|
+
email: body.email,
|
|
190
|
+
createdAt: new Date().toISOString().split('T')[0],
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
users.push(newUser);
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
data: newUser,
|
|
197
|
+
} as LoaderResult<unknown>;
|
|
198
|
+
};`,
|
|
199
|
+
"src/routes/_api/users/[id].ts": `import type { LoaderFunction, LoaderResult, ActionFunction } from '@emberkit/core';
|
|
200
|
+
|
|
201
|
+
// In-memory store (replace with database in production)
|
|
202
|
+
const users = [
|
|
203
|
+
{ id: 1, name: 'John Doe', email: 'john@example.com', createdAt: '2026-01-15' },
|
|
204
|
+
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', createdAt: '2026-02-20' },
|
|
205
|
+
{ id: 3, name: 'Mike Johnson', email: 'mike@example.com', createdAt: '2026-03-10' },
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
export const GET: LoaderFunction = async ({ params }) => {
|
|
209
|
+
const id = parseInt(params.id);
|
|
210
|
+
const user = users.find((u) => u.id === id);
|
|
211
|
+
|
|
212
|
+
if (!user) {
|
|
213
|
+
return {
|
|
214
|
+
error: {
|
|
215
|
+
code: 'NOT_FOUND',
|
|
216
|
+
message: 'User not found',
|
|
217
|
+
status: 404,
|
|
218
|
+
},
|
|
219
|
+
} as LoaderResult<unknown>;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
data: user,
|
|
224
|
+
} as LoaderResult<unknown>;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export const PUT: ActionFunction = async ({ params, request }) => {
|
|
228
|
+
const id = parseInt(params.id);
|
|
229
|
+
const index = users.findIndex((u) => u.id === id);
|
|
230
|
+
|
|
231
|
+
if (index === -1) {
|
|
232
|
+
return {
|
|
233
|
+
error: {
|
|
234
|
+
code: 'NOT_FOUND',
|
|
235
|
+
message: 'User not found',
|
|
236
|
+
status: 404,
|
|
237
|
+
},
|
|
238
|
+
} as LoaderResult<unknown>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const body = await request.json();
|
|
242
|
+
users[index] = { ...users[index], ...body };
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
data: users[index],
|
|
246
|
+
} as LoaderResult<unknown>;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export const DELETE: ActionFunction = async ({ params }) => {
|
|
250
|
+
const id = parseInt(params.id);
|
|
251
|
+
const index = users.findIndex((u) => u.id === id);
|
|
252
|
+
|
|
253
|
+
if (index === -1) {
|
|
254
|
+
return {
|
|
255
|
+
error: {
|
|
256
|
+
code: 'NOT_FOUND',
|
|
257
|
+
message: 'User not found',
|
|
258
|
+
status: 404,
|
|
259
|
+
},
|
|
260
|
+
} as LoaderResult<unknown>;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
users.splice(index, 1);
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
data: { success: true },
|
|
267
|
+
} as LoaderResult<unknown>;
|
|
268
|
+
};`,
|
|
269
|
+
"src/routes/_api/health.ts": `import type { LoaderFunction, LoaderResult } from '@emberkit/core';
|
|
270
|
+
|
|
271
|
+
export const GET: LoaderFunction = async () => {
|
|
272
|
+
return {
|
|
273
|
+
data: {
|
|
274
|
+
status: 'ok',
|
|
275
|
+
timestamp: new Date().toISOString(),
|
|
276
|
+
uptime: process.uptime(),
|
|
277
|
+
},
|
|
278
|
+
} as LoaderResult<unknown>;
|
|
279
|
+
};`,
|
|
280
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const apiRouteTemplate = `import type { LoaderFunction, LoaderResult } from '@emberkit/core';
|
|
2
|
+
|
|
3
|
+
export const GET: LoaderFunction = async ({ params, query, request }) => {
|
|
4
|
+
return {
|
|
5
|
+
data: {
|
|
6
|
+
message: 'Hello from API',
|
|
7
|
+
},
|
|
8
|
+
} as LoaderResult<unknown>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const POST: LoaderFunction = async ({ request }) => {
|
|
12
|
+
const body = await request.json();
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
data: {
|
|
16
|
+
received: body,
|
|
17
|
+
},
|
|
18
|
+
} as LoaderResult<unknown>;
|
|
19
|
+
};
|
|
20
|
+
`;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const apiRouteTemplate = `import type { LoaderFunction, LoaderResult } from '@emberkit/core';
|
|
2
|
+
|
|
3
|
+
export const GET: LoaderFunction = async ({ params, query, request }) => {
|
|
4
|
+
return {
|
|
5
|
+
data: {
|
|
6
|
+
message: 'Hello from API',
|
|
7
|
+
},
|
|
8
|
+
} as LoaderResult<unknown>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const POST: LoaderFunction = async ({ request }) => {
|
|
12
|
+
const body = await request.json();
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
data: {
|
|
16
|
+
received: body,
|
|
17
|
+
},
|
|
18
|
+
} as LoaderResult<unknown>;
|
|
19
|
+
};
|
|
20
|
+
`;
|