AstroCraft — home
AstroCraft
Buy a licence
All documentation

Commands and checks

The four commands CI runs, what each one catches that the others cannot, and how to measure the editor's JavaScript for yourself.

Last updated

The gate

pnpm lint && pnpm check && pnpm test && pnpm build

All four, in that order, which is what CI runs.

Command Tool Catches
pnpm lint Biome style and correctness, frontmatter only in .astro files
pnpm check astro check types across .astro and .ts, including the template below ---
pnpm test Vitest around 2,000 cases, including the git layer against scratch repositories
pnpm build Astro the half no test replaces

pnpm build is the real check. The two rendering rules are build-time integration hooks, so a route on the wrong side of the static/on-demand line is caught there or nowhere. Content-schema errors and config typos surface here too.

If you took the decision to delete the copied tests, steps two and three still apply — pnpm check simply stops reading them.

Two tooling rules worth knowing

TypeScript is pinned to 6.x. The TS 7 native compiler does not expose the programmatic API the Astro language server drives, and astro check refuses it outright.

Never run biome check --unsafe. Its noNonNullAssertion fix rewrites ! to ?., which is a different program rather than a safer spelling — it changes return types.

Tests

They live in __tests__/ beside the module they cover, and pnpm test discovers them. There is no list to update.

Two of them are the ones you will actually care about as an adopter:

pnpm vitest run src/admin/js/__tests__/boundary.test.ts

Fails if anything inside src/admin/ imports something outside the allowed set, and prints the list of files an adopting site has to touch. That list is the adoption cost, and it is a test output rather than a claim in a document.

pnpm vitest run src/admin/js/__tests__/accountsCli.test.ts

Runs the first-account CLI as a real subprocess, under plain Node. It exists because v1.0.0 shipped one aliased import in that file and locked every fresh install out of its own CMS — and no static scan would have caught it, since a .astro import or a Vite-only feature breaks it identically.

Dev

pnpm dev

The admin is at /admin/. Two things behave differently here from a deployed server, both deliberately:

  • pushAfterCommit defaults off, so a save on your laptop cannot reach the real remote by surprise.
  • The composer’s live draft writes to disk, because there is a working tree to write to. Deployed, the canvas renders the model instead, since an uncommitted write would diverge the checkout from HEAD.

Restart the dev server after adding a route or changing astro.config.mjs. Astro reads its config once at startup, and the admin’s route table is scanned at config load rather than per request.

Measuring the editor’s JavaScript

The recorded figures are 1,738 bytes raw / 829 gzipped for the shell, and 201,390 / 61,812 for the editing engine on top of it. Most of that 189 kB is the markdown round trip, which is not optional — serializing has to run in the browser, because that is where the spans and the original bytes are.

Re-measure by walking the route’s entry chunk. There is no dist/**/index.html to read <script src> out of, because every admin route renders on demand:

npx astro build && node -e '
const fs=require("fs"),path=require("path"),zlib=require("zlib");
const dir="dist/client/_astro",seen=new Set();
const walk=f=>{if(seen.has(f))return;seen.add(f);
  const s=fs.readFileSync(path.join(dir,f),"utf8");
  for(const m of s.matchAll(/(?:from|import)\s*"\.\/([^"]+)"/g))walk(m[1]);};
walk(fs.readdirSync(dir).find(f=>f.startsWith("_slug_.astro")));
const all=[...seen].map(f=>fs.readFileSync(path.join(dir,f)));
const raw=all.reduce((a,b)=>a+b.length,0);
console.log([...seen].join("\n"));
console.log(`${raw} bytes raw, ${zlib.gzipSync(Buffer.concat(all),{level:9}).length} gzipped`);'

None of it reaches your public pages — Astro bundles per page. The one thing that is shared is CSS, because Tailwind scans all sources.

Verifying a deploy

Grep dist/ for what your change must emit. After a build, the artifacts worth checking are valid JSON-LD in the head, robots.txt and llms.txt with absolute URLs, sitemap-0.xml listing only indexable routes, and <meta name="build-rev"> on the page that answers /.

Creating an account

node --experimental-strip-types src/admin/js/accounts.cli.ts [email protected] "Your Name" [role]

Role is admin, editor or none, defaulting to admin. The password is read from stdin. See Your first account for what the roles mean and where the store lands. Running it again for an existing address replaces that account’s password, name and role.