Migration to astro#265
Draft
tnagorra wants to merge 48 commits into
Draft
Conversation
Astro 5 static scaffold: i18n routing (6 locales), react + sitemap integrations, redirect stubs, SVG icon sprite, base styles.
All routes from the Next app with exact URL parity: project pages (map + chart islands), data explorer (one island, SSR'd first cards), blogs via Content Collections, static pages, shared chrome. i18next core over the existing public/locales JSON.
Covers two Astro gaps: astro:assets aborts the whole build on one bad remote image (production data contains such images), and island hydration runtimes are force-inlined on every page (~57 MB across 14.6k pages).
blogs/images/ + relative refs let astro:assets optimize natively (incl. animated GIF -> animated webp), replacing the custom local-image pipeline.
18 unreferenced backup files; one had broken the i18next glob.
Trim the query to rendered fields (~20% smaller), paginate 100/page, refetch only projects whose modifiedAt/lastContributionDate changed (~50s -> ~6s warm). staticData.json doubles as the cache.
Kept as owned code (nothing else does render-skip). Activation now requires ASTRO_INCREMENTAL=1 from the orchestrator; the sitemap is regenerated over the merged tree. No-op 5s; one-project change 6s.
Hydration dedup: withastro/docs#2150, roadmap#36, astro#6247. Fail-soft images: no existing issue covers it.
Workspace package: generic recurrence-based discovery, one external file per distinct inline script, in-place replacement; configurable threshold/output/globs.
createRemoteImages(options): widths/quality/cache/concurrency and split onError/onTransientError policies. Failed images render a bundled error SVG and warn; cache layout unchanged (zero re-encode).
selectPaths page API + runIncrementalBuild(config). Hash globs, prune routes, sitemap locales, and build commands all move to config; the app orchestrator becomes a 34-line wrapper.
packages/README.md index; each package README links back to the decision records.
Setup, commands, cache/state table with cold costs, env knobs, structure, gotchas.
Strip change-log/session narration from comments; keep constraint rationale, contracts, and upstream references.
'activation' for the env-var mechanism, 'checklist' for the cutover go/no-go.
The branch now transforms the project in place instead of carrying a parallel astro/ subdir. Next app + its tooling deleted (rollback = main); locales/ moved out of publicDir; fetch-data runs under plain node (ts-node/codegen deps dropped); single root package.json with workspaces. Build verified end-to-end after the move.
32px PNG-in-ICO generated from logo-compact.svg; BaseLayout already linked /favicon.ico but the file never existed (404 under Next too).
node 22 + npm equivalent of the removed yarn/Next compose: mounts the repo, runs the dev server on 4321.
eslint 9 flat config (typescript-eslint, astro, react-hooks classic rules), stylelint-config-standard; both pass. Fixes surfaced by the first run: unused catch binding and dead assignment in fetchData, dead helper in DataExplorer, an irregular whitespace char. Pre-commit hooks call the npm scripts.
packages/ are plain directories imported by relative path — no linking, single node_modules. pnpm's strict resolution surfaced five phantom deps (the unified/remark markdown pipeline was only reachable through npm hoisting); now declared. sharp's build script approved via pnpm.onlyBuiltDependencies. Docker, pre-commit, README, and docs updated to pnpm.
Union narrowing on the mission cards, readonly params in chart.ts (the functions never mutate). pnpm settings moved to pnpm-workspace.yaml (settings-only, no packages key — not a workspace).
13 invariants from the migration's manual verification ladder — page and sitemap counts, data-JSON consistency, hydration file, blog-image and animated-webp guards, per-locale content, dist size early warning.
staticData.json is zod-validated at load (named-field errors instead of silently-empty pages on backend drift). i18n init warns once per build with empty-translation counts per locale.
Three tests against astro preview: map (client:only) mounts, history chart (client:visible) hydrates, data-explorer search filters the fetched dataset over SSR'd cards, blog images decode. Proves the islands hydrate from the deduped external runtime in a real browser — the check deferred since the hydration-dedup change.
tnagorra
commented
Jul 13, 2026
tnagorra
commented
Jul 13, 2026
tnagorra
commented
Jul 13, 2026
tnagorra
commented
Jul 13, 2026
Contributor
Author
There was a problem hiding this comment.
Why was this removed? We do need postcss
tnagorra
commented
Jul 13, 2026
|
|
||
| checks: | ||
| build: . | ||
| command: sh -c 'pre-commit run -vvv --all-files' |
Contributor
Author
There was a problem hiding this comment.
Why remove precommit checks?
tnagorra
commented
Jul 13, 2026
| "extends": "stylelint-config-standard", | ||
| "rules": { | ||
| "declaration-block-single-line-max-declarations": null, | ||
| "selector-class-pattern": null |
Contributor
Author
There was a problem hiding this comment.
Why remove the indent rule and concentric support
tnagorra
commented
Jul 13, 2026
Contributor
Author
There was a problem hiding this comment.
Why remove unimported?
4-space indentation is the repo standard (2 for yaml).
PostCSS pipeline (flexbugs-fixes, nested, normalize, preset-env — same options; object-map form since Vite rejects Next's string-array format), main's design tokens (variables/globals), and all portable component + page modules co-located with their Astro counterparts. Islands share the page modules (hashed classes match across .astro and .tsx). de-card kept as a literal e2e hook. Not portable: JS-positioned Popup/DropdownMenu (CSS hover dropdown instead), next/font injection (font-family vars), some react-icons (text equivalents).
Class attributes were ~22% of every page with the default pattern.
GraphQL documents move to scripts/graphql/*.graphql (codegen's tag plucker cannot parse interpolated templates); fetchData reads the same files at runtime and uses the generated per-operation types. generated/ is no longer committed — 'pnpm generate:type' produces it from backend/schema.graphql, no running server needed.
Astro applies tsconfig paths natively; src imports use components/*, lib/*, layouts/*, i18n/*, styles/*, packages/* instead of relative chains. Boundary: files reachable from plain-node scripts keep relative imports (node does not read tsconfig). Codegen emits union types and operation types only (enums are not erasable; the full schema dump emitted duplicate enum names).
unimported cannot parse .astro entry points; knip understands them. Package public APIs are entry files (exports exempt). Removed two genuinely unused app exports it flagged.
Main's config (recommended + concentric + 4-space indentation) on stylelint 16: @Stylistic plugin supplies the removed indentation rule, stylelint-config-concentric-order the property order. 100+ order issues auto-fixed; one real specificity fix in the navbar dropdown.
eslint @stylistic/indent + stylelint @stylistic/indentation + editorconfig; 2,713 violations autofixed. Navbar dropdown state rule moved after its base rule (no-descending-specificity).
Minimal diff vs main: command/framework swaps, current env vars, updated translation + blog-image paths, and a short Build Internals appendix. Replaces the earlier full rewrite.
Astro's inlineStylesheets:'auto' duplicated ~10 KB of identical CSS into every page (~150 MB across 14.6k pages). External bundles are fetched once and cached; dist 643 -> 487 MB.
Content-hashed (stable across builds for the incremental merge), first char alphabetic, build fails on collision instead of silently merging styles. ~680 B/page.
tnagorra
commented
Jul 14, 2026
| @@ -0,0 +1,29 @@ | |||
| // graphql-codegen wrapper: the typescript + typescript-operations plugin pair | |||
Contributor
Author
There was a problem hiding this comment.
There should be a configuration fix that solves this without a wrapper
tnagorra
commented
Jul 14, 2026
Contributor
Author
There was a problem hiding this comment.
This should not have a diff
tnagorra
commented
Jul 14, 2026
Contributor
Author
There was a problem hiding this comment.
Why do we need a eslint.config.mjs?
tnagorra
commented
Jul 14, 2026
| // post-processes the final HTML after the other build:done hooks. | ||
| import hydrationDedup from './packages/astro-hydration-dedup/src/index.ts'; | ||
|
|
||
| const SITE = 'https://mapswipe.org'; |
Contributor
Author
There was a problem hiding this comment.
This cannot be hard-coded.
tnagorra
commented
Jul 14, 2026
|
|
||
| const SITE = 'https://mapswipe.org'; | ||
|
|
||
| const CLASS_ALPHA = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
Contributor
Author
There was a problem hiding this comment.
Separate this into a separate plugin
Hero/Section/Card/CenteredCard/Heading/Tag/KeyFigure/ListItem/Button as .astro components (slots, zero JS); pages no longer hand-roll their markup. Output verified byte-equivalent across all 14,680 pages modulo CSS chunk order, attr order, and island prelude placement.
43 anchors and 21 wrapper divs move off raw CSS-module classes. Spread components drop the slot placement attr (Astro leaves it in props). Output byte-equivalent across all 14,680 pages.
Section/Card/Tag/Button/Link/Heading/ImageWrapper as .tsx twins of the .astro components (islands can't render .astro); DataExplorer and ProjectsMapIsland drop their hand-rolled markup. Shared cs() joiner in lib/cs. SSR output identical except class order inside one attribute.
Style-bearing components move to Name/{index.astro,index.tsx,
styles.module.css}; no-CSS components stay flat. CLASS_MAP_OUT debug
dump added (class hashes shift when a module moves — the map lets two
builds be compared structurally; verified identical on all pages).
House rule (matches main's JSX style): 2+ attributes each get their own line, closing bracket tag-aligned; single attribute stays inline. Enforced for tsx via @Stylistic jsx rules. Also drops the 'port of the Next component' comment phrasing (why/how comments only) and dedups the map placeholder in DataExplorer. Whitespace-only in the built HTML.
Mark-and-sweep from all text roots, following vite inter-chunk imports and css url()s. Removes the always-emitted image originals (propsData signatures touch coverImage.src) and merge-retained superseded variants — 35 files / 28.7 MB on the current tree.
Fourth package; the incremental runner delegates to it and plain builds run it via scripts/sweep-dist.mjs — previously only incremental builds were swept. A single clean build carries 11 files / 6 MB of unreferenced image originals; merged trees accumulate more.
Measured 55s -> 48s (-13%, +220 MiB peak); 8 gave -18% but +630 MiB. Output verified byte-identical vs sequential across all 14,680 pages. ASTRO_BUILD_CONCURRENCY overrides per-run.
build/out (astro outDir) + build/incremental (state) + build/image-cache — CI caches one tree; fullData/ -> full-data/. Package defaults keep 'dist'; the app overrides everywhere (incl. remote-images distDir, which otherwise recreated a stray dist/_img).
Every cacheable artifact now lives in the one build/ tree.
A crashed dev-server node dumped core.<pid> files into the repo root, and git add -A swept 9.7 GiB into two commits (history rewritten).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.