diff --git a/samples/ast-collector/.vscode/settings.json b/samples/ast-collector/.vscode/settings.json
new file mode 100644
index 00000000..03a29feb
--- /dev/null
+++ b/samples/ast-collector/.vscode/settings.json
@@ -0,0 +1,10 @@
+{
+ "vitest.nodeEnv": {
+ "TEST_CUSTOM_ENV": "hello"
+ },
+ "vitest.experimentalStaticAstCollect": true,
+ "[typescript]": {
+ "editor.defaultFormatter": "dbaeumer.vscode-eslint"
+ },
+ "workbench.sash.hoverDelay": 2000
+}
diff --git a/samples/ast-collector/package.json b/samples/ast-collector/package.json
new file mode 100644
index 00000000..8df3aafe
--- /dev/null
+++ b/samples/ast-collector/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "basic",
+ "version": "1.0.0",
+ "description": "",
+ "author": "",
+ "license": "ISC",
+ "main": "index.js",
+ "scripts": {
+ "test": "vitest run"
+ },
+ "dependencies": {
+ "birpc": "^0.2.2"
+ },
+ "devDependencies": {
+ "@vitest/coverage-v8": "^3.0.4",
+ "vite": "^6.0.11",
+ "vitest": "^3.0.4"
+ }
+}
diff --git a/samples/ast-collector/pnpm-lock.yaml b/samples/ast-collector/pnpm-lock.yaml
new file mode 100644
index 00000000..2a5e0a7c
--- /dev/null
+++ b/samples/ast-collector/pnpm-lock.yaml
@@ -0,0 +1,1218 @@
+lockfileVersion: '6.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+dependencies:
+ birpc:
+ specifier: ^0.2.2
+ version: registry.npmmirror.com/birpc@0.2.3
+
+devDependencies:
+ '@vitest/coverage-v8':
+ specifier: ^3.0.4
+ version: 3.0.4(vitest@3.0.4)
+ vite:
+ specifier: ^6.0.11
+ version: 6.0.11
+ vitest:
+ specifier: ^3.0.4
+ version: 3.0.4
+
+packages:
+
+ /@ampproject/remapping@2.3.0:
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+ dev: true
+
+ /@babel/helper-string-parser@7.25.9:
+ resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/helper-validator-identifier@7.25.9:
+ resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/parser@7.26.5:
+ resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+ dependencies:
+ '@babel/types': 7.26.5
+ dev: true
+
+ /@babel/types@7.26.5:
+ resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-string-parser': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
+ dev: true
+
+ /@bcoe/v8-coverage@1.0.2:
+ resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
+ engines: {node: '>=18'}
+ dev: true
+
+ /@esbuild/aix-ppc64@0.24.2:
+ resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-arm64@0.24.2:
+ resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-arm@0.24.2:
+ resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-x64@0.24.2:
+ resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/darwin-arm64@0.24.2:
+ resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/darwin-x64@0.24.2:
+ resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/freebsd-arm64@0.24.2:
+ resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/freebsd-x64@0.24.2:
+ resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-arm64@0.24.2:
+ resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-arm@0.24.2:
+ resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-ia32@0.24.2:
+ resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-loong64@0.24.2:
+ resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-mips64el@0.24.2:
+ resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-ppc64@0.24.2:
+ resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-riscv64@0.24.2:
+ resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-s390x@0.24.2:
+ resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-x64@0.24.2:
+ resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/netbsd-arm64@0.24.2:
+ resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/netbsd-x64@0.24.2:
+ resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/openbsd-arm64@0.24.2:
+ resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/openbsd-x64@0.24.2:
+ resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/sunos-x64@0.24.2:
+ resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-arm64@0.24.2:
+ resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-ia32@0.24.2:
+ resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-x64@0.24.2:
+ resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@isaacs/cliui@8.0.2:
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: /string-width@4.2.3
+ strip-ansi: 7.1.0
+ strip-ansi-cjs: /strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: /wrap-ansi@7.0.0
+ dev: true
+
+ /@istanbuljs/schema@0.1.3:
+ resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /@jridgewell/gen-mapping@0.3.5:
+ resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
+ engines: {node: '>=6.0.0'}
+ dependencies:
+ '@jridgewell/set-array': 1.2.1
+ '@jridgewell/sourcemap-codec': 1.5.0
+ '@jridgewell/trace-mapping': 0.3.25
+ dev: true
+
+ /@jridgewell/resolve-uri@3.1.2:
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+ dev: true
+
+ /@jridgewell/set-array@1.2.1:
+ resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
+ engines: {node: '>=6.0.0'}
+ dev: true
+
+ /@jridgewell/sourcemap-codec@1.5.0:
+ resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
+ dev: true
+
+ /@jridgewell/trace-mapping@0.3.25:
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.0
+ dev: true
+
+ /@pkgjs/parseargs@0.11.0:
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-android-arm-eabi@4.24.0:
+ resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-android-arm64@4.24.0:
+ resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-darwin-arm64@4.24.0:
+ resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-darwin-x64@4.24.0:
+ resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm-gnueabihf@4.24.0:
+ resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm-musleabihf@4.24.0:
+ resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm64-gnu@4.24.0:
+ resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-arm64-musl@4.24.0:
+ resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-powerpc64le-gnu@4.24.0:
+ resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==}
+ cpu: [ppc64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-riscv64-gnu@4.24.0:
+ resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-s390x-gnu@4.24.0:
+ resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==}
+ cpu: [s390x]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-x64-gnu@4.24.0:
+ resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-linux-x64-musl@4.24.0:
+ resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-arm64-msvc@4.24.0:
+ resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-ia32-msvc@4.24.0:
+ resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@rollup/rollup-win32-x64-msvc@4.24.0:
+ resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@types/estree@1.0.6:
+ resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+ dev: true
+
+ /@vitest/coverage-v8@3.0.4(vitest@3.0.4):
+ resolution: {integrity: sha512-f0twgRCHgbs24Dp8cLWagzcObXMcuKtAwgxjJV/nnysPAJJk1JiKu/W0gIehZLmkljhJXU/E0/dmuQzsA/4jhA==}
+ peerDependencies:
+ '@vitest/browser': 3.0.4
+ vitest: 3.0.4
+ peerDependenciesMeta:
+ '@vitest/browser':
+ optional: true
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@bcoe/v8-coverage': 1.0.2
+ debug: 4.4.0
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.1.7
+ magic-string: 0.30.17
+ magicast: 0.3.5
+ std-env: 3.8.0
+ test-exclude: 7.0.1
+ tinyrainbow: 2.0.0
+ vitest: 3.0.4
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@vitest/expect@3.0.4:
+ resolution: {integrity: sha512-Nm5kJmYw6P2BxhJPkO3eKKhGYKRsnqJqf+r0yOGRKpEP+bSCBDsjXgiu1/5QFrnPMEgzfC38ZEjvCFgaNBC0Eg==}
+ dependencies:
+ '@vitest/spy': 3.0.4
+ '@vitest/utils': 3.0.4
+ chai: 5.1.2
+ tinyrainbow: 2.0.0
+ dev: true
+
+ /@vitest/mocker@3.0.4(vite@6.0.11):
+ resolution: {integrity: sha512-gEef35vKafJlfQbnyOXZ0Gcr9IBUsMTyTLXsEQwuyYAerpHqvXhzdBnDFuHLpFqth3F7b6BaFr4qV/Cs1ULx5A==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^5.0.0 || ^6.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+ dependencies:
+ '@vitest/spy': 3.0.4
+ estree-walker: 3.0.3
+ magic-string: 0.30.17
+ vite: 6.0.11
+ dev: true
+
+ /@vitest/pretty-format@3.0.4:
+ resolution: {integrity: sha512-ts0fba+dEhK2aC9PFuZ9LTpULHpY/nd6jhAQ5IMU7Gaj7crPCTdCFfgvXxruRBLFS+MLraicCuFXxISEq8C93g==}
+ dependencies:
+ tinyrainbow: 2.0.0
+ dev: true
+
+ /@vitest/runner@3.0.4:
+ resolution: {integrity: sha512-dKHzTQ7n9sExAcWH/0sh1elVgwc7OJ2lMOBrAm73J7AH6Pf9T12Zh3lNE1TETZaqrWFXtLlx3NVrLRb5hCK+iw==}
+ dependencies:
+ '@vitest/utils': 3.0.4
+ pathe: 2.0.2
+ dev: true
+
+ /@vitest/snapshot@3.0.4:
+ resolution: {integrity: sha512-+p5knMLwIk7lTQkM3NonZ9zBewzVp9EVkVpvNta0/PlFWpiqLaRcF4+33L1it3uRUCh0BGLOaXPPGEjNKfWb4w==}
+ dependencies:
+ '@vitest/pretty-format': 3.0.4
+ magic-string: 0.30.17
+ pathe: 2.0.2
+ dev: true
+
+ /@vitest/spy@3.0.4:
+ resolution: {integrity: sha512-sXIMF0oauYyUy2hN49VFTYodzEAu744MmGcPR3ZBsPM20G+1/cSW/n1U+3Yu/zHxX2bIDe1oJASOkml+osTU6Q==}
+ dependencies:
+ tinyspy: 3.0.2
+ dev: true
+
+ /@vitest/utils@3.0.4:
+ resolution: {integrity: sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==}
+ dependencies:
+ '@vitest/pretty-format': 3.0.4
+ loupe: 3.1.2
+ tinyrainbow: 2.0.0
+ dev: true
+
+ /ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /ansi-regex@6.1.0:
+ resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
+ engines: {node: '>=12'}
+ dev: true
+
+ /ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+ dependencies:
+ color-convert: 2.0.1
+ dev: true
+
+ /ansi-styles@6.2.1:
+ resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+ engines: {node: '>=12'}
+ dev: true
+
+ /assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+ dev: true
+
+ /balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ dev: true
+
+ /brace-expansion@2.0.1:
+ resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+ dependencies:
+ balanced-match: 1.0.2
+ dev: true
+
+ /cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /chai@5.1.2:
+ resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==}
+ engines: {node: '>=12'}
+ dependencies:
+ assertion-error: 2.0.1
+ check-error: 2.1.1
+ deep-eql: 5.0.2
+ loupe: 3.1.2
+ pathval: 2.0.0
+ dev: true
+
+ /check-error@2.1.1:
+ resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
+ engines: {node: '>= 16'}
+ dev: true
+
+ /color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+ dependencies:
+ color-name: 1.1.4
+ dev: true
+
+ /color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ dev: true
+
+ /cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+ dev: true
+
+ /debug@4.4.0:
+ resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+ dependencies:
+ ms: 2.1.3
+ dev: true
+
+ /deep-eql@5.0.2:
+ resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+ dev: true
+
+ /emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+ dev: true
+
+ /emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+ dev: true
+
+ /es-module-lexer@1.6.0:
+ resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==}
+ dev: true
+
+ /esbuild@0.24.2:
+ resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==}
+ engines: {node: '>=18'}
+ hasBin: true
+ requiresBuild: true
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.24.2
+ '@esbuild/android-arm': 0.24.2
+ '@esbuild/android-arm64': 0.24.2
+ '@esbuild/android-x64': 0.24.2
+ '@esbuild/darwin-arm64': 0.24.2
+ '@esbuild/darwin-x64': 0.24.2
+ '@esbuild/freebsd-arm64': 0.24.2
+ '@esbuild/freebsd-x64': 0.24.2
+ '@esbuild/linux-arm': 0.24.2
+ '@esbuild/linux-arm64': 0.24.2
+ '@esbuild/linux-ia32': 0.24.2
+ '@esbuild/linux-loong64': 0.24.2
+ '@esbuild/linux-mips64el': 0.24.2
+ '@esbuild/linux-ppc64': 0.24.2
+ '@esbuild/linux-riscv64': 0.24.2
+ '@esbuild/linux-s390x': 0.24.2
+ '@esbuild/linux-x64': 0.24.2
+ '@esbuild/netbsd-arm64': 0.24.2
+ '@esbuild/netbsd-x64': 0.24.2
+ '@esbuild/openbsd-arm64': 0.24.2
+ '@esbuild/openbsd-x64': 0.24.2
+ '@esbuild/sunos-x64': 0.24.2
+ '@esbuild/win32-arm64': 0.24.2
+ '@esbuild/win32-ia32': 0.24.2
+ '@esbuild/win32-x64': 0.24.2
+ dev: true
+
+ /estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+ dependencies:
+ '@types/estree': 1.0.6
+ dev: true
+
+ /expect-type@1.1.0:
+ resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==}
+ engines: {node: '>=12.0.0'}
+ dev: true
+
+ /foreground-child@3.3.0:
+ resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
+ engines: {node: '>=14'}
+ dependencies:
+ cross-spawn: 7.0.6
+ signal-exit: 4.1.0
+ dev: true
+
+ /fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /glob@10.4.5:
+ resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+ hasBin: true
+ dependencies:
+ foreground-child: 3.3.0
+ jackspeak: 3.4.3
+ minimatch: 9.0.5
+ minipass: 7.1.2
+ package-json-from-dist: 1.0.1
+ path-scurry: 1.11.1
+ dev: true
+
+ /has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /html-escaper@2.0.2:
+ resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+ dev: true
+
+ /is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+ dev: true
+
+ /istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /istanbul-lib-report@3.0.1:
+ resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+ engines: {node: '>=10'}
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+ dev: true
+
+ /istanbul-lib-source-maps@5.0.6:
+ resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==}
+ engines: {node: '>=10'}
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.25
+ debug: 4.4.0
+ istanbul-lib-coverage: 3.2.2
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /istanbul-reports@3.1.7:
+ resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
+ engines: {node: '>=8'}
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+ dev: true
+
+ /jackspeak@3.4.3:
+ resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+ dev: true
+
+ /loupe@3.1.2:
+ resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==}
+ dev: true
+
+ /lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+ dev: true
+
+ /lru-cache@6.0.0:
+ resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+ engines: {node: '>=10'}
+ dependencies:
+ yallist: 4.0.0
+ dev: true
+
+ /magic-string@0.30.17:
+ resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.0
+ dev: true
+
+ /magicast@0.3.5:
+ resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
+ dependencies:
+ '@babel/parser': 7.26.5
+ '@babel/types': 7.26.5
+ source-map-js: 1.2.1
+ dev: true
+
+ /make-dir@4.0.0:
+ resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+ engines: {node: '>=10'}
+ dependencies:
+ semver: 7.6.0
+ dev: true
+
+ /minimatch@9.0.5:
+ resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ dependencies:
+ brace-expansion: 2.0.1
+ dev: true
+
+ /minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ dev: true
+
+ /ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+ dev: true
+
+ /nanoid@3.3.8:
+ resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+ dev: true
+
+ /package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+ dev: true
+
+ /path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
+ dependencies:
+ lru-cache: 10.4.3
+ minipass: 7.1.2
+ dev: true
+
+ /pathe@2.0.2:
+ resolution: {integrity: sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==}
+ dev: true
+
+ /pathval@2.0.0:
+ resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
+ engines: {node: '>= 14.16'}
+ dev: true
+
+ /picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+ dev: true
+
+ /postcss@8.5.1:
+ resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.8
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+ dev: true
+
+ /rollup@4.24.0:
+ resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+ dependencies:
+ '@types/estree': 1.0.6
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.24.0
+ '@rollup/rollup-android-arm64': 4.24.0
+ '@rollup/rollup-darwin-arm64': 4.24.0
+ '@rollup/rollup-darwin-x64': 4.24.0
+ '@rollup/rollup-linux-arm-gnueabihf': 4.24.0
+ '@rollup/rollup-linux-arm-musleabihf': 4.24.0
+ '@rollup/rollup-linux-arm64-gnu': 4.24.0
+ '@rollup/rollup-linux-arm64-musl': 4.24.0
+ '@rollup/rollup-linux-powerpc64le-gnu': 4.24.0
+ '@rollup/rollup-linux-riscv64-gnu': 4.24.0
+ '@rollup/rollup-linux-s390x-gnu': 4.24.0
+ '@rollup/rollup-linux-x64-gnu': 4.24.0
+ '@rollup/rollup-linux-x64-musl': 4.24.0
+ '@rollup/rollup-win32-arm64-msvc': 4.24.0
+ '@rollup/rollup-win32-ia32-msvc': 4.24.0
+ '@rollup/rollup-win32-x64-msvc': 4.24.0
+ fsevents: 2.3.3
+ dev: true
+
+ /semver@7.6.0:
+ resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ lru-cache: 6.0.0
+ dev: true
+
+ /shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+ dependencies:
+ shebang-regex: 3.0.0
+ dev: true
+
+ /shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+ dev: true
+
+ /signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+ dev: true
+
+ /source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+ dev: true
+
+ /std-env@3.8.0:
+ resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==}
+ dev: true
+
+ /string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+ dev: true
+
+ /string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.1.0
+ dev: true
+
+ /strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+ dependencies:
+ ansi-regex: 5.0.1
+ dev: true
+
+ /strip-ansi@7.1.0:
+ resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ ansi-regex: 6.1.0
+ dev: true
+
+ /supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+ dependencies:
+ has-flag: 4.0.0
+ dev: true
+
+ /test-exclude@7.0.1:
+ resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==}
+ engines: {node: '>=18'}
+ dependencies:
+ '@istanbuljs/schema': 0.1.3
+ glob: 10.4.5
+ minimatch: 9.0.5
+ dev: true
+
+ /tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+ dev: true
+
+ /tinyexec@0.3.2:
+ resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
+ dev: true
+
+ /tinypool@1.0.2:
+ resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ dev: true
+
+ /tinyrainbow@2.0.0:
+ resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
+ /tinyspy@3.0.2:
+ resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
+ /vite-node@3.0.4:
+ resolution: {integrity: sha512-7JZKEzcYV2Nx3u6rlvN8qdo3QV7Fxyt6hx+CCKz9fbWxdX5IvUOmTWEAxMrWxaiSf7CKGLJQ5rFu8prb/jBjOA==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ dependencies:
+ cac: 6.7.14
+ debug: 4.4.0
+ es-module-lexer: 1.6.0
+ pathe: 2.0.2
+ vite: 6.0.11
+ transitivePeerDependencies:
+ - '@types/node'
+ - jiti
+ - less
+ - lightningcss
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+ dev: true
+
+ /vite@6.0.11:
+ resolution: {integrity: sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ jiti: '>=1.21.0'
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+ dependencies:
+ esbuild: 0.24.2
+ postcss: 8.5.1
+ rollup: 4.24.0
+ optionalDependencies:
+ fsevents: 2.3.3
+ dev: true
+
+ /vitest@3.0.4:
+ resolution: {integrity: sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==}
+ engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/debug': ^4.1.12
+ '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ '@vitest/browser': 3.0.4
+ '@vitest/ui': 3.0.4
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/debug':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+ dependencies:
+ '@vitest/expect': 3.0.4
+ '@vitest/mocker': 3.0.4(vite@6.0.11)
+ '@vitest/pretty-format': 3.0.4
+ '@vitest/runner': 3.0.4
+ '@vitest/snapshot': 3.0.4
+ '@vitest/spy': 3.0.4
+ '@vitest/utils': 3.0.4
+ chai: 5.1.2
+ debug: 4.4.0
+ expect-type: 1.1.0
+ magic-string: 0.30.17
+ pathe: 2.0.2
+ std-env: 3.8.0
+ tinybench: 2.9.0
+ tinyexec: 0.3.2
+ tinypool: 1.0.2
+ tinyrainbow: 2.0.0
+ vite: 6.0.11
+ vite-node: 3.0.4
+ why-is-node-running: 2.3.0
+ transitivePeerDependencies:
+ - jiti
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+ dev: true
+
+ /which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+ dependencies:
+ isexe: 2.0.0
+ dev: true
+
+ /why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+ dev: true
+
+ /wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ dev: true
+
+ /wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 5.1.2
+ strip-ansi: 7.1.0
+ dev: true
+
+ /yallist@4.0.0:
+ resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+ dev: true
+
+ registry.npmmirror.com/birpc@0.2.3:
+ resolution: {integrity: sha512-mG7m06C2JkfuHSaLRHhtHtMEvyT1P1nUyyuk5W/7LMT2p7YYX/tfzJzD2ynZZHem3JTi6yJve0nHPdrs/gpXYg==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/birpc/-/birpc-0.2.3.tgz}
+ name: birpc
+ version: 0.2.3
+ dev: false
diff --git a/samples/ast-collector/src/add.ts b/samples/ast-collector/src/add.ts
new file mode 100644
index 00000000..a0e19156
--- /dev/null
+++ b/samples/ast-collector/src/add.ts
@@ -0,0 +1,17 @@
+
+export function add(a: number, b: number) {
+ return a + b
+}
+
+export function sum(from: number, to: number) {
+ return (from + to) * (to - from + 1) / 2
+}
+
+export function addError(from: number, to: number) {
+ doSomething()
+ return add(from, to)
+}
+
+function doSomething() {
+ throw new Error('Something went wrong');
+}
diff --git a/samples/ast-collector/src/should_included_test.ts b/samples/ast-collector/src/should_included_test.ts
new file mode 100644
index 00000000..eb51c768
--- /dev/null
+++ b/samples/ast-collector/src/should_included_test.ts
@@ -0,0 +1,5 @@
+import { describe, it } from 'vitest'
+
+describe('should included', () => {
+ it('is included because of workspace plugin setting', () => {})
+})
diff --git a/samples/ast-collector/test/each.test.ts b/samples/ast-collector/test/each.test.ts
new file mode 100644
index 00000000..d51037ed
--- /dev/null
+++ b/samples/ast-collector/test/each.test.ts
@@ -0,0 +1,26 @@
+import { describe, expect, it, test, } from 'vitest'
+
+describe('testing', (a) => {
+ it.each([
+ [1, 1], [2, 2]
+ ])(`all pass: %i => %i`, (a, b) => {
+ expect(a).toBe(b)
+ })
+ test.each`
+ a | b | expected
+ ${1} | ${1} | ${2}
+ ${'a'} | ${'b'} | ${'ab'}
+ `('table1: returns $expected when $a is added $b', ({ a, b, expected }) => {
+ expect(a + b).toBe(expected)
+ })
+})
+
+describe.each([1, 2])('testing %s', () => {
+ it('hello world', () => {
+ expect(true).toBe(true)
+ })
+
+ it.each([3, 4])('testing test %s', (a) => {
+ expect(a).toBe(a)
+ })
+})
diff --git a/samples/ast-collector/vitest.config.ts b/samples/ast-collector/vitest.config.ts
new file mode 100644
index 00000000..98c49e1c
--- /dev/null
+++ b/samples/ast-collector/vitest.config.ts
@@ -0,0 +1,11 @@
+///
+
+// Configure Vitest (https://vitest.dev/config)
+
+import { defineConfig } from 'vite'
+
+export default defineConfig({
+ esbuild: {
+ target: 'es2022',
+ },
+})
diff --git a/samples/basic/test/each.test.ts b/samples/basic/test/each.test.ts
index f7cff915..b0304d75 100644
--- a/samples/basic/test/each.test.ts
+++ b/samples/basic/test/each.test.ts
@@ -1,84 +1,93 @@
import { describe, expect, it, test, } from 'vitest'
-describe('testing', (a) => {
- it.each([
- [1, 1], [2, 2], [3, 3]
- ])(`all pass: %i => %i`, (a, b) => {
- expect(a).toBe(b)
- })
- it.each([
- [1, 1], [2, 1], [3, 1]
- ])(`first pass: %i => %i`, (a, b) => {
- expect(a).toBe(b)
- })
- it.each([
- [1, 1], [2, 2], [3, 1]
- ])(`last pass: %i => %i`, (a, b) => {
- expect(a).toBe(b)
- })
- it.each([
- [1, 1], [2, 2], [3, 1]
- ])(`first fail: %i => %i`, (a, b) => {
- expect(a).toBe(b)
- })
- it.each([
- [1, 1], [2, 2], [3, 1]
- ])(`last fail: %i => %i`, (a, b) => {
- expect(a).toBe(b)
- })
- it.each([
- [1, 0], [2, 0], [3, 0]
- ])(`all fail: %i => %i`, (a, b) => {
- expect(a).toBe(b)
- })
- it.each([
- 1, 2, 3
- ])('run %i', (a) => {
- expect(a).toBe(a)
- })
- it.each([
- [1, 1], [2, 4], [3, 9]
- ])('run mul %i', (a,b) => {
- expect(a * a).toBe(b)
- })
- test.each([
- ["test1", 1],
- ["test2", 2],
- ["test3", 3],
- ])(`%s => %i`, (a, b) => {
- expect(a.at(-1)).toBe(`${b}`)
- })
- test.each`
- a | b | expected
- ${1} | ${1} | ${2}
- ${'a'} | ${'b'} | ${'ab'}
- ${[]} | ${'b'} | ${'b'}
- ${{}} | ${'b'} | ${'[object Object]b'}
- ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'}
- `('table1: returns $expected when $a is added $b', ({ a, b, expected }) => {
- expect(a + b).toBe(expected)
- })
- test.each`
- a | b | expected
- ${{v: 1}} | ${{v: 1}} | ${2}
- `('table2: returns $expected when $a.v is added $b.v', ({ a, b, expected }) => {
- expect(a.v + b.v).toBe(expected)
- })
- test.each([
- { input: 1, add: 1, sum: 2 },
- { input: 2, add: 2, sum: 4 },
- ])('$input + $add = $sum', ({ input, add, sum }) => {
- expect(input + add).toBe(sum)
- })
-})
-// 'Test result not fourd' error occurs as both .each patterns are matched
-// TODO: Fix this
-describe("over matched test patterns", () => {
- test.each(['1', '2'])('run %s', (a) => {
- expect(a).toBe(String(a))
- })
- test.each(['1', '2'])('run for %s', (a) => {
- expect(a).toBe(String(a))
+describe.each([0, 3])('some terr %s.', () => {
+ describe.each([1, 2])('testing %s', () => {
+ it.each([3, 4])('testing %s and %s', (a) => {
+ expect(a).toBe(a)
+ })
})
})
+
+// describe('testing', (a) => {
+// it.each([
+// [1, 1], [2, 2], [3, 3]
+// ])(`all pass: %i => %i`, (a, b) => {
+// expect(a).toBe(b)
+// })
+// it.each([
+// [1, 1], [2, 1], [3, 1]
+// ])(`first pass: %i => %i`, (a, b) => {
+// expect(a).toBe(b)
+// })
+// it.each([
+// [1, 1], [2, 2], [3, 1]
+// ])(`last pass: %i => %i`, (a, b) => {
+// expect(a).toBe(b)
+// })
+// it.each([
+// [1, 1], [2, 2], [3, 1]
+// ])(`first fail: %i => %i`, (a, b) => {
+// expect(a).toBe(b)
+// })
+// it.each([
+// [1, 1], [2, 2], [3, 1]
+// ])(`last fail: %i => %i`, (a, b) => {
+// expect(a).toBe(b)
+// })
+// it.each([
+// [1, 0], [2, 0], [3, 0]
+// ])(`all fail: %i => %i`, (a, b) => {
+// expect(a).toBe(b)
+// })
+// it.each([
+// 1, 2, 3
+// ])('run %i', (a) => {
+// expect(a).toBe(a)
+// })
+// it.each([
+// [1, 1], [2, 4], [3, 9]
+// ])('run mul %i', (a,b) => {
+// expect(a * a).toBe(b)
+// })
+// test.each([
+// ["test1", 1],
+// ["test2", 2],
+// ["test3", 3],
+// ])(`%s => %i`, (a, b) => {
+// expect(a.at(-1)).toBe(`${b}`)
+// })
+// test.each`
+// a | b | expected
+// ${1} | ${1} | ${2}
+// ${'a'} | ${'b'} | ${'ab'}
+// ${[]} | ${'b'} | ${'b'}
+// ${{}} | ${'b'} | ${'[object Object]b'}
+// ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'}
+// `('table1: returns $expected when $a is added $b', ({ a, b, expected }) => {
+// expect(a + b).toBe(expected)
+// })
+// test.each`
+// a | b | expected
+// ${{v: 1}} | ${{v: 1}} | ${2}
+// `('table2: returns $expected when $a.v is added $b.v', ({ a, b, expected }) => {
+// expect(a.v + b.v).toBe(expected)
+// })
+// test.each([
+// { input: 1, add: 1, sum: 2 },
+// { input: 2, add: 2, sum: 4 },
+// ])('$input + $add = $sum', ({ input, add, sum }) => {
+// expect(input + add).toBe(sum)
+// })
+// })
+
+// // 'Test result not fourd' error occurs as both .each patterns are matched
+// // TODO: Fix this
+// describe("over matched test patterns", () => {
+// test.each(['1', '2'])('run %s', (a) => {
+// expect(a).toBe(String(a))
+// })
+// test.each(['1', '2'])('run for %s', (a) => {
+// expect(a).toBe(String(a))
+// })
+// })
diff --git a/src/runner.ts b/src/runner.ts
index 2f66911d..bfa54299 100644
--- a/src/runner.ts
+++ b/src/runner.ts
@@ -436,7 +436,7 @@ export class TestRunner extends vscode.Disposable {
function enqueue(test: vscode.TestItem) {
const testData = getTestData(test)
// we only change the state of test cases to keep the correct test count
- if (testData instanceof TestCase) {
+ if (testData instanceof TestCase && !testData.dynamic) {
log.verbose?.(`Enqueuing "${test.label}"`)
run.enqueued(test)
}
diff --git a/src/testTree.ts b/src/testTree.ts
index 39460675..ab3f580d 100644
--- a/src/testTree.ts
+++ b/src/testTree.ts
@@ -297,8 +297,22 @@ export class TestTree extends vscode.Disposable {
fileTestItem.canResolveChildren = false
}
+ private cacheDynamic: {
+ [file: string]: {
+ [dynamicTitle: string]: {
+ id: string
+ type: 'test' | 'suite'
+ children: Set
+ }
+ }
+ } = {}
+
collectTasks(tag: vscode.TestTag, fileData: TestFile, tasks: RunnerTask[], parent: vscode.TestItem) {
+ const fileCachedTests = this.cacheDynamic[fileData.filepath] || (this.cacheDynamic[fileData.filepath] = {})
+ const ids = new Set()
+
for (const task of tasks) {
+ ids.add(task.id)
const cachedItem = this.flatTestItems.get(task.id)
// suite became a test or vice versa
if (cachedItem) {
@@ -327,7 +341,7 @@ export class TestTree extends vscode.Disposable {
log.error(`Cannot find location for "${testItem.label}". Using "id" to sort instead.`)
testItem.sortText = task.id
}
- // dynamic exists only during browser collection
+ // dynamic exists only during AST collection
// see src/worker/collect.ts:172
const isDynamic = (task as any).dynamic
if (task.type === 'suite')
@@ -335,6 +349,64 @@ export class TestTree extends vscode.Disposable {
else if (task.type === 'test' || task.type === 'custom')
TestCase.register(testItem, parent, fileData, isDynamic)
+ if (isDynamic) {
+ testItem.description = 'pattern'
+ const dynamicTestRegExp = (getTestData(testItem) as TestCase | TestSuite).getTestNamePattern()
+
+ const cachedDynamicTest = fileCachedTests[dynamicTestRegExp] || (fileCachedTests[dynamicTestRegExp] = {
+ id: task.id,
+ type: task.type === 'custom' ? 'test' : task.type,
+ children: new Set(),
+ })
+ cachedDynamicTest.children.forEach((fileId) => {
+ // don't remove tests that were collected during runtime
+ ids.add(fileId)
+ })
+ }
+ else if (task.each) {
+ const fullName = getTaskFullName(task)
+ // order in the opposite order so we only match one item with the longest name
+ const orderedTests = Object.entries(fileCachedTests).sort(([a1], [a2]) => a2.localeCompare(a1))
+ for (const [testRegexp, cachedDynamicTask] of orderedTests) {
+ if (new RegExp(testRegexp).test(fullName)) {
+ const testId = cachedDynamicTask.id
+ const childId = `${testId}_${task.suite?.id || 'none'}`
+
+ // keep the dynamic pattern to display it alongside normal tests,
+ // if the parent suite was also dynamic, this item will be duplicated
+ // in every suite, but scoped only to that suite
+ const dynamicTestItem = this.flatTestItems.get(testId)
+ ids.add(childId)
+ if (dynamicTestItem) {
+ // we are creating a separate one because we can't use the same one in multiple places
+ const suiteCopyChild = this.flatTestItems.get(childId) || this.controller.createTestItem(
+ childId,
+ dynamicTestItem.label,
+ dynamicTestItem.uri,
+ )
+ this.flatTestItems.set(childId, suiteCopyChild)
+ suiteCopyChild.tags = dynamicTestItem.tags
+ suiteCopyChild.canResolveChildren = dynamicTestItem.canResolveChildren
+ suiteCopyChild.description = dynamicTestItem.description
+ suiteCopyChild.range = dynamicTestItem.range
+ suiteCopyChild.error = dynamicTestItem.error
+ suiteCopyChild.sortText = dynamicTestItem.sortText
+
+ if (task.type === 'suite') {
+ TestSuite.register(suiteCopyChild, parent, fileData, true)
+ }
+ else {
+ TestCase.register(suiteCopyChild, parent, fileData, true)
+ }
+
+ parent.children.add(suiteCopyChild)
+ break
+ }
+ cachedDynamicTask.children.add(task.id)
+ }
+ }
+ }
+
this.flatTestItems.set(task.id, testItem)
parent.children.add(testItem)
@@ -350,7 +422,6 @@ export class TestTree extends vscode.Disposable {
}
// remove tasks that are no longer present
- const ids = new Set(tasks.map(x => x.id))
parent.children.forEach((child) => {
if (!ids.has(child.id))
parent.children.delete(child.id)
@@ -383,3 +454,7 @@ function getAPIFromTestItem(testItem: vscode.TestItem): VitestFolderAPI | null {
return data.api
return data.file.api
}
+
+function getTaskFullName(task: RunnerTask): string {
+ return `${task.suite ? `${getTaskFullName(task.suite)} ` : ''}${task.name}`
+}
diff --git a/src/testTreeData.ts b/src/testTreeData.ts
index 4ba5662e..9e8d4c05 100644
--- a/src/testTreeData.ts
+++ b/src/testTreeData.ts
@@ -105,7 +105,7 @@ export class TestCase extends BaseTestData {
item: vscode.TestItem,
parent: vscode.TestItem,
public readonly file: TestFile,
- dynamic: boolean,
+ public readonly dynamic: boolean,
) {
super(item, parent)
this.name = new TaskName(this, dynamic)
diff --git a/src/worker/collect.ts b/src/worker/collect.ts
index c91e8da0..c1a31399 100644
--- a/src/worker/collect.ts
+++ b/src/worker/collect.ts
@@ -137,19 +137,23 @@ export function astParseFile(filepath: string, code: string) {
let start: number
const end = node.end
// .each or (0, __vite_ssr_exports_0__.test)()
- if (callee.type === 'CallExpression' || callee.type === 'SequenceExpression') {
+ if (
+ callee.type === 'CallExpression'
+ || callee.type === 'SequenceExpression'
+ || callee.type === 'TaggedTemplateExpression'
+ ) {
start = callee.end
}
- else if (callee.type === 'TaggedTemplateExpression') {
- start = callee.end + 1
- }
else {
start = node.start
}
- const {
- arguments: [messageNode],
- } = node
+ const messageNode = node.arguments?.[0]
+
+ if (messageNode == null) {
+ verbose?.(`Skipping node at ${node.start} because it doesn't have a name`)
+ return
+ }
const isQuoted = messageNode?.type === 'Literal' || messageNode?.type === 'TemplateLiteral'
const message = isQuoted
@@ -160,8 +164,14 @@ export function astParseFile(filepath: string, code: string) {
if (mode === 'skipIf' || mode === 'runIf') {
mode = 'skip'
}
+
const parentCalleeName = typeof callee?.callee === 'object' && callee?.callee.type === 'MemberExpression' && callee?.callee.property?.name
- const isDynamicEach = parentCalleeName === 'each' || parentCalleeName === 'for'
+ let isDynamicEach = parentCalleeName === 'each' || parentCalleeName === 'for'
+ if (!isDynamicEach && callee.type === 'TaggedTemplateExpression') {
+ const property = callee.tag?.property?.name
+ isDynamicEach = property === 'each' || property === 'for'
+ }
+
debug?.('Found', name, message, `(${mode})`)
definitions.push({
start,
@@ -243,7 +253,7 @@ export function createFileTask(
const file: ParsedFile = {
filepath: options.filepath,
type: 'suite',
- id: /* @__PURE__ */ generateHash(`${testFilepath}${options.name}`),
+ id: /* @__PURE__ */ generateHash(`${testFilepath}${options.name || ''}`),
name: testFilepath,
mode: 'run',
tasks: [],
@@ -469,8 +479,8 @@ function markDynamicTests(tasks: TaskBase[]) {
if ((task as any).dynamic) {
task.id += '-dynamic'
}
- if ('children' in task) {
- markDynamicTests(task.children as TaskBase[])
+ if ('tasks' in task) {
+ markDynamicTests(task.tasks as TaskBase[])
}
}
}
diff --git a/test-e2e/assertions.ts b/test-e2e/assertions.ts
index b573895d..a5138b01 100644
--- a/test-e2e/assertions.ts
+++ b/test-e2e/assertions.ts
@@ -49,7 +49,12 @@ expect.extend({
const depth = Number(await item.locator.getAttribute('aria-level'))
async function assert(test: string, level: number, state: TestState) {
- await expect(page.locator(`[aria-label*="${test} ${getTitleFromState(state)}"][aria-level="${level}"]`)).toBeVisible()
+ const [name, index] = test.split('|')
+ let locator = `[aria-label*="${name} ${getTitleFromState(state)}"][aria-level="${level}"]`
+ if (index) {
+ locator += `[data-index="${index}"]`
+ }
+ await expect(page.locator(locator)).toBeAttached()
}
async function traverse(tests: TestsTree, level = depth + 1) {
@@ -59,7 +64,12 @@ expect.extend({
await assert(test, level, item)
}
else {
- await expect(page.locator(`[aria-label*="${test}"][aria-level="${level}"]`)).toBeVisible()
+ const [name, index] = test.split('|')
+ let locator = `[aria-label*="${name}"][aria-level="${level}"]`
+ if (index) {
+ locator += `[data-index="${index}"]`
+ }
+ await expect(page.locator(locator)).toBeAttached()
await traverse(item, level + 1)
}
diff --git a/test-e2e/basic.test.ts b/test-e2e/basic.test.ts
index 316e1fb7..d9e59c7e 100644
--- a/test-e2e/basic.test.ts
+++ b/test-e2e/basic.test.ts
@@ -198,6 +198,67 @@ test('watcher updates the file if there are several config files', async ({ laun
})
})
+test('ast collector keeps the pattern on rerun', async ({ launch }) => {
+ const sample = 'samples/ast-collector'
+
+ const { tester } = await launch({
+ workspacePath: sample,
+ })
+
+ await tester.tree.expand('test/each.test.ts/testing')
+ // dynamic tests have a "pattern" label
+ await tester.tree.expand('test/each.test.ts/pattern')
+
+ const item = tester.tree.getFileItem('each.test.ts')
+
+ await expect(item).toHaveTests({
+ 'testing': {
+ // all pass: %i => %i
+ 'pattern|3': 'waiting',
+ // table1: returns $expected when $a is added $b
+ 'pattern|4': 'waiting',
+ },
+ // testing %s
+ 'pattern|5': {
+ 'hello world': 'waiting',
+ // testing %s and %s
+ 'pattern|7': 'waiting',
+ },
+ })
+
+ await tester.runAllTests()
+
+ await tester.tree.expand('test/each.test.ts/testing 1')
+ await tester.tree.expand('test/each.test.ts/testing 2')
+
+ await expect(item).toHaveTests({
+ 'testing|2': {
+ // all pass: %i => %i
+ 'pattern|3': 'waiting',
+ 'all pass: 1 => 1': 'passed',
+ 'all pass: 2 => 2': 'passed',
+ // table1: returns $expected when $a is added $b
+ 'pattern|6': 'waiting',
+ 'table1: returns 2 when 1 is added 1': 'passed',
+ 'table1: returns \'ab\' when \'a\' is added \'b\'': 'passed',
+ },
+ // testing %s
+ 'pattern|9': 'waiting',
+ 'testing 1': {
+ 'hello world|11': 'passed',
+ 'pattern|12': 'waiting',
+ 'testing test 3|13': 'passed',
+ 'testing test 4|14': 'passed',
+ },
+ 'testing 2': {
+ 'hello world|16': 'passed',
+ 'pattern|17': 'waiting',
+ 'testing test 3|18': 'passed',
+ 'testing test 4|19': 'passed',
+ },
+ })
+})
+
describe('continuous testing', () => {
test('reruns tests on test file change', async ({ launch }) => {
const { tester } = await launch({
diff --git a/test-e2e/helper.ts b/test-e2e/helper.ts
index c4e9591d..c09cac64 100644
--- a/test-e2e/helper.ts
+++ b/test-e2e/helper.ts
@@ -13,7 +13,6 @@ import { VSCodeTester } from './tester'
interface Context {
page: Page
tester: VSCodeTester
-
}
type LaunchFixture = (options: {
diff --git a/test-e2e/tester.ts b/test-e2e/tester.ts
index cb0e292d..59dafbfe 100644
--- a/test-e2e/tester.ts
+++ b/test-e2e/tester.ts
@@ -50,7 +50,7 @@ class TesterTree {
const segment = segments[i]
const locator = this.page
// not yet run
- .locator(`[aria-label*="${segment} (Not"][aria-level="${i + 1}"]`)
+ .locator(`[aria-label*="${segment} ("][aria-level="${i + 1}"]`)
// test already run
.or(this.page.locator(`[aria-label="${segment}"][aria-level="${i + 1}"]`))
const state = await locator.getAttribute('aria-expanded')