# 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.

Source: https://editor.astrocraftthemes.com/docs/commands/

## The gate

```bash
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:

```bash
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.

```bash
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

```bash
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](/docs/page-composer/) 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:

```bash
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

```bash
node --experimental-strip-types src/admin/js/accounts.cli.ts you@example.com "Your Name" [role]
```

Role is `admin`, `editor` or `none`, defaulting to `admin`. The password is read from stdin. See [Your first account](/docs/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.