Pulp Engine Document Rendering
Get started
Release v0.10.0

Pulp Engine v0.10.0

Release date: 2026-03-22

Highlights

  • pnpm lint is now a hard-fail gate in CI — a lint error blocks the run; warnings are permitted.
  • Turborepo cache inputs corrected for build, typecheck, test, and lint tasks — stale-cache false-passes are eliminated.
  • Vitest configs standardised across all test-bearing workspaces with explicit src/** discovery and dist/ excluded.
  • No-test workspaces (template-model, template-renderer, preview) now participate cleanly in turbo run test and turbo run lint — no more Command=<NONEXISTENT> in Turbo dry-run output.
  • CI gains a dedicated test-e2e job; 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/js recommended rules
  • typescript-eslint recommended rules
  • eslint-plugin-react-hooks recommended-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

TaskProblemFix
buildMissing src/**/*.tsx, src/**/*.css, vite.config.ts, index.htmlAll four added to inputs
typecheckMissing src/**/*.tsxAdded to inputs
testtest/**/*.ts matched a non-existent directory; vitest.config.ts alone was insufficientReplaced with src/**/*.ts, src/**/*.tsx, assets/**, vitest.file.config.ts, package.json
lint.eslintrc* (always matched nothing); missing src/**/*.tsx and the shared config fileReplaced 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:

PackageStatus
packages/chart-renderer/vitest.config.tsNew
packages/data-adapter/vitest.config.tsNew
packages/html-renderer/vitest.config.tsNew
packages/schema-validator/vitest.config.tsNew

Three existing configs updated:

PackageChange
packages/pdf-renderer/vitest.config.tsAdded include/exclude (previously had testTimeout only)
apps/api/vitest.config.tsAdded include/exclude
apps/editor/vitest.config.tsAdded 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:

  1. Checkout, pnpm setup, Node 22, pnpm install
  2. pnpm --filter @pulp-engine/editor exec playwright install --with-deps chromium
  3. pnpm --filter @pulp-engine/editor test:e2e
  4. On failure: upload apps/editor/playwright-report/ as the playwright-report artifact (7-day retention)

New files

FilePurpose
eslint.config.mjsESLint 9 flat config — shared root config for all workspaces
packages/chart-renderer/vitest.config.tsExplicit Vitest config with src/** include and dist/** exclude
packages/data-adapter/vitest.config.tsSame as above
packages/html-renderer/vitest.config.tsSame as above
packages/schema-validator/vitest.config.tsSame as above

Modified files

FileChange
.github/workflows/ci.ymlAdded Lint step to ci job; added test-e2e job with Playwright install and artifact upload
turbo.jsonCorrected inputs arrays for build, typecheck, test, and lint tasks
package.jsonAdded ESLint devDependencies; bumped turbo 2.8.17 → 2.8.20
apps/api/vitest.config.tsAdded include/exclude
apps/editor/vitest.config.tsAdded include/exclude; added e2e/** to exclude
packages/pdf-renderer/vitest.config.tsAdded include/exclude to existing config (previously testTimeout only)
apps/preview/package.jsonAdded test (no-op) and lint scripts
packages/template-model/package.jsonAdded test (no-op) and lint scripts
packages/template-renderer/package.jsonAdded test (no-op) and lint scripts
README.mdAdded 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.