diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..dbfec435
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,37 @@
+name: Test
+
+on:
+ push:
+ branches: [main]
+
+ pull_request:
+ branches: [main]
+
+jobs:
+ unit-test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: pnpm
+
+ - name: Install deps
+ run: pnpm install --frozen-lockfile
+
+ - name: Unit Test
+ env:
+ NODE_OPTIONS: --max_old_space_size=8192
+ run: pnpm run test --run
+
+ - name: Upload coverage
+ if: github.ref == 'refs/heads/main'
+ uses: codecov/codecov-action@v4
diff --git a/.gitignore b/.gitignore
index aaf239c5..f898f04e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,5 @@ dist/
*.log
*.tsbuildinfo
.mind
+
+coverage/
diff --git a/package.json b/package.json
index c3f955a3..56c7460c 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
"lint:fix": "pnpm lint:check --fix && pnpm lint:css --fix",
"lint:check": "eslint .",
"lint:css": "stylelint **/*.{css,vue}",
+ "test": "cross-env TZ=Etc/UTC vitest --coverage",
"prepare": "husky",
"release": "pnpm release:check && pnpm release:version && pnpm -r publish",
"release:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
@@ -45,19 +46,23 @@
"@types/lodash.merge": "^4.6.9",
"@types/node": "20.12.10",
"@types/webpack-env": "^1.18.5",
+ "@vitest/coverage-istanbul": "^2.1.2",
"bumpp": "^9.6.1",
"commitizen": "^4.3.1",
"conventional-changelog-cli": "^5.0.0",
"cpx2": "^8.0.0",
+ "cross-env": "7.0.3",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^9.12.0",
"husky": "^9.1.6",
"lint-staged": "^15.2.10",
+ "markdown-it": "^14.1.0",
"rimraf": "^6.0.1",
"stylelint": "^16.9.0",
"tsconfig-vuepress": "^5.2.0",
"tsup": "^8.3.0",
"typescript": "^5.6.2",
+ "vitest": "^2.1.2",
"wait-on": "^8.0.1"
},
"resolutions": {
@@ -65,7 +70,8 @@
},
"lint-staged": {
"*": "eslint --fix",
- "*.{css,vue}": "stylelint --fix"
+ "*.{css,vue}": "stylelint --fix",
+ "*.{js,ts,mjs,cjs}": "cross-env TZ=Etc/UTC vitest related --run"
},
"config": {
"commitizen": {
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/bilibiliPlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/bilibiliPlugin.spec.ts.snap
new file mode 100644
index 00000000..d76585fc
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/bilibiliPlugin.spec.ts.snap
@@ -0,0 +1,10 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`bilibiliPlugin > should not work 1`] = `
+"
@[bilibili]xxx
+@[ bilibili]BV12345
+@[bilibili](BV12345
+"
+`;
+
+exports[`bilibiliPlugin > should work 1`] = `" "`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/caniusePlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/caniusePlugin.spec.ts.snap
new file mode 100644
index 00000000..46e510e3
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/caniusePlugin.spec.ts.snap
@@ -0,0 +1,90 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`caniusePlugin > should not work 1`] = `""`;
+
+exports[`caniusePlugin > should not work 2`] = `
+"@[caniuse]xxx
+"
+`;
+
+exports[`caniusePlugin > should not work 3`] = `
+"@ caniuse
+"
+`;
+
+exports[`caniusePlugin > should not work 4`] = `
+"@[caniuse](xxx
+"
+`;
+
+exports[`caniusePlugin > should not work 5`] = `
+"@[caniuse](
+"
+`;
+
+exports[`caniusePlugin > should not work 6`] = `
+"@[caniuse
+"
+`;
+
+exports[`caniusePlugin > should not work 7`] = `
+"@caniuse)
+"
+`;
+
+exports[`caniusePlugin > should not work 8`] = `
+"@caniuseee)
+"
+`;
+
+exports[`caniusePlugin > should work 1`] = `" "`;
+
+exports[`caniusePlugin > should work 2`] = `
+"
+
+
+
+
"
+`;
+
+exports[`caniusePlugin > should work 3`] = `" "`;
+
+exports[`caniusePlugin > should work 4`] = `" "`;
+
+exports[`caniusePlugin > should work 5`] = `" "`;
+
+exports[`caniusePlugin > should work 6`] = `" "`;
+
+exports[`caniusePlugin > should work with options 1`] = `
+"
+
+
+
+
"
+`;
+
+exports[`caniusePlugin > should work with options 2`] = `" "`;
+
+exports[`caniusePlugin > should work with options 3`] = `" "`;
+
+exports[`legacyCaniuse > should work 1`] = `" "`;
+
+exports[`legacyCaniuse > should work 2`] = `" "`;
+
+exports[`legacyCaniuse > should work 3`] = `""`;
+
+exports[`legacyCaniuse > should work with unknown mode 1`] = `
+"
+
+
+
+
"
+`;
+
+exports[`legacyCaniuse > should work with unknown mode 2`] = `
+"
+
+
+
+
"
+`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/codeSandboxPlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/codeSandboxPlugin.spec.ts.snap
new file mode 100644
index 00000000..71e9aaee
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/codeSandboxPlugin.spec.ts.snap
@@ -0,0 +1,9 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`codeSandboxPlugin > should not work 1`] = `
+"@[codesandbox]xxx
+@[codesandbox embed](
+"
+`;
+
+exports[`codeSandboxPlugin > should work 1`] = `" "`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/codeTabsPlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/codeTabsPlugin.spec.ts.snap
new file mode 100644
index 00000000..fb07abcd
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/codeTabsPlugin.spec.ts.snap
@@ -0,0 +1,81 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`codeTabsPlugin > should work with default 1`] = `
+"tab-1 tab-2 tab-3
+
+
+npm pnpm yarn
+
+
+a.ts a.js
+
+ "
+`;
+
+exports[`codeTabsPlugin > should work with no icon 1`] = `
+"tab-1 tab-2 tab-3
+
+
+npm pnpm yarn
+
+
+a.ts a.js
+
+ "
+`;
+
+exports[`codeTabsPlugin > should work with options: \`{ named: false, extensions: false }\` 1`] = `
+"tab-1 tab-2 tab-3
+
+
+npm pnpm yarn
+
+
+a.ts a.js
+
+ "
+`;
+
+exports[`codeTabsPlugin > should work with options: { named: [npm,pnpm,yarn], extensions: [.js,.ts] } 1`] = `
+"tab-1 tab-2 tab-3
+
+
+npm pnpm yarn
+
+
+a.ts a.js
+
+ "
+`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/codepenPlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/codepenPlugin.spec.ts.snap
new file mode 100644
index 00000000..a8832100
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/codepenPlugin.spec.ts.snap
@@ -0,0 +1,9 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`codepenPlugin > should not work 1`] = `
+"@[codepen]xxx
+@[codepen preview](
+"
+`;
+
+exports[`codepenPlugin > should work 1`] = `""`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/fileTreePlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/fileTreePlugin.spec.ts.snap
new file mode 100644
index 00000000..b39ac60a
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/fileTreePlugin.spec.ts.snap
@@ -0,0 +1,66 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`fileTreePlugin > should work with default options 1`] = `
+"
+docs
+
+
+src
+
+client
+
+components
+
+
+index.ts
+
+
+node
+
+
+
+
+.gitignore
+package.json
+
+
+docs
+src
+
+
+README.md
+
+
"
+`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/iconsPlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/iconsPlugin.spec.ts.snap
new file mode 100644
index 00000000..d9a16e9e
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/iconsPlugin.spec.ts.snap
@@ -0,0 +1,108 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`iconsPlugin > should not work with invalid icon 1`] = `
+":[ mdi:11 ]:
+"
+`;
+
+exports[`iconsPlugin > should not work with invalid icon 2`] = `
+":[]:
+"
+`;
+
+exports[`iconsPlugin > should not work with invalid icon 3`] = `
+":[]&
+"
+`;
+
+exports[`iconsPlugin > should not work with invalid icon 4`] = `
+":[:[
+"
+`;
+
+exports[`iconsPlugin > should not work with invalid icon 5`] = `
+":[mdi:11
+"
+`;
+
+exports[`iconsPlugin > should work 1`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work 2`] = `
+"strong
+"
+`;
+
+exports[`iconsPlugin > should work 3`] = `
+"strong
+
+
+"
+`;
+
+exports[`iconsPlugin > should work with options 1`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with options 2`] = `
+"strong
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 1`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 2`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 3`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 4`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 5`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 6`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 7`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 8`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 9`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 10`] = `
+"
+"
+`;
+
+exports[`iconsPlugin > should work with single icon options 11`] = `
+"
+"
+`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/jsfiddlePlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/jsfiddlePlugin.spec.ts.snap
new file mode 100644
index 00000000..528bb0a7
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/jsfiddlePlugin.spec.ts.snap
@@ -0,0 +1,9 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`codeSandboxPlugin > should not work 1`] = `
+"@[jsfiddle]xxx
+@[jsfiddle](
+"
+`;
+
+exports[`codeSandboxPlugin > should work 1`] = `""`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/npmToPlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/npmToPlugin.spec.ts.snap
new file mode 100644
index 00000000..33523420
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/npmToPlugin.spec.ts.snap
@@ -0,0 +1,361 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`npmToPlugin > should work width options: [npm, yarn, pnpm] 1`] = `
+"npm yarn pnpm
+
+
+npm yarn pnpm
+
+
+npm yarn pnpm
+
+
+npm yarn pnpm
+
+
+npm yarn pnpm cross-env NODE_ENV=production npm run docs
+
+
+cross-env NODE_ENV=production yarn docs
+
+
+cross-env NODE_ENV=production pnpm docs
+
+
+npm yarn pnpm npm i -D package1 package2
+npm i --save-peer package3
+npm run docs
+
+
+yarn add -D package1package2
+yarn add --peer package3
+yarn docs
+
+
+pnpm add -D package1package2
+pnpm add --save-peer package3
+pnpm docs
+
+
+npm yarn pnpm npm install && npm run docs
+mkdir foo
+
+
+yarn && yarn docs
+mkdir foo
+
+
+pnpm install && pnpm docs
+mkdir foo
+
+
+npm yarn pnpm npm run docs -- --clean-cache --clean-temp
+
+
+
+yarn docs --clean-cache --clean-temp
+
+
+
+pnpm docs --clean-cache --clean-temp
+
+
+
+npm pnpm yarn npm create vuepress-theme-plume@latest
+
+
+pnpm create vuepress-theme-plume@latest
+
+
+yarn create vuepress-theme-plume@latest
+
+
+npm pnpm yarn bun deno
+
+
+
+deno run -A vp-update
+
+
+npm yarn pnpm
+
+
+anything
+
+"
+`;
+
+exports[`npmToPlugin > should work width options: { tabs: [npm, yarn, pnpm] } 1`] = `
+"npm yarn pnpm
+
+
+npm yarn pnpm
+
+
+npm yarn pnpm
+
+
+npm yarn pnpm
+
+
+npm yarn pnpm cross-env NODE_ENV=production npm run docs
+
+
+cross-env NODE_ENV=production yarn docs
+
+
+cross-env NODE_ENV=production pnpm docs
+
+
+npm yarn pnpm npm i -D package1 package2
+npm i --save-peer package3
+npm run docs
+
+
+yarn add -D package1package2
+yarn add --peer package3
+yarn docs
+
+
+pnpm add -D package1package2
+pnpm add --save-peer package3
+pnpm docs
+
+
+npm yarn pnpm npm install && npm run docs
+mkdir foo
+
+
+yarn && yarn docs
+mkdir foo
+
+
+pnpm install && pnpm docs
+mkdir foo
+
+
+npm yarn pnpm npm run docs -- --clean-cache --clean-temp
+
+
+
+yarn docs --clean-cache --clean-temp
+
+
+
+pnpm docs --clean-cache --clean-temp
+
+
+
+npm pnpm yarn npm create vuepress-theme-plume@latest
+
+
+pnpm create vuepress-theme-plume@latest
+
+
+yarn create vuepress-theme-plume@latest
+
+
+npm pnpm yarn bun deno
+
+
+
+deno run -A vp-update
+
+
+npm yarn pnpm
+
+
+anything
+
+"
+`;
+
+exports[`npmToPlugin > should work with default options 1`] = `
+"npm pnpm yarn
+
+
+npm pnpm yarn
+
+
+npm pnpm yarn
+
+
+npm pnpm yarn
+
+
+npm pnpm yarn cross-env NODE_ENV=production npm run docs
+
+
+cross-env NODE_ENV=production pnpm docs
+
+
+cross-env NODE_ENV=production yarn docs
+
+
+npm pnpm yarn npm i -D package1 package2
+npm i --save-peer package3
+npm run docs
+
+
+pnpm add -D package1package2
+pnpm add --save-peer package3
+pnpm docs
+
+
+yarn add -D package1package2
+yarn add --peer package3
+yarn docs
+
+
+npm pnpm yarn npm install && npm run docs
+mkdir foo
+
+
+pnpm install && pnpm docs
+mkdir foo
+
+
+yarn && yarn docs
+mkdir foo
+
+
+npm pnpm yarn npm run docs -- --clean-cache --clean-temp
+
+
+
+pnpm docs --clean-cache --clean-temp
+
+
+
+yarn docs --clean-cache --clean-temp
+
+
+
+npm pnpm yarn npm create vuepress-theme-plume@latest
+
+
+pnpm create vuepress-theme-plume@latest
+
+
+yarn create vuepress-theme-plume@latest
+
+
+npm pnpm yarn bun deno
+
+
+
+deno run -A vp-update
+
+
+npm pnpm yarn
+
+
+anything
+
+"
+`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/pdfPlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/pdfPlugin.spec.ts.snap
new file mode 100644
index 00000000..46b628a5
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/pdfPlugin.spec.ts.snap
@@ -0,0 +1,12 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`pdfPlugin > should not work 1`] = `
+"@[pdf]xxx
+@ pdf
+@[pdf]
+"
+`;
+
+exports[`pdfPlugin > should work 1`] = `" "`;
+
+exports[`pdfPlugin > should work 2`] = `" "`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/plotPlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/plotPlugin.spec.ts.snap
new file mode 100644
index 00000000..0a94af09
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/plotPlugin.spec.ts.snap
@@ -0,0 +1,38 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`plotPlugin > should not work with invalid tag 1`] = `
+"!!!!
+"
+`;
+
+exports[`plotPlugin > should not work with invalid tag 2`] = `
+"!!plot!
+"
+`;
+
+exports[`plotPlugin > should not work with invalid tag 3`] = `
+"!!!
+"
+`;
+
+exports[`plotPlugin > should not work with invalid tag 4`] = `
+"!! plot !!
+"
+`;
+
+exports[`plotPlugin > should work 1`] = `
+"plot
+"
+`;
+
+exports[`plotPlugin > should work 2`] = `
+"hidden: plot strong
+"
+`;
+
+exports[`plotPlugin > should work 3`] = `
+"hidden:
+plot
+plot
+"
+`;
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/youtubePlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/youtubePlugin.spec.ts.snap
new file mode 100644
index 00000000..e181a063
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/youtubePlugin.spec.ts.snap
@@ -0,0 +1,10 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`youtubePlugin > should not work 1`] = `
+"@[youtube]xxx
+@[ youtube]123456
+@ youtube
+"
+`;
+
+exports[`youtubePlugin > should work 1`] = `" "`;
diff --git a/plugins/plugin-md-power/__test__/alignPlugin.spec.ts b/plugins/plugin-md-power/__test__/alignPlugin.spec.ts
new file mode 100644
index 00000000..0b4ae114
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/alignPlugin.spec.ts
@@ -0,0 +1,13 @@
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { alignPlugin } from '../src/node/container/align.js'
+
+describe('alignPlugin', () => {
+ const md = new MarkdownIt().use(alignPlugin)
+ it('should work', () => {
+ expect(md.render(':::left\n:::')).toContain('style="text-align:left"')
+ expect(md.render(':::center\n:::')).toContain('style="text-align:center"')
+ expect(md.render(':::right\n:::')).toContain('style="text-align:right"')
+ expect(md.render(':::justify\n:::')).toContain('style="text-align:justify"')
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/bilibiliPlugin.spec.ts b/plugins/plugin-md-power/__test__/bilibiliPlugin.spec.ts
new file mode 100644
index 00000000..db602f9e
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/bilibiliPlugin.spec.ts
@@ -0,0 +1,44 @@
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { bilibiliPlugin } from '../src/node/embed/video/bilibili.js'
+
+function createMarkdown() {
+ return MarkdownIt().use((md) => {
+ md.block.ruler.before('code', 'import_code', () => false)
+ md.renderer.rules.import_code = () => ''
+ }).use(bilibiliPlugin)
+}
+
+describe('bilibiliPlugin', () => {
+ it('should work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[bilibili](BV12345)
+
+@[bilibili](12432 12345)
+
+@[bilibili](BV12345 12343 45678)
+
+@[bilibili p1 autoplay time="1"](BV12345)
+
+@[bilibili p1 autoplay width="100%" height="600px"](BV12345)
+
+@[bilibili p1 autoplay width="100%" ratio="16:9"](BV12345)
+`
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should not work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[bilibili]()
+
+@[bilibili]xxx
+
+@[ bilibili]BV12345
+
+@[bilibili](BV12345
+`
+ expect(md.render(code)).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/caniusePlugin.spec.ts b/plugins/plugin-md-power/__test__/caniusePlugin.spec.ts
new file mode 100644
index 00000000..29381195
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/caniusePlugin.spec.ts
@@ -0,0 +1,73 @@
+import type { CanIUseOptions } from '../src/shared/index.js'
+import MarkdownIt from 'markdown-it'
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+import { caniusePlugin, legacyCaniuse } from '../src/node/embed/caniuse.js'
+
+beforeEach(() => {
+ vi.mock('../src/node/utils/nanoid.js', () => ({
+ nanoid: vi.fn(() => 'test-id'),
+ }))
+})
+
+function createMarkdown(options?: CanIUseOptions) {
+ return MarkdownIt().use((md) => {
+ md.block.ruler.before('code', 'import_code', () => false)
+ md.renderer.rules.import_code = () => ''
+ }).use(caniusePlugin, options)
+}
+
+describe('caniusePlugin', () => {
+ it('should work', () => {
+ const md = createMarkdown()
+
+ expect(md.render('@[caniuse](feature)')).toMatchSnapshot()
+ expect(md.render('@[caniuse image](feature)')).toMatchSnapshot()
+ expect(md.render('@[caniuse embed](feature)')).toMatchSnapshot()
+ expect(md.render('@[caniuse {-2,4}](feature)')).toMatchSnapshot()
+ expect(md.render(`\
+@[caniuse {-2,4}](feature)
+@[caniuse {-2,}](feature)
+`)).toMatchSnapshot()
+ expect(md.render('@[caniuse embed{-2,4}](feature)')).toMatchSnapshot()
+ })
+
+ it('should work with options', () => {
+ const md = createMarkdown({ mode: 'image' })
+
+ expect(md.render('@[caniuse](feature)')).toMatchSnapshot()
+ expect(md.render('@[caniuse embed](feature)')).toMatchSnapshot()
+ expect(md.render('@[caniuse embed{-2,4}](feature)')).toMatchSnapshot()
+ })
+
+ it('should not work', () => {
+ const md = createMarkdown()
+
+ expect(md.render('@[caniuse]()')).toMatchSnapshot()
+ expect(md.render('@[caniuse]xxx')).toMatchSnapshot()
+ expect(md.render('@[ caniuse]()')).toMatchSnapshot()
+ expect(md.render('@[caniuse](xxx')).toMatchSnapshot()
+ expect(md.render('@[caniuse](')).toMatchSnapshot()
+ expect(md.render('@[caniuse')).toMatchSnapshot()
+ expect(md.render('@[caniuse)]()')).toMatchSnapshot()
+ expect(md.render('@[caniuseee)]()')).toMatchSnapshot()
+ })
+})
+
+describe('legacyCaniuse', () => {
+ it('should work', () => {
+ const md = MarkdownIt()
+ legacyCaniuse(md)
+
+ expect(md.render(':::caniuse feature\n:::')).toMatchSnapshot()
+ expect(md.render(':::caniuse feature{-2,4}\n:::')).toMatchSnapshot()
+ expect(md.render(':::caniuse\n:::')).toMatchSnapshot()
+ })
+
+ it('should work with unknown mode', () => {
+ const md = MarkdownIt()
+ legacyCaniuse(md, { mode: 'unknown' as any })
+
+ expect(md.render(':::caniuse feature\n:::')).toMatchSnapshot()
+ expect(md.render(':::caniuse feature{-2,4}\n:::')).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/codeSandboxPlugin.spec.ts b/plugins/plugin-md-power/__test__/codeSandboxPlugin.spec.ts
new file mode 100644
index 00000000..975af8f5
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/codeSandboxPlugin.spec.ts
@@ -0,0 +1,40 @@
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { codeSandboxPlugin } from '../src/node/embed/code/codeSandbox.js'
+
+function createMarkdown() {
+ return MarkdownIt().use((md) => {
+ md.block.ruler.before('code', 'import_code', () => false)
+ md.renderer.rules.import_code = () => ''
+ }).use(codeSandboxPlugin)
+}
+
+describe('codeSandboxPlugin', () => {
+ it('should work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[codesandbox](user/id)
+
+@[codesandbox embed](user/id)
+
+@[codesandbox button](user/id)
+
+@[codesandbox title="xxx" layout="Editor+Preview" width="100%" height="500px" navbar="false" console="false"](user/slash#filepath)
+`
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should not work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[codesandbox]()
+
+@[codesandbox]xxx
+
+@[codesandbox embed](
+`
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/codeTabsPlugin.spec.ts b/plugins/plugin-md-power/__test__/codeTabsPlugin.spec.ts
new file mode 100644
index 00000000..d05caf54
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/codeTabsPlugin.spec.ts
@@ -0,0 +1,79 @@
+import type { CodeTabsOptions } from '../src/shared/codeTabs.js'
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { codeTabs } from '../src/node/container/codeTabs.js'
+
+function createMarkdown(options?: CodeTabsOptions) {
+ const md = new MarkdownIt()
+ md.options.highlight = (str, lang) => {
+ return ``
+ }
+ md.use(codeTabs, options)
+ return md
+}
+
+const FENCE = '```'
+
+describe('codeTabsPlugin', () => {
+ const code = `\
+::: code-tabs
+@tab tab-1
+${FENCE}js
+const a = 1
+${FENCE}
+@tab tab-2
+${FENCE}js
+const b = 2
+${FENCE}
+@tab tab-3
+${FENCE}js
+const c = 3
+${FENCE}
+:::
+
+::: code-tabs
+@tab npm
+${FENCE}sh
+npm i
+${FENCE}
+@tab:active pnpm
+${FENCE}sh
+pnpm i
+${FENCE}
+@tab yarn
+${FENCE}sh
+yarn
+${FENCE}
+:::
+
+::: code-tabs#pm
+@tab a.ts
+${FENCE}ts
+const a = 1
+${FENCE}
+@tab a.js
+${FENCE}js
+const a = 1
+${FENCE}
+:::
+`
+ it('should work with default', () => {
+ const md = createMarkdown()
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should work with no icon', () => {
+ const md = createMarkdown({ icon: false })
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should work with options: `{ named: false, extensions: false }`', () => {
+ const md = createMarkdown({ icon: { named: false, extensions: false } })
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should work with options: { named: [npm,pnpm,yarn], extensions: [.js,.ts] }', () => {
+ const md = createMarkdown({ icon: { named: ['npm', 'pnpm', 'yarn'], extensions: ['.js', '.ts'] } })
+ expect(md.render(code)).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/codepenPlugin.spec.ts b/plugins/plugin-md-power/__test__/codepenPlugin.spec.ts
new file mode 100644
index 00000000..cf2e4ae3
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/codepenPlugin.spec.ts
@@ -0,0 +1,38 @@
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { codepenPlugin } from '../src/node/embed/code/codepen.js'
+
+function createMarkdown() {
+ return MarkdownIt().use((md) => {
+ md.block.ruler.before('code', 'import_code', () => false)
+ md.renderer.rules.import_code = () => ''
+ }).use(codepenPlugin)
+}
+
+describe('codepenPlugin', () => {
+ it('should work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[codepen](user/slash)
+
+@[codepen preview](user/slash)
+
+@[codepen preview editable title="codepen" width="100%" height="400px" tab="css,result" theme="dark"](user/slash)
+`
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should not work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[codepen]()
+
+@[codepen]xxx
+
+@[codepen preview](
+`
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/fileTreePlugin.spec.ts b/plugins/plugin-md-power/__test__/fileTreePlugin.spec.ts
new file mode 100644
index 00000000..7ba6a00c
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/fileTreePlugin.spec.ts
@@ -0,0 +1,58 @@
+import type { FileTreeOptions } from '../src/shared/fileTree.js'
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { fileTreePlugin } from '../src/node/container/fileTree.js'
+
+function createMarkdown(options?: FileTreeOptions) {
+ return new MarkdownIt().use(fileTreePlugin, options)
+}
+
+describe('fileTreePlugin', () => {
+ const code = `\
+:::file-tree
+- docs
+ - README.md
+ - foo.md
+- src
+ - client
+ - components
+ - **Navbar.vue**
+ - index.ts \# comment
+ - node
+ - index.ts
+- .gitignore
+- package.json
+:::
+
+::: file-tree title="files"
+- src
+ - js
+ - …
+ - vue/
+ - css/
+- README.md
+:::
+
+::: file-tree icon="simple"
+- docs
+- src
+ - a.js
+ - b.ts
+- README.md
+:::
+
+::: file-tree
+-
+-
+ -
+:::
+
+::: file-tree
+:::
+`
+ it('should work with default options', () => {
+ const md = createMarkdown()
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/findIcon.spec.ts b/plugins/plugin-md-power/__test__/findIcon.spec.ts
new file mode 100644
index 00000000..3053b7a9
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/findIcon.spec.ts
@@ -0,0 +1,48 @@
+import { describe, expect, it } from 'vitest'
+import { getFileIcon } from '../src/node/fileIcons/index.js'
+
+describe('getFileIcon(filename, type)', () => {
+ it('should find icon with default', () => {
+ expect(getFileIcon('a.js')).toBe('vscode-icons:file-type-js')
+ expect(getFileIcon('a.cjs')).toBe('vscode-icons:file-type-js')
+ expect(getFileIcon('a.mjs')).toBe('vscode-icons:file-type-js')
+ expect(getFileIcon('a.jsx')).toBe('vscode-icons:file-type-reactjs')
+ expect(getFileIcon('a.ts')).toBe('vscode-icons:file-type-typescript')
+ expect(getFileIcon('a.d.ts')).toBe('vscode-icons:file-type-typescript')
+ expect(getFileIcon('a.cjs.map')).toBe('vscode-icons:file-type-jsmap')
+ expect(getFileIcon('a.mjs.map')).toBe('vscode-icons:file-type-jsmap')
+
+ expect(getFileIcon('vue')).toBe('vscode-icons:file-type-vue')
+ expect(getFileIcon('Svelte')).toBe('vscode-icons:file-type-svelte')
+ })
+
+ it('should find icon with folder', () => {
+ expect(getFileIcon('xxx', 'folder')).toBe('vscode-icons:default-folder')
+ expect(getFileIcon('src', 'folder')).toBe('vscode-icons:folder-type-src')
+ expect(getFileIcon('src/client', 'folder')).toBe('vscode-icons:folder-type-client')
+ })
+
+ it('should find icon with named file', () => {
+ expect(getFileIcon('webpack.config.js')).toBe('vscode-icons:file-type-webpack')
+ expect(getFileIcon('vite.config.mjs')).toBe('vscode-icons:file-type-vite')
+ expect(getFileIcon('test/vitest.config.js')).toBe('vscode-icons:file-type-js')
+ })
+
+ it('should find icon with extensions', () => {
+ expect(getFileIcon('a.cpp')).toBe('vscode-icons:file-type-cpp')
+ expect(getFileIcon('a.spec.mjs')).toBe('vscode-icons:file-type-light-testjs')
+ })
+
+ it('should find icon with partials', () => {
+ expect(getFileIcon('LICENSE')).toBe('vscode-icons:file-type-license')
+ expect(getFileIcon('Gemfile')).toBe('vscode-icons:file-type-ruby')
+ })
+
+ it('should return default icon when not find', () => {
+ expect(getFileIcon('a.abcxxx')).toBe('vscode-icons:default-file')
+ expect(getFileIcon('a.abcxxx.tbnmmb.z')).toBe('vscode-icons:default-file')
+ expect(getFileIcon('abc.', 'file')).toBe('vscode-icons:default-file')
+ expect(getFileIcon('abc.', 'folder')).toBe('vscode-icons:default-folder')
+ expect(getFileIcon('')).toBe('vscode-icons:default-file')
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/iconsPlugin.spec.ts b/plugins/plugin-md-power/__test__/iconsPlugin.spec.ts
new file mode 100644
index 00000000..eddb32a6
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/iconsPlugin.spec.ts
@@ -0,0 +1,48 @@
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { iconsPlugin } from '../src/node/inline/icons.js'
+
+describe('iconsPlugin', () => {
+ it('should work', () => {
+ const md = MarkdownIt().use(iconsPlugin)
+
+ expect(md.render(':[mdi:11]:')).toMatchSnapshot()
+ expect(md.render('**strong** :[mdi:11]: :[mdi:11]:')).toMatchSnapshot()
+ expect(md.render('**strong**\n:[mdi:11]:\n :[mdi:11]:')).toMatchSnapshot()
+ })
+
+ it('should work with options', () => {
+ const md = MarkdownIt().use(iconsPlugin, { size: '1.25em', color: '#ccc' })
+
+ expect(md.render(':[mdi:11]:')).toMatchSnapshot()
+ expect(md.render('**strong** :[mdi:11]: :[mdi:11]:')).toMatchSnapshot()
+ })
+
+ it('should work with single icon options', () => {
+ const md = MarkdownIt().use(iconsPlugin)
+
+ expect(md.render(':[mdi:11 36px]:')).toMatchSnapshot()
+ expect(md.render(':[mdi:11 32px/#eee]:')).toMatchSnapshot()
+ expect(md.render(':[mdi:11 /#eee]:')).toMatchSnapshot()
+ expect(md.render(':[mdi:11 32px/]:')).toMatchSnapshot()
+ expect(md.render(':[mdi:11 /]:')).toMatchSnapshot()
+
+ const md2 = MarkdownIt().use(iconsPlugin, { size: '1.25em', color: '#ccc' })
+ expect(md2.render(':[mdi:11]:')).toMatchSnapshot()
+ expect(md2.render(':[mdi:11 36px]:')).toMatchSnapshot()
+ expect(md2.render(':[mdi:11 32px/#eee]:')).toMatchSnapshot()
+ expect(md2.render(':[mdi:11 /#eee]:')).toMatchSnapshot()
+ expect(md2.render(':[mdi:11 32px/]:')).toMatchSnapshot()
+ expect(md2.render(':[mdi:11 /]:')).toMatchSnapshot()
+ })
+
+ it('should not work with invalid icon', () => {
+ const md = MarkdownIt().use(iconsPlugin)
+
+ expect(md.render(':[ mdi:11 ]:')).toMatchSnapshot()
+ expect(md.render(':[]:')).toMatchSnapshot()
+ expect(md.render(':[]&')).toMatchSnapshot()
+ expect(md.render(':[:[')).toMatchSnapshot()
+ expect(md.render(':[mdi:11')).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/jsfiddlePlugin.spec.ts b/plugins/plugin-md-power/__test__/jsfiddlePlugin.spec.ts
new file mode 100644
index 00000000..9676c5c4
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/jsfiddlePlugin.spec.ts
@@ -0,0 +1,40 @@
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { jsfiddlePlugin } from '../src/node/embed/code/jsfiddle.js'
+
+function createMarkdown() {
+ return MarkdownIt().use((md) => {
+ md.block.ruler.before('code', 'import_code', () => false)
+ md.renderer.rules.import_code = () => ''
+ }).use(jsfiddlePlugin)
+}
+
+describe('codeSandboxPlugin', () => {
+ it('should work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[jsfiddle](user/id)
+
+@[jsfiddle](user/id)
+
+@[jsfiddle theme="light"](user/id)
+
+@[jsfiddle title="xxx" width="100%" height="500px" theme="dark" tab="js,css,html,result"](user/id)
+`
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should not work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[jsfiddle]()
+
+@[jsfiddle]xxx
+
+@[jsfiddle](
+`
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/npmToPlugin.spec.ts b/plugins/plugin-md-power/__test__/npmToPlugin.spec.ts
new file mode 100644
index 00000000..9d64b42b
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/npmToPlugin.spec.ts
@@ -0,0 +1,131 @@
+import type { NpmToOptions } from '../src/shared/npmTo.js'
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { codeTabs } from '../src/node/container/codeTabs.js'
+import { npmToPlugins, parseLine } from '../src/node/container/npmTo.js'
+
+function createMarkdown(options?: NpmToOptions) {
+ return new MarkdownIt({
+ highlight: (str, lang) => ``,
+ }).use(codeTabs).use(npmToPlugins, options)
+}
+
+const FENCE = '```'
+
+describe('npmToPlugin', () => {
+ const code = `\
+::: npm-to
+${FENCE}sh
+${FENCE}
+:::
+
+::: npm-to
+${FENCE}sh
+npm install
+${FENCE}
+:::
+
+::: npm-to
+${FENCE}shell
+npm install
+${FENCE}
+:::
+
+::: npm-to
+${FENCE}bash
+npm install
+${FENCE}
+:::
+
+::: npm-to
+${FENCE}sh
+cross-env NODE_ENV=production npm run docs
+${FENCE}
+:::
+
+::: npm-to
+${FENCE}sh
+npm i -D package1 package2
+npm i --save-peer package3
+npm run docs
+${FENCE}
+:::
+
+::: npm-to
+${FENCE}sh
+npm install && npm run docs
+mkdir foo
+${FENCE}
+:::
+
+::: npm-to
+${FENCE}sh
+npm run docs -- --clean-cache --clean-temp
+
+${FENCE}
+:::
+
+::: npm-to tabs=","
+${FENCE}sh
+npm create vuepress-theme-plume@latest
+${FENCE}
+:::
+
+::: npm-to tabs="npm,pnpm,yarn,bun,deno"
+${FENCE}sh
+npx vp-update
+${FENCE}
+:::
+
+::: npm-to
+${FENCE}sh
+mkdir foo
+${FENCE}
+:::
+
+::: npm-to
+anything
+${FENCE}sh
+${FENCE}
+:::
+`
+ it('should work with default options', () => {
+ const md = createMarkdown()
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should work width options: { tabs: [npm, yarn, pnpm] }', () => {
+ const md = createMarkdown({ tabs: ['npm', 'yarn', 'pnpm'] })
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should work width options: [npm, yarn, pnpm]', () => {
+ const md = createMarkdown(['npm', 'yarn', 'pnpm'])
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+})
+
+describe('parseLine', () => {
+ it('should work', () => {
+ expect(parseLine('npm install')).toMatchObject({ env: '', cli: 'npm install', cmd: '' })
+ expect(parseLine('npx vp-update')).toMatchObject({ env: '', cli: 'npx', cmd: 'vp-update' })
+ expect(parseLine('npx vp-update --foo')).toMatchObject({ env: '', cli: 'npx', cmd: 'vp-update', scriptArgs: '--foo' })
+ expect(parseLine('npm run docs')).toMatchObject({ env: '', cli: 'npm run', cmd: 'docs' })
+ expect(parseLine('npm i -D package1 -- --foo'))
+ .toMatchObject({ env: '', cli: 'npm i', cmd: 'package1', args: '-D', scriptArgs: '--foo' })
+ expect(parseLine('npm install -y')).toMatchObject({ env: '', cli: 'npm install', cmd: '', args: '-y' })
+ expect(parseLine('npm create vuepress-theme-plume my-blog')).toMatchObject({ env: '', cli: 'npm create', cmd: 'vuepress-theme-plume my-blog' })
+ expect(parseLine('npm create "vuepress-theme-plume my-blog"')).toMatchObject({ env: '', cli: 'npm create', cmd: '"vuepress-theme-plume my-blog"' })
+ expect(parseLine('npm create vuepress-theme-plume my-blog --foo -B')).toMatchObject({ env: '', cli: 'npm create', cmd: 'vuepress-theme-plume my-blog', args: '--foo -B' })
+
+ expect(parseLine('npm run -w="my-workspace" docs')).toMatchObject({ env: '', cli: 'npm run', cmd: 'docs', args: '-w="my-workspace"' })
+ })
+
+ it('should not match', () => {
+ expect(parseLine('mkdir foo')).toBe(false)
+ expect(parseLine('')).toBe(false)
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/parseRect.spec.ts b/plugins/plugin-md-power/__test__/parseRect.spec.ts
new file mode 100644
index 00000000..5399005a
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/parseRect.spec.ts
@@ -0,0 +1,20 @@
+import { describe, expect, it } from 'vitest'
+import { parseRect } from '../src/node/utils/parseRect.js'
+
+describe('parseRect(str)', () => {
+ it('should parse rect', () => {
+ expect(parseRect('')).toBe('')
+ expect(parseRect('1')).toBe('1px')
+ expect(parseRect('1px')).toBe('1px')
+ expect(parseRect('1%')).toBe('1%')
+ expect(parseRect('1em')).toBe('1em')
+ expect(parseRect('1cm')).toBe('1cm')
+ })
+
+ it('should parse rect with unit', () => {
+ expect(parseRect('1', 'px')).toBe('1px')
+ expect(parseRect('1px', 'px')).toBe('1px')
+ expect(parseRect('1%', 'px')).toBe('1%')
+ expect(parseRect('1em', 'px')).toBe('1em')
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/pdfPlugin.spec.ts b/plugins/plugin-md-power/__test__/pdfPlugin.spec.ts
new file mode 100644
index 00000000..e759720d
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/pdfPlugin.spec.ts
@@ -0,0 +1,47 @@
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { pdfPlugin } from '../src/node/embed/pdf.js'
+
+function createMarkdown() {
+ return MarkdownIt().use((md) => {
+ md.block.ruler.before('code', 'import_code', () => false)
+ md.renderer.rules.import_code = () => ''
+ }).use(pdfPlugin)
+}
+
+describe('pdfPlugin', () => {
+ it('should work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[pdf](foo.pdf)
+
+@[pdf no-toolbar](foo.pdf)
+
+@[pdf 2](foo.pdf)
+
+@[pdf 2 no-toolbar](foo.pdf)
+
+@[pdf 2 no-toolbar width="100%" height="600px" zoom="1"](foo.pdf)
+
+@[pdf 2 no-toolbar width="100%" zoom="1" ratio="1:1"](foo.pdf)
+`
+
+ expect(md.render('@[pdf](foo.pdf)')).toMatchSnapshot()
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should not work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[pdf]()
+
+@[pdf]xxx
+
+@[ pdf]()
+
+@[pdf]
+`
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/plotPlugin.spec.ts b/plugins/plugin-md-power/__test__/plotPlugin.spec.ts
new file mode 100644
index 00000000..3131a6cb
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/plotPlugin.spec.ts
@@ -0,0 +1,22 @@
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { plotPlugin } from '../src/node/inline/plot.js'
+
+describe('plotPlugin', () => {
+ it('should work', () => {
+ const md = MarkdownIt().use(plotPlugin)
+
+ expect(md.render('!!plot!!')).toMatchSnapshot()
+ expect(md.render('hidden: !!plot!! **strong**')).toMatchSnapshot()
+ expect(md.render('hidden: \n!!plot!! \n!!plot!!')).toMatchSnapshot()
+ })
+
+ it('should not work with invalid tag', () => {
+ const md = MarkdownIt().use(plotPlugin)
+
+ expect(md.render('!!!!')).toMatchSnapshot()
+ expect(md.render('!!plot!')).toMatchSnapshot()
+ expect(md.render('!!!')).toMatchSnapshot()
+ expect(md.render('!! plot !!')).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/resolveAttrs.spec.ts b/plugins/plugin-md-power/__test__/resolveAttrs.spec.ts
new file mode 100644
index 00000000..83ff8648
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/resolveAttrs.spec.ts
@@ -0,0 +1,35 @@
+import { describe, expect, it } from 'vitest'
+import { resolveAttrs } from '../src/node/utils/resolveAttrs.js'
+
+describe('resolveAttrs(info)', () => {
+ it('should resolve attrs', () => {
+ expect(resolveAttrs('')).toMatchObject({ rawAttrs: '', attrs: {} })
+
+ expect(resolveAttrs('a="1"')).toMatchObject({
+ rawAttrs: 'a="1"',
+ attrs: { a: '1' },
+ })
+
+ expect(resolveAttrs('a="1" b="2"')).toMatchObject({
+ rawAttrs: 'a="1" b="2"',
+ attrs: { a: '1', b: '2' },
+ })
+
+ expect(resolveAttrs('a="1" b="2" c')).toMatchObject({
+ rawAttrs: 'a="1" b="2" c',
+ attrs: { a: '1', b: '2', c: true },
+ })
+
+ expect(resolveAttrs('a b="true" c="false"')).toMatchObject({
+ rawAttrs: 'a b="true" c="false"',
+ attrs: { a: true, b: true, c: false },
+ })
+ })
+
+ it('should resolve attrs with include `-`', () => {
+ expect(resolveAttrs('foo-bar="1" fizz-buzz')).toMatchObject({
+ rawAttrs: 'foo-bar="1" fizz-buzz',
+ attrs: { 'fooBar': '1', 'fizzBuzz': true, 'foo-bar': '1', 'fizz-buzz': true },
+ })
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/timeToSeconds.spec.ts b/plugins/plugin-md-power/__test__/timeToSeconds.spec.ts
new file mode 100644
index 00000000..de06c58d
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/timeToSeconds.spec.ts
@@ -0,0 +1,26 @@
+import { describe, expect, it } from 'vitest'
+import { timeToSeconds } from '../src/node/utils/timeToSeconds.js'
+
+describe('timeToSeconds(timeLike)', () => {
+ it('should return seconds', () => {
+ expect(timeToSeconds('')).toBe(0)
+ expect(timeToSeconds('1')).toBe(1)
+ expect(timeToSeconds('69')).toBe(69)
+ })
+
+ it('should return seconds with h:m:s', () => {
+ expect(timeToSeconds('1:2')).toBe(62)
+ expect(timeToSeconds('1:2:3')).toBe(3723)
+ expect(timeToSeconds('1:2:3.4')).toBe(3723.4)
+ expect(timeToSeconds('1:2:')).toBe(3720)
+ expect(timeToSeconds(':2:3')).toBe(123)
+ expect(timeToSeconds('::3')).toBe(3)
+ })
+
+ it('show return seconds with include incorrect char', () => {
+ expect(timeToSeconds('1:a:b')).toBe(3600)
+ expect(timeToSeconds('1:2:a')).toBe(3720)
+ expect(timeToSeconds('a:b')).toBe(0)
+ expect(timeToSeconds('a : b : c')).toBe(0)
+ })
+})
diff --git a/plugins/plugin-md-power/__test__/youtubePlugin.spec.ts b/plugins/plugin-md-power/__test__/youtubePlugin.spec.ts
new file mode 100644
index 00000000..f887c2fe
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/youtubePlugin.spec.ts
@@ -0,0 +1,44 @@
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { youtubePlugin } from '../src/node/embed/video/youtube.js'
+
+function createMarkdown() {
+ return MarkdownIt().use((md) => {
+ md.block.ruler.before('code', 'import_code', () => false)
+ md.renderer.rules.import_code = () => ''
+ }).use(youtubePlugin)
+}
+
+describe('youtubePlugin', () => {
+ it('should work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[youtube](123456)
+
+@[youtube loop autoplay title="test"](123456)
+
+@[youtube autoplay start="40" end="1:20"](123456)
+
+@[youtube width="100%" height="600px"](123456)
+
+@[youtube width="100%" ratio="16:9"](123456)
+`
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+
+ it('should not work', () => {
+ const md = createMarkdown()
+ const code = `\
+@[youtube]()
+
+@[youtube]xxx
+
+@[ youtube]123456
+
+@[ youtube](123456)
+`
+
+ expect(md.render(code)).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/src/node/container/codeTabs.ts b/plugins/plugin-md-power/src/node/container/codeTabs.ts
index 9e52a45f..1153f54d 100644
--- a/plugins/plugin-md-power/src/node/container/codeTabs.ts
+++ b/plugins/plugin-md-power/src/node/container/codeTabs.ts
@@ -6,7 +6,7 @@ import { definitions, getFileIconName, getFileIconTypeFromExtension } from '../f
import { stringifyProp } from '../utils/stringifyProp.js'
export const codeTabs: PluginWithOptions = (md, options: CodeTabsOptions = {}) => {
- const getIcon = (filename: string): string | undefined => {
+ const getIcon = (filename: string): string | void => {
if (options.icon === false)
return undefined
const { named, extensions } = isPlainObject(options.icon) ? options.icon : {}
@@ -54,20 +54,18 @@ export const codeTabs: PluginWithOptions = (md, options: CodeTa
// Hide all elements excerpt the first fence
for (let i = tokenIndex; i < tokens.length; i++) {
- const { block, type } = tokens[i]
+ const { type } = tokens[i]
- if (block) {
- if (type === 'code-tabs_tab_close')
- break
+ if (type === 'code-tabs_tab_close')
+ break
- if ((type === 'fence' || type === 'import_code') && !foundFence) {
- foundFence = true
- continue
- }
-
- tokens[i].type = 'code_tab_empty'
- tokens[i].hidden = true
+ if ((type === 'fence' || type === 'import_code') && !foundFence) {
+ foundFence = true
+ continue
}
+
+ tokens[i].type = 'code_tab_empty'
+ tokens[i].hidden = true
}
return ``
diff --git a/plugins/plugin-md-power/src/node/container/fileTree.ts b/plugins/plugin-md-power/src/node/container/fileTree.ts
index 0a310945..5cad6e6f 100644
--- a/plugins/plugin-md-power/src/node/container/fileTree.ts
+++ b/plugins/plugin-md-power/src/node/container/fileTree.ts
@@ -128,8 +128,8 @@ export function resolveTreeNodeInfo(
}
export function updateInlineToken(inline: Token, info: FileTreeNode, icon: string) {
- const children = inline.children
- if (!children)
+ const children = inline.children!
+ if (!children || children.length === 0)
return
const tokens: Token[] = []
diff --git a/plugins/plugin-md-power/src/node/container/npmTo.ts b/plugins/plugin-md-power/src/node/container/npmTo.ts
index 2f791aeb..99850895 100644
--- a/plugins/plugin-md-power/src/node/container/npmTo.ts
+++ b/plugins/plugin-md-power/src/node/container/npmTo.ts
@@ -189,7 +189,7 @@ const MANAGERS_CONFIG: CommandConfigs = {
},
}
-export function npmToPlugins(md: Markdown, options: NpmToOptions): void {
+export function npmToPlugins(md: Markdown, options: NpmToOptions = {}): void {
const type = 'npm-to'
const validate = (info: string): boolean => info.trim().startsWith(type)
@@ -197,7 +197,7 @@ export function npmToPlugins(md: Markdown, options: NpmToOptions): void {
const defaultTabs = opt.tabs?.length ? opt.tabs : DEFAULT_TABS
const render = (tokens: Token[], idx: number): string => {
- const { attrs } = resolveAttrs(tokens[idx].info.slice(type.length - 1))
+ const { attrs } = resolveAttrs(tokens[idx].info.trim().slice(type.length))
const tabs = (attrs.tabs ? attrs.tabs.split(/,\s*/) : defaultTabs) as NpmToPackageManager[]
if (tokens[idx].nesting === 1) {
const token = tokens[idx + 1]
@@ -229,25 +229,24 @@ function resolveNpmTo(lines: string[], info: string, idx: number, tabs: NpmToPac
for (const line of lines) {
const config = findConfig(line)
if (config && config[tab]) {
- const parsed = (map[line] ??= parseLine(line))
+ const parsed = (map[line] ??= parseLine(line)) as LineParsed
const { cli, flags } = config[tab] as CommandConfigItem
- if (parsed) {
- let newLine = `${parsed.env ? `${parsed.env} ` : ''}${cli}`
- if (parsed.args && flags) {
- let args = parsed.args
- for (const [key, value] of Object.entries(flags)) {
- args = args.replaceAll(key, value)
- }
- newLine += ` ${args.replace(/\s+-/g, ' -').trim()}`
+
+ let newLine = `${parsed.env ? `${parsed.env} ` : ''}${cli}`
+ if (parsed.args && flags) {
+ let args = parsed.args
+ for (const [key, value] of Object.entries(flags)) {
+ args = args.replaceAll(key, value)
}
-
- if (parsed.cmd)
- newLine += ` ${parsed.cmd}`
-
- if (parsed.scriptArgs)
- newLine += ` ${parsed.scriptArgs}`
- newLines.push(newLine.trim())
+ newLine += ` ${args.replace(/\s+-/g, ' -').trim()}`
}
+
+ if (parsed.cmd)
+ newLine += ` ${parsed.cmd}`
+
+ if (parsed.scriptArgs)
+ newLine += ` ${parsed.scriptArgs}`
+ newLines.push(newLine.trim())
}
else {
newLines.push(line)
@@ -268,10 +267,11 @@ function findConfig(line: string): CommandConfig | undefined {
}
function validateTabs(tabs: NpmToPackageManager[]): NpmToPackageManager[] {
+ tabs = tabs.filter(tab => ALLOW_LIST.includes(tab))
if (tabs.length === 0) {
return DEFAULT_TABS
}
- return tabs.filter(tab => ALLOW_LIST.includes(tab))
+ return tabs
}
interface LineParsed {
@@ -289,10 +289,20 @@ export function parseLine(line: string): false | LineParsed {
return false
const [, env, cli, rest] = match
- if (cli === 'npx')
- return { env, cli, cmd: '', scriptArgs: rest?.trim() }
+ const idx = rest.trim().indexOf(' ')
+ if (cli === 'npx') {
+ let cmd = ''
+ let scriptArgs = ''
+ if (idx !== -1) {
+ cmd = rest.slice(0, idx)
+ scriptArgs = rest.slice(idx + 1).trim()
+ }
+ else {
+ cmd = rest
+ }
+ return { env, cli, cmd, scriptArgs }
+ }
- const idx = rest.indexOf(' ')
if (idx === -1)
return { env, cli: `${cli} ${rest.trim()}`, cmd: '' }
@@ -301,8 +311,6 @@ export function parseLine(line: string): false | LineParsed {
function parseArgs(line: string): { cmd: string, args?: string, scriptArgs?: string } {
line = line?.trim()
- if (!line)
- return { cmd: '' }
const [npmArgs, scriptArgs] = line.split(/\s+--\s+/)
let cmd = ''
@@ -310,11 +318,11 @@ function parseArgs(line: string): { cmd: string, args?: string, scriptArgs?: str
if (npmArgs[0] !== '-') {
if (npmArgs[0] === '"' || npmArgs[0] === '\'') {
const idx = npmArgs.slice(1).indexOf(npmArgs[0])
- cmd = npmArgs.slice(0, idx)
- args = npmArgs.slice(idx + 1)
+ cmd = npmArgs.slice(0, idx + 2)
+ args = npmArgs.slice(idx + 2)
}
else {
- const idx = npmArgs.indexOf(' ')
+ const idx = npmArgs.indexOf(' -')
if (idx === -1) {
cmd = npmArgs
}
@@ -346,16 +354,17 @@ function parseArgs(line: string): { cmd: string, args?: string, scriptArgs?: str
if (i === npmArgs.length - 1) {
value += v
}
+
const isKey = value[0] === '-'
if (isKey) {
isBool = BOOL_FLAGS.includes(value)
isNextValue = !isBool
}
if (!isKey && !isNextValue) {
- cmd += `${value} `
+ cmd += `${value}`
}
else {
- newLine += `${value}${v || ''}`
+ newLine += `${value}${i !== npmArgs.length - 1 ? v : ''}`
if (!isKey && isNextValue) {
isNextValue = false
}
@@ -369,5 +378,5 @@ function parseArgs(line: string): { cmd: string, args?: string, scriptArgs?: str
args = newLine
}
- return { cmd, args: args.trim(), scriptArgs }
+ return { cmd: cmd.trim(), args: args.trim(), scriptArgs }
}
diff --git a/plugins/plugin-md-power/src/node/embed/caniuse.ts b/plugins/plugin-md-power/src/node/embed/caniuse.ts
index b6ba6d6c..66ffcd9b 100644
--- a/plugins/plugin-md-power/src/node/embed/caniuse.ts
+++ b/plugins/plugin-md-power/src/node/embed/caniuse.ts
@@ -1,5 +1,5 @@
/**
- * @[caniuse embed{1,2,3,4}](feature_name)
+ * @[caniuse embed{-2,4}](feature_name)
* @[caniuse image](feature_name)
*/
import type { PluginWithOptions } from 'markdown-it'
@@ -7,10 +7,9 @@ import type MarkdownIt from 'markdown-it'
import type Token from 'markdown-it/lib/token.mjs'
import type { CanIUseMode, CanIUseOptions, CanIUseTokenMeta } from '../../shared/index.js'
import container from 'markdown-it-container'
-import { customAlphabet } from 'nanoid'
+import { nanoid } from '../utils/nanoid.js'
import { createEmbedRuleBlock } from './createEmbedRuleBlock.js'
-const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 5)
const UNDERLINE_RE = /_+/g
/**
* @example
diff --git a/plugins/plugin-md-power/src/node/embed/code/codeSandbox.ts b/plugins/plugin-md-power/src/node/embed/code/codeSandbox.ts
index c9d294db..8c395aad 100644
--- a/plugins/plugin-md-power/src/node/embed/code/codeSandbox.ts
+++ b/plugins/plugin-md-power/src/node/embed/code/codeSandbox.ts
@@ -13,7 +13,7 @@ export const codeSandboxPlugin: PluginWithOptions = (md) => {
createEmbedRuleBlock(md, {
type: 'codesandbox',
syntaxPattern: /^@\[codesandbox(?:\s+(embed|button))?([^\]]*)\]\(([^)]*)\)/,
- meta([, type, info = '', source = '']) {
+ meta([, type, info, source]) {
const { attrs } = resolveAttrs(info)
const [profile, filepath = ''] = source.split('#')
const [user, id] = profile.includes('/') ? profile.split('/') : ['', profile]
diff --git a/plugins/plugin-md-power/src/node/embed/code/codepen.ts b/plugins/plugin-md-power/src/node/embed/code/codepen.ts
index 75cf1971..225383b2 100644
--- a/plugins/plugin-md-power/src/node/embed/code/codepen.ts
+++ b/plugins/plugin-md-power/src/node/embed/code/codepen.ts
@@ -15,7 +15,7 @@ export const codepenPlugin: PluginWithOptions = (md) => {
createEmbedRuleBlock(md, {
type: 'codepen',
syntaxPattern: /^@\[codepen([^\]]*)\]\(([^)]*)\)/,
- meta: ([, info = '', source = '']) => {
+ meta: ([, info, source]) => {
const { attrs } = resolveAttrs(info)
const [user, slash] = source.split('/')
@@ -27,7 +27,7 @@ export const codepenPlugin: PluginWithOptions = (md) => {
title: attrs.title,
preview: attrs.preview,
editable: attrs.editable,
- tab: attrs.tab ?? 'result',
+ tab: attrs.tab || 'result',
theme: attrs.theme,
}
},
@@ -38,9 +38,9 @@ export const codepenPlugin: PluginWithOptions = (md) => {
if (meta.editable) {
params.set('editable', 'true')
}
- if (meta.tab) {
- params.set('default-tab', meta.tab)
- }
+
+ params.set('default-tab', meta.tab!)
+
if (meta.theme) {
params.set('theme-id', meta.theme)
}
diff --git a/plugins/plugin-md-power/src/node/embed/code/jsfiddle.ts b/plugins/plugin-md-power/src/node/embed/code/jsfiddle.ts
index caa6009a..d4844415 100644
--- a/plugins/plugin-md-power/src/node/embed/code/jsfiddle.ts
+++ b/plugins/plugin-md-power/src/node/embed/code/jsfiddle.ts
@@ -12,7 +12,7 @@ export const jsfiddlePlugin: PluginWithOptions = (md) => {
createEmbedRuleBlock(md, {
type: 'jsfiddle',
syntaxPattern: /^@\[jsfiddle([^\]]*)\]\(([^)]*)\)/,
- meta([, info = '', source]) {
+ meta([, info, source]) {
const { attrs } = resolveAttrs(info)
const [user, id] = source.split('/')
@@ -26,7 +26,7 @@ export const jsfiddlePlugin: PluginWithOptions = (md) => {
theme: attrs.theme || 'dark',
}
},
- content: ({ title = 'JS Fiddle', height, width, user, id, tab, theme }) => {
+ content: ({ title, height, width, user, id, tab, theme }) => {
theme = theme === 'dark' ? '/dark/' : ''
const link = `https://jsfiddle.net/${user}/${id}/embedded/${tab}${theme}`
diff --git a/plugins/plugin-md-power/src/node/embed/pdf.ts b/plugins/plugin-md-power/src/node/embed/pdf.ts
index 4b0a008a..b694b456 100644
--- a/plugins/plugin-md-power/src/node/embed/pdf.ts
+++ b/plugins/plugin-md-power/src/node/embed/pdf.ts
@@ -15,7 +15,7 @@ export const pdfPlugin: PluginWithOptions = (md) => {
type: 'pdf',
// eslint-disable-next-line regexp/no-super-linear-backtracking
syntaxPattern: /^@\[pdf(?:\s+(\d+))?([^\]]*)\]\(([^)]*)\)/,
- meta([, page, info = '', src = '']) {
+ meta([, page, info, src]) {
const { attrs } = resolveAttrs(info)
return {
src,
diff --git a/plugins/plugin-md-power/src/node/embed/video/bilibili.ts b/plugins/plugin-md-power/src/node/embed/video/bilibili.ts
index 3a503aea..abec0356 100644
--- a/plugins/plugin-md-power/src/node/embed/video/bilibili.ts
+++ b/plugins/plugin-md-power/src/node/embed/video/bilibili.ts
@@ -20,14 +20,14 @@ export const bilibiliPlugin: PluginWithOptions = (md) => {
name: 'video_bilibili',
// eslint-disable-next-line regexp/no-super-linear-backtracking
syntaxPattern: /^@\[bilibili(?:\s+p(\d+))?([^\]]*)\]\(([^)]*)\)/,
- meta([, page, info = '', source = '']) {
+ meta([, page, info, source]) {
const { attrs } = resolveAttrs(info)
const ids = source.trim().split(/\s+/)
const bvid = ids.find(id => id.startsWith('BV'))
const [aid, cid] = ids.filter(id => !id.startsWith('BV'))
return {
- page: +page || 1,
+ page: +page,
bvid,
aid,
cid,
diff --git a/plugins/plugin-md-power/src/node/embed/video/youtube.ts b/plugins/plugin-md-power/src/node/embed/video/youtube.ts
index 6cdb2279..5e44019d 100644
--- a/plugins/plugin-md-power/src/node/embed/video/youtube.ts
+++ b/plugins/plugin-md-power/src/node/embed/video/youtube.ts
@@ -16,7 +16,7 @@ export const youtubePlugin: PluginWithOptions = (md) => {
type: 'youtube',
name: 'video_youtube',
syntaxPattern: /^@\[youtube([^\]]*)\]\(([^)]*)\)/,
- meta([, info = '', id = '']) {
+ meta([, info, id]) {
const { attrs } = resolveAttrs(info)
return {
diff --git a/plugins/plugin-md-power/src/node/fileIcons/findIcon.ts b/plugins/plugin-md-power/src/node/fileIcons/findIcon.ts
index d123ed60..c6ac9052 100644
--- a/plugins/plugin-md-power/src/node/fileIcons/findIcon.ts
+++ b/plugins/plugin-md-power/src/node/fileIcons/findIcon.ts
@@ -1,9 +1,9 @@
import { defaultFile, defaultFolder, definitions } from './definitions.js'
-export function getFileIcon(fileName: string, type: 'file' | 'folder' = 'file'): string {
+export function getFileIcon(fileName: string, type?: 'file' | 'folder'): string {
const name = getFileIconName(fileName, type)
if (!name)
- return type === 'file' ? defaultFile : defaultFolder
+ return type !== 'folder' ? defaultFile : defaultFolder
return name
}
@@ -20,7 +20,7 @@ export function getFileIconName(fileName: string, type: 'file' | 'folder' = 'fil
let icon: string | undefined = definitions.named[fileName] || definitions.files[fileName]
if (icon)
return icon
- icon = getFileIconTypeFromExtension(fileName)
+ icon = getFileIconTypeFromExtension(fileName) || undefined
if (icon)
return icon
for (const [partial, partialIcon] of Object.entries(definitions.partials)) {
@@ -30,7 +30,7 @@ export function getFileIconName(fileName: string, type: 'file' | 'folder' = 'fil
return icon
}
-export function getFileIconTypeFromExtension(fileName: string): string | undefined {
+export function getFileIconTypeFromExtension(fileName: string): string | void {
const firstDotIndex = fileName.indexOf('.')
if (firstDotIndex === -1)
return
@@ -44,5 +44,4 @@ export function getFileIconTypeFromExtension(fileName: string): string | undefin
return
extension = extension.slice(nextDotIndex)
}
- return undefined
}
diff --git a/plugins/plugin-md-power/src/node/inline/icons.ts b/plugins/plugin-md-power/src/node/inline/icons.ts
index 8956f3e3..81812542 100644
--- a/plugins/plugin-md-power/src/node/inline/icons.ts
+++ b/plugins/plugin-md-power/src/node/inline/icons.ts
@@ -58,15 +58,14 @@ function createTokenizer(options: IconsOptions): RuleInline {
state.pos = start + 2
const [name, opt = ''] = content.split(/\s+/)
- const [size = options.size, color = options.color] = opt.split('/')
+ const [size, color = options.color] = opt.split('/')
const icon = state.push('vp_iconify_open', 'VPIcon', 1)
icon.markup = openTag
- if (name)
- icon.attrSet('name', name)
- if (size)
- icon.attrSet('size', String(size))
+ icon.attrSet('name', name)
+ if (size || options.size)
+ icon.attrSet('size', String(size || options.size))
if (color)
icon.attrSet('color', color)
diff --git a/plugins/plugin-md-power/src/node/utils/nanoid.ts b/plugins/plugin-md-power/src/node/utils/nanoid.ts
new file mode 100644
index 00000000..2b647dc9
--- /dev/null
+++ b/plugins/plugin-md-power/src/node/utils/nanoid.ts
@@ -0,0 +1,3 @@
+import { customAlphabet } from 'nanoid'
+
+export const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 5)
diff --git a/plugins/plugin-md-power/src/node/utils/resolveAttrs.ts b/plugins/plugin-md-power/src/node/utils/resolveAttrs.ts
index 802d6fc0..f2b307a7 100644
--- a/plugins/plugin-md-power/src/node/utils/resolveAttrs.ts
+++ b/plugins/plugin-md-power/src/node/utils/resolveAttrs.ts
@@ -16,7 +16,7 @@ export function resolveAttrs = Record
// eslint-disable-next-line no-cond-assign
while (matched = info.match(RE_ATTR_VALUE)) {
- const { attr, value } = matched.groups || {}
+ const { attr, value } = matched.groups!
attrs[attr] = value ?? true
info = info.slice(matched[0].length)
}
diff --git a/plugins/plugin-md-power/src/node/utils/timeToSeconds.ts b/plugins/plugin-md-power/src/node/utils/timeToSeconds.ts
index 44538856..04bdea23 100644
--- a/plugins/plugin-md-power/src/node/utils/timeToSeconds.ts
+++ b/plugins/plugin-md-power/src/node/utils/timeToSeconds.ts
@@ -5,7 +5,10 @@ export function timeToSeconds(time: string): number {
if (Number.parseFloat(time) === Number(time))
return Number(time)
- const [s, m, h] = time.split(':').reverse().map(n => Number(n) || 0)
+ const [s, m, h = 0] = time
+ .split(/\s*:\s*/)
+ .reverse()
+ .map(n => Number(n) || 0)
return s + m * 60 + h * 3600
}
diff --git a/plugins/plugin-md-power/src/shared/plot.ts b/plugins/plugin-md-power/src/shared/plot.ts
index c03fcf0e..923d44c1 100644
--- a/plugins/plugin-md-power/src/shared/plot.ts
+++ b/plugins/plugin-md-power/src/shared/plot.ts
@@ -1,6 +1,6 @@
export interface PlotOptions {
/**
- * 是否启用 `=| |=` markdown (该标记为非标准标记,脱离插件将不生效)
+ * 是否启用 `!! !!` markdown (该标记为非标准标记,脱离插件将不生效)
* @default true
*/
tag?: boolean
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0ebc13e2..b6e487f9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -19,7 +19,7 @@ importers:
version: 19.5.0
'@pengzhanbo/eslint-config-vue':
specifier: ^1.18.0
- version: 1.18.0(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)
+ version: 1.18.0(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)(vitest@2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4))
'@pengzhanbo/stylelint-config':
specifier: ^1.18.0
version: 1.18.0(stylelint@16.9.0(typescript@5.6.2))
@@ -32,9 +32,12 @@ importers:
'@types/webpack-env':
specifier: ^1.18.5
version: 1.18.5
+ '@vitest/coverage-istanbul':
+ specifier: ^2.1.2
+ version: 2.1.2(vitest@2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4))
bumpp:
specifier: ^9.6.1
- version: 9.6.1
+ version: 9.6.1(magicast@0.3.5)
commitizen:
specifier: ^4.3.1
version: 4.3.1(@types/node@20.12.10)(typescript@5.6.2)
@@ -44,6 +47,9 @@ importers:
cpx2:
specifier: ^8.0.0
version: 8.0.0
+ cross-env:
+ specifier: 7.0.3
+ version: 7.0.3
cz-conventional-changelog:
specifier: ^3.3.0
version: 3.3.0(@types/node@20.12.10)(typescript@5.6.2)
@@ -56,6 +62,9 @@ importers:
lint-staged:
specifier: ^15.2.10
version: 15.2.10
+ markdown-it:
+ specifier: ^14.1.0
+ version: 14.1.0
rimraf:
specifier: ^6.0.1
version: 6.0.1
@@ -71,6 +80,9 @@ importers:
typescript:
specifier: ^5.6.2
version: 5.6.2
+ vitest:
+ specifier: ^2.1.2
+ version: 2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4)
wait-on:
specifier: ^8.0.1
version: 8.0.1
@@ -459,6 +471,9 @@ packages:
peerDependencies:
'@algolia/client-search': '>= 4.9.1 < 6'
algoliasearch: '>= 4.9.1 < 6'
+ peerDependenciesMeta:
+ '@algolia/client-search':
+ optional: true
'@algolia/cache-browser-local-storage@4.24.0':
resolution: {integrity: sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==}
@@ -505,6 +520,10 @@ packages:
'@algolia/transporter@4.24.0':
resolution: {integrity: sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==}
+ '@ampproject/remapping@2.3.0':
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+
'@antfu/install-pkg@0.4.1':
resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==}
@@ -515,27 +534,98 @@ packages:
resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
engines: {node: '>=6.9.0'}
+ '@babel/code-frame@7.25.7':
+ resolution: {integrity: sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.25.7':
+ resolution: {integrity: sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.25.7':
+ resolution: {integrity: sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.25.7':
+ resolution: {integrity: sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.25.7':
+ resolution: {integrity: sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.25.7':
+ resolution: {integrity: sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.25.7':
+ resolution: {integrity: sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-simple-access@7.25.7':
+ resolution: {integrity: sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-string-parser@7.24.8':
resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-string-parser@7.25.7':
+ resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-validator-identifier@7.24.7':
resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-validator-identifier@7.25.7':
+ resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.25.7':
+ resolution: {integrity: sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.25.7':
+ resolution: {integrity: sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==}
+ engines: {node: '>=6.9.0'}
+
'@babel/highlight@7.24.7':
resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
engines: {node: '>=6.9.0'}
+ '@babel/highlight@7.25.7':
+ resolution: {integrity: sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==}
+ engines: {node: '>=6.9.0'}
+
'@babel/parser@7.25.6':
resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==}
engines: {node: '>=6.0.0'}
hasBin: true
+ '@babel/parser@7.25.7':
+ resolution: {integrity: sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/template@7.25.7':
+ resolution: {integrity: sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.25.7':
+ resolution: {integrity: sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==}
+ engines: {node: '>=6.9.0'}
+
'@babel/types@7.25.6':
resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==}
engines: {node: '>=6.9.0'}
+ '@babel/types@7.25.7':
+ resolution: {integrity: sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==}
+ engines: {node: '>=6.9.0'}
+
'@braintree/sanitize-url@7.1.0':
resolution: {integrity: sha512-o+UlMLt49RvtCASlOMW0AkHnabN9wR9rwCCherxO0yG4Npy34GkvrAqdXQvrhNs+jh+gkK8gB8Lf05qL/O7KWg==}
@@ -944,6 +1034,10 @@ packages:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
+ '@istanbuljs/schema@0.1.3':
+ resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+ engines: {node: '>=8'}
+
'@jridgewell/gen-mapping@0.3.5':
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
@@ -1686,6 +1780,11 @@ packages:
vite: ^5.0.0
vue: ^3.2.25
+ '@vitest/coverage-istanbul@2.1.2':
+ resolution: {integrity: sha512-dg7ex3GKrTIenAV0oEp78JucTVFsPMzjl1gYWun22O7SBDxcHFC/REZjWLhMTHRHY8ihm4uBCvmu+CvEu5/Adg==}
+ peerDependencies:
+ vitest: 2.1.2
+
'@vitest/eslint-plugin@1.1.4':
resolution: {integrity: sha512-kudjgefmJJ7xQ2WfbUU6pZbm7Ou4gLYRaao/8Ynide3G0QhVKHd978sDyWX4KOH0CCMH9cyrGAkFd55eGzJ48Q==}
peerDependencies:
@@ -1701,6 +1800,36 @@ packages:
vitest:
optional: true
+ '@vitest/expect@2.1.2':
+ resolution: {integrity: sha512-FEgtlN8mIUSEAAnlvn7mP8vzaWhEaAEvhSXCqrsijM7K6QqjB11qoRZYEd4AKSCDz8p0/+yH5LzhZ47qt+EyPg==}
+
+ '@vitest/mocker@2.1.2':
+ resolution: {integrity: sha512-ExElkCGMS13JAJy+812fw1aCv2QO/LBK6CyO4WOPAzLTmve50gydOlWhgdBJPx2ztbADUq3JVI0C5U+bShaeEA==}
+ peerDependencies:
+ '@vitest/spy': 2.1.2
+ msw: ^2.3.5
+ vite: ^5.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@2.1.2':
+ resolution: {integrity: sha512-FIoglbHrSUlOJPDGIrh2bjX1sNars5HbxlcsFKCtKzu4+5lpsRhOCVcuzp0fEhAGHkPZRIXVNzPcpSlkoZ3LuA==}
+
+ '@vitest/runner@2.1.2':
+ resolution: {integrity: sha512-UCsPtvluHO3u7jdoONGjOSil+uON5SSvU9buQh3lP7GgUXHp78guN1wRmZDX4wGK6J10f9NUtP6pO+SFquoMlw==}
+
+ '@vitest/snapshot@2.1.2':
+ resolution: {integrity: sha512-xtAeNsZ++aRIYIUsek7VHzry/9AcxeULlegBvsdLncLmNCR6tR8SRjn8BbDP4naxtccvzTqZ+L1ltZlRCfBZFA==}
+
+ '@vitest/spy@2.1.2':
+ resolution: {integrity: sha512-GSUi5zoy+abNRJwmFhBDC0yRuVUn8WMlQscvnbbXdKLXX9dE59YbfwXxuJ/mth6eeqIzofU8BB5XDo/Ns/qK2A==}
+
+ '@vitest/utils@2.1.2':
+ resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==}
+
'@volar/language-core@2.4.5':
resolution: {integrity: sha512-F4tA0DCO5Q1F5mScHmca0umsi2ufKULAnMOVBfMsZdT4myhVl4WdKRwCaKcfOkIEuyrAVvtq1ESBdZ+rSyLVww==}
@@ -2031,6 +2160,10 @@ packages:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
astral-regex@2.0.0:
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
engines: {node: '>=8'}
@@ -2157,6 +2290,10 @@ packages:
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+ chai@5.1.1:
+ resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==}
+ engines: {node: '>=12'}
+
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@@ -2185,6 +2322,10 @@ packages:
resolution: {integrity: sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==}
engines: {pnpm: '>=8'}
+ check-error@2.1.1:
+ resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
+ engines: {node: '>= 16'}
+
cheerio-select@2.1.0:
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
@@ -2412,6 +2553,9 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
copy-anything@3.0.5:
resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
engines: {node: '>=12.13'}
@@ -2457,6 +2601,11 @@ packages:
create-filter@1.1.0:
resolution: {integrity: sha512-mSHedS8IpVElLHvTFY9Bg+P0Fs5EyOypkoAWQRhSDyl1c2kzzFJVPv9o7ueOaYxmx/Cn4Cl3HWMLny6pE1b/aA==}
+ cross-env@7.0.3:
+ resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
+ engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
+ hasBin: true
+
cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -2681,6 +2830,10 @@ packages:
dedent@0.7.0:
resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
+ deep-eql@5.0.2:
+ resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
+ engines: {node: '>=6'}
+
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -3019,6 +3172,9 @@ packages:
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
@@ -3209,6 +3365,10 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
@@ -3303,6 +3463,10 @@ packages:
resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==}
engines: {node: '>=6'}
+ globals@11.12.0:
+ resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+ engines: {node: '>=4'}
+
globals@13.24.0:
resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
engines: {node: '>=8'}
@@ -3401,6 +3565,9 @@ packages:
resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
engines: {node: '>=12'}
+ html-escaper@2.0.2:
+ resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
html-tags@3.3.1:
resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==}
engines: {node: '>=8'}
@@ -3616,6 +3783,26 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+ istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-instrument@6.0.3:
+ resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-report@3.0.1:
+ resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-source-maps@5.0.6:
+ resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==}
+ engines: {node: '>=10'}
+
+ istanbul-reports@3.1.7:
+ resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
+ engines: {node: '>=8'}
+
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
@@ -3681,6 +3868,11 @@ packages:
engines: {node: '>= 0.2.0'}
hasBin: true
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
jsonc-eslint-parser@2.4.0:
resolution: {integrity: sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -3842,6 +4034,9 @@ packages:
resolution: {integrity: sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==}
engines: {node: '>=0.10.0'}
+ loupe@3.1.2:
+ resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==}
+
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
@@ -3849,9 +4044,19 @@ packages:
resolution: {integrity: sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==}
engines: {node: 20 || >=22}
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
magic-string@0.30.11:
resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==}
+ magicast@0.3.5:
+ resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
+
+ make-dir@4.0.0:
+ resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+ engines: {node: '>=10'}
+
mark.js@8.11.1:
resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==}
@@ -4369,6 +4574,10 @@ packages:
pathe@1.1.2:
resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+ pathval@2.0.0:
+ resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
+ engines: {node: '>= 14.16'}
+
perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
@@ -4825,6 +5034,10 @@ packages:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
semver@7.6.3:
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
engines: {node: '>=10'}
@@ -4852,6 +5065,9 @@ packages:
resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
engines: {node: '>= 0.4'}
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
@@ -4938,6 +5154,12 @@ packages:
stable-hash@0.0.4:
resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==}
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@3.7.0:
+ resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
+
stdin-discarder@0.2.2:
resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==}
engines: {node: '>=18'}
@@ -5137,6 +5359,10 @@ packages:
resolution: {integrity: sha512-bX655WZI/F7EoTDw9JvQURqAXiPHi8o8+yFxPF2lWYyz1aHnmMRuXWqL6YB6GmeO0o4DIYWHLgGNi/X64T+X4Q==}
engines: {node: '>=14.18'}
+ test-exclude@7.0.1:
+ resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==}
+ engines: {node: '>=18'}
+
text-extensions@2.4.0:
resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==}
engines: {node: '>=8'}
@@ -5154,6 +5380,9 @@ packages:
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
tinyexec@0.3.0:
resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==}
@@ -5161,6 +5390,18 @@ packages:
resolution: {integrity: sha512-qFWYeNxBQxrOTRHvGjlRdBamy8JFqu6c0bwRru9leE+q8J72tLtlT0L3v+2T7fbLXN7FGzDNBhXkWiJqHUHD9g==}
engines: {node: '>=12.0.0'}
+ tinypool@1.0.1:
+ resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+
+ tinyrainbow@1.2.0:
+ resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==}
+ engines: {node: '>=14.0.0'}
+
+ tinyspy@3.0.2:
+ resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
+ engines: {node: '>=14.0.0'}
+
tm-grammars@1.18.0:
resolution: {integrity: sha512-FteVgJAt5FzDlceKVNm3kO+XB5cnRtqzBrbGIOA+46NkpUcylt7gPouqgmjljAdvj2UY+zPyx/TnKNJsjaqagg==}
@@ -5367,6 +5608,11 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+ vite-node@2.1.2:
+ resolution: {integrity: sha512-HPcGNN5g/7I2OtPjLqgOtCRu/qhVvBxTUD3qzitmL0SrG1cWFzxzhMDWussxSbrRYWqnKf8P2jiNhPMSN+ymsQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+
vite@5.4.8:
resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -5398,6 +5644,31 @@ packages:
terser:
optional: true
+ vitest@2.1.2:
+ resolution: {integrity: sha512-veNjLizOMkRrJ6xxb+pvxN6/QAWg95mzcRjtmkepXdN87FNfxAss9RKe2far/G9cQpipfgP2taqg0KiWsquj8A==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/node': ^18.0.0 || >=20.0.0
+ '@vitest/browser': 2.1.2
+ '@vitest/ui': 2.1.2
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
vscode-jsonrpc@8.2.0:
resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
engines: {node: '>=14.0.0'}
@@ -5558,6 +5829,11 @@ packages:
engines: {node: '>= 8'}
hasBin: true
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
wicked-good-xpath@1.3.0:
resolution: {integrity: sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==}
@@ -5599,6 +5875,9 @@ packages:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
@@ -5664,8 +5943,9 @@ snapshots:
'@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)':
dependencies:
- '@algolia/client-search': 4.24.0
algoliasearch: 4.24.0
+ optionalDependencies:
+ '@algolia/client-search': 4.24.0
'@algolia/cache-browser-local-storage@4.24.0':
dependencies:
@@ -5743,6 +6023,11 @@ snapshots:
'@algolia/logger-common': 4.24.0
'@algolia/requester-common': 4.24.0
+ '@ampproject/remapping@2.3.0':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+
'@antfu/install-pkg@0.4.1':
dependencies:
package-manager-detector: 0.2.0
@@ -5755,10 +6040,87 @@ snapshots:
'@babel/highlight': 7.24.7
picocolors: 1.1.0
+ '@babel/code-frame@7.25.7':
+ dependencies:
+ '@babel/highlight': 7.25.7
+ picocolors: 1.1.0
+
+ '@babel/compat-data@7.25.7': {}
+
+ '@babel/core@7.25.7':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@babel/code-frame': 7.25.7
+ '@babel/generator': 7.25.7
+ '@babel/helper-compilation-targets': 7.25.7
+ '@babel/helper-module-transforms': 7.25.7(@babel/core@7.25.7)
+ '@babel/helpers': 7.25.7
+ '@babel/parser': 7.25.7
+ '@babel/template': 7.25.7
+ '@babel/traverse': 7.25.7
+ '@babel/types': 7.25.7
+ convert-source-map: 2.0.0
+ debug: 4.3.7
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.25.7':
+ dependencies:
+ '@babel/types': 7.25.7
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+ jsesc: 3.0.2
+
+ '@babel/helper-compilation-targets@7.25.7':
+ dependencies:
+ '@babel/compat-data': 7.25.7
+ '@babel/helper-validator-option': 7.25.7
+ browserslist: 4.24.0
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-module-imports@7.25.7':
+ dependencies:
+ '@babel/traverse': 7.25.7
+ '@babel/types': 7.25.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.25.7(@babel/core@7.25.7)':
+ dependencies:
+ '@babel/core': 7.25.7
+ '@babel/helper-module-imports': 7.25.7
+ '@babel/helper-simple-access': 7.25.7
+ '@babel/helper-validator-identifier': 7.25.7
+ '@babel/traverse': 7.25.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-simple-access@7.25.7':
+ dependencies:
+ '@babel/traverse': 7.25.7
+ '@babel/types': 7.25.7
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/helper-string-parser@7.24.8': {}
+ '@babel/helper-string-parser@7.25.7': {}
+
'@babel/helper-validator-identifier@7.24.7': {}
+ '@babel/helper-validator-identifier@7.25.7': {}
+
+ '@babel/helper-validator-option@7.25.7': {}
+
+ '@babel/helpers@7.25.7':
+ dependencies:
+ '@babel/template': 7.25.7
+ '@babel/types': 7.25.7
+
'@babel/highlight@7.24.7':
dependencies:
'@babel/helper-validator-identifier': 7.24.7
@@ -5766,16 +6128,51 @@ snapshots:
js-tokens: 4.0.0
picocolors: 1.1.0
+ '@babel/highlight@7.25.7':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.25.7
+ chalk: 2.4.2
+ js-tokens: 4.0.0
+ picocolors: 1.1.0
+
'@babel/parser@7.25.6':
dependencies:
'@babel/types': 7.25.6
+ '@babel/parser@7.25.7':
+ dependencies:
+ '@babel/types': 7.25.7
+
+ '@babel/template@7.25.7':
+ dependencies:
+ '@babel/code-frame': 7.25.7
+ '@babel/parser': 7.25.7
+ '@babel/types': 7.25.7
+
+ '@babel/traverse@7.25.7':
+ dependencies:
+ '@babel/code-frame': 7.25.7
+ '@babel/generator': 7.25.7
+ '@babel/parser': 7.25.7
+ '@babel/template': 7.25.7
+ '@babel/types': 7.25.7
+ debug: 4.3.7
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/types@7.25.6':
dependencies:
'@babel/helper-string-parser': 7.24.8
'@babel/helper-validator-identifier': 7.24.7
to-fast-properties: 2.0.0
+ '@babel/types@7.25.7':
+ dependencies:
+ '@babel/helper-string-parser': 7.25.7
+ '@babel/helper-validator-identifier': 7.25.7
+ to-fast-properties: 2.0.0
+
'@braintree/sanitize-url@7.1.0': {}
'@bufbuild/protobuf@2.1.0': {}
@@ -6163,6 +6560,8 @@ snapshots:
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
+ '@istanbuljs/schema@0.1.3': {}
+
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
@@ -6404,9 +6803,9 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.17.1
- '@pengzhanbo/eslint-config-vue@1.18.0(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)':
+ '@pengzhanbo/eslint-config-vue@1.18.0(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)(vitest@2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4))':
dependencies:
- '@pengzhanbo/eslint-config': 1.18.0(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(eslint-plugin-vue@9.28.0(eslint@9.12.0(jiti@1.21.6)))(eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6)))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)(vue-eslint-parser@9.4.3(eslint@9.12.0(jiti@1.21.6)))
+ '@pengzhanbo/eslint-config': 1.18.0(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(eslint-plugin-vue@9.28.0(eslint@9.12.0(jiti@1.21.6)))(eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6)))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)(vitest@2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4))(vue-eslint-parser@9.4.3(eslint@9.12.0(jiti@1.21.6)))
eslint: 9.12.0(jiti@1.21.6)
eslint-plugin-vue: 9.28.0(eslint@9.12.0(jiti@1.21.6))
eslint-processor-vue-blocks: 0.1.2(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6))
@@ -6429,7 +6828,7 @@ snapshots:
- typescript
- vitest
- '@pengzhanbo/eslint-config@1.18.0(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(eslint-plugin-vue@9.28.0(eslint@9.12.0(jiti@1.21.6)))(eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6)))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)(vue-eslint-parser@9.4.3(eslint@9.12.0(jiti@1.21.6)))':
+ '@pengzhanbo/eslint-config@1.18.0(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(eslint-plugin-vue@9.28.0(eslint@9.12.0(jiti@1.21.6)))(eslint-processor-vue-blocks@0.1.2(@vue/compiler-sfc@3.5.11)(eslint@9.12.0(jiti@1.21.6)))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)(vitest@2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4))(vue-eslint-parser@9.4.3(eslint@9.12.0(jiti@1.21.6)))':
dependencies:
'@antfu/install-pkg': 0.4.1
'@eslint-community/eslint-plugin-eslint-comments': 4.4.0(eslint@9.12.0(jiti@1.21.6))
@@ -6437,7 +6836,7 @@ snapshots:
'@stylistic/eslint-plugin': 2.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)
'@typescript-eslint/eslint-plugin': 8.0.0-alpha.40(@typescript-eslint/parser@8.0.0-alpha.40(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)
'@typescript-eslint/parser': 8.0.0-alpha.40(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)
- '@vitest/eslint-plugin': 1.1.4(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)
+ '@vitest/eslint-plugin': 1.1.4(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)(vitest@2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4))
eslint: 9.12.0(jiti@1.21.6)
eslint-config-flat-gitignore: 0.3.0(eslint@9.12.0(jiti@1.21.6))
eslint-flat-config-utils: 0.4.0
@@ -6897,12 +7296,69 @@ snapshots:
vite: 5.4.8(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4)
vue: 3.5.11(typescript@5.6.2)
- '@vitest/eslint-plugin@1.1.4(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)':
+ '@vitest/coverage-istanbul@2.1.2(vitest@2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4))':
+ dependencies:
+ '@istanbuljs/schema': 0.1.3
+ debug: 4.3.7
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-instrument: 6.0.3
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.1.7
+ magicast: 0.3.5
+ test-exclude: 7.0.1
+ tinyrainbow: 1.2.0
+ vitest: 2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitest/eslint-plugin@1.1.4(@typescript-eslint/utils@8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)(vitest@2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4))':
dependencies:
eslint: 9.12.0(jiti@1.21.6)
optionalDependencies:
'@typescript-eslint/utils': 8.8.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.6.2)
typescript: 5.6.2
+ vitest: 2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4)
+
+ '@vitest/expect@2.1.2':
+ dependencies:
+ '@vitest/spy': 2.1.2
+ '@vitest/utils': 2.1.2
+ chai: 5.1.1
+ tinyrainbow: 1.2.0
+
+ '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.4.8(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4))':
+ dependencies:
+ '@vitest/spy': 2.1.2
+ estree-walker: 3.0.3
+ magic-string: 0.30.11
+ optionalDependencies:
+ vite: 5.4.8(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4)
+
+ '@vitest/pretty-format@2.1.2':
+ dependencies:
+ tinyrainbow: 1.2.0
+
+ '@vitest/runner@2.1.2':
+ dependencies:
+ '@vitest/utils': 2.1.2
+ pathe: 1.1.2
+
+ '@vitest/snapshot@2.1.2':
+ dependencies:
+ '@vitest/pretty-format': 2.1.2
+ magic-string: 0.30.11
+ pathe: 1.1.2
+
+ '@vitest/spy@2.1.2':
+ dependencies:
+ tinyspy: 3.0.2
+
+ '@vitest/utils@2.1.2':
+ dependencies:
+ '@vitest/pretty-format': 2.1.2
+ loupe: 3.1.2
+ tinyrainbow: 1.2.0
'@volar/language-core@2.4.5':
dependencies:
@@ -7425,6 +7881,8 @@ snapshots:
array-union@2.1.0: {}
+ assertion-error@2.0.1: {}
+
astral-regex@2.0.0: {}
async@2.6.4:
@@ -7508,10 +7966,10 @@ snapshots:
builtin-modules@3.3.0: {}
- bumpp@9.6.1:
+ bumpp@9.6.1(magicast@0.3.5):
dependencies:
'@jsdevtools/ez-spawn': 3.0.4
- c12: 1.11.2
+ c12: 1.11.2(magicast@0.3.5)
cac: 6.7.14
escalade: 3.2.0
fast-glob: 3.3.2
@@ -7527,7 +7985,7 @@ snapshots:
esbuild: 0.24.0
load-tsconfig: 0.2.5
- c12@1.11.2:
+ c12@1.11.2(magicast@0.3.5):
dependencies:
chokidar: 3.6.0
confbox: 0.1.7
@@ -7541,6 +7999,8 @@ snapshots:
perfect-debounce: 1.0.0
pkg-types: 1.2.0
rc9: 2.1.2
+ optionalDependencies:
+ magicast: 0.3.5
cac@6.7.14: {}
@@ -7562,6 +8022,14 @@ snapshots:
ccount@2.0.1: {}
+ chai@5.1.1:
+ dependencies:
+ assertion-error: 2.0.1
+ check-error: 2.1.1
+ deep-eql: 5.0.2
+ loupe: 3.1.2
+ pathval: 2.0.0
+
chalk@2.4.2:
dependencies:
ansi-styles: 3.2.1
@@ -7587,6 +8055,8 @@ snapshots:
dependencies:
'@kurkle/color': 0.3.2
+ check-error@2.1.1: {}
+
cheerio-select@2.1.0:
dependencies:
boolbase: 1.0.0
@@ -7845,6 +8315,8 @@ snapshots:
dependencies:
meow: 13.2.0
+ convert-source-map@2.0.0: {}
+
copy-anything@3.0.5:
dependencies:
is-what: 4.1.16
@@ -7903,6 +8375,10 @@ snapshots:
dependencies:
picomatch: 4.0.2
+ cross-env@7.0.3:
+ dependencies:
+ cross-spawn: 7.0.3
+
cross-spawn@7.0.3:
dependencies:
path-key: 3.1.1
@@ -8150,6 +8626,8 @@ snapshots:
dedent@0.7.0: {}
+ deep-eql@5.0.2: {}
+
deep-is@0.1.4: {}
defaults@1.0.4:
@@ -8575,6 +9053,10 @@ snapshots:
estree-walker@2.0.2: {}
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.6
+
esutils@2.0.3: {}
eve-raphael@0.5.0: {}
@@ -8785,6 +9267,8 @@ snapshots:
function-bind@1.1.2: {}
+ gensync@1.0.0-beta.2: {}
+
get-caller-file@2.0.5: {}
get-east-asian-width@1.2.0: {}
@@ -8914,6 +9398,8 @@ snapshots:
kind-of: 6.0.3
which: 1.3.1
+ globals@11.12.0: {}
+
globals@13.24.0:
dependencies:
type-fest: 0.20.2
@@ -9022,6 +9508,8 @@ snapshots:
dependencies:
whatwg-encoding: 2.0.0
+ html-escaper@2.0.2: {}
+
html-tags@3.3.1: {}
html-void-elements@3.0.0: {}
@@ -9207,6 +9695,37 @@ snapshots:
isexe@2.0.0: {}
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-instrument@6.0.3:
+ dependencies:
+ '@babel/core': 7.25.7
+ '@babel/parser': 7.25.6
+ '@istanbuljs/schema': 0.1.3
+ istanbul-lib-coverage: 3.2.2
+ semver: 7.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-lib-report@3.0.1:
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+
+ istanbul-lib-source-maps@5.0.6:
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.25
+ debug: 4.3.7
+ istanbul-lib-coverage: 3.2.2
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-reports@3.1.7:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
jackspeak@3.4.3:
dependencies:
'@isaacs/cliui': 8.0.2
@@ -9262,6 +9781,8 @@ snapshots:
dependencies:
remedial: 1.0.8
+ json5@2.2.3: {}
+
jsonc-eslint-parser@2.4.0:
dependencies:
acorn: 8.12.1
@@ -9431,14 +9952,30 @@ snapshots:
longest@2.0.1: {}
+ loupe@3.1.2: {}
+
lru-cache@10.4.3: {}
lru-cache@11.0.1: {}
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
magic-string@0.30.11:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
+ magicast@0.3.5:
+ dependencies:
+ '@babel/parser': 7.25.6
+ '@babel/types': 7.25.6
+ source-map-js: 1.2.1
+
+ make-dir@4.0.0:
+ dependencies:
+ semver: 7.6.3
+
mark.js@8.11.1: {}
markdown-it-anchor@9.2.0(@types/markdown-it@14.1.2)(markdown-it@14.1.0):
@@ -10124,6 +10661,8 @@ snapshots:
pathe@1.1.2: {}
+ pathval@2.0.0: {}
+
perfect-debounce@1.0.0: {}
photoswipe@5.4.4: {}
@@ -10521,6 +11060,8 @@ snapshots:
semver@5.7.2: {}
+ semver@6.3.1: {}
+
semver@7.6.3: {}
set-function-length@1.2.2:
@@ -10556,6 +11097,8 @@ snapshots:
get-intrinsic: 1.2.4
object-inspect: 1.13.2
+ siginfo@2.0.0: {}
+
signal-exit@3.0.7: {}
signal-exit@4.1.0: {}
@@ -10634,6 +11177,10 @@ snapshots:
stable-hash@0.0.4: {}
+ stackback@0.0.2: {}
+
+ std-env@3.7.0: {}
+
stdin-discarder@0.2.2: {}
string-argv@0.3.2: {}
@@ -10868,6 +11415,12 @@ snapshots:
dependencies:
temp-dir: 3.0.0
+ test-exclude@7.0.1:
+ dependencies:
+ '@istanbuljs/schema': 0.1.3
+ glob: 10.4.5
+ minimatch: 9.0.5
+
text-extensions@2.4.0: {}
text-table@0.2.0: {}
@@ -10882,6 +11435,8 @@ snapshots:
through@2.3.8: {}
+ tinybench@2.9.0: {}
+
tinyexec@0.3.0: {}
tinyglobby@0.2.7:
@@ -10889,6 +11444,12 @@ snapshots:
fdir: 6.4.0(picomatch@4.0.2)
picomatch: 4.0.2
+ tinypool@1.0.1: {}
+
+ tinyrainbow@1.2.0: {}
+
+ tinyspy@3.0.2: {}
+
tm-grammars@1.18.0: {}
tm-themes@1.8.7: {}
@@ -11074,6 +11635,23 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.2
+ vite-node@2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4):
+ dependencies:
+ cac: 6.7.14
+ debug: 4.3.7
+ pathe: 1.1.2
+ vite: 5.4.8(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4)
+ transitivePeerDependencies:
+ - '@types/node'
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+
vite@5.4.8(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4):
dependencies:
esbuild: 0.24.0
@@ -11085,6 +11663,40 @@ snapshots:
sass: 1.79.4
sass-embedded: 1.79.4
+ vitest@2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4):
+ dependencies:
+ '@vitest/expect': 2.1.2
+ '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.4.8(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4))
+ '@vitest/pretty-format': 2.1.2
+ '@vitest/runner': 2.1.2
+ '@vitest/snapshot': 2.1.2
+ '@vitest/spy': 2.1.2
+ '@vitest/utils': 2.1.2
+ chai: 5.1.1
+ debug: 4.3.7
+ magic-string: 0.30.11
+ pathe: 1.1.2
+ std-env: 3.7.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.0
+ tinypool: 1.0.1
+ tinyrainbow: 1.2.0
+ vite: 5.4.8(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4)
+ vite-node: 2.1.2(@types/node@20.12.10)(sass-embedded@1.79.4)(sass@1.79.4)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 20.12.10
+ transitivePeerDependencies:
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+
vscode-jsonrpc@8.2.0: {}
vscode-languageserver-protocol@3.17.5:
@@ -11245,6 +11857,11 @@ snapshots:
dependencies:
isexe: 2.0.0
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
wicked-good-xpath@1.3.0: {}
word-wrap@1.2.5: {}
@@ -11282,6 +11899,8 @@ snapshots:
y18n@5.0.8: {}
+ yallist@3.1.1: {}
+
yallist@4.0.0: {}
yaml-eslint-parser@1.2.3:
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 00000000..ff830ed0
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,12 @@
+import { defineConfig } from 'vitest/config'
+
+export default defineConfig({
+ test: {
+ coverage: {
+ enabled: true,
+ provider: 'istanbul',
+ all: false,
+ reporter: ['text', 'clover', 'json'],
+ },
+ },
+})