Pulp Engine v0.10.0
Release date: 2026-03-22
Highlights
pnpm lintis now a hard-fail gate in CI — a lint error blocks the run; warnings are permitted.- Turborepo cache inputs corrected for
build,typecheck,test, andlinttasks — stale-cache false-passes are eliminated. - Vitest configs standardised across all test-bearing workspaces with explicit
src/**discovery anddist/excluded. - No-test workspaces (
template-model,template-renderer,preview) now participate cleanly inturbo run testandturbo run lint— no moreCommand=<NONEXISTENT>in Turbo dry-run output. - CI gains a dedicated
test-e2ejob; Playwright browsers are installed per-run and the report is uploaded as an artifact on failure. Turbo bumped from 2.8.17 to 2.8.20.
ESLint 9 flat config
Background
Previously there was no enforced lint gate and no shared config across workspaces. The lint Turbo task referenced .eslintrc* in its inputs array, but no such files existed in the repo — the lint task was permanently uncached and produced no output that could be verified in CI.
What changed
A single root-level eslint.config.mjs is now the shared config for all workspaces. It uses ESLint 9’s flat config API (defineConfig) and composes:
@eslint/jsrecommended rulestypescript-eslintrecommended ruleseslint-plugin-react-hooksrecommended-latest rules
Intentionally-unused identifiers follow the _-prefix convention: argsIgnorePattern, varsIgnorePattern, caughtErrorsIgnorePattern, and destructuredArrayIgnorePattern are all set to ^_.
Test files (**/__tests__/**/*.ts, **/*.test.ts, **/*.test.tsx) have @typescript-eslint/no-explicit-any turned off to allow any in mocks without type ceremony.
The following output directories are ignored: dist/, build/, coverage/, .turbo/, node_modules/.
The required devDependencies (@eslint/js, eslint, eslint-plugin-react-hooks, typescript-eslint) are declared in the root package.json. Each workspace already has an "lint": "eslint src" script — no per-workspace config files are needed.
The lint Turbo task’s cache inputs now reference ../../eslint.config.mjs so that changing the shared config correctly invalidates per-workspace lint caches.
pnpm lint runs as a Lint step in the ci job, immediately after Install dependencies and before Build and Test. Lint errors fail the job; warnings do not.
Turbo cache input corrections
Background
Several Turbo task inputs arrays were incomplete or referenced paths that do not exist, allowing Turbo to serve cached results after source changes that should have invalidated the cache.
What changed
| Task | Problem | Fix |
|---|---|---|
build | Missing src/**/*.tsx, src/**/*.css, vite.config.ts, index.html | All four added to inputs |
typecheck | Missing src/**/*.tsx | Added to inputs |
test | test/**/*.ts matched a non-existent directory; vitest.config.ts alone was insufficient | Replaced with src/**/*.ts, src/**/*.tsx, assets/**, vitest.file.config.ts, package.json |
lint | .eslintrc* (always matched nothing); missing src/**/*.tsx and the shared config file | Replaced with src/**/*.ts, src/**/*.tsx, ../../eslint.config.mjs |
Vitest config standardisation
Background
At v0.9.0: four test-bearing packages (chart-renderer, data-adapter, html-renderer, schema-validator) had no vitest.config.ts and relied on Vitest’s default discovery, which includes dist/ build output. packages/pdf-renderer had a config but it only set testTimeout — no include or exclude guards. apps/api and apps/editor had configs without include/exclude guards.
No-test workspaces (template-model, template-renderer, preview) do not run Vitest; they are handled separately below.
What changed
Four new configs added:
| Package | Status |
|---|---|
packages/chart-renderer/vitest.config.ts | New |
packages/data-adapter/vitest.config.ts | New |
packages/html-renderer/vitest.config.ts | New |
packages/schema-validator/vitest.config.ts | New |
Three existing configs updated:
| Package | Change |
|---|---|
packages/pdf-renderer/vitest.config.ts | Added include/exclude (previously had testTimeout only) |
apps/api/vitest.config.ts | Added include/exclude |
apps/editor/vitest.config.ts | Added include/exclude; added **/e2e/** to exclude |
All seven configs now use:
include: ['src/**/*.test.ts', 'src/**/*.test.tsx'],
exclude: ['**/node_modules/**', '**/dist/**'],
No-test workspace cleanup
Background
packages/template-model, packages/template-renderer, and apps/preview had no test or lint scripts at v0.9.0. When Turbo ran turbo run test or turbo run lint across the monorepo, these workspaces showed Command=<NONEXISTENT> in dry-run output and were skipped silently.
What changed
All three workspaces gained explicit scripts:
"test": "node -e \"console.log('No tests in this workspace')\"",
"lint": "eslint src"
They now participate cleanly in turbo run test and turbo run lint with a predictable, zero-exit-code result.
CI — dedicated E2E job
Background
The Playwright E2E suite was documented in docs/release-checklist.md as a manual verification step. There was no CI enforcement — Playwright could not be run in the ci job because it does not share the same job environment.
What changed
A test-e2e job has been added to .github/workflows/ci.yml. It runs on ubuntu-latest independently of the ci job (no PostgreSQL service needed). Steps:
- Checkout, pnpm setup, Node 22,
pnpm install pnpm --filter @pulp-engine/editor exec playwright install --with-deps chromiumpnpm --filter @pulp-engine/editor test:e2e- On failure: upload
apps/editor/playwright-report/as theplaywright-reportartifact (7-day retention)
New files
| File | Purpose |
|---|---|
eslint.config.mjs | ESLint 9 flat config — shared root config for all workspaces |
packages/chart-renderer/vitest.config.ts | Explicit Vitest config with src/** include and dist/** exclude |
packages/data-adapter/vitest.config.ts | Same as above |
packages/html-renderer/vitest.config.ts | Same as above |
packages/schema-validator/vitest.config.ts | Same as above |
Modified files
| File | Change |
|---|---|
.github/workflows/ci.yml | Added Lint step to ci job; added test-e2e job with Playwright install and artifact upload |
turbo.json | Corrected inputs arrays for build, typecheck, test, and lint tasks |
package.json | Added ESLint devDependencies; bumped turbo 2.8.17 → 2.8.20 |
apps/api/vitest.config.ts | Added include/exclude |
apps/editor/vitest.config.ts | Added include/exclude; added e2e/** to exclude |
packages/pdf-renderer/vitest.config.ts | Added include/exclude to existing config (previously testTimeout only) |
apps/preview/package.json | Added test (no-op) and lint scripts |
packages/template-model/package.json | Added test (no-op) and lint scripts |
packages/template-renderer/package.json | Added test (no-op) and lint scripts |
README.md | Added pnpm lint to command reference table |
Behaviour changes / migration notes
Lint is now a required CI gate
pnpm lint runs as a step in the ci job before Build and Test. A lint error blocks the entire CI run. Warnings are permitted — only errors fail the gate. Run pnpm lint locally before pushing.
_-prefix convention is enforced
Variables, function arguments, caught errors, and destructured array slots prefixed with _ are the only permitted form of intentionally-unused identifier. A raw unused identifier (no _ prefix) produces a lint error. Rename any intentionally-ignored identifier to use the _ prefix.
No-test workspaces no longer NONEXISTENT in Turbo output
turbo run test --dry-run and turbo run lint --dry-run now show template-model, template-renderer, and preview as valid tasks with a predictable result rather than Command=<NONEXISTENT>.
No API, schema, or data-format changes
This release contains no changes to the HTTP API, template schema, rendering output, or stored data formats. No migration is required.