From d564605c70a1ae494425314854829c1588c30c70 Mon Sep 17 00:00:00 2001 From: trueberryless Date: Wed, 8 Jan 2025 11:15:37 +0100 Subject: [PATCH] ninth work session; start creating tests --- packages/starlight-spell-checker/package.json | 6 +- .../tests/basics.test.ts | 107 +++++++ .../fixtures/valid-content-de/astro.config.ts | 13 + .../fixtures/valid-content-de/package.json | 10 + .../valid-content-de/src/content.config.ts | 7 + .../src/content/docs/index.md | 5 + .../fixtures/valid-content-fr/astro.config.ts | 13 + .../fixtures/valid-content-fr/package.json | 10 + .../valid-content-fr/src/content.config.ts | 7 + .../src/content/docs/index.md | 5 + .../fixtures/valid-content/astro.config.ts | 13 + .../tests/fixtures/valid-content/package.json | 10 + .../valid-content/src/content.config.ts | 7 + .../valid-content/src/content/docs/index.md | 5 + .../starlight-spell-checker/tests/utils.ts | 124 ++++++++ .../starlight-spell-checker/vitest.config.ts | 13 + pnpm-lock.yaml | 293 ++++++++++++++++++ 17 files changed, 647 insertions(+), 1 deletion(-) create mode 100644 packages/starlight-spell-checker/tests/basics.test.ts create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content-de/astro.config.ts create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content-de/package.json create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content-de/src/content.config.ts create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content-de/src/content/docs/index.md create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content-fr/astro.config.ts create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content-fr/package.json create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content-fr/src/content.config.ts create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content-fr/src/content/docs/index.md create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content/astro.config.ts create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content/package.json create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content/src/content.config.ts create mode 100644 packages/starlight-spell-checker/tests/fixtures/valid-content/src/content/docs/index.md create mode 100644 packages/starlight-spell-checker/tests/utils.ts create mode 100644 packages/starlight-spell-checker/vitest.config.ts diff --git a/packages/starlight-spell-checker/package.json b/packages/starlight-spell-checker/package.json index 31d53da..6448ffc 100644 --- a/packages/starlight-spell-checker/package.json +++ b/packages/starlight-spell-checker/package.json @@ -10,7 +10,8 @@ }, "devDependencies": { "@astrojs/starlight": "^0.30.3", - "astro": "^5.1.2" + "astro": "^5.1.2", + "vitest": "^2.1.8" }, "peerDependencies": { "@astrojs/starlight": ">=0.30" @@ -76,5 +77,8 @@ "unified": "^11.0.5", "unist-util-visit": "^5.0.0", "vfile-reporter": "^8.1.1" + }, + "scripts": { + "test": "vitest" } } diff --git a/packages/starlight-spell-checker/tests/basics.test.ts b/packages/starlight-spell-checker/tests/basics.test.ts new file mode 100644 index 0000000..bc42830 --- /dev/null +++ b/packages/starlight-spell-checker/tests/basics.test.ts @@ -0,0 +1,107 @@ +import { expect, test } from 'vitest' + +import { ValidationErrorType } from '../libs/validation' + +import { buildFixture, expectValidationErrorCount, expectValidationErrors } from './utils' + +test('builds with valid English content', async () => { + const { status } = await buildFixture('valid-content') + + expect(status).toBe('success') +}) + +test('builds with valid German content', async () => { + const { status } = await buildFixture('valid-content-de') + + expect(status).toBe('success') +}) + +test('builds with valid French content', async () => { + const { status } = await buildFixture('valid-content-fr') + + expect(status).toBe('success') +}) + +// test('does not build with invalid links', async () => { +// const { output, status } = await buildFixture('invalid-links') + +// expect(status).toBe('error') + +// expectValidationErrorCount(output, 64, 4) + +// expectValidationErrors(output, 'test/', [ +// ['/https://starlight.astro.build/', ValidationErrorType.InvalidLink], +// ['/', ValidationErrorType.InvalidLink], +// ['/unknown', ValidationErrorType.InvalidLink], +// ['/unknown/', ValidationErrorType.InvalidLink], +// ['/unknown#title', ValidationErrorType.InvalidLink], +// ['/unknown/#title', ValidationErrorType.InvalidLink], +// ['/draft', ValidationErrorType.InvalidLink], +// ['/draft/', ValidationErrorType.InvalidLink], +// ['#links', ValidationErrorType.InvalidHash], +// ['/guides/example/#links', ValidationErrorType.InvalidHash], +// ['/icon.svg', ValidationErrorType.InvalidLink], +// ['/guidelines/ui.pdf', ValidationErrorType.InvalidLink], +// ['/unknown-ref', ValidationErrorType.InvalidLink], +// ['#unknown-ref', ValidationErrorType.InvalidHash], +// ['#anotherDiv', ValidationErrorType.InvalidHash], +// ['/guides/page-with-custom-slug', ValidationErrorType.InvalidLink], +// ['/release/@pkg/v0.2.0', ValidationErrorType.InvalidLink], +// ['/?query=string', ValidationErrorType.InvalidLink], +// ['/unknown?query=string', ValidationErrorType.InvalidLink], +// ['/unknown/?query=string', ValidationErrorType.InvalidLink], +// ['/unknown?query=string#title', ValidationErrorType.InvalidLink], +// ['/unknown/?query=string#title', ValidationErrorType.InvalidLink], +// ['?query=string#links', ValidationErrorType.InvalidHash], +// ['/guides/example/?query=string#links', ValidationErrorType.InvalidHash], +// ['/icon.svg?query=string', ValidationErrorType.InvalidLink], +// ['/guidelines/ui.pdf?query=string', ValidationErrorType.InvalidLink], +// ['/unknown-ref?query=string', ValidationErrorType.InvalidLink], +// ['?query=string#unknown-ref', ValidationErrorType.InvalidHash], +// ['http://localhost', ValidationErrorType.LocalLink], +// ['http://localhost:4321/', ValidationErrorType.LocalLink], +// ['https://127.0.0.1:4321/getting-started', ValidationErrorType.LocalLink], +// ]) + +// expectValidationErrors(output, 'guides/example/', [ +// ['#links', ValidationErrorType.InvalidHash], +// ['/unknown/#links', ValidationErrorType.InvalidLink], +// ['/unknown', ValidationErrorType.InvalidLink], +// ['#anotherBlock', ValidationErrorType.InvalidHash], +// ['/icon.svg', ValidationErrorType.InvalidLink], +// ['/guidelines/ui.pdf', ValidationErrorType.InvalidLink], +// ['/linkcard/', ValidationErrorType.InvalidLink], +// ['/linkcard/#links', ValidationErrorType.InvalidLink], +// ['#linkcard', ValidationErrorType.InvalidHash], +// ['/linkbutton/', ValidationErrorType.InvalidLink], +// ['/linkbutton/#links', ValidationErrorType.InvalidLink], +// ['#linkbutton', ValidationErrorType.InvalidHash], +// ['?query=string#links', ValidationErrorType.InvalidHash], +// ['/unknown/?query=string#links', ValidationErrorType.InvalidLink], +// ['/unknown?query=string', ValidationErrorType.InvalidLink], +// ['/icon.svg?query=string', ValidationErrorType.InvalidLink], +// ['/guidelines/ui.pdf?query=string', ValidationErrorType.InvalidLink], +// ['/linkcard/?query=string', ValidationErrorType.InvalidLink], +// ['/linkbutton/?query=string', ValidationErrorType.InvalidLink], +// ]) + +// expectValidationErrors(output, 'guides/namespacetest/', [ +// ['#some-other-content', ValidationErrorType.InvalidHash], +// ['/guides/namespacetest/#another-content', ValidationErrorType.InvalidHash], +// ]) + +// expectValidationErrors(output, 'relative/', [ +// ['.', ValidationErrorType.RelativeLink], +// ['./relative', ValidationErrorType.RelativeLink], +// ['./test', ValidationErrorType.RelativeLink], +// ['./guides/example', ValidationErrorType.RelativeLink], +// ['../test', ValidationErrorType.RelativeLink], +// ['test', ValidationErrorType.RelativeLink], +// ['.?query=string', ValidationErrorType.RelativeLink], +// ['./relative?query=string', ValidationErrorType.RelativeLink], +// ['./test?query=string', ValidationErrorType.RelativeLink], +// ['./guides/example?query=string', ValidationErrorType.RelativeLink], +// ['../test?query=string', ValidationErrorType.RelativeLink], +// ['test?query=string', ValidationErrorType.RelativeLink], +// ]) +// }) diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content-de/astro.config.ts b/packages/starlight-spell-checker/tests/fixtures/valid-content-de/astro.config.ts new file mode 100644 index 0000000..33392f6 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content-de/astro.config.ts @@ -0,0 +1,13 @@ +import starlight from "@astrojs/starlight"; +import { defineConfig } from "astro/config"; +import starlightSpellChecker from "starlight-spell-checker"; + +export default defineConfig({ + integrations: [ + starlight({ + pagefind: false, + plugins: [starlightSpellChecker()], + title: "Starlight Spell Checker Tests - valid content de", + }), + ], +}); diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content-de/package.json b/packages/starlight-spell-checker/tests/fixtures/valid-content-de/package.json new file mode 100644 index 0000000..b3df1a2 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content-de/package.json @@ -0,0 +1,10 @@ +{ + "name": "@starlight-spell-checker-tests/valid-content-de", + "version": "0.0.0", + "dependencies": { + "@astrojs/starlight": "^0.30.2", + "astro": "^5.1.1", + "starlight-spell-checker": "workspace:*" + }, + "private": true +} diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content-de/src/content.config.ts b/packages/starlight-spell-checker/tests/fixtures/valid-content-de/src/content.config.ts new file mode 100644 index 0000000..0ae6ac4 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content-de/src/content.config.ts @@ -0,0 +1,7 @@ +import { docsLoader } from '@astrojs/starlight/loaders' +import { docsSchema } from '@astrojs/starlight/schema' +import { defineCollection } from 'astro:content' + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +} diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content-de/src/content/docs/index.md b/packages/starlight-spell-checker/tests/fixtures/valid-content-de/src/content/docs/index.md new file mode 100644 index 0000000..2c1fe3b --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content-de/src/content/docs/index.md @@ -0,0 +1,5 @@ +--- +title: Verzeichnis +--- + +Die Sonne tauchte unter den Horizont und malte den Himmel in orangefarbenen und rosafarbenen Tönen, während die Abendbrise durch die Bäume rauschte. \ No newline at end of file diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/astro.config.ts b/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/astro.config.ts new file mode 100644 index 0000000..b702a19 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/astro.config.ts @@ -0,0 +1,13 @@ +import starlight from "@astrojs/starlight"; +import { defineConfig } from "astro/config"; +import starlightSpellChecker from "starlight-spell-checker"; + +export default defineConfig({ + integrations: [ + starlight({ + pagefind: false, + plugins: [starlightSpellChecker()], + title: "Starlight Spell Checker Tests - valid content fr", + }), + ], +}); diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/package.json b/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/package.json new file mode 100644 index 0000000..1975dd2 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/package.json @@ -0,0 +1,10 @@ +{ + "name": "@starlight-spell-checker-tests/valid-content-fr", + "version": "0.0.0", + "dependencies": { + "@astrojs/starlight": "^0.30.2", + "astro": "^5.1.1", + "starlight-spell-checker": "workspace:*" + }, + "private": true +} diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/src/content.config.ts b/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/src/content.config.ts new file mode 100644 index 0000000..0ae6ac4 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/src/content.config.ts @@ -0,0 +1,7 @@ +import { docsLoader } from '@astrojs/starlight/loaders' +import { docsSchema } from '@astrojs/starlight/schema' +import { defineCollection } from 'astro:content' + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +} diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/src/content/docs/index.md b/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/src/content/docs/index.md new file mode 100644 index 0000000..3c52903 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content-fr/src/content/docs/index.md @@ -0,0 +1,5 @@ +--- +title: Indice +--- + +Le soleil a plongé sous l'horizon, peignant le ciel dans des teintes orange et roses tandis que la brise du soir murmurait à travers les arbres. diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content/astro.config.ts b/packages/starlight-spell-checker/tests/fixtures/valid-content/astro.config.ts new file mode 100644 index 0000000..f3c7c90 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content/astro.config.ts @@ -0,0 +1,13 @@ +import starlight from "@astrojs/starlight"; +import { defineConfig } from "astro/config"; +import starlightSpellChecker from "starlight-spell-checker"; + +export default defineConfig({ + integrations: [ + starlight({ + pagefind: false, + plugins: [starlightSpellChecker()], + title: "Starlight Spell Checker Tests - valid content", + }), + ], +}); diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content/package.json b/packages/starlight-spell-checker/tests/fixtures/valid-content/package.json new file mode 100644 index 0000000..e3eba80 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content/package.json @@ -0,0 +1,10 @@ +{ + "name": "@starlight-spell-checker-tests/valid-content", + "version": "0.0.0", + "dependencies": { + "@astrojs/starlight": "^0.30.2", + "astro": "^5.1.1", + "starlight-spell-checker": "workspace:*" + }, + "private": true +} diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content/src/content.config.ts b/packages/starlight-spell-checker/tests/fixtures/valid-content/src/content.config.ts new file mode 100644 index 0000000..0ae6ac4 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content/src/content.config.ts @@ -0,0 +1,7 @@ +import { docsLoader } from '@astrojs/starlight/loaders' +import { docsSchema } from '@astrojs/starlight/schema' +import { defineCollection } from 'astro:content' + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +} diff --git a/packages/starlight-spell-checker/tests/fixtures/valid-content/src/content/docs/index.md b/packages/starlight-spell-checker/tests/fixtures/valid-content/src/content/docs/index.md new file mode 100644 index 0000000..2b863d7 --- /dev/null +++ b/packages/starlight-spell-checker/tests/fixtures/valid-content/src/content/docs/index.md @@ -0,0 +1,5 @@ +--- +title: Index +--- + +The sun dipped below the horizon, painting the sky in hues of orange and pink as the evening breeze whispered through the trees. diff --git a/packages/starlight-spell-checker/tests/utils.ts b/packages/starlight-spell-checker/tests/utils.ts new file mode 100644 index 0000000..db578fa --- /dev/null +++ b/packages/starlight-spell-checker/tests/utils.ts @@ -0,0 +1,124 @@ +import { fileURLToPath } from "node:url"; + +import { build } from "astro"; +import { expect, vi } from "vitest"; + +import type { ValidationErrorType } from "../libs/validation"; + +export async function buildFixture(name: string) { + const fixturePath = fileURLToPath( + new URL(`fixtures/${name}/`, import.meta.url) + ); + + let output = ""; + let status: "success" | "error"; + + function writeOutput(chunk: string | Uint8Array) { + output += String(chunk); + return true; + } + + const stdoutWriteSpy = vi + .spyOn(process.stdout, "write") + .mockImplementation(writeOutput); + const stderrWriteSpy = vi + .spyOn(process.stderr, "write") + .mockImplementation(writeOutput); + + try { + await build({ root: fixturePath }); + status = "success"; + } catch { + status = "error"; + } + + stderrWriteSpy.mockRestore(); + stdoutWriteSpy.mockRestore(); + + return { output, status }; +} + +export function expectValidationWarningCount( + output: string, + count: number, + filesCount: number +) { + expect(output).toMatch( + new RegExp( + `Found ${count} ${ + count === 1 ? "warning" : "warnings" + } in ${filesCount} ${filesCount === 1 ? "file" : "files"}.` + ) + ); +} + +export function expectValidationWarnings( + output: string, + path: string, + validationWarnings: [ + word: string, + type: ValidationErrorType, + suggestions?: string[] + ][] +) { + expect(output).toMatch( + new RegExp(`▶ ${path} +${validationWarnings + .map( + ([word, type, suggestions], index) => + `.* ${ + index < validationWarnings.length - 1 ? "├" : "└" + }─ ${word.replaceAll("?", String.raw`\?`)} - ${type}${ + suggestions + ? suggestions.length > 0 + ? ` \(${suggestions.join(", ")}\)` + : " no suggestions" + : "" + }` + ) + .join("\n")}`) + ); +} + +export function expectValidationErrorCount( + output: string, + count: number, + filesCount: number +) { + expect(output).toMatch( + new RegExp( + `Found ${count} ${count === 1 ? "error" : "errors"} in ${filesCount} ${ + filesCount === 1 ? "file" : "files" + }.` + ) + ); +} + +export function expectValidationErrors( + output: string, + path: string, + validationErrors: [ + word: string, + type: ValidationErrorType, + suggestions?: string[] + ][] +) { + expect(output).toMatch( + new RegExp(`▶ ${path} +${validationErrors + .map( + ([word, type, suggestions], index) => + `.* ${index < validationErrors.length - 1 ? "├" : "└"}─ ${word.replaceAll( + "?", + String.raw`\?` + )} - ${type}${ + suggestions + ? suggestions.length > 0 + ? ` \(${suggestions.join(", ")}\)` + : " no suggestions" + : "" + }` + ) + .join("\n")}`) + ); +} diff --git a/packages/starlight-spell-checker/vitest.config.ts b/packages/starlight-spell-checker/vitest.config.ts new file mode 100644 index 0000000..f8f5e04 --- /dev/null +++ b/packages/starlight-spell-checker/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + poolOptions: { + forks: { + maxForks: 1, + minForks: 1, + }, + }, + testTimeout: 60_000, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a2be133..f29566e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -180,6 +180,9 @@ importers: astro: specifier: ^5.1.2 version: 5.1.2(rollup@4.29.2)(typescript@5.7.2) + vitest: + specifier: ^2.1.8 + version: 2.1.8 packages: @@ -951,6 +954,35 @@ packages: '@ungap/structured-clone@1.2.1': resolution: {integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==} + '@vitest/expect@2.1.8': + resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==} + + '@vitest/mocker@2.1.8': + resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.8': + resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==} + + '@vitest/runner@2.1.8': + resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} + + '@vitest/snapshot@2.1.8': + resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} + + '@vitest/spy@2.1.8': + resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} + + '@vitest/utils@2.1.8': + resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1008,6 +1040,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true @@ -1060,6 +1096,10 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + camelcase@8.0.0: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} @@ -1071,6 +1111,10 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chai@5.1.2: + resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} + engines: {node: '>=12'} + chalk@5.4.1: resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} @@ -1090,6 +1134,10 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -1186,6 +1234,10 @@ packages: decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -1342,6 +1394,10 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} + expressive-code@0.38.3: resolution: {integrity: sha512-COM04AiUotHCKJgWdn7NtW2lqu8OW8owAidMpkXt1qxrZ9Q2iC7+tok/1qIn2ocGnczvr9paIySgGnEwFeEQ8Q==} @@ -1630,6 +1686,9 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loupe@3.1.2: + resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -2009,6 +2068,10 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2266,6 +2329,9 @@ packages: shiki@1.26.1: resolution: {integrity: sha512-Gqg6DSTk3wYqaZ5OaYtzjcdxcBvX5kCy24yvRJEgjT5U+WHlmqCThLuBUx0juyxQBi+6ug53IGeuQS07DWwpcw==} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -2311,6 +2377,12 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + stopwords@0.0.5: resolution: {integrity: sha512-P0x38ZVtTZ4GD3QqQM3TqjOTtt7ING0JNx2DFnZzp/OHh9I6i7rwTdGNnkyAJLlBKrz7zP5qwUR0nDgLhLbrBQ==} @@ -2362,9 +2434,24 @@ packages: thesaurus@0.0.0: resolution: {integrity: sha512-UMBq/VrjRPn0ZmxvW1l+pl66hJbDxOvLjp1J3O6ws44D55MWG5mbjxHKqFJy16H545q/rFHWVpJARtjlHiXXEA==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -2560,6 +2647,42 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-node@2.1.8: + resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.4.11: + resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + vite@6.0.7: resolution: {integrity: sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -2608,6 +2731,31 @@ packages: vite: optional: true + vitest@2.1.8: + resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.8 + '@vitest/ui': 2.1.8 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + weasels@2.0.1: resolution: {integrity: sha512-gRFUOte0u784vpfASyx4dQYprkWFCGQYNVx0y7wMWH90UyNJ9eXyI+C/gPuBFSEPX3r4C3574ury0ICQCiPHEg==} @@ -2633,6 +2781,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + widest-line@5.0.0: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} @@ -3440,6 +3593,46 @@ snapshots: '@ungap/structured-clone@1.2.1': {} + '@vitest/expect@2.1.8': + dependencies: + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.1.2 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.8(vite@5.4.11)': + dependencies: + '@vitest/spy': 2.1.8 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 5.4.11 + + '@vitest/pretty-format@2.1.8': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.8': + dependencies: + '@vitest/utils': 2.1.8 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.8': + dependencies: + '@vitest/pretty-format': 2.1.8 + magic-string: 0.30.17 + pathe: 1.1.2 + + '@vitest/spy@2.1.8': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.8': + dependencies: + '@vitest/pretty-format': 2.1.8 + loupe: 3.1.2 + tinyrainbow: 1.2.0 + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: acorn: 8.14.0 @@ -3479,6 +3672,8 @@ snapshots: array-union@2.1.0: {} + assertion-error@2.0.1: {} + astring@1.9.0: {} astro-expressive-code@0.38.3(astro@5.1.2(rollup@4.29.2)(typescript@5.7.2)): @@ -3622,12 +3817,22 @@ snapshots: dependencies: fill-range: 7.1.1 + cac@6.7.14: {} + camelcase@8.0.0: {} case-police@0.5.14: {} ccount@2.0.1: {} + chai@5.1.2: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.2 + pathval: 2.0.0 + chalk@5.4.1: {} character-entities-html4@2.1.0: {} @@ -3640,6 +3845,8 @@ snapshots: chardet@0.7.0: {} + check-error@2.1.1: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -3720,6 +3927,8 @@ snapshots: dependencies: character-entities: 2.0.2 + deep-eql@5.0.2: {} + defu@6.1.4: {} dequal@2.0.3: {} @@ -3900,6 +4109,8 @@ snapshots: eventemitter3@5.0.1: {} + expect-type@1.1.0: {} + expressive-code@0.38.3: dependencies: '@expressive-code/core': 0.38.3 @@ -4302,6 +4513,8 @@ snapshots: longest-streak@3.1.0: {} + loupe@3.1.2: {} + lru-cache@10.4.3: {} magic-string@0.30.17: @@ -4984,6 +5197,8 @@ snapshots: pathe@1.1.2: {} + pathval@2.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -5483,6 +5698,8 @@ snapshots: '@shikijs/vscode-textmate': 10.0.1 '@types/hast': 3.0.4 + siginfo@2.0.0: {} + signal-exit@4.1.0: {} simple-swizzle@0.2.2: @@ -5519,6 +5736,10 @@ snapshots: sprintf-js@1.0.3: {} + stackback@0.0.2: {} + + std-env@3.8.0: {} + stopwords@0.0.5: {} stream-replace-string@2.0.0: {} @@ -5572,8 +5793,16 @@ snapshots: thesaurus@0.0.0: {} + tinybench@2.9.0: {} + tinyexec@0.3.2: {} + tinypool@1.0.2: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -5756,6 +5985,32 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 + vite-node@2.1.8: + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 1.1.2 + vite: 5.4.11 + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.11: + dependencies: + esbuild: 0.21.5 + postcss: 8.4.49 + rollup: 4.29.2 + optionalDependencies: + fsevents: 2.3.3 + vite@6.0.7: dependencies: esbuild: 0.24.2 @@ -5768,6 +6023,39 @@ snapshots: optionalDependencies: vite: 6.0.7 + vitest@2.1.8: + dependencies: + '@vitest/expect': 2.1.8 + '@vitest/mocker': 2.1.8(vite@5.4.11) + '@vitest/pretty-format': 2.1.8 + '@vitest/runner': 2.1.8 + '@vitest/snapshot': 2.1.8 + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.1.2 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 1.1.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 1.2.0 + vite: 5.4.11 + vite-node: 2.1.8 + why-is-node-running: 2.3.0 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + weasels@2.0.1: {} web-namespaces@2.0.1: {} @@ -5789,6 +6077,11 @@ snapshots: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + widest-line@5.0.0: dependencies: string-width: 7.2.0