@kosmic/cli 0.0.1 → 0.0.2
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/src/create/scaffold.d.ts.map +1 -1
- package/dist/src/create/scaffold.js +28 -9
- package/dist/src/create/scaffold.js.map +1 -1
- package/dist/src/create/templates/auth.d.ts +1 -1
- package/dist/src/create/templates/auth.d.ts.map +1 -1
- package/dist/src/create/templates/auth.js +2 -354
- package/dist/src/create/templates/auth.js.map +1 -1
- package/dist/src/create/templates/base.d.ts +1 -1
- package/dist/src/create/templates/base.d.ts.map +1 -1
- package/dist/src/create/templates/base.js +1 -219
- package/dist/src/create/templates/base.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/create/scaffold.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,CAAC,CAOlB;
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/create/scaffold.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,CAAC,CAOlB;AA8DD;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAgBf"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { authTemplateDirectory } from "./templates/auth.js";
|
|
4
|
+
import { baseTemplateDirectory } from "./templates/base.js";
|
|
5
5
|
/**
|
|
6
6
|
* Check whether a directory exists and contains at least one entry.
|
|
7
7
|
*/
|
|
@@ -15,17 +15,36 @@ export async function directoryHasFiles(targetDirectory) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Render placeholders in template content using the provided context.
|
|
19
19
|
*/
|
|
20
20
|
function renderTemplate(fileContents, context) {
|
|
21
|
-
return fileContents.replaceAll(
|
|
21
|
+
return fileContents.replaceAll(/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/gv, (match, key) => context[key] ?? match);
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* Recursively list all files in a template directory.
|
|
25
25
|
*/
|
|
26
|
-
async function
|
|
27
|
-
const
|
|
26
|
+
async function getTemplateFiles(templateDirectory) {
|
|
27
|
+
const entries = await fs.readdir(templateDirectory, { withFileTypes: true });
|
|
28
|
+
const files = await Promise.all(entries.map(async (entry) => {
|
|
29
|
+
const entryPath = path.join(templateDirectory, entry.name);
|
|
30
|
+
if (entry.isDirectory()) {
|
|
31
|
+
return getTemplateFiles(entryPath);
|
|
32
|
+
}
|
|
33
|
+
return [entryPath];
|
|
34
|
+
}));
|
|
35
|
+
return files.flat();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Write template files from a source directory to the target directory.
|
|
39
|
+
*/
|
|
40
|
+
async function writeTemplates(targetDirectory, templateDirectory, context) {
|
|
41
|
+
const templateFiles = await getTemplateFiles(templateDirectory);
|
|
42
|
+
const writes = templateFiles.map(async (templatePath) => {
|
|
43
|
+
const relativePath = path
|
|
44
|
+
.relative(templateDirectory, templatePath)
|
|
45
|
+
.replace(/\.tmpl$/v, '');
|
|
28
46
|
const outputPath = path.join(targetDirectory, relativePath);
|
|
47
|
+
const templateValue = await fs.readFile(templatePath, 'utf8');
|
|
29
48
|
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
30
49
|
await fs.writeFile(outputPath, renderTemplate(templateValue, context), 'utf8');
|
|
31
50
|
});
|
|
@@ -41,9 +60,9 @@ export async function scaffoldKosmicProject(options) {
|
|
|
41
60
|
}
|
|
42
61
|
await fs.mkdir(targetDirectory, { recursive: true });
|
|
43
62
|
const context = { projectName };
|
|
44
|
-
await writeTemplates(targetDirectory,
|
|
63
|
+
await writeTemplates(targetDirectory, baseTemplateDirectory, context);
|
|
45
64
|
if (withAuth) {
|
|
46
|
-
await writeTemplates(targetDirectory,
|
|
65
|
+
await writeTemplates(targetDirectory, authTemplateDirectory, context);
|
|
47
66
|
}
|
|
48
67
|
}
|
|
49
68
|
//# sourceMappingURL=scaffold.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/create/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/create/scaffold.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const authTemplateDirectory: string;
|
|
2
2
|
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../src/create/templates/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../src/create/templates/auth.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB,QAAyC,CAAC"}
|
|
@@ -1,355 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
PORT=3000
|
|
4
|
-
SERVER_HOST=127.0.0.1
|
|
5
|
-
LOG_LEVEL=info
|
|
6
|
-
|
|
7
|
-
# Basic auth credentials for scaffolded login
|
|
8
|
-
APP_AUTH_USERNAME=admin
|
|
9
|
-
APP_AUTH_PASSWORD=kosmic
|
|
10
|
-
`,
|
|
11
|
-
'README.md': `# {{projectName}}
|
|
12
|
-
|
|
13
|
-
A minimal Kosmic application scaffolded with optional auth support.
|
|
14
|
-
|
|
15
|
-
## Scripts
|
|
16
|
-
|
|
17
|
-
- npm run dev
|
|
18
|
-
- npm run build
|
|
19
|
-
- npm run start
|
|
20
|
-
- npm run lint
|
|
21
|
-
|
|
22
|
-
## Development
|
|
23
|
-
|
|
24
|
-
1. Copy .env.example to .env.
|
|
25
|
-
2. Run npm install.
|
|
26
|
-
3. Run npm run dev.
|
|
27
|
-
4. Visit /login and sign in with APP_AUTH_USERNAME and APP_AUTH_PASSWORD.
|
|
28
|
-
`,
|
|
29
|
-
'src/server.ts': `import path from 'node:path';
|
|
30
|
-
import {KosmicServer, type SessionStore} from '@kosmic/server';
|
|
31
|
-
|
|
32
|
-
class MemorySessionStore implements SessionStore {
|
|
33
|
-
readonly #sessions = new Map<string, unknown>();
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Retrieve a serialized session value.
|
|
37
|
-
*/
|
|
38
|
-
async get(key: string): Promise<unknown> {
|
|
39
|
-
return this.#sessions.get(key);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Persist a serialized session value.
|
|
44
|
-
*/
|
|
45
|
-
async set(key: string, value: unknown): Promise<void> {
|
|
46
|
-
this.#sessions.set(key, value);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Remove an existing session value.
|
|
51
|
-
*/
|
|
52
|
-
async destroy(key: string): Promise<void> {
|
|
53
|
-
this.#sessions.delete(key);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export const kosmicServer = new KosmicServer({
|
|
58
|
-
routesDir: path.join(import.meta.dirname, 'routes'),
|
|
59
|
-
sessionStore: new MemorySessionStore(),
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Return the app singleton.
|
|
64
|
-
*/
|
|
65
|
-
export function getServer(): KosmicServer {
|
|
66
|
-
return kosmicServer;
|
|
67
|
-
}
|
|
68
|
-
`,
|
|
69
|
-
'src/routes/index.ts': `import type {Context} from '@kosmic/server';
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Render the default landing page.
|
|
73
|
-
*/
|
|
74
|
-
export async function get(ctx: Context) {
|
|
75
|
-
const user = ctx.session?.user;
|
|
76
|
-
|
|
77
|
-
ctx.type = 'html';
|
|
78
|
-
ctx.body = \`<!doctype html>
|
|
79
|
-
<html lang="en">
|
|
80
|
-
<head>
|
|
81
|
-
<meta charset="utf-8" />
|
|
82
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
83
|
-
<title>{{projectName}}</title>
|
|
84
|
-
<style>
|
|
85
|
-
:root {
|
|
86
|
-
--bg: #f6f7fb;
|
|
87
|
-
--card: #ffffff;
|
|
88
|
-
--text: #14213d;
|
|
89
|
-
--muted: #5f6c80;
|
|
90
|
-
--accent: #fca311;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
body {
|
|
94
|
-
margin: 0;
|
|
95
|
-
min-height: 100vh;
|
|
96
|
-
display: grid;
|
|
97
|
-
place-items: center;
|
|
98
|
-
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif;
|
|
99
|
-
color: var(--text);
|
|
100
|
-
background: radial-gradient(circle at 20% 20%, #fff9eb 0%, var(--bg) 50%);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
main {
|
|
104
|
-
max-width: 42rem;
|
|
105
|
-
padding: 2rem;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
section {
|
|
109
|
-
background: var(--card);
|
|
110
|
-
border-radius: 14px;
|
|
111
|
-
box-shadow: 0 12px 36px rgb(20 33 61 / 0.12);
|
|
112
|
-
padding: 2rem;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
h1 {
|
|
116
|
-
margin-top: 0;
|
|
117
|
-
font-size: 2rem;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
nav {
|
|
121
|
-
margin-top: 1.25rem;
|
|
122
|
-
display: flex;
|
|
123
|
-
gap: 0.75rem;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
a {
|
|
127
|
-
color: #7a4a00;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
p {
|
|
131
|
-
color: var(--muted);
|
|
132
|
-
line-height: 1.6;
|
|
133
|
-
}
|
|
134
|
-
</style>
|
|
135
|
-
</head>
|
|
136
|
-
<body>
|
|
137
|
-
<main>
|
|
138
|
-
<section>
|
|
139
|
-
<h1>{{projectName}}</h1>
|
|
140
|
-
<p>
|
|
141
|
-
This app includes optional auth routes. Edit <code>src/routes</code> to
|
|
142
|
-
extend the template.
|
|
143
|
-
</p>
|
|
144
|
-
<nav>
|
|
145
|
-
\${
|
|
146
|
-
user
|
|
147
|
-
? '<a href="/account">Account</a><a href="/logout">Logout</a>'
|
|
148
|
-
: '<a href="/login">Login</a>'
|
|
149
|
-
}
|
|
150
|
-
</nav>
|
|
151
|
-
</section>
|
|
152
|
-
</main>
|
|
153
|
-
</body>
|
|
154
|
-
</html>\`;
|
|
155
|
-
}
|
|
156
|
-
`,
|
|
157
|
-
'src/routes/login.ts': `import type {Context} from '@kosmic/server';
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Safely read a trimmed string value from an unknown request body object.
|
|
161
|
-
*/
|
|
162
|
-
function readBodyString(
|
|
163
|
-
body: unknown,
|
|
164
|
-
key: 'username' | 'password',
|
|
165
|
-
): string | undefined {
|
|
166
|
-
if (!body || typeof body !== 'object') {
|
|
167
|
-
return undefined;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const value = (body as Record<string, unknown>)[key];
|
|
171
|
-
|
|
172
|
-
if (typeof value !== 'string') {
|
|
173
|
-
return undefined;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const trimmedValue = value.trim();
|
|
177
|
-
return trimmedValue.length > 0 ? trimmedValue : undefined;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Render the login form.
|
|
182
|
-
*/
|
|
183
|
-
export async function get(ctx: Context) {
|
|
184
|
-
ctx.type = 'html';
|
|
185
|
-
ctx.body = \`<!doctype html>
|
|
186
|
-
<html lang="en">
|
|
187
|
-
<head>
|
|
188
|
-
<meta charset="utf-8" />
|
|
189
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
190
|
-
<title>Login</title>
|
|
191
|
-
<style>
|
|
192
|
-
body {
|
|
193
|
-
margin: 0;
|
|
194
|
-
min-height: 100vh;
|
|
195
|
-
display: grid;
|
|
196
|
-
place-items: center;
|
|
197
|
-
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif;
|
|
198
|
-
background: #f6f7fb;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
form {
|
|
202
|
-
width: min(24rem, calc(100vw - 2rem));
|
|
203
|
-
background: #ffffff;
|
|
204
|
-
border-radius: 12px;
|
|
205
|
-
padding: 1.5rem;
|
|
206
|
-
box-shadow: 0 8px 24px rgb(20 33 61 / 0.12);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
label {
|
|
210
|
-
display: block;
|
|
211
|
-
margin-bottom: 0.5rem;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
input {
|
|
215
|
-
width: 100%;
|
|
216
|
-
padding: 0.6rem;
|
|
217
|
-
margin-top: 0.25rem;
|
|
218
|
-
margin-bottom: 0.75rem;
|
|
219
|
-
border: 1px solid #d6dbe7;
|
|
220
|
-
border-radius: 8px;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
button {
|
|
224
|
-
border: 0;
|
|
225
|
-
border-radius: 8px;
|
|
226
|
-
padding: 0.6rem 0.9rem;
|
|
227
|
-
background: #fca311;
|
|
228
|
-
color: #1f2937;
|
|
229
|
-
font-weight: 600;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
.message {
|
|
233
|
-
color: #9a3412;
|
|
234
|
-
}
|
|
235
|
-
</style>
|
|
236
|
-
</head>
|
|
237
|
-
<body>
|
|
238
|
-
<form method="post" action="/login">
|
|
239
|
-
<h1>Sign in</h1>
|
|
240
|
-
<p class="message">\${ctx.query.error ? 'Invalid credentials.' : ''}</p>
|
|
241
|
-
<label>
|
|
242
|
-
Username
|
|
243
|
-
<input required name="username" autocomplete="username" />
|
|
244
|
-
</label>
|
|
245
|
-
<label>
|
|
246
|
-
Password
|
|
247
|
-
<input
|
|
248
|
-
required
|
|
249
|
-
type="password"
|
|
250
|
-
name="password"
|
|
251
|
-
autocomplete="current-password"
|
|
252
|
-
/>
|
|
253
|
-
</label>
|
|
254
|
-
<button type="submit">Login</button>
|
|
255
|
-
</form>
|
|
256
|
-
</body>
|
|
257
|
-
</html>\`;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Validate credentials and establish a session.
|
|
262
|
-
*/
|
|
263
|
-
export async function post(ctx: Context) {
|
|
264
|
-
const expectedUsername = process.env.APP_AUTH_USERNAME ?? 'admin';
|
|
265
|
-
const expectedPassword = process.env.APP_AUTH_PASSWORD ?? 'kosmic';
|
|
266
|
-
const username = readBodyString(ctx.request.body, 'username');
|
|
267
|
-
const password = readBodyString(ctx.request.body, 'password');
|
|
268
|
-
|
|
269
|
-
if (!ctx.session || username !== expectedUsername || password !== expectedPassword) {
|
|
270
|
-
ctx.redirect('/login?error=1');
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
ctx.session.user = {
|
|
275
|
-
username,
|
|
276
|
-
loggedInAt: new Date().toISOString(),
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
ctx.redirect('/account');
|
|
280
|
-
}
|
|
281
|
-
`,
|
|
282
|
-
'src/routes/logout.ts': `import type {Context} from '@kosmic/server';
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Destroy the local session user and redirect home.
|
|
286
|
-
*/
|
|
287
|
-
export async function get(ctx: Context) {
|
|
288
|
-
if (ctx.session) {
|
|
289
|
-
delete ctx.session.user;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
ctx.redirect('/');
|
|
293
|
-
}
|
|
294
|
-
`,
|
|
295
|
-
'src/routes/account.ts': `import type {Context} from '@kosmic/server';
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Render the protected account page.
|
|
299
|
-
*/
|
|
300
|
-
export async function get(ctx: Context) {
|
|
301
|
-
const user = ctx.session?.user;
|
|
302
|
-
|
|
303
|
-
if (!user) {
|
|
304
|
-
ctx.redirect('/login');
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
ctx.type = 'html';
|
|
309
|
-
ctx.body = \`<!doctype html>
|
|
310
|
-
<html lang="en">
|
|
311
|
-
<head>
|
|
312
|
-
<meta charset="utf-8" />
|
|
313
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
314
|
-
<title>Account</title>
|
|
315
|
-
<style>
|
|
316
|
-
body {
|
|
317
|
-
margin: 0;
|
|
318
|
-
min-height: 100vh;
|
|
319
|
-
display: grid;
|
|
320
|
-
place-items: center;
|
|
321
|
-
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif;
|
|
322
|
-
background: #f6f7fb;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
article {
|
|
326
|
-
width: min(40rem, calc(100vw - 2rem));
|
|
327
|
-
background: #ffffff;
|
|
328
|
-
border-radius: 12px;
|
|
329
|
-
padding: 1.5rem;
|
|
330
|
-
box-shadow: 0 8px 24px rgb(20 33 61 / 0.12);
|
|
331
|
-
}
|
|
332
|
-
</style>
|
|
333
|
-
</head>
|
|
334
|
-
<body>
|
|
335
|
-
<article>
|
|
336
|
-
<h1>Account</h1>
|
|
337
|
-
<p>Welcome, \${user.username}.</p>
|
|
338
|
-
<p>Logged in at \${new Date(user.loggedInAt).toLocaleString()}.</p>
|
|
339
|
-
<p><a href="/logout">Logout</a></p>
|
|
340
|
-
</article>
|
|
341
|
-
</body>
|
|
342
|
-
</html>\`;
|
|
343
|
-
}
|
|
344
|
-
`,
|
|
345
|
-
'src/types/session.d.ts': `declare module 'koa-session' {
|
|
346
|
-
interface Session {
|
|
347
|
-
user?: {
|
|
348
|
-
username: string;
|
|
349
|
-
loggedInAt: string;
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
`,
|
|
354
|
-
};
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
export const authTemplateDirectory = path.join(import.meta.dirname, 'auth');
|
|
355
3
|
//# sourceMappingURL=auth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/create/templates/auth.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/create/templates/auth.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const baseTemplateDirectory: string;
|
|
2
2
|
//# sourceMappingURL=base.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/create/templates/base.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/create/templates/base.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB,QAAyC,CAAC"}
|
|
@@ -1,221 +1,3 @@
|
|
|
1
|
-
export const baseTemplateFiles = {
|
|
2
|
-
'.env.example': `# Server settings
|
|
3
|
-
PORT=3000
|
|
4
|
-
SERVER_HOST=127.0.0.1
|
|
5
|
-
LOG_LEVEL=info
|
|
6
|
-
`,
|
|
7
|
-
'.gitignore': `node_modules
|
|
8
|
-
dist
|
|
9
|
-
.env
|
|
10
|
-
`,
|
|
11
|
-
'README.md': `# {{projectName}}
|
|
12
|
-
|
|
13
|
-
A minimal Kosmic application scaffolded with the create command.
|
|
14
|
-
|
|
15
|
-
## Scripts
|
|
16
|
-
|
|
17
|
-
- npm run dev
|
|
18
|
-
- npm run build
|
|
19
|
-
- npm run start
|
|
20
|
-
- npm run lint
|
|
21
|
-
|
|
22
|
-
## Development
|
|
23
|
-
|
|
24
|
-
1. Copy .env.example to .env.
|
|
25
|
-
2. Run npm install.
|
|
26
|
-
3. Run npm run dev.
|
|
27
|
-
`,
|
|
28
|
-
'package.json': `{
|
|
29
|
-
"name": "{{projectName}}",
|
|
30
|
-
"version": "0.0.0",
|
|
31
|
-
"private": true,
|
|
32
|
-
"type": "module",
|
|
33
|
-
"main": "dist/src/index.js",
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "kosmic build",
|
|
36
|
-
"check": "tsc --noEmit",
|
|
37
|
-
"dev": "kosmic dev",
|
|
38
|
-
"lint": "xo",
|
|
39
|
-
"start": "kosmic start",
|
|
40
|
-
"test": "node --test"
|
|
41
|
-
},
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"@kosmic/cli": "latest",
|
|
44
|
-
"@kosmic/config": "latest",
|
|
45
|
-
"@kosmic/logger": "latest",
|
|
46
|
-
"@kosmic/server": "latest"
|
|
47
|
-
},
|
|
48
|
-
"devDependencies": {
|
|
49
|
-
"@types/node": "~25.5.0",
|
|
50
|
-
"typescript": "~5.9.3",
|
|
51
|
-
"vite": "^8.0.3",
|
|
52
|
-
"xo": "^1.2.3"
|
|
53
|
-
},
|
|
54
|
-
"engines": {
|
|
55
|
-
"node": ">=22"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
`,
|
|
59
|
-
'tsconfig.json': `{
|
|
60
|
-
"compilerOptions": {
|
|
61
|
-
"target": "ES2022",
|
|
62
|
-
"module": "NodeNext",
|
|
63
|
-
"moduleResolution": "NodeNext",
|
|
64
|
-
"strict": true,
|
|
65
|
-
"skipLibCheck": true,
|
|
66
|
-
"outDir": "dist",
|
|
67
|
-
"declaration": true,
|
|
68
|
-
"declarationMap": true,
|
|
69
|
-
"sourceMap": true,
|
|
70
|
-
"types": ["node"]
|
|
71
|
-
},
|
|
72
|
-
"include": ["src", "vite.config.ts"],
|
|
73
|
-
"exclude": ["node_modules", "dist"]
|
|
74
|
-
}
|
|
75
|
-
`,
|
|
76
|
-
'vite.config.ts': `/* eslint-disable unicorn/prefer-module */
|
|
77
1
|
import path from 'node:path';
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const config: UserConfig = {
|
|
81
|
-
root: path.join(__dirname, 'src', 'client'),
|
|
82
|
-
build: {
|
|
83
|
-
manifest: true,
|
|
84
|
-
rollupOptions: {
|
|
85
|
-
input: path.join(__dirname, 'src', 'client', 'scripts', 'index.ts'),
|
|
86
|
-
},
|
|
87
|
-
outDir: path.join(__dirname, 'dist', 'src', 'public'),
|
|
88
|
-
emptyOutDir: true,
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
export default config;
|
|
93
|
-
`,
|
|
94
|
-
'src/index.ts': `import process from 'node:process';
|
|
95
|
-
import {config} from '@kosmic/config';
|
|
96
|
-
import {logger} from '@kosmic/logger';
|
|
97
|
-
import {kosmicServer} from './server.ts';
|
|
98
|
-
|
|
99
|
-
const server = await kosmicServer.listen(config.port, config.host);
|
|
100
|
-
|
|
101
|
-
logger.info(\`Server listening on \${config.host}:\${config.port}\`);
|
|
102
|
-
|
|
103
|
-
if (process.send) {
|
|
104
|
-
process.send({status: 'ready'});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
process.on('unhandledRejection', (error) => {
|
|
108
|
-
throw error;
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
process.on('uncaughtException', (error) => {
|
|
112
|
-
logger.error(error);
|
|
113
|
-
process.exit(1);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
process.on('beforeExit', () => {
|
|
117
|
-
logger.info('Server shutting down');
|
|
118
|
-
server.closeAllConnections();
|
|
119
|
-
server.close(() => {
|
|
120
|
-
process.exit(0);
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
`,
|
|
124
|
-
'src/server.ts': `import path from 'node:path';
|
|
125
|
-
import {KosmicServer} from '@kosmic/server';
|
|
126
|
-
|
|
127
|
-
export const kosmicServer = new KosmicServer({
|
|
128
|
-
routesDir: path.join(import.meta.dirname, 'routes'),
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Return the app singleton.
|
|
133
|
-
*/
|
|
134
|
-
export function getServer(): KosmicServer {
|
|
135
|
-
return kosmicServer;
|
|
136
|
-
}
|
|
137
|
-
`,
|
|
138
|
-
'src/routes/index.ts': `import type {Context} from '@kosmic/server';
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Render the default landing page.
|
|
142
|
-
*/
|
|
143
|
-
export async function get(ctx: Context) {
|
|
144
|
-
ctx.type = 'html';
|
|
145
|
-
ctx.body = \`<!doctype html>
|
|
146
|
-
<html lang="en">
|
|
147
|
-
<head>
|
|
148
|
-
<meta charset="utf-8" />
|
|
149
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
150
|
-
<title>{{projectName}}</title>
|
|
151
|
-
<style>
|
|
152
|
-
:root {
|
|
153
|
-
--bg: #f6f7fb;
|
|
154
|
-
--card: #ffffff;
|
|
155
|
-
--text: #14213d;
|
|
156
|
-
--muted: #5f6c80;
|
|
157
|
-
--accent: #fca311;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
body {
|
|
161
|
-
margin: 0;
|
|
162
|
-
min-height: 100vh;
|
|
163
|
-
display: grid;
|
|
164
|
-
place-items: center;
|
|
165
|
-
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif;
|
|
166
|
-
color: var(--text);
|
|
167
|
-
background: radial-gradient(circle at 20% 20%, #fff9eb 0%, var(--bg) 50%);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
main {
|
|
171
|
-
max-width: 42rem;
|
|
172
|
-
padding: 2rem;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
section {
|
|
176
|
-
background: var(--card);
|
|
177
|
-
border-radius: 14px;
|
|
178
|
-
box-shadow: 0 12px 36px rgb(20 33 61 / 0.12);
|
|
179
|
-
padding: 2rem;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
h1 {
|
|
183
|
-
margin-top: 0;
|
|
184
|
-
font-size: 2rem;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
p {
|
|
188
|
-
color: var(--muted);
|
|
189
|
-
line-height: 1.6;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
code {
|
|
193
|
-
background: #fff1d1;
|
|
194
|
-
border-radius: 6px;
|
|
195
|
-
padding: 0.2rem 0.4rem;
|
|
196
|
-
}
|
|
197
|
-
</style>
|
|
198
|
-
</head>
|
|
199
|
-
<body>
|
|
200
|
-
<main>
|
|
201
|
-
<section>
|
|
202
|
-
<h1>{{projectName}}</h1>
|
|
203
|
-
<p>
|
|
204
|
-
Your Kosmic app is ready. Edit <code>src/routes/index.ts</code> to start
|
|
205
|
-
building your first route.
|
|
206
|
-
</p>
|
|
207
|
-
</section>
|
|
208
|
-
</main>
|
|
209
|
-
</body>
|
|
210
|
-
</html>\`;
|
|
211
|
-
}
|
|
212
|
-
`,
|
|
213
|
-
'src/public/.gitkeep': '',
|
|
214
|
-
'src/client/scripts/index.ts': `import '../styles/styles.css';
|
|
215
|
-
`,
|
|
216
|
-
'src/client/styles/styles.css': `:root {
|
|
217
|
-
color-scheme: light;
|
|
218
|
-
}
|
|
219
|
-
`,
|
|
220
|
-
};
|
|
2
|
+
export const baseTemplateDirectory = path.join(import.meta.dirname, 'base');
|
|
221
3
|
//# sourceMappingURL=base.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../../src/create/templates/base.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../../src/create/templates/base.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC"}
|