@alxia/create 0.1.0 → 0.1.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.
Files changed (48) hide show
  1. package/README.md +37 -6
  2. package/dist/copy.d.ts +24 -0
  3. package/dist/copy.d.ts.map +1 -0
  4. package/dist/index.js +61 -281
  5. package/dist/index.js.map +8 -9
  6. package/dist/main.d.ts.map +1 -1
  7. package/dist/registry.d.ts +28 -7
  8. package/dist/registry.d.ts.map +1 -1
  9. package/dist/versions.d.ts +0 -2
  10. package/dist/versions.d.ts.map +1 -1
  11. package/dist/write.d.ts +2 -1
  12. package/dist/write.d.ts.map +1 -1
  13. package/docs/README.md +3 -3
  14. package/docs/guide.md +104 -27
  15. package/docs/roadmap.md +35 -9
  16. package/docs/troubleshooting.md +58 -63
  17. package/package.json +5 -3
  18. package/templates/api/.dockerignore +6 -0
  19. package/templates/api/.env.example +3 -0
  20. package/templates/api/Dockerfile +22 -0
  21. package/templates/api/README.md +64 -0
  22. package/templates/api/gitignore +6 -0
  23. package/templates/api/package.json +21 -0
  24. package/templates/api/src/app.spec.ts +46 -0
  25. package/templates/api/src/app.ts +33 -0
  26. package/templates/api/src/server.ts +4 -0
  27. package/templates/api/tsconfig.json +25 -0
  28. package/templates/react-router/.dockerignore +4 -0
  29. package/templates/react-router/Dockerfile +29 -0
  30. package/templates/react-router/README.md +87 -0
  31. package/templates/react-router/_bunfig.toml +4 -0
  32. package/templates/react-router/app/app.css +15 -0
  33. package/templates/react-router/app/root.tsx +75 -0
  34. package/templates/react-router/app/routes/home.tsx +13 -0
  35. package/templates/react-router/app/routes.ts +3 -0
  36. package/templates/react-router/app/welcome/logo-dark.svg +23 -0
  37. package/templates/react-router/app/welcome/logo-light.svg +23 -0
  38. package/templates/react-router/app/welcome/welcome.tsx +89 -0
  39. package/templates/react-router/gitignore +7 -0
  40. package/templates/react-router/package.json +31 -0
  41. package/templates/react-router/public/favicon.ico +0 -0
  42. package/templates/react-router/react-router.config.ts +7 -0
  43. package/templates/react-router/tsconfig.json +26 -0
  44. package/templates/react-router/vite.config.ts +11 -0
  45. package/dist/templates/api.d.ts +0 -16
  46. package/dist/templates/api.d.ts.map +0 -1
  47. package/dist/templates/react-router.d.ts +0 -26
  48. package/dist/templates/react-router.d.ts.map +0 -1
package/README.md CHANGED
@@ -28,8 +28,8 @@ bun create @alxia my-site --template react-router
28
28
 
29
29
  | template | what it writes |
30
30
  | --- | --- |
31
- | `api` | an `@alxia/core` app with Zod: `POST /todos` validates its body, behind `requireKey`, a hook made with `defineHook` that answers 401 without an API key; a `bun test` spec calling it with `app.request()` and through `@alxia/client`, typed; `bun dev` restarting on change, `typecheck`, `build`, a strict `tsconfig.json`, `.gitignore` and a README |
32
- | `react-router` | React Router's own template, written by its `create-react-router`, then [`@alxia/react-router`](https://www.npmjs.com/package/@alxia/react-router) added as its README says: `alxia()` in `vite.config.ts`'s plugins, `start` running `bun build/server/index.js`, a `bunfig.toml` starting React Router's CLI on Bun. No server file: the default one serves the pages; `bunx alxia-react-router reveal` writes it out to customise |
31
+ | `api` | an `@alxia/core` app with Zod: `POST /todos` validates its body, behind `requireKey`, a hook made with `defineHook` that answers 401 without an API key; a `bun test` spec calling it with `app.request()` and through `@alxia/client`, typed; `bun dev` restarting on change, `typecheck`, `build`, a strict `tsconfig.json`, a `Dockerfile` on `oven/bun:1`, `.dockerignore`, `.gitignore`, `.env.example` and a README |
32
+ | `react-router` | React Router's official template, as `create-react-router` writes it, shipped in this package and copied, with [`@alxia/react-router`](https://www.npmjs.com/package/@alxia/react-router) added as its README says: `alxia()` in `vite.config.ts`'s plugins, `start` running `bun build/server/index.js`, a `bunfig.toml` starting React Router's CLI on Bun, and a `Dockerfile` on `oven/bun:1` in place of React Router's Node one. No server file: the default one serves the pages; `bunx alxia-react-router reveal` writes it out to customise |
33
33
 
