-
Notifications
You must be signed in to change notification settings - Fork 69
Expand file tree
/
Copy pathDockerfile
More file actions
141 lines (127 loc) · 8.03 KB
/
Copy pathDockerfile
File metadata and controls
141 lines (127 loc) · 8.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# Single image for the whole monorepo. Each Railway service runs the
# same image with a different start command. Locally:
# `docker compose up --build` runs all three via compose.yaml.
#
# No build step for JS. webjs serves .ts directly by stripping types at the
# runtime layer (position-preserving whitespace replacement, no sourcemap shipped
# to the browser). webjs is buildless end to end; there is NO bundler or esbuild
# fallback.
#
# The image carries BOTH runtimes by design. The BUILD toolchain runs on Node
# (npm install, the core dist bundle, Tailwind), which keeps
# the proven buildless toolchain unchanged; **Node 24+ is REQUIRED** there (the
# built-in `module.stripTypeScriptTypes` stripper and recursive fs.watch need it),
# which is why the base pins a current Node major. The SERVING process runs on
# Bun: each service's start command is `bun ... webjs.js start`, so `startServer`
# selects the native `Bun.serve` listener shell (more req/s on the listening path)
# and strips `.ts` via `amaro`. The one behavioral difference from the node:http
# shell is that 103 Early Hints are node-only (Bun.serve has no informational-
# response API), so the modulepreload head-start is dropped on Bun; the preloads
# still ship in the document head. The Bun binary is copied from the official
# `oven/bun` image below; nothing is BUILT on Bun, so there is no build-toolchain
# risk.
#
# Tailwind CSS IS built at image time (CLI, no browser runtime). The
# blog applies its Drizzle migrations (`webjs db migrate`) at start via
# `webjs.start.before`; there is no DB codegen step.
FROM node:26-alpine
# ca-certificates for outbound TLS (e.g. the jspm vendor resolve); openssl is
# kept as a small, harmless base lib several native modules link against.
RUN apk add --no-cache openssl ca-certificates
# Drop the Bun binary into the Node image (musl/alpine build) so the serving
# process runs on Bun while the build steps keep using Node. `COPY --from=<image>`
# pulls only the static binary, no extra layers. The dockerfile-copy-paths
# repo-health test skips `--from=` lines, so this image source is not validated
# as a repo path.
COPY --from=oven/bun:1-alpine /usr/local/bin/bun /usr/local/bin/bun
WORKDIR /app
# --- 1. Install deps (layer cached on manifest changes only) ------------
# Copy every workspace manifest before source so dep changes don't bust
# the source layer and vice versa.
COPY package.json package-lock.json ./
COPY packages/cli/package.json ./packages/cli/
COPY packages/core/package.json ./packages/core/
COPY packages/mcp/package.json ./packages/mcp/
COPY packages/server/package.json ./packages/server/
COPY packages/editors/intellisense/package.json ./packages/editors/intellisense/
COPY packages/ui/package.json ./packages/ui/
COPY packages/ui/packages/registry/package.json ./packages/ui/packages/registry/
COPY packages/ui/packages/website/package.json ./packages/ui/packages/website/
COPY examples/blog/package.json ./examples/blog/
COPY website/package.json ./website/
COPY docs/package.json ./docs/
# Copy the CLI's bin/ before install so npm can symlink it into
# /app/node_modules/.bin/webjs. Without this, the bin target doesn't
# exist at install time and npm silently skips the symlink - then
# `npm start` inside any workspace fails with `sh: webjs: not found`.
COPY packages/cli/bin ./packages/cli/bin
RUN npm install --no-audit --no-fund
# --- 2. Copy source -----------------------------------------------------
COPY packages ./packages
COPY examples ./examples
COPY website ./website
COPY docs ./docs
# scripts/build-framework-dist.js is invoked by the step-3
# `npm run build:dist --workspace=@webjsdev/core` line, so the
# scripts tree has to be in the image before that step runs.
COPY scripts ./scripts
# website/app/changelog/page.ts reads ../../../changelog/<pkg>/*.md at
# SSR time. Without copying the changelog tree into the image, the
# deployed page renders "No entries yet."
COPY changelog ./changelog
# website/app/blog/[slug]/page.ts reads ../../../../blog/<slug>.md at
# SSR time (via modules/blog/queries/list-posts.server.ts). Same
# reason as changelog: without the tree in the image, /blog renders
# "No posts yet."
COPY blog ./blog
# website/app/compare/[slug]/page.ts and app/sitemap.ts read
# ../../../../compare/<slug>.md at SSR time (via
# modules/compare/queries/*.server.ts). Same reason as blog: without the
# tree in the image, /compare renders "No comparisons yet."
COPY compare ./compare
# --- 3. Build-time work --------------------------------------------------
# Core: build the dist/ bundles. The package.json `prepare` hook is a
# self-guarded no-op during step 1 (manifests-only npm install, before
# scripts/ and packages/core/src/ are copied in), so the bundle has to
# be built explicitly here, after sources land. Without this step,
# production images serve per-file from src/ via the workspace-dev
# fallback, which is functional but waterfalls the browser through ~15
# requests per page instead of one chunk per subpath.
RUN npm run build:dist --workspace=@webjsdev/core
# Blog: no DB codegen step. Drizzle has no generated client (the schema IS the
# types). The committed migrations are applied at container START by the blog's
# `webjs.start.before` (`webjs db migrate`), not at build time.
# UI registry: the registry JSON is composed on demand by the route handlers
# (no build step). But the ui-website's component DETAIL pages statically import
# the component SOURCES from `components/ui/*.ts`, which the `prestart` hook
# generates by copying them out of packages/ui/packages/registry/. A start
# command that serves directly (the `bun webjs.js start` form, which bypasses
# npm `prestart`, the same way the Tailwind step below is needed) would 500 on
# every component page without these files, so bake them at build time here.
# `copy-registry.js` is pure filesystem (no runtime / network / DB), so it is
# build-safe.
RUN cd packages/ui/packages/website && node scripts/copy-registry.js
# Tailwind: compile per-app CSS (all four use the CLI, no browser runtime).
# Each compose service's command invokes `webjs.js start` directly, which
# bypasses the per-package `prestart: css:build` hook in npm; the CSS has
# to be ready in the image. Keep this list in sync with the apps that
# have a public/input.css and a `css:build` script in their package.json.
RUN npx tailwindcss -i website/public/input.css -o website/public/tailwind.css --minify \
&& npx tailwindcss -i docs/public/input.css -o docs/public/tailwind.css --minify \
&& npx tailwindcss -i examples/blog/public/input.css -o examples/blog/public/tailwind.css --minify \
&& npx tailwindcss -i packages/ui/packages/website/public/input.css -o packages/ui/packages/website/public/tailwind.css --minify
# Default env vars. Railway / compose set their own per service.
ENV NODE_ENV=production
# Platform-neutral readiness gate (mirrors packages/cli/templates/Dockerfile,
# the pattern the scaffold ships to users). webjs answers /__webjs/ready with
# 503 until the instance is fully warm (analysis + first vendor attempt), then
# 200. This image-level HEALTHCHECK is honoured by Docker, compose, and most
# Docker-based platforms, so the readiness gate works the same everywhere
# without a per-platform file. Each service sets its own PORT (compose env, or
# Railway injects it); the probe reads it, defaulting to 8080. Dependency-free
# (Node's built-in fetch, no curl/wget). Platforms that read their own config
# point the equivalent knob at the same path (Railway healthcheckPath in
# railway.json, Fly [checks], k8s readinessProbe).
HEALTHCHECK --interval=15s --timeout=3s --start-period=40s --retries=5 \
CMD ["node", "-e", "fetch('http://127.0.0.1:'+(process.env.PORT||8080)+'/__webjs/ready').then(r=>process.exit(r.ok?0:1),()=>process.exit(1))"]
CMD ["node", "--help"]