mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
# Description of Changes Prettier takes about 10 seconds to run over our frontend folder, but [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html) does an (almost) identical job in 0.2 seconds. This PR converts our Prettier integration to an equivalent Oxfmt integration. There's exactly 2 files in the frontend folder that Oxfmt formats differently to Prettier so it'll barely cause any disruption to the source. I've removed the `--check` option from the frontend tool models generator as part of this because we can do the same thing with Task easily enough and Oxfmt isn't directly importable like Prettier since it's a Rust binary instead of a JS library. Originally I was shelling out to Oxfmt on single-file mode to keep it all in memory but it just seemed more likely that there'd be config mismatches between that script and the task so I think it's better this way.
588 lines
18 KiB
YAML
588 lines
18 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.
|
|
|
|
vars:
|
|
# 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. Dropped from production builds.
|
|
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}}
|
|
|
|
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}}'
|
|
STIRLING_DEV_LABEL: '{{.DEV_LABEL}}'
|
|
cmds:
|
|
- npx vite editor --mode {{.MODE}} --port {{.PORT}}{{if .OPEN}} --open{{end}}
|
|
|
|
# Separate from dev:_run rather than a flag on it: Task sets an `env:` key even
|
|
# when its value resolves to empty, and Vite treats an empty process.env VITE_* as
|
|
# authoritative over the committed editor/.env, so folding these in blanks Supabase
|
|
# config for the core, proprietary and desktop dev servers.
|
|
dev:_run:saas:
|
|
internal: true
|
|
ignore_error: true
|
|
# The backend's own env files, so both halves target one project. Paths are
|
|
# relative to this taskfile's dir, `frontend`.
|
|
dotenv: ['../app/.env.saas.local', '../app/.env.saas']
|
|
vars:
|
|
PORT: '{{.PORT | default "5173"}}'
|
|
BACKEND_URL: '{{.BACKEND_URL | default "http://localhost:8080"}}'
|
|
OPEN: '{{.OPEN | default ""}}'
|
|
SAAS_ENV: '{{.SAAS_ENV | default "dev"}}'
|
|
env:
|
|
BACKEND_URL: '{{.BACKEND_URL}}'
|
|
STIRLING_DEV_LABEL: '{{.DEV_LABEL}}'
|
|
SAAS_ENV: '{{.SAAS_ENV}}'
|
|
# A real process.env VITE_* beats a committed .env in Vite (loadEnv applies
|
|
# process.env last), which is what lets this override editor/.env.
|
|
#
|
|
# These must stay `sh:`, not Go templates: dotenv values are visible to Task's
|
|
# embedded shell but not to templates, where {{.SAAS_DEV_PROJECT_REF}} is
|
|
# always empty.
|
|
VITE_SUPABASE_URL:
|
|
sh: |
|
|
case "${SAAS_ENV:-dev}" in
|
|
staging) ref="${SAAS_STAGING_PROJECT_REF:?set it in app/.env.saas.local}" ;;
|
|
*) ref="${SAAS_DEV_PROJECT_REF:?set it in app/.env.saas.local, or run task staging:saas}" ;;
|
|
esac
|
|
echo "https://${ref}.supabase.co"
|
|
VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY:
|
|
sh: |
|
|
case "${SAAS_ENV:-dev}" in
|
|
staging) echo "${SAAS_STAGING_PUBLISHABLE_KEY:?set it in app/.env.saas.local}" ;;
|
|
*) echo "${SAAS_DEV_PUBLISHABLE_KEY:?set it in app/.env.saas.local}" ;;
|
|
esac
|
|
cmds:
|
|
- 'echo ">> frontend Supabase target: $VITE_SUPABASE_URL"'
|
|
- npx vite editor --mode saas --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 (SAAS_ENV=dev|staging|prod)"
|
|
deps:
|
|
- task: prepare
|
|
vars: { MODE: saas }
|
|
vars:
|
|
SAAS_ENV: '{{.SAAS_ENV | default "dev"}}'
|
|
# prod routes to the plain runner, which sets no VITE_SUPABASE_* and so leaves
|
|
# the committed editor/.env alone.
|
|
RUNNER: '{{if eq .SAAS_ENV "prod"}}dev:_run{{else}}dev:_run:saas{{end}}'
|
|
cmds:
|
|
- task: '{{.RUNNER}}'
|
|
vars:
|
|
MODE: saas
|
|
PORT: '{{.PORT}}'
|
|
BACKEND_URL: '{{.BACKEND_URL}}'
|
|
OPEN: '{{.OPEN}}'
|
|
SAAS_ENV: '{{.SAAS_ENV}}'
|
|
|
|
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:light:
|
|
desc: "a11y gate over every story in light mode"
|
|
deps: [prepare, storybook:browser]
|
|
cmds:
|
|
- node .storybook/a11y-scan.mjs {{.CLI_ARGS}}
|
|
- node .storybook/a11y-check.mjs --in .a11y-scan --manifest .a11y-scan/manifest.txt
|
|
|
|
storybook:a11y:dark:
|
|
desc: "a11y gate over every story in dark mode"
|
|
deps: [prepare, storybook:browser]
|
|
cmds:
|
|
- SCAN_THEME=dark node .storybook/a11y-scan.mjs {{.CLI_ARGS}}
|
|
- node .storybook/a11y-check.mjs --in .a11y-scan --manifest .a11y-scan/manifest.txt --baseline .storybook/a11y-baseline.dark.json
|
|
|
|
storybook:a11y:
|
|
desc: "a11y gate over every story, light and dark"
|
|
cmds:
|
|
- task: storybook:a11y:light
|
|
- task: storybook:a11y:dark
|
|
|
|
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
|
|
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
|
|
rc=0
|
|
task frontend:storybook:a11y:light -- {{.CHANGED}} || rc=1
|
|
task frontend:storybook:a11y:dark -- {{.CHANGED}} || rc=1
|
|
exit $rc
|
|
|
|
storybook:a11y:record:
|
|
desc: "Re-record both a11y baselines (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
|
|
- SCAN_THEME=dark node .storybook/a11y-scan.mjs
|
|
- node .storybook/a11y-check.mjs --in .a11y-scan --manifest .a11y-scan/manifest.txt --record --baseline .storybook/a11y-baseline.dark.json
|
|
|
|
# ============================================================
|
|
# 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 oxfmt --write .
|
|
|
|
format:check:
|
|
desc: "Check code formatting"
|
|
deps: [install]
|
|
cmds:
|
|
- npx oxfmt --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]
|
|
vars:
|
|
COVERAGE: '{{.COVERAGE | default .CI | default "false"}}'
|
|
cmds:
|
|
- >
|
|
npx vitest run --root editor
|
|
{{if eq .COVERAGE "true"}}--coverage
|
|
--coverage.provider=v8
|
|
--coverage.reporter=text-summary
|
|
--coverage.reporter=json-summary
|
|
--coverage.reporter=html
|
|
--coverage.reportsDirectory=./coverage{{end}}
|
|
|
|
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)."
|
|
cmds:
|
|
- task: test:editor
|
|
vars: { COVERAGE: "true" }
|
|
|
|
# ============================================================
|
|
# 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
|
|
- task: format
|
|
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"
|
|
cmds:
|
|
- task: tool-models
|
|
- git diff --exit-code -- editor/src/core/types/toolApiTypes.ts editor/src/core/types/toolIO.ts
|
|
|
|
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]
|