From dbd60d47659e6042eee86b11177e319354b4c1f5 Mon Sep 17 00:00:00 2001 From: James Brunton Date: Mon, 24 Aug 2026 10:34:12 +0000 Subject: [PATCH] Replace Prettier with Oxfmt (#7422) # 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. --- .editorconfig | 15 +- .taskfiles/frontend.yml | 9 +- frontend/.oxfmtrc.json | 33 ++ frontend/.prettierignore | 30 -- frontend/.prettierrc | 6 - frontend/README.md | 2 +- .../scripts/generate-tool-api-types.mts | 62 +-- .../users/UsersDirectory.stories.tsx | 29 +- .../shared/ChangeUserPasswordModal.tsx | 2 +- frontend/package-lock.json | 387 +++++++++++++++++- frontend/package.json | 2 +- frontend/stylelint.config.mjs | 2 +- 12 files changed, 453 insertions(+), 126 deletions(-) create mode 100644 frontend/.oxfmtrc.json delete mode 100644 frontend/.prettierignore delete mode 100644 frontend/.prettierrc diff --git a/.editorconfig b/.editorconfig index 665a74a09a..e6bda814c1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -22,26 +22,15 @@ indent_size = 4 [*.html] indent_size = 2 -insert_final_newline = false -trim_trailing_whitespace = false -[{*.js,*.jsx,*.mjs,*.ts,*.tsx}] +[{*.js,*.jsx,*.mjs,*.ts,*.tsx,*.mts}] indent_size = 2 [*.css] -# CSS files typically use an indent size of 2 spaces for better readability and alignment with community standards. indent_size = 2 [*.{yml,yaml}] -# YAML files use an indent size of 2 spaces to maintain consistency with common YAML formatting practices. -indent_size = 2 -insert_final_newline = false -trim_trailing_whitespace = false - -[*.json] -# JSON files use an indent size of 2 spaces, which is the standard for JSON formatting. indent_size = 2 -[*.jsonc] -# JSONC (JSON with comments) files also follow the standard JSON formatting with an indent size of 2 spaces. +[*.{json,jsonc}] indent_size = 2 diff --git a/.taskfiles/frontend.yml b/.taskfiles/frontend.yml index f5325c9ed7..844306824d 100644 --- a/.taskfiles/frontend.yml +++ b/.taskfiles/frontend.yml @@ -375,13 +375,13 @@ tasks: desc: "Auto-fix code formatting" deps: [install] cmds: - - npx prettier --write . + - npx oxfmt --write . format:check: desc: "Check code formatting" deps: [install] cmds: - - npx prettier --check . + - npx oxfmt --check . fix: desc: "Auto-fix lint and format" @@ -554,6 +554,7 @@ tasks: 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 @@ -563,9 +564,9 @@ tasks: 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 + - 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" diff --git a/frontend/.oxfmtrc.json b/frontend/.oxfmtrc.json new file mode 100644 index 0000000000..cb801651b8 --- /dev/null +++ b/frontend/.oxfmtrc.json @@ -0,0 +1,33 @@ +{ + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "endOfLine": "lf", + "sortPackageJson": false, + "ignorePatterns": [ + "dist/", + "editor/dist/", + "editor/src-tauri/**/target/", + "editor/src-tauri/gen/", + "node_modules/", + "editor/public/vendor/", + "editor/public/mockServiceWorker.js", + "editor/public/og-metadata.json", + "editor/public/og-metadata.saas.json", + "editor/src/core/data/ogImageMap.json", + "editor/src/portal/generated/docsManifest.json", + "editor/public/pdfjs*/", + "editor/public/js/thirdParty/", + "editor/public/css/cookieconsent.css", + "storybook-static/", + "playwright-report/", + "editor/playwright-report/", + "test-results/", + "editor/test-results/", + "*.min.*", + "*.md", + "*.wxs", + "*.toml", + "editor/src/output.css" + ] +} diff --git a/frontend/.prettierignore b/frontend/.prettierignore deleted file mode 100644 index fa8ab6c502..0000000000 --- a/frontend/.prettierignore +++ /dev/null @@ -1,30 +0,0 @@ -dist/ -editor/dist/ -# Tauri/Cargo build output (binary assets named *.js etc. confuse Prettier). -# Match nested target/ dirs too - provisioner/ and thumbnail-handler/ each -# have their own Cargo workspace under src-tauri/. -editor/src-tauri/**/target/ -editor/src-tauri/gen/ -node_modules/ -editor/public/vendor/ -# Auto-generated by MSW (`msw init`); regenerated verbatim, not hand-formatted. -editor/public/mockServiceWorker.js -# Auto-generated OG/social-preview metadata (scripts/generate-og-metadata.mjs); regenerated verbatim. -editor/public/og-metadata.json -editor/public/og-metadata.saas.json -editor/src/core/data/ogImageMap.json -# Auto-generated portal docs manifest (scripts/sync-portal-docs.mts); regenerated verbatim. -editor/src/portal/generated/docsManifest.json -editor/public/pdfjs*/ -editor/public/js/thirdParty/ -editor/public/css/cookieconsent.css -# Build / test artifacts that may exist locally even though they're gitignored -storybook-static/ -playwright-report/ -editor/playwright-report/ -test-results/ -editor/test-results/ -*.min.* -*.md -*.wxs -editor/src/output.css diff --git a/frontend/.prettierrc b/frontend/.prettierrc deleted file mode 100644 index 58bc875631..0000000000 --- a/frontend/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "printWidth": 80, - "tabWidth": 2, - "useTabs": false, - "endOfLine": "lf" -} diff --git a/frontend/README.md b/frontend/README.md index ea759538e6..a4945ffae1 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -18,7 +18,7 @@ For desktop app development, see the [Tauri](#tauri) section below. `frontend/` is a workspace containing one or more apps. Today it holds the PDF editor under `frontend/editor/`; new apps (the developer portal, etc.) will sit alongside it as siblings. Shared tooling — `package.json`, `node_modules`, -`.storybook/`, oxlint, Prettier — lives at `frontend/` so every app installs +`.storybook/`, oxlint, oxfmt — lives at `frontend/` so every app installs once and lints with the same config. ## Environment Variables diff --git a/frontend/editor/scripts/generate-tool-api-types.mts b/frontend/editor/scripts/generate-tool-api-types.mts index 38eb7fe7b6..23daa1dd76 100644 --- a/frontend/editor/scripts/generate-tool-api-types.mts +++ b/frontend/editor/scripts/generate-tool-api-types.mts @@ -10,7 +10,6 @@ import { readFileSync, writeFileSync, mkdirSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { parseArgs } from "node:util"; import { compile, type JSONSchema } from "json-schema-to-typescript"; -import * as prettier from "prettier"; // The API namespaces whose endpoints a pipeline can reference. `/api/v1/ai/tools/` // is absent from the spec, so it cannot appear here. Extend this list when other @@ -227,11 +226,7 @@ function collectToolIO( return { table, dropped }; } -async function renderToolIO( - spec: Json, - table: Record, - outputPath: string, -): Promise { +function renderToolIO(spec: Json, table: Record): string { if (Object.keys(table).length === 0) { throw new Error( `No ${IO_EXTENSION} declarations in the spec. The backend publishes these from @ToolIO; regenerate with 'task backend:swagger'.`, @@ -312,33 +307,12 @@ export function toolIOFor( } `; - const prettierConfig = await prettier.resolveConfig(outputPath); - return prettier.format(body, { ...prettierConfig, parser: "typescript" }); + return body; } -/** In check mode, fail when the committed file is out of date. */ -function writeOrCheck( - outputPath: string, - formatted: string, - check: boolean, - task: string, -): void { - if (check) { - let current = ""; - try { - current = readFileSync(outputPath, "utf-8"); - } catch { - // Missing file counts as out of date. - } - if (current !== formatted) { - throw new Error( - `${outputPath} is out of date. Run '${task}' and commit the result.`, - ); - } - return; - } +function writeOutput(outputPath: string, contents: string): void { mkdirSync(dirname(outputPath), { recursive: true }); - writeFileSync(outputPath, formatted, "utf-8"); + writeFileSync(outputPath, contents, "utf-8"); } async function main(): Promise { @@ -347,12 +321,11 @@ async function main(): Promise { spec: { type: "string" }, output: { type: "string" }, "io-output": { type: "string" }, - check: { type: "boolean", default: false }, }, }); if (!values.spec || !values.output || !values["io-output"]) { throw new Error( - "Usage: generate-tool-api-types.mts --spec --output --io-output [--check]", + "Usage: generate-tool-api-types.mts --spec --output --io-output ", ); } const specPath = resolve(values.spec); @@ -478,14 +451,9 @@ async function main(): Promise { `Dropped ${dropped.length} @ToolIO declaration(s) on paths that are not tool endpoints. Add the namespace to ALLOWED_PATH_PREFIXES if a pipeline can contain these steps:\n ${dropped.join("\n ")}`, ); } - writeOrCheck( - ioOutputPath, - await renderToolIO(spec, ioDeclarations, ioOutputPath), - values.check ?? false, - "task frontend:tool-models", - ); + writeOutput(ioOutputPath, renderToolIO(spec, ioDeclarations)); console.log( - `${values.check ? "Up to date" : "Generated"}: ${Object.keys(ioDeclarations).length} tool I/O declarations.`, + `Generated ${Object.keys(ioDeclarations).length} tool I/O declarations.`, ); // Transitively inline every referenced component into `definitions`, rewriting its refs too. @@ -508,7 +476,6 @@ async function main(): Promise { definitions, fileFieldsByClass, outputPath, - values.check ?? false, skipped, ); } @@ -518,7 +485,6 @@ async function compileAndWrite( definitions: Record, fileFieldsByClass: Record, outputPath: string, - check: boolean, skipped: string[], ): Promise { // json-schema-to-typescript only emits a named, exported interface per schema @@ -597,17 +563,9 @@ async function compileAndWrite( ].join("\n"); const body = `${FILE_HEADER}\n\n${models}\n\n${footer}\n`; - const prettierConfig = await prettier.resolveConfig(outputPath); - const formatted = await prettier.format(body, { - ...prettierConfig, - parser: "typescript", - }); - - writeOrCheck(outputPath, formatted, check, "task frontend:tool-models"); - console.log( - `${check ? "Up to date" : "Generated"}: ${tools.length} tool endpoints.`, - ); - if (!check && skipped.length > 0) { + writeOutput(outputPath, body); + console.log(`Generated ${tools.length} tool endpoints.`); + if (skipped.length > 0) { console.log( `Skipped ${skipped.length} POST endpoint(s) with no request body: ${skipped.join(", ")}`, ); diff --git a/frontend/editor/src/portal/components/users/UsersDirectory.stories.tsx b/frontend/editor/src/portal/components/users/UsersDirectory.stories.tsx index 2925958efd..75da086806 100644 --- a/frontend/editor/src/portal/components/users/UsersDirectory.stories.tsx +++ b/frontend/editor/src/portal/components/users/UsersDirectory.stories.tsx @@ -215,22 +215,19 @@ const BIG_MEMBERS: Member[] = [ isSelf: true, portalAccess: "admin", }, - ...Array.from( - { length: 11 }, - (_, i): Member => ({ - id: `big-${i}`, - name: `Teammate ${i + 1}`, - email: `teammate${i + 1}@acme.com`, - role: i === 0 ? "team_owner" : "member", - status: "active", - lastActive: `${i + 1}h ago`, - username: `tm${i + 1}`, - teamId: 9, - teamName: "Platform", - teamLead: i === 0, - portalAccess: i === 0 ? "role" : "none", - }), - ), + ...Array.from({ length: 11 }, (_, i): Member => ({ + id: `big-${i}`, + name: `Teammate ${i + 1}`, + email: `teammate${i + 1}@acme.com`, + role: i === 0 ? "team_owner" : "member", + status: "active", + lastActive: `${i + 1}h ago`, + username: `tm${i + 1}`, + teamId: 9, + teamName: "Platform", + teamLead: i === 0, + portalAccess: i === 0 ? "role" : "none", + })), ]; const TEAMS: Team[] = [ diff --git a/frontend/editor/src/proprietary/components/shared/ChangeUserPasswordModal.tsx b/frontend/editor/src/proprietary/components/shared/ChangeUserPasswordModal.tsx index 89d648fcc5..a482b3fab1 100644 --- a/frontend/editor/src/proprietary/components/shared/ChangeUserPasswordModal.tsx +++ b/frontend/editor/src/proprietary/components/shared/ChangeUserPasswordModal.tsx @@ -38,7 +38,7 @@ function generateSecurePassword() { const uint8Array = new Uint8Array(length); window.crypto.getRandomValues(uint8Array); // To avoid modulo bias, discard values >= 256 - (256 % charsetLength) - for (let i = 0; password.length < length; ) { + for (let i = 0; password.length < length;) { const randomByte = uint8Array[i]; i++; if (randomByte >= Math.floor(256 / charsetLength) * charsetLength) { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9de0a5bf6a..6ade3d2a28 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -121,12 +121,12 @@ "license-checker": "^25.0.1", "msw": "^2.14.6", "msw-storybook-addon": "^2.0.7", + "oxfmt": "^0.62.0", "oxlint": "^1.77.0", "postcss": "^8.5.12", "postcss-cli": "^11.0.1", "postcss-preset-mantine": "^1.18.0", "postcss-simple-vars": "^7.0.1", - "prettier": "^3.8.1", "puppeteer": "^24.25.0", "rollup-plugin-visualizer": "^7.0.1", "storybook": "^9.1.20", @@ -2905,6 +2905,329 @@ "dev": true, "license": "MIT" }, + "node_modules/@oxfmt/binding-android-arm-eabi": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.62.0.tgz", + "integrity": "sha512-pdsv0C4gPjJ8H1+sd8u0BDx+yLACTL+rgeMIOL1ln4ihSnhw8CWXtYWgvcSkyTfgGBIzFKab+d8rx9Xl4en/Kw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-android-arm64": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.62.0.tgz", + "integrity": "sha512-WC3YQ7uS/KtDrjmqwBviwFKe9qeoi+eXx8aX1z/ffG23Md75myjrJaQqTuJvdOLPoa4EYTjDWH0dHXfwulCVog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-darwin-arm64": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.62.0.tgz", + "integrity": "sha512-GM8Yf3LjjaR1I8PD0SfeoIlwhsh9GvSF+cQ8sf624Yxnjsyumn95aFzYfKJVefblfDIiOAnZ7QVm2sa21Er/0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-darwin-x64": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.62.0.tgz", + "integrity": "sha512-d5THp7F8bCxLqNogEXDORRsQD6dosf3EyFtnXfBer6v+8tGdcWIjoDX9WaXrrF/26zOmL8qHpPTKCEvpBDmZkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-freebsd-x64": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.62.0.tgz", + "integrity": "sha512-1DnrtXGZooOZ0fHgAXZUaDQzBVh1CM2MNW4oBXyQ2aWKvCHjyljvT9fgBkOM0fEOb96X5eqtcfJ0YUVt9jj66g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm-gnueabihf": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.62.0.tgz", + "integrity": "sha512-4pQDHOYRH+Huqe0StIaWyvk2CVl/aTaqSrbZpA3/pLS2xH24ME7lBgYprhQF2fRkHBzhGGGKliwxFsDdHwx59g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm-musleabihf": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.62.0.tgz", + "integrity": "sha512-X0jAaZJFMCVKhB6YyWVTQ/wN2DLsBcZKSMqTS76bF6riT+XZdtg2FPEdjDvdVbunO9cG+tWiVaEs4Zs38lxYog==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm64-gnu": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.62.0.tgz", + "integrity": "sha512-682Z8T5s8T5ATArYtsejKvbIfd8LEAXyyDkKkoZVq8HND7Vx8TYLlrDjDSeYfodMeVwHOgkj13lJYR8cj6vUSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm64-musl": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.62.0.tgz", + "integrity": "sha512-lk25fAl7KWaLWVJcW0CHEXB7QlQZtx5eDkjpaGMK0hzXTjUe0Wmlu8IKuFHoviSOcEJedRTs4VE/506VqGxGew==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-ppc64-gnu": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.62.0.tgz", + "integrity": "sha512-SFyNqHQLwySceWNLhiSldx7wPXRAzP0L0WcW9GegP3uWrpZGJiZlQO85NbHAFPEfxR9PhZ9qSnZryEh7+v+4Gw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-riscv64-gnu": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.62.0.tgz", + "integrity": "sha512-KYj55C1ywJfHo6+aKDuEmUtVEdJALsC5GwayDGsI6FGz2GxFqNr/mA8nxVsNbJzm7sE5MRqTQ9ziImSzhYXysA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-riscv64-musl": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.62.0.tgz", + "integrity": "sha512-BhZDNo5GOU5nC378RhD0/XpvaEBHsH3HLgJp8YZX3A0InC7oivzA63HsRmiXFLtLSHAstEVrDf6fbC7Rs8Jh/A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-s390x-gnu": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.62.0.tgz", + "integrity": "sha512-UyAFmyHkgSgUJ/wOM4p3U8AC2yAFvRH5PNBs7TnK0fObTT/XSWcdr/lAzPSWaekHaZFaMeFZyk9n93Joq3J93A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-x64-gnu": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.62.0.tgz", + "integrity": "sha512-1iYMP0leytWazFubD/WnINJuIrzRPuoL1aWEJdlGezEzDbTxcd29R4r8IUzP2oWeKst5V02uMJgR2NILlPlG6w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-x64-musl": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.62.0.tgz", + "integrity": "sha512-4rA/URtJSTVNVAQz6Q8wf7SaRvOXVy+TizriT9hs/Y1XhLR/R+92uWKRQG8yFWRAIEBbFHJ6WevQcl/G9SXEfw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-openharmony-arm64": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.62.0.tgz", + "integrity": "sha512-mSZuFHU2ar1KLUjXpI2QBQcJ1VsOB3mOCgQXuXCpKs19dgh4u+OaovNfrWDfiJb+ihJ2+f7YFcaO9bS2dlTCXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-arm64-msvc": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.62.0.tgz", + "integrity": "sha512-OfwuhkcjDlqC4EgDojtiV9mzpLqeB9KqTOWPOjLEYBVdDCVSxqW3qzp/xcIxsbtI0UgGCnKvAqYKyY25kf5JZw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-ia32-msvc": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.62.0.tgz", + "integrity": "sha512-P9uDDNFRzghO3X8QAzhkjKhK7JvtABsVn8UYtFX7uor12IAnwNt8nNIctvfWj1JkQU/kE+fmLRPiw7XlrIHsZw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-x64-msvc": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.62.0.tgz", + "integrity": "sha512-dlI5SY7XYQCiCBafntWagCR6HcAJB/NpsLtdlPx8x08+Osz8Ok1HHz1GZuusegCe/VoJ6pAnF5a4pd5OZAq7qQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@oxlint/binding-android-arm-eabi": { "version": "1.77.0", "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.77.0.tgz", @@ -12611,6 +12934,68 @@ "dev": true, "license": "MIT" }, + "node_modules/oxfmt": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.62.0.tgz", + "integrity": "sha512-vxgGHTmnDU9j4CX7dDBLzxgmHxfda/yPcgJkGCMUSCwRmz+euo/V08xXLNgXTeqAB9Fhf3Pe2nO1RNKLCVgphQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinypool": "2.1.0" + }, + "bin": { + "oxfmt": "bin/oxfmt" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxfmt/binding-android-arm-eabi": "0.62.0", + "@oxfmt/binding-android-arm64": "0.62.0", + "@oxfmt/binding-darwin-arm64": "0.62.0", + "@oxfmt/binding-darwin-x64": "0.62.0", + "@oxfmt/binding-freebsd-x64": "0.62.0", + "@oxfmt/binding-linux-arm-gnueabihf": "0.62.0", + "@oxfmt/binding-linux-arm-musleabihf": "0.62.0", + "@oxfmt/binding-linux-arm64-gnu": "0.62.0", + "@oxfmt/binding-linux-arm64-musl": "0.62.0", + "@oxfmt/binding-linux-ppc64-gnu": "0.62.0", + "@oxfmt/binding-linux-riscv64-gnu": "0.62.0", + "@oxfmt/binding-linux-riscv64-musl": "0.62.0", + "@oxfmt/binding-linux-s390x-gnu": "0.62.0", + "@oxfmt/binding-linux-x64-gnu": "0.62.0", + "@oxfmt/binding-linux-x64-musl": "0.62.0", + "@oxfmt/binding-openharmony-arm64": "0.62.0", + "@oxfmt/binding-win32-arm64-msvc": "0.62.0", + "@oxfmt/binding-win32-ia32-msvc": "0.62.0", + "@oxfmt/binding-win32-x64-msvc": "0.62.0" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/oxfmt/node_modules/tinypool": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-2.1.0.tgz", + "integrity": "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + } + }, "node_modules/oxlint": { "version": "1.77.0", "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.77.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 6bbb3e9137..71a71661e7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -143,12 +143,12 @@ "license-checker": "^25.0.1", "msw": "^2.14.6", "msw-storybook-addon": "^2.0.7", + "oxfmt": "^0.62.0", "oxlint": "^1.77.0", "postcss": "^8.5.12", "postcss-cli": "^11.0.1", "postcss-preset-mantine": "^1.18.0", "postcss-simple-vars": "^7.0.1", - "prettier": "^3.8.1", "puppeteer": "^24.25.0", "rollup-plugin-visualizer": "^7.0.1", "storybook": "^9.1.20", diff --git a/frontend/stylelint.config.mjs b/frontend/stylelint.config.mjs index 2781e78516..79f3108c9c 100644 --- a/frontend/stylelint.config.mjs +++ b/frontend/stylelint.config.mjs @@ -1,4 +1,4 @@ -// Deliberately minimal: Prettier owns formatting and `task frontend:lint:colors` +// Deliberately minimal: oxfmt owns formatting and `task frontend:lint:colors` // owns the theme tokens, so this only carries rules that catch real bugs. export default { ignoreFiles: [