mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
# Description of Changes Smaller scope than #6689 to try and get this finished. Replace ESLint and dpdm with Oxlint, a TS linter written in Rust so its performance is dramatically better than the existing tools we use. ## Speed improvement - Current ESLint run: 13.76s - Current dpdm run: 3.59s - Total time: 17.35s - New Oxlint run: 0.90s So Oxlint is about a 20x speed improvement. ## Differences When I last tried to do this, we could recreate our rules identically with Oxlint, but that's not true any more. Oxlint has no current equivalent for ESLint's `no-restricted-syntax` rule, which we were using to ban usages of `<button>` and stuff in specific components to try and encourage them to use our shared UI. This is a very recent addition to our linting config, and personally I'm willing to drop it for now at least. We can still ban specific imports in files, so the files which we were trying to enforce shared UI will still ban directly importing Mantine, so that'll probably be most of the cases still caught, but I think there are other ways we can encourage using the shared UI beyond just using the linter for it. I did try building a custom TS rule for it and it only slowed it down a tiny bit (it took 1.1s) but it had to be built on an unreleased alpha API which just sounds like a maintenance headache we don't need to deal with for a rule that we don't really need.
523 lines
16 KiB
YAML
523 lines
16 KiB
YAML
version: '3'
|
|
|
|
# Tasks operate from the workspace root (frontend/). Editor commands pass
|
|
# `editor` as the vite project root (positional after `build` / before the
|
|
# mode flag) or use `--project editor/...` for tsc — so the editor lives
|
|
# under frontend/editor/ without each task needing a cd.
|
|
|
|
tasks:
|
|
install:
|
|
desc: "Install dependencies"
|
|
run: once
|
|
cmds:
|
|
- '{{ if eq .CI "true" }}npm ci{{ else }}npm install{{ end }}'
|
|
sources:
|
|
- package-lock.json
|
|
- package.json
|
|
status:
|
|
- test -d node_modules
|
|
env:
|
|
CI: '{{ .CI | default "false" }}'
|
|
|
|
prepare:env:
|
|
internal: true
|
|
run: when_changed
|
|
deps: [install]
|
|
vars:
|
|
MODE: '{{.MODE | default ""}}'
|
|
cmds:
|
|
- npx tsx editor/scripts/setup-env.mts{{if .MODE}} --{{.MODE}}{{end}}
|
|
sources:
|
|
- editor/scripts/setup-env.mts
|
|
generates:
|
|
- editor/.env.local
|
|
- editor/.env{{if .MODE}}.{{.MODE}}{{end}}.local
|
|
|
|
prepare:icons:
|
|
internal: true
|
|
run: once
|
|
deps: [install]
|
|
cmds:
|
|
- node editor/scripts/generate-icons.js
|
|
|
|
prepare:og:
|
|
internal: true
|
|
run: when_changed
|
|
desc: "Regenerate OG/social-preview metadata from the tool registry"
|
|
cmds:
|
|
- node editor/scripts/generate-og-metadata.mjs
|
|
sources:
|
|
- editor/src/core/types/toolId.ts
|
|
- editor/src/core/utils/urlMapping.ts
|
|
- editor/src/core/data/useTranslatedToolRegistry.tsx
|
|
- editor/public/og_images/*.png
|
|
generates:
|
|
- editor/src/core/data/ogImageMap.json
|
|
- editor/public/og-metadata.json
|
|
|
|
prepare:
|
|
desc: "Set up dev environment"
|
|
run: when_changed
|
|
vars:
|
|
MODE: '{{.MODE | default ""}}'
|
|
deps:
|
|
- task: prepare:env
|
|
vars: { MODE: '{{.MODE}}' }
|
|
- prepare:icons
|
|
- prepare:og
|
|
|
|
# ============================================================
|
|
# Development
|
|
# ============================================================
|
|
|
|
dev:_run:
|
|
internal: true
|
|
ignore_error: true
|
|
vars:
|
|
MODE: '{{.MODE}}'
|
|
PORT: '{{.PORT | default "5173"}}'
|
|
BACKEND_URL: '{{.BACKEND_URL | default "http://localhost:8080"}}'
|
|
OPEN: '{{.OPEN | default ""}}'
|
|
env:
|
|
BACKEND_URL: '{{.BACKEND_URL}}'
|
|
# Dev-only browser-tab label so concurrent worktrees are distinguishable.
|
|
# Only the worktree folder basename (e.g. "wt1") is exposed — never the
|
|
# full path, hostname, or user. Consumed at dev-serve time by vite.config
|
|
# and dropped from production builds.
|
|
STIRLING_DEV_LABEL:
|
|
sh: >-
|
|
{{if eq OS "windows"}}powershell -NoProfile -Command '$root = git rev-parse --show-toplevel 2>$null; if (-not $root) { $root = (Get-Location).Path }; Split-Path -Leaf $root'{{else}}basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"{{end}}
|
|
cmds:
|
|
- npx vite editor --mode {{.MODE}} --port {{.PORT}}{{if .OPEN}} --open{{end}}
|
|
|
|
dev:
|
|
desc: "Start frontend dev server"
|
|
cmds:
|
|
- task: dev:proprietary
|
|
vars: { PORT: '{{.PORT}}', BACKEND_URL: '{{.BACKEND_URL}}', OPEN: '{{.OPEN}}' }
|
|
|
|
dev:core:
|
|
desc: "Start frontend dev server in core mode"
|
|
deps: [prepare]
|
|
cmds:
|
|
- task: dev:_run
|
|
vars: { MODE: core, PORT: '{{.PORT}}', BACKEND_URL: '{{.BACKEND_URL}}', OPEN: '{{.OPEN}}' }
|
|
|
|
dev:proprietary:
|
|
desc: "Start frontend dev server in proprietary mode"
|
|
deps: [prepare]
|
|
cmds:
|
|
- task: dev:_run
|
|
vars: { MODE: proprietary, PORT: '{{.PORT}}', BACKEND_URL: '{{.BACKEND_URL}}', OPEN: '{{.OPEN}}' }
|
|
|
|
dev:saas:
|
|
desc: "Start frontend dev server in SaaS mode"
|
|
deps:
|
|
- task: prepare
|
|
vars: { MODE: saas }
|
|
cmds:
|
|
- task: dev:_run
|
|
vars: { MODE: saas, PORT: '{{.PORT}}', BACKEND_URL: '{{.BACKEND_URL}}', OPEN: '{{.OPEN}}' }
|
|
|
|
dev:desktop:
|
|
desc: "Start frontend dev server in desktop mode"
|
|
deps:
|
|
- task: prepare
|
|
vars: { MODE: desktop }
|
|
cmds:
|
|
- task: dev:_run
|
|
vars: { MODE: desktop, PORT: '{{.PORT}}', BACKEND_URL: '{{.BACKEND_URL}}', OPEN: '{{.OPEN}}' }
|
|
|
|
dev:prototypes:
|
|
desc: "Start frontend dev server in prototypes mode"
|
|
deps: [prepare]
|
|
cmds:
|
|
- task: dev:_run
|
|
vars: { MODE: prototypes, PORT: '{{.PORT}}', BACKEND_URL: '{{.BACKEND_URL}}', OPEN: '{{.OPEN}}' }
|
|
|
|
# ============================================================
|
|
# Build
|
|
# ============================================================
|
|
|
|
build:
|
|
desc: "Production build (default mode)"
|
|
deps: [prepare]
|
|
cmds:
|
|
- npx vite build editor
|
|
|
|
build:core:
|
|
desc: "Build for core mode"
|
|
deps: [prepare]
|
|
cmds:
|
|
- npx vite build editor --mode core
|
|
|
|
build:proprietary:
|
|
desc: "Build for proprietary mode"
|
|
deps: [prepare]
|
|
vars:
|
|
PREVIEW: '{{.PREVIEW | default ""}}'
|
|
cmds:
|
|
- '{{if .PREVIEW}}VITE_BUILD_FOR_PREVIEW=1 {{end}}npx vite build editor --mode proprietary'
|
|
|
|
build:saas:
|
|
desc: "Build for SaaS mode"
|
|
deps:
|
|
- task: prepare
|
|
vars: { MODE: saas }
|
|
cmds:
|
|
- npx vite build editor --mode saas
|
|
|
|
build:desktop:
|
|
desc: "Build for desktop mode"
|
|
deps:
|
|
- task: prepare
|
|
vars: { MODE: desktop }
|
|
cmds:
|
|
- npx vite build editor --mode desktop
|
|
|
|
build:prototypes:
|
|
desc: "Build for prototypes mode"
|
|
deps: [prepare]
|
|
cmds:
|
|
- npx vite build editor --mode prototypes
|
|
|
|
|
|
storybook:
|
|
desc: "Start Storybook dev server"
|
|
deps: [prepare]
|
|
cmds:
|
|
- npx storybook dev -p 6006 {{.CLI_ARGS}}
|
|
|
|
storybook:build:
|
|
desc: "Build static Storybook"
|
|
deps: [prepare]
|
|
cmds:
|
|
- npx storybook build {{.CLI_ARGS}}
|
|
|
|
storybook:browser:
|
|
internal: true
|
|
desc: "Install the Chromium build the story scan runs in"
|
|
run: once
|
|
deps: [install]
|
|
cmds:
|
|
- npx playwright install chromium
|
|
|
|
storybook:test:
|
|
desc: "Scan every story in real Chromium: it must render and pass axe"
|
|
deps: [prepare, storybook:browser]
|
|
cmds:
|
|
# Runs each story as a browser test. Pass a filter through, e.g.
|
|
# task frontend:storybook:test -- Button
|
|
- npx vitest run --config .storybook/vitest.config.ts {{.CLI_ARGS}}
|
|
|
|
storybook:a11y:
|
|
desc: "a11y regression gate over every story: fail only on NEW axe violations"
|
|
deps: [prepare, storybook:browser]
|
|
cmds:
|
|
- node .storybook/a11y-scan.mjs
|
|
- node .storybook/a11y-check.mjs --in .a11y-scan --manifest .a11y-scan/manifest.txt
|
|
|
|
storybook:a11y:changed:
|
|
desc: "a11y gate over the stories this branch affects (default base origin/main)"
|
|
summary: |
|
|
Scans the stories a branch affects, which is what pull requests run — a
|
|
full scan takes ~30 minutes, far too long to sit in front of every merge.
|
|
A story is affected if its file changed, or if a same-named sibling
|
|
source file changed (editing Button.tsx or Button.css re-scans
|
|
Button.stories.tsx — the story renders the live component, so a component
|
|
edit changes what the story shows without touching the story file).
|
|
Changes that ripple further than a component's own stories are covered by
|
|
the nightly full sweep.
|
|
|
|
Pass a base ref through CLI_ARGS, e.g.
|
|
task frontend:storybook:a11y:changed -- origin/release
|
|
deps: [prepare, storybook:browser]
|
|
vars:
|
|
BASE: '{{.CLI_ARGS | default "origin/main"}}'
|
|
CHANGED:
|
|
sh: node .storybook/a11y-changed.mjs {{.CLI_ARGS | default "origin/main"}}
|
|
cmds:
|
|
- cmd: |
|
|
if [ -z '{{.CHANGED}}' ]; then
|
|
echo "a11y: no story files affected vs {{.BASE}} — nothing to check"
|
|
exit 0
|
|
fi
|
|
node .storybook/a11y-scan.mjs {{.CHANGED}}
|
|
node .storybook/a11y-check.mjs --in .a11y-scan --manifest .a11y-scan/manifest.txt
|
|
|
|
storybook:a11y:record:
|
|
desc: "Re-record the a11y baseline (run after intentionally fixing/adding violations)"
|
|
deps: [prepare, storybook:browser]
|
|
cmds:
|
|
- node .storybook/a11y-scan.mjs
|
|
- node .storybook/a11y-check.mjs --in .a11y-scan --manifest .a11y-scan/manifest.txt --record
|
|
|
|
# ============================================================
|
|
# Code quality
|
|
# ============================================================
|
|
|
|
lint:
|
|
desc: "Run linting"
|
|
deps: [install]
|
|
cmds:
|
|
- task: lint:oxlint
|
|
- task: lint:colors
|
|
- task: lint:css
|
|
|
|
lint:css:
|
|
desc: "Lint stylesheets for duplicate selectors"
|
|
deps: [install]
|
|
cmds:
|
|
# Covers the whole editor tree, including the portal/processor layer and
|
|
# public/css. Vendored CSS and build output are excluded via ignoreFiles
|
|
# in stylelint.config.mjs.
|
|
- npx stylelint "editor/**/*.css"
|
|
|
|
lint:colors:
|
|
desc: "Enforce theme tokens — no hardcoded colours or raw primitives in components"
|
|
aliases: [lint:colours]
|
|
deps: [install]
|
|
cmds:
|
|
- node editor/scripts/lint/theme-lint.mjs
|
|
- node editor/scripts/lint/theme-lint.mjs css-colors
|
|
- node editor/scripts/lint/theme-lint.mjs code-colors
|
|
- node editor/scripts/lint/theme-lint.mjs no-primitives
|
|
|
|
contrast:
|
|
desc: "Report low-contrast theme token pairs (warning only, never blocks)"
|
|
deps: [install]
|
|
cmds:
|
|
- node editor/scripts/lint/theme-lint.mjs contrast
|
|
|
|
lint:oxlint:
|
|
desc: "Run oxlint linting"
|
|
deps: [install]
|
|
cmds:
|
|
- npx oxlint --config oxlint.config.ts --max-warnings=0
|
|
|
|
lint:fix:
|
|
desc: "Auto-fix lint issues"
|
|
deps: [install]
|
|
cmds:
|
|
- npx oxlint --config oxlint.config.ts --fix
|
|
|
|
format:
|
|
desc: "Auto-fix code formatting"
|
|
deps: [install]
|
|
cmds:
|
|
- npx prettier --write .
|
|
|
|
format:check:
|
|
desc: "Check code formatting"
|
|
deps: [install]
|
|
cmds:
|
|
- npx prettier --check .
|
|
|
|
fix:
|
|
desc: "Auto-fix lint and format"
|
|
cmds:
|
|
- task: format
|
|
- task: lint:fix
|
|
|
|
typecheck:
|
|
desc: "Typecheck default build of the app"
|
|
cmds:
|
|
- task: typecheck:proprietary
|
|
|
|
typecheck:_run:
|
|
internal: true
|
|
cmds:
|
|
- 'npx tsc --noEmit --project {{.PROJECT}}'
|
|
|
|
typecheck:core:
|
|
desc: "Typecheck core build variant"
|
|
deps: [prepare]
|
|
cmds:
|
|
- task: typecheck:_run
|
|
vars: { PROJECT: editor/src/core/tsconfig.json }
|
|
|
|
typecheck:proprietary:
|
|
desc: "Typecheck proprietary build variant"
|
|
deps: [prepare]
|
|
cmds:
|
|
- task: typecheck:_run
|
|
vars: { PROJECT: editor/src/proprietary/tsconfig.json }
|
|
|
|
typecheck:saas:
|
|
desc: "Typecheck SaaS build variant"
|
|
deps:
|
|
- task: prepare
|
|
vars: { MODE: saas }
|
|
cmds:
|
|
- task: typecheck:_run
|
|
vars: { PROJECT: editor/src/saas/tsconfig.json }
|
|
|
|
typecheck:desktop:
|
|
desc: "Typecheck desktop build variant"
|
|
deps:
|
|
- task: prepare
|
|
vars: { MODE: desktop }
|
|
cmds:
|
|
- task: typecheck:_run
|
|
vars: { PROJECT: editor/src/desktop/tsconfig.json }
|
|
|
|
typecheck:cloud:
|
|
desc: "Typecheck cloud shared layer (standalone)"
|
|
deps: [prepare]
|
|
cmds:
|
|
- task: typecheck:_run
|
|
vars: { PROJECT: editor/src/cloud/tsconfig.json }
|
|
|
|
typecheck:scripts:
|
|
desc: "Typecheck scripts"
|
|
deps: [prepare]
|
|
cmds:
|
|
- task: typecheck:_run
|
|
vars: { PROJECT: editor/scripts/tsconfig.json }
|
|
|
|
typecheck:prototypes:
|
|
desc: "Typecheck prototypes build variant"
|
|
deps: [prepare]
|
|
cmds:
|
|
- task: typecheck:_run
|
|
vars: { PROJECT: editor/src/prototypes/tsconfig.json }
|
|
|
|
typecheck:portal:
|
|
desc: "Typecheck developer portal build variant"
|
|
deps: [install]
|
|
cmds:
|
|
- task: typecheck:_run
|
|
vars: { PROJECT: editor/src/portal/tsconfig.json }
|
|
|
|
typecheck:storybook:
|
|
desc: "Typecheck Storybook config and stories"
|
|
deps: [prepare]
|
|
cmds:
|
|
- task: typecheck:_run
|
|
vars: { PROJECT: .storybook/tsconfig.json }
|
|
|
|
typecheck:all:
|
|
desc: "Typecheck all build variants"
|
|
cmds:
|
|
- task: typecheck:core
|
|
- task: typecheck:proprietary
|
|
- task: typecheck:saas
|
|
- task: typecheck:desktop
|
|
- task: typecheck:cloud
|
|
- task: typecheck:scripts
|
|
- task: typecheck:prototypes
|
|
- task: typecheck:portal
|
|
- task: typecheck:storybook
|
|
|
|
# ============================================================
|
|
# Quality Gate
|
|
# ============================================================
|
|
|
|
check:
|
|
desc: "Quick quality gate for local development"
|
|
cmds:
|
|
- task: typecheck
|
|
- task: lint
|
|
- task: format:check
|
|
- task: test
|
|
|
|
og:check:
|
|
desc: "Fail if committed OG/social-preview metadata is out of date"
|
|
cmds:
|
|
- node editor/scripts/generate-og-metadata.mjs --check
|
|
|
|
check:all:
|
|
desc: "Full CI quality gate"
|
|
cmds:
|
|
# Runs first, before prepare regenerates: guards the committed og-metadata.json /
|
|
# ogImageMap.json that the Cloudflare Pages (plain `vite build`) deploy relies on.
|
|
- task: og:check
|
|
- task: typecheck:all
|
|
- task: lint
|
|
- task: format:check
|
|
- task: build
|
|
- task: test
|
|
- task: storybook:build
|
|
|
|
# ============================================================
|
|
# Test
|
|
# ============================================================
|
|
|
|
test:
|
|
desc: "Run tests"
|
|
cmds:
|
|
- task: test:editor
|
|
|
|
test:editor:
|
|
desc: "Run editor tests"
|
|
deps: [prepare]
|
|
cmds:
|
|
- npx vitest run --root editor
|
|
|
|
test:watch:
|
|
desc: "Run tests in watch mode"
|
|
deps: [prepare]
|
|
cmds:
|
|
- npx vitest --watch --root editor
|
|
|
|
test:coverage:
|
|
desc: "Run tests with coverage (one-shot; CI-friendly)."
|
|
deps: [prepare]
|
|
cmds:
|
|
# `vitest run` makes this CI-safe (the bare `vitest` form enters watch
|
|
# mode). Explicit reporter list because v8 + json-summary is what the
|
|
# coverage-summary.py helper consumes; html/text are kept for humans.
|
|
#
|
|
# reportsDirectory is pinned to ./coverage relative to vitest's root
|
|
# (--root editor), so output lands at frontend/editor/coverage/. The
|
|
# CI upload step reads from that path. An earlier attempt with
|
|
# `./editor/coverage` double-nested into frontend/editor/editor/coverage;
|
|
# pinning future-proofs against vitest changing the default.
|
|
- >
|
|
npx vitest run --root editor --coverage
|
|
--coverage.provider=v8
|
|
--coverage.reporter=text-summary
|
|
--coverage.reporter=json-summary
|
|
--coverage.reporter=html
|
|
--coverage.reportsDirectory=./coverage
|
|
|
|
# ============================================================
|
|
# Code Generation
|
|
# ============================================================
|
|
|
|
tool-models:
|
|
desc: "Generate tool API types from the Java OpenAPI spec"
|
|
deps: [install, ":backend:swagger"]
|
|
cmds:
|
|
- npx tsx editor/scripts/generate-tool-api-types.mts --spec ../SwaggerDoc.json --output editor/src/core/types/toolApiTypes.ts --io-output editor/src/core/types/toolIO.ts
|
|
sources:
|
|
- editor/scripts/generate-tool-api-types.mts
|
|
- ../SwaggerDoc.json
|
|
generates:
|
|
- editor/src/core/types/toolApiTypes.ts
|
|
- editor/src/core/types/toolIO.ts
|
|
|
|
tool-models:check:
|
|
desc: "Fail if committed tool API types are out of date"
|
|
deps: [install, ":backend:swagger"]
|
|
cmds:
|
|
- npx tsx editor/scripts/generate-tool-api-types.mts --spec ../SwaggerDoc.json --output editor/src/core/types/toolApiTypes.ts --io-output editor/src/core/types/toolIO.ts --check
|
|
|
|
licenses:generate:
|
|
desc: "Generate frontend license report"
|
|
deps: [install]
|
|
cmds:
|
|
- node editor/scripts/generate-licenses.js
|
|
|
|
# ============================================================
|
|
# Clean
|
|
# ============================================================
|
|
|
|
clean:
|
|
desc: "Clean build artifacts and caches"
|
|
cmds:
|
|
- cmd: powershell rm -Recurse -Force -ErrorAction SilentlyContinue node_modules/.vite, editor/dist, dist
|
|
platforms: [windows]
|
|
- cmd: rm -rf node_modules/.vite editor/dist dist
|
|
platforms: [linux, darwin]
|