@alxia/create 0.1.3 → 0.1.4
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 +8 -5
- package/docs/guide.md +18 -7
- package/docs/roadmap.md +10 -0
- package/docs/troubleshooting.md +30 -0
- package/package.json +1 -1
- package/templates/api/Dockerfile +4 -2
- package/templates/api/README.md +5 -2
- package/templates/api/src/server.ts +14 -0
- package/templates/react-router/Dockerfile +4 -2
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`, 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 |
|
|
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` running on `oven/bun:1-alpine`, `.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` running on `oven/bun:1-alpine` 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)):
|
|
@@ -53,8 +53,9 @@ export const app = alxia()
|
|
|
53
53
|
|
|
54
54
|
## Docker
|
|
55
55
|
|
|
56
|
-
Both projects build into an image as they are written
|
|
57
|
-
|
|
56
|
+
Both projects build into an image as they are written: built on
|
|
57
|
+
`oven/bun:1`, run on `oven/bun:1-alpine` as the non-root `bun` user, an
|
|
58
|
+
image of about 130 MB. Each `Dockerfile`
|
|
58
59
|
builds in a stage of its own, and the image holds the build output alone,
|
|
59
60
|
no `node_modules`:
|
|
60
61
|
|
|
@@ -81,7 +82,9 @@ with `--frozen-lockfile`. The
|
|
|
81
82
|
[guide](https://github.com/softistx/alxia/blob/develop/packages/create/docs/guide.md#docker)
|
|
82
83
|
has the stages, and
|
|
83
84
|
[troubleshooting](https://github.com/softistx/alxia/blob/develop/packages/create/docs/troubleshooting.md#error-cannot-find-package--from-appdistserverjs)
|
|
84
|
-
what to do for a dependency that cannot be bundled
|
|
85
|
+
what to do for a dependency that cannot be bundled, and for a
|
|
86
|
+
[native addon built for glibc alone](https://github.com/softistx/alxia/blob/develop/packages/create/docs/troubleshooting.md#error--is-linked-against-glibc-dt_needed-libmso6-but-this-bun-build-uses-musl),
|
|
87
|
+
which does not load on Alpine.
|
|
85
88
|
|
|
86
89
|
## Options
|
|
87
90
|
|
package/docs/guide.md
CHANGED
|
@@ -67,10 +67,10 @@ my-api/
|
|
|
67
67
|
├── src/
|
|
68
68
|
│ ├── app.ts the app, and its type
|
|
69
69
|
│ ├── app.spec.ts bun test: app.request() and @alxia/client
|
|
70
|
-
│ └── server.ts app.listen(PORT)
|
|
70
|
+
│ └── server.ts app.listen(PORT), stopped on SIGTERM
|
|
71
71
|
├── package.json
|
|
72
72
|
├── tsconfig.json
|
|
73
|
-
├── Dockerfile bun run build, then dist/ alone, on oven/bun:1
|
|
73
|
+
├── Dockerfile bun run build, then dist/ alone, on oven/bun:1-alpine
|
|
74
74
|
├── .dockerignore
|
|
75
75
|
├── .env.example PORT and API_KEY, for a .env Bun loads
|
|
76
76
|
├── .gitignore
|
|
@@ -230,15 +230,25 @@ goes from there.
|
|
|
230
230
|
|
|
231
231
|
## Docker
|
|
232
232
|
|
|
233
|
-
Each project's `Dockerfile`
|
|
234
|
-
image,
|
|
233
|
+
Each project's `Dockerfile` builds it on Bun's official `oven/bun:1`
|
|
234
|
+
image, Debian's, and runs it on `oven/bun:1-alpine`, as that image's
|
|
235
|
+
non-root `bun` user (uid 1000). The installs are
|
|
235
236
|
`--frozen-lockfile`, from the `bun.lock` the command's `bun install`
|
|
236
237
|
wrote: commit it.
|
|
237
238
|
|
|
238
239
|
Each one builds in a stage of its own, and the image holds the build
|
|
239
240
|
output alone: no `node_modules`, no sources. The dependencies are inside
|
|
240
241
|
the bundle, so the image is the base image and a few hundred kilobytes to
|
|
241
|
-
a few megabytes
|
|
242
|
+
a few megabytes: about 130 MB, where the same build on `oven/bun:1` is
|
|
243
|
+
about 345 MB.
|
|
244
|
+
|
|
245
|
+
The build stages stay on Debian: the tools a build runs, Vite's,
|
|
246
|
+
Tailwind's and any package's install script, are tried on glibc first,
|
|
247
|
+
and Alpine saves nothing there, since the stage is not shipped. What is
|
|
248
|
+
shipped is JavaScript, which Bun runs the same on musl. A native addon is
|
|
249
|
+
the exception: one built for glibc alone cannot load on Alpine, and the
|
|
250
|
+
final stage goes back to `oven/bun:1`
|
|
251
|
+
([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
252
|
copied in: see
|
|
243
253
|
[troubleshooting](troubleshooting.md#error-cannot-find-package--from-appdistserverjs).
|
|
244
254
|
|
|
@@ -250,7 +260,8 @@ Two stages: every dependency, installed with
|
|
|
250
260
|
running `bun --no-install dist/server.js`, `start`'s command with
|
|
251
261
|
Bun's `--no-install` (not create-alxia's option of the same name), so that a package missing from the bundle fails at
|
|
252
262
|
startup rather than being fetched from npm, written out so that Bun
|
|
253
|
-
is the container's process.
|
|
263
|
+
is the container's process. `src/server.ts` stops the app on `SIGTERM`:
|
|
264
|
+
as process 1, Bun would otherwise ignore it, and `docker stop` would wait.
|
|
254
265
|
|
|
255
266
|
```dockerfile
|
|
256
267
|
FROM oven/bun:1 AS build
|
|
@@ -260,7 +271,7 @@ RUN bun install --frozen-lockfile
|
|
|
260
271
|
COPY . .
|
|
261
272
|
RUN bun run build
|
|
262
273
|
|
|
263
|
-
FROM oven/bun:1
|
|
274
|
+
FROM oven/bun:1-alpine
|
|
264
275
|
WORKDIR /app
|
|
265
276
|
ENV NODE_ENV=production
|
|
266
277
|
COPY --from=build /app/dist ./dist
|
package/docs/roadmap.md
CHANGED
|
@@ -30,6 +30,13 @@ Nothing scheduled yet.
|
|
|
30
30
|
|
|
31
31
|
### Next release
|
|
32
32
|
|
|
33
|
+
- **The images run on Alpine.** Every `Dockerfile`'s final stage is
|
|
34
|
+
`oven/bun:1-alpine`, the build stages staying on `oven/bun:1`: each
|
|
35
|
+
image is about 130 MB, where it was about 345 MB. The `api` project
|
|
36
|
+
stops on `SIGTERM`, so `docker stop` no longer waits.
|
|
37
|
+
|
|
38
|
+
### 0.1.3
|
|
39
|
+
|
|
33
40
|
- **Every `Dockerfile` builds, and the image holds the build alone.** The
|
|
34
41
|
`api` template's builds `dist/server.js`, bundled, minified and source
|
|
35
42
|
mapped, and runs it with no `node_modules` and no `src/`; `start` runs
|
|
@@ -37,6 +44,9 @@ Nothing scheduled yet.
|
|
|
37
44
|
copies `build/` alone, which `@alxia/react-router`'s plugin now bundles
|
|
38
45
|
whole. Each image is about 50 MB (api) and 150 MB (react-router)
|
|
39
46
|
smaller.
|
|
47
|
+
|
|
48
|
+
### 0.1.2
|
|
49
|
+
|
|
40
50
|
- **The `api` template is files, copied, with a `Dockerfile`.** It ships
|
|
41
51
|
under `templates/api/` and is copied as `react-router`'s is. New in it:
|
|
42
52
|
a `Dockerfile` on `oven/bun:1`, `.dockerignore` and `.env.example`.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -38,6 +38,7 @@ 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
|
|
|
43
44
|
## Before it runs
|
|
@@ -325,6 +326,35 @@ COPY --from=production-dependencies /app/node_modules ./node_modules
|
|
|
325
326
|
the bundle still imports: Node's and Bun's modules, and the packages left
|
|
326
327
|
external.
|
|
327
328
|
|
|
329
|
+
### `error: … is linked against glibc (DT_NEEDED libm.so.6), but this Bun build uses musl.`
|
|
330
|
+
|
|
331
|
+
The container stops at startup, or at the first request that loads the
|
|
332
|
+
package, with the path of a `.node` file in `node_modules` and
|
|
333
|
+
`code: "ERR_DLOPEN_FAILED"`. The library named after `DT_NEEDED` is
|
|
334
|
+
whichever one the addon links first: `libc.so.6`, `libstdc++.so.6` or
|
|
335
|
+
another; the fix is the same.
|
|
336
|
+
|
|
337
|
+
**When:** a package is kept external and installed beside the build, as
|
|
338
|
+
the previous entry says, and it loads a native addon built for glibc
|
|
339
|
+
alone.
|
|
340
|
+
|
|
341
|
+
**Why:** the final stage is `oven/bun:1-alpine`, whose C library is
|
|
342
|
+
musl. The bundle is JavaScript, which runs the same there, but a `.node`
|
|
343
|
+
file is compiled against one C library, and one compiled for glibc
|
|
344
|
+
cannot load on musl, even with `gcompat`. A package that publishes a
|
|
345
|
+
musl build too, as `sharp` does, works: `bun install`, `--production` too,
|
|
346
|
+
puts both variants in `node_modules`, and the package picks musl's.
|
|
347
|
+
|
|
348
|
+
**Fix:** run the final stage on Debian's image, `oven/bun:1`, which
|
|
349
|
+
holds glibc. The build stages stay as they are:
|
|
350
|
+
|
|
351
|
+
```dockerfile
|
|
352
|
+
# The final stage.
|
|
353
|
+
FROM oven/bun:1
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
The image is about 200 MB larger.
|
|
357
|
+
|
|
328
358
|
### The project's `@alxia/*` are older than npm's latest
|
|
329
359
|
|
|
330
360
|
**Symptom:** a fresh project declares `@alxia/core` at `^0.3.4` while npm
|
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
|
@@ -60,13 +60,16 @@ beside `dist/`.
|
|
|
60
60
|
|
|
61
61
|
## Docker
|
|
62
62
|
|
|
63
|
-
The `Dockerfile` builds in a stage of its own, on `oven/bun:1
|
|
63
|
+
The `Dockerfile` builds in a stage of its own, on `oven/bun:1`, and runs
|
|
64
|
+
on `oven/bun:1-alpine`. The build stage
|
|
64
65
|
installs every dependency with `--frozen-lockfile`, from the `bun.lock`
|
|
65
66
|
that `bun install` wrote (commit it), and runs `bun run build`. The image
|
|
66
67
|
holds `dist/` alone, no `node_modules` and no `src/`, and runs
|
|
67
68
|
`bun --no-install dist/server.js` as its non-root `bun` user: a package
|
|
68
69
|
missing from the bundle fails at startup instead of being fetched from
|
|
69
|
-
npm.
|
|
70
|
+
npm. `src/server.ts` stops the app on `SIGTERM`, so `docker stop` is
|
|
71
|
+
immediate. A native addon built for glibc alone does not load on
|
|
72
|
+
Alpine: put the final stage back on `oven/bun:1`.
|
|
70
73
|
|
|
71
74
|
```sh
|
|
72
75
|
docker build -t my-api .
|
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import { app } from './app';
|
|
2
2
|
|
|
3
|
+
// Stop as the platform asks. In a container Bun is process 1, which a
|
|
4
|
+
// signal with no handler does not stop: `docker stop` would wait.
|
|
5
|
+
for (const signal of ['SIGINT', 'SIGTERM'] as const) {
|
|
6
|
+
process.once(signal, () => {
|
|
7
|
+
void app.stop().then(
|
|
8
|
+
() => process.exit(0),
|
|
9
|
+
(error: unknown) => {
|
|
10
|
+
console.error(error);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
},
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
3
17
|
const server = app.listen(Number(Bun.env['PORT'] ?? 3000));
|
|
4
18
|
console.log(`listening on ${server.url}`);
|
|
@@ -16,8 +16,10 @@ RUN bun run build
|
|
|
16
16
|
# command, and --no-install: with no node_modules, Bun would otherwise
|
|
17
17
|
# fetch a package the build left out from the registry at startup,
|
|
18
18
|
# where this fails.
|
|
19
|
-
# The server listens on PORT (3000) and HOST (0.0.0.0).
|
|
20
|
-
|
|
19
|
+
# The server listens on PORT (3000) and HOST (0.0.0.0). On Alpine, about
|
|
20
|
+
# 200 MB lighter: build/ is JavaScript, which runs the same on musl. A
|
|
21
|
+
# native addon built for glibc alone needs oven/bun:1 here.
|
|
22
|
+
FROM oven/bun:1-alpine
|
|
21
23
|
WORKDIR /app
|
|
22
24
|
ENV NODE_ENV=production
|
|
23
25
|
COPY --from=build /app/build ./build
|