Skip to content

Migration to astro#265

Draft
tnagorra wants to merge 42 commits into
mainfrom
feat/astro-migration
Draft

Migration to astro#265
tnagorra wants to merge 42 commits into
mainfrom
feat/astro-migration

Conversation

@tnagorra

Copy link
Copy Markdown
Contributor

No description provided.

tnagorra added 26 commits July 10, 2026 11:48
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.
Comment thread generated/types.ts Outdated
Comment thread tsconfig.json
Comment thread README.md
Comment thread postcss.config.js Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this removed? We do need postcss

Comment thread pnpm-workspace.yaml
Comment thread package.json
Comment thread Dockerfile

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove "git config --global --add safe.directory /code"

Comment thread docker-compose.yml Outdated

services:
next:
web:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"web" can be confusing as we use this to refer to web server in other projects

Comment thread docker-compose.yml

checks:
build: .
command: sh -c 'pre-commit run -vvv --all-files'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove precommit checks?

Comment thread .stylelintrc.json Outdated
"extends": "stylelint-config-standard",
"rules": {
"declaration-block-single-line-max-declarations": null,
"selector-class-pattern": null

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the indent rule and concentric support

Comment thread .unimportedrc.json Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove unimported?

tnagorra added 11 commits July 14, 2026 07:41
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.
Comment thread scripts/generateTypes.mjs
@@ -0,0 +1,29 @@
// graphql-codegen wrapper: the typescript + typescript-operations plugin pair

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a configuration fix that solves this without a wrapper

Comment thread postcss.config.cjs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not have a diff

Comment thread .eslintrc.json Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a eslint.config.mjs?

Comment thread astro.config.mjs
// 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';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cannot be hard-coded.

Comment thread astro.config.mjs

const SITE = 'https://mapswipe.org';

const CLASS_ALPHA = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate this into a separate plugin

tnagorra added 5 commits July 14, 2026 12:29
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant