@alxia/create 0.1.3 → 0.1.5
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/README.md +53 -18
- package/dist/copy.d.ts +11 -3
- package/dist/copy.d.ts.map +1 -1
- package/dist/index.js +23 -5
- package/dist/index.js.map +4 -4
- package/dist/registry.d.ts +11 -1
- package/dist/registry.d.ts.map +1 -1
- package/docs/README.md +2 -2
- package/docs/guide.md +176 -39
- package/docs/roadmap.md +23 -0
- package/docs/troubleshooting.md +97 -0
- package/package.json +1 -1
- package/templates/api/.vscode/extensions.json +3 -0
- package/templates/api/.vscode/settings.json +8 -0
- package/templates/api/Dockerfile +4 -2
- package/templates/api/README.md +32 -2
- package/templates/api/_biome.json +32 -0
- package/templates/api/package.json +25 -19
- package/templates/api/src/app.spec.ts +38 -38
- package/templates/api/src/app.ts +17 -17
- package/templates/api/src/server.ts +16 -2
- package/templates/api/tsconfig.json +23 -23
- package/templates/react-router/.vscode/extensions.json +3 -0
- package/templates/react-router/.vscode/settings.json +8 -0
- package/templates/react-router/Dockerfile +4 -2
- package/templates/react-router/README.md +27 -0
- package/templates/react-router/_biome.json +49 -0
- package/templates/react-router/app/app.css +3 -2
- package/templates/react-router/app/routes/home.tsx +1 -1
- package/templates/react-router/app/routes.ts +1 -1
- package/templates/react-router/package.json +7 -1
package/docs/guide.md
CHANGED
|
@@ -6,6 +6,7 @@ chooses the versions it writes.
|
|
|
6
6
|
- [Running it](#running-it)
|
|
7
7
|
- [The `api` template](#the-api-template)
|
|
8
8
|
- [The `react-router` template](#the-react-router-template)
|
|
9
|
+
- [Lint and format](#lint-and-format)
|
|
9
10
|
- [Docker](#docker)
|
|
10
11
|
- [Versions](#versions)
|
|
11
12
|
- [In a script or CI](#in-a-script-or-ci)
|
|
@@ -45,7 +46,10 @@ What the command line gives is not asked: `bun create @alxia my-app
|
|
|
45
46
|
--template react-router` asks nothing. The directory must be empty or not
|
|
46
47
|
exist yet; `.` writes into the current one, when it is empty, and the next
|
|
47
48
|
steps then start at `bun dev`. The package name in `package.json` is the
|
|
48
|
-
directory's name, lowercased, with `-` for anything npm refuses
|
|
49
|
+
directory's name, lowercased, with `-` for anything npm refuses, and the
|
|
50
|
+
same name replaces the template's own (`my-api`, `my-app`) in its other
|
|
51
|
+
files, as the README's `docker build -t` and `docker run`: `bun create
|
|
52
|
+
@alxia "Mon Super Projet"` writes `mon-super-projet` in both.
|
|
49
53
|
|
|
50
54
|
`bun create @alxia` is `bunx @alxia/create`: Bun maps `bun create @scope`
|
|
51
55
|
to the package `@scope/create`, and npm maps `npm create @scope` the same
|
|
@@ -53,12 +57,15 @@ way. Any of the three runs the same bin, on Bun.
|
|
|
53
57
|
|
|
54
58
|
Both templates are files shipped in this package, under
|
|
55
59
|
`templates/api/` and `templates/react-router/`, and copied as they are:
|
|
56
|
-
nothing is downloaded but the dependencies
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
name,
|
|
60
|
-
back on the copy: `gitignore` is
|
|
61
|
-
as `bunfig.toml
|
|
60
|
+
nothing is downloaded but the dependencies. `package.json` is written
|
|
61
|
+
again, with the directory's name, alxia's versions and the newest of the
|
|
62
|
+
others ([Versions](#versions)); in every other text file, the template's
|
|
63
|
+
own name, as a whole word, becomes the project's. Three files are stored
|
|
64
|
+
under another name and take theirs back on the copy: `gitignore` is
|
|
65
|
+
written as `.gitignore` and `_bunfig.toml` as `bunfig.toml`, since `bun
|
|
66
|
+
publish` leaves those out of a tarball, and `_biome.json` as `biome.json`,
|
|
67
|
+
since alxia's own Biome refuses a second root configuration inside its
|
|
68
|
+
repository ([Lint and format](#lint-and-format)).
|
|
62
69
|
|
|
63
70
|
## The `api` template
|
|
64
71
|
|
|
@@ -67,10 +74,12 @@ my-api/
|
|
|
67
74
|
├── src/
|
|
68
75
|
│ ├── app.ts the app, and its type
|
|
69
76
|
│ ├── app.spec.ts bun test: app.request() and @alxia/client
|
|
70
|
-
│ └── server.ts app.listen(PORT)
|
|
77
|
+
│ └── server.ts app.listen(PORT), stopped on SIGTERM
|
|
71
78
|
├── package.json
|
|
72
79
|
├── tsconfig.json
|
|
73
|
-
├──
|
|
80
|
+
├── biome.json Biome: lint, format, imports sorted
|
|
81
|
+
├── .vscode/ Biome's extension recommended, format on save
|
|
82
|
+
├── Dockerfile bun run build, then dist/ alone, on oven/bun:1-alpine
|
|
74
83
|
├── .dockerignore
|
|
75
84
|
├── .env.example PORT and API_KEY, for a .env Bun loads
|
|
76
85
|
├── .gitignore
|
|
@@ -81,35 +90,35 @@ my-api/
|
|
|
81
90
|
body validated by a Zod schema, a declared reply, and a hook of its own.
|
|
82
91
|
|
|
83
92
|
```ts
|
|
84
|
-
import { alxia, defineHook } from
|
|
85
|
-
import { z } from
|
|
93
|
+
import { alxia, defineHook } from "@alxia/core";
|
|
94
|
+
import { z } from "zod";
|
|
86
95
|
|
|
87
96
|
const Todo = z.object({ id: z.number(), title: z.string(), done: z.boolean() });
|
|
88
97
|
const NewTodo = z.object({ title: z.string().min(1) });
|
|
89
98
|
|
|
90
99
|
/** Set API_KEY in the environment: this default is for development. */
|
|
91
|
-
export const apiKey = Bun.env[
|
|
100
|
+
export const apiKey = Bun.env["API_KEY"] ?? "dev-key";
|
|
92
101
|
|
|
93
102
|
const requireKey = defineHook(({ request, reply }) =>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
request.headers.get("x-api-key") === apiKey
|
|
104
|
+
? undefined
|
|
105
|
+
: reply(401, { error: "unauthorized" as const }),
|
|
97
106
|
);
|
|
98
107
|
|
|
99
108
|
const todos: z.infer<typeof Todo>[] = [];
|
|
100
109
|
|
|
101
110
|
export const app = alxia()
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
.decorate({ todos })
|
|
112
|
+
.post(
|
|
113
|
+
"/todos",
|
|
114
|
+
[requireKey],
|
|
115
|
+
{ body: NewTodo, response: { 201: Todo } },
|
|
116
|
+
({ body, todos, reply }) => {
|
|
117
|
+
const todo = { id: todos.length + 1, title: body.title, done: false };
|
|
118
|
+
todos.push(todo);
|
|
119
|
+
return reply.created(todo);
|
|
120
|
+
},
|
|
121
|
+
);
|
|
113
122
|
|
|
114
123
|
export type App = typeof app;
|
|
115
124
|
```
|
|
@@ -126,10 +135,10 @@ JSON body, then `@alxia/client` given the app itself, whose result is typed
|
|
|
126
135
|
by status:
|
|
127
136
|
|
|
128
137
|
```ts
|
|
129
|
-
const api = client(app, { headers: {
|
|
130
|
-
const created = await api.post(
|
|
138
|
+
const api = client(app, { headers: { "x-api-key": apiKey } });
|
|
139
|
+
const created = await api.post("/todos", { body: { title: "Call it typed" } });
|
|
131
140
|
if (created.status !== 201) throw new Error(`got ${created.status}`);
|
|
132
|
-
expect(created.data.title).toBe(
|
|
141
|
+
expect(created.data.title).toBe("Call it typed"); // data is the Todo schema's type
|
|
133
142
|
```
|
|
134
143
|
|
|
135
144
|
The scripts:
|
|
@@ -141,6 +150,10 @@ The scripts:
|
|
|
141
150
|
| `bun run typecheck` | `tsc --noEmit` |
|
|
142
151
|
| `bun run build` | `bun build src/server.ts --target=bun --outdir=dist --minify --sourcemap=linked`: one file, its dependencies bundled |
|
|
143
152
|
| `bun start` | `bun dist/server.js`: the build, after `bun run build` |
|
|
153
|
+
| `bun run check` | `biome check --write`: lint, format, sort imports, fixing what it can |
|
|
154
|
+
| `bun run lint`, `bun run format` | `biome lint`, `biome format --write` |
|
|
155
|
+
| `bun run check:ci` | `biome ci`: read-only, for CI |
|
|
156
|
+
| `bun run verify` | `check:ci`, `typecheck`, then `test` |
|
|
144
157
|
|
|
145
158
|
`start` runs what `build` wrote, as production and the image do:
|
|
146
159
|
|
|
@@ -162,9 +175,10 @@ keeps out of git and `.dockerignore` out of the image.
|
|
|
162
175
|
`tsconfig.json` holds the settings alxia's own packages are checked under:
|
|
163
176
|
`strict`, and past it `exactOptionalPropertyTypes`,
|
|
164
177
|
`noUncheckedIndexedAccess`, `noPropertyAccessFromIndexSignature`,
|
|
165
|
-
`noUnusedLocals` and the rest. Hence `Bun.env[
|
|
166
|
-
`Bun.env.API_KEY
|
|
167
|
-
|
|
178
|
+
`noUnusedLocals` and the rest. Hence `Bun.env["API_KEY"]` and not
|
|
179
|
+
`Bun.env.API_KEY`, and Biome's `useLiteralKeys`, which would ask for the
|
|
180
|
+
second, is off in `biome.json`. Loosen what you would rather not keep:
|
|
181
|
+
alxia's types compile under each one, and under none.
|
|
168
182
|
|
|
169
183
|
## The `react-router` template
|
|
170
184
|
|
|
@@ -195,6 +209,19 @@ before `bun install`. The change, against React Router's files:
|
|
|
195
209
|
+ plugins: [tailwindcss(), reactRouter(), alxia()],
|
|
196
210
|
```
|
|
197
211
|
|
|
212
|
+
```diff
|
|
213
|
+
// package.json
|
|
214
|
+
"scripts": {
|
|
215
|
+
+ "lint": "biome lint",
|
|
216
|
+
+ "format": "biome format --write",
|
|
217
|
+
+ "check": "biome check --write",
|
|
218
|
+
+ "check:ci": "biome ci",
|
|
219
|
+
+ "verify": "bun run check:ci && bun run typecheck && bun run build"
|
|
220
|
+
},
|
|
221
|
+
"devDependencies": {
|
|
222
|
+
+ "@biomejs/biome": "2.5.15",
|
|
223
|
+
```
|
|
224
|
+
|
|
198
225
|
```toml
|
|
199
226
|
# bunfig.toml, new
|
|
200
227
|
[run]
|
|
@@ -204,7 +231,12 @@ bun = true
|
|
|
204
231
|
```
|
|
205
232
|
|
|
206
233
|
The `Dockerfile` is alxia's, in place of React Router's, which builds and
|
|
207
|
-
runs on Node ([Docker](#docker)).
|
|
234
|
+
runs on Node ([Docker](#docker)). `biome.json` and `.vscode/` are new,
|
|
235
|
+
and the README gains a Lint and format section
|
|
236
|
+
([Lint and format](#lint-and-format)); Biome formatted the scaffold once,
|
|
237
|
+
which changed three of its files: `app/app.css`'s font list wraps
|
|
238
|
+
differently, and `app/routes.ts` and `app/routes/home.tsx` have their
|
|
239
|
+
imports sorted. Every other file is React Router's:
|
|
208
240
|
`app/`, `public/`, `tsconfig.json`, `react-router.config.ts`, its
|
|
209
241
|
`README.md` (with Bun's commands where it wrote npm's: `bun install`,
|
|
210
242
|
`bun dev`, `bun run build`), `.gitignore` and `.dockerignore`. `package.json` takes the directory's name, and its
|
|
@@ -228,17 +260,119 @@ bunx alxia-react-router reveal
|
|
|
228
260
|
The [`@alxia/react-router` guide](https://github.com/softistx/alxia/blob/develop/packages/react-router/docs/guide.md)
|
|
229
261
|
goes from there.
|
|
230
262
|
|
|
263
|
+
## Lint and format
|
|
264
|
+
|
|
265
|
+
Both projects lint and format with [Biome](https://biomejs.dev), in one
|
|
266
|
+
style whichever template wrote them. Their `biome.json` is a root
|
|
267
|
+
configuration of its own, extending nothing:
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
{
|
|
271
|
+
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
|
272
|
+
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
|
|
273
|
+
"files": { "includes": ["**", "!!**/build", "!!**/.react-router"] },
|
|
274
|
+
"formatter": { "enabled": true, "indentStyle": "space" },
|
|
275
|
+
"css": { "parser": { "tailwindDirectives": true } },
|
|
276
|
+
"linter": {
|
|
277
|
+
"enabled": true,
|
|
278
|
+
"rules": {
|
|
279
|
+
"preset": "recommended",
|
|
280
|
+
"correctness": { "noEmptyPattern": "off" }
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
"assist": {
|
|
284
|
+
"enabled": true,
|
|
285
|
+
"actions": { "source": { "organizeImports": "on" } }
|
|
286
|
+
},
|
|
287
|
+
"overrides": [
|
|
288
|
+
{
|
|
289
|
+
"includes": ["**/app/welcome/**"],
|
|
290
|
+
"linter": { "rules": { "a11y": { "noSvgWithoutTitle": "off" } } }
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
That is the `react-router` project's, written here compact. The `api`
|
|
297
|
+
project's skips `dist/` instead of `build/` and `.react-router/`, has no
|
|
298
|
+
CSS settings and no overrides, and turns `complexity.useLiteralKeys` off
|
|
299
|
+
in place of `noEmptyPattern` ([the `api` template](#the-api-template)).
|
|
300
|
+
|
|
301
|
+
- **The style is Biome's default but for spaces**: two spaces, double
|
|
302
|
+
quotes, 80 columns. React Router's scaffold is written so, and so is
|
|
303
|
+
the `package.json` the command writes: formatting the scaffold once
|
|
304
|
+
changed three of its files, where tabs would change every line. The
|
|
305
|
+
`api` project takes the same style, so the two read alike.
|
|
306
|
+
- **`$schema` is the installed Biome's own schema**, so an editor checks
|
|
307
|
+
the file against the version `bun install` put in `node_modules`,
|
|
308
|
+
whichever the command wrote.
|
|
309
|
+
- **What is generated is skipped.** `files.includes` leaves `dist/`, or
|
|
310
|
+
`build/` and `.react-router/`, out, and `vcs.useIgnoreFile` every path
|
|
311
|
+
`.gitignore` names, the project in a git repository or not (with no
|
|
312
|
+
`.gitignore` and no git repository, Biome refuses to run:
|
|
313
|
+
[troubleshooting](troubleshooting.md#-biome-couldnt-find-an-ignore-file-in-the-following-folder-)).
|
|
314
|
+
- **Two rules are off in the `react-router` project, for the scaffold's
|
|
315
|
+
own code.** `noEmptyPattern`: `meta({}: Route.MetaArgs)` is React
|
|
316
|
+
Router's idiom for a route module's function that reads none of its
|
|
317
|
+
arguments. `noSvgWithoutTitle`, under `app/welcome/` alone: the welcome
|
|
318
|
+
page's logos, which a project replaces.
|
|
319
|
+
- **`css.parser.tailwindDirectives`** lets Biome read Tailwind v4's
|
|
320
|
+
`@import "tailwindcss"` and `@theme` in `app/app.css`.
|
|
321
|
+
|
|
322
|
+
`@biomejs/biome` is a devDependency pinned exactly, as Biome recommends,
|
|
323
|
+
since a release may format differently. The command moves it to the
|
|
324
|
+
newest patch of the same minor and keeps it exact: a minor may add a
|
|
325
|
+
recommended rule the template was not checked against
|
|
326
|
+
([Versions](#versions)). To move it later:
|
|
327
|
+
|
|
328
|
+
```sh
|
|
329
|
+
bun add --dev --exact @biomejs/biome@latest
|
|
330
|
+
bunx biome migrate --write
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
| script | runs |
|
|
334
|
+
| --- | --- |
|
|
335
|
+
| `bun run check` | `biome check --write`: lint, format and sort imports, fixing what it can |
|
|
336
|
+
| `bun run lint` | `biome lint` |
|
|
337
|
+
| `bun run format` | `biome format --write` |
|
|
338
|
+
| `bun run check:ci` | `biome ci`: changes nothing, and fails on any error |
|
|
339
|
+
| `bun run verify` | `check:ci`, `typecheck`, then `test` (`api`) or `build` (`react-router`) |
|
|
340
|
+
|
|
341
|
+
The read-only one is `check:ci`, not `ci`: `bun ci` is Bun's
|
|
342
|
+
`bun install --frozen-lockfile`, and a script named `ci` would only run
|
|
343
|
+
as `bun run ci`. A CI job runs:
|
|
344
|
+
|
|
345
|
+
```sh
|
|
346
|
+
bun install --frozen-lockfile
|
|
347
|
+
bun run verify
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
`.vscode/extensions.json` recommends Biome's extension, `biomejs.biome`,
|
|
351
|
+
and `.vscode/settings.json` makes it the default formatter, formatting on
|
|
352
|
+
save. Another editor reads `biome.json` through Biome's own extension for
|
|
353
|
+
it.
|
|
354
|
+
|
|
231
355
|
## Docker
|
|
232
356
|
|
|
233
|
-
Each project's `Dockerfile`
|
|
234
|
-
image,
|
|
357
|
+
Each project's `Dockerfile` builds it on Bun's official `oven/bun:1`
|
|
358
|
+
image, Debian's, and runs it on `oven/bun:1-alpine`, as that image's
|
|
359
|
+
non-root `bun` user (uid 1000). The installs are
|
|
235
360
|
`--frozen-lockfile`, from the `bun.lock` the command's `bun install`
|
|
236
361
|
wrote: commit it.
|
|
237
362
|
|
|
238
363
|
Each one builds in a stage of its own, and the image holds the build
|
|
239
364
|
output alone: no `node_modules`, no sources. The dependencies are inside
|
|
240
365
|
the bundle, so the image is the base image and a few hundred kilobytes to
|
|
241
|
-
a few megabytes
|
|
366
|
+
a few megabytes: about 130 MB, where the same build on `oven/bun:1` is
|
|
367
|
+
about 345 MB.
|
|
368
|
+
|
|
369
|
+
The build stages stay on Debian: the tools a build runs, Vite's,
|
|
370
|
+
Tailwind's and any package's install script, are tried on glibc first,
|
|
371
|
+
and Alpine saves nothing there, since the stage is not shipped. What is
|
|
372
|
+
shipped is JavaScript, which Bun runs the same on musl. A native addon is
|
|
373
|
+
the exception: one built for glibc alone cannot load on Alpine, and the
|
|
374
|
+
final stage goes back to `oven/bun:1`
|
|
375
|
+
([troubleshooting](troubleshooting.md#error--is-linked-against-glibc-dt_needed-libmso6-but-this-bun-build-uses-musl)). A dependency that cannot be bundled is kept external and
|
|
242
376
|
copied in: see
|
|
243
377
|
[troubleshooting](troubleshooting.md#error-cannot-find-package--from-appdistserverjs).
|
|
244
378
|
|
|
@@ -250,7 +384,8 @@ Two stages: every dependency, installed with
|
|
|
250
384
|
running `bun --no-install dist/server.js`, `start`'s command with
|
|
251
385
|
Bun's `--no-install` (not create-alxia's option of the same name), so that a package missing from the bundle fails at
|
|
252
386
|
startup rather than being fetched from npm, written out so that Bun
|
|
253
|
-
is the container's process.
|
|
387
|
+
is the container's process. `src/server.ts` stops the app on `SIGTERM`:
|
|
388
|
+
as process 1, Bun would otherwise ignore it, and `docker stop` would wait.
|
|
254
389
|
|
|
255
390
|
```dockerfile
|
|
256
391
|
FROM oven/bun:1 AS build
|
|
@@ -260,7 +395,7 @@ RUN bun install --frozen-lockfile
|
|
|
260
395
|
COPY . .
|
|
261
396
|
RUN bun run build
|
|
262
397
|
|
|
263
|
-
FROM oven/bun:1
|
|
398
|
+
FROM oven/bun:1-alpine
|
|
264
399
|
WORKDIR /app
|
|
265
400
|
ENV NODE_ENV=production
|
|
266
401
|
COPY --from=build /app/dist ./dist
|
|
@@ -308,7 +443,8 @@ are in
|
|
|
308
443
|
A template ships the versions it was generated with, and React Router's
|
|
309
444
|
lags behind its own releases. So before installing, the command
|
|
310
445
|
asks the registry for every dependency's versions, and writes `^` the
|
|
311
|
-
newest one alxia accepts
|
|
446
|
+
newest one alxia accepts, or the version alone for a dependency the
|
|
447
|
+
template pins exactly:
|
|
312
448
|
|
|
313
449
|
| dependency | moved to the newest within |
|
|
314
450
|
| --- | --- |
|
|
@@ -317,6 +453,7 @@ newest one alxia accepts:
|
|
|
317
453
|
| `zod` | `^4.2.0`, `@alxia/zod`'s |
|
|
318
454
|
| `vite` | `^7.0.0 \|\| ^8.0.0`, `@alxia/react-router`'s |
|
|
319
455
|
| `react-router`, `@react-router/*` | `^8.0.0`, `@alxia/react-router`'s; the `@react-router/*` packages take `react-router`'s version, which `@react-router/node` pins exactly |
|
|
456
|
+
| `@biomejs/biome` | its own minor, from the exact version the template pins (`~2.5.15`): written exactly, `2.5.16`, never `^` |
|
|
320
457
|
| anything else: `react`, `isbot`, Tailwind, `@types/*` | no alxia range: npm's `latest` |
|
|
321
458
|
|
|
322
459
|
The command prints each move, and each newer major it left out:
|
package/docs/roadmap.md
CHANGED
|
@@ -30,6 +30,26 @@ Nothing scheduled yet.
|
|
|
30
30
|
|
|
31
31
|
### Next release
|
|
32
32
|
|
|
33
|
+
- **Both projects lint and format with Biome.** Each has a `biome.json`
|
|
34
|
+
of its own (recommended rules, spaces and double quotes, imports
|
|
35
|
+
sorted, the build output skipped), `@biomejs/biome` pinned exactly,
|
|
36
|
+
the scripts `lint`, `format`, `check`, `check:ci` and `verify`, and
|
|
37
|
+
`.vscode/` recommending Biome's extension. A new project passes
|
|
38
|
+
`bun run check:ci` with no finding.
|
|
39
|
+
- **The project's name in its README.** The name given to the command,
|
|
40
|
+
normalised, replaces the template's own, as a whole word, in every text
|
|
41
|
+
file, as the README's
|
|
42
|
+
`docker build -t` and `docker run`.
|
|
43
|
+
|
|
44
|
+
### 0.1.4
|
|
45
|
+
|
|
46
|
+
- **The images run on Alpine.** Every `Dockerfile`'s final stage is
|
|
47
|
+
`oven/bun:1-alpine`, the build stages staying on `oven/bun:1`: each
|
|
48
|
+
image is about 130 MB, where it was about 345 MB. The `api` project
|
|
49
|
+
stops on `SIGTERM`, so `docker stop` no longer waits.
|
|
50
|
+
|
|
51
|
+
### 0.1.3
|
|
52
|
+
|
|
33
53
|
- **Every `Dockerfile` builds, and the image holds the build alone.** The
|
|
34
54
|
`api` template's builds `dist/server.js`, bundled, minified and source
|
|
35
55
|
mapped, and runs it with no `node_modules` and no `src/`; `start` runs
|
|
@@ -37,6 +57,9 @@ Nothing scheduled yet.
|
|
|
37
57
|
copies `build/` alone, which `@alxia/react-router`'s plugin now bundles
|
|
38
58
|
whole. Each image is about 50 MB (api) and 150 MB (react-router)
|
|
39
59
|
smaller.
|
|
60
|
+
|
|
61
|
+
### 0.1.2
|
|
62
|
+
|
|
40
63
|
- **The `api` template is files, copied, with a `Dockerfile`.** It ships
|
|
41
64
|
under `templates/api/` and is copied as `react-router`'s is. New in it:
|
|
42
65
|
a `Dockerfile` on `oven/bun:1`, `.dockerignore` and `.env.example`.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -38,8 +38,15 @@ nothing — the symptom.
|
|
|
38
38
|
- [`error: lockfile had changes, but lockfile is frozen`](#error-lockfile-had-changes-but-lockfile-is-frozen)
|
|
39
39
|
- [`error: Module not found "dist/server.js"`](#error-module-not-found-distserverjs)
|
|
40
40
|
- [`error: Cannot find package '…' from '/app/dist/server.js'`](#error-cannot-find-package--from-appdistserverjs)
|
|
41
|
+
- [`error: … is linked against glibc (DT_NEEDED libm.so.6), but this Bun build uses musl.`](#error--is-linked-against-glibc-dt_needed-libmso6-but-this-bun-build-uses-musl)
|
|
41
42
|
- [The project's `@alxia/*` are older than npm's latest](#the-projects-alxia-are-older-than-npms-latest)
|
|
42
43
|
|
|
44
|
+
**Biome**
|
|
45
|
+
|
|
46
|
+
- [`bun ci` installs, and checks nothing](#bun-ci-installs-and-checks-nothing)
|
|
47
|
+
- [`× Found a nested root configuration, but there's already a root configuration.`](#-found-a-nested-root-configuration-but-theres-already-a-root-configuration)
|
|
48
|
+
- [`× Biome couldn't find an ignore file in the following folder: …`](#-biome-couldnt-find-an-ignore-file-in-the-following-folder-)
|
|
49
|
+
|
|
43
50
|
## Before it runs
|
|
44
51
|
|
|
45
52
|
### `error: GET https://registry.npmjs.org/@alxia%2fcreate - 404`
|
|
@@ -325,6 +332,35 @@ COPY --from=production-dependencies /app/node_modules ./node_modules
|
|
|
325
332
|
the bundle still imports: Node's and Bun's modules, and the packages left
|
|
326
333
|
external.
|
|
327
334
|
|
|
335
|
+
### `error: … is linked against glibc (DT_NEEDED libm.so.6), but this Bun build uses musl.`
|
|
336
|
+
|
|
337
|
+
The container stops at startup, or at the first request that loads the
|
|
338
|
+
package, with the path of a `.node` file in `node_modules` and
|
|
339
|
+
`code: "ERR_DLOPEN_FAILED"`. The library named after `DT_NEEDED` is
|
|
340
|
+
whichever one the addon links first: `libc.so.6`, `libstdc++.so.6` or
|
|
341
|
+
another; the fix is the same.
|
|
342
|
+
|
|
343
|
+
**When:** a package is kept external and installed beside the build, as
|
|
344
|
+
the previous entry says, and it loads a native addon built for glibc
|
|
345
|
+
alone.
|
|
346
|
+
|
|
347
|
+
**Why:** the final stage is `oven/bun:1-alpine`, whose C library is
|
|
348
|
+
musl. The bundle is JavaScript, which runs the same there, but a `.node`
|
|
349
|
+
file is compiled against one C library, and one compiled for glibc
|
|
350
|
+
cannot load on musl, even with `gcompat`. A package that publishes a
|
|
351
|
+
musl build too, as `sharp` does, works: `bun install`, `--production` too,
|
|
352
|
+
puts both variants in `node_modules`, and the package picks musl's.
|
|
353
|
+
|
|
354
|
+
**Fix:** run the final stage on Debian's image, `oven/bun:1`, which
|
|
355
|
+
holds glibc. The build stages stay as they are:
|
|
356
|
+
|
|
357
|
+
```dockerfile
|
|
358
|
+
# The final stage.
|
|
359
|
+
FROM oven/bun:1
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
The image is about 200 MB larger.
|
|
363
|
+
|
|
328
364
|
### The project's `@alxia/*` are older than npm's latest
|
|
329
365
|
|
|
330
366
|
**Symptom:** a fresh project declares `@alxia/core` at `^0.3.4` while npm
|
|
@@ -342,3 +378,64 @@ an existing project
|
|
|
342
378
|
`bun add @alxia/core@latest @alxia/react-router@latest` (`react-router`), reading
|
|
343
379
|
[`@alxia/core`'s upgrading page](https://github.com/softistx/alxia/blob/develop/packages/core/docs/upgrading.md)
|
|
344
380
|
for what a minor changed.
|
|
381
|
+
|
|
382
|
+
## Biome
|
|
383
|
+
|
|
384
|
+
### `bun ci` installs, and checks nothing
|
|
385
|
+
|
|
386
|
+
**Symptom:** `bun ci` in a project prints an install, `bun install
|
|
387
|
+
v1.4.2`, and no Biome output; a CI step meant to lint passes whatever the
|
|
388
|
+
code.
|
|
389
|
+
|
|
390
|
+
**Why:** `bun ci` is Bun's own command, `bun install --frozen-lockfile`,
|
|
391
|
+
and a command Bun knows wins over a script. The projects name their
|
|
392
|
+
read-only check `check:ci` for that reason
|
|
393
|
+
([Lint and format](guide.md#lint-and-format)).
|
|
394
|
+
|
|
395
|
+
**Fix:** run the script by `bun run`:
|
|
396
|
+
|
|
397
|
+
```sh
|
|
398
|
+
bun run check:ci # biome ci alone
|
|
399
|
+
bun run verify # biome ci, typecheck, then test or build
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### `× Found a nested root configuration, but there's already a root configuration.`
|
|
403
|
+
|
|
404
|
+
**When:** Biome runs from a folder above the project that has its own
|
|
405
|
+
`biome.json`: the project was created inside a monorepo, and `biome ci`
|
|
406
|
+
runs from the monorepo's root.
|
|
407
|
+
|
|
408
|
+
**Why:** the project's `biome.json` is a root configuration, so that it
|
|
409
|
+
works on its own. Biome takes one root per run; a configuration below it
|
|
410
|
+
must say it is nested.
|
|
411
|
+
|
|
412
|
+
**Fix:** to keep the monorepo's settings and add the project's, make the
|
|
413
|
+
project's nested, at the top of its `biome.json`:
|
|
414
|
+
|
|
415
|
+
```json
|
|
416
|
+
{
|
|
417
|
+
"root": false,
|
|
418
|
+
"extends": "//",
|
|
419
|
+
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json"
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
and keep the rest of the file. `"extends": "//"` takes the root's
|
|
424
|
+
settings, which the project's own override. To use the monorepo's
|
|
425
|
+
settings alone, delete the project's `biome.json` and its
|
|
426
|
+
`@biomejs/biome`.
|
|
427
|
+
|
|
428
|
+
### `× Biome couldn't find an ignore file in the following folder: …`
|
|
429
|
+
|
|
430
|
+
**When:** `bun run check:ci`, `check`, `lint` or `format` in a project
|
|
431
|
+
with no `.gitignore`, deleted or renamed, and outside any git repository.
|
|
432
|
+
Inside one, Biome reads git's ignore rules and runs.
|
|
433
|
+
|
|
434
|
+
**Why:** `biome.json` sets `vcs.useIgnoreFile`, which skips what
|
|
435
|
+
`.gitignore` names. Inside a git repository Biome falls back on git's
|
|
436
|
+
own ignore rules; with no `.gitignore` and no repository, it refuses to
|
|
437
|
+
run rather than skip nothing.
|
|
438
|
+
|
|
439
|
+
**Fix:** put a `.gitignore` back, even an empty one, or set
|
|
440
|
+
`"useIgnoreFile": false` in `biome.json`'s `vcs`: `files.includes` still
|
|
441
|
+
skips the build output.
|
package/package.json
CHANGED
package/templates/api/Dockerfile
CHANGED
|
@@ -14,8 +14,10 @@ RUN bun run build
|
|
|
14
14
|
# command, written out so that Bun is the container's process, and
|
|
15
15
|
# --no-install: with no node_modules, Bun would otherwise fetch a package
|
|
16
16
|
# the build left out from the registry at startup, where this fails. The
|
|
17
|
-
# server listens on PORT (3000); set API_KEY.
|
|
18
|
-
|
|
17
|
+
# server listens on PORT (3000); set API_KEY. On Alpine, about 200 MB
|
|
18
|
+
# lighter: the bundle is JavaScript, which runs the same on musl. A native
|
|
19
|
+
# addon built for glibc alone needs oven/bun:1 here.
|
|
20
|
+
FROM oven/bun:1-alpine
|
|
19
21
|
WORKDIR /app
|
|
20
22
|
ENV NODE_ENV=production
|
|
21
23
|
COPY --from=build /app/dist ./dist
|
package/templates/api/README.md
CHANGED
|
@@ -7,6 +7,7 @@ An [alxia](https://github.com/softistx/alxia) app with
|
|
|
7
7
|
its own hook, `requireKey`, answers 401 without the `x-api-key` header.
|
|
8
8
|
- `src/server.ts`: listens on `PORT`, 3000 by default.
|
|
9
9
|
- `src/app.spec.ts`: `app.request()` and `@alxia/client`, no port.
|
|
10
|
+
- `biome.json`: Biome's lint and format settings ([Lint and format](#lint-and-format)).
|
|
10
11
|
|
|
11
12
|
## Environment
|
|
12
13
|
|
|
@@ -37,6 +38,32 @@ bun test # src/app.spec.ts: in process, and through the typed client
|
|
|
37
38
|
bun run typecheck
|
|
38
39
|
```
|
|
39
40
|
|
|
41
|
+
## Lint and format
|
|
42
|
+
|
|
43
|
+
[Biome](https://biomejs.dev) lints and formats the project, as `biome.json`
|
|
44
|
+
sets it: Biome's recommended rules, spaces, double quotes, imports
|
|
45
|
+
sorted. What the build writes, `dist/`, is skipped.
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
bun run check # lint, format and sort imports, fixing what it can
|
|
49
|
+
bun run lint # lint only
|
|
50
|
+
bun run format # format only, in place
|
|
51
|
+
bun run check:ci # what CI runs: changes nothing, fails on an error
|
|
52
|
+
bun run verify # check:ci, then typecheck, then test
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`bun run check:ci`, not `bun ci`: `bun ci` is Bun's frozen-lockfile
|
|
56
|
+
install. In VS Code, `.vscode/` recommends Biome's extension and formats
|
|
57
|
+
on save with it.
|
|
58
|
+
|
|
59
|
+
`@biomejs/biome` is pinned exactly, since a release of Biome may format
|
|
60
|
+
differently. To move it:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
bun add --dev --exact @biomejs/biome@latest
|
|
64
|
+
bunx biome migrate --write
|
|
65
|
+
```
|
|
66
|
+
|
|
40
67
|
## Build
|
|
41
68
|
|
|
42
69
|
`bun run build` bundles the server and its dependencies into one file,
|
|
@@ -60,13 +87,16 @@ beside `dist/`.
|
|
|
60
87
|
|
|
61
88
|
## Docker
|
|
62
89
|
|
|
63
|
-
The `Dockerfile` builds in a stage of its own, on `oven/bun:1
|
|
90
|
+
The `Dockerfile` builds in a stage of its own, on `oven/bun:1`, and runs
|
|
91
|
+
on `oven/bun:1-alpine`. The build stage
|
|
64
92
|
installs every dependency with `--frozen-lockfile`, from the `bun.lock`
|
|
65
93
|
that `bun install` wrote (commit it), and runs `bun run build`. The image
|
|
66
94
|
holds `dist/` alone, no `node_modules` and no `src/`, and runs
|
|
67
95
|
`bun --no-install dist/server.js` as its non-root `bun` user: a package
|
|
68
96
|
missing from the bundle fails at startup instead of being fetched from
|
|
69
|
-
npm.
|
|
97
|
+
npm. `src/server.ts` stops the app on `SIGTERM`, so `docker stop` is
|
|
98
|
+
immediate. A native addon built for glibc alone does not load on
|
|
99
|
+
Alpine: put the final stage back on `oven/bun:1`.
|
|
70
100
|
|
|
71
101
|
```sh
|
|
72
102
|
docker build -t my-api .
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": true
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"includes": ["**", "!!**/dist"]
|
|
10
|
+
},
|
|
11
|
+
"formatter": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"indentStyle": "space"
|
|
14
|
+
},
|
|
15
|
+
"linter": {
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"rules": {
|
|
18
|
+
"preset": "recommended",
|
|
19
|
+
"complexity": {
|
|
20
|
+
"useLiteralKeys": "off"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"assist": {
|
|
25
|
+
"enabled": true,
|
|
26
|
+
"actions": {
|
|
27
|
+
"source": {
|
|
28
|
+
"organizeImports": "on"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|