34
34
  The heart of the `api` project, its route and hook (the whole file, with
35
35
  its imports and schemas, is in the [guide](https://github.com/softistx/alxia/blob/develop/packages/create/docs/guide.md#the-api-template)):
@@ -51,6 +51,33 @@ export const app = alxia()
51
51
  });
52
52
  ```
53
53
 
54
+ ## Docker
55
+
56
+ Both projects build into an image as they are written, on `oven/bun:1`,
57
+ the image running the app as the non-root `bun` user:
58
+
59
+ - `api`: the production dependencies, then `src/server.ts` run as it is.
60
+ Bun runs TypeScript, so there is no build stage.
61
+ - `react-router`: the production dependencies, then `bun run build`, then
62
+ `bun build/server/index.js`.
63
+
64
+ ```sh
65
+ cd my-api
66
+ docker build -t my-api .
67
+ docker run -p 3000:3000 -e API_KEY=change-me my-api
68
+ ```
69
+
70
+ ```sh
71
+ cd my-site
72
+ docker build -t my-site .
73
+ docker run -p 3000:3000 my-site
74
+ ```
75
+
76
+ Commit the `bun.lock` that `bun install` wrote: the image installs from it
77
+ with `--frozen-lockfile`. The
78
+ [guide](https://github.com/softistx/alxia/blob/develop/packages/create/docs/guide.md#docker)
79
+ has the stages.
80
+
54
81
  ## Options
55
82
 
56
83
  | option | |
@@ -65,9 +92,13 @@ or CI.
65
92
 
66
93
  ## Versions
67
94
 
68
- - **alxia's packages** are at the versions this release of `@alxia/create`
69
- was published with, as `^` ranges: `@alxia/core`, `@alxia/client`,
70
- `@alxia/react-router`. `bunx @alxia/create@<version>` picks an older set.
95
+ - **alxia's packages** — `@alxia/core`, `@alxia/client`,
96
+ `@alxia/react-router` — are moved to the newest version on the registry
97
+ within the ranges this release of `@alxia/create` was published with:
98
+ `^0.3.1` writes `^0.3.4` once 0.3.4 is out, never `^0.4.0`. Just after a
99
+ release, while the registry does not serve that version yet, the newest of
100
+ the same minor is written (`^0.3.0`, which takes 0.3.1 once it arrives),
101
+ and the output says so. `bunx @alxia/create@<version>` picks an older set.
71
102
  - **Everything else** — Zod, TypeScript, Vite, React, React Router, Tailwind
72
103
  — is moved to the newest version on the registry when the project is
73
104
  written, within the range alxia's packages accept: TypeScript within
@@ -96,6 +127,6 @@ the project it writes installs with `bun install`.
96
127
 
97
128
  ## Documentation
98
129
 
99
- - [Guide](https://github.com/softistx/alxia/blob/develop/packages/create/docs/guide.md): each template file by file, what the `react-router` template changes in React Router's, how versions are chosen, and running it in CI.
130
+ - [Guide](https://github.com/softistx/alxia/blob/develop/packages/create/docs/guide.md): each template file by file, what the `react-router` template adds to React Router's, each `Dockerfile`, how versions are chosen, and running it in CI.
100
131
  - [Troubleshooting](https://github.com/softistx/alxia/blob/develop/packages/create/docs/troubleshooting.md): each message the command prints, and what to do about it.
101
132
  - [Roadmap](https://github.com/softistx/alxia/blob/develop/packages/create/docs/roadmap.md): what is coming, and what is not planned.
package/dist/copy.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import type { Template } from './args';
2
+ import type { Manifest } from './registry';
3
+ import type { AlxiaPackage } from './versions';
4
+ /**
5
+ * Where the templates are, beside `src/` here and beside `dist/` published:
6
+ * this module is bundled into `dist/index.js`, as `versions.ts` reads
7
+ * `../package.json`.
8
+ */
9
+ export declare const TEMPLATES: string;
10
+ /**
11
+ * Files a template stores under another name, because a published tarball
12
+ * would not hold them: `bun publish`, as `npm publish`, leaves every
13
+ * `.gitignore` out, and Bun leaves `bunfig.toml` out too.
14
+ */
15
+ export declare const RENAMED: Readonly<Record<string, string>>;
16
+ /**
17
+ * `template`, from `root`: its manifest, named `name` with alxia's packages
18
+ * at `alxia`'s ranges, and every other file, by the path it is written at.
19
+ */
20
+ export declare function copyTemplate(template: Template, name: string, alxia: Record<AlxiaPackage, string>, root?: string): Promise<{
21
+ manifest: Manifest;
22
+ files: Record<string, Blob>;
23
+ }>;
24
+ //# sourceMappingURL=copy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.d.ts","sourceRoot":"","sources":["../src/copy.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;GAIG;AACH,eAAO,MAAM,SAAS,QAErB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGpD,CAAC;AAEF;;;GAGG;AACH,wBAAsB,YAAY,CACjC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,EACnC,IAAI,GAAE,MAAkB,GACtB,OAAO,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;CAAE,CAAC,CA4B9D"}
package/dist/index.js CHANGED
@@ -78,9 +78,6 @@ async function clean(target, existed) {
78
78
  }
79
79
  }
80
80
 
81
- // src/templates/react-router.ts
82
- import { join as join2 } from "node:path";
83
-
84
81
  // src/versions.ts
85
82
  import { dirname } from "node:path";
86
83
  var ALXIA_PACKAGES = [
@@ -94,12 +91,6 @@ var PEER_RANGES = {
94
91
  "react-router": "^8.0.0",
95
92
  vite: "^7.0.0 || ^8.0.0"
96
93
  };
97
- function highestMajor(range) {
98
- const majors = [...range.matchAll(/(?:^|[\s|^~>=])(\d+)/g)].map((match) => Number(match[1]));
99
- if (majors.length === 0)
100
- throw new Error(`no major in range ${range}`);
101
- return Math.max(...majors);
102
- }
103
94
  async function alxiaRanges(manifestUrl = new URL("../package.json", import.meta.url)) {
104
95
  const manifest = await Bun.file(manifestUrl).json();
105
96
  const declared = manifest.devDependencies ?? {};
@@ -120,84 +111,43 @@ async function workspaceRange(name, range, from) {
120
111
  return prefix === "*" ? version : `${prefix}${version}`;
121
112
  }
122
113
 
123
- // src/templates/react-router.ts
124
- function scaffoldCommand(dir) {
125
- return [
126
- `create-react-router@${highestMajor(PEER_RANGES["react-router"])}`,
127
- dir,
128
- "--yes",
129
- "--no-install",
130
- "--no-git-init",
131
- "--no-agent-skills",
132
- "--no-motion"
133
- ];
134
- }
114
+ // src/write.ts
115
+ import { join as join3 } from "node:path";
135
116
 
136
- class ScaffoldChanged extends Error {
137
- constructor(file, expected) {
138
- super(`create-react-router's ${file} is not what this @alxia/create expects: ${expected}. ` + "Nothing was kept. Try the newest @alxia/create, or add @alxia/react-router to a React Router app by hand, as its README shows.");
139
- this.name = "ScaffoldChanged";
140
- }
141
- }
142
- var BUNFIG = `[run]
143
- # The React Router CLI is a node script: this starts it on Bun, which
144
- # alxia's server needs, even where a node is installed.
145
- bun = true
146
- `;
147
- var IMPORT = `import { alxia } from "@alxia/react-router/vite";
148
- `;
149
- function addPlugin(config) {
150
- const calls = config.match(/\breactRouter\(\)/g) ?? [];
151
- if (!config.includes('from "@react-router/dev/vite"') || !/plugins:\s*\[[^\]]*\breactRouter\(\)[^\]]*\]/.test(config) || calls.length !== 1) {
152
- throw new ScaffoldChanged("vite.config.ts", 'reactRouter() from "@react-router/dev/vite", called once in plugins: [...]');
153
- }
154
- if (config.includes("@alxia/react-router/vite")) {
155
- throw new ScaffoldChanged("vite.config.ts", "it already imports @alxia/react-router/vite");
117
+ // src/copy.ts
118
+ import { join as join2 } from "node:path";
119
+ import { fileURLToPath } from "node:url";
120
+ var TEMPLATES2 = fileURLToPath(new URL("../templates", import.meta.url));
121
+ var RENAMED = {
122
+ gitignore: ".gitignore",
123
+ "_bunfig.toml": "bunfig.toml"
124
+ };
125
+ async function copyTemplate(template, name, alxia, root = TEMPLATES2) {
126
+ const dir = join2(root, template);
127
+ const stored = await Bun.file(join2(dir, "package.json")).json();
128
+ const manifest = { ...stored, name };
129
+ for (const field of ["dependencies", "devDependencies"]) {
130
+ const deps = stored[field];
131
+ if (deps === undefined)
132
+ continue;
133
+ manifest[field] = Object.fromEntries(Object.entries(deps).map(([pkg, range]) => [
134
+ pkg,
135
+ Object.hasOwn(alxia, pkg) ? alxia[pkg] : range
136
+ ]));
156
137
  }
157
- return IMPORT + config.replace(/\breactRouter\(\)/, "reactRouter(), alxia()");
158
- }
159
- function addAlxia(manifest, alxia) {
160
- const scripts = manifest["scripts"];
161
- if (scripts?.["dev"] !== "react-router dev" || scripts["build"] !== "react-router build" || scripts["start"] === undefined || manifest.dependencies?.["react-router"] === undefined) {
162
- throw new ScaffoldChanged("package.json", "react-router in its dependencies, and the scripts `dev: react-router dev`, `build: react-router build` and a `start`");
138
+ const left = JSON.stringify(manifest).match(/"([^"]+)":"workspace:/);
139
+ if (left) {
140
+ throw new Error(`the ${template} template names ${left[1]} at workspace:, which this @alxia/create has no version for`);
163
141
  }
164
- const dependencies = sorted({
165
- ...manifest.dependencies,
166
- "@alxia/core": alxia["@alxia/core"],
167
- "@alxia/react-router": alxia["@alxia/react-router"]
168
- });
169
- return {
170
- ...manifest,
171
- scripts: { ...scripts, start: "bun build/server/index.js" },
172
- dependencies
173
- };
174
- }
175
- function sorted(deps) {
176
- return Object.fromEntries(Object.entries(deps).sort(([a], [b]) => a < b ? -1 : 1));
177
- }
178
- async function alxiaLayer(dir, alxia) {
179
- const read = async (file) => {
180
- const handle = Bun.file(join2(dir, file));
181
- if (!await handle.exists()) {
182
- throw new ScaffoldChanged(file, "it is missing");
183
- }
184
- return handle.text();
185
- };
186
- const manifest = addAlxia(JSON.parse(await read("package.json")), alxia);
187
- const config = addPlugin(await read("vite.config.ts"));
188
- if (await Bun.file(join2(dir, "bunfig.toml")).exists()) {
189
- throw new ScaffoldChanged("bunfig.toml", "none, and there is one");
142
+ const files = {};
143
+ for await (const path of new Bun.Glob("**").scan({ cwd: dir, dot: true })) {
144
+ if (path === "package.json" || path.endsWith(".DS_Store"))
145
+ continue;
146
+ files[RENAMED[path] ?? path] = Bun.file(join2(dir, path));
190
147
  }
191
- return {
192
- manifest,
193
- files: { "vite.config.ts": config, "bunfig.toml": BUNFIG }
194
- };
148
+ return { manifest, files };
195
149
  }
196
150
 
197
- // src/write.ts
198
- import { mkdir } from "node:fs/promises";
199
- import { basename as basename2, dirname as dirname2, join as join3 } from "node:path";
200
-
201
151
  // src/registry.ts
202
152
  function registryUrl(env) {
203
153
  const url = env["BUN_CONFIG_REGISTRY"] || env["npm_config_registry"] || "https://registry.npmjs.org";
@@ -227,9 +177,19 @@ function choose(versions, range, latest) {
227
177
  return newestWithin(versions, range);
228
178
  return latest && versions.includes(latest) ? latest : newestWithin(versions, "*");
229
179
  }
230
- async function bumpDependencies(manifest, registry, keep = new Set) {
180
+ function sameMinor(range) {
181
+ const match = /(\d+)\.(\d+)\.\d+/.exec(range);
182
+ return match ? `~${match[1]}.${match[2]}.0` : undefined;
183
+ }
184
+ function newestOfMinor(versions, range) {
185
+ const minor = sameMinor(range);
186
+ const start = /\d+\.\d+\.\d+/.exec(range)?.[0];
187
+ const version = minor && newestWithin(versions, minor);
188
+ return version && start && Bun.semver.satisfies(start, `^${version}`) ? version : undefined;
189
+ }
190
+ async function bumpDependencies(manifest, registry, built = {}) {
231
191
  const fields = ["dependencies", "devDependencies"];
232
- const names = fields.flatMap((field) => Object.keys(manifest[field] ?? {}).filter((name) => !keep.has(name)));
192
+ const names = fields.flatMap((field) => Object.keys(manifest[field] ?? {}));
233
193
  const fetched = new Map(await Promise.all(names.map(async (name) => [
234
194
  name,
235
195
  await packument(name, registry).catch(() => {
@@ -240,6 +200,7 @@ async function bumpDependencies(manifest, registry, keep = new Set) {
240
200
  const held = [];
241
201
  const failed = [];
242
202
  const unmatched = [];
203
+ const behind = [];
243
204
  const chosen = new Map;
244
205
  const ordered = [...names].sort((a, b) => Number(b === "react-router") - Number(a === "react-router"));
245
206
  for (const name of ordered) {
@@ -254,10 +215,17 @@ async function bumpDependencies(manifest, registry, keep = new Set) {
254
215
  continue;
255
216
  }
256
217
  const versions = Object.keys(meta.versions);
257
- const range = allowedRange(name);
218
+ const pinned = built[name];
219
+ const range = pinned ?? allowedRange(name);
258
220
  const latest = meta["dist-tags"]?.["latest"];
259
221
  const follow = chosen.get("react-router");
260
- const version = name.startsWith("@react-router/") && follow && versions.includes(follow) ? follow : choose(versions, range, latest);
222
+ let version = name.startsWith("@react-router/") && follow && versions.includes(follow) ? follow : choose(versions, range, latest);
223
+ if (version === undefined && pinned !== undefined) {
224
+ version = newestOfMinor(versions, pinned);
225
+ if (version !== undefined) {
226
+ behind.push(`${name}: the registry has no release within ${range} yet; wrote ^${version}, the newest of ${sameMinor(pinned)}`);
227
+ }
228
+ }
261
229
  if (version === undefined) {
262
230
  unmatched.push(`${name}: no release within ${range ?? "any range"}; kept ${current}`);
263
231
  continue;
@@ -271,214 +239,26 @@ async function bumpDependencies(manifest, registry, keep = new Set) {
271
239
  moved.push(`${name} ${current} -> ${next}`);
272
240
  deps[name] = next;
273
241
  }
274
- return { moved, held, failed, unmatched };
275
- }
276
-
277
- // src/templates/api.ts
278
- function apiManifest(name, alxia) {
279
- return {
280
- name,
281
- private: true,
282
- type: "module",
283
- scripts: {
284
- dev: "bun --watch src/server.ts",
285
- build: "bun build src/server.ts --target=bun --outdir=dist",
286
- start: "bun dist/server.js",
287
- test: "bun test",
288
- typecheck: "tsc --noEmit"
289
- },
290
- dependencies: {
291
- "@alxia/core": alxia["@alxia/core"],
292
- zod: "^4.2.0"
293
- },
294
- devDependencies: {
295
- "@alxia/client": alxia["@alxia/client"],
296
- "@types/bun": "^1.4.2",
297
- typescript: "^6.0.3"
298
- }
299
- };
300
- }
301
- var APP = `import { alxia, defineHook } from '@alxia/core';
302
- import { z } from 'zod';
303
-
304
- const Todo = z.object({ id: z.number(), title: z.string(), done: z.boolean() });
305
- const NewTodo = z.object({ title: z.string().min(1) });
306
-
307
- /** Set API_KEY in the environment: this default is for development. */
308
- export const apiKey = Bun.env['API_KEY'] ?? 'dev-key';
309
-
310
- // A hook of the routes it is given to: it answers 401 without the key, and
311
- // that 401 joins the type of each, so the client reads it.
312
- const requireKey = defineHook(({ request, reply }) =>
313
- request.headers.get('x-api-key') === apiKey
314
- ? undefined
315
- : reply(401, { error: 'unauthorized' as const }),
316
- );
317
-
318
- const todos: z.infer<typeof Todo>[] = [];
319
-
320
- export const app = alxia()
321
- .decorate({ todos })
322
- .post(
323
- '/todos',
324
- [requireKey],
325
- { body: NewTodo, response: { 201: Todo } },
326
- ({ body, todos, reply }) => {
327
- const todo = { id: todos.length + 1, title: body.title, done: false };
328
- todos.push(todo);
329
- return reply.created(todo);
330
- },
331
- );
332
-
333
- export type App = typeof app;
334
- `;
335
- var SERVER = `import { app } from './app';
336
-
337
- const server = app.listen(Number(Bun.env['PORT'] ?? 3000));
338
- console.log(\`listening on \${server.url}\`);
339
- `;
340
- var SPEC = `import { expect, test } from 'bun:test';
341
- import { client } from '@alxia/client';
342
- import { apiKey, app } from './app';
343
-
344
- const json = { 'content-type': 'application/json', 'x-api-key': apiKey };
345
-
346
- test('creates a todo from JSON', async () => {
347
- const response = await app.request('/todos', {
348
- method: 'POST',
349
- headers: json,
350
- body: JSON.stringify({ title: 'Write a route' }),
351
- });
352
- expect(response.status).toBe(201);
353
- expect(await response.json()).toEqual({
354
- id: expect.any(Number),
355
- title: 'Write a route',
356
- done: false,
357
- });
358
- });
359
-
360
- test('refuses an empty title with a 400 naming it', async () => {
361
- const response = await app.request('/todos', {
362
- method: 'POST',
363
- headers: json,
364
- body: JSON.stringify({ title: '' }),
365
- });
366
- expect(response.status).toBe(400);
367
- expect((await response.json()).issues[0].path).toEqual(['title']);
368
- });
369
-
370
- test('the typed client reads each status the route answers', async () => {
371
- // Given the app itself, the client calls its fetch in process.
372
- const api = client(app, { headers: { 'x-api-key': apiKey } });
373
- const created = await api.post('/todos', { body: { title: 'Call it typed' } });
374
- if (created.status !== 201) throw new Error(\`got \${created.status}\`);
375
- expect(created.data.title).toBe('Call it typed'); // data is the Todo schema's type
376
-
377
- const anonymous = await client(app).post('/todos', { body: { title: 'No key' } });
378
- expect(anonymous.status).toBe(401);
379
- if (anonymous.status === 401) expect(anonymous.data.error).toBe('unauthorized');
380
- });
381
- `;
382
- var TSCONFIG = `{
383
- "compilerOptions": {
384
- "types": ["bun"],
385
- "lib": ["ESNext", "DOM"],
386
- "target": "ESNext",
387
- "module": "ESNext",
388
- "moduleDetection": "force",
389
- "moduleResolution": "bundler",
390
- "verbatimModuleSyntax": true,
391
- "noEmit": true,
392
- "skipLibCheck": true,
393
- "resolveJsonModule": true,
394
- "strict": true,
395
- "noImplicitOverride": true,
396
- "noImplicitReturns": true,
397
- "noFallthroughCasesInSwitch": true,
398
- "noUncheckedIndexedAccess": true,
399
- "exactOptionalPropertyTypes": true,
400
- "noPropertyAccessFromIndexSignature": true,
401
- "noUnusedLocals": true,
402
- "noUnusedParameters": true,
403
- "forceConsistentCasingInFileNames": true
404
- },
405
- "include": ["src"]
406
- }
407
- `;
408
- var GITIGNORE = `node_modules
409
- dist
410
- *.log
411
- .DS_Store
412
- .env
413
- .env.local
414
- `;
415
- var readme = (name) => `# ${name}
416
-
417
- An [alxia](https://github.com/softistx/alxia) app with
418
- [Zod](https://zod.dev), made with \`bun create @alxia\`.
419
-
420
- \`\`\`sh
421
- bun dev # http://localhost:3000, restarted on every change
422
- bun test # src/app.spec.ts: in process, and through the typed client
423
- bun run typecheck
424
- bun run build # dist/server.js, run with \`bun start\`
425
- \`\`\`
426
-
427
- \`\`\`sh
428
- curl -X POST localhost:3000/todos \\
429
- -H 'content-type: application/json' -H 'x-api-key: dev-key' \\
430
- -d '{"title":"Write a route"}'
431
- \`\`\`
432
-
433
- - \`src/app.ts\`: the app. \`POST /todos\` validates its body with Zod, and
434
- its own hook, \`requireKey\`, answers 401 without the \`x-api-key\` header.
435
- Set \`API_KEY\` in the environment outside development.
436
- - \`src/server.ts\`: listens on \`PORT\`, 3000 by default.
437
- - \`src/app.spec.ts\`: \`app.request()\` and \`@alxia/client\`, no port.
438
-
439
- Next: [Getting started](https://github.com/softistx/alxia/blob/develop/packages/core/docs/guide/getting-started.md)
440
- and the rest of [\`@alxia/core\`'s guide](https://github.com/softistx/alxia/tree/develop/packages/core/docs).
441
- `;
442
- function apiFiles(name) {
443
- return {
444
- "src/app.ts": APP,
445
- "src/server.ts": SERVER,
446
- "src/app.spec.ts": SPEC,
447
- "tsconfig.json": TSCONFIG,
448
- ".gitignore": GITIGNORE,
449
- "README.md": readme(name)
450
- };
242
+ return { moved, held, failed, unmatched, behind };
451
243
  }
452
244
 
453
245
  // src/write.ts
454
246
  async function write(target, template, alxia, io) {
455
- let manifest;
456
- let files;
457
- if (template === "api") {
458
- manifest = apiManifest(packageName(target), alxia);
459
- files = apiFiles(packageName(target));
460
- } else {
461
- await mkdir(dirname2(target), { recursive: true });
462
- const code = await io.run([process.execPath, "x", ...scaffoldCommand(basename2(target))], dirname2(target));
463
- if (code !== 0) {
464
- throw new Error(`create-react-router exited with ${code}.`);
465
- }
466
- ({ manifest, files } = await alxiaLayer(target, alxia));
467
- }
247
+ const { manifest, files } = await copyTemplate(template, packageName(target), alxia);
468
248
  io.out("Resolving the newest versions alxia accepts...");
469
- const bumped = await bumpDependencies(manifest, { url: registryUrl(io.env), ...io.fetch ? { fetch: io.fetch } : {} }, new Set(Object.keys(alxia)));
249
+ const bumped = await bumpDependencies(manifest, { url: registryUrl(io.env), ...io.fetch ? { fetch: io.fetch } : {} }, alxia);
470
250
  for (const line of bumped.moved)
471
251
  io.out(` ${line}`);
472
- for (const line of [...bumped.held, ...bumped.unmatched])
252
+ for (const line of [...bumped.held, ...bumped.unmatched, ...bumped.behind])
473
253
  io.out(` ${line}`);
474
254
  if (bumped.failed.length > 0) {
475
255
  io.err(`${NAME}: warning: the registry did not answer for ${bumped.failed.join(", ")}; ` + "kept the versions the template ships.");
476
256
  }
477
- files["package.json"] = `${JSON.stringify(manifest, null, 2)}
478
- `;
479
257
  for (const [file, content] of Object.entries(files)) {
480
258
  await Bun.write(join3(target, file), content);
481
259
  }
260
+ await Bun.write(join3(target, "package.json"), `${JSON.stringify(manifest, null, 2)}
261
+ `);
482
262
  return true;
483
263
  }
484
264
  // src/io.ts
@@ -553,7 +333,7 @@ ${USAGE}`);
553
333
  }) !== undefined;
554
334
  const made = await alxiaRanges().then((alxia) => write(target, template, alxia, io)).catch(async (error) => {
555
335
  await clean(target, existed);
556
- io.err(`${NAME}: ${error instanceof ScaffoldChanged ? "" : "failed: "}${error instanceof Error ? error.message : String(error)}`);
336
+ io.err(`${NAME}: failed: ${error instanceof Error ? error.message : String(error)}`);
557
337
  return false;
558
338
  });
559
339
  if (!made)
@@ -595,5 +375,5 @@ function askTemplate(io) {
595
375
  // src/index.ts
596
376
  process.exitCode = await main(process.argv.slice(2), process.cwd(), processIo());
597
377
 
598
- //# debugId=8B48AE430B9AACCF64756E2164756E21
378
+ //# debugId=DC169542838EEB9F64756E2164756E21
599
379
  //# sourceMappingURL=index.js.map