diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6543b8b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +node_modules +dist +ideas +external +legacy +docs +.VSCodeCounter +.github +.vscode +.husky diff --git a/.github/workflows/jsr.yml b/.github/workflows/jsr.yml new file mode 100644 index 0000000..b3e10c4 --- /dev/null +++ b/.github/workflows/jsr.yml @@ -0,0 +1,15 @@ +name: Publish to JSR + +on: + release: + types: [published] + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write # The OIDC ID token is used for authentication with JSR. + steps: + - uses: actions/checkout@v4 + - run: npx jsr publish diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 2acd2e3..98e9afa 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -13,6 +13,6 @@ jobs: node-version: '20.x' registry-url: 'https://registry.npmjs.org' - run: npm ci - - run: npm publish + - run: npm publish --provenance env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index ba18c2b..baf485d 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -1,20 +1,28 @@ name: Playwright Tests on: push: - branches: [main, master] + branches: [main] pull_request: - branches: [main, master] + branches: [main] jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: xu-cheng/texlive-action@v2 + with: + scheme: small + run: | + apk add make + make - uses: actions/setup-node@v4 with: node-version: lts/* - - name: Install dependencies - run: npm install -g pnpm && pnpm install + - name: Install PNPM + run: npm install -g pnpm + - name: Install Node.js dependencies + run: pnpm install - name: Install Playwright Browsers run: pnpm exec playwright install --with-deps - name: Run Playwright tests diff --git a/.gitignore b/.gitignore index b38b7db..477ba3a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,12 +11,17 @@ dist examples external ideas +legacy node_modules playwright-report static test-results tmp +# Generated by Sveltex in E2E tests +**/sveltex/fonts/** +**/sveltex/*.css + .VSCodeCounter #——————————————————————————————————————————————————————————# diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1fc6261 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# Extend the Playwright Docker image +FROM mcr.microsoft.com/playwright:v1.44.0-jammy + +# Set environment variables to skip interactive prompts +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Etc/UTC + +# Install TeX Live for LaTeX document compilation +RUN apt-get update && \ + apt-get install -y texlive tzdata && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Install Node.js dependencies (assuming package.json is present) +COPY package*.json ./ +RUN npm install + +# Copy the rest of your application +COPY . . + +# Entrypoint for your application (adjust as needed) +CMD ["npm", "run", "test:e2e"] diff --git a/README.md b/README.md index 515f2c2..f92586a 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,10 @@ -# Sveltex = Svelte + $\TeX$ +# Sveltex = Svelte + $\mathrm{\TeX}$ [![JSR](https://jsr.io/badges/@nvlang/sveltex?style=flat-square&labelColor=1A3644)](https://jsr.io/@nvlang/sveltex) [![NPM Version](https://img.shields.io/npm/v/sveltex-preprocess?style=flat-square&logo=npm&logoColor=white&label=&labelColor=BD453B&color=BD453B)](https://npmjs.com/sveltex-preprocess) [![GitHub Tag](https://img.shields.io/github/v/tag/nvlang/sveltex?style=flat-square&logo=GitHub&logoColor=aaa&label=&labelColor=333&color=333)](https://github.com/nvlang/sveltex) [![Codecov](https://img.shields.io/codecov/c/github/nvlang/sveltex?style=flat-square&logo=codecov&label=&logoColor=aaa&labelColor=333&color=333)]() -- NPM: [sveltex-preprocess](https://npmjs.com/sveltex-preprocess) -- JSR: [@nvlang/sveltex](https://jsr.io/@nvlang/sveltex) -- GitHub: [nvlang/sveltex](https://github.com/nvlang/sveltex) - ## Installation ```sh @@ -17,7 +13,12 @@ ## Documentation -TODO +### Quickstart + +```js +// svelte.config.js +import { sveltex } from 'sveltex-preprocess'; +``` ## How it works @@ -34,6 +35,7 @@ In alphabetical order: - Improve source map support. - Support preprocessing LaTeX content before passing it to MathJax/KaTeX. - VSCode extension for proper syntax highlighting of `.sveltex` files. +- Yeoman generator for scaffolding new Sveltex projects. ## Contributing diff --git a/docs/advanced/tex-texlive/caching.mdx b/docs/advanced/tex-texlive/caching.mdx index ca82a37..57e15bf 100644 --- a/docs/advanced/tex-texlive/caching.mdx +++ b/docs/advanced/tex-texlive/caching.mdx @@ -26,7 +26,9 @@ apply: ```js sveltex.config.js import { sveltex } from 'sveltex-preprocess'; -export const sveltexPreprocessor = await sveltex({ advancedTexBackend: 'local' }) +export const sveltexPreprocessor = await sveltex({ + advancedTexBackend: 'local', +}); await sveltexPreprocessor.configure({ advancedTex: { diff --git a/docs/essentials/tex.mdx b/docs/essentials/tex.mdx index f4be2f9..0a104aa 100644 --- a/docs/essentials/tex.mdx +++ b/docs/essentials/tex.mdx @@ -1,76 +1,50 @@ --- title: 'TeX' -description: 'Choose your markdown ecosystem' +description: 'Choose your TeX backend' icon: 'square-root-variable' mode: 'wide' --- -SvelTeX natively supports the following backends to render "basic" TeX (listed -in alphabetical order): +SvelTeX natively supports two backends to render "basic" TeX (listed in +alphabetical order): - [KaTeX](https://katex.org): "The fastest math typesetting library for the - web." + web." NPM: [`katex`](https://www.npmjs.com/package/katex). - [MathJax](http://mathjax.org): "Beautiful and accessible math in all - browsers." - - [MathJax-full](https://github.com/mathjax/MathJax-src): - - [MathJax-node](https://github.com/mathjax/MathJax-node): API to call - MathJax from Node.js. At the time of writing, it uses MathJax v2, which is - a bit outdated (MathJax v3 is mature, and v4 is in beta). - -Additionally, you can also provide a **custom** markdown backend. + browsers." NPM: [`mathjax-full`](https://github.com/mathjax/MathJax-src). ### Backend-specific setup - + ```bash pnpm - pnpm add -D markdown-it + pnpm add -D katex ``` ```bash bun - bun add -D markdown-it + bun add -D katex ``` ```bash npm - npm add -D markdown-it + npm add -D katex ``` ```bash yarn - yarn add -D markdown-it + yarn add -D katex ``` ```js sveltex.config.js import { sveltex } from 'sveltex-preprocess'; - // import multimdTablePlugin from 'markdown-it-multimd-table'; export const sveltexPreprocessor = await sveltex({ - markdownBackend: 'markdown-it', + texBackend: 'katex', }); await sveltexPreprocessor.configure({ - markdown: { - options: { - html: false, - xhtmlOut: false, - breaks: false, - linkify: false, - typographer: false, - quotes: '“”‘’', - }, - extensions: [ - // [ - // multimdTablePlugin, - // { - // multiline: false, - // rowspan: false, - // headerless: false, - // multibody: true, - // autolabel: true, - // }, - // ], - ], + tex: { + // ... (configuration possible, but not necessary) }, }); ``` @@ -78,170 +52,40 @@ Additionally, you can also provide a **custom** markdown backend. - + ```bash pnpm - pnpm add -D marked + pnpm add -D mathjax-full ``` ```bash bun - bun add -D marked + bun add -D mathjax-full ``` ```bash npm - npm add -D marked + npm add -D mathjax-full ``` ```bash yarn - yarn add -D marked + yarn add -D mathjax-full ``` - + ```js sveltex.config.js import { sveltex } from 'sveltex-preprocess'; - // import { gfmHeadingId } from 'marked-gfm-heading-id'; export const sveltexPreprocessor = await sveltex({ - markdownBackend: 'marked', - }); - - await sveltexPreprocessor.configure({ - markdown: { - options: { - async: false, - breaks: true, - gfm: true, - pedantic: false, - silent: false, - }, - extensions: [ - // gfmHeadingId() - ], - }, - }); - ``` - - - - - - - - - ```bash pnpm - pnpm add -D micromark - ``` - ```bash bun - bun add -D micromark - ``` - ```bash npm - npm add -D micromark - ``` - ```bash yarn - yarn add -D micromark - ``` - - - - ```js sveltex.config.js - import { sveltex } from 'sveltex-preprocess'; - - export const sveltexPreprocessor = await sveltex({ - 'micromark', - }); - - await sveltexPreprocessor.configure({ - markdown: { - options: { - gfm: true, - }, - extensions: [], - }, + texBackend: 'mathjax', }); - ``` - - - - - - - - - ```bash pnpm - pnpm add -D unified remark-parse remark-rehype rehype-stringify - ``` - ```bash bun - bun add -D unified remark-parse remark-rehype rehype-stringify - ``` - ```bash npm - npm add -D unified remark-parse remark-rehype rehype-stringify - ``` - ```bash yarn - yarn add -D unified remark-parse remark-rehype rehype-stringify - ``` - - - - ```js sveltex.config.js - import { sveltex } from 'sveltex-preprocess'; - - export const sveltexPreprocessor = await sveltex( - 'unified', // Markdown backend - 'none', // Code backend (syntax highlighting) - 'none', // TeX backend - 'none', // Advanced TeX backend - ); await sveltexPreprocessor.configure({ - markdown: { - options: { - gfm: true, - }, - extensions: [], + tex: { + // ... (configuration possible, but not necessary) }, }); ``` - - -By way of example, here is how you might set up a custom markdown backend to use -[Marked](https://github.com/markedjs/marked) if it weren't already supported: - -```js sveltex.config.js -import { sveltex, MarkdownHandler } from 'sveltex-preprocess'; -import { Marked } from 'marked'; - -export const sveltexPreprocessor = await sveltex({ - markdownBackend: 'custom', -}); - -sveltexPreprocessor.markdownHandler = await MarkdownHandler.create( - 'custom', - { - configuration: { extensions: [], options: {} }, - processor: new Marked(), - configure: (config, markdownHandler) => { - if (config.options) { - markdownHandler.processor.setOptions(config.options); - } - if (config.extensions) { - markdownHandler.processor.use(...config.extensions); - } - // Before calling the `configure` function you provide, the - // MarkdownHandler class will always do the following: - // markdownHandler.configuration = - // mergeConfigs(markdownHandler.configuration, config); - }, - process: (markdown: string, inline: boolean, markdownHandler) => { - return inline - ? await markdownHandler.processor.parseInline(markdown) - : await markdownHandler.processor.parse(markdown); - }, - } -) -``` - diff --git a/e2e/full-examples/markdown-it/README.md b/e2e/full-examples/markdown-it/README.md index 5ce6766..435a567 100644 --- a/e2e/full-examples/markdown-it/README.md +++ b/e2e/full-examples/markdown-it/README.md @@ -1,6 +1,7 @@ # create-svelte -Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). +Everything you need to build a Svelte project, powered by +[`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). ## Creating a project @@ -16,7 +17,8 @@ npm create svelte@latest my-app ## Developing -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: +Once you've created a project and installed dependencies with `npm install` (or +`pnpm install` or `yarn`), start a development server: ```bash npm run dev @@ -35,4 +37,5 @@ npm run build You can preview the production build with `npm run preview`. -> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. +> To deploy your app, you may need to install an +> [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/e2e/full-examples/markdown-it/basic.spec.ts b/e2e/full-examples/markdown-it/basic.spec.ts new file mode 100644 index 0000000..53b212f --- /dev/null +++ b/e2e/full-examples/markdown-it/basic.spec.ts @@ -0,0 +1,35 @@ +import { test, expect } from '@playwright/test'; + +// test('has title', async ({ page }) => { +// await page.goto('https://playwright.dev/'); + +// // Expect a title "to contain" a substring. +// await expect(page).toHaveTitle(/Playwright/); +// }); + +/** + * - 3001 - markdown-it + * - 3002 - marked + * - 3003 - micromark + * - 3004 - unified + */ +test('markdown-it', async ({ page }) => { + await page.goto('http://localhost:3001/'); + + // Expects page to have a heading with the name of Installation. + await expect( + page.getByRole('heading', { name: 'Heading 1' }), + ).toBeVisible(); + + // expect + // + //

Welcome to SvelteKit

Visit kit.svelte.dev to read the documentation

+ // + + // Expects page to have a link with the href "https://kit.svelte.dev". + await expect( + page.getByRole('link', { name: 'kit.svelte.dev' }), + ).toHaveAttribute('href', 'https://kit.svelte.dev'); + + await expect(page).toHaveScreenshot({ fullPage: true }); +}); diff --git a/e2e/full-examples/markdown-it/basic.spec.ts-snapshots/markdown-it-1-chrome-darwin.png b/e2e/full-examples/markdown-it/basic.spec.ts-snapshots/markdown-it-1-chrome-darwin.png new file mode 100644 index 0000000..e4c6c66 Binary files /dev/null and b/e2e/full-examples/markdown-it/basic.spec.ts-snapshots/markdown-it-1-chrome-darwin.png differ diff --git a/e2e/full-examples/markdown-it/package.json b/e2e/full-examples/markdown-it/package.json index f7d57bd..e37818a 100644 --- a/e2e/full-examples/markdown-it/package.json +++ b/e2e/full-examples/markdown-it/package.json @@ -14,7 +14,6 @@ "@sveltejs/kit": "^2.5.5", "@sveltejs/vite-plugin-svelte": "^3.0.2", "esbuild": "0.20.2", - "katex": "^0.16.10", "markdown-it": "^14.1.0", "svelte": "^4.2.12", "svelte-check": "^3.6.8", @@ -25,6 +24,7 @@ }, "type": "module", "dependencies": { - "@types/unist": "^2.0.10" + "@types/unist": "^2.0.10", + "mathjax-full": "^3.2.2" } } diff --git a/e2e/full-examples/markdown-it/src/app.d.ts b/e2e/full-examples/markdown-it/src/app.d.ts index 743f07b..c7c0ed1 100644 --- a/e2e/full-examples/markdown-it/src/app.d.ts +++ b/e2e/full-examples/markdown-it/src/app.d.ts @@ -1,13 +1,13 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } } export {}; diff --git a/e2e/full-examples/markdown-it/src/app.html b/e2e/full-examples/markdown-it/src/app.html index 77a5ff5..e499e56 100644 --- a/e2e/full-examples/markdown-it/src/app.html +++ b/e2e/full-examples/markdown-it/src/app.html @@ -1,12 +1,14 @@ - - - - - %sveltekit.head% - - -
%sveltekit.body%
- + + + + %sveltekit.head% + + +
%sveltekit.body%
+ diff --git a/e2e/full-examples/markdown-it/src/routes/+page.sveltex b/e2e/full-examples/markdown-it/src/routes/+page.sveltex index 4ef77f3..9d21561 100644 --- a/e2e/full-examples/markdown-it/src/routes/+page.sveltex +++ b/e2e/full-examples/markdown-it/src/routes/+page.sveltex @@ -1,7 +1,86 @@ -# Welcome to SvelteKit + -Visit [kit.svelte.dev](https://kit.svelte.dev) to read the documentation +# Heading 1 +###### Heading 6 +text **bold** text *italic (asterisks)* text _italic (underscores)_ text +[kit.svelte.dev](https://kit.svelte.dev) text `inline code` text $$ 1/n < \color{#fc0}\int_0^{1} \color{var(--some-color)}x^{x}\ dx < n. $$ + +Text: \[ 1/n < \color{#fc0}\int_0^{1} \color{var(--some-color)}x^{x}\ dx < n. \] Mustache tags: {1+1}, {`$1 + $${2 + +3} = $6`}. + +Regexes in mustache tags: {('a$b$c\(d\)e\[f\]g$$h$$'.match(/\$/g))}. + +` foo ` text $a$; +$ +b +$. + +
+> $x^{a}$ `monospaced $x$` +
+ + +Verbatim $$x^{2}\}$$ {{{ \[ \( + </Verb> < + + + + +$\texttt{}\}\\\{\ldots\text{\(f\)}$ + +\(\texttt{}\}\\\{\ldots\text{$f$}\) + +```ts +let a = { b: 3 }; +let b = `${2 + 2}a$b$c\(d\)\(e\[f\]\[g$$h$$i$` // { +let c = `{@html a} b {@debug c} d {@const e} f ` +``` + +text + +```html + + + + +``` + +An "advanced tex" block: + + +\begin{tikzpicture} + \draw (1,2) circle (2.4); + \draw[dashed, var(--some-color), thick] (-.5,0) rectangle (3, 3); + \draw[red, thick] (-1,1) rectangle (2.5, 4); + \node at (1,2) {\(\int_{0}^{1} \sum x_{i}^{3} \mathrm{d}\text{$x$}\)}; +\end{tikzpicture} + + +That same advanced tex block, referenced with a self-closing tag: + + + +some *more* text... diff --git a/e2e/full-examples/markdown-it/sveltex.config.js b/e2e/full-examples/markdown-it/sveltex.config.js index 13f7361..2b6f1f9 100644 --- a/e2e/full-examples/markdown-it/sveltex.config.js +++ b/e2e/full-examples/markdown-it/sveltex.config.js @@ -2,11 +2,80 @@ import { sveltex } from 'sveltex-preprocess'; export const sveltexPreprocessor = await sveltex({ markdownBackend: 'markdown-it', - codeBackend: 'none', - texBackend: 'none', - advancedTexBackend: 'none', + codeBackend: 'highlight.js', + texBackend: 'mathjax', + advancedTexBackend: 'local', }); await sveltexPreprocessor.configure({ markdown: {}, + code: { + escapeBraces: true, + escapeHtml: true, + wrap: undefined, + wrapClassPrefix: 'language-', + languages: ['ts'], + }, + tex: { + outputFormat: 'svg', + }, + verbatim: { + verbatimEnvironments: { + Verb: { + processInner: { + escapeBraces: true, + escapeHtml: true, + }, + component: 'p', + }, + }, + }, + advancedTex: { + components: { + tex: { + aliases: ['TikZ'], + preamble: [ + '\\usepackage{mathtools}', + '\\usepackage{amssymb}', + '\\usepackage{microtype}', + // '\\usepackage{fontspec}', + // '\\usepackage{unicode-math}', + // '\\usepackage{geometry}', + '\\usepackage{tikz}', + '\\usetikzlibrary{arrows.meta, calc, matrix, patterns, shadings, shadows, plotmarks, shapes.geometric, shapes.misc}', + '\\usepgflibrary{shadings}', + ].join('\n'), + overrides: { + engine: 'lualatex', + // overrideSvgPostprocess: null, + // svgoOptions: { + // plugins: [], + // }, + intermediateFiletype: 'dvi', + dvisvgmOptions: { + svg: { + // fontFormat: 'svg', + bbox: '3pt', + gradSimplify: null, + gradOverlap: null, + gradSegments: null, + bitmapFormat: null, + clipJoin: null, + comments: null, + currentColor: null, + optimize: null, + precision: null, + linkmark: null, + noStyles: null, + relative: null, + zip: null, + }, + processing: {}, + svgTransformations: {}, + }, + }, + documentClass: '\\documentclass{standalone}', + }, + }, + }, }); diff --git a/e2e/full-examples/marked/README.md b/e2e/full-examples/marked/README.md index 5ce6766..435a567 100644 --- a/e2e/full-examples/marked/README.md +++ b/e2e/full-examples/marked/README.md @@ -1,6 +1,7 @@ # create-svelte -Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). +Everything you need to build a Svelte project, powered by +[`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). ## Creating a project @@ -16,7 +17,8 @@ npm create svelte@latest my-app ## Developing -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: +Once you've created a project and installed dependencies with `npm install` (or +`pnpm install` or `yarn`), start a development server: ```bash npm run dev @@ -35,4 +37,5 @@ npm run build You can preview the production build with `npm run preview`. -> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. +> To deploy your app, you may need to install an +> [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/e2e/full-examples/marked/basic.spec.ts b/e2e/full-examples/marked/basic.spec.ts new file mode 100644 index 0000000..33f6097 --- /dev/null +++ b/e2e/full-examples/marked/basic.spec.ts @@ -0,0 +1,35 @@ +import { test, expect } from '@playwright/test'; + +// test('has title', async ({ page }) => { +// await page.goto('https://playwright.dev/'); + +// // Expect a title "to contain" a substring. +// await expect(page).toHaveTitle(/Playwright/); +// }); + +/** + * - 3001 - markdown-it + * - 3002 - marked + * - 3003 - micromark + * - 3004 - unified + */ +test('marked', async ({ page }) => { + await page.goto('http://localhost:3002/'); + + // Expects page to have a heading with the name of Installation. + await expect( + page.getByRole('heading', { name: 'Heading 1' }), + ).toBeVisible(); + + // expect + // + //

Welcome to SvelteKit

Visit kit.svelte.dev to read the documentation

+ // + + // Expects page to have a link with the href "https://kit.svelte.dev". + await expect( + page.getByRole('link', { name: 'kit.svelte.dev' }), + ).toHaveAttribute('href', 'https://kit.svelte.dev'); + + await expect(page).toHaveScreenshot({ fullPage: true }); +}); diff --git a/e2e/full-examples/marked/basic.spec.ts-snapshots/marked-1-chrome-darwin.png b/e2e/full-examples/marked/basic.spec.ts-snapshots/marked-1-chrome-darwin.png new file mode 100644 index 0000000..c4b739d Binary files /dev/null and b/e2e/full-examples/marked/basic.spec.ts-snapshots/marked-1-chrome-darwin.png differ diff --git a/e2e/full-examples/marked/package.json b/e2e/full-examples/marked/package.json index c1145bc..b2a4cbe 100644 --- a/e2e/full-examples/marked/package.json +++ b/e2e/full-examples/marked/package.json @@ -14,7 +14,6 @@ "@sveltejs/kit": "^2.5.5", "@sveltejs/vite-plugin-svelte": "^3.0.2", "esbuild": "0.20.2", - "katex": "^0.16.10", "marked": "^12.0.1", "svelte": "^4.2.12", "svelte-check": "^3.6.8", @@ -26,6 +25,7 @@ "type": "module", "dependencies": { "@types/unist": "^2.0.10", - "marked-gfm-heading-id": "^3.1.3" + "marked-gfm-heading-id": "^3.1.3", + "mathjax-full": "^3.2.2" } } diff --git a/e2e/full-examples/marked/src/app.d.ts b/e2e/full-examples/marked/src/app.d.ts index 743f07b..c7c0ed1 100644 --- a/e2e/full-examples/marked/src/app.d.ts +++ b/e2e/full-examples/marked/src/app.d.ts @@ -1,13 +1,13 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } } export {}; diff --git a/e2e/full-examples/marked/src/app.html b/e2e/full-examples/marked/src/app.html index 77a5ff5..e499e56 100644 --- a/e2e/full-examples/marked/src/app.html +++ b/e2e/full-examples/marked/src/app.html @@ -1,12 +1,14 @@ - - - - - %sveltekit.head% - - -
%sveltekit.body%
- + + + + %sveltekit.head% + + +
%sveltekit.body%
+ diff --git a/e2e/full-examples/marked/src/routes/+page.sveltex b/e2e/full-examples/marked/src/routes/+page.sveltex index 962cddd..9d21561 100644 --- a/e2e/full-examples/marked/src/routes/+page.sveltex +++ b/e2e/full-examples/marked/src/routes/+page.sveltex @@ -1,15 +1,86 @@ + + + +# Heading 1 +###### Heading 6 + +text **bold** text *italic (asterisks)* text _italic (underscores)_ text +[kit.svelte.dev](https://kit.svelte.dev) text `inline code` text $$ 1/n < \color{#fc0}\int_0^{1} \color{var(--some-color)}x^{x}\ dx < n. $$ + +Text: \[ 1/n < \color{#fc0}\int_0^{1} \color{var(--some-color)}x^{x}\ dx < n. \] Mustache tags: {1+1}, {`$1 + $${2 + +3} = $6`}. + +Regexes in mustache tags: {('a$b$c\(d\)e\[f\]g$$h$$'.match(/\$/g))}. + +` foo ` text $a$; +$ +b +$. + +
+> $x^{a}$ `monospaced $x$` +
+ + +Verbatim $$x^{2}\}$$ {{{ \[ \( + </Verb> < + + + + +$\texttt{}\}\\\{\ldots\text{\(f\)}$ + +\(\texttt{}\}\\\{\ldots\text{$f$}\) + +```ts +let a = { b: 3 }; +let b = `${2 + 2}a$b$c\(d\)\(e\[f\]\[g$$h$$i$` // { +let c = `{@html a} b {@debug c} d {@const e} f ` +``` + +text + +```html + -test1 *italic* test2 +``` + +An "advanced tex" block: \begin{tikzpicture} - \draw (0,0) circle (3); - \draw (0,-1) circle (1.8); - \draw[var(--red), thick] (0,0) rectangle (3, 3); + \draw (1,2) circle (2.4); + \draw[dashed, var(--some-color), thick] (-.5,0) rectangle (3, 3); + \draw[red, thick] (-1,1) rectangle (2.5, 4); + \node at (1,2) {\(\int_{0}^{1} \sum x_{i}^{3} \mathrm{d}\text{$x$}\)}; \end{tikzpicture} -test3 **bold** test4 +That same advanced tex block, referenced with a self-closing tag: + + + +some *more* text... + + diff --git a/e2e/full-examples/marked/src/sveltex/tex/something.svelte b/e2e/full-examples/marked/src/sveltex/tex/something.svelte deleted file mode 100644 index 2b7097b..0000000 --- a/e2e/full-examples/marked/src/sveltex/tex/something.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/e2e/full-examples/marked/sveltex.config.js b/e2e/full-examples/marked/sveltex.config.js index 2c9a028..6a806cc 100644 --- a/e2e/full-examples/marked/sveltex.config.js +++ b/e2e/full-examples/marked/sveltex.config.js @@ -1,10 +1,10 @@ import { sveltex } from 'sveltex-preprocess'; import { gfmHeadingId } from 'marked-gfm-heading-id'; -export const sveltexPreprocessor = sveltex({ +export const sveltexPreprocessor = await sveltex({ markdownBackend: 'marked', - codeBackend: 'none', - texBackend: 'none', + codeBackend: 'highlight.js', + texBackend: 'mathjax', advancedTexBackend: 'local', }); @@ -19,11 +19,70 @@ await sveltexPreprocessor.configure({ }, extensions: [gfmHeadingId()], }, + code: { + escapeBraces: true, + escapeHtml: true, + wrap: undefined, + wrapClassPrefix: 'language-', + languages: ['ts'], + }, + tex: {}, + verbatim: { + verbatimEnvironments: { + Verb: { + processInner: { + escapeBraces: true, + escapeHtml: true, + }, + component: 'p', + }, + }, + }, advancedTex: { - // caching: false, components: { tex: { - documentClass: '\\documentclass[tikz]{standalone}', + aliases: ['TikZ'], + preamble: [ + '\\usepackage{mathtools}', + '\\usepackage{amssymb}', + '\\usepackage{microtype}', + // '\\usepackage{fontspec}', + // '\\usepackage{unicode-math}', + // '\\usepackage{geometry}', + '\\usepackage{tikz}', + '\\usetikzlibrary{arrows.meta, calc, matrix, patterns, shadings, shadows, plotmarks, shapes.geometric, shapes.misc}', + '\\usepgflibrary{shadings}', + ].join('\n'), + overrides: { + engine: 'lualatex', + // overrideSvgPostprocess: null, + // svgoOptions: { + // plugins: [], + // }, + intermediateFiletype: 'dvi', + dvisvgmOptions: { + svg: { + // fontFormat: 'svg', + bbox: '3pt', + gradSimplify: null, + gradOverlap: null, + gradSegments: null, + bitmapFormat: null, + clipJoin: null, + comments: null, + currentColor: null, + optimize: null, + precision: null, + linkmark: null, + noStyles: null, + relative: null, + zip: null, + }, + processing: {}, + svgTransformations: {}, + }, + }, + documentClass: '\\documentclass{standalone}', }, }, }, diff --git a/e2e/full-examples/micromark/README.md b/e2e/full-examples/micromark/README.md index 5ce6766..435a567 100644 --- a/e2e/full-examples/micromark/README.md +++ b/e2e/full-examples/micromark/README.md @@ -1,6 +1,7 @@ # create-svelte -Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). +Everything you need to build a Svelte project, powered by +[`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). ## Creating a project @@ -16,7 +17,8 @@ npm create svelte@latest my-app ## Developing -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: +Once you've created a project and installed dependencies with `npm install` (or +`pnpm install` or `yarn`), start a development server: ```bash npm run dev @@ -35,4 +37,5 @@ npm run build You can preview the production build with `npm run preview`. -> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. +> To deploy your app, you may need to install an +> [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/e2e/full-examples/micromark/basic.spec.ts b/e2e/full-examples/micromark/basic.spec.ts new file mode 100644 index 0000000..e9071c7 --- /dev/null +++ b/e2e/full-examples/micromark/basic.spec.ts @@ -0,0 +1,35 @@ +import { test, expect } from '@playwright/test'; + +// test('has title', async ({ page }) => { +// await page.goto('https://playwright.dev/'); + +// // Expect a title "to contain" a substring. +// await expect(page).toHaveTitle(/Playwright/); +// }); + +/** + * - 3001 - markdown-it + * - 3002 - marked + * - 3003 - micromark + * - 3004 - unified + */ +test('micromark', async ({ page }) => { + await page.goto('http://localhost:3003/'); + + // Expects page to have a heading with the name of Installation. + await expect( + page.getByRole('heading', { name: 'Heading 1' }), + ).toBeVisible(); + + // expect + // + //

Welcome to SvelteKit

Visit kit.svelte.dev to read the documentation

+ // + + // Expects page to have a link with the href "https://kit.svelte.dev". + await expect( + page.getByRole('link', { name: 'kit.svelte.dev' }), + ).toHaveAttribute('href', 'https://kit.svelte.dev'); + + await expect(page).toHaveScreenshot({ fullPage: true }); +}); diff --git a/e2e/full-examples/micromark/basic.spec.ts-snapshots/micromark-1-chrome-darwin.png b/e2e/full-examples/micromark/basic.spec.ts-snapshots/micromark-1-chrome-darwin.png new file mode 100644 index 0000000..b1080bc Binary files /dev/null and b/e2e/full-examples/micromark/basic.spec.ts-snapshots/micromark-1-chrome-darwin.png differ diff --git a/e2e/full-examples/micromark/package.json b/e2e/full-examples/micromark/package.json index 2017dd7..046144a 100644 --- a/e2e/full-examples/micromark/package.json +++ b/e2e/full-examples/micromark/package.json @@ -11,20 +11,22 @@ }, "devDependencies": { "@sveltejs/adapter-auto": "^3.2.0", - "@sveltejs/kit": "^2.5.5", - "@sveltejs/vite-plugin-svelte": "^3.0.2", + "@sveltejs/kit": "^2.5.9", + "@sveltejs/vite-plugin-svelte": "^3.1.0", "esbuild": "0.20.2", "katex": "^0.16.10", + "mathjax-full": "^3.2.2", "micromark": "^4.0.0", - "svelte": "^4.2.12", - "svelte-check": "^3.6.8", - "sveltex-preprocess": "file:../../../", + "svelte": "^4.2.17", + "svelte-check": "^3.7.1", + "sveltex-preprocess": "file:../../..", "tslib": "^2.6.2", - "typescript": "^5.4.3", - "vite": "^5.2.7" + "typescript": "^5.4.5", + "vite": "^5.2.11" }, "type": "module", "dependencies": { - "@types/unist": "^2.0.10" + "@types/unist": "^2.0.10", + "mathjax-node": "^2.1.1" } } diff --git a/e2e/full-examples/micromark/src/app.d.ts b/e2e/full-examples/micromark/src/app.d.ts index 743f07b..c7c0ed1 100644 --- a/e2e/full-examples/micromark/src/app.d.ts +++ b/e2e/full-examples/micromark/src/app.d.ts @@ -1,13 +1,13 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } } export {}; diff --git a/e2e/full-examples/micromark/src/app.html b/e2e/full-examples/micromark/src/app.html index 77a5ff5..e499e56 100644 --- a/e2e/full-examples/micromark/src/app.html +++ b/e2e/full-examples/micromark/src/app.html @@ -1,12 +1,14 @@ - - - - - %sveltekit.head% - - -
%sveltekit.body%
- + + + + %sveltekit.head% + + +
%sveltekit.body%
+ diff --git a/e2e/full-examples/micromark/src/routes/+page.sveltex b/e2e/full-examples/micromark/src/routes/+page.sveltex index 4ef77f3..9d21561 100644 --- a/e2e/full-examples/micromark/src/routes/+page.sveltex +++ b/e2e/full-examples/micromark/src/routes/+page.sveltex @@ -1,7 +1,86 @@ -# Welcome to SvelteKit + -Visit [kit.svelte.dev](https://kit.svelte.dev) to read the documentation +# Heading 1 +###### Heading 6 +text **bold** text *italic (asterisks)* text _italic (underscores)_ text +[kit.svelte.dev](https://kit.svelte.dev) text `inline code` text $$ 1/n < \color{#fc0}\int_0^{1} \color{var(--some-color)}x^{x}\ dx < n. $$ + +Text: \[ 1/n < \color{#fc0}\int_0^{1} \color{var(--some-color)}x^{x}\ dx < n. \] Mustache tags: {1+1}, {`$1 + $${2 + +3} = $6`}. + +Regexes in mustache tags: {('a$b$c\(d\)e\[f\]g$$h$$'.match(/\$/g))}. + +` foo ` text $a$; +$ +b +$. + +
+> $x^{a}$ `monospaced $x$` +
+ + +Verbatim $$x^{2}\}$$ {{{ \[ \( + </Verb> < + + + + +$\texttt{}\}\\\{\ldots\text{\(f\)}$ + +\(\texttt{}\}\\\{\ldots\text{$f$}\) + +```ts +let a = { b: 3 }; +let b = `${2 + 2}a$b$c\(d\)\(e\[f\]\[g$$h$$i$` // { +let c = `{@html a} b {@debug c} d {@const e} f ` +``` + +text + +```html + + + + +``` + +An "advanced tex" block: + + +\begin{tikzpicture} + \draw (1,2) circle (2.4); + \draw[dashed, var(--some-color), thick] (-.5,0) rectangle (3, 3); + \draw[red, thick] (-1,1) rectangle (2.5, 4); + \node at (1,2) {\(\int_{0}^{1} \sum x_{i}^{3} \mathrm{d}\text{$x$}\)}; +\end{tikzpicture} + + +That same advanced tex block, referenced with a self-closing tag: + + + +some *more* text... diff --git a/e2e/full-examples/micromark/src/sveltex/tex/something.svelte b/e2e/full-examples/micromark/src/sveltex/tex/something.svelte new file mode 100644 index 0000000..e16ae5a --- /dev/null +++ b/e2e/full-examples/micromark/src/sveltex/tex/something.svelte @@ -0,0 +1,11 @@ +10x3idx \ No newline at end of file diff --git a/e2e/full-examples/micromark/sveltex.config.js b/e2e/full-examples/micromark/sveltex.config.js index 8aba0f8..ed24798 100644 --- a/e2e/full-examples/micromark/sveltex.config.js +++ b/e2e/full-examples/micromark/sveltex.config.js @@ -2,11 +2,113 @@ import { sveltex } from 'sveltex-preprocess'; export const sveltexPreprocessor = await sveltex({ markdownBackend: 'micromark', - codeBackend: 'none', - texBackend: 'none', - advancedTexBackend: 'none', + codeBackend: 'escapeOnly', + texBackend: 'mathjax', + advancedTexBackend: 'local', }); await sveltexPreprocessor.configure({ + general: { + tex: {}, + }, markdown: {}, + code: { + escapeBraces: true, + escapeHtml: true, + wrap: undefined, + wrapClassPrefix: 'language-', + }, + tex: { + // inputConfiguration: { + // svgNode: true, + // }, + outputFormat: 'chtml', + mathjaxConfiguration: { + svg: { + // displayIndent: '2em', + // displayAlign: 'center', + }, + chtml: { + adaptiveCSS: false, + fontURL: + 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2', + }, + options: {}, + }, + + // inputConfiguration: { + // html: true, + // css: true, + // }, + // mathjaxNodeConfiguration: { + // fontURL: + // 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2/', + // }, + // mathjaxConfiguration: { + // chtml: { + // fontURL: + // 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2/', + // }, + // }, + // outputFormat: 'chtml', + }, + verbatim: { + verbatimEnvironments: { + Verb: { + processInner: { + escapeBraces: true, + escapeHtml: true, + }, + component: 'p', + }, + }, + }, + advancedTex: { + components: { + tex: { + aliases: ['TikZ'], + preamble: [ + '\\usepackage{mathtools}', + '\\usepackage{amssymb}', + '\\usepackage{microtype}', + // '\\usepackage{fontspec}', + // '\\usepackage{unicode-math}', + // '\\usepackage{geometry}', + '\\usepackage{tikz}', + '\\usetikzlibrary{arrows.meta, calc, matrix, patterns, shadings, shadows, plotmarks, shapes.geometric, shapes.misc}', + '\\usepgflibrary{shadings}', + ].join('\n'), + overrides: { + engine: 'lualatex', + // overrideSvgPostprocess: null, + // svgoOptions: { + // plugins: [], + // }, + intermediateFiletype: 'dvi', + dvisvgmOptions: { + svg: { + // fontFormat: 'svg', + bbox: '3pt', + gradSimplify: null, + gradOverlap: null, + gradSegments: null, + bitmapFormat: null, + clipJoin: null, + comments: null, + currentColor: null, + optimize: null, + precision: null, + linkmark: null, + noStyles: null, + relative: null, + zip: null, + }, + processing: {}, + svgTransformations: {}, + }, + }, + documentClass: '\\documentclass{standalone}', + }, + }, + }, }); diff --git a/e2e/full-examples/unified/basic.spec.ts b/e2e/full-examples/unified/basic.spec.ts index f5390fd..b7744f2 100644 --- a/e2e/full-examples/unified/basic.spec.ts +++ b/e2e/full-examples/unified/basic.spec.ts @@ -18,7 +18,7 @@ test('unified', async ({ page }) => { // Expects page to have a heading with the name of Installation. await expect( - page.getByRole('heading', { name: 'Welcome to SvelteKit' }), + page.getByRole('heading', { name: 'Heading 1' }), ).toBeVisible(); // expect @@ -26,14 +26,6 @@ test('unified', async ({ page }) => { //

Welcome to SvelteKit

Visit kit.svelte.dev to read the documentation

// - // Expects page to have a paragraph with the text "Visit kit.svelte.dev to read the documentation". - await expect( - page.getByText('Visit kit.svelte.dev to read the documentation'), - ).toBeVisible(); - - // Expects page to have a link with the text "kit.svelte.dev". - await expect(page.getByText('kit.svelte.dev')).toBeVisible(); - // Expects page to have a link with the href "https://kit.svelte.dev". await expect( page.getByRole('link', { name: 'kit.svelte.dev' }), @@ -41,9 +33,8 @@ test('unified', async ({ page }) => { // Expect H1 to have ID "welcome-to-sveltekit" await expect( - page.getByRole('heading', { name: 'Welcome to SvelteKit' }), - ).toHaveAttribute('id', 'welcome-to-sveltekit'); + page.getByRole('heading', { name: 'Heading 1' }), + ).toHaveAttribute('id', 'heading-1'); - // Expect there to be a strong "something" within a paragraph on the page - await expect(page.getByText('something')).toBeVisible(); + await expect(page).toHaveScreenshot({ fullPage: true }); }); diff --git a/e2e/full-examples/unified/basic.spec.ts-snapshots/unified-1-chrome-darwin.png b/e2e/full-examples/unified/basic.spec.ts-snapshots/unified-1-chrome-darwin.png new file mode 100644 index 0000000..df6e134 Binary files /dev/null and b/e2e/full-examples/unified/basic.spec.ts-snapshots/unified-1-chrome-darwin.png differ diff --git a/e2e/full-examples/unified/src/app.html b/e2e/full-examples/unified/src/app.html index 1966776..e499e56 100644 --- a/e2e/full-examples/unified/src/app.html +++ b/e2e/full-examples/unified/src/app.html @@ -5,7 +5,10 @@ %sveltekit.head% - +
%sveltekit.body%
diff --git a/e2e/full-examples/unified/src/routes/+page.sveltex b/e2e/full-examples/unified/src/routes/+page.sveltex index 5c09370..9d21561 100644 --- a/e2e/full-examples/unified/src/routes/+page.sveltex +++ b/e2e/full-examples/unified/src/routes/+page.sveltex @@ -1,43 +1,86 @@ + # Heading 1 -## Heading 2 -### Heading 3 -#### Heading 4 -##### Heading 5 ###### Heading 6 text **bold** text *italic (asterisks)* text _italic (underscores)_ text -[svelte.dev](https://kit.svelte.dev) text `inline code` text $x^{x}$ text +[kit.svelte.dev](https://kit.svelte.dev) text `inline code` text $$ 1/n < \color{#fc0}\int_0^{1} \color{var(--some-color)}x^{x}\ dx < n. $$ -$$ 1/n < \int_0^{1} x^{x}\ dx < n. $$ +Text: \[ 1/n < \color{#fc0}\int_0^{1} \color{var(--some-color)}x^{x}\ dx < n. \] Mustache tags: {1+1}, {`$1 + $${2 + +3} = $6`}. -text +Regexes in mustache tags: {('a$b$c\(d\)e\[f\]g$$h$$'.match(/\$/g))}. + +` foo ` text $a$; +$ +b +$. + +
+> $x^{a}$ `monospaced $x$` +
+ + +Verbatim $$x^{2}\}$$ {{{ \[ \( + </Verb> < + + + + +$\texttt{}\}\\\{\ldots\text{\(f\)}$ + +\(\texttt{}\}\\\{\ldots\text{$f$}\) ```ts let a = { b: 3 }; +let b = `${2 + 2}a$b$c\(d\)\(e\[f\]\[g$$h$$i$` // { +let c = `{@html a} b {@debug c} d {@const e} f ` ``` text +```html + + + +``` + +An "advanced tex" block: + \begin{tikzpicture} - \draw (0,0) circle (3); - \draw[thin] (0,-1) circle (3.9); - \draw[var(--some-color), thick] (0,0) rectangle (3, 3); + \draw (1,2) circle (2.4); + \draw[dashed, var(--some-color), thick] (-.5,0) rectangle (3, 3); + \draw[red, thick] (-1,1) rectangle (2.5, 4); + \node at (1,2) {\(\int_{0}^{1} \sum x_{i}^{3} \mathrm{d}\text{$x$}\)}; \end{tikzpicture} -text +That same advanced tex block, referenced with a self-closing tag: -text +some *more* text... + + diff --git a/e2e/full-examples/unified/src/sveltex/tex/something.svelte b/e2e/full-examples/unified/src/sveltex/tex/something.svelte deleted file mode 100644 index 8cf89c2..0000000 --- a/e2e/full-examples/unified/src/sveltex/tex/something.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - diff --git a/e2e/full-examples/unified/sveltex.config.js b/e2e/full-examples/unified/sveltex.config.js index cac6240..83e8ac0 100644 --- a/e2e/full-examples/unified/sveltex.config.js +++ b/e2e/full-examples/unified/sveltex.config.js @@ -18,11 +18,62 @@ await sveltexPreprocessor.configure({ languages: 'common', }, tex: {}, + verbatim: { + verbatimEnvironments: { + Verb: { + processInner: { + escapeBraces: true, + escapeHtml: true, + }, + component: 'p', + }, + }, + }, advancedTex: { - // caching: false, components: { tex: { - documentClass: '\\documentclass[tikz]{standalone}', + aliases: ['TikZ'], + preamble: [ + '\\usepackage{mathtools}', + '\\usepackage{amssymb}', + '\\usepackage{microtype}', + // '\\usepackage{fontspec}', + // '\\usepackage{unicode-math}', + // '\\usepackage{geometry}', + '\\usepackage{tikz}', + '\\usetikzlibrary{arrows.meta, calc, matrix, patterns, shadings, shadows, plotmarks, shapes.geometric, shapes.misc}', + '\\usepgflibrary{shadings}', + ].join('\n'), + overrides: { + engine: 'lualatex', + // overrideSvgPostprocess: null, + // svgoOptions: { + // plugins: [], + // }, + intermediateFiletype: 'dvi', + dvisvgmOptions: { + svg: { + // fontFormat: 'svg', + bbox: '3pt', + gradSimplify: null, + gradOverlap: null, + gradSegments: null, + bitmapFormat: null, + clipJoin: null, + comments: null, + currentColor: null, + optimize: null, + precision: null, + linkmark: null, + noStyles: null, + relative: null, + zip: null, + }, + processing: {}, + svgTransformations: {}, + }, + }, + documentClass: '\\documentclass{standalone}', }, }, }, diff --git a/eslint.config.js b/eslint.config.js index 94eb5b0..1d270d6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -20,6 +20,8 @@ export default tseslint.config( 'yarn.lock', 'external', 'examples', + 'legacy', + 'ideas', ], }, { diff --git a/jsr.jsonc b/jsr.jsonc new file mode 100644 index 0000000..7213b0e --- /dev/null +++ b/jsr.jsonc @@ -0,0 +1,35 @@ +{ + "name": "@nvl/sveltex", + "version": "0.1.0", + "exports": { + // Default entry point, which can be imported as `@nvlang/sveltex` + ".": "./src/mod.ts" + }, + "publish": { + "exclude": [ + // End-to-end tests + "**/e2e/**", + + // Unit tests + "**/tests/**", + + // JS distribution files (NPM uses these, but JSR doesn't need them) + "**/dist/**", + + // Node modules + "**/node_modules/**", + + // Documentation deployed with Mintlify + "**/docs/**", + + // GitHub Actions workflows + "**/.github/**", + + // Editor settings + "**/.vscode/**", + + // Git hooks + "**/.husky/**" + ] + } +} diff --git a/map.json b/map.json deleted file mode 100644 index 69e23d7..0000000 --- a/map.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": 3, - "file": "test.sveltex_final", - "sources": [ - "test.sveltex" - ], - "sourcesContent": [ - "hello\n\ntest1\ntest2\ntest3\ntest4\ntest5\ntest6\ntest7\ntest8\ntest9\n\nthere\n`1`\n{#if condition}\n*a* {mustache}\n{:else}\n**b**\n{/if}\n*italic*\n`code`\nor **bold**" - ], - "names": [], - "mappings": ";;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcA,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC;AAAA;AAEf,CAAC,CAAC,IAAI,CAAC;AAAA;AAEP,CAAC,CAAC,EAAE,CAAC;AAAA;AAAA;AAAA" -} \ No newline at end of file diff --git a/out.html b/out.html deleted file mode 100644 index 8e434c2..0000000 --- a/out.html +++ /dev/null @@ -1,24 +0,0 @@ - -hello - -test1 -test2 -test3 -test4 -test5 -test6 -test7 -test8 -test9 - -there -1 -{#if condition} -a {mustache} -{:else} -b -{/if} -italic -code -or bold diff --git a/package.json b/package.json index f6e75e8..d60f12a 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,8 @@ "build:release": "pnpm run clean && tsc -p tsconfig.release.json && tsc-alias -p tsconfig.release.json", "lint": "eslint . && tsc -p tsconfig.check.json", "test": "vitest run --coverage --watch --", - "test:e2e": "playwright test", + "test:e2e": "pnpm test:e2e:prepare && playwright test", + "test:e2e:prepare": "pnpm build:release && cd e2e/full-examples/unified && pnpm i && cd ../micromark && pnpm i && cd ../marked && pnpm i && cd ../markdown-it && pnpm i && cd ../../..", "format": "prettier --write .", "test:watch": "vitest --watch", "prepare": "husky" @@ -59,30 +60,26 @@ "devDependencies": { "@commitlint/config-conventional": "^19.2.2", "@commitlint/types": "^19.0.3", - "@playwright/test": "^1.43.1", - "@sveltejs/kit": "^2.5.7", + "@playwright/test": "^1.44.0", "@types/clean-css": "^4.2.11", "@types/estree": "^1.0.5", "@types/hast": "^3.0.4", - "@types/jsdom": "^21.1.6", "@types/katex": "^0.16.7", "@types/markdown-it": "^14.1.1", "@types/mathjax": "^0.0.40", - "@types/mathjax-node": "^2.1.0", - "@types/mdast": "^4.0.3", "@types/mock-fs": "^4.13.4", - "@types/node": "^20.12.8", - "@types/prismjs": "^1.26.3", - "@types/semver": "^7.5.8", + "@types/node": "^20.12.12", + "@types/prismjs": "^1.26.4", "@types/uuid": "^9.0.8", - "@typescript-eslint/eslint-plugin": "^7.8.0", - "@typescript-eslint/parser": "^7.8.0", + "@typescript-eslint/eslint-plugin": "8.0.0-alpha.13", + "@typescript-eslint/parser": "8.0.0-alpha.13", + "@typescript-eslint/utils": "8.0.0-alpha.13", "@vitest/coverage-v8": "^1.6.0", "@wooorm/starry-night": "^3.3.0", "commitlint": "^19.3.0", - "eslint": "^8.57.0", + "eslint": "^9.3.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-playwright": "^1.6.0", + "eslint-plugin-playwright": "^1.6.1", "eslint-plugin-tsdoc": "^0.2.17", "eslint-plugin-vitest": "^0.5.4", "hast-util-find-and-replace": "^5.0.1", @@ -96,7 +93,6 @@ "marked": "^12.0.2", "mathjax": "^3.2.2", "mathjax-full": "3.2.2", - "mathjax-node": "^2.1.1", "micromark": "^4.0.0", "mock-fs": "^5.2.0", "prettier": "^3.2.5", @@ -106,12 +102,10 @@ "rehype-stringify": "^10.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.0", - "source-map": "^0.7.4", - "svelte": "^4.2.15", "ts-node": "^10.9.2", - "tsc-alias": "^1.8.8", + "tsc-alias": "^1.8.10", "typescript": "^5.4.5", - "typescript-eslint": "^7.8.0", + "typescript-eslint": "8.0.0-alpha.13", "unified": "^11.0.4", "vite": "^5.2.11", "vite-tsconfig-paths": "^4.3.2", @@ -119,30 +113,42 @@ }, "dependencies": { "@types/html-escaper": "^3.0.2", + "@types/mdast": "^4.0.4", "@types/unist": "^3.0.2", "clean-css": "^5.3.3", "deepmerge-ts": "^5.1.0", "find-cache-dir": "^5.0.0", - "glob": "^10.3.12", + "glob": "^10.3.15", "html-escaper": "^3.0.3", "html-tag-names": "^2.1.0", "magic-string": "^0.30.10", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-math": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "micromark-core-commonmark": "^2.0.1", + "micromark-extension-math": "^3.0.0", + "micromark-extension-mdx-expression": "^3.0.0", + "micromark-extension-mdx-jsx": "^3.0.0", + "micromark-extension-mdx-md": "^2.0.0", + "micromark-util-character": "^2.1.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", "node-fetch": "^3.3.2", "ora": "^8.0.1", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "pretty-bytes": "^6.1.1", "radash": "^12.1.0", "rfdc": "^1.3.1", - "rimraf": "^5.0.5", - "semver": "^7.6.2", - "sorcery": "^0.11.0", - "svgo": "^3.2.0", + "rimraf": "^5.0.7", + "svelte": "^4.2.17", + "svgo": "^3.3.2", "tsafe": "^1.6.6", "uuid": "^9.0.1", + "xregexp": "^5.1.1", "yaml": "^2.4.2" }, "peerDependencies": { - "@types/mdast": "^4.0.3", "@wooorm/starry-night": "^3.3.0", "hast-util-find-and-replace": "^5.0.1", "hast-util-to-html": "^9.0.1", @@ -151,13 +157,11 @@ "markdown-it": "^14.1.0", "marked": "^12.0.1", "mathjax-full": "^3.2.2 || ^4.0.0", - "mathjax-node": "^2.1.1", "micromark": "^4.0.0", "prismjs": "^1.29.0", "rehype-stringify": "^10.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.0", - "svelte": ">=4", "unified": "^11.0.4" }, "peerDependenciesMeta": { @@ -185,9 +189,6 @@ "mathjax-full": { "optional": true }, - "mathjax-node": { - "optional": true - }, "markdown-it": { "optional": true }, diff --git a/playwright.config.ts b/playwright.config.ts index 16c3fed..8dcdf0d 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -61,18 +61,33 @@ export const config = defineConfig({ /* Run your local dev server before starting the tests */ webServer: [ - // { - // cwd: './e2e/full-examples/markdown-it', - // command: 'pnpm build && pnpm preview', - // url: 'http://127.0.0.1:3001', - // reuseExistingServer: !process.env['CI'], - // }, + { + cwd: './e2e/full-examples/markdown-it/', + command: 'pnpm build && pnpm preview', + url: 'http://localhost:3001', + reuseExistingServer: !process.env['CI'], + timeout: 40000, + }, + { + cwd: './e2e/full-examples/marked/', + command: 'pnpm build && pnpm preview', + url: 'http://localhost:3002', + reuseExistingServer: !process.env['CI'], + timeout: 40000, + }, + { + cwd: './e2e/full-examples/micromark/', + command: 'pnpm build && pnpm preview', + url: 'http://localhost:3003', + reuseExistingServer: !process.env['CI'], + timeout: 40000, + }, { cwd: './e2e/full-examples/unified/', command: 'pnpm build && pnpm preview', url: 'http://localhost:3004/', - // reuseExistingServer: !process.env['CI'], - timeout: 20000, + reuseExistingServer: !process.env['CI'], + timeout: 40000, }, ], }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f20692..ffeb471 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@types/html-escaper': specifier: ^3.0.2 version: 3.0.2 + '@types/mdast': + specifier: ^4.0.4 + version: 4.0.4 '@types/unist': specifier: ^3.0.2 version: 3.0.2 @@ -24,8 +27,8 @@ importers: specifier: ^5.0.0 version: 5.0.0 glob: - specifier: ^10.3.12 - version: 10.3.12 + specifier: ^10.3.15 + version: 10.3.15 html-escaper: specifier: ^3.0.3 version: 3.0.3 @@ -35,6 +38,42 @@ importers: magic-string: specifier: ^0.30.10 version: 0.30.10 + mdast-util-from-markdown: + specifier: ^2.0.0 + version: 2.0.0 + mdast-util-math: + specifier: ^3.0.0 + version: 3.0.0 + mdast-util-mdx-expression: + specifier: ^2.0.0 + version: 2.0.0 + micromark-core-commonmark: + specifier: ^2.0.1 + version: 2.0.1 + micromark-extension-math: + specifier: ^3.0.0 + version: 3.0.0 + micromark-extension-mdx-expression: + specifier: ^3.0.0 + version: 3.0.0 + micromark-extension-mdx-jsx: + specifier: ^3.0.0 + version: 3.0.0 + micromark-extension-mdx-md: + specifier: ^2.0.0 + version: 2.0.0 + micromark-util-character: + specifier: ^2.1.0 + version: 2.1.0 + micromark-util-html-tag-name: + specifier: ^2.0.0 + version: 2.0.0 + micromark-util-symbol: + specifier: ^2.0.0 + version: 2.0.0 + micromark-util-types: + specifier: ^2.0.0 + version: 2.0.0 node-fetch: specifier: ^3.3.2 version: 3.3.2 @@ -42,8 +81,8 @@ importers: specifier: ^8.0.1 version: 8.0.1 picocolors: - specifier: ^1.0.0 - version: 1.0.0 + specifier: ^1.0.1 + version: 1.0.1 pretty-bytes: specifier: ^6.1.1 version: 6.1.1 @@ -54,23 +93,23 @@ importers: specifier: ^1.3.1 version: 1.3.1 rimraf: - specifier: ^5.0.5 - version: 5.0.5 - semver: - specifier: ^7.6.2 - version: 7.6.2 - sorcery: - specifier: ^0.11.0 - version: 0.11.0 + specifier: ^5.0.7 + version: 5.0.7 + svelte: + specifier: ^4.2.17 + version: 4.2.17 svgo: - specifier: ^3.2.0 - version: 3.2.0 + specifier: ^3.3.2 + version: 3.3.2 tsafe: specifier: ^1.6.6 version: 1.6.6 uuid: specifier: ^9.0.1 version: 9.0.1 + xregexp: + specifier: ^5.1.1 + version: 5.1.1 yaml: specifier: ^2.4.2 version: 2.4.2 @@ -82,11 +121,8 @@ importers: specifier: ^19.0.3 version: 19.0.3 '@playwright/test': - specifier: ^1.43.1 - version: 1.43.1 - '@sveltejs/kit': - specifier: ^2.5.7 - version: 2.5.7(@sveltejs/vite-plugin-svelte@3.1.0(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8)))(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8)) + specifier: ^1.44.0 + version: 1.44.0 '@types/clean-css': specifier: ^4.2.11 version: 4.2.11 @@ -96,9 +132,6 @@ importers: '@types/hast': specifier: ^3.0.4 version: 3.0.4 - '@types/jsdom': - specifier: ^21.1.6 - version: 21.1.6 '@types/katex': specifier: ^0.16.7 version: 0.16.7 @@ -108,57 +141,51 @@ importers: '@types/mathjax': specifier: ^0.0.40 version: 0.0.40 - '@types/mathjax-node': - specifier: ^2.1.0 - version: 2.1.0 - '@types/mdast': - specifier: ^4.0.3 - version: 4.0.3 '@types/mock-fs': specifier: ^4.13.4 version: 4.13.4 '@types/node': - specifier: ^20.12.8 - version: 20.12.8 + specifier: ^20.12.12 + version: 20.12.12 '@types/prismjs': - specifier: ^1.26.3 - version: 1.26.3 - '@types/semver': - specifier: ^7.5.8 - version: 7.5.8 + specifier: ^1.26.4 + version: 1.26.4 '@types/uuid': specifier: ^9.0.8 version: 9.0.8 '@typescript-eslint/eslint-plugin': - specifier: ^7.8.0 - version: 7.8.0(@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) + specifier: 8.0.0-alpha.13 + version: 8.0.0-alpha.13(@typescript-eslint/parser@8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5) '@typescript-eslint/parser': - specifier: ^7.8.0 - version: 7.8.0(eslint@8.57.0)(typescript@5.4.5) + specifier: 8.0.0-alpha.13 + version: 8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/utils': + specifier: 8.0.0-alpha.13 + version: 8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5) '@vitest/coverage-v8': specifier: ^1.6.0 - version: 1.6.0(vitest@1.6.0(@types/node@20.12.8)(jsdom@24.0.0)) + version: 1.6.0(vitest@1.6.0(@types/node@20.12.12)(jsdom@24.0.0)) '@wooorm/starry-night': specifier: ^3.3.0 version: 3.3.0 commitlint: specifier: ^19.3.0 - version: 19.3.0(@types/node@20.12.8)(typescript@5.4.5) + version: 19.3.0(@types/node@20.12.12)(typescript@5.4.5) eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.3.0 + version: 9.3.0 eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) + version: 9.1.0(eslint@9.3.0) eslint-plugin-playwright: - specifier: ^1.6.0 - version: 1.6.0(eslint@8.57.0) + specifier: ^1.6.1 + version: 1.6.1(eslint@9.3.0) eslint-plugin-tsdoc: specifier: ^0.2.17 version: 0.2.17 eslint-plugin-vitest: specifier: ^0.5.4 - version: 0.5.4(@typescript-eslint/eslint-plugin@7.8.0(@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)(vitest@1.6.0(@types/node@20.12.8)(jsdom@24.0.0)) + version: 0.5.4(@typescript-eslint/eslint-plugin@8.0.0-alpha.13(@typescript-eslint/parser@8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5)(vitest@1.6.0(@types/node@20.12.12)(jsdom@24.0.0)) hast-util-find-and-replace: specifier: ^5.0.1 version: 5.0.1 @@ -192,9 +219,6 @@ importers: mathjax-full: specifier: 3.2.2 version: 3.2.2 - mathjax-node: - specifier: ^2.1.1 - version: 2.1.1 micromark: specifier: ^4.0.0 version: 4.0.0 @@ -222,36 +246,30 @@ importers: remark-rehype: specifier: ^11.1.0 version: 11.1.0 - source-map: - specifier: ^0.7.4 - version: 0.7.4 - svelte: - specifier: ^4.2.15 - version: 4.2.15 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.12.8)(typescript@5.4.5) + version: 10.9.2(@types/node@20.12.12)(typescript@5.4.5) tsc-alias: - specifier: ^1.8.8 - version: 1.8.8 + specifier: ^1.8.10 + version: 1.8.10 typescript: specifier: ^5.4.5 version: 5.4.5 typescript-eslint: - specifier: ^7.8.0 - version: 7.8.0(eslint@8.57.0)(typescript@5.4.5) + specifier: 8.0.0-alpha.13 + version: 8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5) unified: specifier: ^11.0.4 version: 11.0.4 vite: specifier: ^5.2.11 - version: 5.2.11(@types/node@20.12.8) + version: 5.2.11(@types/node@20.12.12) vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.8)) + version: 4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.12)) vitest: specifier: ^1.6.0 - version: 1.6.0(@types/node@20.12.8)(jsdom@24.0.0) + version: 1.6.0(@types/node@20.12.12)(jsdom@24.0.0) packages: @@ -280,6 +298,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/runtime-corejs3@7.24.5': + resolution: {integrity: sha512-GWO0mgzNMLWaSYM4z4NVIuY0Cd1fl8cPnuetuddu5w/qGuvt5Y7oUi/kvvQGK9xgOkFJDQX2heIvTRn/OQ1XTg==} + engines: {node: '>=6.9.0'} + '@babel/types@7.24.5': resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==} engines: {node: '>=6.9.0'} @@ -508,16 +530,16 @@ packages: resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@3.1.0': + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@8.57.0': - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/js@9.3.0': + resolution: {integrity: sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} engines: {node: '>=10.10.0'} '@humanwhocodes/module-importer@1.0.1': @@ -527,6 +549,10 @@ packages: '@humanwhocodes/object-schema@2.0.3': resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + '@humanwhocodes/retry@0.3.0': + resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} + engines: {node: '>=18.18'} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -582,14 +608,11 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.43.1': - resolution: {integrity: sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==} + '@playwright/test@1.44.0': + resolution: {integrity: sha512-rNX5lbNidamSUorBhB4XZ9SQTjAqfe5M+p37Z8ic0jPFBMo5iCtQz1kRWkEMg+rYOKSlVycpQmpqjSFq7LXOfg==} engines: {node: '>=16'} hasBin: true - '@polka/url@1.0.0-next.25': - resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} - '@rollup/rollup-android-arm-eabi@4.17.2': resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==} cpu: [arm] @@ -673,30 +696,6 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sveltejs/kit@2.5.7': - resolution: {integrity: sha512-6uedTzrb7nQrw6HALxnPrPaXdIN2jJJTzTIl96Z3P5NiG+OAfpdPbrWrvkJ3GN4CfWqrmU4dJqwMMRMTD/C7ow==} - engines: {node: '>=18.13'} - hasBin: true - peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 - svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.3 - - '@sveltejs/vite-plugin-svelte-inspector@2.1.0': - resolution: {integrity: sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==} - engines: {node: ^18.0.0 || >=20} - peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 - svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.0 - - '@sveltejs/vite-plugin-svelte@3.1.0': - resolution: {integrity: sha512-sY6ncCvg+O3njnzbZexcVtUqOBE3iYmQPJ9y+yXSkOwG576QI/xJrBnQSRXFLGwJNBa0T78JEKg5cIR0WOAuUw==} - engines: {node: ^18.0.0 || >=20} - peerDependencies: - svelte: ^4.0.0 || ^5.0.0-next.0 - vite: ^5.0.0 - '@trysound/sax@0.2.0': resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -713,18 +712,21 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@types/acorn@4.0.6': + resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} + '@types/clean-css@4.2.11': resolution: {integrity: sha512-Y8n81lQVTAfP2TOdtJJEsCoYl1AnOkqDqMvXb9/7pfgZZ7r8YrEyurrAvAoAjHOGXKRybay+5CsExqIH6liccw==} '@types/conventional-commits-parser@5.0.0': resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} - '@types/cookie@0.6.0': - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} @@ -734,9 +736,6 @@ packages: '@types/html-escaper@3.0.2': resolution: {integrity: sha512-A8vk09eyYzk8J/lFO4OUMKCmRN0rRzfZf4n3Olwapgox/PtTiU8zPYlL1UEkJ/WeHvV6v9Xnj3o/705PKz9r4Q==} - '@types/jsdom@21.1.6': - resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -749,14 +748,11 @@ packages: '@types/markdown-it@14.1.1': resolution: {integrity: sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==} - '@types/mathjax-node@2.1.0': - resolution: {integrity: sha512-s1i5U1yVQBINaWuIa/950yxLRvF1DdaB+P4+hm+CjchbkjoufBnTQC91Rw7qFN2Z9UYvJQ4GPHdHlCcgiNx9oA==} - '@types/mathjax@0.0.40': resolution: {integrity: sha512-rHusx08LCg92WJxrsM3SPjvLTSvK5C+gealtSuhKbEOcUZfWlwigaFoPLf6Dfxhg4oryN5qP9Sj7zOQ4HYXINw==} - '@types/mdast@4.0.3': - resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} @@ -767,65 +763,69 @@ packages: '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - '@types/node@20.12.8': - resolution: {integrity: sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==} + '@types/node@20.12.12': + resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==} - '@types/prismjs@1.26.3': - resolution: {integrity: sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==} + '@types/prismjs@1.26.4': + resolution: {integrity: sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==} '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - '@types/tough-cookie@4.0.5': - resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} - '@types/unist@3.0.2': resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@typescript-eslint/eslint-plugin@7.8.0': - resolution: {integrity: sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/eslint-plugin@8.0.0-alpha.13': + resolution: {integrity: sha512-FQeu2HGVZ6wDAn2m6MxpS+or7LyBEjjCXnFv79aCJtcJnxtgDQa0po88GHZv1tZwzIsgxQ3bnbz4vNgbPisMbA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/parser@7.8.0': - resolution: {integrity: sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/parser@8.0.0-alpha.13': + resolution: {integrity: sha512-CWFhVzrA6n7OeTHmvtxxUy+BSvUwHl8BcT6QsVi87MGcGQpLQZ1TeU0kYoIISkzFojoULF5q0de9hP/FbANi/g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/scope-manager@7.8.0': - resolution: {integrity: sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==} + '@typescript-eslint/scope-manager@7.9.0': + resolution: {integrity: sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/type-utils@7.8.0': - resolution: {integrity: sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==} - engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/scope-manager@8.0.0-alpha.13': + resolution: {integrity: sha512-5XA+tfDumd6Y5GWVFMF2810HB8GIijMawdLgMptSeN3CeOfxQB1J3EBtmbgSWuValNSvV6jujIxKRVgnrZ/WpA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.0.0-alpha.13': + resolution: {integrity: sha512-4Rkwf4YaQIqeF/l2/F9hqxiWlr9P5a0M7Ep0kK99WDYDAhABraIgvsScd2PAtKU1smJXdozbqKDywKCg6etH5Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@typescript-eslint/types@7.8.0': - resolution: {integrity: sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==} + '@typescript-eslint/types@7.9.0': + resolution: {integrity: sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/typescript-estree@7.8.0': - resolution: {integrity: sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==} + '@typescript-eslint/types@8.0.0-alpha.13': + resolution: {integrity: sha512-uJN8BSa8YHs9E/lFR9nkzLyaEijXGzMLgkh8DuQMlOP8epqJ9Jnzx+gOFUIyJV+C8uavpGp+YnCMvq4IDG9L+Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@7.9.0': + resolution: {integrity: sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' @@ -833,16 +833,35 @@ packages: typescript: optional: true - '@typescript-eslint/utils@7.8.0': - resolution: {integrity: sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==} + '@typescript-eslint/typescript-estree@8.0.0-alpha.13': + resolution: {integrity: sha512-p5SZlikPhHg4510ttfqwjwMgBG7ehzRj5dLk8JR5x9bV0D8zWNotqkWNa0vftbYHb+mdGV6D9zNrJwDjWVd4ag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@7.9.0': + resolution: {integrity: sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 - '@typescript-eslint/visitor-keys@7.8.0': - resolution: {integrity: sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==} + '@typescript-eslint/utils@8.0.0-alpha.13': + resolution: {integrity: sha512-BMAbK/fC7RgvRjGQh8AixfQErFXIpCqL1DfFmMnp+oBZvYLON/4/iIj0yPPpY3E8gbljT7s4LHVkNSu7K/MQug==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + + '@typescript-eslint/visitor-keys@7.9.0': + resolution: {integrity: sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==} engines: {node: ^18.18.0 || >=20.0.0} + '@typescript-eslint/visitor-keys@8.0.0-alpha.13': + resolution: {integrity: sha512-q8r/RVc5AtAnnRGqKBIoWMwChET2hqydVGqAXlvXXnB6JOE3prpMLvnpUp7JK1jma3VPt2JieCGhzX3YZSLY/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} @@ -873,36 +892,15 @@ packages: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true - abab@2.0.6: - resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} - deprecated: Use your platform's native atob() and btoa() methods instead - - acorn-globals@4.3.4: - resolution: {integrity: sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==} - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@6.2.0: - resolution: {integrity: sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==} - engines: {node: '>=0.4.0'} - acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} - acorn@5.7.4: - resolution: {integrity: sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==} - engines: {node: '>=0.4.0'} - hasBin: true - - acorn@6.4.2: - resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.11.3: resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} engines: {node: '>=0.4.0'} @@ -955,9 +953,6 @@ packages: aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} - array-equal@1.0.2: - resolution: {integrity: sha512-gUHx76KtnhEgB3HOuFYiCm3FIdEs6ocM2asHvNTkfu/Y09qQVrrVVaOKENmS2KkSaGoxgXNqC+ZVtR/n0MOkSA==} - array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} @@ -965,28 +960,12 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - asn1@0.2.6: - resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} - - assert-plus@1.0.0: - resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} - engines: {node: '>=0.8'} - assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - async-limiter@1.0.1: - resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - aws-sign2@0.7.0: - resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - - aws4@1.12.0: - resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==} - axobject-query@4.0.0: resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} @@ -996,9 +975,6 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bcrypt-pbkdf@1.0.2: - resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} - binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -1016,12 +992,6 @@ packages: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} - browser-process-hrtime@1.0.0: - resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} - - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1030,9 +1000,6 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caseless@0.12.0: - resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1153,12 +1120,8 @@ packages: engines: {node: '>=16'} hasBin: true - cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - - core-util-is@1.0.2: - resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + core-js-pure@3.37.1: + resolution: {integrity: sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==} cosmiconfig-typescript-loader@5.0.0: resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} @@ -1203,12 +1166,6 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} - cssom@0.3.8: - resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} - - cssstyle@1.4.0: - resolution: {integrity: sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==} - cssstyle@4.0.1: resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} engines: {node: '>=18'} @@ -1217,17 +1174,10 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} - dashdash@1.14.1: - resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} - engines: {node: '>=0.10'} - data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} - data-urls@1.1.0: - resolution: {integrity: sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==} - data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} @@ -1258,10 +1208,6 @@ packages: resolution: {integrity: sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==} engines: {node: '>=16.0.0'} - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1270,9 +1216,6 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - devalue@5.0.0: - resolution: {integrity: sha512-gO+/OMXF7488D+u3ue+G7Y4AA3ZmUnB3eHJXmBTgNHvr4ZNzl36A0ZtG+XCRNYCkYx/bFmw4qtkoFLa+wSrwAA==} - devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -1288,20 +1231,12 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - domexception@1.0.1: - resolution: {integrity: sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==} - deprecated: Use your platform's native DOMException instead - domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} @@ -1316,9 +1251,6 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - ecc-jsbn@0.1.2: - resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} - emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} @@ -1339,9 +1271,6 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es6-promise@3.3.1: - resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} - esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} @@ -1363,19 +1292,14 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - escodegen@1.14.3: - resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} - engines: {node: '>=4.0'} - hasBin: true - eslint-config-prettier@9.1.0: resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-plugin-playwright@1.6.0: - resolution: {integrity: sha512-tI1E/EDbHT4Fx5KvukUG3RTIT0gk44gvTP8bNwxLCFsUXVM98ZJG5zWU6Om5JOzH9FrmN4AhMu/UKyEsu0ZoDA==} + eslint-plugin-playwright@1.6.1: + resolution: {integrity: sha512-pfcaeAENH0jZEuUxynfVgMlLY9CSp68D9Ja2z32Xq5zhWCN1HoixSHOzhYVuh/N6fYwtBp4kLu/+IIUTXBTGZg==} engines: {node: '>=16.6.0'} peerDependencies: eslint: '>=8.40.0' @@ -1400,34 +1324,30 @@ packages: vitest: optional: true - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.0.1: + resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true + eslint-visitor-keys@4.0.0: + resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - esm-env@1.0.0: - resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} + eslint@9.3.0: + resolution: {integrity: sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} engines: {node: '>=6'} - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true + espree@10.0.1: + resolution: {integrity: sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} @@ -1437,14 +1357,16 @@ packages: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} - estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -1459,10 +1381,6 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - extsprintf@1.3.0: - resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} - engines: {'0': node >=0.6.0} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1483,9 +1401,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} @@ -1507,9 +1425,9 @@ packages: resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} engines: {node: '>=18'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} @@ -1518,13 +1436,6 @@ packages: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} - forever-agent@0.6.1: - resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - - form-data@2.3.3: - resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} - engines: {node: '>= 0.12'} - form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} @@ -1564,9 +1475,6 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} - getpass@0.1.7: - resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} - git-raw-commits@4.0.0: resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} engines: {node: '>=16'} @@ -1580,9 +1488,9 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.3.12: - resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} - engines: {node: '>=16 || 14 >=14.17'} + glob@10.3.15: + resolution: {integrity: sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==} + engines: {node: '>=16 || 14 >=14.18'} hasBin: true glob@7.2.3: @@ -1596,8 +1504,9 @@ packages: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} - globalyzer@0.1.0: - resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -1606,21 +1515,9 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - har-schema@2.0.0: - resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} - engines: {node: '>=4'} - - har-validator@5.1.5: - resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} - engines: {node: '>=6'} - deprecated: this library is no longer supported - has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -1660,8 +1557,8 @@ packages: hast-util-phrasing@3.0.1: resolution: {integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==} - hast-util-raw@9.0.2: - resolution: {integrity: sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA==} + hast-util-raw@9.0.3: + resolution: {integrity: sha512-ICWvVOF2fq4+7CMmtCPD5CM4QKjPbHpPotE6+8tDooV0ZuyJVUzHsrNX+O5NaRbieTf0F7FfeBOMAwi6Td0+yQ==} hast-util-to-html@9.0.1: resolution: {integrity: sha512-hZOofyZANbyWo+9RP75xIDV/gq+OUKx+T46IlwERnKmfpwp81XBFbT9mi26ws+SJchA4RVUQwIBJpqEOBhMzEQ==} @@ -1679,9 +1576,6 @@ packages: resolution: {integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==} engines: {node: '>=12.0.0'} - html-encoding-sniffer@1.0.2: - resolution: {integrity: sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==} - html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -1705,10 +1599,6 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - http-signature@1.2.0: - resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} - engines: {node: '>=0.8', npm: '>=1.3.7'} - https-proxy-agent@7.0.4: resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} engines: {node: '>= 14'} @@ -1722,10 +1612,6 @@ packages: engines: {node: '>=18'} hasBin: true - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -1769,10 +1655,6 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@2.0.0: - resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} - engines: {node: '>=4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -1815,9 +1697,6 @@ packages: resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} engines: {node: '>=8'} - is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} @@ -1829,9 +1708,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isstream@0.1.2: - resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -1869,12 +1745,6 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsbn@0.1.1: - resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - - jsdom@11.12.0: - resolution: {integrity: sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==} - jsdom@24.0.0: resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==} engines: {node: '>=18'} @@ -1896,23 +1766,13 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-schema@0.4.0: - resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - jsonparse@1.3.1: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} - jsprim@1.4.2: - resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} - engines: {node: '>=0.6.0'} - katex@0.16.10: resolution: {integrity: sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==} hasBin: true @@ -1920,18 +1780,6 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - - left-pad@1.3.0: - resolution: {integrity: sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==} - deprecated: use String.prototype.padStart() - - levn@0.3.0: - resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} - engines: {node: '>= 0.8.0'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1975,9 +1823,6 @@ packages: lodash.snakecase@4.1.1: resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} - lodash.sortby@4.7.0: - resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} @@ -1987,13 +1832,13 @@ packages: lodash.upperfirst@4.3.1: resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - log-symbols@6.0.0: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loupe@2.3.7: resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} @@ -2032,22 +1877,27 @@ packages: mathjax-full@3.2.2: resolution: {integrity: sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==} - mathjax-node@2.1.1: - resolution: {integrity: sha512-i29tvqD8yHPB2WhrGV5rvliYnKwTT8a/TO8SCnuYtatpSHxLGy3aF7lDTVLD6B1bfuVMTFB6McZu2TBxk0XGeg==} - engines: {node: '>=6.0.0'} - - mathjax@2.7.9: - resolution: {integrity: sha512-NOGEDTIM9+MrsqnjPEjVGNx4q0GQxqm61yQwSK+/5S59i26wId5IC5gNu9/bu8+CCVl5p9G2IHcAl/wJa+5+BQ==} - mathjax@3.2.2: resolution: {integrity: sha512-Bt+SSVU8eBG27zChVewOicYs7Xsdt40qm4+UpHyX7k0/O9NliPc+x77k1/FEsPsjKPZGJvtRZM1vO+geW0OhGw==} mdast-util-from-markdown@2.0.0: resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==} + mdast-util-math@3.0.0: + resolution: {integrity: sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==} + + mdast-util-mdx-expression@2.0.0: + resolution: {integrity: sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + mdast-util-to-hast@13.1.0: resolution: {integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==} + mdast-util-to-markdown@2.1.0: + resolution: {integrity: sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==} + mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} @@ -2077,12 +1927,27 @@ packages: micromark-core-commonmark@2.0.1: resolution: {integrity: sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==} + micromark-extension-math@3.0.0: + resolution: {integrity: sha512-iJ2Q28vBoEovLN5o3GO12CpqorQRYDPT+p4zW50tGwTfJB+iv/VnB6Ini+gqa24K97DwptMBBIvVX6Bjk49oyQ==} + + micromark-extension-mdx-expression@3.0.0: + resolution: {integrity: sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ==} + + micromark-extension-mdx-jsx@3.0.0: + resolution: {integrity: sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w==} + + micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + micromark-factory-destination@2.0.0: resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==} micromark-factory-label@2.0.0: resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==} + micromark-factory-mdx-expression@2.0.1: + resolution: {integrity: sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg==} + micromark-factory-space@2.0.0: resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==} @@ -2113,6 +1978,9 @@ packages: micromark-util-encode@2.0.0: resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + micromark-util-events-to-acorn@2.0.2: + resolution: {integrity: sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA==} + micromark-util-html-tag-name@2.0.0: resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==} @@ -2167,17 +2035,13 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.0: - resolution: {integrity: sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig==} + minipass@7.1.1: + resolution: {integrity: sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==} engines: {node: '>=16 || 14 >=14.17'} mj-context-menu@0.6.1: resolution: {integrity: sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==} - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - mlly@1.7.0: resolution: {integrity: sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==} @@ -2185,14 +2049,6 @@ packages: resolution: {integrity: sha512-2dF2R6YMSZbpip1V1WHKGLNjr/k48uQClqMVb5H3MOvwc9qhYis3/IWbj02qIg/Y8MDXKFF4c5v0rxx2o6xTZw==} engines: {node: '>=12.0.0'} - mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - - mrmime@2.0.0: - resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} - engines: {node: '>=10'} - ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -2227,11 +2083,8 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - nwsapi@2.2.9: - resolution: {integrity: sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==} - - oauth-sign@0.9.0: - resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} + nwsapi@2.2.10: + resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==} once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -2244,10 +2097,6 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} - optionator@0.8.3: - resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} - engines: {node: '>= 0.8.0'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2284,9 +2133,6 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - parse5@4.0.0: - resolution: {integrity: sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==} - parse5@7.1.2: resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} @@ -2313,9 +2159,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.10.2: - resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==} - engines: {node: '>=16 || 14 >=14.17'} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -2327,14 +2173,11 @@ packages: pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - performance-now@2.1.0: - resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} - picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} @@ -2344,16 +2187,16 @@ packages: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} - pkg-types@1.1.0: - resolution: {integrity: sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==} + pkg-types@1.1.1: + resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} - playwright-core@1.43.1: - resolution: {integrity: sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==} + playwright-core@1.44.0: + resolution: {integrity: sha512-ZTbkNpFfYcGWohvTTl+xewITm7EOuqIqex0c7dNZ+aXsbrLj0qI8XlGKfPpipjm0Wny/4Lt4CJsWJk1stVS5qQ==} engines: {node: '>=16'} hasBin: true - playwright@1.43.1: - resolution: {integrity: sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==} + playwright@1.44.0: + resolution: {integrity: sha512-F9b3GUCLQ3Nffrfb6dunPOkE5Mh68tR7zN32L4jCk4FjQamgesGay7/dAAe1WaMEGV04DkdJfcJzjoCKygUaRQ==} engines: {node: '>=16'} hasBin: true @@ -2361,17 +2204,10 @@ packages: resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} engines: {node: '>=12'} - pn@1.1.0: - resolution: {integrity: sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==} - postcss@8.4.38: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} - prelude-ls@1.1.2: - resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} - engines: {node: '>= 0.8.0'} - prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2407,10 +2243,6 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.5.3: - resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} - engines: {node: '>=0.6'} - querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -2432,6 +2264,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + rehype-format@5.0.0: resolution: {integrity: sha512-kM4II8krCHmUhxrlvzFSptvaWh280Fr7UGNJU5DCMuvmAwGCNmGfi9CvFAQK6JDjsNoRMWQStglK3zKJH685Wg==} @@ -2453,24 +2288,6 @@ packages: remark-rehype@11.1.0: resolution: {integrity: sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==} - request-promise-core@1.1.4: - resolution: {integrity: sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==} - engines: {node: '>=0.10.0'} - peerDependencies: - request: ^2.34 - - request-promise-native@1.0.9: - resolution: {integrity: sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==} - engines: {node: '>=0.12.0'} - deprecated: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142 - peerDependencies: - request: ^2.34 - - request@2.88.2: - resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} - engines: {node: '>= 6'} - deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -2504,17 +2321,9 @@ packages: rfdc@1.3.1: resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true - - rimraf@5.0.5: - resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==} - engines: {node: '>=14'} + rimraf@5.0.7: + resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==} + engines: {node: '>=14.18'} hasBin: true rollup@4.17.2: @@ -2528,22 +2337,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - sade@1.8.1: - resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} - engines: {node: '>=6'} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sander@0.5.1: - resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} - - sax@1.3.0: - resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} - saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -2553,9 +2349,6 @@ packages: engines: {node: '>=10'} hasBin: true - set-cookie-parser@2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2574,18 +2367,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sirv@2.0.4: - resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} - engines: {node: '>= 10'} - slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - sorcery@0.11.0: - resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} - hasBin: true - source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} @@ -2594,10 +2379,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.7.4: - resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} - engines: {node: '>= 8'} - space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -2609,11 +2390,6 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - sshpk@1.18.0: - resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} - engines: {node: '>=0.10.0'} - hasBin: true - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -2624,10 +2400,6 @@ packages: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} - stealthy-require@1.1.1: - resolution: {integrity: sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==} - engines: {node: '>=0.10.0'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2670,18 +2442,12 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - svelte-hmr@0.16.0: - resolution: {integrity: sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==} - engines: {node: ^12.20 || ^14.13.1 || >= 16} - peerDependencies: - svelte: ^3.19.0 || ^4.0.0 - - svelte@4.2.15: - resolution: {integrity: sha512-j9KJSccHgLeRERPlhMKrCXpk2TqL2m5Z+k+OBTQhZOhIdCCd3WfqV+ylPWeipEwq17P/ekiSFWwrVQv93i3bsg==} + svelte@4.2.17: + resolution: {integrity: sha512-N7m1YnoXtRf5wya5Gyx3TWuTddI4nAyayyIWFojiWV5IayDYNV5i2mRp/7qNGol4DtxEYxljmrbgp1HM6hUbmQ==} engines: {node: '>=16'} - svgo@3.2.0: - resolution: {integrity: sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==} + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} engines: {node: '>=14.0.0'} hasBin: true @@ -2702,9 +2468,6 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - tiny-glob@0.2.9: - resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} - tinybench@2.8.0: resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} @@ -2724,21 +2487,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - - tough-cookie@2.5.0: - resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} - engines: {node: '>=0.8'} - tough-cookie@4.1.4: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} - tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - tr46@5.0.0: resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} engines: {node: '>=18'} @@ -2772,8 +2524,8 @@ packages: tsafe@1.6.6: resolution: {integrity: sha512-gzkapsdbMNwBnTIjgO758GujLCj031IgHK/PKr2mrmkCSJMhSOR5FeOuSxKLMUoYc0vAA4RGEYYbjt/v6afD3g==} - tsc-alias@1.8.8: - resolution: {integrity: sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==} + tsc-alias@1.8.10: + resolution: {integrity: sha512-Ibv4KAWfFkFdKJxnWfVtdOmB0Zi1RJVxcbPGiCDsFpCQSsmpWyuzHG3rQyI5YkobWwxFPEyQfu1hdo4qLG2zPw==} hasBin: true tsconfck@3.0.3: @@ -2786,16 +2538,6 @@ packages: typescript: optional: true - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - - tweetnacl@0.14.5: - resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - - type-check@0.3.2: - resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} - engines: {node: '>= 0.8.0'} - type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -2808,11 +2550,10 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} - typescript-eslint@7.8.0: - resolution: {integrity: sha512-sheFG+/D8N/L7gC3WT0Q8sB97Nm573Yfr+vZFzl/4nBdYcmviBPtwGSX9TJ7wpVg28ocerKVOt+k2eGmHzcgVA==} - engines: {node: ^18.18.0 || >=20.0.0} + typescript-eslint@8.0.0-alpha.13: + resolution: {integrity: sha512-DMFKGlZTYPGrjejz1G8Cp0LuvxlelQqy1BY/T95yNVK13aVYTnDrAZ5ytW9a79OzserqBDoJnXs1AFaUqJoNEg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: @@ -2842,9 +2583,15 @@ packages: unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + unist-util-remove-position@5.0.0: + resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} + unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} @@ -2864,11 +2611,6 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - uuid@3.4.0: - resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} - deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. - hasBin: true - uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -2876,10 +2618,6 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - verror@1.10.0: - resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} - engines: {'0': node >=0.6.0} - vfile-location@5.0.2: resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==} @@ -2930,14 +2668,6 @@ packages: terser: optional: true - vitefu@0.2.5: - resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} - peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - vite: - optional: true - vitest@1.6.0: resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -2969,10 +2699,6 @@ packages: vscode-textmate@9.0.0: resolution: {integrity: sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg==} - w3c-hr-time@1.0.2: - resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} - deprecated: Use your platform's native performance.now() and performance.timeOrigin. - w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -2984,23 +2710,14 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} - webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} - whatwg-encoding@1.0.5: - resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} - whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} - whatwg-mimetype@2.3.0: - resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} - whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} @@ -3009,12 +2726,6 @@ packages: resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} engines: {node: '>=18'} - whatwg-url@6.5.0: - resolution: {integrity: sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==} - - whatwg-url@7.1.0: - resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3043,17 +2754,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@5.2.3: - resolution: {integrity: sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.17.0: resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} engines: {node: '>=10.0.0'} @@ -3066,9 +2766,6 @@ packages: utf-8-validate: optional: true - xml-name-validator@3.0.0: - resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} - xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} @@ -3080,6 +2777,9 @@ packages: resolution: {integrity: sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==} engines: {node: '>=0.1'} + xregexp@5.1.1: + resolution: {integrity: sha512-fKXeVorD+CzWvFs7VBuKTYIW63YD1e1osxwQ8caZ6o1jg6pDAbABDG54LCIq0j5cy7PjRvGIq6sef9DYPXpncg==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -3122,7 +2822,7 @@ snapshots: '@babel/code-frame@7.24.2': dependencies: '@babel/highlight': 7.24.5 - picocolors: 1.0.0 + picocolors: 1.0.1 '@babel/helper-string-parser@7.24.1': {} @@ -3133,12 +2833,17 @@ snapshots: '@babel/helper-validator-identifier': 7.24.5 chalk: 2.4.2 js-tokens: 4.0.0 - picocolors: 1.0.0 + picocolors: 1.0.1 '@babel/parser@7.24.5': dependencies: '@babel/types': 7.24.5 + '@babel/runtime-corejs3@7.24.5': + dependencies: + core-js-pure: 3.37.1 + regenerator-runtime: 0.14.1 + '@babel/types@7.24.5': dependencies: '@babel/helper-string-parser': 7.24.1 @@ -3147,11 +2852,11 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@commitlint/cli@19.3.0(@types/node@20.12.8)(typescript@5.4.5)': + '@commitlint/cli@19.3.0(@types/node@20.12.12)(typescript@5.4.5)': dependencies: '@commitlint/format': 19.3.0 '@commitlint/lint': 19.2.2 - '@commitlint/load': 19.2.0(@types/node@20.12.8)(typescript@5.4.5) + '@commitlint/load': 19.2.0(@types/node@20.12.12)(typescript@5.4.5) '@commitlint/read': 19.2.1 '@commitlint/types': 19.0.3 execa: 8.0.1 @@ -3198,7 +2903,7 @@ snapshots: '@commitlint/rules': 19.0.3 '@commitlint/types': 19.0.3 - '@commitlint/load@19.2.0(@types/node@20.12.8)(typescript@5.4.5)': + '@commitlint/load@19.2.0(@types/node@20.12.12)(typescript@5.4.5)': dependencies: '@commitlint/config-validator': 19.0.3 '@commitlint/execute-rule': 19.0.0 @@ -3206,7 +2911,7 @@ snapshots: '@commitlint/types': 19.0.3 chalk: 5.3.0 cosmiconfig: 9.0.0(typescript@5.4.5) - cosmiconfig-typescript-loader: 5.0.0(@types/node@20.12.8)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5) + cosmiconfig-typescript-loader: 5.0.0(@types/node@20.12.12)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -3331,19 +3036,19 @@ snapshots: '@esbuild/win32-x64@0.20.2': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.3.0)': dependencies: - eslint: 8.57.0 + eslint: 9.3.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.10.0': {} - '@eslint/eslintrc@2.1.4': + '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 debug: 4.3.4 - espree: 9.6.1 - globals: 13.24.0 + espree: 10.0.1 + globals: 14.0.0 ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -3352,9 +3057,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@8.57.0': {} + '@eslint/js@9.3.0': {} - '@humanwhocodes/config-array@0.11.14': + '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 debug: 4.3.4 @@ -3366,6 +3071,8 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} + '@humanwhocodes/retry@0.3.0': {} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -3427,11 +3134,9 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.43.1': + '@playwright/test@1.44.0': dependencies: - playwright: 1.43.1 - - '@polka/url@1.0.0-next.25': {} + playwright: 1.44.0 '@rollup/rollup-android-arm-eabi@4.17.2': optional: true @@ -3483,47 +3188,6 @@ snapshots: '@sinclair/typebox@0.27.8': {} - '@sveltejs/kit@2.5.7(@sveltejs/vite-plugin-svelte@3.1.0(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8)))(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8))': - dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.0(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8)) - '@types/cookie': 0.6.0 - cookie: 0.6.0 - devalue: 5.0.0 - esm-env: 1.0.0 - import-meta-resolve: 4.1.0 - kleur: 4.1.5 - magic-string: 0.30.10 - mrmime: 2.0.0 - sade: 1.8.1 - set-cookie-parser: 2.6.0 - sirv: 2.0.4 - svelte: 4.2.15 - tiny-glob: 0.2.9 - vite: 5.2.11(@types/node@20.12.8) - - '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.0(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8)))(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8))': - dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.0(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8)) - debug: 4.3.4 - svelte: 4.2.15 - vite: 5.2.11(@types/node@20.12.8) - transitivePeerDependencies: - - supports-color - - '@sveltejs/vite-plugin-svelte@3.1.0(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8))': - dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.0(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8)))(svelte@4.2.15)(vite@5.2.11(@types/node@20.12.8)) - debug: 4.3.4 - deepmerge: 4.3.1 - kleur: 4.1.5 - magic-string: 0.30.10 - svelte: 4.2.15 - svelte-hmr: 0.16.0(svelte@4.2.15) - vite: 5.2.11(@types/node@20.12.8) - vitefu: 0.2.5(vite@5.2.11(@types/node@20.12.8)) - transitivePeerDependencies: - - supports-color - '@trysound/sax@0.2.0': {} '@tsconfig/node10@1.0.11': {} @@ -3534,21 +3198,27 @@ snapshots: '@tsconfig/node16@1.0.4': {} + '@types/acorn@4.0.6': + dependencies: + '@types/estree': 1.0.5 + '@types/clean-css@4.2.11': dependencies: - '@types/node': 20.12.8 + '@types/node': 20.12.12 source-map: 0.6.1 '@types/conventional-commits-parser@5.0.0': dependencies: - '@types/node': 20.12.8 - - '@types/cookie@0.6.0': {} + '@types/node': 20.12.12 '@types/debug@4.1.12': dependencies: '@types/ms': 0.7.34 + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.5 + '@types/estree@1.0.5': {} '@types/hast@3.0.4': @@ -3557,12 +3227,6 @@ snapshots: '@types/html-escaper@3.0.2': {} - '@types/jsdom@21.1.6': - dependencies: - '@types/node': 20.12.8 - '@types/tough-cookie': 4.0.5 - parse5: 7.1.2 - '@types/json-schema@7.0.15': {} '@types/katex@0.16.7': {} @@ -3574,13 +3238,9 @@ snapshots: '@types/linkify-it': 5.0.0 '@types/mdurl': 2.0.0 - '@types/mathjax-node@2.1.0': - dependencies: - '@types/mathjax': 0.0.40 - '@types/mathjax@0.0.40': {} - '@types/mdast@4.0.3': + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.2 @@ -3588,34 +3248,32 @@ snapshots: '@types/mock-fs@4.13.4': dependencies: - '@types/node': 20.12.8 + '@types/node': 20.12.12 '@types/ms@0.7.34': {} - '@types/node@20.12.8': + '@types/node@20.12.12': dependencies: undici-types: 5.26.5 - '@types/prismjs@1.26.3': {} + '@types/prismjs@1.26.4': {} '@types/semver@7.5.8': {} - '@types/tough-cookie@4.0.5': {} - '@types/unist@3.0.2': {} '@types/uuid@9.0.8': {} - '@typescript-eslint/eslint-plugin@7.8.0(@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@8.0.0-alpha.13(@typescript-eslint/parser@8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.8.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/type-utils': 7.8.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.8.0 + '@typescript-eslint/parser': 8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/scope-manager': 8.0.0-alpha.13 + '@typescript-eslint/type-utils': 8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/utils': 8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 8.0.0-alpha.13 debug: 4.3.4 - eslint: 8.57.0 + eslint: 9.3.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -3626,42 +3284,64 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/parser@8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5)': dependencies: - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) - '@typescript-eslint/visitor-keys': 7.8.0 + '@typescript-eslint/scope-manager': 8.0.0-alpha.13 + '@typescript-eslint/types': 8.0.0-alpha.13 + '@typescript-eslint/typescript-estree': 8.0.0-alpha.13(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 8.0.0-alpha.13 debug: 4.3.4 - eslint: 8.57.0 + eslint: 9.3.0 optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.8.0': + '@typescript-eslint/scope-manager@7.9.0': dependencies: - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/visitor-keys': 7.8.0 + '@typescript-eslint/types': 7.9.0 + '@typescript-eslint/visitor-keys': 7.9.0 - '@typescript-eslint/type-utils@7.8.0(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/scope-manager@8.0.0-alpha.13': dependencies: - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) - '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/types': 8.0.0-alpha.13 + '@typescript-eslint/visitor-keys': 8.0.0-alpha.13 + + '@typescript-eslint/type-utils@8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5)': + dependencies: + '@typescript-eslint/typescript-estree': 8.0.0-alpha.13(typescript@5.4.5) + '@typescript-eslint/utils': 8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5) debug: 4.3.4 - eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: + - eslint - supports-color - '@typescript-eslint/types@7.8.0': {} + '@typescript-eslint/types@7.9.0': {} + + '@typescript-eslint/types@8.0.0-alpha.13': {} + + '@typescript-eslint/typescript-estree@7.9.0(typescript@5.4.5)': + dependencies: + '@typescript-eslint/types': 7.9.0 + '@typescript-eslint/visitor-keys': 7.9.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.4 + semver: 7.6.2 + ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color - '@typescript-eslint/typescript-estree@7.8.0(typescript@5.4.5)': + '@typescript-eslint/typescript-estree@8.0.0-alpha.13(typescript@5.4.5)': dependencies: - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/visitor-keys': 7.8.0 + '@typescript-eslint/types': 8.0.0-alpha.13 + '@typescript-eslint/visitor-keys': 8.0.0-alpha.13 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -3673,28 +3353,44 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.8.0(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/utils@7.9.0(eslint@9.3.0)(typescript@5.4.5)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) + '@typescript-eslint/scope-manager': 7.9.0 + '@typescript-eslint/types': 7.9.0 + '@typescript-eslint/typescript-estree': 7.9.0(typescript@5.4.5) + eslint: 9.3.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.8.0 - '@typescript-eslint/types': 7.8.0 - '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) - eslint: 8.57.0 + '@typescript-eslint/scope-manager': 8.0.0-alpha.13 + '@typescript-eslint/types': 8.0.0-alpha.13 + '@typescript-eslint/typescript-estree': 8.0.0-alpha.13(typescript@5.4.5) + eslint: 9.3.0 semver: 7.6.2 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@7.8.0': + '@typescript-eslint/visitor-keys@7.9.0': dependencies: - '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/types': 7.9.0 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@8.0.0-alpha.13': + dependencies: + '@typescript-eslint/types': 8.0.0-alpha.13 eslint-visitor-keys: 3.4.3 '@ungap/structured-clone@1.2.0': {} - '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.12.8)(jsdom@24.0.0))': + '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.12.12)(jsdom@24.0.0))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -3705,11 +3401,11 @@ snapshots: istanbul-reports: 3.1.7 magic-string: 0.30.10 magicast: 0.3.4 - picocolors: 1.0.0 + picocolors: 1.0.1 std-env: 3.7.0 strip-literal: 2.1.0 test-exclude: 6.0.0 - vitest: 1.6.0(@types/node@20.12.8)(jsdom@24.0.0) + vitest: 1.6.0(@types/node@20.12.12)(jsdom@24.0.0) transitivePeerDependencies: - supports-color @@ -3754,25 +3450,12 @@ snapshots: jsonparse: 1.3.1 through: 2.3.8 - abab@2.0.6: {} - - acorn-globals@4.3.4: - dependencies: - acorn: 6.4.2 - acorn-walk: 6.2.0 - acorn-jsx@5.3.2(acorn@8.11.3): dependencies: acorn: 8.11.3 - acorn-walk@6.2.0: {} - acorn-walk@8.3.2: {} - acorn@5.7.4: {} - - acorn@6.4.2: {} - acorn@8.11.3: {} agent-base@7.1.1: @@ -3825,27 +3508,14 @@ snapshots: dependencies: dequal: 2.0.3 - array-equal@1.0.2: {} - array-ify@1.0.0: {} array-union@2.1.0: {} - asn1@0.2.6: - dependencies: - safer-buffer: 2.1.2 - - assert-plus@1.0.0: {} - assertion-error@1.1.0: {} - async-limiter@1.0.1: {} - - asynckit@0.4.0: {} - - aws-sign2@0.7.0: {} - - aws4@1.12.0: {} + asynckit@0.4.0: + optional: true axobject-query@4.0.0: dependencies: @@ -3855,10 +3525,6 @@ snapshots: balanced-match@1.0.2: {} - bcrypt-pbkdf@1.0.2: - dependencies: - tweetnacl: 0.14.5 - binary-extensions@2.3.0: {} boolbase@1.0.0: {} @@ -3876,16 +3542,10 @@ snapshots: dependencies: fill-range: 7.0.1 - browser-process-hrtime@1.0.0: {} - - buffer-crc32@0.2.13: {} - cac@6.7.14: {} callsites@3.1.0: {} - caseless@0.12.0: {} - ccount@2.0.1: {} chai@4.4.1: @@ -3972,6 +3632,7 @@ snapshots: combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 + optional: true comma-separated-tokens@2.0.3: {} @@ -3983,9 +3644,9 @@ snapshots: commander@9.5.0: {} - commitlint@19.3.0(@types/node@20.12.8)(typescript@5.4.5): + commitlint@19.3.0(@types/node@20.12.12)(typescript@5.4.5): dependencies: - '@commitlint/cli': 19.3.0(@types/node@20.12.8)(typescript@5.4.5) + '@commitlint/cli': 19.3.0(@types/node@20.12.12)(typescript@5.4.5) '@commitlint/types': 19.0.3 transitivePeerDependencies: - '@types/node' @@ -4017,13 +3678,11 @@ snapshots: meow: 12.1.1 split2: 4.2.0 - cookie@0.6.0: {} + core-js-pure@3.37.1: {} - core-util-is@1.0.2: {} - - cosmiconfig-typescript-loader@5.0.0(@types/node@20.12.8)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5): + cosmiconfig-typescript-loader@5.0.0(@types/node@20.12.12)(cosmiconfig@9.0.0(typescript@5.4.5))(typescript@5.4.5): dependencies: - '@types/node': 20.12.8 + '@types/node': 20.12.12 cosmiconfig: 9.0.0(typescript@5.4.5) jiti: 1.21.0 typescript: 5.4.5 @@ -4069,12 +3728,6 @@ snapshots: dependencies: css-tree: 2.2.1 - cssom@0.3.8: {} - - cssstyle@1.4.0: - dependencies: - cssom: 0.3.8 - cssstyle@4.0.1: dependencies: rrweb-cssom: 0.6.0 @@ -4082,18 +3735,8 @@ snapshots: dargs@8.1.0: {} - dashdash@1.14.1: - dependencies: - assert-plus: 1.0.0 - data-uri-to-buffer@4.0.1: {} - data-urls@1.1.0: - dependencies: - abab: 2.0.6 - whatwg-mimetype: 2.3.0 - whatwg-url: 7.1.0 - data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 @@ -4119,14 +3762,11 @@ snapshots: deepmerge-ts@5.1.0: {} - deepmerge@4.3.1: {} - - delayed-stream@1.0.0: {} + delayed-stream@1.0.0: + optional: true dequal@2.0.3: {} - devalue@5.0.0: {} - devlop@1.1.0: dependencies: dequal: 2.0.3 @@ -4139,10 +3779,6 @@ snapshots: dependencies: path-type: 4.0.0 - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -4151,10 +3787,6 @@ snapshots: domelementtype@2.3.0: {} - domexception@1.0.1: - dependencies: - webidl-conversions: 4.0.2 - domhandler@5.0.3: dependencies: domelementtype: 2.3.0 @@ -4171,11 +3803,6 @@ snapshots: eastasianwidth@0.2.0: {} - ecc-jsbn@0.1.2: - dependencies: - jsbn: 0.1.1 - safer-buffer: 2.1.2 - emoji-regex@10.3.0: {} emoji-regex@8.0.0: {} @@ -4190,8 +3817,6 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es6-promise@3.3.1: {} - esbuild@0.20.2: optionalDependencies: '@esbuild/aix-ppc64': 0.20.2 @@ -4226,22 +3851,13 @@ snapshots: escape-string-regexp@5.0.0: {} - escodegen@1.14.3: + eslint-config-prettier@9.1.0(eslint@9.3.0): dependencies: - esprima: 4.0.1 - estraverse: 4.3.0 - esutils: 2.0.3 - optionator: 0.8.3 - optionalDependencies: - source-map: 0.6.1 + eslint: 9.3.0 - eslint-config-prettier@9.1.0(eslint@8.57.0): + eslint-plugin-playwright@1.6.1(eslint@9.3.0): dependencies: - eslint: 8.57.0 - - eslint-plugin-playwright@1.6.0(eslint@8.57.0): - dependencies: - eslint: 8.57.0 + eslint: 9.3.0 globals: 13.24.0 eslint-plugin-tsdoc@0.2.17: @@ -4249,56 +3865,54 @@ snapshots: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@7.8.0(@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)(vitest@1.6.0(@types/node@20.12.8)(jsdom@24.0.0)): + eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@8.0.0-alpha.13(@typescript-eslint/parser@8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5)(vitest@1.6.0(@types/node@20.12.12)(jsdom@24.0.0)): dependencies: - '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.5) - eslint: 8.57.0 + '@typescript-eslint/utils': 7.9.0(eslint@9.3.0)(typescript@5.4.5) + eslint: 9.3.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 7.8.0(@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) - vitest: 1.6.0(@types/node@20.12.8)(jsdom@24.0.0) + '@typescript-eslint/eslint-plugin': 8.0.0-alpha.13(@typescript-eslint/parser@8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5) + vitest: 1.6.0(@types/node@20.12.12)(jsdom@24.0.0) transitivePeerDependencies: - supports-color - typescript - eslint-scope@7.2.2: + eslint-scope@8.0.1: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint@8.57.0: + eslint-visitor-keys@4.0.0: {} + + eslint@9.3.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.3.0) '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 + '@eslint/eslintrc': 3.1.0 + '@eslint/js': 9.3.0 + '@humanwhocodes/config-array': 0.13.0 '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.3.0 '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 debug: 4.3.4 - doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + eslint-scope: 8.0.1 + eslint-visitor-keys: 4.0.0 + espree: 10.0.1 esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 lodash.merge: 4.6.2 @@ -4310,17 +3924,13 @@ snapshots: transitivePeerDependencies: - supports-color - esm-env@1.0.0: {} - esm@3.2.25: {} - espree@9.6.1: + espree@10.0.1: dependencies: acorn: 8.11.3 acorn-jsx: 5.3.2(acorn@8.11.3) - eslint-visitor-keys: 3.4.3 - - esprima@4.0.1: {} + eslint-visitor-keys: 4.0.0 esquery@1.5.0: dependencies: @@ -4330,10 +3940,15 @@ snapshots: dependencies: estraverse: 5.3.0 - estraverse@4.3.0: {} - estraverse@5.3.0: {} + estree-util-is-identifier-name@3.0.0: {} + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.2 + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.5 @@ -4354,8 +3969,6 @@ snapshots: extend@3.0.2: {} - extsprintf@1.3.0: {} - fast-deep-equal@3.1.3: {} fast-glob@3.3.2: @@ -4379,9 +3992,9 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 - file-entry-cache@6.0.1: + file-entry-cache@8.0.0: dependencies: - flat-cache: 3.2.0 + flat-cache: 4.0.1 fill-range@7.0.1: dependencies: @@ -4408,11 +4021,10 @@ snapshots: path-exists: 5.0.0 unicorn-magic: 0.1.0 - flat-cache@3.2.0: + flat-cache@4.0.1: dependencies: flatted: 3.3.1 keyv: 4.5.4 - rimraf: 3.0.2 flatted@3.3.1: {} @@ -4421,14 +4033,6 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 - forever-agent@0.6.1: {} - - form-data@2.3.3: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - form-data@4.0.0: dependencies: asynckit: 0.4.0 @@ -4458,10 +4062,6 @@ snapshots: get-stream@8.0.1: {} - getpass@0.1.7: - dependencies: - assert-plus: 1.0.0 - git-raw-commits@4.0.0: dependencies: dargs: 8.1.0 @@ -4476,13 +4076,13 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.3.12: + glob@10.3.15: dependencies: foreground-child: 3.1.1 jackspeak: 2.3.6 minimatch: 9.0.4 - minipass: 7.1.0 - path-scurry: 1.10.2 + minipass: 7.1.1 + path-scurry: 1.11.1 glob@7.2.3: dependencies: @@ -4501,7 +4101,7 @@ snapshots: dependencies: type-fest: 0.20.2 - globalyzer@0.1.0: {} + globals@14.0.0: {} globby@11.1.0: dependencies: @@ -4514,17 +4114,8 @@ snapshots: globrex@0.1.2: {} - graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - har-schema@2.0.0: {} - - har-validator@5.1.5: - dependencies: - ajv: 6.12.6 - har-schema: 2.0.0 - has-flag@3.0.0: {} has-flag@4.0.0: {} @@ -4589,7 +4180,7 @@ snapshots: hast-util-is-body-ok-link: 3.0.0 hast-util-is-element: 3.0.0 - hast-util-raw@9.0.2: + hast-util-raw@9.0.3: dependencies: '@types/hast': 3.0.4 '@types/unist': 3.0.2 @@ -4611,7 +4202,7 @@ snapshots: '@types/unist': 3.0.2 ccount: 2.0.1 comma-separated-tokens: 2.0.3 - hast-util-raw: 9.0.2 + hast-util-raw: 9.0.3 hast-util-whitespace: 3.0.0 html-void-elements: 3.0.0 mdast-util-to-hast: 13.1.0 @@ -4644,10 +4235,6 @@ snapshots: highlight.js@11.9.0: {} - html-encoding-sniffer@1.0.2: - dependencies: - whatwg-encoding: 1.0.5 - html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -4671,12 +4258,6 @@ snapshots: - supports-color optional: true - http-signature@1.2.0: - dependencies: - assert-plus: 1.0.0 - jsprim: 1.4.2 - sshpk: 1.18.0 - https-proxy-agent@7.0.4: dependencies: agent-base: 7.1.1 @@ -4689,10 +4270,6 @@ snapshots: husky@9.0.11: {} - iconv-lite@0.4.24: - dependencies: - safer-buffer: 2.1.2 - iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -4730,8 +4307,6 @@ snapshots: is-extglob@2.1.1: {} - is-fullwidth-code-point@2.0.0: {} - is-fullwidth-code-point@3.0.0: {} is-glob@4.0.3: @@ -4761,16 +4336,12 @@ snapshots: dependencies: text-extensions: 2.4.0 - is-typedarray@1.0.0: {} - is-unicode-supported@1.3.0: {} is-unicode-supported@2.0.0: {} isexe@2.0.0: {} - isstream@0.1.2: {} - istanbul-lib-coverage@3.2.2: {} istanbul-lib-report@3.0.1: @@ -4810,40 +4381,6 @@ snapshots: dependencies: argparse: 2.0.1 - jsbn@0.1.1: {} - - jsdom@11.12.0: - dependencies: - abab: 2.0.6 - acorn: 5.7.4 - acorn-globals: 4.3.4 - array-equal: 1.0.2 - cssom: 0.3.8 - cssstyle: 1.4.0 - data-urls: 1.1.0 - domexception: 1.0.1 - escodegen: 1.14.3 - html-encoding-sniffer: 1.0.2 - left-pad: 1.3.0 - nwsapi: 2.2.9 - parse5: 4.0.0 - pn: 1.1.0 - request: 2.88.2 - request-promise-native: 1.0.9(request@2.88.2) - sax: 1.3.0 - symbol-tree: 3.2.4 - tough-cookie: 2.5.0 - w3c-hr-time: 1.0.2 - webidl-conversions: 4.0.2 - whatwg-encoding: 1.0.5 - whatwg-mimetype: 2.3.0 - whatwg-url: 6.5.0 - ws: 5.2.3 - xml-name-validator: 3.0.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - jsdom@24.0.0: dependencies: cssstyle: 4.0.1 @@ -4854,7 +4391,7 @@ snapshots: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.4 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.9 + nwsapi: 2.2.10 parse5: 7.1.2 rrweb-cssom: 0.6.0 saxes: 6.0.0 @@ -4881,21 +4418,10 @@ snapshots: json-schema-traverse@1.0.0: {} - json-schema@0.4.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} - json-stringify-safe@5.0.1: {} - jsonparse@1.3.1: {} - jsprim@1.4.2: - dependencies: - assert-plus: 1.0.0 - extsprintf: 1.3.0 - json-schema: 0.4.0 - verror: 1.10.0 - katex@0.16.10: dependencies: commander: 8.3.0 @@ -4904,15 +4430,6 @@ snapshots: dependencies: json-buffer: 3.0.1 - kleur@4.1.5: {} - - left-pad@1.3.0: {} - - levn@0.3.0: - dependencies: - prelude-ls: 1.1.2 - type-check: 0.3.2 - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -4927,7 +4444,7 @@ snapshots: local-pkg@0.5.0: dependencies: mlly: 1.7.0 - pkg-types: 1.1.0 + pkg-types: 1.1.1 locate-character@3.0.0: {} @@ -4951,21 +4468,19 @@ snapshots: lodash.snakecase@4.1.1: {} - lodash.sortby@4.7.0: {} - lodash.startcase@4.4.0: {} lodash.uniq@4.5.0: {} lodash.upperfirst@4.3.1: {} - lodash@4.17.21: {} - log-symbols@6.0.0: dependencies: chalk: 5.3.0 is-unicode-supported: 1.3.0 + longest-streak@3.1.0: {} + loupe@2.3.7: dependencies: get-func-name: 2.0.2 @@ -5010,22 +4525,11 @@ snapshots: mj-context-menu: 0.6.1 speech-rule-engine: 4.0.7 - mathjax-node@2.1.1: - dependencies: - is-fullwidth-code-point: 2.0.0 - jsdom: 11.12.0 - mathjax: 2.7.9 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - mathjax@2.7.9: {} - mathjax@3.2.2: {} mdast-util-from-markdown@2.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 '@types/unist': 3.0.2 decode-named-character-reference: 1.0.2 devlop: 1.1.0 @@ -5040,10 +4544,38 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-math@3.0.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + longest-streak: 3.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + unist-util-remove-position: 5.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.0 + mdast-util-to-markdown: 2.1.0 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + mdast-util-to-hast@13.1.0: dependencies: '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 '@ungap/structured-clone': 1.2.0 devlop: 1.1.0 micromark-util-sanitize-uri: 2.0.0 @@ -5052,9 +4584,20 @@ snapshots: unist-util-visit: 5.0.0 vfile: 6.0.1 + mdast-util-to-markdown@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.2 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-decode-string: 2.0.0 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + mdast-util-to-string@4.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdn-data@2.0.28: {} @@ -5089,6 +4632,44 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 + micromark-extension-math@3.0.0: + dependencies: + '@types/katex': 0.16.7 + devlop: 1.1.0 + katex: 0.16.10 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-extension-mdx-expression@3.0.0: + dependencies: + '@types/estree': 1.0.5 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.1 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + + micromark-extension-mdx-jsx@3.0.0: + dependencies: + '@types/acorn': 4.0.6 + '@types/estree': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.1 + micromark-factory-space: 2.0.0 + micromark-util-character: 2.1.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + vfile-message: 4.0.2 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.0 + micromark-factory-destination@2.0.0: dependencies: micromark-util-character: 2.1.0 @@ -5102,6 +4683,17 @@ snapshots: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 + micromark-factory-mdx-expression@2.0.1: + dependencies: + '@types/estree': 1.0.5 + devlop: 1.1.0 + micromark-util-character: 2.1.0 + micromark-util-events-to-acorn: 2.0.2 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.2 + micromark-factory-space@2.0.0: dependencies: micromark-util-character: 2.1.0 @@ -5154,6 +4746,17 @@ snapshots: micromark-util-encode@2.0.0: {} + micromark-util-events-to-acorn@2.0.2: + dependencies: + '@types/acorn': 4.0.6 + '@types/estree': 1.0.5 + '@types/unist': 3.0.2 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.0 + micromark-util-types: 2.0.0 + vfile-message: 4.0.2 + micromark-util-html-tag-name@2.0.0: {} micromark-util-normalize-identifier@2.0.0: @@ -5208,11 +4811,13 @@ snapshots: braces: 3.0.2 picomatch: 2.3.1 - mime-db@1.52.0: {} + mime-db@1.52.0: + optional: true mime-types@2.1.35: dependencies: mime-db: 1.52.0 + optional: true mimic-fn@2.1.0: {} @@ -5228,27 +4833,19 @@ snapshots: minimist@1.2.8: {} - minipass@7.1.0: {} + minipass@7.1.1: {} mj-context-menu@0.6.1: {} - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - mlly@1.7.0: dependencies: acorn: 8.11.3 pathe: 1.1.2 - pkg-types: 1.1.0 + pkg-types: 1.1.1 ufo: 1.5.3 mock-fs@5.2.0: {} - mri@1.2.0: {} - - mrmime@2.0.0: {} - ms@2.1.2: {} mylas@2.1.13: {} @@ -5275,9 +4872,8 @@ snapshots: dependencies: boolbase: 1.0.0 - nwsapi@2.2.9: {} - - oauth-sign@0.9.0: {} + nwsapi@2.2.10: + optional: true once@1.4.0: dependencies: @@ -5291,15 +4887,6 @@ snapshots: dependencies: mimic-fn: 4.0.0 - optionator@0.8.3: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.3.0 - prelude-ls: 1.1.2 - type-check: 0.3.2 - word-wrap: 1.2.5 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -5352,8 +4939,6 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - parse5@4.0.0: {} - parse5@7.1.2: dependencies: entities: 4.5.0 @@ -5370,10 +4955,10 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.10.2: + path-scurry@1.11.1: dependencies: lru-cache: 10.2.2 - minipass: 7.1.0 + minipass: 7.1.1 path-type@4.0.0: {} @@ -5381,15 +4966,13 @@ snapshots: pathval@1.1.1: {} - performance-now@2.1.0: {} - periscopic@3.1.0: dependencies: '@types/estree': 1.0.5 estree-walker: 3.0.3 is-reference: 3.0.2 - picocolors@1.0.0: {} + picocolors@1.0.1: {} picomatch@2.3.1: {} @@ -5397,17 +4980,17 @@ snapshots: dependencies: find-up: 6.3.0 - pkg-types@1.1.0: + pkg-types@1.1.1: dependencies: confbox: 0.1.7 mlly: 1.7.0 pathe: 1.1.2 - playwright-core@1.43.1: {} + playwright-core@1.44.0: {} - playwright@1.43.1: + playwright@1.44.0: dependencies: - playwright-core: 1.43.1 + playwright-core: 1.44.0 optionalDependencies: fsevents: 2.3.2 @@ -5415,16 +4998,12 @@ snapshots: dependencies: queue-lit: 1.5.2 - pn@1.1.0: {} - postcss@8.4.38: dependencies: nanoid: 3.3.7 - picocolors: 1.0.0 + picocolors: 1.0.1 source-map-js: 1.2.0 - prelude-ls@1.1.2: {} - prelude-ls@1.2.1: {} prettier@3.2.5: {} @@ -5441,14 +5020,13 @@ snapshots: property-information@6.5.0: {} - psl@1.9.0: {} + psl@1.9.0: + optional: true punycode.js@2.3.1: {} punycode@2.3.1: {} - qs@6.5.3: {} - querystringify@2.2.0: optional: true @@ -5464,6 +5042,8 @@ snapshots: dependencies: picomatch: 2.3.1 + regenerator-runtime@0.14.1: {} + rehype-format@5.0.0: dependencies: '@types/hast': 3.0.4 @@ -5504,7 +5084,7 @@ snapshots: remark-parse@11.0.0: dependencies: - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-from-markdown: 2.0.0 micromark-util-types: 2.0.0 unified: 11.0.4 @@ -5514,46 +5094,11 @@ snapshots: remark-rehype@11.1.0: dependencies: '@types/hast': 3.0.4 - '@types/mdast': 4.0.3 + '@types/mdast': 4.0.4 mdast-util-to-hast: 13.1.0 unified: 11.0.4 vfile: 6.0.1 - request-promise-core@1.1.4(request@2.88.2): - dependencies: - lodash: 4.17.21 - request: 2.88.2 - - request-promise-native@1.0.9(request@2.88.2): - dependencies: - request: 2.88.2 - request-promise-core: 1.1.4(request@2.88.2) - stealthy-require: 1.1.1 - tough-cookie: 2.5.0 - - request@2.88.2: - dependencies: - aws-sign2: 0.7.0 - aws4: 1.12.0 - caseless: 0.12.0 - combined-stream: 1.0.8 - extend: 3.0.2 - forever-agent: 0.6.1 - form-data: 2.3.3 - har-validator: 5.1.5 - http-signature: 1.2.0 - is-typedarray: 1.0.0 - isstream: 0.1.2 - json-stringify-safe: 5.0.1 - mime-types: 2.1.35 - oauth-sign: 0.9.0 - performance-now: 2.1.0 - qs: 6.5.3 - safe-buffer: 5.2.1 - tough-cookie: 2.5.0 - tunnel-agent: 0.6.0 - uuid: 3.4.0 - require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -5579,17 +5124,9 @@ snapshots: rfdc@1.3.1: {} - rimraf@2.7.1: - dependencies: - glob: 7.2.3 - - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - - rimraf@5.0.5: + rimraf@5.0.7: dependencies: - glob: 10.3.12 + glob: 10.3.15 rollup@4.17.2: dependencies: @@ -5620,22 +5157,8 @@ snapshots: dependencies: queue-microtask: 1.2.3 - sade@1.8.1: - dependencies: - mri: 1.2.0 - - safe-buffer@5.2.1: {} - - safer-buffer@2.1.2: {} - - sander@0.5.1: - dependencies: - es6-promise: 3.3.1 - graceful-fs: 4.2.11 - mkdirp: 0.5.6 - rimraf: 2.7.1 - - sax@1.3.0: {} + safer-buffer@2.1.2: + optional: true saxes@6.0.0: dependencies: @@ -5644,8 +5167,6 @@ snapshots: semver@7.6.2: {} - set-cookie-parser@2.6.0: {} - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -5658,27 +5179,12 @@ snapshots: signal-exit@4.1.0: {} - sirv@2.0.4: - dependencies: - '@polka/url': 1.0.0-next.25 - mrmime: 2.0.0 - totalist: 3.0.1 - slash@3.0.0: {} - sorcery@0.11.0: - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - buffer-crc32: 0.2.13 - minimist: 1.2.8 - sander: 0.5.1 - source-map-js@1.2.0: {} source-map@0.6.1: {} - source-map@0.7.4: {} - space-separated-tokens@2.0.2: {} speech-rule-engine@4.0.7: @@ -5689,26 +5195,12 @@ snapshots: split2@4.2.0: {} - sshpk@1.18.0: - dependencies: - asn1: 0.2.6 - assert-plus: 1.0.0 - bcrypt-pbkdf: 1.0.2 - dashdash: 1.14.1 - ecc-jsbn: 0.1.2 - getpass: 0.1.7 - jsbn: 0.1.1 - safer-buffer: 2.1.2 - tweetnacl: 0.14.5 - stackback@0.0.2: {} std-env@3.7.0: {} stdin-discarder@0.2.2: {} - stealthy-require@1.1.1: {} - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -5756,11 +5248,7 @@ snapshots: dependencies: has-flag: 4.0.0 - svelte-hmr@0.16.0(svelte@4.2.15): - dependencies: - svelte: 4.2.15 - - svelte@4.2.15: + svelte@4.2.17: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.4.15 @@ -5777,7 +5265,7 @@ snapshots: magic-string: 0.30.10 periscopic: 3.1.0 - svgo@3.2.0: + svgo@3.3.2: dependencies: '@trysound/sax': 0.2.0 commander: 7.2.0 @@ -5785,9 +5273,10 @@ snapshots: css-tree: 2.3.1 css-what: 6.1.0 csso: 5.0.5 - picocolors: 1.0.0 + picocolors: 1.0.1 - symbol-tree@3.2.4: {} + symbol-tree@3.2.4: + optional: true test-exclude@6.0.0: dependencies: @@ -5801,11 +5290,6 @@ snapshots: through@2.3.8: {} - tiny-glob@0.2.9: - dependencies: - globalyzer: 0.1.0 - globrex: 0.1.2 - tinybench@2.8.0: {} tinypool@0.8.4: {} @@ -5818,13 +5302,6 @@ snapshots: dependencies: is-number: 7.0.0 - totalist@3.0.1: {} - - tough-cookie@2.5.0: - dependencies: - psl: 1.9.0 - punycode: 2.3.1 - tough-cookie@4.1.4: dependencies: psl: 1.9.0 @@ -5833,10 +5310,6 @@ snapshots: url-parse: 1.5.10 optional: true - tr46@1.0.1: - dependencies: - punycode: 2.3.1 - tr46@5.0.0: dependencies: punycode: 2.3.1 @@ -5850,14 +5323,14 @@ snapshots: dependencies: typescript: 5.4.5 - ts-node@10.9.2(@types/node@20.12.8)(typescript@5.4.5): + ts-node@10.9.2(@types/node@20.12.12)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.12.8 + '@types/node': 20.12.12 acorn: 8.11.3 acorn-walk: 8.3.2 arg: 4.1.3 @@ -5870,7 +5343,7 @@ snapshots: tsafe@1.6.6: {} - tsc-alias@1.8.8: + tsc-alias@1.8.10: dependencies: chokidar: 3.6.0 commander: 9.5.0 @@ -5883,16 +5356,6 @@ snapshots: optionalDependencies: typescript: 5.4.5 - tunnel-agent@0.6.0: - dependencies: - safe-buffer: 5.2.1 - - tweetnacl@0.14.5: {} - - type-check@0.3.2: - dependencies: - prelude-ls: 1.1.2 - type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -5901,15 +5364,15 @@ snapshots: type-fest@0.20.2: {} - typescript-eslint@7.8.0(eslint@8.57.0)(typescript@5.4.5): + typescript-eslint@8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5): dependencies: - '@typescript-eslint/eslint-plugin': 7.8.0(@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/parser': 7.8.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.5) - eslint: 8.57.0 + '@typescript-eslint/eslint-plugin': 8.0.0-alpha.13(@typescript-eslint/parser@8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5))(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/parser': 8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5) + '@typescript-eslint/utils': 8.0.0-alpha.13(eslint@9.3.0)(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: + - eslint - supports-color typescript@5.4.5: {} @@ -5936,10 +5399,19 @@ snapshots: dependencies: '@types/unist': 3.0.2 + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.2 + unist-util-position@5.0.0: dependencies: '@types/unist': 3.0.2 + unist-util-remove-position@5.0.0: + dependencies: + '@types/unist': 3.0.2 + unist-util-visit: 5.0.0 + unist-util-stringify-position@4.0.0: dependencies: '@types/unist': 3.0.2 @@ -5968,18 +5440,10 @@ snapshots: requires-port: 1.0.0 optional: true - uuid@3.4.0: {} - uuid@9.0.1: {} v8-compile-cache-lib@3.0.1: {} - verror@1.10.0: - dependencies: - assert-plus: 1.0.0 - core-util-is: 1.0.2 - extsprintf: 1.3.0 - vfile-location@5.0.2: dependencies: '@types/unist': 3.0.2 @@ -5996,13 +5460,13 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-node@1.6.0(@types/node@20.12.8): + vite-node@1.6.0(@types/node@20.12.12): dependencies: cac: 6.7.14 debug: 4.3.4 pathe: 1.1.2 - picocolors: 1.0.0 - vite: 5.2.11(@types/node@20.12.8) + picocolors: 1.0.1 + vite: 5.2.11(@types/node@20.12.12) transitivePeerDependencies: - '@types/node' - less @@ -6013,31 +5477,27 @@ snapshots: - supports-color - terser - vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.8)): + vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.12)): dependencies: debug: 4.3.4 globrex: 0.1.2 tsconfck: 3.0.3(typescript@5.4.5) optionalDependencies: - vite: 5.2.11(@types/node@20.12.8) + vite: 5.2.11(@types/node@20.12.12) transitivePeerDependencies: - supports-color - typescript - vite@5.2.11(@types/node@20.12.8): + vite@5.2.11(@types/node@20.12.12): dependencies: esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.17.2 optionalDependencies: - '@types/node': 20.12.8 + '@types/node': 20.12.12 fsevents: 2.3.3 - vitefu@0.2.5(vite@5.2.11(@types/node@20.12.8)): - optionalDependencies: - vite: 5.2.11(@types/node@20.12.8) - - vitest@1.6.0(@types/node@20.12.8)(jsdom@24.0.0): + vitest@1.6.0(@types/node@20.12.12)(jsdom@24.0.0): dependencies: '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 @@ -6051,16 +5511,16 @@ snapshots: local-pkg: 0.5.0 magic-string: 0.30.10 pathe: 1.1.2 - picocolors: 1.0.0 + picocolors: 1.0.1 std-env: 3.7.0 strip-literal: 2.1.0 tinybench: 2.8.0 tinypool: 0.8.4 - vite: 5.2.11(@types/node@20.12.8) - vite-node: 1.6.0(@types/node@20.12.8) + vite: 5.2.11(@types/node@20.12.12) + vite-node: 1.6.0(@types/node@20.12.12) why-is-node-running: 2.2.2 optionalDependencies: - '@types/node': 20.12.8 + '@types/node': 20.12.12 jsdom: 24.0.0 transitivePeerDependencies: - less @@ -6075,10 +5535,6 @@ snapshots: vscode-textmate@9.0.0: {} - w3c-hr-time@1.0.2: - dependencies: - browser-process-hrtime: 1.0.0 - w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -6088,22 +5544,14 @@ snapshots: web-streams-polyfill@3.3.3: {} - webidl-conversions@4.0.2: {} - webidl-conversions@7.0.0: optional: true - whatwg-encoding@1.0.5: - dependencies: - iconv-lite: 0.4.24 - whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 optional: true - whatwg-mimetype@2.3.0: {} - whatwg-mimetype@4.0.0: optional: true @@ -6113,18 +5561,6 @@ snapshots: webidl-conversions: 7.0.0 optional: true - whatwg-url@6.5.0: - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - - whatwg-url@7.1.0: - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -6152,15 +5588,9 @@ snapshots: wrappy@1.0.2: {} - ws@5.2.3: - dependencies: - async-limiter: 1.0.1 - ws@8.17.0: optional: true - xml-name-validator@3.0.0: {} - xml-name-validator@5.0.0: optional: true @@ -6169,6 +5599,10 @@ snapshots: xmldom-sre@0.1.31: {} + xregexp@5.1.1: + dependencies: + '@babel/runtime-corejs3': 7.24.5 + y18n@5.0.8: {} yaml@2.4.2: {} diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index 3cd44e9..0000000 Binary files a/screenshot.png and /dev/null differ diff --git a/src/README.md b/src/README.md index 2e4b37f..bc76237 100644 --- a/src/README.md +++ b/src/README.md @@ -1,13 +1,11 @@ - # Source directory -- `config/`: Default configurations -- `handlers/`: Handlers to which Sveltex delegates the rendering of the - different kinds of content it encounters -- `type-guards/`: Type guards and similar verification functions -- `types/`: Type definitions -- `utils/`: Utility functions -- `index.ts`: Entry point of the package -- `Sveltex.ts`: Definition of the `Sveltex` class, which implements Svelte's - `PreprocessorGroup` interface. - +- `config/`: Default configurations +- `handlers/`: Handlers to which Sveltex delegates the rendering of the + different kinds of content it encounters +- `type-guards/`: Type guards and similar verification functions +- `types/`: Type definitions +- `utils/`: Utility functions +- `index.ts`: Entry point of the package +- `Sveltex.ts`: Definition of the `Sveltex` class, which implements Svelte's + `PreprocessorGroup` interface. diff --git a/src/Sveltex.ts b/src/Sveltex.ts index b4df9d8..4045e06 100644 --- a/src/Sveltex.ts +++ b/src/Sveltex.ts @@ -1,4 +1,9 @@ // Types +import type { + MarkupPreprocessor, + Preprocessor, + PreprocessorGroup, +} from '$deps.js'; import type { Processed } from '$types/Sveltex.js'; import type { BackendChoices, @@ -8,13 +13,12 @@ import type { import type { AdvancedTexBackend } from '$types/handlers/AdvancedTex.js'; import type { CodeBackend, CodeConfiguration } from '$types/handlers/Code.js'; import type { MarkdownBackend } from '$types/handlers/Markdown.js'; -import type { TexBackend, TexConfiguration } from '$types/handlers/Tex.js'; -import type { Location } from '$types/utils/Ast.js'; import type { - MarkupPreprocessor, - Preprocessor, - PreprocessorGroup, -} from 'svelte/compiler'; + TexBackend, + TexConfiguration, + TexProcessOptions, +} from '$types/handlers/Tex.js'; +import type { ProcessedSnippet, Snippet } from '$types/utils/Escape.js'; // Internal dependencies import { getDefaultSveltexConfig } from '$config/defaults.js'; @@ -24,15 +28,19 @@ import { MarkdownHandler } from '$handlers/MarkdownHandler.js'; import { TexHandler } from '$handlers/TexHandler.js'; import { VerbatimHandler } from '$handlers/VerbatimHandler.js'; import { TexComponent } from '$utils/TexComponent.js'; -import { parse, pushRangeIf, walk } from '$utils/ast.js'; import { log, prettifyError } from '$utils/debug.js'; -import { escapeMustacheTags, escapeVerb, unescape } from '$utils/escape.js'; -import { missingDeps, packageManager } from '$utils/globals.js'; -import { mergeConfigs } from '$utils/merge.js'; import { diagnoseBackendChoices } from '$utils/diagnosers/backendChoices.js'; +import { detectPackageManager, missingDeps } from '$utils/env.js'; +import { + colonUuid, + escape, + unescapeColons, + unescapeSnippets, +} from '$utils/escape.js'; +import { mergeConfigs } from '$utils/merge.js'; // External dependencies -import { MagicString, sorcery } from '$deps.js'; +import { MagicString, is, typeAssert } from '$deps.js'; /** * Returns a promise that resolves to a new instance of `Sveltex`. @@ -154,8 +162,12 @@ export class Sveltex< // append CoffeeScript code, which would (presumably) throw an error. const lang = attributes['lang']?.toString().toLowerCase() ?? 'js'; - script.push(...this.texHandler.scriptLines); - script.push(...this.codeHandler.scriptLines); + if (this.texPresent[filename]) { + script.push(...this.texHandler.scriptLines); + } + if (this.codePresent[filename]) { + script.push(...this.codeHandler.scriptLines); + } if (script.length === 0) return; @@ -194,6 +206,9 @@ export class Sveltex< } as Processed; }; + private codePresent: Record = {}; + private texPresent: Record = {}; + /** * @param content - The whole Svelte file content. * @param filename - The filename of the Svelte file. (Isn't this actually a @@ -219,7 +234,7 @@ export class Sveltex< // configured for SvelTeX, return early. if ( !filename || - !this.configuration.general.extensions.some((ext) => + !this._configuration.general.extensions.some((ext) => filename.endsWith(ext), ) ) { @@ -234,227 +249,135 @@ export class Sveltex< try { // Step 1: Escape potentially tricky code (fenced code blocks, // inline code, and content inside "verbatim" environments). - const { - escapedContent: escapedVerb, - savedMatches: savedMatchesVerb, - map: mapFromEscapingVerb, - } = escapeVerb(this, content, filename); - - // Step 2: Process the saved matches. - const processSavedMatches = Array.from( - savedMatchesVerb.entries(), - ).map(async ([uuid, content]) => { - let processedContent = ''; - if (content.startsWith('`') || content.startsWith('~')) { - processedContent = await codeHandler.process(content); - } else if (content.startsWith('<')) { - processedContent = await verbatimHandler.process(content, { - filename, - }); - } else { - processedContent = await texHandler.process(content); - } - savedMatchesVerb.set(uuid, processedContent); - }); - await Promise.all(processSavedMatches); - - // Step 3: Generate an AST of the (once) escaped content of the file - // using Svelte's parser. - - /** - * AST (abstract syntax tree) of the (once) escaped markup, - * generated using Svelte's parser. - */ - const { ast: astEscapedVerb, scriptPresent: scriptPresentV4 } = - parse(escapedVerb, filename); - - // Step 4: Walk the AST and keep track of the ranges of the mustache - // tag nodes. - - /** - * Ranges (starting and ending positions in the (once) escaped - * source code) of the mustache tags (i.e., nodes of type - * `'MustacheTag'`). These are strings of the form `{...}`, which - * are used to embed JavaScript expressions in the markup, and, as - * far as the markup preprocessor is concerned, should be treated as - * plain text (since that is what the contents of the expression - * will ultimately return). - */ - const mtRanges: Location[] = []; - - walk( - astEscapedVerb, - pushRangeIf('MustacheTag', mtRanges, escapedVerb), - ); - - // Step 5: Escape the mustache tag nodes by replacing them with - // UUIDv4 strings. - - const { - escapedContent: escapedVerbAndMT, - savedMatches: savedMatchesMT, - map: mapFromEscapingMT, - } = escapeMustacheTags( - escapedVerb, - mtRanges, - filename + '_escapedVerb', + const { escapedDocument, escapedSnippets } = escape( + content, + [ + ...this.verbatimHandler.verbEnvs.keys(), + ...this.advancedTexHandler.tccNames, + ...this.advancedTexHandler.tccAliases, + ], + this.configuration.general.tex, ); - // Step 6: Merge the savedMatches maps (for convenience later on). + let headId: string | undefined = undefined; + let headSnippet: ProcessedSnippet | undefined = undefined; + let scriptPresent: boolean = false; + const prependToProcessed: string[] = []; - const savedMatches = new Map([ - ...savedMatchesVerb, - ...savedMatchesMT, - ]); + let codePresent = false; + let texPresent = false; - // Step 7: Generate an AST of the (twice) escaped content of the - // file using Svelte's parser. + // Step 2: Process the saved matches. + const processedSnippets: [string, ProcessedSnippet][] = []; + const processEscapedSnippets = escapedSnippets.map( + async ([uuid, snippet]) => { + let processedSnippet: ProcessedSnippet; + let processed = ''; + const { unescapeOptions } = snippet; + if (snippet.type === 'code') { + typeAssert(is>(snippet)); + if (!codePresent) codePresent = true; + processedSnippet = await codeHandler.process( + snippet.processable.innerContent, + snippet.processable.optionsForProcessor, + ); + } else if (snippet.type === 'verbatim') { + typeAssert(is>(snippet)); + processedSnippet = await verbatimHandler.process( + snippet.processable.innerContent, + { + filename, + ...snippet.processable.optionsForProcessor, + }, + ); + } else if (snippet.type === 'tex') { + typeAssert(is>(snippet)); + if (!texPresent) texPresent = true; + processedSnippet = await texHandler.process( + snippet.processable.innerContent, + snippet.processable + .optionsForProcessor as TexProcessOptions, + ); + } else { + typeAssert( + is>(snippet), + ); + processed = snippet.original.outerContent; + if (snippet.type === 'svelte') { + if ( + !headId && + processed.startsWith(`` tag in - // the file. - - /** - * Ranges (starting and ending positions in the escaped source code) - * of the text nodes (i.e., nodes of type `'Text'`). - */ - const textRanges: Location[] = []; - - /** - * Whether a `\n'); + prependToProcessed.push(''); } + /* eslint-enable @typescript-eslint/no-unnecessary-condition */ - /** - * Fully preprocessed content. - */ - const code = s.toString(); + const html = (await markdownHandler.process(escapedDocument)) + .processed; - /** - * Source map for the unescaping and markdown preprocessing. - */ - const mapFromUnescapingAndMarkdownProcessing = s.generateMap({ - source: filename + '_escapedVerbAndMT', - hires: 'boundary', - includeContent: true, - }); + let code = unescapeSnippets(html, processedSnippets); + code = unescapeColons(code); + if (prependToProcessed.length > 0) { + code = prependToProcessed.join('\n') + '\n' + code; + } - const chain = await sorcery.load(filename + '_final', { - content: { - [filename + '_final']: code, - [filename + '_escapedVerb']: escapedVerb, - [filename + '_escapedVerbAndMT']: escapedVerbAndMT, - [filename]: content, - }, - sourcemaps: { - [filename + '_final']: - mapFromUnescapingAndMarkdownProcessing, - [filename + '_escapedVerbAndMT']: mapFromEscapingMT, - [filename + '_escapedVerb']: mapFromEscapingVerb, - }, - }); - const map = chain?.apply() ?? { - version: 3, - file: filename, - mappings: '', - names: [], - sources: [], - sourcesContent: [], - }; - - return { code, map }; + return { code }; } catch (err) { log('error', prettifyError(err)); return; @@ -721,7 +644,7 @@ export class Sveltex< if (errors.length > 0) { const install = '\n\nPlease install the necessary dependencies by running:\n\n' + - `${packageManager} add -D ${missingDeps.join(' ')}`; + `${detectPackageManager()} add -D ${missingDeps.join(' ')}`; throw Error(`Failed to create Sveltex preprocessor.` + install, { cause: diff --git a/src/config/defaults.ts b/src/config/defaults.ts index 53a557e..d5d7994 100644 --- a/src/config/defaults.ts +++ b/src/config/defaults.ts @@ -26,10 +26,10 @@ import { isPresentAndDefined, isString, } from '$type-guards/utils.js'; -import { interpretAttributes } from '$utils/misc.js'; // External dependencies import { findCacheDirectory, homedir, join, relative, resolve } from '$deps.js'; +import { interpretAttributes } from '$utils/parseComponent.js'; /** * Get the default configuration for a TeX backend. @@ -60,7 +60,14 @@ export function getDefaultTexConfiguration( timeout: 1000, }, outputFormat: 'svg', - mathjax: {}, + mathjax: { + tex: { + inlineMath: [ + ['$', '$'], + ['\\(', '\\)'], + ], + }, + }, // mathjax: { chtml: { fontURL: // 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2', // }} @@ -351,11 +358,16 @@ export function getDefaultCodeConfiguration( * subsequent calls to this function. */ export function getDefaultSveltexConfig< - M extends MarkdownBackend, - C extends CodeBackend, - T extends TexBackend, - A extends AdvancedTexBackend, ->(m: M, c: C, t: T, a: A): FullSveltexConfiguration { + M extends MarkdownBackend = 'none', + C extends CodeBackend = 'none', + T extends TexBackend = 'none', + A extends AdvancedTexBackend = 'none', +>( + m: M = 'none' as M, + c: C = 'none' as C, + t: T = 'none' as T, + a: A = 'none' as A, +): FullSveltexConfiguration { return { general: { wrap: { @@ -364,10 +376,12 @@ export function getDefaultSveltexConfig< }, extensions: ['.sveltex'] as `.${string}`[], tex: { - delimiters: { - inline: ['$', '$'] as [string, string], - display: ['$$', '$$'] as [string, string], + enabled: t !== 'none', + delims: { + inline: { escapedParentheses: true, singleDollar: true }, + display: { escapedSquareBrackets: true }, }, + $$: { isDisplayMath: 'always' }, }, }, tex: getDefaultTexConfiguration(t), diff --git a/src/deps.ts b/src/deps.ts index b90faa8..5f2ecb5 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -15,19 +15,95 @@ export { Glob } from 'glob'; export { escape as escapeHtml } from 'html-escaper'; export { htmlTagNames } from 'html-tag-names'; export { default as MagicString, type SourceMap } from 'magic-string'; + +/** + * MDAST types for better type-safety. + */ +export type { + Code as MdastCodeNode, + InlineCode as MdastInlineCodeNode, + Root as MdastRoot, +} from 'mdast'; + +/** + * MDAST utility that turns markdown into a syntax tree. + * + * - `fromMarkdown`: Turn markdown into a syntax tree. + * + * Types: + * + * - `Extension`: An Extension object can change how markdown tokens from + * micromark are turned into MDAST. + * - `Options`: Options for `fromMarkdown`. + */ +export { + fromMarkdown as mdastFromMarkdown, + type Extension as MdastExtension, + type Options as MdastFromMarkdownOptions, +} from 'mdast-util-from-markdown'; + +/** + * MDAST extensions to parse and serialize math (e.g. $x^2$). + * + * - `mathFromMarkdown`: Create an extension for `mdast-util-from-markdown` that + * will help parse math. + * + * Types: + * + * - `InlineMath`: An inline math node within an MDAST. + * - `Math`: A display math node within an MDAST. + */ +export { + mathFromMarkdown as mdastMathFromMarkdown, + type InlineMath as MdastInlineMathNode, + type Math as MdastMathNode, +} from 'mdast-util-math'; + +/** + * + */ +export { + mdxExpressionFromMarkdown as mdastMdxExpressionFromMarkdown, + type MdxFlowExpression as MdastMdxFlowExpressionNode, + type MdxTextExpression as MdastMdxTextExpressionNode, +} from 'mdast-util-mdx-expression'; +export { + math as micromarkMath, + type Options as MicromarkMathOptions, +} from 'micromark-extension-math'; +export { + mdxExpression as micromarkMdxExpression, + type Options as MicromarkMdxExpressionOptions, +} from 'micromark-extension-mdx-expression'; +export { + mdxJsx as micromarkMdxJsx, + type Options as MicromarkMdxJsxOptions, +} from 'micromark-extension-mdx-jsx'; +export { + mdxMd as micromarkMdxMd, + type Extension as MicromarkExtension, +} from 'micromark-extension-mdx-md'; +export type { + Value as MdastFromMarkdownValue, + Options, +} from 'micromark-util-types'; export { AbortError, default as nodeFetch } from 'node-fetch'; export { default as ora, type Ora } from 'ora'; export { default as pc } from 'picocolors'; -export { type Colors } from 'picocolors/types.js'; +export type { Colors } from 'picocolors/types.js'; export { default as prettyBytes } from 'pretty-bytes'; export { get as getKey, get as getProperty, set as setProperty } from 'radash'; export { default as rfdc } from 'rfdc'; // "Really Fast Deep Clone" export { rimraf } from 'rimraf'; -export { major as semverMajor } from 'semver'; -export { default as sorcery } from 'sorcery'; -export { VERSION, parse as svelteParse } from 'svelte/compiler'; -export { type HTMLAttributes } from 'svelte/elements'; +export type { + MarkupPreprocessor, + Preprocessor, + PreprocessorGroup, +} from 'svelte/compiler'; +export type { HTMLAttributes } from 'svelte/elements'; export { optimize as svgoOptimize, type Config as SvgoOptions } from 'svgo'; -export { assert, is, type Equals, type Extends } from 'tsafe'; +export { is, assert as typeAssert, type Equals, type Extends } from 'tsafe'; +export type { Node as UnistNode } from 'unist'; export { v4 as uuid } from 'uuid'; +export { default as XRegExp } from 'xregexp'; export { default as YAML } from 'yaml'; diff --git a/src/handlers/CodeHandler.ts b/src/handlers/CodeHandler.ts index ca14e81..33ef77b 100644 --- a/src/handlers/CodeHandler.ts +++ b/src/handlers/CodeHandler.ts @@ -7,6 +7,7 @@ import type { CodeProcessOptions, CodeProcessor, FullCodeConfiguration, + FullCodeProcessOptions, ThemableCodeBackend, } from '$types/handlers/Code.js'; @@ -15,7 +16,7 @@ import { getDefaultCodeConfiguration } from '$config/defaults.js'; import { Handler } from '$handlers/Handler.js'; import { isThemableCodeBackend } from '$type-guards/code.js'; import { isArray } from '$type-guards/utils.js'; -import { cdnLink, fancyFetch, fancyWrite, getVersion } from '$utils/cdn.js'; +import { cdnLink, fancyFetch, fancyWrite } from '$utils/cdn.js'; import { log } from '$utils/debug.js'; import { diagnoseCodeConfiguration } from '$utils/diagnosers/codeConfiguration.js'; import { @@ -24,23 +25,27 @@ import { uniqueEscapeSequences, } from '$utils/escape.js'; import { fs } from '$utils/fs.js'; -import { missingDeps } from '$utils/globals.js'; +import { getVersion, missingDeps } from '$utils/env.js'; import { mergeConfigs } from '$utils/merge.js'; -import { prefixWithSlash, re } from '$utils/misc.js'; +import { prefixWithSlash } from '$utils/misc.js'; // External dependencies -import { assert, escapeHtml, is, join, pc } from '$deps.js'; +import { typeAssert, escapeHtml, is, join, pc } from '$deps.js'; +import { ProcessedSnippet, UnescapeOptions } from '$types/utils/Escape.js'; export class CodeHandler extends Handler< B, CodeBackend, CodeProcessor, - CodeProcessOptions, + FullCodeProcessOptions, CodeConfiguration, FullCodeConfiguration, CodeHandler > { - override get process() { + override get process(): ( + code: string, + options?: CodeProcessOptions | undefined, + ) => Promise { return async ( code: string, options?: CodeProcessOptions | undefined, @@ -50,36 +55,33 @@ export class CodeHandler extends Handler< diagnoseCodeConfiguration(this.backend, this.configuration) .errors === 0; } - if (!this.configIsValid) return code; - await this.handleCss(); - let innerCode: string = code; - let opts: CodeProcessOptions | undefined = { _wrap: true }; - - // If the options are provided, interpret `code` as `innerCode` and - // don't wrap the output with anything. Similarly, if - // optionsFromDelims === undefined, it means that the code snippet - // didn't have delimiters, so we should treat `code` as `innerCode` - // and not wrap the output with anything. - if (options !== undefined) { - opts = mergeConfigs(opts, options); - } else { - const res = this.consumeDelims(code); - if (res.optionsFromDelims !== undefined) { - innerCode = res.innerCode; - opts = mergeConfigs(opts, res.optionsFromDelims); - } - } + let opts: FullCodeProcessOptions = { + _wrap: true, + inline: false, + }; - const processedCode = await super.process(innerCode, opts); + if (options) opts = mergeConfigs(opts, options); + + let processed = code; + if (opts.inline) processed = processed.replace(/\r\n?|\n/gu, ' '); + const unescapeOptions: UnescapeOptions = { + removeParagraphTag: !opts.inline, + }; + + if (!this.configIsValid) return { processed, unescapeOptions }; + + await this.handleCss(); + + processed = (await super.process(processed, opts)).processed; // If the `_wrap` option is set to `false`, don't wrap the output. // (This is sometimes used, for example, by the VerbatimHandler.) - if (opts._wrap === false) return processedCode; + if (!opts._wrap) return { processed, unescapeOptions }; const configuration = this.configuration; - assert( + typeAssert( is>(configuration), ); @@ -88,12 +90,17 @@ export class CodeHandler extends Handler< wrapClassPrefix: configuration.wrapClassPrefix, }); - // Add a newline before and after the code block, since - // consumeDelims gobbles up the first newline after the opening - // delimiter and the last newline before the closing delimiter - const n = opts.inline === true ? '' : '\n'; + // Ensure that fenced code blocks end with newline + const n = + !opts.inline && + // !processed.match(/(\r\n?|\n)\s*$/u) && + processed !== '' + ? '\n' + : ''; + + processed = wrapArray[0] + processed + n + wrapArray[1]; - return wrapArray[0] + n + processedCode + n + wrapArray[1]; + return { processed, unescapeOptions }; }; } @@ -137,7 +144,7 @@ export class CodeHandler extends Handler< // Unfortunately, TypeScript doesn't realize that, at this point, B // extends ThemableCodeBackend - assert(is>(this)); + typeAssert(is>(this)); // Convenience alias + type assertion const theme = this.configuration.theme; @@ -192,120 +199,6 @@ export class CodeHandler extends Handler< this._scriptLines = [`import '${prefixWithSlash(path)}';`]; } - private static codeBlockDelimsRegex = re` - ^ # start of string - \s* # leading whitespace - ( \`\`\`+ | ~~~+ ) # $1: ≥3 backticks or tildes - [\ \t]* # inline whitespace (spaces or tabs) - ([\w-]*) # $2: language tag - [\ \t]* # inline whitespace (spaces or tabs) - (.*) # $3: info string - \r?\n # newline (supporting both CRLF and LF line endings) - ${'u'} # support unicode`; - - private static codeInlineDelimsRegex = re` - ^ # start of string - \s* # leading whitespace - ( \`+ | ~+ ) # $1: ≥1 backticks or tildes - ${'u'} # support unicode`; - - consumeDelims = (codeInDelims: string) => { - let innerCode = codeInDelims; - let optionsFromDelims: CodeProcessOptions | undefined = undefined; - const codeBlockMatchArray = innerCode.match( - CodeHandler.codeBlockDelimsRegex, - ); - if (codeBlockMatchArray) { - /* v8 ignore next 9 (unreachable code) */ - if (!codeBlockMatchArray[1]) { - log( - 'error', - 'Error parsing code snippet' + - "(opening delimiters couldn't be matched): " + - innerCode, - ); - return { innerCode, optionsFromDelims }; - } - - // If the code block was opened with n≥3 backticks resp. tildes, - // it must be closed with k≥n backticks resp. tildes. - const closingDelimRegExp = new RegExp( - `(\\r?\\n)[ \\t]*${codeBlockMatchArray[1]}+\\s*$`, - 'u', - ); - - if (!closingDelimRegExp.test(innerCode)) { - log( - 'error', - `Error parsing code block (closing delimiters not found); expected the following to end with ≥${String(codeBlockMatchArray[1].length)} ${codeBlockMatchArray[1].includes('`') ? 'backticks' : 'tildes'}:\n${innerCode}`, - ); - return { innerCode, optionsFromDelims }; - } - - // Extract the language and info string from the opening delimiter - let lang = codeBlockMatchArray[2]; - const info = codeBlockMatchArray[3]; - if (lang === '') lang = 'plaintext'; - - // Remove the opening and closing delimiters - innerCode = innerCode - .replace(CodeHandler.codeBlockDelimsRegex, '') - .replace(closingDelimRegExp, ''); - optionsFromDelims = { inline: false, lang, info }; - return { innerCode, optionsFromDelims }; - } - - // Let's remove the opening and closing delimiters - const codeInlineMatchArray = innerCode.match( - CodeHandler.codeInlineDelimsRegex, - ); - if (codeInlineMatchArray) { - /* v8 ignore next 9 (unreachable code) */ - if (!codeInlineMatchArray[1]) { - log( - 'error', - 'Error parsing code snippet' + - "(opening delimiters couldn't be matched): " + - innerCode, - ); - return { innerCode, optionsFromDelims }; - } - - // If the code block was opened with n≥1 backticks resp. tildes, - // it must be closed with n backticks resp. tildes. - const closingDelimRegExp = new RegExp( - `${codeInlineMatchArray[1]}+\\s*$`, - 'u', - ); - - if (!closingDelimRegExp.test(innerCode)) { - log( - 'error', - `Error parsing inline code snippet (closing delimiters not found); expected the following to end with ≥${String(codeInlineMatchArray[1].length)} ${codeInlineMatchArray[1].includes('`') ? 'backticks' : 'tildes'}:\n${innerCode}`, - ); - return { innerCode, optionsFromDelims }; - } - - // Currently we can't set the language or info string for inline - // code snippets, so we'll just set the language to 'plaintext' and - // the info string to an empty string. - optionsFromDelims = { inline: true, lang: 'plaintext', info: '' }; - - // Remove the opening and closing delimiters - innerCode = innerCode - .replace(CodeHandler.codeInlineDelimsRegex, '') - .replace(closingDelimRegExp, ''); - } else { - log( - 'error', - `Error parsing code snippet (no delimiters could be found/matched): ${innerCode}`, - ); - return { innerCode, optionsFromDelims }; - } - - return { innerCode, optionsFromDelims }; - }; - /** * Creates a code handler of the specified type. * @@ -369,18 +262,15 @@ export class CodeHandler extends Handler< try { type Backend = 'highlight.js'; const processor = (await import('highlight.js')).default; - return new CodeHandler({ + const handler = new CodeHandler({ backend: 'highlight.js', processor, configuration: getDefaultCodeConfiguration('highlight.js'), - process: (code, { lang } = {}, codeHandler) => { + process: (code, { lang }, codeHandler) => { return escapeBraces( codeHandler.processor.highlight(code, { - language: - lang === undefined || lang.length === 0 - ? 'plaintext' - : lang, + language: lang ?? 'plaintext', }).value, ); }, @@ -388,6 +278,16 @@ export class CodeHandler extends Handler< codeHandler.processor.configure(config); }, }); + // Apparently, the highlight.js processor isn't + // serializable. This will upset Vite, so we need to. + // remove it from the object returned by `toJSON` + (handler as unknown as { toJSON: () => object }).toJSON = + () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { processor, ...rest } = handler; + return rest; + }; + return handler; } catch (error) { missingDeps.push('highlight.js'); throw error; @@ -405,7 +305,7 @@ export class CodeHandler extends Handler< const codeHandler = new CodeHandler({ backend: 'prismjs', processor, - process: (code, { lang } = {}, codeHandler) => { + process: (code, { lang }, codeHandler) => { const langOrDefault = lang ?? 'plaintext'; const grammar = codeHandler.processor.prism.languages[ @@ -458,9 +358,9 @@ export class CodeHandler extends Handler< return new CodeHandler({ backend: 'starry-night', processor, - process: (code, { lang } = {}, codeHandler) => { + process: (code, { lang }, codeHandler) => { if ( - lang === undefined || + !lang || [ 'plaintext', 'plain', @@ -539,8 +439,8 @@ export class CodeHandler extends Handler< grammar.dependencies .length > 0 ) { - deps = deps.concat( - grammar.dependencies, + deps.push( + ...grammar.dependencies, ); } return grammar; diff --git a/src/handlers/Handler.ts b/src/handlers/Handler.ts index 00d212b..bb98317 100644 --- a/src/handlers/Handler.ts +++ b/src/handlers/Handler.ts @@ -10,6 +10,7 @@ import { mergeConfigs } from '$utils/merge.js'; // External dependencies import { getProperty, rfdc, setProperty } from '$deps.js'; +import { isString } from '$type-guards/utils.js'; export const deepClone = rfdc(); @@ -107,7 +108,7 @@ export class Handler< * @internal * @readonly */ - private readonly _process: ProcessFn; + protected readonly _process: ProcessFn; /** * Process content. @@ -117,12 +118,14 @@ export class Handler< * @returns The processed content, or promise resolving to it. */ get process(): SimplerProcessFn { - return (content: string, options: ProcessOptions) => { - return this._process( + return async (content: string, options: ProcessOptions) => { + const res = await this._process( content, options, this as unknown as ActualHandler, ); + if (isString(res)) return { processed: res }; + return res; }; } diff --git a/src/handlers/MarkdownHandler.ts b/src/handlers/MarkdownHandler.ts index 686966b..6f35914 100644 --- a/src/handlers/MarkdownHandler.ts +++ b/src/handlers/MarkdownHandler.ts @@ -10,7 +10,7 @@ import type { } from '$types/handlers/Markdown.js'; // Internal dependencies -import { missingDeps } from '$utils/globals.js'; +import { missingDeps } from '$utils/env.js'; import { Handler } from '$handlers/Handler.js'; import { re } from '$utils/misc.js'; @@ -289,7 +289,9 @@ export class MarkdownHandler extends Handler< .use(markdownHandler.configuration.remarkPlugins) .use(remarkRehype, { allowDangerousHtml: true }) .use(markdownHandler.configuration.rehypePlugins) - .use(rehypeStringify, { allowDangerousHtml: true }); + .use(rehypeStringify, { + allowDangerousHtml: true, + }); }; const process: MarkdownProcessFn = async ( markdown: string, @@ -350,7 +352,7 @@ export class MarkdownHandler extends Handler< \n\s*\n # multiple newlines | ^\s*\#{1,6} \s # headings | ( ^.* \S .*\n )+ # setext heading - ^\s*(-+|=+) \s* $ # setext heading underline + ^\s*(-+|=+) \s* $ # setext heading underline | ^( [*+-] | \d+\. ) \s # lists | ^\s* ( \`{3,} | ~{3,} ) # fenced code blocks | ^\s* # thematic breaks diff --git a/src/handlers/TexHandler.ts b/src/handlers/TexHandler.ts index 107a141..768fb61 100644 --- a/src/handlers/TexHandler.ts +++ b/src/handlers/TexHandler.ts @@ -15,16 +15,16 @@ import { katexFonts } from '$data/tex.js'; import { Handler, deepClone } from '$handlers/Handler.js'; import { isArray, isOneOf } from '$type-guards/utils.js'; import { CssApproach, CssApproachLocal } from '$types/handlers/misc.js'; -import { cdnLink, fancyFetch, fancyWrite, getVersion } from '$utils/cdn.js'; +import { cdnLink, fancyFetch, fancyWrite } from '$utils/cdn.js'; import { runWithSpinner } from '$utils/debug.js'; import { escapeBraces } from '$utils/escape.js'; import { fs } from '$utils/fs.js'; -import { missingDeps } from '$utils/globals.js'; +import { getVersion, missingDeps } from '$utils/env.js'; import { mergeConfigs } from '$utils/merge.js'; -import { copyTransformation, prefixWithSlash, re } from '$utils/misc.js'; +import { copyTransformation, prefixWithSlash } from '$utils/misc.js'; // External dependencies -import { CleanCSS, Output, assert, is, join, prettyBytes } from '$deps.js'; +import { CleanCSS, Output, typeAssert, is, join, prettyBytes } from '$deps.js'; import { escapeCssColorVars, unescapeCssColorVars } from '$utils/css.js'; export class TexHandler< @@ -48,7 +48,7 @@ export class TexHandler< // rfdc doesn't handle RegExps well, so we have to copy them manually if (isOneOf(this.backend, ['mathjax', 'katex'])) { - assert(is>(this)); + typeAssert(is>(this)); clone.transformations = { pre: [ ...this._configuration.transformations.pre.map( @@ -84,47 +84,44 @@ export class TexHandler< ]; override get process() { - return async ( - tex: string, - options?: TexProcessOptions['options'], - ) => { + return async (tex: string, options?: TexProcessOptions) => { await this.handleCss(); - const displayMath = tex.match( - re`^ (?: \$\$ (.*) \$\$ | \\\[ (.*) \\\] ) $ ${'su'}`, - ); - const inlineMath = tex.match( - re`^ (?: \$ (.*) \$ | \\\( (.*) \\\) ) $ ${'su'}`, - ); - if (displayMath) { - return super.process( - // We ignore the line below because the regex guarantees - // that one of the groups is not null, but TypeScript - // doesn't realize this, and I don't want to use a non-null - // assertion. - /* v8 ignore next */ - displayMath[1] ?? displayMath[2] ?? '', - { - options, - inline: false, - } as TexProcessOptions, - ); - } - if (inlineMath) { - return super.process( - // We ignore the line below because the regex guarantees - // that one of the groups is not null, but TypeScript - // doesn't realize this, and I don't want to use a non-null - // assertion. - /* v8 ignore next */ - inlineMath[1] ?? inlineMath[2] ?? '', - { - options, - inline: true, - } as TexProcessOptions, - ); - } - return super.process(tex, { options } as TexProcessOptions); + // const displayMath = tex.match( + // re`^ (?: \$\$ (.*) \$\$ | \\\[ (.*) \\\] ) $ ${'su'}`, + // ); + // const inlineMath = tex.match( + // re`^ (?: \$ (.*) \$ | \\\( (.*) \\\) ) $ ${'su'}`, + // ); + // if (displayMath) { + // return super.process( + // // We ignore the line below because the regex guarantees + // // that one of the groups is not null, but TypeScript + // // doesn't realize this, and I don't want to use a non-null + // // assertion. + // /* v8 ignore next */ + // displayMath[1] ?? displayMath[2] ?? '', + // { + // options, + // inline: false, + // } as TexProcessOptions, + // ); + // } + // if (inlineMath) { + // return super.process( + // // We ignore the line below because the regex guarantees + // // that one of the groups is not null, but TypeScript + // // doesn't realize this, and I don't want to use a non-null + // // assertion. + // /* v8 ignore next */ + // inlineMath[1] ?? inlineMath[2] ?? '', + // { + // options, + // inline: true, + // } as TexProcessOptions, + // ); + // } + return super.process(tex, options ?? ({} as TexProcessOptions)); }; } @@ -494,7 +491,7 @@ export class TexHandler< { startMessage: `Minifying MathJax stylesheet (${fmt})`, successMessage: (t) => - `Minified MathJax stylesheet (${fmt}) (${prettyBytes(opt.stats.originalSize)} → ${prettyBytes(opt.stats.minifiedSize)}, ${String(opt.stats.efficiency * 100)}% reduction) in ${t}`, + `Minified MathJax stylesheet (${fmt}) (${prettyBytes(opt.stats.originalSize)} → ${prettyBytes(opt.stats.minifiedSize)}, ${(opt.stats.efficiency * 100).toFixed(0)}% reduction) in ${t}`, failMessage: (t) => `Error minifying MathJax stylesheet (${fmt}) after ${t}`, }, diff --git a/src/handlers/VerbatimHandler.ts b/src/handlers/VerbatimHandler.ts index 5f0ffea..ab5d7a6 100644 --- a/src/handlers/VerbatimHandler.ts +++ b/src/handlers/VerbatimHandler.ts @@ -12,6 +12,7 @@ import type { VerbatimConfiguration, VerbatimProcessOptions, } from '$types/handlers/Verbatim.js'; +import type { UnescapeOptions } from '$types/utils/Escape.js'; // Internal dependencies import { getDefaultVerbatimEnvironmentConfiguration } from '$config/defaults.js'; @@ -19,12 +20,10 @@ import { AdvancedTexHandler } from '$handlers/AdvancedTexHandler.js'; import { CodeHandler } from '$handlers/CodeHandler.js'; import { Handler } from '$handlers/Handler.js'; import { isSimpleEscapeInstruction } from '$type-guards/verbatim.js'; -import { log, prettifyError } from '$utils/debug.js'; +import { log } from '$utils/debug.js'; import { diagnoseVerbatimEnvironmentConfiguration } from '$utils/diagnosers/verbatimEnvironmentConfiguration.js'; import { escapeBraces } from '$utils/escape.js'; import { mergeConfigs } from '$utils/merge.js'; -import { interpretAttributes } from '$utils/misc.js'; -import { parseComponent } from '$utils/parseComponent.js'; // External dependencies import { escapeHtml, nodeAssert, rfdc } from '$deps.js'; @@ -78,172 +77,158 @@ export class VerbatimHandler< * */ const process = async ( - content: string, + innerContent: string, options: VerbatimProcessOptions, verbatimHandler: VerbatimHandler, ) => { - try { - const { - tag, - attributes, - content: innerContent, - selfClosing, - } = parseComponent(content); - - if (selfClosing && innerContent !== undefined) { - log( - 'error', - `Self-closing HTML tag "${tag}" should not have content.`, - ); - return content; - } + const { tag, attributes, selfClosing, outerContent } = options; - if (!selfClosing && innerContent === undefined) { - log( - 'error', - `HTML tag "${tag}" should have content, but none was found.`, - ); - return content; - } + let processed: string = innerContent; + let unescapeOptions: UnescapeOptions | undefined = { + removeParagraphTag: !options.attributes['inline'], + }; - const isVerbEnv = verbatimHandler.verbEnvs.has(tag); - const isAdvancedTexComponent = - verbatimHandler.advancedTexHandler.tccMap.has(tag); + if (selfClosing && innerContent) { + log( + 'error', + `Self-closing HTML tag "${tag}" should not have content.`, + ); + return { processed: outerContent, unescapeOptions }; + } - if (!isVerbEnv && !isAdvancedTexComponent) { - log('error', `Unknown verbatim environment "${tag}".`); - return content; - } + if (!selfClosing && !innerContent) { + log( + 'error', + `HTML tag "${tag}" should have content, but none was found.`, + ); + return { processed: outerContent, unescapeOptions }; + } - if (isVerbEnv && isAdvancedTexComponent) { - log( - 'error', - `HTML tag "${tag}" is ambiguous, as it refers to both a verbatim environment and an advanced TeX component.`, - ); - return content; - } + const isVerbEnv = verbatimHandler.verbEnvs.has(tag); + const isAdvancedTexComponent = + verbatimHandler.advancedTexHandler.tccMap.has(tag); - if (isVerbEnv) { - const config = verbatimHandler.verbEnvs.get(tag); - nodeAssert(config !== undefined); + if (!isVerbEnv && !isAdvancedTexComponent) { + log('error', `Unknown verbatim environment "${tag}".`); + return { processed: outerContent, unescapeOptions }; + } - const defaultAttributes: Record< - string, - string | boolean | number | null | undefined - > = config.defaultAttributes; + if (isVerbEnv && isAdvancedTexComponent) { + log( + 'error', + `HTML tag "${tag}" is ambiguous, as it refers to both a verbatim environment and an advanced TeX component.`, + ); + return { processed: outerContent, unescapeOptions }; + } - const mergedAttributes = { - ...defaultAttributes, - ...attributes, - }; + if (isVerbEnv) { + const config = verbatimHandler.verbEnvs.get(tag); + nodeAssert(config !== undefined); - const fullConfig = mergeConfigs( - getDefaultVerbatimEnvironmentConfiguration(), - config, - ); + const mergedAttributes = { + ...config.defaultAttributes, + ...attributes, + }; - // isVerbEnv === true + const fullConfig = mergeConfigs( + getDefaultVerbatimEnvironmentConfiguration(), + config, + ); - const processInner = fullConfig.processInner; + // isVerbEnv === true - const component = fullConfig.component ?? tag; + const processInner = fullConfig.processInner; - let closingBracket = '>'; - if (selfClosing && fullConfig.respectSelfClosing) { - if (fullConfig.selfCloseOutputWith === 'auto') { - closingBracket = `${content.match(/\s+\/>\s*$/) ? ' ' : ''}/>`; - } else { - closingBracket = fullConfig.selfCloseOutputWith; - } + const component = fullConfig.component ?? tag; + + let closingBracket = '>'; + if (selfClosing && fullConfig.respectSelfClosing) { + if (fullConfig.selfCloseOutputWith === 'auto') { + closingBracket = `${outerContent.match(/\s+\/>\s*$/) ? ' ' : ''}/>`; + } else { + closingBracket = fullConfig.selfCloseOutputWith; } + } - const filteredMergedAttributes = Object.entries( - mergedAttributes, - ).filter( - ([key]) => - (fullConfig.attributeForwardingAllowlist === - 'all' || - fullConfig.attributeForwardingAllowlist.includes( - key, - )) && - !fullConfig.attributeForwardingBlocklist.includes( + const filteredMergedAttributes = Object.entries( + mergedAttributes, + ).filter( + ([key]) => + (fullConfig.attributeForwardingAllowlist === 'all' || + fullConfig.attributeForwardingAllowlist.includes( key, - ), - ); - - const returnComponentOpen = - `<${component}` + - (filteredMergedAttributes.length === 0 ? '' : ' ') + - filteredMergedAttributes - .map( - (attr) => - `${attr[0]}${attr[1] === undefined ? '' : `="${String(attr[1])}"`}`, - ) - .join(' ') + - closingBracket; - - if (selfClosing || innerContent === undefined) { - return returnComponentOpen; - } + )) && + !fullConfig.attributeForwardingBlocklist.includes(key), + ); - let processedInner: string = innerContent; - const returnComponentClose = ``; + const returnComponentOpen = + `<${component}` + + (filteredMergedAttributes.length === 0 ? '' : ' ') + + filteredMergedAttributes + .map( + (attr) => + `${attr[0]}${attr[1] === undefined ? '' : `="${String(attr[1])}"`}`, + ) + .join(' ') + + closingBracket; + + unescapeOptions = { + removeParagraphTag: !mergedAttributes['inline'], + }; + + if (selfClosing) { + return { + processed: returnComponentOpen, + unescapeOptions, + }; + } - if (isSimpleEscapeInstruction(fullConfig.processInner)) { - let escaped = innerContent; - if (fullConfig.processInner.escapeHtml) { - escaped = escapeHtml(escaped); - } - // NB: It's important to escape braces _after_ escaping HTML, since - // escaping braces will introduce ampersands which escapeHtml would - // escape - if (fullConfig.processInner.escapeBraces) { - escaped = escapeBraces(escaped); - } - processedInner = escaped; - } else if (typeof processInner === 'string') { - if (processInner === 'code') { - processedInner = - await verbatimHandler.codeHandler.process( - innerContent, - { - ...interpretAttributes( - mergedAttributes, - ), - _wrap: config.wrap, - }, - ); - } - if (processInner === 'noop') { - processedInner = innerContent; - } - } else if (typeof processInner === 'function') { - processedInner = processInner( - innerContent, - mergedAttributes, - ); - } + const returnComponentClose = ``; - return [ - returnComponentOpen, - processedInner, - returnComponentClose, - ].join(''); - } else { - return verbatimHandler.advancedTexHandler.process( - innerContent ?? '', - { - attributes, - tag, - filename: options.filename, - selfClosing, - }, - ); + if (isSimpleEscapeInstruction(fullConfig.processInner)) { + if (fullConfig.processInner.escapeHtml) { + processed = escapeHtml(processed); + } + // NB: It's important to escape braces _after_ escaping HTML, since + // escaping braces will introduce ampersands which escapeHtml would + // escape + if (fullConfig.processInner.escapeBraces) { + processed = escapeBraces(processed); + } + } else if (typeof processInner === 'string') { + if (processInner === 'code') { + const processedSnippet = + await verbatimHandler.codeHandler.process( + innerContent, + { ...mergedAttributes, _wrap: config.wrap }, + ); + processed = processedSnippet.processed; + unescapeOptions = processedSnippet.unescapeOptions; + } + if (processInner === 'noop') processed = innerContent; + } else if (typeof processInner === 'function') { + processed = processInner(innerContent, mergedAttributes); } - } catch (err) { - log('error', prettifyError(err)); - return content; + processed = [ + returnComponentOpen, + processed, + returnComponentClose, + ].join(''); + } else { + // Advanced TeX Content + const res = await verbatimHandler.advancedTexHandler.process( + innerContent, + { + attributes, + tag, + filename: options.filename, + selfClosing, + }, + ); + processed = res.processed; + unescapeOptions = res.unescapeOptions ?? unescapeOptions; } + return { processed, unescapeOptions }; }; const configure = ( _configuration: VerbatimConfiguration, diff --git a/src/type-guards/ast.ts b/src/type-guards/ast.ts index d0778b7..e15398c 100644 --- a/src/type-guards/ast.ts +++ b/src/type-guards/ast.ts @@ -8,26 +8,11 @@ // Types import type { - BaseNode, - BaseNode_ESTree, - BaseNode_v4, - BaseNode_v5, - Fragment_v5, LineColumn, StartEnd_LineColumn, StartEnd_Offset, } from '$types/utils/Ast.js'; -/** - * Type guard for `BaseNode`. - * - * @param node - The node to check. - * @returns True if the node is of type `BaseNode`, and false otherwise. - */ -export function isBaseNode(node: unknown): node is BaseNode { - return typeof node === 'object' && node !== null && 'type' in node; -} - /** * Checks that the given object has `start` and `end` properties, no matter * their types. @@ -43,22 +28,6 @@ export function hasStartEndUnknown( ); } -/** - * Checks that the given node has `start` and `end` properties, that both of them are - * numbers, and that 0 ≤ `start` ≤ `end`. - */ -export function hasStartEnd( - node: unknown, -): node is { start: number; end: number } { - return ( - hasStartEndUnknown(node) && - typeof node.start === 'number' && - typeof node.end === 'number' && - node.start >= 0 && - node.end >= node.start - ); -} - /** * Type guard for {@link StartEnd_Offset | `StartEnd_Offset`}. */ @@ -117,70 +86,3 @@ export function hasStartEnd_LineColumn( node.end.column >= node.start.column) ); } - -/** - * ESTree-compliant nodes may have a `range` property, which is an array of two - * numbers. This type guard checks for the presence of that property. - */ -export function hasRange(node: unknown): node is { range: [number, number] } { - return ( - typeof node === 'object' && - node !== null && - 'range' in node && - node.range instanceof Array && - node.range.length === 2 && - typeof node.range[0] === 'number' && - typeof node.range[1] === 'number' && - node.range[0] <= node.range[1] - ); -} - -/** - * Type guard for `BaseNode`. - * - * @param node - The node to check. - * @returns True if the node is of type `BaseNode`, and false otherwise. - */ -export function isBaseNode_v5(node: unknown): node is BaseNode_v5 { - return isBaseNode(node) && hasStartEnd(node); -} - -/** - * Type guard for `BaseNode_v4`. - * - * @param node - The node to check. - * @returns True if the node is of type `BaseNode_v4`, and false otherwise. - */ -export function isBaseNode_v4(node: unknown): node is BaseNode_v4 { - return isBaseNode(node) && hasStartEnd(node); -} - -/** - * Type guard for {@link Fragment_v5 | `Fragment_v5`}. - * - * @param node - The node to check. - * @returns True if the node is of type `Fragment_v5`, and false otherwise. - */ -export function isFragment_v5(node: unknown): node is Fragment_v5 { - return ( - isBaseNode_v5(node) && - node.type === 'Fragment' && - 'nodes' in node && - (node['nodes'] === null || - node['nodes'] === undefined || - (typeof node['nodes'] === 'object' && - Array.isArray(node['nodes']) && - node['nodes'].every(isBaseNode_v5))) - ); -} - -/** - * - */ -export function isBaseNode_ESTree(node: unknown): node is BaseNode_ESTree { - return ( - isBaseNode(node) && - (hasRange(node) || - ('loc' in node && hasStartEnd_LineColumn(node['loc']))) - ); -} diff --git a/src/type-guards/code.ts b/src/type-guards/code.ts index 940669a..fa7fc6d 100644 --- a/src/type-guards/code.ts +++ b/src/type-guards/code.ts @@ -12,7 +12,7 @@ import { import { highlightJsThemeNames, starryNightThemeNames } from '$data/code.js'; // External dependencies -import { assert } from '$deps.js'; +import { typeAssert } from '$deps.js'; export function isThemableCodeBackend( input: unknown, @@ -39,10 +39,12 @@ export function isHighlightJsThemeName( return isString(input) && isOneOf(input, highlightJsThemeNames); } -assert>(); -assert>(); -assert>(); -assert>(); +typeAssert>(); +typeAssert< + Equals +>(); +typeAssert>(); +typeAssert>(); export const codeBackends = [ 'escapeOnly', diff --git a/src/types/SveltexConfiguration.ts b/src/types/SveltexConfiguration.ts index c2bdd91..9f10be2 100644 --- a/src/types/SveltexConfiguration.ts +++ b/src/types/SveltexConfiguration.ts @@ -34,6 +34,7 @@ import type { FullVerbatimConfiguration, VerbatimConfiguration, } from '$types/handlers/Verbatim.js'; +import { TexEscapeSettings } from '$types/utils/Escape.js'; import type { RequiredNonNullable } from '$types/utils/utility-types.js'; /** @@ -341,24 +342,7 @@ export interface SveltexConfiguration< /** * General options surrounding SvelTeX's LaTeX support. */ - tex?: - | undefined - | { - delimiters?: { - /** - * Delimiter to use to render inline TeX blocks (e.g., `$x^2$`). - * @defaultValue `['$', '$']`. - */ - inline?: [string, string] | undefined; - - /** - * Delimiter to use to render display TeX blocks (e.g., - * `$$x^2$$`). - * @defaultValue `['$$', '$$']`. - */ - display?: [string, string] | undefined; - }; - }; + tex?: undefined | Omit; }; } @@ -373,7 +357,7 @@ export interface FullSveltexConfiguration< > { general: NonNullable< RequiredNonNullable['general']> - >; + > & { tex: { enabled: boolean } }; markdown: FullMarkdownConfiguration; code: FullCodeConfiguration; tex: FullTexConfiguration; diff --git a/src/types/handlers/AdvancedTex.ts b/src/types/handlers/AdvancedTex.ts index 16dccd7..c980f20 100644 --- a/src/types/handlers/AdvancedTex.ts +++ b/src/types/handlers/AdvancedTex.ts @@ -5,6 +5,10 @@ import type { SupportedTexEngine } from '$types/SveltexConfiguration.js'; import type { ConfigureFn, ProcessFn } from '$types/handlers/Handler.js'; import type { CliInstruction } from '$types/utils/CliInstruction.js'; import type { DvisvgmOptions } from '$types/utils/DvisvgmOptions.js'; +import type { + EscapeOptions, + InterpretedAttributes, +} from '$types/utils/Escape.js'; import type { RequiredNonNullable, RequiredNonNullableExcept, @@ -352,9 +356,10 @@ export type AdvancedTexProcessor = object; export interface AdvancedTexProcessOptions { tag: string; - attributes: Record; + attributes: InterpretedAttributes; filename: string; selfClosing: boolean; + escapeOptions?: EscapeOptions | undefined; } /** diff --git a/src/types/handlers/Code.ts b/src/types/handlers/Code.ts index 2aac0dd..01d6639 100644 --- a/src/types/handlers/Code.ts +++ b/src/types/handlers/Code.ts @@ -88,7 +88,7 @@ export interface GeneralCodeConfiguration { wrap?: | undefined | (( - options: CodeProcessOptions & { wrapClassPrefix: string }, + options: FullCodeProcessOptions & { wrapClassPrefix: string }, ) => [string, string]); } @@ -251,6 +251,14 @@ export interface CodeProcessOptions { _wrap?: boolean | undefined; } +/** + * + */ +export type FullCodeProcessOptions = RequiredNonNullable< + Omit +> & + Pick; + /** * Type of the function that configures a code processor of the specified * type. diff --git a/src/types/handlers/Handler.ts b/src/types/handlers/Handler.ts index 8b74336..ab00be7 100644 --- a/src/types/handlers/Handler.ts +++ b/src/types/handlers/Handler.ts @@ -1,3 +1,5 @@ +import { ProcessedSnippet } from '$types/utils/Escape.js'; + /** * Generic type for the a handler's `process` function, as returned by the * handler's getter for the function. @@ -8,7 +10,7 @@ export type SimplerProcessFn = ( content: string, options: ProcessOptions, -) => string | Promise; +) => ProcessedSnippet | Promise; /** * Generic type for the a handler's `process` function, as stored internally and @@ -22,7 +24,7 @@ export type ProcessFn = ( content: string, options: ProcessOptions, handler: H, -) => string | Promise; +) => string | ProcessedSnippet | Promise; /** * Generic type for a handler's `configure` function. diff --git a/src/types/handlers/Tex.ts b/src/types/handlers/Tex.ts index 1d18028..f14a7c5 100644 --- a/src/types/handlers/Tex.ts +++ b/src/types/handlers/Tex.ts @@ -1,5 +1,5 @@ // Types -import { Equals, assert } from '$deps.js'; +import { Equals, typeAssert } from '$deps.js'; import type { TexHandler } from '$handlers/TexHandler.js'; import type { CssApproach, @@ -212,24 +212,24 @@ export type TexProcessOptions = B extends 'mathjax' // Compile-time unit tests -assert< +typeAssert< Equals< keyof TexConfiguration<'mathjax'>, keyof FullTexConfiguration<'mathjax'> > >(); -assert< +typeAssert< Equals, keyof FullTexConfiguration<'katex'>> >(); -assert< +typeAssert< Equals< keyof TexConfiguration<'custom'>, keyof FullTexConfiguration<'custom'> > >(); -assert< +typeAssert< Equals, keyof FullTexConfiguration<'none'>> >(); diff --git a/src/types/handlers/Verbatim.ts b/src/types/handlers/Verbatim.ts index b83d151..3f915c6 100644 --- a/src/types/handlers/Verbatim.ts +++ b/src/types/handlers/Verbatim.ts @@ -5,6 +5,10 @@ import type { export interface VerbatimProcessOptions { filename: string; + selfClosing: boolean; + attributes: Record; + tag: string; + outerContent: string; } export type FullVerbatimConfiguration = diff --git a/src/types/utils/Ast.ts b/src/types/utils/Ast.ts index 43651e5..5d576c4 100644 --- a/src/types/utils/Ast.ts +++ b/src/types/utils/Ast.ts @@ -18,7 +18,7 @@ export interface BaseNode extends Record { * `'0123'` and a `Location` object `{ start: 0, end: 2 }`, the `Location` would * refer to the substring `'01'`. */ -export interface Location { +export interface Offsets { /** * The index of the first character of the substring to which this * `Location` object refers. diff --git a/src/types/utils/Escape.ts b/src/types/utils/Escape.ts new file mode 100644 index 0000000..44878c5 --- /dev/null +++ b/src/types/utils/Escape.ts @@ -0,0 +1,255 @@ +import { Extends, typeAssert } from '$deps.js'; +import { TexBackend } from '$mod.js'; +import { FullCodeProcessOptions } from '$types/handlers/Code.js'; +import { TexProcessOptions } from '$types/handlers/Tex.js'; +import { VerbatimProcessOptions } from '$types/handlers/Verbatim.js'; +import { Offsets } from '$types/utils/Ast.js'; + +/** + * Vernacular: + * - "snippet": A small segment (or fragment, part, excerpt... or, indeed, + * snippet) of a document that should be escaped and processed by a handler + * other than the markdown processor. + * - "escaped snippet": A snippet that has been replaced with a UUID. + * - "processed snippet": An escaped snippet that has been processed by a + * handler. + */ + +export type InterpretedAttributes = Record< + string, + string | number | boolean | null | undefined +>; + +export interface ParsedComponent { + innerContent: string; + selfClosing: boolean; + attributes: InterpretedAttributes; + tag: string; +} + +export interface ProcessableSnippet { + /** + * The "inner" content of the snippet, i.e., the content that should be + * passed, as-is, as the first argument of the appropriate handler's + * `process` function. At the very least, this means that `innerContent` + * should lack the outermost delimiters of the snippet. + */ + innerContent: string; + + /** + * Object that contains options that should be passed as the second argument + * of the `process` function of the handler that should process the snippet. + * For example, for a fenced code block, this could be the language tag and + * the meta/info string, alongside `inline: false` to indicate that the + * `innerContent` should be processed as a code block, not a code span. + */ + optionsForProcessor: T extends 'code' + ? FullCodeProcessOptions + : T extends 'tex' + ? TexProcessOptions + : T extends 'verbatim' + ? Omit + : undefined; +} + +export type PaddingInstruction = + | boolean + | string + | number + | [string | boolean | number, string | boolean | number]; + +export interface EscapeOptions { + /** + * Whether (or how) to pad the UUID generated for the snippet when inserting + * it into the document. + */ + pad?: PaddingInstruction | undefined; + /** + * Whether to include hyphens in the UUID generated for the snippet. + * + * @defaultValue `true` + */ + hyphens?: boolean | undefined; +} + +export interface UnescapeOptions { + /** + * Sveltex escapes "snippets" by replacing them with UUIDs before running + * the markdown processor. This can often result in the UUIDs being wrapped + * in paragraph tags. Setting this option to `true` will remove such tags, + * if present. + */ + removeParagraphTag?: boolean | undefined; +} + +/** + * Description of a "snippet" in the original document. + */ +export interface OriginalSnippet { + /** + * The location of the snippet in the original document. + * - `start` is the index of the first character of the snippet. + * - `end` is the index of the character immediately following the + * snippet. + */ + loc: Offsets; + + /** + * The original content of the snippet, including the delimiters etc. + * Essentially just the slice of the original document at + * {@link loc | `loc`}. + */ + outerContent: T extends 'svelte' | 'mustacheTag' | 'verbatim' + ? string + : string | undefined; +} + +export type SnippetType = + | 'tex' + | 'code' + | 'svelte' + | 'mustacheTag' + | 'verbatim'; +// | 'advancedTex'; + +export interface ProcessedSnippet { + processed: string; + unescapeOptions?: UnescapeOptions | undefined; +} + +export interface EscapableSnippet + extends Snippet { + escapeOptions?: EscapeOptions | undefined; + unescapeOptions?: UnescapeOptions | undefined; +} + +export interface EscapedSnippet + extends Snippet { + unescapeOptions?: UnescapeOptions | undefined; +} + +type ProcessableSnippetType = 'code' | 'tex' | 'verbatim'; + +/** + * A small segment (or fragment, part, excerpt... or, indeed, snippet) of a + * document that should be escaped and processed by a handler other than the + * markdown processor. + */ +export interface Snippet { + /** + * Snippet type. This is used to determine which handler, if any, should + * process the snippet. Possible values are: + * - `'tex'`: A snippet of LaTeX code. + * - `'code'`: A code span or a fenced code block. This is forwarded to the + * `CodeHandler`. + * - `'verbatim'`: A verbatim environment. This is forwarded to the + * `VerbatimHandler`, which checks for some errors and then may forward + * the snippet to: + * - `AdvancedTexHandler`, if the verbatim environment was set up as a TeX + * component. + * - `CodeHandler`, if the verbatim environment was set up with + * `processInner` set to `code`. + * - Otherwise: the `VerbatimHandler` will take care of the processing + * itself (this is the case for `processInner` values `noop`, + * `escapeOnly`). + * - `'mustacheTag'`: A mustache tag, like `{...}`. + * - `'svelte'`: Svelte syntax (other than mustache tags), like + * `{@html ...}`, `{#if ...}`, ``, `b')) + * // [ + * // { + * // escapeOptions: { pad: 2 }, + * // original: { + * // loc: { start: 1, end: 21 }, + * // outerContent: '', + * // }, + * // processable: undefined, + * // type: 'svelte', + * // unescapeOptions: { removeParagraphTag: true }, + * // }, + * // ] + * ``` + */ +export function getSvelteES(document: string): EscapableSnippet<'svelte'>[] { + const escapableSnippets: EscapableSnippet<'svelte'>[] = []; + [ + normalComponentsRegExp(['script', 'style', `svelte${colonUuid}head`]), + normalAndSelfClosingComponentsRegExp([ + `svelte${colonUuid}window`, + `svelte${colonUuid}document`, + `svelte${colonUuid}body`, + `svelte${colonUuid}options`, + ]), + ].forEach((regExp) => { + // console.log('document: ', document); + // console.log('regExp: ', regExp.source); + for (const match of document.matchAll(regExp)) { + // console.log('match: ', match[0]); + const { index, 0: outerContent } = match; + const loc = { + start: index, + end: index + outerContent.length, + }; + escapableSnippets.push({ + type: 'svelte', + original: { loc, outerContent }, + processable: undefined, + escapeOptions: { pad: 2 }, + unescapeOptions: { removeParagraphTag: true }, + }); + } + }); + return escapableSnippets; +} + +export const colonUuid = uuid().replace(/-/g, ''); + +export function getColonES(document: string): EscapableSnippet<'svelte'>[] { + const escapableSnippets: EscapableSnippet<'svelte'>[] = []; + const addColon = (loc: Offsets) => { + escapableSnippets.push({ + type: 'svelte', + original: { loc, outerContent: ':' }, + processable: undefined, + escapeOptions: { pad: false, hyphens: false }, + unescapeOptions: { removeParagraphTag: false }, + }); + }; + [ + normalComponentsRegExp(['svelte:head']), + normalAndSelfClosingComponentsRegExp([ + 'svelte:self', + 'svelte:component', + 'svelte:element', + 'svelte:fragment', + // + 'svelte:window', + 'svelte:document', + 'svelte:body', + 'svelte:options', + ]), + ].forEach((regExp) => { + for (const match of document.matchAll(regExp)) { + const { index, 0: outerContent } = match; + // console.log(match); + const firstColonIdx = outerContent.indexOf(':'); + const locFirst = { + start: index + firstColonIdx, + end: index + firstColonIdx + 1, + }; + // console.log(locFirst); + addColon(locFirst); + const lastColonIdx = outerContent.lastIndexOf(':'); + if (lastColonIdx !== firstColonIdx) { + const locLast = { + start: index + lastColonIdx, + end: index + lastColonIdx + 1, + }; + addColon(locLast); + } + } + }); + return escapableSnippets; +} + +// /|'"/]+=(?:"[^"]*"|'[^']*'|[^>\s]+)|\s+[^=>'"/]+)*\s*)(?:\/>|>([\S\s]*?)<\/style>)/g + +/* eslint-disable tsdoc/syntax */ +/** + * @param document - The document in which to find escapable snippets. + * @param documentLines - The lines of the document, i.e., `document.split('\n')`. + * @returns An array of escapable snippets of: + * - `'code'`: Code spans (`` `...` ``) and fenced code blocks + * (```` ```...\n...\n``` ````) + * - `'math'`: Inline math (`$...$`) and display math (`$$...$$`) + * - `'svelte'`: Special tags (`{@const ...}`, `{@debug ...}`) + * - `'svelte'`: Delimiters of logic blocks (`{#if ...}`, `{#else ...}`, + * `{/if}`, etc.) + * - `'mustacheTag'` Mustache tags (`{...}`), i.e., almost anything in braces + * that hasn't already been escaped by this method or otherwise. + */ +/* eslint-enable tsdoc/syntax */ +export function getMdastES({ + ast, + document, + lines, + texSettings, +}: { + ast: MdastRoot; + document: string; + lines: string[]; + texSettings: TexEscapeSettings; +}): EscapableSnippet<'code' | 'mustacheTag' | 'tex' | 'svelte'>[] { + const escapableSnippets: EscapableSnippet< + 'code' | 'mustacheTag' | 'tex' | 'svelte' + >[] = []; + walkMdast(ast, (node) => { + // If the node is not one of the interesting types, we don't need to + // analyze it any further; instead, we just keep walking this branch + // of the tree. + if ( + !isOneOf(node.type, [ + 'math', + 'inlineMath', + 'code', + 'inlineCode', + 'mdxTextExpression', + 'mdxFlowExpression', + ]) + ) { + return true; + } + const loc = getLocationUnist(node, lines); + // Inline math and display math + if (isOneOf(node.type, ['inlineMath', 'math'])) { + if (!texSettings.enabled) return true; + typeAssert(is(node)); + nodeAssert(node.position, 'Node has positional information.'); + const isDisplayMath = texSettings.$$?.isDisplayMath ?? 'always'; + let inline: boolean = node.type === 'inlineMath'; + // Micromark parses a lot of stuff that some might expect to be + // display math as inline math, so we need to check if it really is + // inline math given the user's preferences. On the other hand, if + // Micromark parses something as display math, we can be sure that + // it really is display math. + const { start, end } = node.position; + + // `true` iff the first line of the outer content matches /^\s*\$/ + const startsWithNewline = !!lines[start.line - 1] + ?.trimStart() + .startsWith('$'); + + // `true` iff the last line of the outer content matches /\$\s*$/ + const endsWithNewline = !!lines[end.line - 1] + ?.trimEnd() + .endsWith('$'); + + if (inline) { + // Get math content _with_ the surrounding delimiters + const outerContent = document.slice(loc.start, loc.end); + // Check if the delimiters are ≥2 dollar signs + if (outerContent.match(/^\s*\$\$.*\$\$\s*$/su)) { + if (isDisplayMath === 'always') { + inline = false; + } else if (isDisplayMath === 'newline') { + inline = !(startsWithNewline && endsWithNewline); + } + } + } + + // If we're dealing with inline math, we don't pad the escape + // string. However, if we're dealing with display math, we want to + // make sure that the processed math won't be caught within a + // paragraph tag with other content, so we pad the escape string + // with newlines. + const pad = inline ? 0 : 2; + + // To follow CommonMark conventions, if the inner content starts + // resp. ends with ≥1 spaces or newlines, we trim the first resp. + // last such whitespace character. + const innerContent = node.value.replace( + /^(?: |\r\n?|\n)(.*)(?: |\r\n?|\n)$/su, + '$1', + ); + + escapableSnippets.push({ + type: 'tex', + original: { loc, outerContent: undefined }, + escapeOptions: { pad }, + processable: { + innerContent, + optionsForProcessor: { inline }, + }, + unescapeOptions: { removeParagraphTag: !inline }, + }); + } + // Code spans and fenced code blocks + else if (isOneOf(node.type, ['inlineCode', 'code'])) { + typeAssert(is(node)); + nodeAssert(node.position, 'Node has positional information.'); + const inline = node.type === 'inlineCode'; + const { start, end } = node.position; + escapableSnippets.push({ + type: 'code', + original: { loc, outerContent: undefined }, + processable: { + innerContent: node.value, + optionsForProcessor: inline + ? { _wrap: true, inline } + : { + _wrap: true, + lang: node.lang ?? undefined, + inline, + info: node.meta ?? undefined, + }, + }, + escapeOptions: { + pad: inline + ? false + : [ + lines[start.line - 1 - 1]?.trim().length !== 0, + lines[end.line - 1 + 1]?.trim().length !== 0, + ], + }, + unescapeOptions: { removeParagraphTag: !inline }, + }); + } else if ( + isOneOf(node.type, ['mdxTextExpression', 'mdxFlowExpression']) + ) { + typeAssert( + is( + node, + ), + ); + const outerContent = document.slice(loc.start, loc.end); + // const innerContent = node.value; + // If the expression starts with `#`, `:`, or `/`, it's a Svelte + // logic block. In this case, we want to remove any

...

tags + // with which the markdown processor may wrap the escaped string. + // If the expression starts with `@html`, it's a raw HTML block. In + // this case, it should be treated as plain text, just like mustache + // tags. + if ( + node.value.match(/^\s*[#@:/]/) && + !node.value.startsWith('@html') + ) { + let pad: boolean | [string, string] = true; + if (node.position) { + // We don't want a logic block to be caught within a + // paragraph tag with other content, so we pad the escape + // string with newlines if necessary. + pad = [ + // `node.position.start.line - 1` = 0-based line number + !lines[node.position.start.line - 1 - 1]?.trim() + ? '\n\n' + : '\n', + // `node.position.end.line - 1` = 0-based line number + !lines[node.position.end.line - 1 + 1]?.trim() + ? '\n\n' + : '\n', + ]; + // pad = false; + } + escapableSnippets.push({ + original: { loc, outerContent }, + processable: undefined, + escapeOptions: { pad }, + type: 'svelte', + unescapeOptions: { removeParagraphTag: true }, + }); + } else { + escapableSnippets.push({ + type: 'mustacheTag', + original: { loc, outerContent }, + processable: undefined, + escapeOptions: { pad: false }, + unescapeOptions: { removeParagraphTag: false }, + }); + } + } + return false; + }); + return escapableSnippets; +} + +/** + * Pad a string left and right. + * + * @param str - The string to pad. + * @param padInstr - The padding instruction. If `true` (the default), a newline + * is added on both sides. If `false`, no padding is added. If a number, then + * that number of newlines (`\n`) is added to both sides. If a string, that + * string is added on both sides. If a 2-tuple, the previous procedure is + * applied independently to the left and right side of the string with the first + * and second element of the tuple, respectively. + * @returns The padded string. + * + * @example + * ```ts + * padString('foo'); // '\nfoo\n' + * padString('foo', true); // '\nfoo\n' + * padString('foo', false); // 'foo' + * padString('foo', 'bar'); // 'barfoobar' + * padString('foo', [true, false]); // '\nfoo' + * padString('foo', [true, 'bar']); // '\nfoobar' + * padString('foo', [0, 2]); // 'foo\n\n' + * padString('foo', ['bar\n', 1]); // 'bar\nfoo\n' + * padString('foo', ['bar', 'baz']); // 'barfoobaz' + * ``` + */ +export function padString( + str: string, + padInstr: PaddingInstruction | undefined = true, +): string { + if (!padInstr) return str; + const pad: [string | boolean | number, string | boolean | number] = + !isArray(padInstr) ? [padInstr, padInstr] : [...padInstr]; + return ( + (isString(pad[0]) ? pad[0] : '\n'.repeat(+pad[0])) + + str + + (isString(pad[1]) ? pad[1] : '\n'.repeat(+pad[1])) + ); +} -// export const svelteEscapeStrings: Record = { -// '&': '&', -// '<': '<', -// '>': '>', -// '"': '"', -// "'": ''', -// '{': '{', -// '}': '}', -// }; +/** + * Escape snippets in a document. + * + * @param document - The document in which to escape the snippets. + * @param snippets - The (escapable) snippets to escape. + * + */ +export function escapeSnippets( + document: string, + snippets: EscapableSnippet[], +): { + escapedDocument: string; + escapedSnippets: [string, EscapedSnippet][]; +} { + const s = new MagicString(document); + const ranges = outermostRanges([...snippets], 'original.loc'); + const escapedSnippets: [string, Snippet][] = ranges.map((range) => { + const id = uuid(); + const paddedId = padString(id, range.escapeOptions?.pad ?? false); + nodeAssert(paddedId, 'paddedId must be truthy'); + s.overwrite(range.original.loc.start, range.original.loc.end, paddedId); + return [id, range]; + }); + const escapedDocument = s.toString(); + return { escapedDocument, escapedSnippets }; +} -// export function svelteEscapeString(char: string) { -// return svelteEscapeStrings[char] ?? char; -// } +/** + * Escape colons (in tag/component names) in a document. + * + * @param document - The document in which to escape the colons. + * @returns The document with the colons escaped. + */ +export function escapeColons(document: string): string { + const snippets = getColonES(document); + const s = new MagicString(document); + snippets.forEach((snippet) => { + s.overwrite( + snippet.original.loc.start, + snippet.original.loc.end, + colonUuid, + ); + }); + return s.toString(); +} + +/** + * Unescape colons (in tag/component names) in a document. + * + * @param document - The document in which to unescape the colons. + * @returns The document with the colons unescaped. + */ +export function unescapeColons(document: string): string { + return document.replaceAll(colonUuid, ':'); +} + +export function escape( + document: string, + verbatimTags: string[] = [], + texSettings: TexEscapeSettings = { enabled: true }, +) { + // Escape colons inside special Svelte elements (e.g. ) so + // that they don't confuse the markdown processor. We don't want to escape + // the entire special Svelte element because we want the markdown processor + // to be able to transform any markdown that it may find within it. + const escapedDocument = escapeColons(document); + + // Escape other things using MDAST + const ast = parseToMdast( + escapedDocument, + [ + ...verbatimTags, + 'script', + 'style', + // `svelte${colonUuid}head`, + `svelte${colonUuid}window`, + `svelte${colonUuid}document`, + `svelte${colonUuid}body`, + `svelte${colonUuid}options`, + ], + texSettings, + ); + const lines = escapedDocument.split(/\r\n?|\n/); + return escapeSnippets(escapedDocument, [ + ...getMdastES({ + ast, + document: escapedDocument, + lines, + texSettings, + }), + // Escape Svelte syntax + ...getSvelteES(escapedDocument), + // Escape math in \[...\] and \(...\) + ...getMathInSpecialDelimsES(escapedDocument, texSettings), + ...getVerbatimES(document, verbatimTags), + ]); +} + +/** + * "Unescapes" content. + * + * @param document - Escaped content. + * @param processedSnippets - An array of 2-tuples, with the first element being + * the UUIDv4 and the second element being the processed snippet corresponding + * to that UUIDv4. + * @returns The original content, with the escaped matches restored. + */ +export function unescapeSnippets( + document: string, + processedSnippets: [string, ProcessedSnippet][], +): string { + let unescaped = document; + const keys: string[] = processedSnippets.map((v) => v[0]); + const processedSnippetsRecord = Object.fromEntries(processedSnippets); + if (keys.length === 0) return document; + unescaped = XRegExp.replace( + document, + new RegExp( + `(?:(

\\s*)(${keys.join('|')})(\\s*

))|(${keys.join('|')})`, + 'g', + ), + (...match) => { + typeAssert(is<(string | undefined)[]>(match)); + const key = match[2] ?? match[4]; + nodeAssert(key, "RegExp must've matched an UUIDv4 key."); + const processedSnippet = processedSnippetsRecord[key]; + if (!processedSnippet) return key; + if ( + !processedSnippet.unescapeOptions?.removeParagraphTag && + match[2] + ) { + nodeAssert( + match[1] && match[3], + 'Matched

...

, so

and

tags must be present.', + ); + return match[1] + processedSnippet.processed + match[3]; + } + return processedSnippet.processed; + }, + ); + return unescaped; +} + +/** + * Parse a document into an MDAST. + * + * @param document - The document to parse. + * @param texSettings - The settings for escaping TeX math. + * @returns The MDAST of the document. + * + * @remarks This function uses the following modules and plugins to parse the + * document: + * - `mdast-util-from-markdown`: Its `fromMarkdown` method is the backbone of + * our `parseToMdast` method. + * - + */ +export function parseToMdast( + document: string, + verbatimTags: string[] | undefined = undefined, + texSettings: TexEscapeSettings = getDefaultSveltexConfig().general.tex, +): MdastRoot { + return mdastFromMarkdown(document, { + extensions: [ + micromarkMdxMd(), + micromarkMdxJsx(), + // micromarkSkip(verbatimTags), + micromarkMath({ + singleDollarTextMath: + texSettings.enabled && + (texSettings.delims?.inline?.singleDollar ?? true), + }), + micromarkMdxExpression(), + micromarkSkip(verbatimTags), + ], + mdastExtensions: [ + mdastMathFromMarkdown(), + mdastMdxExpressionFromMarkdown(), + ], + }); +} diff --git a/src/utils/fs.ts b/src/utils/fs.ts index 2c57ecc..c9f78cd 100644 --- a/src/utils/fs.ts +++ b/src/utils/fs.ts @@ -44,3 +44,17 @@ async function writeFileEnsureDir(file: string, content: string) { if (dir !== '.') await ensureDir(dir); await fs.writeFile(file, content, 'utf8'); } + +/** + * Checks if a file or directory exists at the specified path. + * + * @param path - The path to check. + * @returns `true` if the path exists, `false` otherwise. + */ +export function pathExists(path: string) { + try { + return fs.existsSync(path); + } catch { + return false; + } +} diff --git a/src/utils/globals.ts b/src/utils/globals.ts deleted file mode 100644 index f1c931c..0000000 --- a/src/utils/globals.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Internal dependencies -import { detectPackageManager, log, prettifyError } from '$utils/debug.js'; - -// External dependencies -import { VERSION, semverMajor } from '$deps.js'; - -function getMajorVersion(version: string): number { - try { - return (semverMajor as (v: string) => number)(version); - } catch (error) { - log( - 'error', - `Could not determine Svelte compiler version. Got: "${version}".\n\n${prettifyError(error)}\n\n`, - ); - return -1; - } -} - -/** - * The major version of the Svelte compiler (e.g., `4` or `5`). - * - * @see {@link VERSION | `VERSION`}, from `svelte/compiler`. - */ -export const SVELTE_MAJOR_VERSION: number = getMajorVersion(VERSION); - -/** - * Array to keep track of missing dependencies - */ -export const missingDeps: string[] = []; - -/** - * Package manager used in the project, detected by the `detect-package-manager` - * package. - */ -export const packageManager: 'pnpm' | 'bun' | 'npm' | 'yarn' = - detectPackageManager(); diff --git a/src/utils/html.ts b/src/utils/html.ts deleted file mode 100644 index d13ef90..0000000 --- a/src/utils/html.ts +++ /dev/null @@ -1,21 +0,0 @@ -// External dependencies -import { htmlTagNames } from '$deps.js'; - -/** - * Check if a string is a valid name for a component. For this to be the case, - * it has to not be an HTML tag name, and it has to be an alphanumeric string - * that starts with a letter. - * - * @param name - The name to check. - * @returns `true` if the name is valid, `false` otherwise. - * - * @example - * ```ts - * isValidComponentName('div'); // false - * isValidComponentName('div2'); // true - * isValidComponentName('MyComponent'); // true - * ``` - */ -export function isValidComponentName(name: string): boolean { - return /^[a-zA-Z][a-zA-Z0-9]*$/.test(name) && !htmlTagNames.includes(name); -} diff --git a/src/utils/micromark/skip-flow.ts b/src/utils/micromark/skip-flow.ts new file mode 100644 index 0000000..297fe65 --- /dev/null +++ b/src/utils/micromark/skip-flow.ts @@ -0,0 +1,400 @@ +/* eslint-disable @typescript-eslint/no-this-alias */ +// Types +import type { + Code, + Construct, + Effects, + State, + TokenizeContext, +} from 'micromark-util-types'; + +import { + markdownLineEnding, + markdownLineEndingOrSpace, +} from 'micromark-util-character'; +import { codes } from 'micromark-util-symbol'; +import { skipTags } from '$utils/micromark/syntax.js'; +import { htmlRawNames } from 'micromark-util-html-tag-name'; + +declare module 'micromark-util-types' { + interface TokenTypeMap { + skipFlow: 'skipFlow'; + skipFlowData: 'skipFlowData'; + } +} + +const nonLazyContinuationStart: Construct = { + tokenize: tokenizeNonLazyContinuationStart, + partial: true, +}; + +// // Could also still provide second param `context: TokenizeContext`, see +// // `Resolver` type. +// export function resolveToSkipFlow(events: Event[]): Event[] { +// let index = events.length; +// while (index--) { +// if ( +// events[index]?.[0] === 'enter' && +// events[index]?.[1].type === 'skipFlow' +// ) { +// break; +// } +// } +// if (index > 1 && events[index - 2]?.[1].type === 'linePrefix') { +// // Add the prefix start to the HTML token. +// // eslint-disable-next-line @typescript-eslint/no-non-null-assertion +// events[index]![1].start = events[index - 2]![1].start; +// // Add the prefix start to the HTML line token. +// // eslint-disable-next-line @typescript-eslint/no-non-null-assertion +// events[index + 1]![1].start = events[index - 2]![1].start; +// // Remove the line prefix. +// events.splice(index - 2, 2); +// } +// return events; +// } + +export function tokenizeSkipFlow( + this: TokenizeContext, + effects: Effects, + ok: State, + nok: State, +): State { + const self = this; + let closingTag: boolean; + let buffer: string; + let openingTagString: string = ''; + + return start; + + /** + * Start of HTML (flow). + * + * ```markdown + * > | + * ^ + * ``` + */ + function start(code: Code): State | undefined { + // To do: parse indent like `markdown-rs`. + return before(code); + } + + /** + * At `<`, after optional whitespace. + * + * ```markdown + * > | + * ^ + * ``` + */ + function before(code: Code): State | undefined { + effects.enter('skipFlow'); + effects.enter('skipFlowData'); + effects.consume(code); + return open; + } + + /** + * After `<`, at tag name or other stuff. + * + * ```markdown + * > | + * ^ + * > | + * ^ + * > | + * ^ + * ``` + */ + function open(this: TokenizeContext, code: Code): State | undefined { + // ASCII alphabetical + if (code !== null && asciiAlphabetic(code)) { + effects.consume(code); + buffer = String.fromCharCode(code); + return tagName; + } + return nok(code); + } + + /** + * In tag name. + * + * ```markdown + * > | + * ^^ + * > | + * ^^ + * ``` + */ + function tagName(this: TokenizeContext, code: Code): State | undefined { + if ( + code === null || + code === codes.slash || + code === codes.greaterThan || + markdownLineEndingOrSpace(code) + ) { + const slash = code === codes.slash; + const bufferLowerCase = buffer.toLowerCase(); + // `script`, `style`, `textarea`, and `pre` should be treated as + // case-insensitive. + if (htmlRawNames.includes(bufferLowerCase)) { + openingTagString = bufferLowerCase; + } else { + openingTagString = buffer; + } + if (!slash && !closingTag && skipTags.includes(openingTagString)) { + return self.interrupt ? ok(code) : continuation(code); + } + return nok(code); + } + + // ASCII alphanumerical and `-`, `.`, `:`, and `_`. + if (asciiAlphanumeric(code) || tagPunctuation(code)) { + effects.consume(code); + buffer += String.fromCharCode(code); + return tagName; + } + return nok(code); + } + + /** + * In continuation of any HTML kind. + * + * ```markdown + * > | + * ^ + * ``` + */ + function continuation(code: Code): State | undefined { + if (code === codes.lessThan) { + effects.consume(code); + return continuationRawTagOpen; + } + if (code === null || markdownLineEnding(code)) { + effects.exit('skipFlowData'); + return continuationStart(code); + } + effects.consume(code); + return continuation; + } + + /** + * In continuation, at eol. + * + * ```markdown + * > | + * ^ + * | asd + * ``` + */ + function continuationStart(code: Code): State | undefined { + return effects.check( + nonLazyContinuationStart, + continuationStartNonLazy, + continuationAfter, + )(code); + } + + /** + * In continuation, at eol, before non-lazy content. + * + * ```markdown + * > | + * ^ + * | asd + * ``` + */ + function continuationStartNonLazy(code: Code): State | undefined { + effects.enter('lineEnding'); + effects.consume(code); + effects.exit('lineEnding'); + return continuationBefore; + } + + /** + * In continuation, before non-lazy content. + * + * ```markdown + * | + * > | asd + * ^ + * ``` + */ + function continuationBefore(code: Code): State | undefined { + if (code === null || markdownLineEnding(code)) { + return continuationStart(code); + } + effects.enter('skipFlowData'); + return continuation(code); + } + + /** + * In raw continuation, after `<`, at `/`. + * + * ```markdown + * > | + * ^ + * ``` + */ + function continuationRawTagOpen(code: Code): State | undefined { + if (code === codes.slash) { + effects.consume(code); + buffer = ''; + return continuationRawEndTagFirstChar; + } + return continuation(code); + } + + /** + * In raw continuation, after ` | + * ^^^^^^ + * ``` + */ + function continuationRawEndTagFirstChar(code: Code): State | undefined { + if (code !== null && asciiAlphabetic(code)) { + effects.consume(code); + buffer += String.fromCharCode(code); + return continuationRawEndTag; + } + return continuation(code); + } + + /** + * In raw continuation, after ` | + * ^^^^^^ + * ``` + */ + function continuationRawEndTag(code: Code): State | undefined { + if (code === codes.greaterThan) { + const bufferLowerCase = buffer.toLowerCase(); + let closingTagString: string; + // `script`, `style`, `textarea`, and `pre` should be treated as + // case-insensitive. + if (htmlRawNames.includes(bufferLowerCase)) { + closingTagString = bufferLowerCase; + } else { + closingTagString = buffer; + } + if (closingTagString === openingTagString) { + effects.consume(code); + return continuationClose; + } + return continuation(code); + } + + // ASCII alphanumerical and `-`, `.`, `:`, and `_`. + if ( + code !== null && + (asciiAlphanumeric(code) || tagPunctuation(code)) + ) { + effects.consume(code); + buffer += String.fromCharCode(code); + return continuationRawEndTag; + } + return continuation(code); + } + + /** + * In closed continuation: everything we get until the eol/eof is part of it. + * + * ```markdown + * > | + * ^ + * ``` + */ + function continuationClose(code: Code): State | undefined { + // if (code === null || markdownLineEnding(code)) { + effects.exit('skipFlowData'); + return continuationAfter(code); + // } + // effects.consume(code); + // return continuationClose; + } + + /** + * Done. + * + * ```markdown + * > | + * ^ + * ``` + */ + function continuationAfter(code: Code): State | undefined { + effects.exit('skipFlow'); + // // Feel free to interrupt. + // tokenizer.interrupt = false + // // No longer concrete. + // tokenizer.concrete = false + return ok(code); + } +} + +function tokenizeNonLazyContinuationStart( + this: TokenizeContext, + effects: Effects, + ok: State, + nok: State, +): State { + const self = this; + return start; + + /** + * At eol, before continuation. + * + * ```markdown + * > | * ```js + * ^ + * | b + * ``` + */ + function start(code: Code): State | undefined { + if (markdownLineEnding(code)) { + effects.enter('lineEnding'); + effects.consume(code); + effects.exit('lineEnding'); + return after; + } + return nok(code); + } + + /** + * A continuation. + * + * ```markdown + * | * ```js + * > | b + * ^ + * ``` + */ + function after(this: TokenizeContext, code: Code): State | undefined { + return self.parser.lazy[self.now().line] ? nok(code) : ok(code); + } +} + +function tagPunctuation(code: NonNullable): boolean { + return ( + code === codes.dash || + code === codes.dot || + code === codes.colon || + code === codes.underscore + ); +} + +function asciiAlphabetic(code: NonNullable): boolean { + return ( + (codes.uppercaseA <= code && code <= codes.uppercaseZ) || + (codes.lowercaseA <= code && code <= codes.lowercaseZ) + ); +} + +function asciiAlphanumeric(code: NonNullable): boolean { + return ( + asciiAlphabetic(code) || (codes.digit0 <= code && code <= codes.digit9) + ); +} diff --git a/src/utils/micromark/syntax.ts b/src/utils/micromark/syntax.ts new file mode 100644 index 0000000..00abda0 --- /dev/null +++ b/src/utils/micromark/syntax.ts @@ -0,0 +1,28 @@ +import type { Construct } from 'micromark-util-types'; +import { codes } from 'micromark-util-symbol'; +import { tokenizeSkipFlow } from './skip-flow.js'; +// import { tokenizeSkipText } from '$utils/micromark/skip-text.js'; + +const skipFlow: Construct = { + name: 'skipFlow', + tokenize: tokenizeSkipFlow, + // resolveTo: resolveToSkipFlow, + concrete: true, +}; + +export let skipTags: string[]; + +// const skipText: Construct = { +// name: 'skipText', +// tokenize: tokenizeSkipText, +// }; + +export function micromarkSkip( + tags: string[] | undefined = ['script', 'style'], +) { + skipTags = tags; + return { + flow: { [codes.lessThan]: skipFlow }, + text: { [codes.lessThan]: skipFlow }, + }; +} diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 755918c..bcce067 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -1,120 +1,31 @@ // Types import type { BinaryToTextEncoding } from '$deps.js'; +import type { Transformation } from '$types/handlers/Tex.js'; // Internal dependencies -import { isBoolean, isNumber, isString } from '$type-guards/utils.js'; +import { isString } from '$type-guards/utils.js'; import { log } from '$utils/debug.js'; // External dependencies -import { createHash } from '$deps.js'; -import { Transformation } from '$types/handlers/Tex.js'; +import { createHash, htmlTagNames } from '$deps.js'; /** - * "Interprets" a string as a boolean, number, null, or undefined, if - * applicable. Otherwise, returns the string as is. + * Check if a string is a valid name for a component. For this to be the case, + * it has to not be an HTML tag name, and it has to be an alphanumeric string + * that starts with a letter. * - * @param str - The string to interpret. - * @returns The interpreted value. + * @param name - The name to check. + * @returns `true` if the name is valid, `false` otherwise. * * @example - * // All of the below are true - * interpretString('true') === true; - * interpretString('false') === false; - * interpretString('null') === null; - * interpretString('undefined') === undefined; - * interpretString('NaN') === NaN; - * interpretString('Infinity') === Infinity; - * interpretString('-Infinity') === -Infinity; - * interpretString('5') === 5; - * interpretString('5.5') === 5.5; - * interpretString('something') === 'something'; - */ -export function interpretString( - str: string | undefined, -): string | number | boolean | null | undefined { - if (str === undefined) return undefined; - const trimmedStr = str.trim(); - switch (trimmedStr) { - case 'true': - return true; - case 'false': - return false; - case 'null': - return null; - case 'undefined': - return undefined; - case 'NaN': - return NaN; - case 'Infinity': - return Infinity; - } - if (trimmedStr.endsWith('Infinity')) { - if (trimmedStr.match(/^[+]\s*Infinity$/)) { - return +Infinity; - } - if (trimmedStr.match(/^[-]\s*Infinity$/)) { - return -Infinity; - } - } - if (trimmedStr.replace(/\s*/g, '').match(/^[+-]?(\d+|\d*.\d+)$/)) { - const num = Number(trimmedStr); - if (!isNaN(num)) { - return num; - } - } - return str; -} - -/** - * Calls {@link interpretString | `interpretString`} on each value in the given - * object. - * - * @param attrs - The object whose values to interpret. - * @returns A new object with the interpreted values. - * - * @example - * ```ts - * interpretAttributes({ - * a: 'true', - * b: '5', - * c: 'something', - * }); - * ``` - * - * ...would return... - * * ```ts - * { - * a: true, - * b: 5, - * c: 'something', - * } + * isValidComponentName('div'); // false + * isValidComponentName('div2'); // true + * isValidComponentName('MyComponent'); // true * ``` */ -export function interpretAttributes( - attrs: Record, - strict: boolean = true, -): Record { - const rv: Record = {}; - for (const [key, value] of Object.entries(attrs)) { - if (value !== undefined && !isString(value)) { - const supportedValueType: boolean = - isBoolean(value) || - isNumber(value) || - (value as unknown) === null; - log( - strict ? 'error' : 'warn', - `Expected string for attribute \`${key}\`, but got \`${String(value)}\`. ${strict || !supportedValueType ? 'Ignoring attribute.' : 'Passing value as-is.'} (Hint: Numbers, booleans, null and undefined, if wrapped in double quotes, will be transformed back into their original types by Sveltex.)`, - ); - if (!strict && supportedValueType) - rv[key] = value as boolean | number | null; - } else if (value === undefined) { - rv[key] === value; - } else { - rv[key] = interpretString(value); - } - } - return rv; +export function isValidComponentName(name: string): boolean { + return /^[a-zA-Z][a-zA-Z0-9]*$/.test(name) && !htmlTagNames.includes(name); } /** @@ -204,60 +115,6 @@ export function re(strings: TemplateStringsArray, ...flags: string[]) { ); } -/** - * Splits content into segments based on ` - * B - * - * D - * ` - * ``` - * - * Then the output of `splitContent(content)` would be: - * - * ```ts - * [ - * "", - * 'B', - * '', - * 'D' - * ] - * ``` - */ -export function splitContent(content: string): string[] { - const regex = re` - (?: - \n', ''); + html = html.replace(/

(.*?)<\/p>/gsu, '$1'); + code = code?.replace(/

(.*?)<\/p>/gsu, '$1'); + if ( + (s.markdownBackend === 'markdown-it' && + [127, 137, 139].includes(example)) || + (s.markdownBackend === 'marked' && + [126, 130].includes(example)) + ) { + expect(code?.replace(/\s/g, '')).toContain( + html.replace(/\s/g, ''), + ); + } else if ( + s.markdownBackend === 'marked' && + [12].includes(example) + ) { + // + } else { + expect(code).toContain(html.trim()); + } + expect(log).not.toHaveBeenCalled(); + }); + }, + ); + + describe.concurrent.each(processors)( + 'CommonMark: code spans ($markdownBackend)', + (s) => { + fixture(); + it.concurrent.each([ + { + markdown: '`foo`\n', + html: '

foo

\n', + example: 328, + }, + { + markdown: '`` foo ` bar ``\n', + html: '

foo ` bar

\n', + example: 329, + }, + { + markdown: '` `` `\n', + html: '

``

\n', + example: 330, + }, + { + markdown: '` `` `\n', + html: '

``

\n', + example: 331, + }, + { + markdown: '` a`\n', + html: '

a

\n', + example: 332, + }, + { + markdown: '`\tb\t`\n', + html: '

\tb\t

\n', + example: 333, + }, + { + markdown: '` `\n` `\n', + html: '

\n

\n', + example: 334, + }, + { + markdown: '``\nfoo\nbar \nbaz\n``\n', + html: '

foo bar baz

\n', + example: 335, + }, + { + markdown: '``\nfoo \n``\n', + html: '

foo

\n', + example: 336, + }, + { + markdown: '`foo bar \nbaz`\n', + html: '

foo bar baz

\n', + example: 337, + }, + { + markdown: '`foo\\`bar`\n', + html: '

foo\\bar`

\n', + example: 338, + }, + { + markdown: '``foo`bar``\n', + html: '

foo`bar

\n', + example: 339, + }, + { + markdown: '` foo `` bar `\n', + html: '

foo `` bar

\n', + example: 340, + }, + { + markdown: '*foo`*`\n', + html: '

*foo*

\n', + example: 341, + }, + { + markdown: '[not a `link](/foo`)\n', + html: '

[not a link](/foo)

\n', + example: 342, + }, + { + markdown: '``\n', + html: '

<a href="">`

\n', + example: 343, + }, + // { + // markdown: '
`\n', + // html: '

`

\n', + // example: 344, + // }, + { + markdown: '``\n', + html: '

<https://foo.bar.baz>`

\n', + example: 345, + }, + // { + // markdown: '`\n', + // html: '

https://foo.bar.`baz`

\n', + // example: 346, + // }, + { + markdown: '```foo``\n', + html: '

```foo``

\n', + example: 347, + }, + { + markdown: '`foo\n', + html: '

`foo

\n', + example: 348, + }, + { + markdown: '`foo``bar``\n', + html: '

`foobar

\n', + example: 349, + }, + ])('Example $example', async ({ markdown, html, example }) => { + let code = ( + await s.markup({ + content: markdown, + filename: `${String(example)}.sveltex`, + }) + )?.code; + code = code?.replaceAll(' class="language-plaintext"', ''); + code = code?.replace('\n', ''); + html = html.replace(/

(.*?)<\/p>/gsu, '$1'); + code = code?.replace(/

(.*?)<\/p>/gsu, '$1'); + if ( + s.markdownBackend === 'unified' && + [343, 345].includes(example) + ) { + // It seems unified does not escapes some special characters in + // the same way as other markdown processors. + if (example === 343) { + expect(code).toContain( + '<a href="">`', + ); + } else if (example === 345) { + expect(code).toContain( + '<https://foo.bar.baz>`', + ); + } + } else expect(code).toContain(html.trim()); + expect(log).not.toHaveBeenCalled(); + }); + }, + ); +}); diff --git a/tests/Sveltex/sveltex.error.test.ts b/tests/Sveltex/sveltex.error.test.ts index deec726..8f10176 100644 --- a/tests/Sveltex/sveltex.error.test.ts +++ b/tests/Sveltex/sveltex.error.test.ts @@ -1,27 +1,30 @@ -import { missingDeps } from '$utils/globals.js'; +import { missingDeps } from '$utils/env.js'; import { sveltex } from '$Sveltex.js'; import { consoles } from '$utils/debug.js'; -import { afterAll, describe, expect, it, suite, vi } from 'vitest'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import { spy } from '$tests/fixtures.js'; -suite('sveltex error handling', async () => { - await spy(['writeFile', 'log', 'mkdir'], true); +describe('sveltex error handling', () => { + beforeAll(async () => { + await spy(['writeFile', 'log', 'mkdir'], true); + }); afterAll(() => { vi.restoreAllMocks(); }); it('catches errors', async () => { - vi.mock('svelte/compiler', async (importOriginal) => { - const actual = await importOriginal(); - if (typeof actual !== 'object') { - throw new Error('test error'); - } - return { - ...actual, - parse: () => { - throw new Error('test error'); - }, - }; - }); + vi.mock( + 'mdast-util-from-markdown', + async ( + orig: () => Promise, + ) => { + return { + ...(await orig()), + fromMarkdown: () => { + throw new Error('test error'); + }, + }; + }, + ); const consoleErrorMock = vi .spyOn(consoles, 'error') .mockImplementation(() => undefined); diff --git a/tests/Sveltex/sveltex.real.test.ts b/tests/Sveltex/sveltex.real.test.ts index 6ca8602..6341051 100644 --- a/tests/Sveltex/sveltex.real.test.ts +++ b/tests/Sveltex/sveltex.real.test.ts @@ -9,17 +9,18 @@ import type { TexBackend } from '$types/handlers/Tex.js'; import { Sveltex, sveltex } from '$Sveltex.js'; import { spy } from '$tests/fixtures.js'; import { isArray, isString } from '$type-guards/utils.js'; -import { splitContent } from '$utils/misc.js'; +import { re } from '$utils/misc.js'; -import { assert, is, uuid } from '$deps.js'; +import { typeAssert, is, uuid } from '$deps.js'; import { + type MockInstance, afterAll, afterEach, + beforeAll, beforeEach, describe, expect, it, - suite, vi, } from 'vitest'; @@ -108,6 +109,18 @@ const preprocessors = [ ), ] as Sveltex[]; +function splitContent(content: string): string[] { + return content.match(splitContentRegExp) ?? []; +} + +const splitContentRegExp = re` + (?: + \n

\n\n
code', + '\n
\n\n
\ncode', ); expect( Object.keys(sp.advancedTexHandler.texComponents).length, @@ -103,6 +106,7 @@ suite('Sveltex', async () => { expect((scriptOut as Processed).code).toEqual( "\nimport Sveltex__tex__something from '/src/sveltex/tex/something.svelte';\nimport '/src/sveltex/mathjax@3.2.2.svg.min.css';\nimport '/src/sveltex/highlight.js@11.9.0.default.min.css';\n", ); + expect(log).toHaveBeenCalledTimes(1); sp.advancedTexHandler.texComponents = {}; }); @@ -134,18 +138,19 @@ suite('Sveltex', async () => { '7a541239-3058-460b-b3c6-5076a2f3f73b.sveltex', }) )?.code, - ).not.toContain('Sveltex__'); + ).toBeUndefined(); sp.advancedTexHandler.texComponents = {}; }); it('works', async () => { const markupOut = await sp.markup({ content: - 'x`code`$x$', + 'x\n`code`\n$x$', filename: '9ae17b43-d19c-4ca3-9772-36e506ffb4a5.sveltex', }); + expect((markupOut as Processed).code).toContain( - '\n
\n\n
some text here
\n
code', + '\n
\n\n
some text here
\n
\ncode\n', ); expect( Object.keys(sp.advancedTexHandler.texComponents).length, @@ -159,11 +164,11 @@ suite('Sveltex', async () => { const scriptOut = await sp.script({ content: '', attributes: {}, - markup: 'x`code`$x$', + markup: 'x`code`$x$', filename: '9ae17b43-d19c-4ca3-9772-36e506ffb4a5.sveltex', }); expect((scriptOut as Processed).code).toEqual( - "\nimport Sveltex__tex__ref_as_valueless_attribute from '/src/sveltex/tex/ref-as-valueless-attribute.svelte';\nimport '/src/sveltex/mathjax@3.2.2.svg.min.css';\nimport '/src/sveltex/highlight.js@11.9.0.default.min.css';\n", + "\nimport Sveltex__tex__ref_without_quotation_marks from '/src/sveltex/tex/ref-without-quotation-marks.svelte';\nimport '/src/sveltex/mathjax@3.2.2.svg.min.css';\nimport '/src/sveltex/highlight.js@11.9.0.default.min.css';\n", ); sp.advancedTexHandler.texComponents = {}; @@ -172,11 +177,11 @@ suite('Sveltex', async () => { it('works (coffeescript)', async () => { const markupOut = await sp.markup({ content: - 'x`code`$x$', + 'x\n`code`\n$x$', filename: '420274ac-0f4d-49b9-842e-f9937ae45ca6.sveltex', }); expect((markupOut as Processed).code).toContain( - '\n
\n\n
some text here
\n
', + '\n
\n\n
some text here
\n
', ); expect( Object.keys(sp.advancedTexHandler.texComponents).length, @@ -190,11 +195,11 @@ suite('Sveltex', async () => { const scriptOut = await sp.script({ content: '', attributes: { lang: 'coffeescript' }, - markup: 'x', + markup: 'x', filename: '420274ac-0f4d-49b9-842e-f9937ae45ca6.sveltex', }); expect((scriptOut as Processed).code).toEqual( - "\n```\nimport Sveltex__tex__ref_as_valueless_attribute from '/src/sveltex/tex/ref-as-valueless-attribute.svelte';\nimport '/src/sveltex/mathjax@3.2.2.svg.min.css';\nimport '/src/sveltex/highlight.js@11.9.0.default.min.css';\n```\n", + "\n```\nimport Sveltex__tex__ref_without_quotation_marks from '/src/sveltex/tex/ref-without-quotation-marks.svelte';\nimport '/src/sveltex/mathjax@3.2.2.svg.min.css';\nimport '/src/sveltex/highlight.js@11.9.0.default.min.css';\n```\n", ); sp.advancedTexHandler.texComponents = {}; diff --git a/tests/Sveltex/sveltex.test.ts b/tests/Sveltex/sveltex.test.ts index 1dd2b4b..d5e1483 100644 --- a/tests/Sveltex/sveltex.test.ts +++ b/tests/Sveltex/sveltex.test.ts @@ -7,19 +7,19 @@ import type { MarkdownBackend } from '$types/handlers/Markdown.js'; import type { TexBackend } from '$types/handlers/Tex.js'; import { sveltex, type Sveltex } from '$Sveltex.js'; -import { Processed } from '$types/Sveltex.js'; import { AdvancedTexHandler } from '$handlers/AdvancedTexHandler.js'; import { CodeHandler } from '$handlers/CodeHandler.js'; import { MarkdownHandler } from '$handlers/MarkdownHandler.js'; import { TexHandler } from '$handlers/TexHandler.js'; -import { spy } from '$tests/fixtures.js'; +import { removeEmptyLines, spy } from '$tests/fixtures.js'; import { range } from '$tests/utils.js'; -import { SourceMapConsumer } from 'source-map'; -import { afterAll, describe, expect, it, suite, vi } from 'vitest'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; -suite.concurrent('Sveltex', async () => { - await spy(['writeFile', 'log', 'existsSync', 'mkdir'], true); +describe.concurrent('Sveltex', () => { + beforeAll(async () => { + await spy(['writeFile', 'log', 'existsSync', 'mkdir'], true); + }); afterAll(() => { vi.restoreAllMocks(); }); @@ -82,22 +82,24 @@ suite.concurrent('Sveltex', async () => { process: () => 'custom output advanced tex', }, ); - expect(await preprocessor.markdownHandler.process('')).toEqual( - 'custom output markdown', - ); - expect(await preprocessor.codeHandler.process('')).toEqual( - '
\ncustom output code\n
', - ); - expect(await preprocessor.texHandler.process('')).toEqual( - 'custom output tex', - ); expect( - await preprocessor.advancedTexHandler.process('', { - attributes: { ref: 'ref' }, - selfClosing: false, - tag: 'name', - filename: 'test.sveltex', - }), + (await preprocessor.markdownHandler.process('')).processed, + ).toEqual('custom output markdown'); + expect( + (await preprocessor.codeHandler.process('')).processed, + ).toEqual('
custom output code\n
'); + expect( + (await preprocessor.texHandler.process('')).processed, + ).toEqual('custom output tex'); + expect( + ( + await preprocessor.advancedTexHandler.process('', { + attributes: { ref: 'ref' }, + selfClosing: false, + tag: 'name', + filename: 'test.sveltex', + }) + ).processed, ).toEqual('custom output advanced tex'); }); it('should throw error if corresponding backend is not custom', async () => { @@ -204,194 +206,196 @@ preprocessor.configure({ }, }); -suite.each([ - [ - [ - 'hello', - '', - 'test1', - 'test2', - 'test3', - 'test4', - 'test5', - 'test6', - 'test7', - 'test8', - 'test9', - '', - 'there', // 1 - '`1`', // 2 - '{#if condition}', // 3 - '*a* {mustache}', // 4 - '{:else}', // 5 - '**b**', // 6 - '{/if}', // 7 - '*italic*', // 8 - '`code`', // 9 - 'or **bold**', // 10 - ], - [ - null, - null, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 15, - 15, - 17, - 17, - 19, - 19, - 19, - 19, - ], - ], -])('source maps', async (inputLines, lineMap) => { - const input = inputLines.join('\n'); - const result = (await preprocessor.markup({ - content: input, - filename: 'test.sveltex', - })) as Processed | undefined; - if (result === undefined) { - return; - } - const output = result.code; - const outputLines = output.split('\n'); - const map = result.map; - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, - // @typescript-eslint/no-explicit-any, - // @typescript-eslint/no-non-null-assertion - if (map === undefined) { - return; - } - const smc = await new SourceMapConsumer(map); - describe('output', () => { - it('should be as expected', () => { - expect(output).toEqual( - [ - '', // 2 - 'hello', // 3 - '', // 4 - 'test1', - 'test2', - 'test3', - 'test4', - 'test5', - 'test6', - 'test7', - 'test8', - 'test9', - '', // 10 - 'there', // 11 - '1', // 12 - '{#if condition}', // 13 - 'a {mustache}', // 14 - '{:else}', // 15 - 'b', // 16 - '{/if}', // 17 - 'italic', // 18 - 'code', // 19 - 'or bold', // 20 - ].join('\n'), - ); - }); - }); +// suite.skip.each([ +// [ +// [ +// 'hello', +// '', +// 'test1', +// 'test2', +// 'test3', +// 'test4', +// 'test5', +// 'test6', +// 'test7', +// 'test8', +// 'test9', +// '', +// 'there', // 1 +// '`1`', // 2 +// '{#if condition}', // 3 +// '*a* {mustache}', // 4 +// '{:else}', // 5 +// '**b**', // 6 +// '{/if}', // 7 +// '*italic*', // 8 +// '`code`', // 9 +// 'or **bold**', // 10 +// ], +// [ +// null, +// null, +// 1, +// 1, +// 1, +// 1, +// 1, +// 1, +// 1, +// 1, +// 1, +// 1, +// 1, +// 1, +// 1, +// 1, +// 15, +// 15, +// 17, +// 17, +// 19, +// 19, +// 19, +// 19, +// ], +// ], +// ])('source maps', async (inputLines, lineMap) => { +// const input = inputLines.join('\n'); +// const result = (await preprocessor.markup({ +// content: input, +// filename: 'test.sveltex', +// })) as Processed | undefined; +// if (result === undefined) { +// return; +// } +// const output = result.code; +// const outputLines = output.split('\n'); +// const map = result.map; +// // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, +// // @typescript-eslint/no-explicit-any, +// // @typescript-eslint/no-non-null-assertion +// if (map === undefined) { +// return; +// } +// const smc = await new SourceMapConsumer(map); +// describe('output', () => { +// it('should be as expected', () => { +// expect(output).toEqual( +// [ +// '', // 2 +// 'hello', // 3 +// '', // 4 +// 'test1', +// 'test2', +// 'test3', +// 'test4', +// 'test5', +// 'test6', +// 'test7', +// 'test8', +// 'test9', +// '', // 10 +// 'there', // 11 +// '1', // 12 +// '{#if condition}', // 13 +// 'a {mustache}', // 14 +// '{:else}', // 15 +// 'b', // 16 +// '{/if}', // 17 +// 'italic', // 18 +// 'code', // 19 +// 'or bold', // 20 +// ].join('\n'), +// ); +// }); +// }); - describe.each(range(1, outputLines.length))( - 'preimage of line %o of result', - (outputLine) => { - const outputLineIndex0 = outputLine - 1; - const origPos = smc.originalPositionFor({ - line: outputLine, - column: 0, - }); - it(`output line ${String(outputLine)} comes from input line ${String(lineMap[outputLineIndex0])}`, async () => { - const input = inputLines.join('\n'); - await preprocessor.markup({ - content: input, - filename: 'test.sveltex', - }); - // const outputLines = output.split('\n'); - // const map = result?.map; - expect(origPos.line).toEqual(lineMap[outputLineIndex0]); - }); - }, - ); +// describe.each(range(1, outputLines.length))( +// 'preimage of line %o of result', +// (outputLine) => { +// const outputLineIndex0 = outputLine - 1; +// const origPos = smc.originalPositionFor({ +// line: outputLine, +// column: 0, +// }); +// it(`output line ${String(outputLine)} comes from input line ${String(lineMap[outputLineIndex0])}`, async () => { +// const input = inputLines.join('\n'); +// await preprocessor.markup({ +// content: input, +// filename: 'test.sveltex', +// }); +// // const outputLines = output.split('\n'); +// // const map = result?.map; +// expect(origPos.line).toEqual(lineMap[outputLineIndex0]); +// }); +// }, +// ); - describe('should provide empty source map if sorcery fails', () => { - it('should provide empty source map', async () => { - const sorceryLoadMock = vi - .spyOn(await import('sorcery'), 'load') - .mockResolvedValueOnce(null); - const result = (await preprocessor.markup({ - content: input, - filename: 'test.sveltex', - })) as Processed; - expect(result.map).toEqual({ - file: 'test.sveltex', - mappings: '', - names: [], - sources: [], - sourcesContent: [], - version: 3, - }); - sorceryLoadMock.mockRestore(); - }); - }); -}); +// describe('should provide empty source map if sorcery fails', () => { +// it('should provide empty source map', async () => { +// const sorceryLoadMock = vi +// .spyOn(await import('sorcery'), 'load') +// .mockResolvedValueOnce(null); +// const result = (await preprocessor.markup({ +// content: input, +// filename: 'test.sveltex', +// })) as Processed; +// expect(result.map).toEqual({ +// file: 'test.sveltex', +// mappings: '', +// names: [], +// sources: [], +// sourcesContent: [], +// version: 3, +// }); +// sorceryLoadMock.mockRestore(); +// }); +// }); +// }); -suite('edge cases', async () => { - const s = await sveltex({ - markdownBackend: 'marked', - codeBackend: 'starry-night', - texBackend: 'none', - advancedTexBackend: 'none', - }); - await s.configure({ - advancedTex: { - components: { - TeX: {}, +describe('edge cases', () => { + let s: Sveltex<'marked', 'starry-night'>; + beforeAll(async () => { + s = await sveltex({ + markdownBackend: 'marked', + codeBackend: 'starry-night', + }); + await s.configure({ + advancedTex: { + components: { + TeX: {}, + }, }, - }, - verbatim: { - verbatimEnvironments: { - Verbatim: {}, + verbatim: { + verbatimEnvironments: { + Verbatim: {}, + Verb: {}, + }, }, - }, - code: { - languages: 'common', - }, + code: { + languages: 'common', + }, + }); }); async function markup(content: string, filename: string = 'test.sveltex') { return (await s.markup({ content, filename }))?.code; } describe('escaping edge cases', () => { - it('Verbatim inside Code', async () => { + it('Verb inside Verbatim', async () => { expect( await markup( [ - '```html', '', + '', 'test', + '', '', - '```', ].join('\n'), ), ).toEqual( - '\n\n\n\n
\n<Verbatim>\ntest\n</Verbatim>\n
', + '\n\n<Verb>\ntest\n</Verb>\n', ); }); @@ -404,11 +408,17 @@ suite('edge cases', async () => { ), ).toContain('\n```\ntest\n```\n'); }); + + it('dollar signs in mustache tags', async () => { + expect(await markup(["text {'$$'} text"].join('\n'))).toContain( + "text {'$$'} text", + ); + }); }); }); -suite.concurrent('sveltex()', async () => { - describe.concurrent('basics', () => { +describe('sveltex()', () => { + describe('basics', () => { it('is defined', () => { expect(preprocessor).toBeDefined(); expect(preprocessor.markup).toBeDefined(); @@ -436,23 +446,23 @@ suite.concurrent('sveltex()', async () => { }); }); - describe.sequential('respects svelte syntax', () => { + describe('respects svelte syntax', () => { const tests = [ { label: 'script tag', - input: 'test', + input: '\n

test

', }, { label: 'style tag', input: '', expected: - '\n', + '\n\n\n', }, { label: 'svelte:head tag', input: '_test_', expected: - '\n<em>test</em>', + '\n_test_', }, { label: 'svelte:options tag', @@ -478,43 +488,64 @@ suite.concurrent('sveltex()', async () => { expected: '\n', }, + { + label: 'svelte:element tag', + input: 'foo', + expected: + '\nfoo', + }, { label: 'if block', input: '{#if condition}\nA\n{/if}', - expected: '\n{#if condition}\nA\n{/if}', + expected: + '\n{#if condition}\n

A

\n{/if}', }, { label: 'else block', input: '{#if condition}\nA\n{:else}\nB\n{/if}', expected: - '\n{#if condition}\nA\n{:else}\nB\n{/if}', + '\n{#if condition}\n

A

\n{:else}\n

B

\n{/if}', }, { label: 'else if block', input: '{#if condition}\n>A\n{:else if otherCondition}\n*B*\n{:else}\nC\n{/if}', expected: - '\n{#if condition}
\n

A

\n
\n{:else if otherCondition}\nB\n{:else}\nC\n{/if}', + '\n{#if condition}\n
\n

A

\n
\n{:else if otherCondition}\n

B

\n{:else}\n

C

\n{/if}\n', }, { label: 'each block', input: '{#each items as item}\n*A* {item} B\n{/each}', expected: - '\n{#each items as item}\nA {item} B\n{/each}', + '\n{#each items as item}\n

A {item} B

\n{/each}\n', }, { label: 'await block', input: '{#await promise}\n*loading...*\n{:then value}\n{value}\n{:catch error}\n

{error.message}

\n{/await}', expected: - '\n{#await promise}\nloading...\n{:then value}\n{value}\n{:catch error}\n

{error.message}

\n{/await}', + '\n{#await promise}\n

loading...

\n{:then value}\n

{value}

\n{:catch error}\n

{error.message}

\n{/await}', }, { label: 'html block', input: '\n{@html "

hello, world!

"}', + expected: + '\n

{@html "

hello, world!

"}

', }, ]; - it.each(tests)('$test.label', async (test) => { - expect(await preprocess(test.input)).toEqual( - test.expected ?? test.input, + it.each(tests)('$label', async (test) => { + expect(removeEmptyLines(await preprocess(test.input))).toContain( + removeEmptyLines(test.expected ?? test.input), + ); + }); + }); + + describe("code blocks don't need more than one newline character around them", () => { + it('should work', async () => { + expect( + await preprocess( + '\n\ntext\n```typescript\n() => {let a}\n```\ntext\n\n', + ), + ).toEqual( + '\n

text

\n
() => {let a}\n
\n

text

\n', ); }); }); @@ -564,7 +595,7 @@ suite.concurrent('sveltex()', async () => { '\nalt', }, ]; - it.each(tests)('$test.label', async (test) => { + it.each(tests)('$label', async (test) => { expect(await preprocess(test.input)).toEqual(test.expected); }); }); @@ -616,15 +647,14 @@ suite.concurrent('sveltex()', async () => { label: 'code block', input: '```\nsomething\n```', expected: - '\n
\nsomething\n
', + '\n\n
something\n
\n', }, ]; - it.each(tests)('$test.label', async (test) => { + it.each(tests)('$label', async (test) => { expect(await preprocess(test.input)).toEqual(test.expected); }); }); - // eslint-disable-next-line vitest/valid-describe-callback describe('works with code blocks', () => { it('starry night should work with this', async () => { const preprocessor = await sveltex({ @@ -635,11 +665,13 @@ suite.concurrent('sveltex()', async () => { }); await preprocessor.configure({ code: { languages: 'common' } }); expect( - await preprocessor.codeHandler.process( - '```typescript\n() => {let a}\n```', - ), + ( + await preprocessor.codeHandler.process('() => {let a}', { + lang: 'typescript', + }) + ).processed, ).toEqual( - '
\n() => {let a}\n
', + '
() => {let a}\n
', ); }); @@ -648,15 +680,15 @@ suite.concurrent('sveltex()', async () => { label: 'code block (plain)', input: '```\n() => {let a}\n```', expected: - '\n\n\n\n
\n() => {let a}\n
', + '\n\n\n\n\n
() => {let a}\n
\n', }, { label: 'code block (ts)', input: '```typescript\n() => {let a}\n```', expected: - '\n\n\n\n
\n() => {let a}\n
', + '\n\n\n\n\n
() => {let a}\n
\n', }, - ])('$test.label', async (test) => { + ])('$label', async (test) => { const preprocessor = await sveltex({ markdownBackend: 'marked', codeBackend: 'starry-night', @@ -705,35 +737,36 @@ suite.concurrent('sveltex()', async () => { '\n{alt}', }, ]; - it.each(tests)('$test.label', async (test) => { + it.each(tests)('$label', async (test) => { expect(await preprocess(test.input)).toEqual(test.expected); }); }); - const preprocessorVerbatim = await sveltex({ - markdownBackend: 'none', - codeBackend: 'none', - texBackend: 'none', - advancedTexBackend: 'none', - }); - await preprocessorVerbatim.configure({ - verbatim: { - verbatimEnvironments: { - Verbatim: { - processInner: { escapeBraces: true, escapeHtml: true }, - }, - }, - }, - }); - const preprocessVerbatim = preprocessFn(preprocessorVerbatim); - describe('verbatim environments', () => { + let preprocessVerbatim: ( + input: string, + filename?: string, + ) => Promise; + beforeAll(async () => { + const preprocessorVerbatim = await sveltex(); + await preprocessorVerbatim.configure({ + verbatim: { + verbatimEnvironments: { + Verbatim: { + processInner: { + escapeBraces: true, + escapeHtml: true, + }, + }, + }, + }, + }); + preprocessVerbatim = preprocessFn(preprocessorVerbatim); + }); it('should work with custom verbatim environments', async () => { expect( await preprocessVerbatim('{test}'), - ).toEqual( - '\n{test}', - ); + ).toContain('{test}'); }); it('should work with TeX verbatim environments', async () => { @@ -756,27 +789,32 @@ suite.concurrent('sveltex()', async () => { }, }); const preprocess = preprocessFn(preprocessor); - expect(await preprocess('$x$')).toEqual('\nx'); + expect(await preprocess('$x$')).toEqual('\n$x$'); }); }); - const preprocessorMisc = await sveltex({ - markdownBackend: 'none', - codeBackend: 'escapeOnly', - texBackend: 'none', - advancedTexBackend: 'none', - }); - await preprocessorMisc.configure({ - general: { - extensions: undefined, - }, - }); - const preprocessMisc = preprocessFn(preprocessorMisc); - describe('misc', () => { - it('should work', async () => { - expect(await preprocessMisc('`{}`')).toEqual( - '\n{}', + let preprocessMisc: ( + input: string, + filename?: string, + ) => Promise; + beforeAll(async () => { + const preprocessorMisc = await sveltex({ + markdownBackend: 'none', + codeBackend: 'escapeOnly', + texBackend: 'none', + advancedTexBackend: 'none', + }); + await preprocessorMisc.configure({ + general: { + extensions: undefined, + }, + }); + preprocessMisc = preprocessFn(preprocessorMisc); + }); + it('`{}`', async () => { + expect(await preprocessMisc('`{}`')).toContain( + '{}', ); }); }); diff --git a/tests/config/defaults.test.ts b/tests/config/defaults.test.ts index 4541725..b916817 100644 --- a/tests/config/defaults.test.ts +++ b/tests/config/defaults.test.ts @@ -3,10 +3,11 @@ import { it, expect, vi, - suite, afterAll, beforeEach, afterEach, + beforeAll, + MockInstance, } from 'vitest'; import { getDefaultCodeConfiguration, @@ -29,12 +30,16 @@ function fixture() { }); } -suite.concurrent('config/defaults', async () => { +describe.concurrent('config/defaults', () => { + let log: MockInstance; + beforeAll(async () => { + const mocks = await spy(['log']); + log = mocks.log; + }); afterAll(() => { vi.restoreAllMocks(); }); fixture(); - const { log } = await spy(['log']); describe('getDefaultTexComponentConfig', () => { fixture(); @@ -142,6 +147,7 @@ suite.concurrent('config/defaults', async () => { inline: true, lang: 'javascript', wrapClassPrefix: 'language-', + _wrap: true, }; const [openingTag, closingTag] = getDefaultCodeConfiguration('escapeOnly').wrap(opts); @@ -154,6 +160,7 @@ suite.concurrent('config/defaults', async () => { inline: false, lang: 'typescript', wrapClassPrefix: 'language-', + _wrap: true, }; const [openingTag, closingTag] = getDefaultCodeConfiguration('escapeOnly').wrap(opts); @@ -165,6 +172,7 @@ suite.concurrent('config/defaults', async () => { const opts = { inline: false, wrapClassPrefix: 'language-', + _wrap: true, }; const [openingTag, closingTag] = getDefaultCodeConfiguration('escapeOnly').wrap(opts); diff --git a/tests/fixtures.ts b/tests/fixtures.ts index 549e727..08fcb3f 100644 --- a/tests/fixtures.ts +++ b/tests/fixtures.ts @@ -1,6 +1,6 @@ import { isArray, isString } from '$type-guards/utils.js'; import { mockFs } from '$dev_deps.js'; -import { type MockInstance, vi, beforeEach, afterAll } from 'vitest'; +import { type MockInstance, vi, beforeEach, afterAll, expect } from 'vitest'; type Mockable = (typeof mockableFunctions)[number]; @@ -120,6 +120,17 @@ export function fixture1( }); } +export function removeEmptyLines(input: string = '') { + return input + .split('\n') + .filter((line) => line.trim().length > 0) + .join('\n'); +} + +export function equalUpToNewlines(a: string = '', b: string = '') { + expect(removeEmptyLines(a)).toEqual(removeEmptyLines(b)); +} + // export async function mockNodeFs() { // vi.mock( // 'node:fs', @@ -160,14 +171,6 @@ export function fixture1( // }; // } -export async function createLogSpy(): Promise { - return vi.spyOn(await import('$utils/debug.js'), 'log'); -} - -export async function createSpawnCliInstructionSpy(): Promise { - return vi.spyOn(await import('$utils/cli.js'), 'spawnCliInstruction'); -} - // export async function spy(target: { // module: 'node:fs'; // readFileSync?: 'spy' | 'mock' | undefined; diff --git a/tests/handlers/AdvancedTexHandler/AdvancedTexHandler.test.ts b/tests/handlers/AdvancedTexHandler/AdvancedTexHandler.test.ts index 50d8c9e..8efeca0 100644 --- a/tests/handlers/AdvancedTexHandler/AdvancedTexHandler.test.ts +++ b/tests/handlers/AdvancedTexHandler/AdvancedTexHandler.test.ts @@ -1,10 +1,9 @@ import { AdvancedTexHandler } from '$handlers/AdvancedTexHandler.js'; -// import { sveltex } from '$Sveltex.js'; import { spy } from '$tests/fixtures.js'; -import { pathExists } from '$utils/debug.js'; import { rimraf, resolve } from '$deps.js'; import { + type MockInstance, afterAll, afterEach, beforeAll, @@ -12,11 +11,11 @@ import { describe, expect, it, - suite, vi, } from 'vitest'; import { sha256 } from '$utils/misc.js'; import { AdvancedTexConfiguration } from '$mod.js'; +import { pathExists } from '$utils/fs.js'; // async function handlers() { // const ath = await AdvancedTexHandler.create('local'); @@ -101,7 +100,7 @@ function fixture(config?: AdvancedTexConfiguration<'local'>) { }); } -suite("AdvancedTexHandler<'local'>", async () => { +describe("AdvancedTexHandler<'local'>", () => { vi.restoreAllMocks(); beforeAll(async () => { vi.spyOn(await import('$deps.js'), 'ora').mockImplementation((() => ({ @@ -112,16 +111,27 @@ suite("AdvancedTexHandler<'local'>", async () => { fail: vi.fn(), }), })) as unknown as typeof import('ora').default); + const mocks = await spy( + ['writeFile', 'log', 'spawnCliInstruction'], + false, + ); + writeFile = mocks.writeFile; + log = mocks.log; + spawnCliInstruction = mocks.spawnCliInstruction; + log.mockImplementation(() => undefined); }); afterAll(() => { vi.restoreAllMocks(); }); fixture(); - const { writeFile, log, spawnCliInstruction } = await spy( - ['writeFile', 'log', 'spawnCliInstruction'], - false, - ); - log.mockImplementation(() => undefined); + let writeFile: MockInstance; + let log: MockInstance; + let spawnCliInstruction: MockInstance; + // const { writeFile, log, spawnCliInstruction } = await spy( + // ['writeFile', 'log', 'spawnCliInstruction'], + // false, + // ); + // writeFile.mockImplementation( // async (path: string, data: string, opts: ObjectEncodingOptions) => { // if (path.endsWith('cache.json')) { @@ -178,16 +188,15 @@ suite("AdvancedTexHandler<'local'>", async () => { ); }); - it.each([ - { components: undefined }, - { components: {} }, - { components: { newComponent1: {} } }, - { - components: { newComponent2: {}, newComponent3: {} }, - }, - ])( - 'appends new tex components to current array of tex components', - async ({ components }) => { + describe('adds new tex components to current record of tex components', () => { + it.each([ + { components: undefined }, + { components: {} }, + { components: { newComponent1: {} } }, + { + components: { newComponent2: {}, newComponent3: {} }, + }, + ])('adding $components', async ({ components }) => { const origComponents = { ...ath.configuration.components, }; @@ -197,8 +206,8 @@ suite("AdvancedTexHandler<'local'>", async () => { ...origComponents, ...components, }); - }, - ); + }); + }); }); describe.each([{}])('advancedTexHandler.noteTcInFile', (config) => { @@ -254,12 +263,14 @@ suite("AdvancedTexHandler<'local'>", async () => { fixture(config); it('should work with self-closing components', async () => { expect( - await ath.process('x', { - tag: 'ExampleAlias', - attributes: { ref: 'ref' }, - selfClosing: true, - filename: 'test-73cd8d85.sveltex', - }), + ( + await ath.process('x', { + tag: 'ExampleAlias', + attributes: { ref: 'ref' }, + selfClosing: true, + filename: 'test-73cd8d85.sveltex', + }) + ).processed, ).toEqual( '
\n\n
', ); @@ -269,12 +280,14 @@ suite("AdvancedTexHandler<'local'>", async () => { it('supports aliases', async () => { expect( - await ath.process('x', { - tag: 'ExampleAlias', - attributes: { ref: 'ref' }, - selfClosing: false, - filename: 'test-d9f77b2e.sveltex', - }), + ( + await ath.process('x', { + tag: 'ExampleAlias', + attributes: { ref: 'ref' }, + selfClosing: false, + filename: 'test-d9f77b2e.sveltex', + }) + ).processed, ).toEqual( '
\n\n
', ); @@ -358,16 +371,18 @@ suite("AdvancedTexHandler<'local'>", async () => { it('css variables', async () => { expect( - await ath.process( - '\\textcolor{var(--some-css-variable)}{$x$}', - { - attributes: { ref: 'ath-something-test-348902' }, - selfClosing: false, - tag: 'tex', - filename: - 'css variables (ath-something-test-348902).sveltex', - }, - ), + ( + await ath.process( + '\\textcolor{var(--some-css-variable)}{$x$}', + { + attributes: { ref: 'ath-something-test-348902' }, + selfClosing: false, + tag: 'tex', + filename: + 'css variables (ath-something-test-348902).sveltex', + }, + ) + ).processed, ).toEqual( '
\n\n
', ); @@ -471,12 +486,14 @@ describe.each([ ])("AdvancedTexHandler<'custom'>", (handler) => { it('should work', async () => { expect( - await handler.process('input', { - tag: '', - attributes: { ref: '' }, - selfClosing: false, - filename: 'test.sveltex', - }), + ( + await handler.process('input', { + tag: '', + attributes: { ref: '' }, + selfClosing: false, + filename: 'test.sveltex', + }) + ).processed, ).toEqual('output'); }); }); @@ -494,12 +511,14 @@ describe('AdvancedTexHandler.create', () => { it('edge cases', async () => { const handler = await AdvancedTexHandler.create('local'); expect( - await handler.process('', { - attributes: { ref: 'ref' }, - selfClosing: false, - tag: '', - filename: 'test.sveltex', - }), + ( + await handler.process('', { + attributes: { ref: 'ref' }, + selfClosing: false, + tag: '', + filename: 'test.sveltex', + }) + ).processed, ).toEqual(''); }); @@ -516,12 +535,14 @@ describe('AdvancedTexHandler.create', () => { expect(handler).not.toBeNull(); expect(handler).toBeInstanceOf(AdvancedTexHandler); expect( - handler.process('ath-something-test-908423', { - tag: '', - attributes: { ref: '' }, - selfClosing: false, - filename: 'test.sveltex', - }), + ( + await handler.process('ath-something-test-908423', { + tag: '', + attributes: { ref: '' }, + selfClosing: false, + filename: 'test.sveltex', + }) + ).processed, ).toEqual('ath-something-test-908423'); }); @@ -555,12 +576,14 @@ describe('AdvancedTexHandler.create', () => { expect(handler.processor).toEqual({}); expect(handler.process).toBeTypeOf('function'); expect( - await handler.process('input', { - attributes: { ref: '' }, - selfClosing: false, - tag: '', - filename: 'test.sveltex', - }), + ( + await handler.process('input', { + attributes: { ref: '' }, + selfClosing: false, + tag: '', + filename: 'test.sveltex', + }) + ).processed, ).toEqual('custom process'); // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression expect(await handler.configure({})).toBeUndefined(); diff --git a/tests/handlers/CodeHandler/CodeHandler.escapeOnly.test.ts b/tests/handlers/CodeHandler/CodeHandler.escapeOnly.test.ts index ea0d39f..a641032 100644 --- a/tests/handlers/CodeHandler/CodeHandler.escapeOnly.test.ts +++ b/tests/handlers/CodeHandler/CodeHandler.escapeOnly.test.ts @@ -1,12 +1,15 @@ -import { suite, describe, it, expect, vi } from 'vitest'; +import { describe, it, expect, vi, beforeAll } from 'vitest'; import { CodeHandler } from '$handlers/CodeHandler.js'; import { consoles } from '$utils/debug.js'; vi.spyOn(consoles, 'error').mockImplementation(() => undefined); -suite("CodeHandler<'escapeOnly'>", async () => { - const handler = await CodeHandler.create('escapeOnly'); +describe("CodeHandler<'escapeOnly'>", () => { + let handler: CodeHandler<'escapeOnly'>; + beforeAll(async () => { + handler = await CodeHandler.create('escapeOnly'); + }); describe("CodeHandler.create('escapeOnly')", () => { it('returns instance of CodeHandler', () => { @@ -24,11 +27,13 @@ suite("CodeHandler<'escapeOnly'>", async () => { }); it('escapes `{`, `}`, `<`, and `>` in plain code correctly', async () => { - const output = await handler.process('a {c}', { - lang: 'plaintext', - }); + const output = ( + await handler.process('a {c}', { + lang: 'plaintext', + }) + ).processed; const expected = - '
\na <b> {c}\n
'; + '
a <b> {c}\n
'; expect(output).toEqual(expected); }); }); diff --git a/tests/handlers/CodeHandler/CodeHandler.highlight-js.test.ts b/tests/handlers/CodeHandler/CodeHandler.highlight-js.test.ts index a9ba22e..1ec89d8 100644 --- a/tests/handlers/CodeHandler/CodeHandler.highlight-js.test.ts +++ b/tests/handlers/CodeHandler/CodeHandler.highlight-js.test.ts @@ -1,20 +1,21 @@ /* eslint-disable vitest/no-commented-out-tests */ import { - suite, + afterAll, + afterEach, + beforeAll, + beforeEach, describe, - it, expect, + it, vi, - afterAll, - beforeEach, - afterEach, + type MockInstance, } from 'vitest'; -import { CodeHandler } from '$handlers/CodeHandler.js'; import { getDefaultCodeConfiguration } from '$config/defaults.js'; -import { consoles } from '$utils/debug.js'; -import { isFunction } from '$type-guards/utils.js'; +import { CodeHandler } from '$handlers/CodeHandler.js'; import { spy } from '$tests/fixtures.js'; +import { isFunction } from '$type-guards/utils.js'; +import { consoles } from '$utils/debug.js'; import { v4 as uuid } from 'uuid'; function fixture() { @@ -26,20 +27,30 @@ function fixture() { }); } -suite("CodeHandler<'highlight.js'>", async () => { +describe("CodeHandler<'highlight.js'>", () => { fixture(); const consoleErrorMock = vi .spyOn(consoles, 'error') .mockImplementation(() => undefined); vi.spyOn(consoles, 'warn').mockImplementation(() => undefined); - const { writeFileEnsureDir, existsSync, log } = await spy( - ['writeFileEnsureDir', 'writeFile', 'existsSync', 'log'], - true, - ); + beforeAll(async () => { + const mocks = await spy( + ['writeFileEnsureDir', 'writeFile', 'existsSync', 'log'], + true, + ); + writeFileEnsureDir = mocks.writeFileEnsureDir; + existsSync = mocks.existsSync; + log = mocks.log; + handler = await CodeHandler.create('highlight.js'); + }); + let handler: CodeHandler<'highlight.js'>; + let writeFileEnsureDir: MockInstance; + let existsSync: MockInstance; + let log: MockInstance; + afterAll(() => { vi.restoreAllMocks(); }); - const handler = await CodeHandler.create('highlight.js'); describe("CodeHandler.create('highlight.js')", () => { fixture(); it('returns instance of CodeHandler', () => { @@ -54,7 +65,7 @@ suite("CodeHandler<'highlight.js'>", async () => { it('fetches and generates CSS if run for the first time', async () => { const handler = await CodeHandler.create('highlight.js'); await handler.configure({ theme: { type: 'self-hosted' } }); - await handler.process('', {}); + (await handler.process('', {})).processed; expect(writeFileEnsureDir).toHaveBeenCalledTimes(1); expect(writeFileEnsureDir).toHaveBeenNthCalledWith( 1, @@ -64,7 +75,7 @@ suite("CodeHandler<'highlight.js'>", async () => { expect.stringContaining('pre code'), ); await handler.configure({ theme: { type: 'self-hosted' } }); - await handler.process('', {}); + (await handler.process('', {})).processed; expect(writeFileEnsureDir).toHaveBeenCalledTimes(1); }); @@ -75,7 +86,7 @@ suite("CodeHandler<'highlight.js'>", async () => { }); const handler = await CodeHandler.create('highlight.js'); await handler.configure({ theme: { type: 'self-hosted' } }); - await handler.process('', {}); + (await handler.process('', {})).processed; expect(consoleErrorMock).toHaveBeenCalledTimes(1); expect(consoleErrorMock).toHaveBeenNthCalledWith( 1, @@ -86,7 +97,7 @@ suite("CodeHandler<'highlight.js'>", async () => { it("shouldn't write CSS if configuration.theme.type is none", async () => { const handler = await CodeHandler.create('highlight.js'); await handler.configure({ theme: { type: 'none' } }); - await handler.process('', {}); + (await handler.process('', {})).processed; expect(consoleErrorMock).toHaveBeenCalledTimes(0); expect(writeFileEnsureDir).not.toHaveBeenCalled(); expect(existsSync).not.toHaveBeenCalled(); @@ -97,7 +108,7 @@ suite("CodeHandler<'highlight.js'>", async () => { await handler.configure({ theme: 123 as unknown as { type: 'none' }, }); - await handler.process('', {}); + (await handler.process('', {})).processed; expect(log).toHaveBeenCalledTimes(1); expect(writeFileEnsureDir).not.toHaveBeenCalled(); expect(existsSync).not.toHaveBeenCalled(); @@ -105,11 +116,11 @@ suite("CodeHandler<'highlight.js'>", async () => { it("should work even if version can't be fetched", async () => { const getVersionMock = vi - .spyOn(await import('$utils/cdn.js'), 'getVersion') + .spyOn(await import('$utils/env.js'), 'getVersion') .mockResolvedValueOnce(undefined); const handler = await CodeHandler.create('highlight.js'); await handler.configure({ theme: { type: 'self-hosted' } }); - await handler.process('', {}); + (await handler.process('', {})).processed; expect(writeFileEnsureDir).toHaveBeenCalledTimes(1); expect(writeFileEnsureDir).toHaveBeenNthCalledWith( 1, @@ -127,7 +138,7 @@ suite("CodeHandler<'highlight.js'>", async () => { .mockResolvedValueOnce(undefined); const handler = await CodeHandler.create('highlight.js'); await handler.configure({ theme: { type: 'self-hosted' } }); - await handler.process('', {}); + (await handler.process('', {})).processed; expect(writeFileEnsureDir).toHaveBeenCalledTimes(0); fetchCssMock.mockRestore(); }); @@ -139,7 +150,7 @@ suite("CodeHandler<'highlight.js'>", async () => { existsSync.mockReturnValueOnce(true); const handler = await CodeHandler.create('highlight.js'); await handler.configure({ theme: { type: 'self-hosted' } }); - await handler.process('', {}); + (await handler.process('', {})).processed; expect(writeFileEnsureDir).toHaveBeenCalledTimes(0); fetchCssMock.mockRestore(); }); @@ -155,69 +166,33 @@ suite("CodeHandler<'highlight.js'>", async () => { }); it('processes simple JS code correctly', async () => { - const output = await handler.process('let a', { lang: 'js' }); + const output = (await handler.process('let a', { lang: 'js' })) + .processed; const expected = - '
\nlet a\n
'; + '
let a\n
'; expect(output).toEqual(expected); }); - it('parses info from delimiters if present', async () => { - const codeList = [ - 'something', - 'let a', - '/**\n * @remarks comment\n */\nconst a = new Map();', - ]; - - for (const code of codeList) { - await (async () => { - expect( - await handler.process('```\n' + code + '\n```'), - ).toEqual( - expect.stringMatching( - /^
\n[\w\W]*\n<\/code><\/pre>$/,
-                            ),
-                        );
-                        expect(await handler.process('`' + code + '`')).toEqual(
-                            expect.stringMatching(
-                                /^[\w\W]*<\/code>$/,
-                            ),
-                        );
-                        expect(
-                            await handler.process('```js\n' + code + '\n```'),
-                        ).toEqual(
-                            expect.stringMatching(
-                                /^
\n[\w\W]*\n<\/code><\/pre>$/,
-                            ),
-                        );
-                        expect(
-                            await handler.process(
-                                '```javascript\n' + code + '\n```',
-                            ),
-                        ).toEqual(
-                            expect.stringMatching(
-                                /^
\n[\w\W]*\n<\/code><\/pre>$/,
-                            ),
-                        );
-                    })();
-                }
-            });
-
             it('escapes `{`, `}`, `<`, and `>` in plain code correctly', async () => {
-                const output = await handler.process('a  {c}', {
-                    lang: 'plaintext',
-                });
+                const output = (
+                    await handler.process('a  {c}', {
+                        lang: 'plaintext',
+                    })
+                ).processed;
                 const expected =
-                    '
\na <b> {c}\n
'; + '
a <b> {c}\n
'; expect(output).toEqual(expected); }); it('escapes `{`, `}`, `<`, and `>` in JS code correctly', async () => { - const output = await handler.process( - 'const a = new Map();', - { lang: 'js' }, - ); + const output = ( + await handler.process( + 'const a = new Map();', + { lang: 'js' }, + ) + ).processed; const expected = - '
\nconst a = new Map<string, {prop: number}>();\n
'; + '
const a = new Map<string, {prop: number}>();\n
'; expect(output).toEqual(expected); }); }); @@ -231,22 +206,26 @@ suite("CodeHandler<'highlight.js'>", async () => { it('configures code correctly', async () => { await handler.configure({ classPrefix: 'test_' }); - expect(await handler.process('let a', { lang: 'js' })).toEqual( - '
\nlet a\n
', + expect( + (await handler.process('let a', { lang: 'js' })).processed, + ).toEqual( + '
let a\n
', ); await handler.configure({ classPrefix: 'hljs-' }); - expect(await handler.process('let a', { lang: 'js' })).toEqual( - '
\nlet a\n
', + expect( + (await handler.process('let a', { lang: 'js' })).processed, + ).toEqual( + '
let a\n
', ); }); // it('can add new languages', () => { // handler.configure({ languages: [] }); - // expect(await handler.process('let a', { lang: 'js' })).toEqual( + // expect((await handler.process('let a', { lang: 'js' })).processed).toEqual( // 'let a', // ); // handler.configure({ languages: ['js'] }); - // expect(await handler.process('let a', { lang: 'js' })).toEqual( + // expect((await handler.process('let a', { lang: 'js' })).processed).toEqual( // 'let a', // ); // }); @@ -273,21 +252,23 @@ suite("CodeHandler<'highlight.js'>", async () => { ).toEqual( Object.entries(defaultCC).filter(([, v]) => !isFunction(v)), ); - expect( - handler.configuration.wrap({ wrapClassPrefix: 'test-' }), - ).toEqual( - defaultCC.wrap({ - wrapClassPrefix: 'test-', - }), + const defaultOptions = { + lang: 'plaintext', + _wrap: true, + inline: false, + wrapClassPrefix: 'test-', + }; + expect(handler.configuration.wrap(defaultOptions)).toEqual( + defaultCC.wrap(defaultOptions), ); expect( handler.configuration.wrap({ - wrapClassPrefix: 'test-', + ...defaultOptions, inline: true, }), ).toEqual( defaultCC.wrap({ - wrapClassPrefix: 'test-', + ...defaultOptions, inline: true, }), ); diff --git a/tests/handlers/CodeHandler/CodeHandler.none.test.ts b/tests/handlers/CodeHandler/CodeHandler.none.test.ts index e6089ba..0e9994c 100644 --- a/tests/handlers/CodeHandler/CodeHandler.none.test.ts +++ b/tests/handlers/CodeHandler/CodeHandler.none.test.ts @@ -1,21 +1,14 @@ -import { - suite, - describe, - it, - expect, - afterAll, - beforeEach, - vi, - type MockInstance, -} from 'vitest'; +import { describe, it, expect, beforeAll } from 'vitest'; import { CodeHandler } from '$handlers/CodeHandler.js'; -import { consoles } from '$utils/debug.js'; import { mockFs } from '$dev_deps.js'; mockFs({}); -suite("CodeHandler<'none'>", async () => { - const handler = await CodeHandler.create('none'); +describe("CodeHandler<'none'>", () => { + let handler: CodeHandler<'none'>; + beforeAll(async () => { + handler = await CodeHandler.create('none'); + }); describe("CodeHandler.create('none')", () => { it('returns instance of CodeHandler', () => { @@ -33,10 +26,12 @@ suite("CodeHandler<'none'>", async () => { }); it("doesn't escape anything", async () => { - const output = await handler.process('a {c}', { - lang: 'plaintext', - }); - const expected = '\na {c}\n'; + const output = ( + await handler.process('a {c}', { + lang: 'plaintext', + }) + ).processed; + const expected = 'a {c}\n'; expect(output).toEqual(expected); }); }); @@ -61,8 +56,15 @@ suite("CodeHandler<'none'>", async () => { describe('configuration getter', () => { it('has correct defaults', async () => { + const defaultOptions = { + lang: 'plaintext', + _wrap: true, + inline: false, + wrapClassPrefix: 'test-', + }; expect( handler.configuration.wrap({ + ...defaultOptions, wrapClassPrefix: 'test-', lang: 'js', }), @@ -73,114 +75,13 @@ suite("CodeHandler<'none'>", async () => { }); expect( handler.configuration.wrap({ + ...defaultOptions, wrapClassPrefix: 'test-', lang: 'js', }), ).toEqual(['', '']); }); }); - - describe('consumeDelims()', () => { - let consoleErrorMock: MockInstance; - - beforeEach(() => { - consoleErrorMock = vi - .spyOn(consoles, 'error') - .mockImplementation(() => undefined); - }); - afterAll(() => { - consoleErrorMock.mockReset(); - }); - it('should work if no options are set with the delims', () => { - const { innerCode, optionsFromDelims } = - handler.consumeDelims('```\nabc\n```'); - expect(innerCode).toEqual('abc'); - expect(optionsFromDelims).toEqual({ - info: '', - inline: false, - lang: 'plaintext', - }); - }); - - it('should work if language is set with the delims', () => { - const { innerCode, optionsFromDelims } = handler.consumeDelims( - '```language\nabc\n```', - ); - expect(innerCode).toEqual('abc'); - expect(optionsFromDelims).toEqual({ - info: '', - inline: false, - lang: 'language', - }); - }); - - it('should work if language and options are set with the delims', () => { - const { innerCode, optionsFromDelims } = handler.consumeDelims( - '```some text here\nabc\n```', - ); - expect(innerCode).toEqual('abc'); - expect(optionsFromDelims).toEqual({ - info: 'text here', - inline: false, - lang: 'some', - }); - }); - - it.each([ - { input: '```\n\nabc\n\n```', expected: '\nabc\n' }, - { input: '`\n\nabc\n\n`', expected: '\n\nabc\n\n' }, - ])('should respect whitespace', (test) => { - expect(handler.consumeDelims(test.input).innerCode).toEqual( - test.expected, - ); - }); - - it.each([ - { - input: '```some text here\nabc\n~~~', - expectedError: - 'Error parsing code block (closing delimiters not found); expected the following to end with ≥3 backticks:\n```some text here\nabc\n~~~', - opts: undefined, - }, - { - input: '~~~~language\nabc', - expectedError: - 'Error parsing code block (closing delimiters not found); expected the following to end with ≥4 tildes:\n~~~~language\nabc', - opts: undefined, - }, - { - input: '`some text here\nabc', - expectedError: - 'Error parsing inline code snippet (closing delimiters not found); expected the following to end with ≥1 backticks:\n`some text here\nabc', - opts: undefined, // { inline: true, lang: 'plaintext', info: '' }, - }, - { - input: '~~some text here\nabc', - expectedError: - 'Error parsing inline code snippet (closing delimiters not found); expected the following to end with ≥2 tildes:\n~~some text here\nabc', - opts: undefined, // { inline: true, lang: 'plaintext', info: '' }, - }, - { - input: 'text', - expectedError: - 'Error parsing code snippet (no delimiters could be found/matched): text', - opts: undefined, // { inline: true, lang: 'plaintext', info: '' }, - }, - ])( - 'should log error if delims are mismatched', - ({ input, expectedError, opts }) => { - const { innerCode, optionsFromDelims } = - handler.consumeDelims(input); - expect(innerCode).toEqual(input); - expect(optionsFromDelims).toEqual(opts); - expect(consoleErrorMock).toHaveBeenCalledTimes(1); - expect(consoleErrorMock).toHaveBeenNthCalledWith( - 1, - expect.stringContaining(expectedError), - ); - }, - ); - }); }); describe('backend', () => { diff --git a/tests/handlers/CodeHandler/CodeHandler.prismjs.test.ts b/tests/handlers/CodeHandler/CodeHandler.prismjs.test.ts index fb40a42..d05f62c 100644 --- a/tests/handlers/CodeHandler/CodeHandler.prismjs.test.ts +++ b/tests/handlers/CodeHandler/CodeHandler.prismjs.test.ts @@ -1,5 +1,5 @@ /* eslint-disable vitest/no-commented-out-tests */ -import { suite, describe, it, expect, vi } from 'vitest'; +import { describe, it, expect, vi, beforeAll } from 'vitest'; import { CodeHandler } from '$handlers/CodeHandler.js'; import { getDefaultCodeConfiguration } from '$config/defaults.js'; @@ -8,8 +8,11 @@ import { consoles } from '$utils/debug.js'; vi.spyOn(consoles, 'error').mockImplementation(() => undefined); vi.spyOn(consoles, 'warn').mockImplementation(() => undefined); -suite("CodeHandler<'prismjs'>", async () => { - const handler = await CodeHandler.create('prismjs'); +describe("CodeHandler<'prismjs'>", () => { + let handler: CodeHandler<'prismjs'>; + beforeAll(async () => { + handler = await CodeHandler.create('prismjs'); + }); describe("CodeHandler.create('prismjs')", () => { it('returns instance of CodeHandler', () => { @@ -27,78 +30,51 @@ suite("CodeHandler<'prismjs'>", async () => { }); it('processes simple JS code correctly', async () => { - const output = await handler.process('let a', { lang: 'js' }); + const output = (await handler.process('let a', { lang: 'js' })) + .processed; const expected = - '
\nlet a\n
'; + '
let a\n
'; expect(output).toEqual(expected); }); - it.each(['unknown-language', undefined])( - 'processes code of unknown language correctly', - async (lang) => { - expect( + it('processes code of unknown language correctly', async () => { + expect( + ( await handler.process('let a = b', { - lang, - }), - ).toEqual( - `
\nlet a = b\n
`, - ); - }, - ); - - it.each([ - 'something', - 'let a', - '/**\n * @remarks comment\n */\nconst a = new Map();', - ])('parses info from delimiters if present', async (code) => { - await (async () => { - expect( - await handler.process('```\n' + code + '\n```'), - ).toEqual( - expect.stringMatching( - /^
\n[\w\W]*\n<\/code><\/pre>$/,
-                        ),
-                    );
-                    expect(await handler.process('`' + code + '`')).toEqual(
-                        expect.stringMatching(
-                            /^[\w\W]*<\/code>$/,
-                        ),
-                    );
-                    expect(
-                        await handler.process('```js\n' + code + '\n```'),
-                    ).toEqual(
-                        expect.stringMatching(
-                            /^
\n[\w\W]*\n<\/code><\/pre>$/,
-                        ),
-                    );
-                    expect(
-                        await handler.process(
-                            '```javascript\n' + code + '\n```',
-                        ),
-                    ).toEqual(
-                        expect.stringMatching(
-                            /^
\n[\w\W]*\n<\/code><\/pre>$/,
-                        ),
-                    );
-                })();
+                            lang: 'unknown-language',
+                        })
+                    ).processed,
+                ).toEqual(
+                    `
let a = b\n
`, + ); + }); + + it('defaults to no language', async () => { + const output = (await handler.process('let a')).processed; + const expected = '
let a\n
'; + expect(output).toEqual(expected); }); it('escapes `{`, `}`, `<`, and `>` in plain code correctly', async () => { - const output = await handler.process('a {c}', { - lang: 'plaintext', - }); + const output = ( + await handler.process('a {c}', { + lang: 'plaintext', + }) + ).processed; const expected = - '
\na <b> {c}\n
'; + '
a <b> {c}\n
'; expect(output).toEqual(expected); }); it('escapes `{`, `}`, `<`, and `>` in JS code correctly', async () => { - const output = await handler.process( - 'const a = new Map();', - { lang: 'js' }, - ); + const output = ( + await handler.process( + 'const a = new Map();', + { lang: 'js' }, + ) + ).processed; const expected = - '
\nconst a = new Map<string, {prop: number}>();\n
'; + '
const a = new Map<string, {prop: number}>();\n
'; expect(output).toEqual(expected); }); }); @@ -115,19 +91,19 @@ suite("CodeHandler<'prismjs'>", async () => { // it('configures code correctly', () => { // expect( - // await handler.process('\\documentclass{article}\n$math$', { + // (await handler.process('\\documentclass{article}\n$math$', { // lang: 'latex', - // }), + // })).processed, // ).toEqual('\\example'); // }); // it('can add new languages', () => { // handler.configure({ languages: [] }); - // expect(await handler.process('let a', { lang: 'js' })).toEqual( + // expect((await handler.process('let a', { lang: 'js' })).processed).toEqual( // 'let a', // ); // handler.configure({ languages: ['js'] }); - // expect(await handler.process('let a', { lang: 'js' })).toEqual( + // expect((await handler.process('let a', { lang: 'js' })).processed).toEqual( // 'let a', // ); // }); diff --git a/tests/handlers/CodeHandler/CodeHandler.starry-night.test.ts b/tests/handlers/CodeHandler/CodeHandler.starry-night.test.ts index 7f819fb..40b3696 100644 --- a/tests/handlers/CodeHandler/CodeHandler.starry-night.test.ts +++ b/tests/handlers/CodeHandler/CodeHandler.starry-night.test.ts @@ -1,6 +1,4 @@ -/* eslint-disable vitest/no-commented-out-tests */ import { - suite, describe, it, expect, @@ -8,6 +6,8 @@ import { beforeEach, afterEach, afterAll, + beforeAll, + type MockInstance, } from 'vitest'; import { CodeHandler } from '$handlers/CodeHandler.js'; @@ -25,19 +25,22 @@ function fixture() { }); } -suite.concurrent("CodeHandler<'starry-night'>", async () => { +describe.concurrent("CodeHandler<'starry-night'>", () => { fixture(); - const { writeFileEnsureDir } = await spy( - ['writeFileEnsureDir', 'existsSync'], - true, - ); + + let handler: CodeHandler<'starry-night'>; + let writeFileEnsureDir: MockInstance; + + beforeAll(async () => { + handler = await CodeHandler.create('starry-night'); + await handler.configure({ languages: 'common' }); + const mocks = await spy(['writeFileEnsureDir', 'existsSync'], true); + writeFileEnsureDir = mocks.writeFileEnsureDir; + }); afterAll(() => { vi.restoreAllMocks(); }); - const handler = await CodeHandler.create('starry-night'); - await handler.configure({ languages: 'common' }); - describe.concurrent("CodeHandler.create('starry-night')", () => { fixture(); it('returns instance of CodeHandler', () => { @@ -52,14 +55,14 @@ suite.concurrent("CodeHandler<'starry-night'>", async () => { it('fetches and generates CSS if run for the first time', async () => { const handler = await CodeHandler.create('starry-night'); await handler.configure({ theme: { type: 'self-hosted' } }); - await handler.process(''); + (await handler.process('')).processed; expect(writeFileEnsureDir).toHaveBeenCalledTimes(1); expect(writeFileEnsureDir).toHaveBeenNthCalledWith( 1, expect.stringMatching(/src\/sveltex\/starry-night@.*\.css/), expect.stringContaining(':root'), ); - await handler.process(''); + (await handler.process('')).processed; expect(writeFileEnsureDir).toHaveBeenCalledTimes(1); }); }); @@ -74,77 +77,50 @@ suite.concurrent("CodeHandler<'starry-night'>", async () => { }); it('processes simple JS code correctly', async () => { - const output = await handler.process('let a', { lang: 'js' }); + const output = (await handler.process('let a', { lang: 'js' })) + .processed; const expected = - '
\nlet a\n
'; + '
let a\n
'; expect(output).toEqual(expected); }); - it('processes plaintext correctly', async () => { - const output = await handler.process('let a', { - lang: undefined, - }); - const expected = '
\nlet a\n
'; + it('defaults to no language', async () => { + const output = (await handler.process('let a')).processed; + const expected = '
let a\n
'; expect(output).toEqual(expected); }); - it('parses info from delimiters if present', async () => { - const codeList = [ - 'something', - 'let a', - '/**\n * @remarks comment\n */\nconst a = new Map();', - ]; - - for (const code of codeList) { - await (async () => { - expect( - await handler.process('```\n' + code + '\n```'), - ).toEqual( - expect.stringMatching( - /^
\n[\w\W]*\n<\/code><\/pre>$/,
-                            ),
-                        );
-                        expect(await handler.process('`' + code + '`')).toEqual(
-                            expect.stringMatching(
-                                /^[\w\W]*<\/code>$/,
-                            ),
-                        );
-                        expect(
-                            await handler.process('```js\n' + code + '\n```'),
-                        ).toEqual(
-                            expect.stringMatching(
-                                /^
\n[\w\W]*\n<\/code><\/pre>$/,
-                            ),
-                        );
-                        expect(
-                            await handler.process(
-                                '```javascript\n' + code + '\n```',
-                            ),
-                        ).toEqual(
-                            expect.stringMatching(
-                                /^
\n[\w\W]*\n<\/code><\/pre>$/,
-                            ),
-                        );
-                    })();
-                }
+            it('processes plaintext correctly', async () => {
+                const output = (
+                    await handler.process('let a', {
+                        lang: 'plaintext',
+                    })
+                ).processed;
+                const expected =
+                    '
let a\n
'; + expect(output).toEqual(expected); }); it('escapes `{`, `}`, `<`, and `>` in plain code correctly', async () => { - const output = await handler.process('a {c}', { - lang: 'plaintext', - }); + const output = ( + await handler.process('a {c}', { + lang: 'plaintext', + }) + ).processed; const expected = - '
\na <b> {c}\n
'; + '
a <b> {c}\n
'; expect(output).toEqual(expected); }); it('escapes `{`, `}`, `<`, and `>` in JS code correctly', async () => { - const output = await handler.process( - 'const a = new Map();', - { lang: 'js' }, - ); + const output = ( + await handler.process( + 'const a = new Map();', + { lang: 'js' }, + ) + ).processed; const expected = - '
\nconst a = new Map<string, {prop: number}>();\n
'; + '
const a = new Map<string, {prop: number}>();\n
'; expect(output).toEqual(expected); }); }); @@ -170,36 +146,41 @@ suite.concurrent("CodeHandler<'starry-night'>", async () => { it('configures code correctly', async () => { expect( - await handler.process('\\example', { lang: 'tex' }), + (await handler.process('\\example', { lang: 'tex' })) + .processed, ).toEqual( - '
\n\\example\n
', + '
\\example\n
', ); await handler.configure({ languages: ['text.tex.latex'], }); expect( - await handler.process('\\example', { lang: 'tex' }), + (await handler.process('\\example', { lang: 'tex' })) + .processed, ).toEqual( - '
\n\\example\n
', + '
\\example\n
', ); expect( - await handler.process('a ::= b', { lang: 'ebnf' }), + (await handler.process('a ::= b', { lang: 'ebnf' })) + .processed, ).toEqual( - '
\na ::= b\n
', + '
a ::= b\n
', ); await handler.configure({ languages: 'all' }); expect( - await handler.process('a ::= b', { lang: 'ebnf' }), + (await handler.process('a ::= b', { lang: 'ebnf' })) + .processed, ).toEqual( - '
\na ::= b\n
', + '
a ::= b\n
', ); // can't unload languages though... await handler.configure({ languages: [] }); expect( - await handler.process('a ::= b', { lang: 'ebnf' }), + (await handler.process('a ::= b', { lang: 'ebnf' })) + .processed, ).toEqual( - '
\na ::= b\n
', + '
a ::= b\n
', ); expect(consoleWarnMock).toHaveBeenCalledTimes(2); @@ -216,9 +197,10 @@ suite.concurrent("CodeHandler<'starry-night'>", async () => { it('can add new languages', async () => { const handler = await CodeHandler.create('starry-night'); expect( - await handler.process('a ::= b', { lang: 'ebnf' }), + (await handler.process('a ::= b', { lang: 'ebnf' })) + .processed, ).toEqual( - '
\na ::= b\n
', + '
a ::= b\n
', ); expect(consoleWarnMock).toHaveBeenCalledTimes(1); expect(consoleWarnMock).toHaveBeenNthCalledWith( @@ -227,20 +209,23 @@ suite.concurrent("CodeHandler<'starry-night'>", async () => { ); await handler.configure({ languages: ['source.ebnf'] }); expect( - await handler.process('a ::= b', { lang: 'ebnf' }), + (await handler.process('a ::= b', { lang: 'ebnf' })) + .processed, ).toEqual( - '
\na ::= b\n
', + '
a ::= b\n
', ); }); it('can add new custom languages', async () => { const handler = await CodeHandler.create('starry-night'); expect( - await handler.process('something a', { - lang: 'custom-language', - }), + ( + await handler.process('something a', { + lang: 'custom-language', + }) + ).processed, ).toEqual( - '
\nsomething a\n
', + '
something a\n
', ); expect(consoleWarnMock).toHaveBeenCalledTimes(1); expect(consoleWarnMock).toHaveBeenNthCalledWith( @@ -265,11 +250,13 @@ suite.concurrent("CodeHandler<'starry-night'>", async () => { ], }); expect( - await handler.process('something a', { - lang: 'custom-language', - }), + ( + await handler.process('something a', { + lang: 'custom-language', + }) + ).processed, ).toEqual( - '
\nsomething a\n
', + '
something a\n
', ); }); }); @@ -295,20 +282,32 @@ suite.concurrent("CodeHandler<'starry-night'>", async () => { ).toEqual( Object.entries(defaultCC).filter(([, v]) => !isFunction(v)), ); + const defaultOptions = { + lang: 'plaintext', + _wrap: true, + inline: false, + wrapClassPrefix: 'test-', + }; expect( - handler.configuration.wrap({ wrapClassPrefix: 'test-' }), + handler.configuration.wrap({ + ...defaultOptions, + wrapClassPrefix: 'test-', + }), ).toEqual( defaultCC.wrap({ + ...defaultOptions, wrapClassPrefix: 'test-', }), ); expect( handler.configuration.wrap({ + ...defaultOptions, wrapClassPrefix: 'test-', inline: true, }), ).toEqual( defaultCC.wrap({ + ...defaultOptions, wrapClassPrefix: 'test-', inline: true, }), diff --git a/tests/handlers/CodeHandler/CodeHandler.test.ts b/tests/handlers/CodeHandler/CodeHandler.test.ts index e133d2f..836d6d7 100644 --- a/tests/handlers/CodeHandler/CodeHandler.test.ts +++ b/tests/handlers/CodeHandler/CodeHandler.test.ts @@ -1,14 +1,17 @@ -import { suite, describe, it, expect, vi } from 'vitest'; +import { describe, it, expect, vi, beforeAll } from 'vitest'; import { CodeHandler } from '$handlers/CodeHandler.js'; -import { missingDeps } from '$utils/globals.js'; +import { missingDeps } from '$utils/env.js'; import { getDefaultCodeConfiguration } from '$config/defaults.js'; import { consoles } from '$utils/debug.js'; vi.spyOn(consoles, 'error').mockImplementation(() => undefined); -suite("CodeHandler<'custom'>", async () => { - const handler = await CodeHandler.create('custom', { - process: (input: string) => input, +describe("CodeHandler<'custom'>", () => { + let handler: CodeHandler<'custom'>; + beforeAll(async () => { + handler = await CodeHandler.create('custom', { + process: (input: string) => input, + }); }); describe("CodeHandler.create('custom', ...)", () => { it('returns instance of CodeHandler', () => { @@ -38,10 +41,10 @@ suite("CodeHandler<'custom'>", async () => { describe('codeHandler', () => { describe('process()', () => { - it('should work', async () => { - expect(await handler.process('x', {})).toEqual( - '
\nx\n
', - ); + it('defaults to no language', async () => { + const output = (await handler.process('let a')).processed; + const expected = '
let a\n
'; + expect(output).toEqual(expected); }); }); @@ -62,7 +65,7 @@ suite("CodeHandler<'custom'>", async () => { }); }); -suite('CodeHandler error handling', () => { +describe('CodeHandler error handling', () => { describe("CodeHandler.create('highlight.js') with highlight.js mocked to throw error", () => { vi.mock('highlight.js', () => { throw new Error('highlight.js not found'); diff --git a/tests/handlers/MarkdownHandler/MarkdownHandler.custom.errors.test.ts b/tests/handlers/MarkdownHandler/MarkdownHandler.custom.errors.test.ts index 137966f..598ab00 100644 --- a/tests/handlers/MarkdownHandler/MarkdownHandler.custom.errors.test.ts +++ b/tests/handlers/MarkdownHandler/MarkdownHandler.custom.errors.test.ts @@ -1,10 +1,10 @@ -import { suite, describe, it, expect } from 'vitest'; +import { describe, it, expect } from 'vitest'; import { MarkdownHandler } from '$handlers/MarkdownHandler.js'; import { Marked, type MarkedOptions, type MarkedExtension } from 'marked'; -suite("MarkdownHandler<'custom'>", () => { +describe("MarkdownHandler<'custom'>", () => { const customProcessor = new Marked(); interface Configuration { options?: MarkedOptions; diff --git a/tests/handlers/MarkdownHandler/MarkdownHandler.custom.test.ts b/tests/handlers/MarkdownHandler/MarkdownHandler.custom.test.ts index ddc5f63..fc6b0e9 100644 --- a/tests/handlers/MarkdownHandler/MarkdownHandler.custom.test.ts +++ b/tests/handlers/MarkdownHandler/MarkdownHandler.custom.test.ts @@ -1,45 +1,50 @@ -import { suite, describe, it, expect } from 'vitest'; +import { describe, it, expect, beforeAll } from 'vitest'; import { MarkdownHandler } from '$handlers/MarkdownHandler.js'; import { Marked, type MarkedOptions, type MarkedExtension } from 'marked'; -suite("MarkdownHandler<'custom'>", async () => { - const customProcessor = new Marked(); - interface Configuration { - options?: MarkedOptions; - extensions?: MarkedExtension[]; - } - const customConfigure = ( - opts: unknown, - markdownHandler: MarkdownHandler<'custom'>, - ) => { - if ((opts as Configuration).options) { - (markdownHandler.processor as Marked).setOptions( - (opts as Configuration).options ?? {}, - ); +describe("MarkdownHandler<'custom'>", () => { + let handler: MarkdownHandler<'custom'>; + beforeAll(async () => { + const customProcessor = new Marked(); + interface Configuration { + options?: MarkedOptions; + extensions?: MarkedExtension[]; } - if ((opts as Configuration).extensions) { - (markdownHandler.processor as Marked).use( - ...((opts as Configuration).extensions ?? []), - ); - } - }; - const customProcess = async ( - markdown: string, - { inline }: { inline?: boolean | undefined } | undefined = { - inline: !MarkdownHandler.shouldParseAsInline(markdown), - }, - markdownHandler: MarkdownHandler<'custom'>, - ) => { - return inline - ? await (markdownHandler.processor as Marked).parseInline(markdown) - : await (markdownHandler.processor as Marked).parse(markdown); - }; - const handler = await MarkdownHandler.create('custom', { - processor: customProcessor, - process: customProcess, - configure: customConfigure, + const customConfigure = ( + opts: unknown, + markdownHandler: MarkdownHandler<'custom'>, + ) => { + if ((opts as Configuration).options) { + (markdownHandler.processor as Marked).setOptions( + (opts as Configuration).options ?? {}, + ); + } + if ((opts as Configuration).extensions) { + (markdownHandler.processor as Marked).use( + ...((opts as Configuration).extensions ?? []), + ); + } + }; + const customProcess = async ( + markdown: string, + { inline }: { inline?: boolean | undefined } | undefined = { + inline: !MarkdownHandler.shouldParseAsInline(markdown), + }, + markdownHandler: MarkdownHandler<'custom'>, + ) => { + return inline + ? await (markdownHandler.processor as Marked).parseInline( + markdown, + ) + : await (markdownHandler.processor as Marked).parse(markdown); + }; + handler = await MarkdownHandler.create('custom', { + processor: customProcessor, + process: customProcess, + configure: customConfigure, + }); }); describe("MarkdownHandler.create('custom')", () => { @@ -58,25 +63,28 @@ suite("MarkdownHandler<'custom'>", async () => { }); it('processes markdown correctly', async () => { - const output = await handler.process('**strong** *em*'); + const output = (await handler.process('**strong** *em*')) + .processed; const expected = 'strong em'; expect(output).toEqual(expected); }); it("automatically distinguishes between inline and 'block' markdown", async () => { - expect(await handler.process('a\nb')).toEqual('a\nb'); - expect(await handler.process('a\n\nb')).toEqual( + expect((await handler.process('a\nb')).processed).toEqual( + 'a\nb', + ); + expect((await handler.process('a\n\nb')).processed).toEqual( '

a

\n

b

\n', ); }); it('has working `inline` parameter', async () => { - expect(await handler.process('a', { inline: true })).toEqual( - 'a', - ); - expect(await handler.process('a', { inline: false })).toEqual( - '

a

\n', - ); + expect( + (await handler.process('a', { inline: true })).processed, + ).toEqual('a'); + expect( + (await handler.process('a', { inline: false })).processed, + ).toEqual('

a

\n'); }); }); @@ -90,11 +98,15 @@ suite("MarkdownHandler<'custom'>", async () => { await handler.configure({ options: { gfm: true, breaks: true }, }); - expect(await handler.process('a\nb')).toEqual('a
b'); + expect((await handler.process('a\nb')).processed).toEqual( + 'a
b', + ); await handler.configure({ options: { gfm: false, breaks: false }, }); - expect(await handler.process('a\nb')).toEqual('a\nb'); + expect((await handler.process('a\nb')).processed).toEqual( + 'a\nb', + ); expect(handler.configuration).toEqual({ options: { gfm: false, breaks: false }, }); diff --git a/tests/handlers/MarkdownHandler/MarkdownHandler.markdown-it.test.ts b/tests/handlers/MarkdownHandler/MarkdownHandler.markdown-it.test.ts index f2f235f..942a101 100644 --- a/tests/handlers/MarkdownHandler/MarkdownHandler.markdown-it.test.ts +++ b/tests/handlers/MarkdownHandler/MarkdownHandler.markdown-it.test.ts @@ -1,11 +1,14 @@ -import { suite, describe, it, expect } from 'vitest'; +import { describe, it, expect, beforeAll } from 'vitest'; import multimdTablePlugin from 'markdown-it-multimd-table'; import { MarkdownHandler } from '$handlers/MarkdownHandler.js'; import MarkdownIt from 'markdown-it'; -suite("MarkdownHandler<'markdown-it'>", async () => { - const handler = await MarkdownHandler.create('markdown-it'); +describe("MarkdownHandler<'markdown-it'>", () => { + let handler: MarkdownHandler<'markdown-it'>; + beforeAll(async () => { + handler = await MarkdownHandler.create('markdown-it'); + }); describe("MarkdownHandler.create('markdown-it')", () => { it('returns instance of MarkdownHandler', () => { @@ -23,25 +26,28 @@ suite("MarkdownHandler<'markdown-it'>", async () => { }); it('processes markdown correctly', async () => { - const output = await handler.process('**strong** *em*'); + const output = (await handler.process('**strong** *em*')) + .processed; const expected = 'strong em'; expect(output).toEqual(expected); }); it("automatically distinguishes between inline and 'block' markdown", async () => { - expect(await handler.process('a\nb')).toEqual('a\nb'); - expect(await handler.process('a\n\nb')).toEqual( + expect((await handler.process('a\nb')).processed).toEqual( + 'a\nb', + ); + expect((await handler.process('a\n\nb')).processed).toEqual( '

a

\n

b

\n', ); }); it('has working `inline` parameter', async () => { - expect(await handler.process('a', { inline: true })).toEqual( - 'a', - ); - expect(await handler.process('a', { inline: false })).toEqual( - '

a

\n', - ); + expect( + (await handler.process('a', { inline: true })).processed, + ).toEqual('a'); + expect( + (await handler.process('a', { inline: false })).processed, + ).toEqual('

a

\n'); }); }); @@ -57,13 +63,17 @@ suite("MarkdownHandler<'markdown-it'>", async () => { }); expect(handler.processor.options.breaks).toBeTruthy(); expect(handler.processor.options.xhtmlOut).toBeTruthy(); - expect(handler.process('a\nb')).toEqual('a
\nb'); + expect((await handler.process('a\nb')).processed).toEqual( + 'a
\nb', + ); await handler.configure({ options: { breaks: true, xhtmlOut: false }, }); expect(handler.processor.options.breaks).toBeTruthy(); expect(handler.processor.options.xhtmlOut).toBeFalsy(); - expect(handler.process('a\nb')).toEqual('a
\nb'); + expect((await handler.process('a\nb')).processed).toEqual( + 'a
\nb', + ); expect(handler.configuration).toEqual({ options: { breaks: true, @@ -137,7 +147,9 @@ suite("MarkdownHandler<'markdown-it'>", async () => { '\n' + '\n' + '\n'; - expect(await handler.process(exampleTable)).toEqual(expected); + expect((await handler.process(exampleTable)).processed).toEqual( + expected, + ); }); }); diff --git a/tests/handlers/MarkdownHandler/MarkdownHandler.marked.test.ts b/tests/handlers/MarkdownHandler/MarkdownHandler.marked.test.ts index dd8843a..9c5baad 100644 --- a/tests/handlers/MarkdownHandler/MarkdownHandler.marked.test.ts +++ b/tests/handlers/MarkdownHandler/MarkdownHandler.marked.test.ts @@ -1,10 +1,13 @@ -import { suite, describe, it, expect } from 'vitest'; +import { describe, it, expect, beforeAll } from 'vitest'; import { MarkdownHandler } from '$handlers/MarkdownHandler.js'; import { Marked } from 'marked'; -suite("MarkdownHandler<'marked'>", async () => { - const handler = await MarkdownHandler.create('marked'); +describe("MarkdownHandler<'marked'>", () => { + let handler: MarkdownHandler<'marked'>; + beforeAll(async () => { + handler = await MarkdownHandler.create('marked'); + }); describe("MarkdownHandler.create('marked')", () => { it('returns instance of MarkdownHandler', () => { @@ -22,25 +25,28 @@ suite("MarkdownHandler<'marked'>", async () => { }); it('processes markdown correctly', async () => { - const output = await handler.process('**strong** *em*'); + const output = (await handler.process('**strong** *em*')) + .processed; const expected = 'strong em'; expect(output).toEqual(expected); }); it("automatically distinguishes between inline and 'block' markdown", async () => { - expect(await handler.process('a\nb')).toEqual('a\nb'); - expect(await handler.process('a\n\nb')).toEqual( + expect((await handler.process('a\nb')).processed).toEqual( + 'a\nb', + ); + expect((await handler.process('a\n\nb')).processed).toEqual( '

a

\n

b

\n', ); }); it('has working `inline` parameter', async () => { - expect(await handler.process('a', { inline: true })).toEqual( - 'a', - ); - expect(await handler.process('a', { inline: false })).toEqual( - '

a

\n', - ); + expect( + (await handler.process('a', { inline: true })).processed, + ).toEqual('a'); + expect( + (await handler.process('a', { inline: false })).processed, + ).toEqual('

a

\n'); }); }); @@ -54,13 +60,19 @@ suite("MarkdownHandler<'marked'>", async () => { await handler.configure({ options: { gfm: true, breaks: true }, }); - expect(await handler.process('a\nb')).toEqual('a
b'); + expect((await handler.process('a\nb')).processed).toEqual( + 'a
b', + ); await handler.configure({ extensions: [] }); - expect(await handler.process('a\nb')).toEqual('a
b'); + expect((await handler.process('a\nb')).processed).toEqual( + 'a
b', + ); await handler.configure({ options: { gfm: false, breaks: false }, }); - expect(await handler.process('a\nb')).toEqual('a\nb'); + expect((await handler.process('a\nb')).processed).toEqual( + 'a\nb', + ); expect(handler.configuration).toEqual({ extensions: [], options: { gfm: false, breaks: false }, diff --git a/tests/handlers/MarkdownHandler/MarkdownHandler.micromark.test.ts b/tests/handlers/MarkdownHandler/MarkdownHandler.micromark.test.ts index f211e88..5a4a285 100644 --- a/tests/handlers/MarkdownHandler/MarkdownHandler.micromark.test.ts +++ b/tests/handlers/MarkdownHandler/MarkdownHandler.micromark.test.ts @@ -1,9 +1,12 @@ -import { suite, describe, it, expect } from 'vitest'; +import { describe, it, expect, beforeAll } from 'vitest'; import { MarkdownHandler } from '$handlers/MarkdownHandler.js'; -suite("MarkdownHandler<'micromark'>", async () => { - const handler = await MarkdownHandler.create('micromark'); +describe("MarkdownHandler<'micromark'>", () => { + let handler: MarkdownHandler<'micromark'>; + beforeAll(async () => { + handler = await MarkdownHandler.create('micromark'); + }); describe("MarkdownHandler.create('micromark')", () => { it('returns instance of MarkdownHandler', () => { @@ -21,7 +24,8 @@ suite("MarkdownHandler<'micromark'>", async () => { }); it('processes markdown correctly', async () => { - const output = await handler.process('**strong** *em*'); + const output = (await handler.process('**strong** *em*')) + .processed; const expected = '

strong em

'; expect(output).toEqual(expected); }); @@ -37,14 +41,16 @@ suite("MarkdownHandler<'micromark'>", async () => { await handler.configure({ allowDangerousProtocol: true }); expect(handler.configuration.allowDangerousProtocol).toBe(true); expect( - await handler.process('[example](unsafe://example.com)'), + (await handler.process('[example](unsafe://example.com)')) + .processed, ).toEqual('

example

'); await handler.configure({ allowDangerousProtocol: false }); expect(handler.configuration.allowDangerousProtocol).toBe( false, ); expect( - await handler.process('[example](unsafe://example.com)'), + (await handler.process('[example](unsafe://example.com)')) + .processed, ).toEqual('

example

'); }); }); diff --git a/tests/handlers/MarkdownHandler/MarkdownHandler.none.test.ts b/tests/handlers/MarkdownHandler/MarkdownHandler.none.test.ts index c90a870..faa50d8 100644 --- a/tests/handlers/MarkdownHandler/MarkdownHandler.none.test.ts +++ b/tests/handlers/MarkdownHandler/MarkdownHandler.none.test.ts @@ -1,8 +1,11 @@ -import { suite, describe, it, expect } from 'vitest'; +import { describe, it, expect, beforeAll } from 'vitest'; import { MarkdownHandler } from '$handlers/MarkdownHandler.js'; -suite("MarkdownHandler<'none'>", async () => { - const handler = await MarkdownHandler.create('none'); +describe("MarkdownHandler<'none'>", () => { + let handler: MarkdownHandler<'none'>; + beforeAll(async () => { + handler = await MarkdownHandler.create('none'); + }); describe("MarkdownHandler.create('none')", () => { it('returns instance of MarkdownHandler', () => { @@ -20,7 +23,8 @@ suite("MarkdownHandler<'none'>", async () => { }); it('leaves markdown as-is', async () => { - const output = await handler.process('**strong** *em*'); + const output = (await handler.process('**strong** *em*')) + .processed; const expected = '**strong** *em*'; expect(output).toEqual(expected); }); diff --git a/tests/handlers/MarkdownHandler/MarkdownHandler.test.ts b/tests/handlers/MarkdownHandler/MarkdownHandler.test.ts index 1f8964f..10e9622 100644 --- a/tests/handlers/MarkdownHandler/MarkdownHandler.test.ts +++ b/tests/handlers/MarkdownHandler/MarkdownHandler.test.ts @@ -1,8 +1,8 @@ -import { suite, describe, it, expect, vi } from 'vitest'; +import { describe, it, expect, vi } from 'vitest'; import { MarkdownHandler } from '$handlers/MarkdownHandler.js'; -import { missingDeps } from '$utils/globals.js'; +import { missingDeps } from '$utils/env.js'; -suite('MarkdownHandler', () => { +describe('MarkdownHandler', () => { describe('MarkdownHandler.shouldParseAsInline()', () => { it('should return true for inline markdown', () => { expect(MarkdownHandler.shouldParseAsInline('**strong** *em*')).toBe( @@ -63,7 +63,7 @@ suite('MarkdownHandler', () => { }); }); -suite('MarkdownHandler error handling', () => { +describe('MarkdownHandler error handling', () => { describe("MarkdownHandler.create('marked') with marked mocked to throw error", () => { vi.mock('marked', () => { throw new Error('marked not found'); diff --git a/tests/handlers/MarkdownHandler/MarkdownHandler.unified.test.ts b/tests/handlers/MarkdownHandler/MarkdownHandler.unified.test.ts index 13ebaaa..051f3c3 100644 --- a/tests/handlers/MarkdownHandler/MarkdownHandler.unified.test.ts +++ b/tests/handlers/MarkdownHandler/MarkdownHandler.unified.test.ts @@ -1,9 +1,12 @@ -import { suite, describe, it, expect } from 'vitest'; +import { describe, it, expect, beforeAll } from 'vitest'; import { MarkdownHandler } from '$handlers/MarkdownHandler.js'; -suite("MarkdownHandler<'unified'>", async () => { - const handler = await MarkdownHandler.create('unified'); +describe("MarkdownHandler<'unified'>", () => { + let handler: MarkdownHandler<'unified'>; + beforeAll(async () => { + handler = await MarkdownHandler.create('unified'); + }); describe("MarkdownHandler.create('unified')", () => { it('returns instance of MarkdownHandler', () => { @@ -21,7 +24,8 @@ suite("MarkdownHandler<'unified'>", async () => { }); it('processes markdown correctly', async () => { - const output = await handler.process('**strong** *em*'); + const output = (await handler.process('**strong** *em*')) + .processed; const expected = '

strong em

'; expect(output).toEqual(expected); }); @@ -39,13 +43,13 @@ suite("MarkdownHandler<'unified'>", async () => { remarkPlugins: undefined, rehypePlugins: undefined, }); - expect(await handler.process('**strong** *em*')).toEqual( - '

strong em

', - ); + expect( + (await handler.process('**strong** *em*')).processed, + ).toEqual('

strong em

'); await handler.configure({}); - expect(await handler.process('**strong** *em*')).toEqual( - '

strong em

', - ); + expect( + (await handler.process('**strong** *em*')).processed, + ).toEqual('

strong em

'); }); }); diff --git a/tests/handlers/TexHandler/TexHandler.katex.errors.2.test.ts b/tests/handlers/TexHandler/TexHandler.katex.errors.2.test.ts index c8ac726..c862eb6 100644 --- a/tests/handlers/TexHandler/TexHandler.katex.errors.2.test.ts +++ b/tests/handlers/TexHandler/TexHandler.katex.errors.2.test.ts @@ -1,6 +1,4 @@ -/* eslint-disable vitest/no-commented-out-tests */ import { - suite, describe, it, expect, @@ -8,6 +6,7 @@ import { vi, beforeEach, beforeAll, + type MockInstance, } from 'vitest'; import { TexHandler } from '$handlers/TexHandler.js'; import { spy } from '$tests/fixtures.js'; @@ -15,14 +14,13 @@ import fetch, { type Response } from 'node-fetch'; import { v4 as uuid } from 'uuid'; import { range } from '$tests/utils.js'; -suite("TexHandler<'katex'>", async () => { - beforeEach(() => { +describe("TexHandler<'katex'>", () => { + let log: MockInstance; + beforeEach(async () => { vi.clearAllMocks(); + log = (await spy(['fancyWrite', 'log', 'mkdir', 'existsSync'], true)) + .log; }); - const { log } = await spy( - ['fancyWrite', 'log', 'mkdir', 'existsSync'], - true, - ); beforeAll(async () => { vi.spyOn(await import('$deps.js'), 'ora').mockImplementation((() => ({ start: vi.fn().mockReturnValue({ diff --git a/tests/handlers/TexHandler/TexHandler.katex.errors.test.ts b/tests/handlers/TexHandler/TexHandler.katex.errors.test.ts index 5a12000..58cc8d4 100644 --- a/tests/handlers/TexHandler/TexHandler.katex.errors.test.ts +++ b/tests/handlers/TexHandler/TexHandler.katex.errors.test.ts @@ -1,6 +1,4 @@ -/* eslint-disable vitest/no-commented-out-tests */ import { - suite, describe, it, expect, @@ -8,6 +6,7 @@ import { vi, beforeEach, beforeAll, + MockInstance, } from 'vitest'; import { TexHandler } from '$handlers/TexHandler.js'; import { spy } from '$tests/fixtures.js'; @@ -15,14 +14,11 @@ import fetch, { type Response } from 'node-fetch'; import { v4 as uuid } from 'uuid'; import { range } from '$tests/utils.js'; -suite("TexHandler<'katex'>", async () => { +describe("TexHandler<'katex'>", () => { beforeEach(() => { vi.clearAllMocks(); }); - const { log } = await spy( - ['writeFile', 'log', 'mkdir', 'existsSync'], - true, - ); + let log: MockInstance; beforeAll(async () => { vi.spyOn(await import('$deps.js'), 'ora').mockImplementation((() => ({ start: vi.fn().mockReturnValue({ @@ -32,6 +28,11 @@ suite("TexHandler<'katex'>", async () => { fail: vi.fn(), }), })) as unknown as typeof import('ora').default); + const mocks = await spy( + ['writeFile', 'log', 'mkdir', 'existsSync'], + true, + ); + log = mocks.log; }); afterAll(() => { vi.restoreAllMocks(); diff --git a/tests/handlers/TexHandler/TexHandler.katex.test.ts b/tests/handlers/TexHandler/TexHandler.katex.test.ts index 272bc74..c65cc82 100644 --- a/tests/handlers/TexHandler/TexHandler.katex.test.ts +++ b/tests/handlers/TexHandler/TexHandler.katex.test.ts @@ -1,6 +1,4 @@ -/* eslint-disable vitest/no-commented-out-tests */ import { - suite, describe, it, expect, @@ -8,6 +6,7 @@ import { vi, beforeEach, beforeAll, + MockInstance, } from 'vitest'; import { TexHandler } from '$handlers/TexHandler.js'; import { spy } from '$tests/fixtures.js'; @@ -19,13 +18,13 @@ function fixture() { }); } -suite("TexHandler<'katex'>", async () => { +describe("TexHandler<'katex'>", () => { + let handler: TexHandler<'katex'>; + let log: MockInstance; + let writeFileEnsureDir: MockInstance; + let fancyWrite: MockInstance; + let existsSync: MockInstance; fixture(); - const { writeFileEnsureDir, fancyWrite, log, existsSync } = await spy( - ['writeFileEnsureDir', 'fancyWrite', 'log', 'existsSync', 'mkdir'], - true, - ); - const handler = await TexHandler.create('katex'); beforeAll(async () => { vi.spyOn(await import('$deps.js'), 'ora').mockImplementation((() => ({ start: vi.fn().mockReturnValue({ @@ -35,6 +34,15 @@ suite("TexHandler<'katex'>", async () => { fail: vi.fn(), }), })) as unknown as typeof import('ora').default); + const mocks = await spy( + ['writeFileEnsureDir', 'fancyWrite', 'log', 'existsSync', 'mkdir'], + true, + ); + writeFileEnsureDir = mocks.writeFileEnsureDir; + fancyWrite = mocks.fancyWrite; + log = mocks.log; + existsSync = mocks.existsSync; + handler = await TexHandler.create('katex'); }); afterAll(() => { vi.restoreAllMocks(); @@ -103,14 +111,16 @@ suite("TexHandler<'katex'>", async () => { describe('process()', () => { fixture(); it('should work for basic inline math', async () => { - expect(await handler.process('$x$')).toEqual( + expect((await handler.process('x')).processed).toEqual( 'xx', ); expect(log).not.toHaveBeenCalled(); }); it('should work for basic display math', async () => { - expect(await handler.process('$$x$$')).toEqual( + expect( + (await handler.process('x', { inline: false })).processed, + ).toEqual( 'xx', ); expect(log).not.toHaveBeenCalled(); @@ -118,7 +128,11 @@ suite("TexHandler<'katex'>", async () => { it('should support CSS color variables', async () => { expect( - await handler.process('$$\\color{var(--red)}x$$'), + ( + await handler.process('\\color{var(--red)}x', { + inline: false, + }) + ).processed, ).toEqual( 'x\\color{var(--red)}x', ); @@ -136,7 +150,10 @@ suite("TexHandler<'katex'>", async () => { post: [[/ xmlns:xlink=".*?"/g, '']], }, }); - expect(await handler.process('$a * b$')).toEqual( + expect( + (await handler.process('a * b', { inline: true })) + .processed, + ).toEqual( 'ccc \\cdot c', ); await handler.configure({ @@ -162,7 +179,9 @@ suite("TexHandler<'katex'>", async () => { it('configures code correctly', async () => { await handler.configure({ katex: { output: 'mathml' } }); - expect(await handler.process('$x$')).toEqual( + expect( + (await handler.process('x', { inline: true })).processed, + ).toEqual( 'xx', ); await handler.configure({ katex: { output: 'html' } }); diff --git a/tests/handlers/TexHandler/TexHandler.mathjax.errors.test.ts b/tests/handlers/TexHandler/TexHandler.mathjax.errors.test.ts index 7fc9ce4..78f31fb 100644 --- a/tests/handlers/TexHandler/TexHandler.mathjax.errors.test.ts +++ b/tests/handlers/TexHandler/TexHandler.mathjax.errors.test.ts @@ -1,5 +1,4 @@ import { - suite, describe, it, expect, @@ -18,8 +17,7 @@ function fixture() { }); } -suite("TexHandler<'mathjax'>", async () => { - await spy(['writeFile', 'log', 'existsSync', 'mkdir'], true); +describe("TexHandler<'mathjax'>", () => { beforeAll(async () => { vi.spyOn(await import('$deps.js'), 'ora').mockImplementation((() => ({ start: vi.fn().mockReturnValue({ @@ -29,6 +27,7 @@ suite("TexHandler<'mathjax'>", async () => { fail: vi.fn(), }), })) as unknown as typeof import('ora').default); + await spy(['writeFile', 'log', 'existsSync', 'mkdir'], true); }); afterAll(() => { vi.restoreAllMocks(); diff --git a/tests/handlers/TexHandler/TexHandler.mathjax.test.ts b/tests/handlers/TexHandler/TexHandler.mathjax.test.ts index 50dde4e..df1c237 100644 --- a/tests/handlers/TexHandler/TexHandler.mathjax.test.ts +++ b/tests/handlers/TexHandler/TexHandler.mathjax.test.ts @@ -1,5 +1,4 @@ import { - suite, describe, it, expect, @@ -7,6 +6,7 @@ import { vi, beforeEach, beforeAll, + MockInstance, } from 'vitest'; import { TexHandler } from '$handlers/TexHandler.js'; import { MathDocument } from 'mathjax-full/js/core/MathDocument.js'; @@ -20,8 +20,12 @@ function fixture() { }); } -suite("TexHandler<'mathjax'>", async () => { +describe("TexHandler<'mathjax'>", () => { fixture(); + let writeFile: MockInstance; + let fancyWrite: MockInstance; + let log: MockInstance; + let existsSync: MockInstance; beforeAll(async () => { vi.spyOn(await import('$deps.js'), 'ora').mockImplementation((() => ({ start: vi.fn().mockReturnValue({ @@ -31,11 +35,15 @@ suite("TexHandler<'mathjax'>", async () => { fail: vi.fn(), }), })) as unknown as typeof import('ora').default); + const mocks = await spy( + ['writeFile', 'fancyWrite', 'mkdir', 'log', 'existsSync'], + true, + ); + writeFile = mocks.writeFile; + fancyWrite = mocks.fancyWrite; + log = mocks.log; + existsSync = mocks.existsSync; }); - const { writeFile, fancyWrite, log, existsSync } = await spy( - ['writeFile', 'fancyWrite', 'mkdir', 'log', 'existsSync'], - true, - ); afterAll(() => { vi.restoreAllMocks(); @@ -108,14 +116,14 @@ suite("TexHandler<'mathjax'>", async () => { fixture(); it('should work, and output SVG by default', async () => { const handler = await TexHandler.create('mathjax'); - expect(await handler.process('x')).toEqual(xSvg); + expect((await handler.process('x')).processed).toEqual(xSvg); expect(log).not.toHaveBeenCalled(); }); it('should be able to output CHTML', async () => { const handler = await TexHandler.create('mathjax'); await handler.configure({ outputFormat: 'chtml' }); - expect(await handler.process('x')).toEqual(xChtml); + expect((await handler.process('x')).processed).toEqual(xChtml); await handler.configure({ outputFormat: 'svg' }); expect(log).not.toHaveBeenCalled(); }); @@ -137,7 +145,11 @@ suite("TexHandler<'mathjax'>", async () => { it('should support CSS color variables', async () => { const handler = await TexHandler.create('mathjax'); expect( - await handler.process('$$\\color{var(--red)}x$$'), + ( + await handler.process('\\color{var(--red)}x', { + inline: false, + }) + ).processed, ).toEqual( '', ); @@ -161,7 +173,10 @@ suite("TexHandler<'mathjax'>", async () => { ], }, }); - expect(await handler.process('$a * b$')).toEqual( + expect( + (await handler.process('a * b', { inline: true })) + .processed, + ).toEqual( '', ); expect(log).not.toHaveBeenCalled(); @@ -188,7 +203,7 @@ suite("TexHandler<'mathjax'>", async () => { it('configures code correctly', async () => { const handler = await TexHandler.create('mathjax'); await handler.configure({ outputFormat: 'svg' }); - expect(await handler.process('x')).toEqual(xSvg); + expect((await handler.process('x')).processed).toEqual(xSvg); expect(log).not.toHaveBeenCalled(); }); diff --git a/tests/handlers/TexHandler/TexHandler.test.ts b/tests/handlers/TexHandler/TexHandler.test.ts index a939112..cfef849 100644 --- a/tests/handlers/TexHandler/TexHandler.test.ts +++ b/tests/handlers/TexHandler/TexHandler.test.ts @@ -1,9 +1,13 @@ -import { suite, describe, it, expect, vi, beforeAll } from 'vitest'; +import { describe, it, expect, vi, beforeAll } from 'vitest'; import { TexHandler } from '$handlers/TexHandler.js'; -import { missingDeps } from '$utils/globals.js'; +import { missingDeps } from '$utils/env.js'; + +describe("TexHandler<'none'>", () => { + let handler: TexHandler<'none'>; + beforeAll(async () => { + handler = await TexHandler.create('none'); + }); -suite("TexHandler<'none'>", async () => { - const handler = await TexHandler.create('none'); beforeAll(async () => { vi.spyOn(await import('$deps.js'), 'ora').mockImplementation((() => ({ start: vi.fn().mockReturnValue({ @@ -25,7 +29,7 @@ suite("TexHandler<'none'>", async () => { describe('texHandler', () => { describe('process()', () => { it('should work, and output the input as-is', async () => { - expect(await handler.process('x')).toEqual('x'); + expect((await handler.process('x')).processed).toEqual('x'); }); }); @@ -44,9 +48,12 @@ suite("TexHandler<'none'>", async () => { }); }); -suite("TexHandler<'custom'>", async () => { - const handler = await TexHandler.create('custom', { - process: (input: string) => input, +describe("TexHandler<'custom'>", () => { + let handler: TexHandler<'custom'>; + beforeAll(async () => { + handler = await TexHandler.create('custom', { + process: (input: string) => input, + }); }); describe("TexHandler.create('custom', ...)", () => { it('returns instance of TexHandler', () => { @@ -75,14 +82,7 @@ suite("TexHandler<'custom'>", async () => { describe('texHandler', () => { describe('process()', () => { it('should work, and output the input as-is', async () => { - expect(await handler.process('x')).toEqual('x'); - }); - - it('should work strip math delims', async () => { - expect(await handler.process('$x$')).toEqual('x'); - expect(await handler.process('\\(x\\)')).toEqual('x'); - expect(await handler.process('$$x$$')).toEqual('x'); - expect(await handler.process('\\[x\\]')).toEqual('x'); + expect((await handler.process('x')).processed).toEqual('x'); }); }); @@ -103,7 +103,7 @@ suite("TexHandler<'custom'>", async () => { }); }); -suite('TexHandler error handling', () => { +describe('TexHandler error handling', () => { describe("TexHandler.create('mathjax') with mathjax mocked to throw error", () => { vi.mock('mathjax-full/js/handlers/html.js', () => { throw new Error('MathJax not found'); diff --git a/tests/handlers/VerbatimHandler/VerbatimHandler.test.ts b/tests/handlers/VerbatimHandler/VerbatimHandler.test.ts index 76f9e98..5827997 100644 --- a/tests/handlers/VerbatimHandler/VerbatimHandler.test.ts +++ b/tests/handlers/VerbatimHandler/VerbatimHandler.test.ts @@ -1,11 +1,12 @@ import { afterAll, afterEach, + beforeAll, beforeEach, describe, expect, it, - suite, + type MockInstance, vi, } from 'vitest'; @@ -68,11 +69,16 @@ function fixture() { }); } -suite('VerbatimHandler', async () => { +describe('VerbatimHandler', () => { + let log: MockInstance; + beforeAll(async () => { + const mocks = await spy(['log']); + log = mocks.log; + }); afterAll(() => { vi.restoreAllMocks(); }); - const { log } = await spy(['log']); + fixture(); describe('constructor(sveltex: Sveltex)', () => { @@ -134,32 +140,44 @@ suite('VerbatimHandler', async () => { describe('process(content: string)', () => { fixture(); it('should process JS code correctly', async () => { - const result = await sveltexPreprocessor.verbatimHandler.process( - '\nconst a = new Map();\n', - { filename: 'test.sveltex' }, - ); + const result = ( + await sveltexPreprocessor.verbatimHandler.process( + '\nconst a = new Map();\n', + { + filename: 'test.sveltex', + selfClosing: false, + tag: 'Code', + attributes: { lang: 'js' }, + outerContent: + '\nconst a = new Map();\n', + }, + ) + ).processed; expect(result).toEqual( '\nconst a = new Map<string, { prop: number[] }>();\n', ); }); it('should escape plain text correctly', async () => { - const result = await sveltexPreprocessor.verbatimHandler.process( - '\nconst a = new Map();\n', - { filename: 'test.sveltex' }, - ); + const result = ( + await sveltexPreprocessor.verbatimHandler.process( + '\nconst a = new Map();\n', + { + filename: 'test.sveltex', + selfClosing: false, + tag: 'Code', + attributes: {}, + outerContent: + '\nconst a = new Map();\n', + }, + ) + ).processed; expect(result).toEqual( '\nconst a = new Map<string, { prop: number[] }>();\n', ); }); it('should correctly handle self-closing components', async () => { - expect( - await sveltexPreprocessor.verbatimHandler.process( - '', - { filename: 'test.sveltex' }, - ), - ).toEqual(''); await sveltexPreprocessor.configure({ verbatim: { verbatimEnvironments: { @@ -170,10 +188,15 @@ suite('VerbatimHandler', async () => { }, }); expect( - await sveltexPreprocessor.verbatimHandler.process( - '', - { filename: 'test.sveltex' }, - ), + ( + await sveltexPreprocessor.verbatimHandler.process('', { + filename: 'test.sveltex', + selfClosing: true, + tag: 'Code', + attributes: { id: 'something' }, + outerContent: '', + }) + ).processed, ).toEqual(''); await sveltexPreprocessor.configure({ verbatim: { @@ -185,10 +208,18 @@ suite('VerbatimHandler', async () => { }, }); expect( - await sveltexPreprocessor.verbatimHandler.process( - '', - { filename: 'test.sveltex' }, - ), + ( + await sveltexPreprocessor.verbatimHandler.process( + '', + { + filename: 'test.sveltex', + selfClosing: true, + tag: 'Code', + attributes: { id: 'something' }, + outerContent: '', + }, + ) + ).processed, ).toEqual(''); await sveltexPreprocessor.configure({ verbatim: { @@ -200,17 +231,27 @@ suite('VerbatimHandler', async () => { }, }); expect( - await sveltexPreprocessor.verbatimHandler.process( - '', - { filename: 'test.sveltex' }, - ), - ).toEqual(''); - expect( - await sveltexPreprocessor.verbatimHandler.process( - '', - { filename: 'test.sveltex' }, - ), + ( + await sveltexPreprocessor.verbatimHandler.process('', { + filename: 'test.sveltex', + selfClosing: true, + tag: 'Code', + attributes: { id: 'something' }, + outerContent: '', + }) + ).processed, ).toEqual(''); + expect( + ( + await sveltexPreprocessor.verbatimHandler.process('', { + filename: 'test.sveltex', + selfClosing: true, + tag: 'Code', + attributes: { id: 'something' }, + outerContent: '', + }) + ).processed, + ).toEqual(''); }); it('should correctly handle self-closing components (TeX)', async () => { @@ -223,9 +264,15 @@ suite('VerbatimHandler', async () => { }, }); expect( - await sp.verbatimHandler.process('', { - filename: 'test.sveltex', - }), + ( + await sp.verbatimHandler.process('', { + filename: 'test.sveltex', + selfClosing: true, + tag: 'tex', + attributes: { ref: 'something' }, + outerContent: '', + }) + ).processed, ).toEqual( '
\n\n
', ); @@ -244,46 +291,48 @@ suite('VerbatimHandler', async () => { }, }); expect( - await sp.verbatimHandler.process( - '', - { + ( + await sp.verbatimHandler.process('', { filename: 'test.sveltex', - }, - ), + selfClosing: true, + tag: 'Code', + attributes: { a: 1, b: 2, c: 3, d: 4 }, + outerContent: '', + }) + ).processed, ).toEqual(''); expect( - await sp.verbatimHandler.process('', { - filename: 'test.sveltex', - }), + ( + await sp.verbatimHandler.process('', { + filename: 'test.sveltex', + selfClosing: true, + tag: 'Code', + attributes: { + a: undefined, + b: undefined, + c: undefined, + d: undefined, + }, + outerContent: '', + }) + ).processed, ).toEqual(''); }); - it.each([ - 'x', - '', - 'x', - 'x', - '', - '', - '', - ])('should deal with weird content gracefully: %o', async (content) => { - const result = await sveltexPreprocessor.verbatimHandler.process( - content, - { - filename: 'test.sveltex', - }, - ); - expect(result).toEqual(content); - expect(log).toHaveBeenCalledTimes(1); - expect(log).toHaveBeenNthCalledWith(1, 'error', expect.any(String)); - }); - it('should deal with unknown verbatim tags gracefully', async () => { expect( - await sveltexPreprocessor.verbatimHandler.process( - 'something', - { filename: 'test.sveltex' }, - ), + ( + await sveltexPreprocessor.verbatimHandler.process( + 'something', + { + filename: 'test.sveltex', + selfClosing: false, + tag: 'unknown', + attributes: {}, + outerContent: 'something', + }, + ) + ).processed, ).toEqual('something'); expect(log).toHaveBeenCalledTimes(1); expect(log).toHaveBeenNthCalledWith( @@ -297,10 +346,18 @@ suite('VerbatimHandler', async () => { it('should deal with ambiguous verbatim tags gracefully', async () => { expect( - await sveltexPreprocessor.verbatimHandler.process( - 'something', - { filename: 'test.sveltex' }, - ), + ( + await sveltexPreprocessor.verbatimHandler.process( + 'something', + { + filename: 'test.sveltex', + selfClosing: false, + tag: 'Ambiguous', + attributes: {}, + outerContent: 'something', + }, + ) + ).processed, ).toEqual('something'); expect(log).toHaveBeenCalledTimes(1); expect(log).toHaveBeenNthCalledWith( @@ -313,20 +370,38 @@ suite('VerbatimHandler', async () => { }); it('should work with aliases', async () => { - const result = await sveltexPreprocessor.verbatimHandler.process( - '\nconst a = new Map();\n', - { filename: 'test.sveltex' }, - ); + const result = ( + await sveltexPreprocessor.verbatimHandler.process( + '\nconst a = new Map();\n', + { + filename: 'test.sveltex', + selfClosing: false, + tag: 'CodeBlock', + attributes: { lang: 'js' }, + outerContent: + '\nconst a = new Map();\n', + }, + ) + ).processed; expect(result).toEqual( '\nconst a = new Map<string, { prop: number[] }>();\n', ); }); it('should support transformation of component', async () => { - const result = await sveltexPreprocessor.verbatimHandler.process( - '\nconst a = new Map();\n', - { filename: 'test.sveltex' }, - ); + const result = ( + await sveltexPreprocessor.verbatimHandler.process( + '\nconst a = new Map();\n', + { + filename: 'test.sveltex', + selfClosing: false, + tag: 'JS', + attributes: {}, + outerContent: + '\nconst a = new Map();\n', + }, + ) + ).processed; expect(result).toEqual( '\nconst a = new Map<string, { prop: number[] }>();\n', ); @@ -334,49 +409,81 @@ suite('VerbatimHandler', async () => { it('should support simple escape instructions', async () => { expect( - await sveltexPreprocessor.verbatimHandler.process( - '\n<>&{', - { filename: 'test.sveltex' }, - ), - ).toEqual('\n<>&{'); - - expect( - await sveltexPreprocessor.verbatimHandler.process( - '\n<>&{', - { filename: 'test.sveltex' }, - ), + ( + await sveltexPreprocessor.verbatimHandler.process( + '\n<>&{', + { + filename: 'test.sveltex', + selfClosing: false, + tag: 'Verbatim', + attributes: {}, + outerContent: '\n<>&{', + }, + ) + ).processed, ).toEqual('\n<>&{'); }); it('should support custom processInner functions', async () => { expect( - await sveltexPreprocessor.verbatimHandler.process( - 'content', - { filename: 'test.sveltex' }, - ), + ( + await sveltexPreprocessor.verbatimHandler.process( + 'content', + { + filename: 'test.sveltex', + selfClosing: false, + tag: 'Custom', + attributes: { attr: 'test', attr2: 'test2' }, + outerContent: + 'content', + }, + ) + ).processed, ).toEqual( 'Custom: content{"attr":"test","attr2":"test2"}', ); expect( - await sveltexPreprocessor.verbatimHandler.process( - 'content', - { filename: 'test.sveltex' }, - ), - ).toEqual('Custom: content{}'); + ( + await sveltexPreprocessor.verbatimHandler.process( + 'content', + { + filename: 'test.sveltex', + selfClosing: false, + tag: 'Custom', + attributes: { attr: 'test', attr2: 'test2' }, + outerContent: + 'content', + }, + ) + ).processed, + ).toEqual( + 'Custom: content{"attr":"test","attr2":"test2"}', + ); }); it('misc', async () => { expect( - await sveltexPreprocessor.verbatimHandler.process('', { - filename: 'test.sveltex', - }), + ( + await sveltexPreprocessor.verbatimHandler.process('', { + filename: 'test.sveltex', + selfClosing: false, + tag: '', + attributes: {}, + outerContent: '', + }) + ).processed, ).toEqual(''); expect( - await sveltexPreprocessor.verbatimHandler.process( - 'abc', - { filename: 'test.sveltex' }, - ), + ( + await sveltexPreprocessor.verbatimHandler.process('abc', { + filename: 'test.sveltex', + selfClosing: false, + tag: 'noop', + attributes: {}, + outerContent: 'abc', + }) + ).processed, ).toEqual('abc'); }); }); diff --git a/tests/type-guards/ast.test.ts b/tests/type-guards/ast.test.ts index f1ed6c4..9ad1df5 100644 --- a/tests/type-guards/ast.test.ts +++ b/tests/type-guards/ast.test.ts @@ -1,7 +1,6 @@ import { hasStartEnd_LineColumn, hasStartEnd_Offset, - isFragment_v5, } from '$type-guards/ast.js'; import { describe, expect, it } from 'vitest'; @@ -164,70 +163,3 @@ describe('hasStartEnd_LineColumn', () => { expect(result).toBe(false); }); }); - -describe('isFragment_v5', () => { - it('should return true for a valid Fragment_v5 node', () => { - const node = { - type: 'Fragment', - nodes: [ - { type: 'Text', data: 'Hello', start: 1, end: 2 }, - { type: 'Element', name: 'div', start: 3, end: 4 }, - ], - start: 1, - end: 4, - }; - - const result = isFragment_v5(node); - - expect(result).toBe(true); - }); - - it('should return false for a node with incorrect type', () => { - const node = { - type: 'Element', - name: 'div', - start: 1, - end: 2, - }; - - const result = isFragment_v5(node); - - expect(result).toBe(false); - }); - - it('should return false for a node without "nodes" property', () => { - const node = { - type: 'Fragment', - }; - - const result = isFragment_v5(node); - - expect(result).toBe(false); - }); - - it('should return false for a node with "nodes" property that is not an array', () => { - const node = { - type: 'Fragment', - nodes: {}, - }; - - const result = isFragment_v5(node); - - expect(result).toBe(false); - }); - - it('should return false for a node with "nodes" property that contains non-BaseNode elements', () => { - const node = { - type: 'Fragment', - nodes: [ - { type: 'Text', data: 'Hello', start: 1, end: 2 }, - { type: 'Element', name: 'div', start: 3, end: 4 }, - 'InvalidNode', - ], - }; - - const result = isFragment_v5(node); - - expect(result).toBe(false); - }); -}); diff --git a/tests/utils.ts b/tests/utils.ts index db50cfd..53f6d36 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -46,7 +46,7 @@ export function expectWith( export function range(start: number, end: number, step: number = 1): number[] { const arr = []; - for (let i = start; i < end; i += step) { + for (let i = start; i <= end; i += step) { arr.push(i); } return arr; @@ -62,8 +62,37 @@ interface Variables { advancedTex?: boolean | AdvancedTexBackend[]; } -const cartesianProduct = (...a: unknown[][]) => - a.reduce((a, b) => a.flatMap((d) => b.map((e) => [d, e].flat()))); +export function cartesianProduct(x1: X1[]): [X1][]; +export function cartesianProduct(x1: X1[], x2: X2[]): [X1, X2][]; +export function cartesianProduct( + x1: X1[], + x2: X2[], + x3: X3[], +): [X1, X2, X3][]; +export function cartesianProduct( + x1: X1[], + x2: X2[], + x3: X3[], + x4: X4[], +): [X1, X2, X3, X4][]; +export function cartesianProduct( + x1: X1[], + x2: X2[], + x3: X3[], + x4: X4[], + x5: X5[], +): [X1, X2, X3, X4, X5][]; +export function cartesianProduct( + x1: X1[], + x2: X2[], + x3: X3[], + x4: X4[], + x5: X5[], + x6: X6[], +): [X1, X2, X3, X4, X5, X6][]; +export function cartesianProduct(...a: unknown[][]) { + return a.reduce((a, b) => a.flatMap((d) => b.map((e) => [d, e].flat()))); +} type BackendCombination = `${MarkdownBackend}-${CodeBackend}-${TexBackend}-${AdvancedTexBackend}`; diff --git a/tests/utils/TexComponent.test.ts b/tests/utils/TexComponent.test.ts index fd28db6..5227bea 100644 --- a/tests/utils/TexComponent.test.ts +++ b/tests/utils/TexComponent.test.ts @@ -1,12 +1,11 @@ +import { resolve, rimraf } from '$deps.js'; import { AdvancedTexHandler } from '$handlers/AdvancedTexHandler.js'; import { spy } from '$tests/fixtures.js'; import type { SupportedTexEngine } from '$types/SveltexConfiguration.js'; import { TexComponent } from '$utils/TexComponent.js'; import { unescapeCssColorVars } from '$utils/css.js'; -import { pathExists } from '$utils/debug.js'; +import { pathExists } from '$utils/fs.js'; import { sha256 } from '$utils/misc.js'; -import { resolve } from 'node:path'; -import { rimraf } from 'rimraf'; import { afterAll, afterEach, @@ -15,8 +14,8 @@ import { describe, expect, it, - suite, vi, + type MockInstance, } from 'vitest'; let ath: AdvancedTexHandler<'local'>; @@ -68,8 +67,12 @@ function fixture() { }); } -suite('TexComponent', async () => { +describe('TexComponent', () => { vi.restoreAllMocks(); + let log: MockInstance; + let writeFile: MockInstance; + let spawnCliInstruction: MockInstance; + let readFile: MockInstance; beforeAll(async () => { vi.spyOn(await import('$deps.js'), 'ora').mockImplementation((() => ({ start: vi.fn().mockReturnValue({ @@ -79,6 +82,15 @@ suite('TexComponent', async () => { fail: vi.fn(), }), })) as unknown as typeof import('ora').default); + const mocks = await spy( + ['writeFile', 'log', 'spawnCliInstruction', 'readFile'], + false, + ); + log = mocks.log; + writeFile = mocks.writeFile; + spawnCliInstruction = mocks.spawnCliInstruction; + readFile = mocks.readFile; + log.mockImplementation(() => undefined); }); afterAll(async () => { vi.restoreAllMocks(); @@ -86,12 +98,6 @@ suite('TexComponent', async () => { await rimraf(resolve(`tmp/tests`)); } }); - // fixture(); - const { writeFile, log, spawnCliInstruction, readFile } = await spy( - ['writeFile', 'log', 'spawnCliInstruction', 'readFile'], - false, - ); - log.mockImplementation(() => undefined); describe('create', () => { describe('error handling', () => { @@ -106,62 +112,10 @@ suite('TexComponent', async () => { filename: 'TexComponent_test_ts.sveltex', selfClosing: false, }), - ).toThrowError('TeX component must have a ref attribute.'); + ).toThrowError(/ref.*attribute/isu); }, ); }); - - describe('valueless attributes', () => { - fixture(); - it('if exactly 1 valueless attribute is provided, but a ref attribute is also provided, log a warning about the valueless attribute', () => { - const tc = ath.createTexComponent('content', { - tag: 'tex', - attributes: { - ref: 'something', - someValuelessAttribute: undefined, - }, - filename: 'TexComponent_test_ts.sveltex', - selfClosing: false, - }); - expect(tc.ref).toEqual('something'); - expect(log).toHaveBeenCalledTimes(1); - expect(log).toHaveBeenNthCalledWith( - 1, - 'warn', - 'TeX component tex with ref "something" has attributes with no value: someValuelessAttribute. These attributes will be ignored.', - ); - }); - - it('if exactly 1 valueless attribute is provided, and no ref attribute, the valueless attribute is used as ref', () => { - const tc = ath.createTexComponent('content', { - tag: 'tex', - attributes: { refFromValuelessAttribute: undefined }, - filename: 'TexComponent_test_ts.sveltex', - selfClosing: false, - }); - expect(tc.ref).toEqual('refFromValuelessAttribute'); - expect(log).not.toHaveBeenCalled(); - }); - - it('if ≥1 valueless attributes are provided, and no ref attribute, the first valueless attribute is used as ref', () => { - const tc = ath.createTexComponent('content', { - tag: 'tex', - attributes: { - refFromValuelessAttribute: undefined, - anotherValuelessAttribute: undefined, - }, - filename: 'TexComponent_test_ts.sveltex', - selfClosing: false, - }); - expect(tc.ref).toEqual('refFromValuelessAttribute'); - expect(log).toHaveBeenCalledTimes(1); - expect(log).toHaveBeenNthCalledWith( - 1, - 'warn', - 'TeX component tex with ref "refFromValuelessAttribute" has attributes with no value: refFromValuelessAttribute, anotherValuelessAttribute. Of these, the first, "refFromValuelessAttribute", is being used as the ref attribute, and the rest will be ignored.', - ); - }); - }); }); describe('configuration setter', () => { diff --git a/tests/utils/ast.test.ts b/tests/utils/ast.test.ts index c438a34..5311124 100644 --- a/tests/utils/ast.test.ts +++ b/tests/utils/ast.test.ts @@ -1,421 +1,119 @@ -import { describe, it, expect, vi, suite, beforeEach } from 'vitest'; -import { - getChildren, - getLocation, - lineColToLocation, - parse, - stringifyAst, - walk, -} from '$utils/ast.js'; - -suite('AST utilities', () => { - describe.each([3, 4, 5, 6, 7, 8])('getLocation', (version) => { - it('should return the correct location for a node with start and end properties', () => { - const node = { - type: 'test', - start: 1, - end: 10, - }; - - const location = getLocation(node, undefined, version); - - expect(location).toEqual({ - start: 1, - end: 10, - }); - }); - - it('should return the correct location for a node with range property', () => { - const node = { type: 'test', range: [5, 15] }; - - const location = getLocation(node, undefined, version); - - expect(location).toEqual({ - start: 5, - end: 15, - }); - }); - - it('should return the correct location for a node with loc property and source', () => { - const node = { - type: 'test', - loc: { - start: { line: 1, column: 5 }, - end: { line: 2, column: 10 }, - }, - }; - - const source = - 'const x = "something";\nconst y = "something else";'; - - const location = getLocation(node, source, version); - - expect(location).toEqual({ - start: 5, - end: 32, - }); - }); - - it('should throw an error for a node without start and end properties', () => { - const node = { - type: 'test', - }; - - expect(() => getLocation(node, undefined, version)).toThrowError( - 'Could not determine location of node: ' + JSON.stringify(node), - ); - }); - +import { describe, it, expect } from 'vitest'; +import { getLocationUnist, lineColToLocation } from '$utils/ast.js'; +import type { UnistNode } from '$deps.js'; +import { cartesianProduct, range } from '$tests/utils.js'; +import { LineColumn, Offsets } from '$types/utils/Ast.js'; + +describe('getLocationUnist', () => { + describe('core functionality', () => { it.each([ - { - start: { - offset: 5, - }, - end: { - offset: 32, - }, - }, - { - start: { - line: 1, - column: 5, - }, - end: { - line: 1, - column: 32, + [ + { + start: { offset: 0, column: 1, line: 1 }, + end: { offset: 10, column: 11, line: 1 }, }, - }, - { - loc: { - start: { - offset: 5, - }, - end: { - offset: 32, - }, + ['const x = "something";', 'const y = "something else";'], + ], + [ + { + start: { offset: 20, column: 21, line: 1 }, + end: { offset: 25, column: 3, line: 2 }, }, - }, - { - loc: { - start: { - line: 1, - column: 5, - }, - end: { - line: 1, - column: 32, - }, + ['const x = "something";', 'const y = "something else";'], + ], + [ + { + start: { offset: 2, column: 1, line: 2 }, + end: { offset: 6, column: 2, line: 4 }, }, - }, - ])('should work with some weird formats', (props) => { - const node = { - type: 'test', - ...props, - }; - - const source = - 'const x = "something";\nconst y = "something else";'; - - const location = getLocation(node, source, version); + ['a', '', 'b', 'c'], + ], + ] as [NonNullable, string[]][])( + '%o on %o', + (position, source) => { + const n1: UnistNode = { type: '', position }; + + const expected = { + start: position.start.offset, + end: position.end.offset, + }; - expect(location).toEqual({ - start: 5, - end: 32, - }); - }); + position.start.offset = undefined; + position.end.offset = undefined; - vi.unmock('$utils/globals.js'); - }); + const n2: UnistNode = { type: '', position }; - describe('lineColToLocation', () => { - it('should correctly convert line-column to offset location with string source', () => { - const source = 'line 1 - text\nline 2 - text\nline 3 - text'; - const start = { line: 2, column: 1 }; - const end = { line: 3, column: 5 }; - const location = lineColToLocation(start, end, source); - expect(location).toEqual({ start: 14, end: 31 }); - }); + const loc1 = getLocationUnist(n1, source); + const loc2 = getLocationUnist(n2, source); - it('should correctly convert line-column to offset location with source', () => { - const source = ['line 1 - text', 'line 2 - text', 'line 3 - text']; - const start = { line: 1, column: 6 }; - const end = { line: 2, column: 3 }; - const location = lineColToLocation(start, end, source); - expect(location).toEqual({ start: 6, end: 16 }); - }); + expect(loc1).toEqual(expected); + expect(loc2).toEqual(expected); + }, + ); }); - - describe('getChildren', () => { - it('should return child nodes for a given Svelte AST node', () => { - const node = { - type: 'Fragment', - children: [ - { type: 'Text', data: 'Hello' }, - { type: 'Element', name: 'div' }, - ], - start: 1, - end: 4, - }; - const children = getChildren(node, 4); - expect(children).toEqual(node.children); - expect(children).not.toBe(node.children); - }); - - it('should return (a copy of) the .nodes property if version is 5', () => { - const node = { - type: 'Fragment', - nodes: [ - { type: 'Text', data: 'Hello', start: 1, end: 2 }, - { type: 'Element', name: 'div', start: 3, end: 4 }, - ], - start: 1, - end: 4, - }; - const children = getChildren(node, 5); - expect(children).toEqual(node.nodes); - expect(children).not.toBe(node.nodes); - }); - - it.each([1, 5, 100])('AwaitBlock (v5)', (version) => { - const node = { - type: 'Fragment', - nodes: [ - { type: 'AwaitBlock', data: 'Hello', start: 1, end: 2 }, - { type: 'Element', name: 'div', start: 3, end: 4 }, - ], - start: 1, - end: 4, - }; - const children = getChildren(node, version); - expect(children).toEqual(node.nodes); - expect(children).not.toBe(node.nodes); - }); - - it.each([1, 4, 100])('AwaitBlock (v4)', (version) => { - const node = { - type: 'AwaitBlock', - children: [ - { type: 'Text', data: 'Hello', start: 1, end: 2 }, - { type: 'Element', name: 'div', start: 3, end: 4 }, - ], - start: 1, - end: 4, - pending: { type: 'Text', data: 'Pending', start: 1, end: 2 }, - then: { type: 'Text', data: 'Then', start: 1, end: 2 }, - catch: [ - { type: 'Text', data: 'Catch', start: 1, end: 2 }, - { type: 'Text', data: 'Catch2', start: 1, end: 2 }, - ], - }; - const children = getChildren(node, version); - expect(children).toEqual([ - ...node.children, - node.pending, - node.then, - ...node.catch, - ]); + describe('error handling', () => { + it('no position prop → throw error', () => { + const node = { type: 'test' }; + expect(() => getLocationUnist(node, [''])).toThrowError( + /Could not determine location of node:/, + ); }); }); +}); - describe('walk', () => { - it('should perform an action on each node in the AST', () => { - const node = { - type: 'Fragment', - children: [{ type: 'Text', data: 'Hello' }], - }; - const walkedNodes: { node: unknown; depth: number }[] = []; - const mockAction = vi.fn((node: unknown, depth: number) => - walkedNodes.push({ node, depth }), +describe('lineColToLocation', () => { + describe("'123456789\\n123...9\\n1...'", () => { + /** + * This source string has the neat property that within it one can + * convert line-column pairs to offsets with the following + * formula: + * + * ``` + * offset = (line - 1) * 10 + col - 1 + * ``` + */ + const source = range(1, 10).map(() => '123456789'); + + const tests: [string, LineColumn, LineColumn, Offsets][] = + cartesianProduct([1, 2, 5], [1, 4, 10], [1, 2, 3], [1, 4, 10]).map( + ([line, column, lineShift, endColumn]) => { + const start = (line - 1) * 10 + column - 1; + const end = (line + lineShift - 1) * 10 + endColumn - 1; + const label = `${String(line)}:${String(column)} to ${String(line + lineShift)}:${String(endColumn)} → ${String(start)} to ${String(end)}`; + return [ + label, + { line, column }, + { line: line + lineShift, column: endColumn }, + { start, end }, + ]; + }, ); - walk(node, mockAction); - expect(mockAction).toHaveBeenCalledTimes(2); // Once for Fragment, once for Text - expect(walkedNodes).toHaveLength(2); - expect(walkedNodes).toEqual([ - { - depth: 0, - node: { - children: [ - { - data: 'Hello', - type: 'Text', - }, - ], - type: 'Fragment', - }, - }, - { - depth: 1, - node: { - data: 'Hello', - type: 'Text', - }, - }, - ]); + describe.each([0, 1])(`%i-based cols`, (colOrigin) => { + it.each(tests)('%s', (_label, start, end, expected) => { + const location = lineColToLocation( + start, + end, + source, + !!colOrigin, + ); + expect(location.start).toEqual(expected.start + +!colOrigin); + expect(location.end).toEqual(expected.end + +!colOrigin); + }); }); }); - describe('stringifyAst', () => { - it('should return a string representation of the Svelte AST', () => { - const ast = { - type: 'Fragment', - children: [{ type: 'Text', data: 'Hello, world!' }], - }; - const result = stringifyAst(ast); - expect(result).toBe('Fragment\n Text (Hello, world!)'); - }); - - it('should abridge long data', () => { - const ast = { - type: 'Fragment', - children: [ - { type: 'Text', data: '123456789012345678901234567890' }, - ], - }; - const result = stringifyAst(ast); - expect(result).toBe('Fragment\n Text (12345678901234567890[...])'); - }); - - it('should accept non-string data (and print "[data]" to represent it)', () => { - const ast = { - type: 'Fragment', - children: [{ type: 'Text', data: {} }], - }; - const result = stringifyAst(ast); - expect(result).toBe('Fragment\n Text ([data])'); - }); - - it("should show a node's name, if there is one", () => { - const ast = { - type: 'Fragment', - name: 'test', - children: [{ type: 'Text', data: {} }], - }; - const result = stringifyAst(ast); - expect(result).toBe('Fragment (test)\n Text ([data])'); - }); - - it("should prioritize a node's name over the data it contains", () => { - expect( - stringifyAst({ - type: 'Fragment', - children: [{ type: 'Text', name: 'test', data: {} }], - }), - ).toBe('Fragment\n Text (test)'); + describe('empty string', () => { + it.each([0, 1])('%i-based cols', (colOrigin) => { + const source = ['']; expect( - stringifyAst({ - type: 'Fragment', - children: [{ type: 'Text', name: 'test', data: 'data' }], - }), - ).toBe('Fragment\n Text (test)'); - }); - }); - - describe('parse', () => { - beforeEach(() => { - vi.restoreAllMocks(); - }); - - it('should parse Svelte content into an AST (v4)', () => { - const content = - '
Hello, world!
\n{#if true}Hello, world!{/if}'; - const ast = parse(content).ast; - expect(ast).toEqual({ - children: [ - { - attributes: [], - children: [ - { - data: 'Hello, world!', - end: 18, - raw: 'Hello, world!', - start: 5, - type: 'Text', - }, - ], - end: 24, - name: 'div', - start: 0, - type: 'Element', - }, - { - data: '\n', - end: 25, - raw: '\n', - start: 24, - type: 'Text', - }, - { - children: [ - { - data: 'Hello, world!', - end: 48, - raw: 'Hello, world!', - start: 35, - type: 'Text', - }, - ], - end: 53, - expression: { - end: 34, - loc: { - end: { - column: 9, - line: 2, - }, - start: { - column: 5, - line: 2, - }, - }, - raw: 'true', - start: 30, - type: 'Literal', - value: true, - }, - start: 25, - type: 'IfBlock', - }, - ], - end: 53, - start: 0, - type: 'Fragment', - }); - }); - - it('should parse Svelte content into an AST (v6)', () => { - vi.mock('$utils/globals.js', async (importOriginal) => { - const actual = await importOriginal(); - if (typeof actual !== 'object') { - throw new Error('test error'); - } - return { - ...actual, - SVELTE_MAJOR_VERSION: 6, - }; - }); - const content = - '
Hello, world!
\n{#if true}Hello, world!{/if}'; - const ast = parse(content); - expect(ast).toBeDefined(); // We currently have Svelte 4 installed, so we can't generate the actual v5 AST (more precisely, even though we're mocking the version, the actual AST will be the same as from the v4 test) - vi.unmock('$utils/globals.js'); - }); - - it('should parse Svelte content into an AST (v100)', () => { - vi.mock( - 'svelte/compiler', - async (importOriginal: () => Promise) => { - return { - ...(await importOriginal()), - VERSION: '100.0.0', - }; - }, - ); - const content = - '
Hello, world!
\n{#if true}Hello, world!{/if}'; - const ast = parse(content); - expect(ast).toBeDefined(); - vi.unmock('svelte/compiler'); + lineColToLocation( + { line: 1, column: colOrigin }, + { line: 1, column: colOrigin }, + source, + !!colOrigin, + ), + ).toEqual({ start: 0, end: 0 }); }); }); }); diff --git a/tests/utils/cache.test.ts b/tests/utils/cache.test.ts index 2bc2cee..3b1510d 100644 --- a/tests/utils/cache.test.ts +++ b/tests/utils/cache.test.ts @@ -1,15 +1,29 @@ -import { suite, describe, it, expect, vi, afterAll } from 'vitest'; +import { + describe, + it, + expect, + vi, + afterAll, + type MockInstance, + beforeAll, +} from 'vitest'; import { spy } from '$tests/fixtures.js'; import { mockFs } from '$dev_deps.js'; import { SveltexCache } from '$utils/cache.js'; -suite('SveltexCache', async () => { +describe('SveltexCache', () => { + let writeFile: MockInstance; + let log: MockInstance; + beforeAll(async () => { + const mocks = await spy(['writeFile', 'log'], true); + writeFile = mocks.writeFile; + log = mocks.log; + }); afterAll(() => { vi.restoreAllMocks(); mockFs.restore(); }); - const { writeFile, log } = await spy(['writeFile', 'log'], true); describe('misc', () => { it('is serializable', async () => { diff --git a/tests/utils/cdn.error.test.ts b/tests/utils/cdn.error.test.ts index 766299e..41bddcc 100644 --- a/tests/utils/cdn.error.test.ts +++ b/tests/utils/cdn.error.test.ts @@ -1,7 +1,6 @@ import { fancyFetch, fetchWithTimeout } from '$utils/cdn.js'; import { spy } from '$tests/fixtures.js'; import { - suite, describe, it, expect, @@ -10,6 +9,7 @@ import { beforeEach, afterEach, beforeAll, + type MockInstance, } from 'vitest'; function fixture() { @@ -21,12 +21,16 @@ function fixture() { }); } -suite.sequential('utils/cdn', async () => { +describe.sequential('utils/cdn', () => { + let log: MockInstance; fixture(); - const { log } = await spy( - ['writeFileEnsureDir', 'log', 'existsSync'], - true, - ); + beforeAll(async () => { + const mocks = await spy( + ['writeFileEnsureDir', 'log', 'existsSync'], + true, + ); + log = mocks.log; + }); afterAll(() => { vi.restoreAllMocks(); vi.unmock('node-fetch'); diff --git a/tests/utils/cdn.test.ts b/tests/utils/cdn.test.ts index 19bc70c..9303a74 100644 --- a/tests/utils/cdn.test.ts +++ b/tests/utils/cdn.test.ts @@ -1,7 +1,6 @@ -import { fetchWithTimeout, getVersion, cdnLink } from '$utils/cdn.js'; +import { fetchWithTimeout, cdnLink } from '$utils/cdn.js'; import { spy } from '$tests/fixtures.js'; import { - suite, describe, it, expect, @@ -9,6 +8,8 @@ import { afterAll, beforeEach, afterEach, + type MockInstance, + beforeAll, } from 'vitest'; function fixture() { @@ -20,12 +21,16 @@ function fixture() { }); } -suite('utils/cdn', async () => { +describe('utils/cdn', () => { + let log: MockInstance; fixture(); - const { log } = await spy( - ['writeFileEnsureDir', 'log', 'existsSync'], - true, - ); + beforeAll(async () => { + const mocks = await spy( + ['writeFileEnsureDir', 'log', 'existsSync'], + true, + ); + log = mocks.log; + }); afterAll(() => { vi.restoreAllMocks(); }); @@ -97,14 +102,4 @@ suite('utils/cdn', async () => { }, ); }); - - describe('getVersion', () => { - fixture(); - it.each(['katex', 'highlight.js', 'mathjax-full'] as const)( - 'getVersion(%o) has format "x.y.z"', - async (dep) => { - expect(await getVersion(dep)).toMatch(/(\d+\.\d+\.\d+)/); - }, - ); - }); }); diff --git a/tests/utils/cli.test.ts b/tests/utils/cli.test.ts index 57751b3..7774025 100644 --- a/tests/utils/cli.test.ts +++ b/tests/utils/cli.test.ts @@ -1,4 +1,5 @@ import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; + import { spawnCliInstruction } from '$utils/cli.js'; describe('spawnCliInstruction', () => { diff --git a/tests/utils/debug.test.ts b/tests/utils/debug.test.ts index aa16fac..8c2c32c 100644 --- a/tests/utils/debug.test.ts +++ b/tests/utils/debug.test.ts @@ -1,23 +1,19 @@ import { mockFs } from '$dev_deps.js'; import { + type MockInstance, afterAll, beforeAll, beforeEach, describe, expect, it, - suite, vi, } from 'vitest'; import { consoles, - detectPackageManager, escapeWhitespace, - getPmFromLockfile, - getPmFromPackageJson, log, - pathExists, prettifyError, runWithSpinner, } from '$utils/debug.js'; @@ -26,7 +22,8 @@ import { spy } from 'tests/fixtures.js'; import { readFileSync } from 'node:fs'; import pc from 'picocolors'; -suite('debug', async () => { +describe('debug', () => { + let existsSync: MockInstance; beforeAll(async () => { vi.spyOn(await import('$deps.js'), 'ora').mockImplementation((() => ({ start: vi.fn().mockReturnValue({ @@ -36,23 +33,22 @@ suite('debug', async () => { fail: vi.fn(), }), })) as unknown as typeof import('ora').default); + const mocks = await spy(['existsSync'], false); + existsSync = mocks.existsSync; + existsSync.mockImplementation((path: string) => { + try { + readFileSync(path); + return true; + } catch { + return false; + } + }); }); afterAll(() => { vi.restoreAllMocks(); mockFs.restore(); }); - const { existsSync } = await spy(['existsSync'], false); - - existsSync.mockImplementation((path: string) => { - try { - readFileSync(path); - return true; - } catch { - return false; - } - }); - describe('log', () => { beforeEach(() => { vi.clearAllMocks(); @@ -187,126 +183,4 @@ suite('debug', async () => { expect(output).toEqual(expectedOutput); }); }); - - describe('pathExists', () => { - it.each([[{ 'exists.txt': '' }, 'exists.txt']])( - 'returns true when the path exists', - (files, path) => { - mockFs(files); - const res = pathExists(path); - expect(existsSync).toHaveBeenCalledTimes(1); - expect(existsSync).toHaveBeenNthCalledWith(1, path); - expect(existsSync).toHaveNthReturnedWith(1, true); - expect(res).toEqual(true); - }, - ); - - it('returns false when the path does not exist', () => { - mockFs({}); - expect(pathExists('does-not-exist')).toEqual(false); - }); - - it('catches errors and silently returns false', () => { - mockFs({}); - existsSync.mockImplementationOnce(() => { - throw new Error(); - }); - expect(pathExists('does-not-exist')).toEqual(false); - }); - }); - - describe('getPmFromPackageJson', () => { - it('identifies a valid package manager (pnpm)', () => { - mockFs({ - project: { - 'package.json': '{ "packageManager": "pnpm@6.14.2"}', - }, - }); - expect(getPmFromPackageJson('project')).toEqual('pnpm'); - }); - - it('identifies a valid package manager (bun)', () => { - mockFs({ - project: { - 'package.json': JSON.stringify({ - packageManager: 'bun@6.14.2', - }), - }, - }); - expect(getPmFromPackageJson('project')).toEqual('bun'); - }); - - it('identifies a valid package manager (yarn)', () => { - mockFs({ - 'package.json': JSON.stringify({ - packageManager: 'yarn@6.14.2', - }), - }); - expect(getPmFromPackageJson()).toEqual('yarn'); - }); - - it('returns undefined for an unrecognized package manager', () => { - mockFs({ - 'project/package.json': JSON.stringify({ - packageManager: 'unknown@1.0.0', - }), - }); - expect(getPmFromPackageJson('project')).toBeUndefined(); - }); - - it('returns undefined if package.json does not have packageManager set', () => { - mockFs({ - 'project/package.json': JSON.stringify({}), - }); - expect(getPmFromPackageJson('project')).toBeUndefined(); - }); - - it('returns undefined if package.json does not exist', () => { - mockFs({}); - expect(getPmFromPackageJson('project')).toBeUndefined(); - }); - }); - - describe('getPmFromLockfile', () => { - it.each([ - { file: 'pnpm-lock.yaml', pm: 'pnpm' }, - { file: 'bun.lockb', pm: 'bun' }, - { file: 'package-lock.json', pm: 'npm' }, - { file: 'yarn.lock', pm: 'yarn' }, - ])('detects $pm from the lockfile', ({ file, pm }) => { - mockFs({ [file]: '' }); - expect(getPmFromLockfile()).toEqual(pm); - }); - - it('returns undefined if no known lock file is found', () => { - mockFs({}); - expect(getPmFromLockfile()).toBeUndefined(); - }); - }); - - describe('detectPackageManager', () => { - it('defaults to npm if no package manager is detected', () => { - mockFs({}); - expect(detectPackageManager('project')).toEqual('npm'); - }); - - it('detects package manager from package.json', () => { - mockFs({ - 'project/package.json': JSON.stringify({ - packageManager: 'yarn@6.14.2', - }), - }); - expect(detectPackageManager('project')).toEqual('yarn'); - }); - - it.each([ - { file: 'pnpm-lock.yaml', pm: 'pnpm' }, - { file: 'bun.lockb', pm: 'bun' }, - { file: 'package-lock.json', pm: 'npm' }, - { file: 'yarn.lock', pm: 'yarn' }, - ])('detects package manager from lockfile', ({ file, pm }) => { - mockFs({ [file]: 'test' }); - expect(detectPackageManager()).toEqual(pm); - }); - }); }); diff --git a/tests/utils/diagnosers/backendChoices.test.ts b/tests/utils/diagnosers/backendChoices.test.ts index 4fd93f5..d2242a7 100644 --- a/tests/utils/diagnosers/backendChoices.test.ts +++ b/tests/utils/diagnosers/backendChoices.test.ts @@ -6,7 +6,17 @@ import type { TexBackend } from '$types/handlers/Tex.js'; import { spy } from '$tests/fixtures.js'; import { diagnoseBackendChoices } from '$utils/diagnosers/backendChoices.js'; -import { suite, it, expect, vi, afterAll, beforeEach, afterEach } from 'vitest'; +import { + it, + expect, + vi, + afterAll, + beforeEach, + afterEach, + type MockInstance, + beforeAll, + describe, +} from 'vitest'; function fixture() { beforeEach(() => { @@ -17,15 +27,19 @@ function fixture() { }); } -suite('utils/diagnosers/backendChoices', async () => { +describe('utils/diagnosers/backendChoices', () => { fixture(); afterAll(() => { vi.restoreAllMocks(); }); - const { log } = await spy( - ['writeFileEnsureDir', 'log', 'existsSync'], - true, - ); + let log: MockInstance; + beforeAll(async () => { + const mocks = await spy( + ['writeFileEnsureDir', 'log', 'existsSync'], + true, + ); + log = mocks.log; + }); it.each([ ['something', 1, 0], diff --git a/tests/utils/diagnosers/codeConfiguration.test.ts b/tests/utils/diagnosers/codeConfiguration.test.ts index 048c488..c006942 100644 --- a/tests/utils/diagnosers/codeConfiguration.test.ts +++ b/tests/utils/diagnosers/codeConfiguration.test.ts @@ -2,7 +2,6 @@ import { spy } from '$tests/fixtures.js'; import { codeBackends, isThemableCodeBackend } from '$type-guards/code.js'; import { diagnoseCodeConfiguration } from '$utils/diagnosers/codeConfiguration.js'; import { - suite, it, expect, vi, @@ -10,6 +9,7 @@ import { beforeEach, afterEach, describe, + beforeAll, } from 'vitest'; function fixture() { @@ -21,12 +21,14 @@ function fixture() { }); } -suite('utils/diagnosers/codeConfiguration', async () => { +describe('utils/diagnosers/codeConfiguration', () => { fixture(); + beforeAll(async () => { + await spy(['writeFileEnsureDir', 'log', 'existsSync'], true); + }); afterAll(() => { vi.restoreAllMocks(); }); - await spy(['writeFileEnsureDir', 'log', 'existsSync'], true); describe.each(codeBackends)('code backend: %o', (b) => { it.each([ diff --git a/tests/utils/diagnosers/diagnosers.test.ts b/tests/utils/diagnosers/diagnosers.test.ts index 88c2f7c..d09ea75 100644 --- a/tests/utils/diagnosers/diagnosers.test.ts +++ b/tests/utils/diagnosers/diagnosers.test.ts @@ -3,7 +3,6 @@ import { spy } from '$tests/fixtures.js'; import { isString } from '$type-guards/utils.js'; import { Diagnoser } from '$utils/diagnosers/Diagnoser.js'; import { - suite, describe, it, expect, @@ -11,6 +10,8 @@ import { afterAll, beforeEach, afterEach, + type MockInstance, + beforeAll, } from 'vitest'; function fixture() { @@ -22,15 +23,19 @@ function fixture() { }); } -suite('utils/diagnosers', async () => { +describe('utils/diagnosers', () => { + let log: MockInstance; fixture(); + beforeAll(async () => { + const mocks = await spy( + ['writeFileEnsureDir', 'log', 'existsSync'], + true, + ); + log = mocks.log; + }); afterAll(() => { vi.restoreAllMocks(); }); - const { log } = await spy( - ['writeFileEnsureDir', 'log', 'existsSync'], - true, - ); describe('Diagnoser', () => { fixture(); diff --git a/tests/utils/dvisvgm.test.ts b/tests/utils/dvisvgm.test.ts index a22f2e7..9fac686 100644 --- a/tests/utils/dvisvgm.test.ts +++ b/tests/utils/dvisvgm.test.ts @@ -1,10 +1,5 @@ -import { describe, it, expect, suite } from 'vitest'; +import { describe, expect, it } from 'vitest'; -import { - bboxToFlagValue, - bitmapFormatToFlagValue, - buildDvisvgmInstruction, -} from '$utils/dvisvgm.js'; import type { BBox, BitmapFormat, @@ -13,360 +8,364 @@ import type { TexDim, } from '$types/utils/DvisvgmOptions.js'; -suite.concurrent('utils/dvisvgm', () => { - describe.concurrent('bboxToFlagValue', () => { - it('should return the string representation of a TexDim', () => { - const bbox: TexDim = [10, 'pt']; - const result = bboxToFlagValue(bbox); - expect(result).toEqual('10pt'); - }); +import { + bboxToFlagValue, + bitmapFormatToFlagValue, + buildDvisvgmInstruction, +} from '$utils/dvisvgm.js'; - it('should return the string representation of a TexDim with unitless value', () => { - const bbox: TexDim = 20; - const result = bboxToFlagValue(bbox); - expect(result).toEqual('20'); - }); +describe.concurrent('bboxToFlagValue', () => { + it('should return the string representation of a TexDim', () => { + const bbox: TexDim = [10, 'pt']; + const result = bboxToFlagValue(bbox); + expect(result).toEqual('10pt'); + }); - it('should return the string representation of a BoundingBox', () => { - const bbox: BoundingBox = { - topLeft: { x: 0, y: 0 }, - bottomRight: { x: 100, y: 100 }, - }; - const result = bboxToFlagValue(bbox); - expect(result).toEqual('0,0,100,100'); - }); + it('should return the string representation of a TexDim with unitless value', () => { + const bbox: TexDim = 20; + const result = bboxToFlagValue(bbox); + expect(result).toEqual('20'); + }); - it('should return the string representation of a PaperSize (A4, landscape)', () => { - const bbox: PaperSize = { - paperSize: 'A4', - orientation: 'landscape', - }; - const result = bboxToFlagValue(bbox); - expect(result).toEqual('A4-landscape'); - }); + it('should return the string representation of a BoundingBox', () => { + const bbox: BoundingBox = { + topLeft: { x: 0, y: 0 }, + bottomRight: { x: 100, y: 100 }, + }; + const result = bboxToFlagValue(bbox); + expect(result).toEqual('0,0,100,100'); + }); - it('should return the string representation of a PaperSize (letter, portrait)', () => { - const bbox: PaperSize = { - paperSize: 'letter', - orientation: 'portrait', - }; - const result = bboxToFlagValue(bbox); - expect(result).toEqual('letter-portrait'); - }); + it('should return the string representation of a PaperSize (A4, landscape)', () => { + const bbox: PaperSize = { + paperSize: 'A4', + orientation: 'landscape', + }; + const result = bboxToFlagValue(bbox); + expect(result).toEqual('A4-landscape'); + }); - it('should return the string representation of a PaperSize (C5)', () => { - const bbox: PaperSize = { - paperSize: 'C5', - }; - const result = bboxToFlagValue(bbox); - expect(result).toEqual('C5'); - }); + it('should return the string representation of a PaperSize (letter, portrait)', () => { + const bbox: PaperSize = { + paperSize: 'letter', + orientation: 'portrait', + }; + const result = bboxToFlagValue(bbox); + expect(result).toEqual('letter-portrait'); + }); - it('should return the input value if it is not a recognized type', () => { - const bbox = 'custom-bbox' as BBox; - const result = bboxToFlagValue(bbox); - expect(result).toEqual('custom-bbox'); - }); + it('should return the string representation of a PaperSize (C5)', () => { + const bbox: PaperSize = { + paperSize: 'C5', + }; + const result = bboxToFlagValue(bbox); + expect(result).toEqual('C5'); }); - describe.concurrent('bitmapFormatToFlagValue', () => { - it('should return the bitmap format as string if it is already a string', () => { - const bitmapFormat = 'png'; - const result = bitmapFormatToFlagValue(bitmapFormat); - expect(result).toEqual(bitmapFormat); - }); + it('should return the input value if it is not a recognized type', () => { + const bbox = 'custom-bbox' as BBox; + const result = bboxToFlagValue(bbox); + expect(result).toEqual('custom-bbox'); + }); +}); - it('should return the bitmap format as string with percentage if it is an array', () => { - const bitmapFormat: BitmapFormat = ['jpeggray', 80]; - const result = bitmapFormatToFlagValue(bitmapFormat); - expect(result).toEqual('jpeggray:80'); - }); +describe.concurrent('bitmapFormatToFlagValue', () => { + it('should return the bitmap format as string if it is already a string', () => { + const bitmapFormat = 'png'; + const result = bitmapFormatToFlagValue(bitmapFormat); + expect(result).toEqual(bitmapFormat); + }); - it('should return the bitmap format as string with 0 percentage if it is less than 0', () => { - const bitmapFormat: BitmapFormat = ['jpeg', -10]; - const result = bitmapFormatToFlagValue(bitmapFormat); - expect(result).toEqual('jpeg:0'); - }); + it('should return the bitmap format as string with percentage if it is an array', () => { + const bitmapFormat: BitmapFormat = ['jpeggray', 80]; + const result = bitmapFormatToFlagValue(bitmapFormat); + expect(result).toEqual('jpeggray:80'); + }); - it('should return the bitmap format as string with 100 percentage if it is greater than 100', () => { - const bitmapFormat: BitmapFormat = ['jpeg', 120]; - const result = bitmapFormatToFlagValue(bitmapFormat); - expect(result).toEqual('jpeg:100'); - }); + it('should return the bitmap format as string with 0 percentage if it is less than 0', () => { + const bitmapFormat: BitmapFormat = ['jpeg', -10]; + const result = bitmapFormatToFlagValue(bitmapFormat); + expect(result).toEqual('jpeg:0'); }); - describe.concurrent('buildDvisvgmInstruction', () => { - it('should build the correct CLI instruction for DVI input', () => { - const instruction = buildDvisvgmInstruction({ - outputPath: '/path/to/output.svg', - texPath: '/path/to/input.tex', - inputType: 'dvi', - dvisvgmOptions: { - processing: { - cache: '/path/to/cache', - exactBbox: true, - keep: true, - mag: 1.2, - noMktexmf: true, - noSpecials: ['bgcolor', 'html'], - traceAll: 'retrace', - }, - svg: { - bbox: { - topLeft: { x: 0, y: 0 }, - bottomRight: { x: 100, y: 100 }, - }, - bitmapFormat: 'png', - clipJoin: true, - comments: true, - currentColor: '#000000', - fontFormat: 'woff2', - gradOverlap: true, - gradSegments: 10, - gradSimplify: 0.5, - linkmark: 'box', - noStyles: true, - optimize: [ - 'collapse-groups', - 'reassign-clippaths', - 'remove-clippaths', - ], - precision: 4, - relative: true, - zip: 5, - }, - console: { - color: true, - message: 'Hello, world!', - progress: 50, - }, - svgTransformations: { - rotate: 90, - scale: [2, 2], - transform: 'matrix(1, 0, 0, 1, 0, 0)', - translate: [10, 10], - zoom: 2, + it('should return the bitmap format as string with 100 percentage if it is greater than 100', () => { + const bitmapFormat: BitmapFormat = ['jpeg', 120]; + const result = bitmapFormatToFlagValue(bitmapFormat); + expect(result).toEqual('jpeg:100'); + }); +}); + +describe.concurrent('buildDvisvgmInstruction', () => { + it('should build the correct CLI instruction for DVI input', () => { + const instruction = buildDvisvgmInstruction({ + outputPath: '/path/to/output.svg', + texPath: '/path/to/input.tex', + inputType: 'dvi', + dvisvgmOptions: { + processing: { + cache: '/path/to/cache', + exactBbox: true, + keep: true, + mag: 1.2, + noMktexmf: true, + noSpecials: ['bgcolor', 'html'], + traceAll: 'retrace', + }, + svg: { + bbox: { + topLeft: { x: 0, y: 0 }, + bottomRight: { x: 100, y: 100 }, }, - customArgs: ['--custom-flag'], + bitmapFormat: 'png', + clipJoin: true, + comments: true, + currentColor: '#000000', + fontFormat: 'woff2', + gradOverlap: true, + gradSegments: 10, + gradSimplify: 0.5, + linkmark: 'box', + noStyles: true, + optimize: [ + 'collapse-groups', + 'reassign-clippaths', + 'remove-clippaths', + ], + precision: 4, + relative: true, + zip: 5, + }, + console: { + color: true, + message: 'Hello, world!', + progress: 50, }, - }); + svgTransformations: { + rotate: 90, + scale: [2, 2], + transform: 'matrix(1, 0, 0, 1, 0, 0)', + translate: [10, 10], + zoom: 2, + }, + customArgs: ['--custom-flag'], + }, + }); - expect(instruction).toEqual({ - command: 'dvisvgm', - args: [ - '--output=/path/to/output.svg', - '--cache=/path/to/cache', - '--exact-bbox', - '--keep', - '--mag=1.2', - '--no-mktexmf', - '--no-specials=bgcolor,html', - '--trace-all=true', - '--bbox=0,0,100,100', - '--bitmap-format=png', - '--clip-join', - '--comments', - '--currentcolor=#000000', - '--embed-bitmaps', - '--font-format=woff2', - '--grad-overlap', - '--grad-segments=10', - '--grad-simplify=0.5', - '--linkmark=box', - '--no-styles', - '--optimize=collapse-groups,reassign-clippaths,remove-clippaths', - '--precision=4', - '--relative', - '--zip=5', - '--color', - '--message=Hello, world!', - '--progress=50', - '--verbosity=3', - '--rotate=90', - '--scale=2,2', - '--transform=matrix(1, 0, 0, 1, 0, 0)', - '--translate=10,10', - '--zoom=2', - '--custom-flag', - '/path/to/input.tex', - ], - }); + expect(instruction).toEqual({ + command: 'dvisvgm', + args: [ + '--output=/path/to/output.svg', + '--cache=/path/to/cache', + '--exact-bbox', + '--keep', + '--mag=1.2', + '--no-mktexmf', + '--no-specials=bgcolor,html', + '--trace-all=true', + '--bbox=0,0,100,100', + '--bitmap-format=png', + '--clip-join', + '--comments', + '--currentcolor=#000000', + '--embed-bitmaps', + '--font-format=woff2', + '--grad-overlap', + '--grad-segments=10', + '--grad-simplify=0.5', + '--linkmark=box', + '--no-styles', + '--optimize=collapse-groups,reassign-clippaths,remove-clippaths', + '--precision=4', + '--relative', + '--zip=5', + '--color', + '--message=Hello, world!', + '--progress=50', + '--verbosity=3', + '--rotate=90', + '--scale=2,2', + '--transform=matrix(1, 0, 0, 1, 0, 0)', + '--translate=10,10', + '--zoom=2', + '--custom-flag', + '/path/to/input.tex', + ], }); + }); - it('should build the correct CLI instruction for PDF input', () => { - const instruction = buildDvisvgmInstruction({ - outputPath: '/path/to/output.svg', - texPath: '/path/to/input.tex', - inputType: 'pdf', - dvisvgmOptions: { - processing: { - cache: '/path/to/cache', - exactBbox: true, - keep: true, - mag: 1.2, - noMktexmf: true, - noSpecials: true, - traceAll: 'retrace', - }, - svg: { - bbox: 'A4', - bitmapFormat: 'png', - clipJoin: true, - comments: true, - currentColor: '#000000', - fontFormat: 'woff2', - gradOverlap: true, - gradSegments: 10, - gradSimplify: 0.5, - linkmark: 'box', - noStyles: true, - optimize: [ - 'collapse-groups', - 'reassign-clippaths', - 'remove-clippaths', - ], - precision: 4, - relative: true, - zip: 5, - }, - console: { - color: true, - message: 'Hello, world!', - progress: 50, - }, - svgTransformations: { - rotate: 90, - scale: [2, 2], - transform: 'matrix(1, 0, 0, 1, 0, 0)', - translate: [10, 10], - zoom: 2, - }, - customArgs: ['--custom-flag'], + it('should build the correct CLI instruction for PDF input', () => { + const instruction = buildDvisvgmInstruction({ + outputPath: '/path/to/output.svg', + texPath: '/path/to/input.tex', + inputType: 'pdf', + dvisvgmOptions: { + processing: { + cache: '/path/to/cache', + exactBbox: true, + keep: true, + mag: 1.2, + noMktexmf: true, + noSpecials: true, + traceAll: 'retrace', + }, + svg: { + bbox: 'A4', + bitmapFormat: 'png', + clipJoin: true, + comments: true, + currentColor: '#000000', + fontFormat: 'woff2', + gradOverlap: true, + gradSegments: 10, + gradSimplify: 0.5, + linkmark: 'box', + noStyles: true, + optimize: [ + 'collapse-groups', + 'reassign-clippaths', + 'remove-clippaths', + ], + precision: 4, + relative: true, + zip: 5, + }, + console: { + color: true, + message: 'Hello, world!', + progress: 50, }, - }); + svgTransformations: { + rotate: 90, + scale: [2, 2], + transform: 'matrix(1, 0, 0, 1, 0, 0)', + translate: [10, 10], + zoom: 2, + }, + customArgs: ['--custom-flag'], + }, + }); - expect(instruction).toEqual({ - command: 'dvisvgm', - args: [ - '--pdf', - '--output=/path/to/output.svg', - '--cache=/path/to/cache', - '--keep', - '--mag=1.2', - '--no-mktexmf', - '--no-specials', - '--trace-all=true', - '--bitmap-format=png', - '--clip-join', - '--comments', - '--currentcolor=#000000', - '--font-format=woff2', - '--grad-overlap', - '--grad-segments=10', - '--grad-simplify=0.5', - '--linkmark=box', - '--no-styles', - '--optimize=collapse-groups,reassign-clippaths,remove-clippaths', - '--precision=4', - '--relative', - '--zip=5', - '--color', - '--message=Hello, world!', - '--progress=50', - '--verbosity=3', - '--rotate=90', - '--scale=2,2', - '--transform=matrix(1, 0, 0, 1, 0, 0)', - '--translate=10,10', - '--zoom=2', - '--custom-flag', - '/path/to/input.tex', - ], - }); + expect(instruction).toEqual({ + command: 'dvisvgm', + args: [ + '--pdf', + '--output=/path/to/output.svg', + '--cache=/path/to/cache', + '--keep', + '--mag=1.2', + '--no-mktexmf', + '--no-specials', + '--trace-all=true', + '--bitmap-format=png', + '--clip-join', + '--comments', + '--currentcolor=#000000', + '--font-format=woff2', + '--grad-overlap', + '--grad-segments=10', + '--grad-simplify=0.5', + '--linkmark=box', + '--no-styles', + '--optimize=collapse-groups,reassign-clippaths,remove-clippaths', + '--precision=4', + '--relative', + '--zip=5', + '--color', + '--message=Hello, world!', + '--progress=50', + '--verbosity=3', + '--rotate=90', + '--scale=2,2', + '--transform=matrix(1, 0, 0, 1, 0, 0)', + '--translate=10,10', + '--zoom=2', + '--custom-flag', + '/path/to/input.tex', + ], }); + }); - it('should build the correct CLI instruction for DVI input 2', () => { - const instruction = buildDvisvgmInstruction({ - outputPath: '/path/to/output.svg', - texPath: '/path/to/input.tex', - inputType: 'dvi', - dvisvgmOptions: { - processing: { - noSpecials: 'html', - traceAll: true, - }, - svg: { - bbox: { - topLeft: { x: [0, 'cc'], y: 0 }, - bottomRight: { x: [100, 'cm'], y: [100, 'bp'] }, - }, - currentColor: true, - zip: true, - }, - customArgs: ['--custom-flag=1', '--custom-flag-two'], - svgTransformations: { - scale: 2, - translate: 3, + it('should build the correct CLI instruction for DVI input 2', () => { + const instruction = buildDvisvgmInstruction({ + outputPath: '/path/to/output.svg', + texPath: '/path/to/input.tex', + inputType: 'dvi', + dvisvgmOptions: { + processing: { + noSpecials: 'html', + traceAll: true, + }, + svg: { + bbox: { + topLeft: { x: [0, 'cc'], y: 0 }, + bottomRight: { x: [100, 'cm'], y: [100, 'bp'] }, }, + currentColor: true, + zip: true, + }, + customArgs: ['--custom-flag=1', '--custom-flag-two'], + svgTransformations: { + scale: 2, + translate: 3, }, - }); + }, + }); - expect(instruction).toEqual({ - command: 'dvisvgm', - args: [ - '--output=/path/to/output.svg', - '--exact-bbox', - '--no-specials=html', - '--trace-all', - '--bbox=0cc,0,100cm,100bp', - '--bitmap-format=png', - '--currentcolor', - '--embed-bitmaps', - '--font-format=woff2', - '--linkmark=none', - '--optimize=all', - '--precision=0', - '--relative', - '--zip', - '--color', - '--progress', - '--verbosity=3', - '--scale=2', - '--translate=3', - '--custom-flag=1', - '--custom-flag-two', - '/path/to/input.tex', - ], - }); + expect(instruction).toEqual({ + command: 'dvisvgm', + args: [ + '--output=/path/to/output.svg', + '--exact-bbox', + '--no-specials=html', + '--trace-all', + '--bbox=0cc,0,100cm,100bp', + '--bitmap-format=png', + '--currentcolor', + '--embed-bitmaps', + '--font-format=woff2', + '--linkmark=none', + '--optimize=all', + '--precision=0', + '--relative', + '--zip', + '--color', + '--progress', + '--verbosity=3', + '--scale=2', + '--translate=3', + '--custom-flag=1', + '--custom-flag-two', + '/path/to/input.tex', + ], }); + }); - it('should build the correct default CLI instruction', () => { - const instruction = buildDvisvgmInstruction({ - outputPath: "'/path/to/output.svg'", - texPath: '/path/to/input.tex', - inputType: 'dvi', - }); + it('should build the correct default CLI instruction', () => { + const instruction = buildDvisvgmInstruction({ + outputPath: "'/path/to/output.svg'", + texPath: '/path/to/input.tex', + inputType: 'dvi', + }); - expect(instruction).toEqual({ - command: 'dvisvgm', - args: [ - "--output='/path/to/output.svg'", - '--exact-bbox', - '--bbox=2pt', - '--bitmap-format=png', - '--currentcolor=#000', - '--embed-bitmaps', - '--font-format=woff2', - '--linkmark=none', - '--optimize=all', - '--precision=0', - '--relative', - '--color', - '--progress', - '--verbosity=3', - '/path/to/input.tex', - ], - }); + expect(instruction).toEqual({ + command: 'dvisvgm', + args: [ + "--output='/path/to/output.svg'", + '--exact-bbox', + '--bbox=2pt', + '--bitmap-format=png', + '--currentcolor=#000', + '--embed-bitmaps', + '--font-format=woff2', + '--linkmark=none', + '--optimize=all', + '--precision=0', + '--relative', + '--color', + '--progress', + '--verbosity=3', + '/path/to/input.tex', + ], }); }); }); diff --git a/tests/utils/env.test.ts b/tests/utils/env.test.ts new file mode 100644 index 0000000..c2fb9aa --- /dev/null +++ b/tests/utils/env.test.ts @@ -0,0 +1,160 @@ +import { + MockInstance, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; + +import { + detectPackageManager, + getPmFromLockfile, + getPmFromPackageJson, + getVersion, +} from '$utils/env.js'; + +import { mockFs } from '$dev_deps.js'; +import { spy } from '$tests/fixtures.js'; +import { readFileSync } from '$deps.js'; + +function fixture() { + beforeEach(() => { + vi.clearAllMocks(); + }); + afterEach(() => { + vi.clearAllMocks(); + }); +} + +let existsSync: MockInstance; +beforeAll(async () => { + vi.spyOn(await import('$deps.js'), 'ora').mockImplementation((() => ({ + start: vi.fn().mockReturnValue({ + stop: vi.fn(), + text: vi.fn(), + succeed: vi.fn(), + fail: vi.fn(), + }), + })) as unknown as typeof import('ora').default); + const mocks = await spy(['existsSync'], false); + existsSync = mocks.existsSync; + existsSync.mockImplementation((path: string) => { + try { + readFileSync(path); + return true; + } catch { + return false; + } + }); +}); + +describe('getVersion', () => { + fixture(); + it.each(['katex', 'highlight.js', 'mathjax-full'] as const)( + 'getVersion(%o) has format "x.y.z"', + async (dep) => { + expect(await getVersion(dep)).toMatch(/(\d+\.\d+\.\d+)/); + }, + ); +}); + +describe('getPmFromPackageJson', () => { + fixture(); + it('identifies a valid package manager (pnpm)', () => { + mockFs({ + project: { + 'package.json': '{ "packageManager": "pnpm@6.14.2"}', + }, + }); + expect(getPmFromPackageJson('project')).toEqual('pnpm'); + }); + + it('identifies a valid package manager (bun)', () => { + mockFs({ + project: { + 'package.json': JSON.stringify({ + packageManager: 'bun@6.14.2', + }), + }, + }); + expect(getPmFromPackageJson('project')).toEqual('bun'); + }); + + it('identifies a valid package manager (yarn)', () => { + mockFs({ + 'package.json': JSON.stringify({ + packageManager: 'yarn@6.14.2', + }), + }); + expect(getPmFromPackageJson()).toEqual('yarn'); + }); + + it('returns undefined for an unrecognized package manager', () => { + mockFs({ + 'project/package.json': JSON.stringify({ + packageManager: 'unknown@1.0.0', + }), + }); + expect(getPmFromPackageJson('project')).toBeUndefined(); + }); + + it('returns undefined if package.json does not have packageManager set', () => { + mockFs({ + 'project/package.json': JSON.stringify({}), + }); + expect(getPmFromPackageJson('project')).toBeUndefined(); + }); + + it('returns undefined if package.json does not exist', () => { + mockFs({}); + expect(getPmFromPackageJson('project')).toBeUndefined(); + }); +}); + +describe('detectPackageManager', () => { + fixture(); + it('defaults to npm if no package manager is detected', () => { + mockFs({}); + expect(detectPackageManager('project')).toEqual('npm'); + }); + + it('detects package manager from package.json', () => { + mockFs({ + 'project/package.json': JSON.stringify({ + packageManager: 'yarn@6.14.2', + }), + }); + expect(detectPackageManager('project')).toEqual('yarn'); + }); + + it.each([ + { file: 'pnpm-lock.yaml', pm: 'pnpm' }, + { file: 'bun.lockb', pm: 'bun' }, + { file: 'package-lock.json', pm: 'npm' }, + { file: 'yarn.lock', pm: 'yarn' }, + ])('detects package manager from lockfile', ({ file, pm }) => { + mockFs({ [file]: 'test' }); + expect(detectPackageManager()).toEqual(pm); + }); +}); + +describe('getPmFromLockfile', () => { + fixture(); + it.each([ + { file: 'pnpm-lock.yaml', pm: 'pnpm' }, + { file: 'bun.lockb', pm: 'bun' }, + { file: 'package-lock.json', pm: 'npm' }, + { file: 'yarn.lock', pm: 'yarn' }, + ])('detects $pm from the lockfile', ({ file, pm }) => { + mockFs({ [file]: '' }); + expect(getPmFromLockfile()).toEqual(pm); + }); + + it('returns undefined if no known lock file is found', () => { + mockFs({}); + expect(getPmFromLockfile()).toBeUndefined(); + }); +}); diff --git a/tests/utils/escape.test.ts b/tests/utils/escape.test.ts index bedfae2..225e798 100644 --- a/tests/utils/escape.test.ts +++ b/tests/utils/escape.test.ts @@ -1,423 +1,561 @@ -/* eslint-disable vitest/no-commented-out-tests */ -import { type Sveltex, sveltex } from '$Sveltex.js'; -import { uuidV4Regexp } from '$tests/utils.js'; import { - escapeBraces, - escapeRegExps, - escapeVerb, - intersection, + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from 'vitest'; +import { spy } from '$tests/fixtures.js'; +import { + colonUuid, + escape, + escapeSnippets, + escapeStringForRegExp, + getColonES, + getMathInSpecialDelimsES, + getMdastES, + getSvelteES, outermostRanges, - unescape, + padString, + parseToMdast, + unescapeSnippets, } from '$utils/escape.js'; -import { describe, expect, it, suite } from 'vitest'; - -suite.concurrent('processor/escape', async () => { - describe.concurrent('escape regexes', () => { - it('should match LaTeX display math', () => { - const input = 'A$$B$$C'; - const expected = ['$$B$$']; - const result = input.match(escapeRegExps.texDisplay); - expect(result).toEqual(expected); - }); - - it('should match inline LaTeX code', () => { - const input = 'Some text $x^2 + y^2 = \\frac{z^2}{2}$ more text'; - const expected = ['$x^2 + y^2 = \\frac{z^2}{2}$']; - const result = input.match(escapeRegExps.texInline); - expect(result).toEqual(expected); - }); - - it('should match fenced code blocks', () => { - const input = '```typescript\nconsole.log("Hello, world!");\n```'; - const expected = [ - '```typescript\nconsole.log("Hello, world!");\n```', - ]; - const result = input.match(escapeRegExps.codeBlock); - expect(result).toEqual(expected); - }); +import { cartesianProduct, range, uuidV4Regexp } from '$tests/utils.js'; +import { typeAssert, is } from '$deps.js'; +import { + EscapableSnippet, + EscapedSnippet, + ProcessedSnippet, + Snippet, + TexEscapeSettings, + UnescapeOptions, +} from '$types/utils/Escape.js'; +import { isArray, isString } from '$type-guards/utils.js'; - it('should match inline code (double backtick)', () => { - const input = - 'Some text ``console.log("Hello, world!");`` more text'; - const expected = ['``console.log("Hello, world!");``']; - const result = input.match(escapeRegExps.codeInlineDoubleBacktick); - expect(result).toEqual(expected); - }); +function fixture() { + beforeEach(() => { + vi.resetAllMocks(); + }); + afterEach(() => { + vi.resetAllMocks(); + }); +} - it('should match inline code (single backtick)', () => { - const input = 'Some text `console.log("Hello, world!");` more text'; - const expected = ['`console.log("Hello, world!");`']; - const result = input.match(escapeRegExps.codeInlineSingleBacktick); - expect(result).toEqual(expected); - }); +describe.concurrent.shuffle('escape()', () => { + fixture(); + beforeAll(async () => { + await spy(['log', 'fancyWrite', 'writeFile']); + }); + afterAll(() => { + vi.restoreAllMocks(); }); - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - const preprocessor1 = (await sveltex()) as Sveltex< - 'none', - 'none', - 'none', - 'none' | 'local' - >; - - const preprocessor2 = (await sveltex({ - advancedTexBackend: 'local', - })) as Sveltex<'none', 'none', 'none', 'none' | 'local'>; - - const preprocessor3 = (await sveltex({ - advancedTexBackend: 'local', - })) as Sveltex<'none', 'none', 'none', 'none' | 'local'>; - - await preprocessor3.configure({ - verbatim: { - verbatimEnvironments: { - Verbatim: { - processInner: { escapeBraces: true, escapeHtml: true }, - }, + describe.concurrent.each([ + [ + 'code (inline)', + [1, 2, 3, 4, 5].map((n) => [ + `a ${'`'.repeat(n)}b${'`'.repeat(n)} c`, + 'a □ c', + [ + { + type: 'code', + processable: { + innerContent: 'b', + optionsForProcessor: { inline: true }, + }, + }, + ], + ]), + ], + [ + 'code (block)', + ( + [[0], [0, '1', '2', 0], [1], [2, undefined, undefined, 1]] as [ + number, + string | undefined, + string | undefined, + number | undefined, + ][] + ).map(([n, lang, info, padding]) => [ + `a${'\n'.repeat(padding ?? 0)}\n\`\`\`${'`'.repeat(n)}${lang ?? ''}${info ? ` ${info}` : ''}\nb\n${'`'.repeat(n)}\`\`\`\n${'\n'.repeat(padding ?? 0)}c`, + `a${'\n'.repeat(padding ? padding - 1 : 0)}\n\n□\n\n${'\n'.repeat(padding ? padding - 1 : 0)}c`, + [ + { + type: 'code', + processable: { + innerContent: 'b', + optionsForProcessor: { inline: false, lang, info }, + }, + } as Partial>, + ], + ]), + ], + ...generateTexTests(), + [ + 'mustacheTag', + [1, 2, 3, 4, 5].map((n) => [ + `a ${'{'.repeat(n)}b${'}'.repeat(n)} c`, + 'a □ c', + [ + { + type: 'mustacheTag', + original: { + outerContent: `${'{'.repeat(n)}b${'}'.repeat(n)}`, + }, + }, + ], + ]), + ], + [ + 'svelte', + [ + 'script', + 'style', + 'svelte:head', + 'svelte:window', + 'svelte:document', + 'svelte:body', + 'svelte:options', + ].map((tag) => [ + `a <${tag}>b c`, + 'a \n\n□\n\n c', + [ + { + type: 'svelte', + original: { + outerContent: `<${tag.replace(':', colonUuid)}>b`, + }, + processable: undefined, + escapeOptions: { pad: 2 }, + unescapeOptions: { removeParagraphTag: true }, + }, + ], + ]), + ], + ] as [ + string, + [ + string, + string, + Partial[], + TexEscapeSettings | undefined, + ][], + ][])('%s', (_label, tests) => { + it.concurrent.each(tests)( + '%o → %o', + (raw, escaped, escapedSnippets, texEscapeSettings) => { + // expect(escape('a')).toEqual({ + // escapedDocument: 'a', + // escapedSnippets: [], + // }); + const res = escape(raw, undefined, texEscapeSettings); + expect(res.escapedDocument).toEqual( + expect.stringMatching( + new RegExp( + escapeStringForRegExp(escaped).replaceAll( + '□', + uuidV4Regexp.source, + ), + ), + ) as unknown, + ); + escapedSnippets.forEach((snip, i) => { + expect(res.escapedSnippets[i]?.[0]).toEqual( + expect.stringMatching(uuidV4Regexp), + ); + expect(res.escapedSnippets[i]?.[1]).toMatchObject(snip); + }); }, - }, - advancedTex: { components: { TeX: {} } }, + ); }); - const preprocessor4 = (await sveltex({ - advancedTexBackend: 'local', - })) as Sveltex<'none', 'none', 'none', 'none' | 'local'>; + describe('edge cases', () => { + fixture(); - await preprocessor4.configure({ - verbatim: { - verbatimEnvironments: { - Verbatim: { - processInner: { escapeBraces: true, escapeHtml: true }, - aliases: ['verbatim'], - }, + it.each([ + [ + '${...}$', + '□', + [ + { + type: 'tex', + processable: { + innerContent: '{...}', + optionsForProcessor: { inline: true }, + }, + escapeOpts: { pad: false }, + unescapeOpts: { removeParagraphTag: false }, + }, + ], + ], + [ + '${$}', + '□}', + [ + { + type: 'tex', + processable: { + innerContent: '{', + optionsForProcessor: { inline: true }, + }, + }, + ], + ], + [ + '{$}$', + '□$', + [ + { + type: 'mustacheTag', + original: { + outerContent: '{$}', + }, + }, + ], + ], + [ + '`{$}$`', + '□', + [ + { + type: 'code', + processable: { + innerContent: '{$}$', + }, + }, + ], + ], + [ + '', + '□', + [ + { + type: 'svelte', + original: { + loc: { + end: 43, + start: 0, + }, + outerContent: + '', + }, + }, + ], + ], + [ + '\n...\n', + '\n\n□\n\n\n...\n\n\n□\n\n', + [ + { + type: 'svelte', + original: { + outerContent: '', + }, + }, + { + type: 'svelte', + original: { + outerContent: '', + }, + }, + ], + ], + [ + 'a $$b$$ c', + 'a \n\n□\n\n c', + [ + { + type: 'tex', + processable: { + innerContent: 'b', + optionsForProcessor: { inline: false }, + }, + }, + ], + ], + ] as [string, string, PartialSnippet[]][])( + '%o → %o', + (str, escaped, snippets) => { + const res = escape(str); + expect(res.escapedDocument).toMatch( + new RegExp( + escapeStringForRegExp(escaped).replaceAll( + '□', + uuidV4Regexp.source, + ), + ), + ); + expect(res.escapedSnippets.length).toEqual(snippets.length); + res.escapedSnippets.forEach((snippet, i) => { + expect(snippet[0]).toEqual( + expect.stringMatching(uuidV4Regexp), + ); + const snip = snippets[i]; + typeAssert(is(snip)); + expect(snippet[1].type).toEqual(snip.type); + if (snip.original?.outerContent) { + expect(snippet[1].original.outerContent).toEqual( + snip.original.outerContent, + ); + } + if (snip.original?.loc) { + expect(snippet[1].original.loc).toEqual( + snip.original.loc, + ); + } + if (snip.processable?.innerContent) { + expect(snippet[1].processable?.innerContent).toEqual( + snip.processable.innerContent, + ); + } + if (snip.processable?.optionsForProcessor) { + expect( + snippet[1].processable?.optionsForProcessor, + ).toEqual(snip.processable.optionsForProcessor); + } + if (snip.unescapeOpts?.removeParagraphTag) { + expect( + snippet[1].unescapeOptions?.removeParagraphTag, + ).toEqual(snip.unescapeOpts.removeParagraphTag); + } + }); }, - }, - advancedTex: { components: { TeX: { aliases: ['tex'] } } }, + ); }); +}); - describe.concurrent.each([ - { preprocessor: preprocessor1, num: 1 }, - { preprocessor: preprocessor2, num: 2 }, - { preprocessor: preprocessor3, num: 3 }, - { preprocessor: preprocessor4, num: 4 }, - ])('escapeVerb()', ({ preprocessor, num }) => { - it('should noop empty content', () => { - const content = ''; - const expected = ''; - const result = escapeVerb(preprocessor, content).escapedContent; - expect(result).toEqual(expected); - }); - - it('should noop plain text', () => { - const content = 'Some text without markup'; - const expected = 'Some text without markup'; - const result = escapeVerb(preprocessor, content).escapedContent; - expect(result).toEqual(expected); - }); - - it('should escape display math 1', () => { - const content = 'a$$b$$c'; - // WARNING: uncommenting the line below will cause the test to fail for - // some reason beyond my understanding. - // expect(escapeRegExps.texDisplay.test(content)).toBe(true); - const result = escapeVerb(preprocessor, content).escapedContent; - // expect(result).toMatch(/^A [a-f0-9-]{36} B$/); - expect(result).toEqual( - `a${(result.match(uuidV4Regexp) ?? [''])[0]}c`, - ); - }); - - it('should escape display math 2', () => { - const content = '$$2 + 2 = 4.$$'; - // WARNING: uncommenting the line below will cause the test to fail for - // some reason beyond my understanding. - // expect(escapeRegExps.texDisplay.test(content)).toBe(true); - const result = escapeVerb(preprocessor, content).escapedContent; - // expect(result).toMatch(/^A [a-f0-9-]{36} B$/); - expect(result).toEqual((result.match(uuidV4Regexp) ?? [''])[0]); - }); - - it('should escape display math 3', () => { - const content = 'A $$\\LaTeX$$ B'; - // expect(escapeRegExps.texDisplay.test(content)).toBe(true); - const result = escapeVerb(preprocessor, content).escapedContent; - // expect(result).toMatch(/^A [a-f0-9-]{36} B$/); - expect(result).toEqual( - `A ${(result.match(uuidV4Regexp) ?? [''])[0]} B`, - ); - }); - - it('should escape inline math', () => { - const content = 'A $\\LaTeX$ B'; - const result = escapeVerb(preprocessor, content).escapedContent; - expect(result).toMatch(/^A [a-f0-9-]{36} B$/); - expect(result).toEqual( - `A ${(result.match(uuidV4Regexp) ?? [''])[0]} B`, - ); - }); - - it('should escape fenced code blocks', () => { - const content = - 'A\n```typescript\nconsole.log("Hello, world!");\n```\nB'; - const result = escapeVerb(preprocessor, content).escapedContent; - expect(result).toMatch(/^A\n[a-f0-9-]{36}\nB$/); - expect(result).toEqual( - `A\n${(result.match(uuidV4Regexp) ?? [''])[0]}\nB`, - ); - }); +interface PartialSnippet { + type: Snippet['type']; + original?: Partial; + processable?: Partial; + unescapeOpts?: Partial; +} - it('should escape inline code (double backtick)', () => { - const content = - 'Some text ``console.log("Hello, world!");`` more text'; - const result = escapeVerb(preprocessor, content).escapedContent; - expect(result).toMatch(/^Some text [a-f0-9-]{36} more text$/); - expect(result).toEqual( - `Some text ${(result.match(uuidV4Regexp) ?? [''])[0]} more text`, - ); - }); +describe.concurrent.shuffle('padString()', () => { + it('should add a newline on both sides by default', () => { + expect(padString('foo')).toEqual('\nfoo\n'); + }); - it('should escape inline code (single backtick)', () => { - const content = - 'Some text `console.log("Hello, world!");` more text'; - const { escapedContent, savedMatches } = escapeVerb( - preprocessor, - content, - ); - expect(escapedContent).toMatch( - /^Some text [a-f0-9-]{36} more text$/, - ); - expect(escapedContent).toEqual( - `Some text ${(escapedContent.match(uuidV4Regexp) ?? [''])[0]} more text`, - ); - expect( - [...savedMatches.values()].includes( - '`console.log("Hello, world!");`', - ), - ).toBe(true); - }); + it('should add a newline on both sides when padInstr is true', () => { + expect(padString('foo', true)).toEqual('\nfoo\n'); + }); - it('should escape verbatim components', () => { - if (num < 3) return; - const content = - 'Some text x^{2} 3 more text'; - const { escapedContent, savedMatches } = escapeVerb( - preprocessor, - content, - ); - expect(escapedContent).toMatch( - /^Some text [a-f0-9-]{36} more text$/, - ); - expect(escapedContent).toEqual( - `Some text ${(escapedContent.match(uuidV4Regexp) ?? [''])[0]} more text`, - ); - expect( - [...savedMatches.values()].includes( - 'x^{2} 3', - ), - ).toBe(true); - }); + it('should add a newline on both sides when padInstr is 1', () => { + expect(padString('foo', 1)).toEqual('\nfoo\n'); + }); - it('should escape verbatim components (aliases)', () => { - if (num < 4) return; - const content = - 'Some text x^{2} 3 more text'; - const { escapedContent, savedMatches } = escapeVerb( - preprocessor, - content, - ); - expect(escapedContent).toMatch( - /^Some text [a-f0-9-]{36} more text$/, - ); - expect(escapedContent).toEqual( - `Some text ${(escapedContent.match(uuidV4Regexp) ?? [''])[0]} more text`, - ); - expect( - [...savedMatches.values()].includes( - 'x^{2} 3', - ), - ).toBe(true); - }); + it('should add 2 newlines on each side when padInstr is 2', () => { + expect(padString('foo', 2)).toEqual('\n\nfoo\n\n'); + }); - it('should escape tex components', () => { - if (num < 3) return; - const content = 'Some text x^{2} < 3 more text'; - const { escapedContent, savedMatches } = escapeVerb( - preprocessor, - content, - ); - expect(escapedContent).toMatch( - /^Some text [a-f0-9-]{36} more text$/, - ); - expect(escapedContent).toEqual( - `Some text ${(escapedContent.match(uuidV4Regexp) ?? [''])[0]} more text`, - ); - expect( - [...savedMatches.values()].includes('x^{2} < 3'), - ).toBe(true); - }); + it('should not add any padding when padInstr is false', () => { + expect(padString('foo', false)).toEqual('foo'); + }); - it('should escape tex components (special characters)', () => { - if (num < 3) return; - const content = - 'Some text \n\\begin{a}}&<>/>>;\n more text'; - const { escapedContent, savedMatches } = escapeVerb( - preprocessor, - content, - ); - expect(escapedContent).toMatch( - /^Some text [a-f0-9-]{36} more text$/, - ); - expect(escapedContent).toEqual( - `Some text ${(escapedContent.match(uuidV4Regexp) ?? [''])[0]} more text`, - ); - expect( - [...savedMatches.values()].includes( - '\n\\begin{a}}&<>/>>;\n', - ), - ).toBe(true); - }); + it('should add the specified string on both sides', () => { + expect(padString('foo', 'bar')).toEqual('barfoobar'); + }); - it('should escape tex components (tikz)', () => { - if (num < 3) return; - const content = - '\n\ntest1 *italic* test2\n\n\n\\begin{tikzpicture}\n\\draw (0,0) circle (3);\n\\draw (0,-1) circle (1.8);\n\\draw[var(--red), thick] (0,0) rectangle (3, 3);\n\\end{tikzpicture}\n\n\ntest3 **bold** test4'; - const { escapedContent, savedMatches } = escapeVerb( - preprocessor, - content, - ); - expect(escapedContent).toEqual( - `\n\ntest1 *italic* test2\n\n${(escapedContent.match(uuidV4Regexp) ?? [''])[0]}\n\ntest3 **bold** test4`, - ); - expect( - [...savedMatches.values()].includes( - '\n\\begin{tikzpicture}\n\\draw (0,0) circle (3);\n\\draw (0,-1) circle (1.8);\n\\draw[var(--red), thick] (0,0) rectangle (3, 3);\n\\end{tikzpicture}\n', - ), - ).toBe(true); - }); + it('should add the specified string on the left side', () => { + expect(padString('foo', ['bar', false])).toEqual('barfoo'); + }); - it('should escape tex components (aliases)', () => { - if (num < 4) return; - const content = 'Some text x^{2} < 3 more text'; - const { escapedContent, savedMatches } = escapeVerb( - preprocessor, - content, - ); - expect(escapedContent).toMatch( - /^Some text [a-f0-9-]{36} more text$/, - ); - expect(escapedContent).toEqual( - `Some text ${(escapedContent.match(uuidV4Regexp) ?? [''])[0]} more text`, - ); - expect( - [...savedMatches.values()].includes('x^{2} < 3'), - ).toBe(true); - }); + it('should add the specified string on the right side', () => { + expect(padString('foo', [false, 'bar'])).toEqual('foobar'); }); - describe.concurrent('unescape()', () => { - it('should unescape content', () => { - expect(unescape('123', new Map([['123', 'something']]))).toEqual( - 'something', - ); - }); + it('should add the specified strings on both sides', () => { + expect(padString('foo', ['bar', 'baz'])).toEqual('barfoobaz'); + }); - it('should remove

tags by default content', () => { - expect( - unescape('

123

', new Map([['123', 'something']])), - ).toEqual('something'); - }); + it('should work with 2-tuples of strings _and_ numbers', () => { + expect(padString('foo', ['bar', 3])).toEqual('barfoo\n\n\n'); + expect(padString('foo', [3, 'bar'])).toEqual('\n\n\nfoobar'); + }); +}); - it('should accept optional removeParagraphTag argument', () => { - expect( - unescape( - '

123

\n

456

', - new Map([ - ['123', 'something'], - ['456', 'something else'], - ]), - (code) => code === 'something', +describe.concurrent.shuffle('escapeSnippets()', () => { + // vi.mock('□', () => { + // return { + // v4: vi.fn().mockReturnValue('□'), + // }; + // }); + // const □Mock = vi.spyOn(await import('□'), 'v4'); + // let c = 1; + // □Mock.mockReturnValue('□' + String(c++)); + // beforeEach(() => { + // vi.resetAllMocks(); + // c = 1; + // }); + // afterAll(() => { + // vi.restoreAllMocks(); + // }); + it.each([ + { + document: 'a b c', + type: 'svelte', + original: { loc: { start: 2, end: 3 } }, + escapeOptions: { pad: false }, + escapedDocument: 'a □ c', + escapedSnippets: [ + [ + '□', + { + type: 'svelte', + original: { loc: { start: 2, end: 3 } }, + }, + ], + ], + }, + { + document: 'a b c', + type: 'svelte', + original: { loc: { start: 2, end: 3 } }, + escapeOptions: { pad: true }, + escapedDocument: 'a \n□\n c', + escapedSnippets: [ + [ + '□', + { + type: 'svelte', + original: { loc: { start: 2, end: 3 } }, + }, + ], + ], + }, + { + document: 'a b c', + type: 'svelte', + original: { loc: { start: 2, end: 3 } }, + escapeOptions: { pad: ['\n\n', '\n\n'] }, + escapedDocument: 'a \n\n□\n\n c', + escapedSnippets: [ + [ + '□', + { + type: 'svelte', + original: { loc: { start: 2, end: 3 } }, + }, + ], + ], + }, + { + document: '\ntest', + type: 'svelte', + original: { loc: { start: 0, end: 20 } }, + escapeOptions: { pad: ['\n\n', '\n\n'] }, + escapedDocument: '\n\n□\n\n\ntest', + escapedSnippets: [ + [ + '□', + { + type: 'svelte', + original: { loc: { start: 0, end: 20 } }, + }, + ], + ], + }, + ] as (ProcessedSnippet & + EscapableSnippet & { + document: string; + escapedDocument: string; + escapedSnippets: [string, EscapedSnippet][]; + })[])('$type: $document → $escapedDocument', (test) => { + expect( + escapeSnippets(test.document, [ + { + type: test.type, + escapeOptions: test.escapeOptions, + original: test.original, + processable: test.processable, + }, + ]), + ).toMatchObject({ + escapedDocument: expect.stringMatching( + new RegExp( + escapeStringForRegExp(test.escapedDocument).replaceAll( + '□', + uuidV4Regexp.source, + ), ), - ).toEqual('something\n

something else

'); + ) as unknown, + escapedSnippets: test.escapedSnippets.map( + (snip) => + [expect.stringMatching(uuidV4Regexp), snip[1]] as [ + string, + EscapedSnippet, + ], + ), }); }); +}); - describe.concurrent('escapeCurlyBraces()', () => { - it('should escape curly braces in the content', () => { - const content = 'Some {text} with {curly} braces'; - const expected = - 'Some {text} with {curly} braces'; - const result = escapeBraces(content); - expect(result).toEqual(expected); - }); - - it('should escape multiple curly braces in the content', () => { - const content = '{{{multiple}}} {{{curly}}} {{{braces}}}'; - const expected = - '{{{multiple}}} {{{curly}}} {{{braces}}}'; - const result = escapeBraces(content); - expect(result).toEqual(expected); - }); - - it('should not escape other characters in the content', () => { - const content = 'Some {text} with other characters: !@#$%^&*()'; - const expected = - 'Some {text} with other characters: !@#$%^&*()'; - const result = escapeBraces(content); - expect(result).toEqual(expected); - }); - - it('should not escape already escaped curly braces in the content', () => { - const content = - 'Some {text} with {escaped} curly braces'; - const expected = - 'Some {text} with {escaped} curly braces'; - const result = escapeBraces(content); - expect(result).toEqual(expected); - }); - - it('should escape "() => {}" properly', () => { - const content = '
() => {}\n
'; - const expected = - '
() => {}\n
'; - const result = escapeBraces(content); - expect(result).toEqual(expected); - }); +describe.concurrent.shuffle('unescapeSnippets()', () => { + describe('removeParagraphTag: true', () => { + const ps: ProcessedSnippet = { + processed: 'b', + unescapeOptions: { removeParagraphTag: true }, + }; + it.each([ + ['a □ c', 'a b c', [['□', ps]]], + ['a

c', 'a b c', [['□', ps]]], + ['a

\n□

c', 'a b c', [['□', ps]]], + ['a

□\n

c', 'a b c', [['□', ps]]], + ['a

\n\t \n□\n\n\n

c', 'a b c', [['□', ps]]], + ['

\nc', 'b\nc', [['□', ps]]], + ] as [string, string, [string, ProcessedSnippet][]][])( + '%o → %o', + (document, unescaped, processedSnippets) => { + expect(unescapeSnippets(document, processedSnippets)).toEqual( + unescaped, + ); + }, + ); }); - describe.concurrent('intersection()', () => { - it('should return null if there is no intersection', () => { - const a = { start: 1, end: 5 }; - const b = { start: 6, end: 10 }; - const result = intersection(a, b); - expect(result).toBeNull(); - }); - - it('should return the intersection if it exists', () => { - const a = { start: 1, end: 10 }; - const b = { start: 5, end: 15 }; - const expected = { start: 5, end: 10 }; - const result = intersection(a, b); - expect(result).toEqual(expected); - }); - - it('should handle overlapping intervals', () => { - const a = { start: 1, end: 10 }; - const b = { start: 5, end: 8 }; - const expected = { start: 5, end: 8 }; - const result = intersection(a, b); - expect(result).toEqual(expected); - }); + describe('removeParagraphTag: false', () => { + const ps: ProcessedSnippet = { + processed: 'b', + unescapeOptions: { removeParagraphTag: false }, + }; + it.each([ + ['a □ c', 'a b c', [['□', ps]]], + ['a

c', 'a

b

c', [['□', ps]]], + ['a

\n□

c', 'a

\nb

c', [['□', ps]]], + ['a

□\n

c', 'a

b\n

c', [['□', ps]]], + [ + 'a

\n\t \n□\n\n\n

c', + 'a

\n\t \nb\n\n\n

c', + [['□', ps]], + ], + ['

\nc', '

b

\nc', [['□', ps]]], + ] as [string, string, [string, ProcessedSnippet][]][])( + '%o → %o', + (document, unescaped, processedSnippets) => { + expect(unescapeSnippets(document, processedSnippets)).toEqual( + unescaped, + ); + }, + ); + }); - it('should handle identical intervals', () => { - const a = { start: 1, end: 10 }; - const b = { start: 1, end: 10 }; - const expected = { start: 1, end: 10 }; - const result = intersection(a, b); - expect(result).toEqual(expected); + describe('behaves gracefully if processed snippet for UUID is undefined', () => { + it.each([['a □ c', 'a □ c', [['□', undefined]]]] as unknown as [ + string, + string, + [string, ProcessedSnippet][], + ][])('%o → %o', (document, unescaped, processedSnippets) => { + expect(unescapeSnippets(document, processedSnippets)).toEqual( + unescaped, + ); }); }); }); -describe('outermostRanges()', () => { + +describe.concurrent.shuffle('outermostRanges()', () => { it('should return the outermost ranges', () => { const ranges = [ { start: 0, end: 100 }, // outermost @@ -461,3 +599,903 @@ describe('outermostRanges()', () => { expect(result).toEqual(expected); }); }); + +describe.concurrent.shuffle('getSvelteES()', () => { + describe.each([ + [ + 'normal', + [ + ...[ + 'script', + 'style', + 'svelte:head', + 'svelte:window', + 'svelte:document', + 'svelte:body', + 'svelte:options', + ].map( + (tag) => + [ + `a<${tag}>...b`, + `<${tag}>...`, + [ + { + escapeOptions: { pad: 2 }, + original: { + loc: { + start: 1, + end: + 9 + + 2 * + tag.replace(':', colonUuid) + .length, + }, + outerContent: `<${tag.replace(':', colonUuid)}>...`, + }, + processable: undefined, + type: 'svelte', + unescapeOptions: { + removeParagraphTag: true, + }, + }, + ], + ] as [string, string, EscapableSnippet[]], + ), + ], + ], + [ + 'self-closing', + [ + ['a