Skip to content
AstroCraft Docs
On this theme

Commands

Command Action
pnpm install Install dependencies
pnpm dev Dev server at localhost:4321
pnpm build Production build to dist/
pnpm preview Serve the production build locally
pnpm check astro check — type .astro and .ts
pnpm lint ESLint
pnpm format eslint --fix, then Prettier
pnpm test Every *.test.ts self-check under src/
pnpm wiki:lint Verify the wiki’s citations and links

The chain

pnpm lint && pnpm check && pnpm build && pnpm test && pnpm wiki:lint

Five commands, and they catch different things, which is why the order matters.

pnpm lint catches style and the accessibility rules — eslint-plugin-jsx-a11y runs over .astro markup, so a missing label or an aria attribute that cannot apply is a lint error rather than something you find with a screen reader.

pnpm check is the type pass over both .ts and .astro. It is where an illegal <Icon name>, a SiteHref missing its trailing slash, or a section prop that does not match its data shows up.

pnpm build is the real check. Content-collection schemas, config shapes and the SITE_URL gate all resolve at build time, and a Zod failure names the entry and the field. A clean build is the closest thing to a guarantee this project offers.

pnpm test runs the self-checks. pnpm wiki:lint does the same job for the prose.

The twelve self-checks

scripts/test.mjs walks src/, finds every *.test.ts, and runs each one with Node’s type stripping — no framework, no config, no fixtures. Discovery means a check written next to the code it covers runs without being registered anywhere. And zero checks is a failure, not a pass:

if (tests.length === 0) {
  console.error("No checks found under src/ — expected at least one *.test.ts file.");
  process.exit(1);
}

That single condition is what turns the house rule — non-trivial logic leaves one runnable check behind — from a convention into something the tooling enforces. There are twelve at the moment, and they run in about fifty milliseconds:

Check What it pins
config/carsData.test.ts Enough records for the pager, unique refs, every rail value matching a record, featured-extras integrity
config/hubsData.test.ts The same invariants for the new and electric datasets, plus refs unique across all three
config/footerLinks.test.ts Every footer and legal href resolves to a real page or an infoData key
config/homeData.test.ts The section-layout assumptions: photo arrays zipped by index, the dossier’s two rows of three, the {count} token actually replaced
config/reviewsData.test.ts The featured verdict and the shelf are real, non-draft, "Review"-category posts; four icon-backed steps; the make columns sum to their headline
config/searchData.test.ts Every result ref resolves, matches all three query fields the page claims, and is ordered nearest-first — the page’s own printed rule
config/valueMyCarData.test.ts The hero specimen resolves to a record with a histogram; the band has four steps; the grids are the shapes the sections render
js/favorites.test.ts The store’s parse/toggle/sort/median/city-count logic, and the position tiles against the real config data
js/schema.test.ts The JSON-LD builders, the @graph composition, and the < escaping
js/toc.test.ts Only H2s with explicit ids are extracted; an H3 is never mistaken for one
ui/password/strength.test.ts scorePassword across six inputs from empty to strong
ui/reveal/loadIn.test.ts The exact class string of the house entrance, and stagger’s delays

The pattern they share is worth naming: each one checks a cross-file assumption that no type can express. A rail option’s value matching a record’s field, a hub naming a post that exists, a section’s photo array being the same length as its config list — these are the joins that break silently, and each is three lines of assert.

Name a check <thing>.test.ts next to the code it covers and it runs. Name it anything else and nothing runs it.

The wiki linter

pnpm wiki:lint (scripts/wiki-lint.mjs) treats the project’s own wiki/ as code. It resolves every path:line citation in the prose and fails unless the cited line still contains a symbol the surrounding sentence names — which is the check that catches a citation that slid when a file grew above it. It also enforces the wiki’s bidirectional [[links]].

It currently checks 185 citations across 27 pages. If you edit a wiki page, run it.

pnpm format

eslint --fix first, then Prettier over everything with --ignore-unknown and a cache. The order matters: Prettier’s Tailwind plugin sorts class lists, so running it last means the class order in a file ESLint just rewrote is still sorted.

NEXT STEPTroubleshooting