|
| 1 | +# 03 - Stack, layout, and commands |
| 2 | + |
| 3 | +## Stack |
| 4 | + |
| 5 | +ESM TypeScript library. Bundled with `tsdown`. Tested with `vitest`. pnpm workspaces with catalog dependencies (`pnpm-workspace.yaml`); workspace globs reserve `playground`, `docs`, `packages/*`, `examples/*` for future additions. |
| 6 | + |
| 7 | +Per-package layout: |
| 8 | + |
| 9 | +- `src/` - library code; entry `src/index.ts` |
| 10 | +- `test/` - vitest specs; API snapshots via `tsnapi` under `test/__snapshots__/` |
| 11 | +- `dist/` - `tsdown` build output (shipped to the npm tarball via `files`) |
| 12 | + |
| 13 | +## Commands |
| 14 | + |
| 15 | +```sh |
| 16 | +pnpm install # requires pnpm@11.x |
| 17 | +pnpm build # tsdown |
| 18 | +pnpm dev # tsdown --watch |
| 19 | +pnpm test # pnpm build && vitest (api snapshot guards against stale dist) |
| 20 | +pnpm typecheck # turbo run typecheck (per-package tsc --noEmit) |
| 21 | +pnpm lint --fix # ESLint via @antfu/eslint-config |
| 22 | +pnpm knip # unused files/dependencies/exports across every workspace |
| 23 | +pnpm start # tsx src/index.ts |
| 24 | +``` |
| 25 | + |
| 26 | +Before opening a PR, all five gates MUST pass: |
| 27 | + |
| 28 | +```sh |
| 29 | +pnpm lint && pnpm knip && pnpm test && pnpm typecheck && pnpm build |
| 30 | +``` |
| 31 | + |
| 32 | +Commits and PR titles MUST follow Conventional Commits (`feat:`, `fix:`, …). |
| 33 | + |
| 34 | +## Testing |
| 35 | + |
| 36 | +The `pnpm test` script intentionally runs `build` first so `tsnapi` snapshots compare against fresh `dist/`. `tsdown-stale-guard` enforces this in `test/api-snapshot.test.ts`. |
| 37 | + |
| 38 | +## Typechecking |
| 39 | + |
| 40 | +`pnpm typecheck` fans out through Turbo: every workspace package MUST own a `"typecheck": "tsc --noEmit"` script and its own `tsconfig.json` (extending `tsconfig.base.json` with an explicit `include`). Cross-package imports resolve to source through the `paths` aliases in `tsconfig.base.json`, so no prior build is needed. A new package under `packages/*` or `plugins/*` joins the fan-out the moment it ships that script - always add one so type errors can't be silently skipped. |
| 41 | + |
| 42 | +`scripts/verify-typecheck-coverage.ts` runs first and fails the command (and CI, which just runs `pnpm typecheck`) if any workspace package has a `tsconfig.json` but no `typecheck` script. A package that genuinely can't typecheck yet needs a documented exception in that script, not a missing script. |
| 43 | + |
| 44 | +## Generated artifacts under `src/` |
| 45 | + |
| 46 | +Ahead-of-time build artifacts that live under `src/` - the shadow-root stylesheets in `packages/hub-ui/src/client/.generated/` and `packages/json-render-ui/src/.generated/` - are **generated, not committed** (`.generated` is gitignored). Each owning package builds its own with `pnpm run build:css`; three things guarantee the file is on disk before anything imports it: the root `postinstall` runs `turbo run build:css`, the Turbo `typecheck` task depends on both `build:css` tasks, and each package's `build` script chains `build:css` first. A new generated-under-`src` artifact MUST follow the same shape - its own build script, declared `outputs` in `turbo.json`, and a `typecheck` dependency - and MUST NOT be checked in: a minified single-line blob conflicts on every concurrent edit. |
| 47 | + |
| 48 | +## `starter/` |
| 49 | + |
| 50 | +The top-level, self-contained template for creating a new devframe (Vanilla TS, Vite SPA, playgrounds, tests). It uses real versions in its `package.json` (no catalogs, no `workspace:*`) so it is copy-paste ready for users; pnpm links its `devframe`/`@devframes/*` dependencies to the local workspace copies during development. When `bumpp -r` bumps the repo versions, `bumpp.config.ts` runs `scripts/sync-starter-version.ts` to update the starter's dependencies to match. |
| 51 | + |
| 52 | +## knip |
| 53 | + |
| 54 | +`pnpm knip` finds unused files, dependencies, and exports across every workspace (config in `knip.jsonc`), running against source directly - no prior build needed. Most workspaces need no configuration; `knip.jsonc` only carries per-workspace overrides for what knip's defaults can't infer: |
| 55 | + |
| 56 | +- **Non-`index.ts` `exports` subpaths.** knip's package.json→`dist`→`src` source mapping needs a workspace `tsconfig.json` `outDir`, which conflicts with this repo's cross-workspace `src/*.ts` imports, so multi-entry packages list their `exports`-mapped entry files explicitly - keep that list in sync with each `tsdown.config.ts`. |
| 57 | +- **Config files knip's plugins don't discover** in a nested location (a Next.js app rooted below the workspace root, `storybook-solidjs-vite` not matching the Storybook plugin trigger). |
| 58 | +- **Dependencies referenced dynamically** outside the static import graph (icon collections consumed by UnoCSS at build time, built-in devframe packages loaded via a runtime `import()` string). |
| 59 | + |
| 60 | +Prefer fixing the underlying gap or a scoped `ignoreDependencies`/`entry` override over a blanket `ignore`. |
0 commit comments