diff --git a/packages/middleware/package.json b/packages/middleware/package.json index 6ca04e65..6527ea85 100644 --- a/packages/middleware/package.json +++ b/packages/middleware/package.json @@ -6,6 +6,9 @@ "main": "./dist/index.js", "module": "./dist/index.js", "types": "./dist/index.d.ts", + "scripts": { + "test": "vitest --run" + }, "files": [ "dist" ], @@ -43,6 +46,7 @@ "tslib": "^2.3.0" }, "devDependencies": { + "vitest": "^4.0.18", "@react-native/dev-middleware": "~0.76.0", "@types/ejs": "^3.1.5", "@types/express": "^5.0.3", diff --git a/packages/middleware/src/__tests__/pnp.test.ts b/packages/middleware/src/__tests__/pnp.test.ts new file mode 100644 index 00000000..dc5a3087 --- /dev/null +++ b/packages/middleware/src/__tests__/pnp.test.ts @@ -0,0 +1,70 @@ +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { resolvePackagePathFromVirtualPath, isPnP } from '../pnp.js'; + +describe('isPnP', () => { + describe('when the project is a Yarn Plug\'n\'Play project', () => { + beforeAll(() => { + process.versions.pnp = '3'; + }); + + afterAll(() => { + delete process.versions.pnp; + }) + + it('should return true', () => { + expect(isPnP()).toBe(true); + }); + }); + + describe('when the project is not a Yarn Plug\'n\'Play project', () => { + beforeAll(() => { + delete process.versions.pnp; + }); + + it('should return false', () => { + expect(isPnP()).toBe(false); + }); + }); +}); + +describe('resolvePackagePathFromVirtualPath', () => { + describe('when the virtual path is provided', () => { + const SCOPED_PACKAGE_VIRTUAL_PATH = '/path/to/project/.yarn/__virtual__/some-rozenite-plugin-virtual-39bf83846a/0/cache/@rozenite-tanstack-query-plugin-npm-1.2.0-723ced2ce3-a6b5bf6f06.zip/node_modules/@rozenite/tanstack-query-plugin/dist/react-native.cjs'; + const NON_SCOPED_PACKAGE_VIRTUAL_PATH = '/path/to/project/.yarn/__virtual__/some-rozenite-plugin-virtual-39bf83846a/0/cache/some-rozenite-plugin-npm-1.2.0-723ced2ce3-a6b5bf6f06.zip/node_modules/some-rozenite-plugin/index.js'; + + it('should return the package name', () => { + const scoped = resolvePackagePathFromVirtualPath(SCOPED_PACKAGE_VIRTUAL_PATH); + const nonScoped = resolvePackagePathFromVirtualPath(NON_SCOPED_PACKAGE_VIRTUAL_PATH); + + expect(scoped.basePath).toBe( + '/path/to/project/.yarn/__virtual__/some-rozenite-plugin-virtual-39bf83846a/0/cache/@rozenite-tanstack-query-plugin-npm-1.2.0-723ced2ce3-a6b5bf6f06.zip/node_modules/@rozenite/tanstack-query-plugin' + ); + expect(nonScoped.basePath).toBe( + '/path/to/project/.yarn/__virtual__/some-rozenite-plugin-virtual-39bf83846a/0/cache/some-rozenite-plugin-npm-1.2.0-723ced2ce3-a6b5bf6f06.zip/node_modules/some-rozenite-plugin' + ); + + expect(scoped.packageName).toBe('@rozenite/tanstack-query-plugin'); + expect(nonScoped.packageName).toBe('some-rozenite-plugin'); + }); + }); + + describe('when the unplugged virtual path is provided', () => { + const SCOPED_PACKAGE_UNPLUGGED_PATH = '/path/to/project/.yarn/unplugged/@rozenite-tanstack-query-plugin-virtual-39bf83846a/node_modules/@rozenite/tanstack-query-plugin/dist/react-native.cjs'; + const NON_SCOPED_PACKAGE_UNPLUGGED_PATH = '/path/to/project/.yarn/unplugged/some-rozenite-plugin-virtual-39bf83846a/node_modules/some-rozenite-plugin/dist/some-rozenite-plugin/index.js'; + + it('should return the package name', () => { + const scoped = resolvePackagePathFromVirtualPath(SCOPED_PACKAGE_UNPLUGGED_PATH); + const nonScoped = resolvePackagePathFromVirtualPath(NON_SCOPED_PACKAGE_UNPLUGGED_PATH); + + expect(scoped.basePath).toBe( + '/path/to/project/.yarn/unplugged/@rozenite-tanstack-query-plugin-virtual-39bf83846a/node_modules/@rozenite/tanstack-query-plugin' + ); + expect(nonScoped.basePath).toBe( + '/path/to/project/.yarn/unplugged/some-rozenite-plugin-virtual-39bf83846a/node_modules/some-rozenite-plugin' + ); + + expect(scoped.packageName).toBe('@rozenite/tanstack-query-plugin'); + expect(nonScoped.packageName).toBe('some-rozenite-plugin'); + }); + }); +}); diff --git a/packages/middleware/src/auto-discovery.ts b/packages/middleware/src/auto-discovery.ts index 05e67383..f78ebf1e 100644 --- a/packages/middleware/src/auto-discovery.ts +++ b/packages/middleware/src/auto-discovery.ts @@ -6,6 +6,7 @@ import { logger } from './logger.js'; import { getNodeModulesPaths } from './node-modules-paths.js'; import { ROZENITE_MANIFEST } from './constants.js'; import { RozeniteConfig } from './config.js'; +import { isPnP, resolvePackagePathFromVirtualPath } from './pnp.js'; const require = createRequire(import.meta.url); @@ -85,8 +86,62 @@ export const getInstalledPlugins = ( return getIncludedPlugins(options); } - const nodeModulesPaths = getNodeModulesPaths(); + return isPnP() ? getInstalledPluginsFromPnP(options) : getInstalledPluginsFromNodeModules(options); +}; + +const getInstalledPluginsFromPnP = (options: RozeniteConfig): InstalledPlugin[] => { const plugins: InstalledPlugin[] = []; + const packageJsonPath = path.join(options.projectRoot, 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + + const dependencies = [ + ...Object.keys(packageJson.dependencies || {}), + ...Object.keys(packageJson.devDependencies || {}), + ]; + + for (const dependency of dependencies) { + let packagePath: string; + let actualPackageName: string; + + try { + let resolvedPackagePath: string; + + try { + // First try to resolve the `/package.json` path. + resolvedPackagePath = require.resolve(path.join(dependency, 'package.json'), { paths: [options.projectRoot] }); + } catch { + // If the path to the package.json is not found, try to resolve the entry point. + resolvedPackagePath = require.resolve(dependency, { paths: [options.projectRoot] }); + } + + const resolvedVirtualPath = resolvePackagePathFromVirtualPath(resolvedPackagePath); + + packagePath = resolvedVirtualPath.basePath; + actualPackageName = resolvedVirtualPath.packageName; + } catch { + continue; + } + + if ( + options.exclude && + options.exclude.includes(actualPackageName) + ) { + continue; + } + + const plugin = tryExtractPlugin(packagePath, actualPackageName); + + if (plugin) { + plugins.push(plugin); + } + } + + return plugins; +} + +const getInstalledPluginsFromNodeModules = (options: RozeniteConfig): InstalledPlugin[] => { + const plugins: InstalledPlugin[] = []; + const nodeModulesPaths = getNodeModulesPaths(); for (const nodeModulesPath of nodeModulesPaths) { try { @@ -168,12 +223,12 @@ export const getInstalledPlugins = ( packagePath = path.join(nodeModulesPath, packageName); actualPackageName = packageName; - const plugin = tryExtractPlugin(packagePath, actualPackageName); - if (options.exclude && options.exclude.includes(actualPackageName)) { continue; } + const plugin = tryExtractPlugin(packagePath, actualPackageName); + if (plugin) { plugins.push(plugin); } diff --git a/packages/middleware/src/pnp.ts b/packages/middleware/src/pnp.ts new file mode 100644 index 00000000..16aa3909 --- /dev/null +++ b/packages/middleware/src/pnp.ts @@ -0,0 +1,39 @@ +import path from 'path'; + +export const isPnP = (): boolean => { + return typeof process.versions.pnp !== 'undefined'; +} + +export interface VirtualPath { + packageName: string; + basePath: string; +} + +export const resolvePackagePathFromVirtualPath = (virtualPath: string): VirtualPath => { + const nodeModulesPath = `node_modules${path.sep}`; + const nodeModulesIndex = virtualPath.lastIndexOf(nodeModulesPath); + + if (nodeModulesIndex === -1) { + throw new Error(`Could not find package path: ${virtualPath}`); + } + + const afterNodeModules = virtualPath.substring(nodeModulesIndex + nodeModulesPath.length); + const beforeNodeModules = virtualPath.substring(0, nodeModulesIndex + nodeModulesPath.length); + let packageSegments: number; + + if (afterNodeModules.startsWith('@')) { + // Scoped package + packageSegments = 2; + } else { + // Non-scoped package + packageSegments = 1; + } + + const parts = afterNodeModules.split(path.sep); + const packageName = parts.slice(0, packageSegments).join(path.sep); + + return { + packageName: packageName, + basePath: beforeNodeModules + packageName, + }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b009c259..5e23ebae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -399,6 +399,9 @@ importers: '@types/semver': specifier: ^7.7.0 version: 7.7.0 + vitest: + specifier: ^4.0.18 + version: 4.0.18(@types/node@22.17.0)(@vitest/ui@3.2.4(vitest@3.2.4))(jiti@2.6.1)(jsdom@22.1.0)(terser@5.43.1)(yaml@2.8.1) packages/mmkv-plugin: dependencies: @@ -2436,6 +2439,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.4': resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} @@ -4995,6 +5001,9 @@ packages: '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/expect@4.0.18': + resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} + '@vitest/mocker@3.2.4': resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: @@ -5006,18 +5015,41 @@ packages: vite: optional: true + '@vitest/mocker@4.0.18': + resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/pretty-format@4.0.18': + resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} + '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + '@vitest/runner@4.0.18': + resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} + '@vitest/snapshot@3.2.4': resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + '@vitest/snapshot@4.0.18': + resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} + '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/spy@4.0.18': + resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} + '@vitest/ui@3.2.4': resolution: {integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==} peerDependencies: @@ -5026,6 +5058,9 @@ packages: '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@4.0.18': + resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@volar/language-core@2.4.17': resolution: {integrity: sha512-chmRZMbKmcGpKMoO7Reb70uiLrzo0KWC2CkFttKUuKvrE+VYgi+fL9vWMJ07Fv5ulX0V1TAyyacN9q3nc5/ecA==} @@ -5639,6 +5674,10 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk@3.0.0: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} engines: {node: '>=8'} @@ -8238,6 +8277,9 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} @@ -8996,6 +9038,9 @@ packages: resolution: {integrity: sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==} engines: {node: '>= 10.12.0'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} @@ -10343,6 +10388,9 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -10619,6 +10667,10 @@ packages: tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} @@ -10638,6 +10690,10 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + tinyspy@4.0.4: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} @@ -11068,6 +11124,40 @@ packages: jsdom: optional: true + vitest@4.0.18: + resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.18 + '@vitest/browser-preview': 4.0.18 + '@vitest/browser-webdriverio': 4.0.18 + '@vitest/ui': 4.0.18 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vlq@0.2.3: resolution: {integrity: sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==} @@ -13223,6 +13313,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.4': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.29': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -17271,6 +17363,15 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 + '@vitest/expect@4.0.18': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.2 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + chai: 6.2.2 + tinyrainbow: 3.0.3 + '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@18.16.9)(jiti@2.4.2)(terser@5.43.1)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 @@ -17279,26 +17380,51 @@ snapshots: optionalDependencies: vite: 6.3.5(@types/node@18.16.9)(jiti@2.4.2)(terser@5.43.1)(yaml@2.8.1) + '@vitest/mocker@4.0.18(vite@6.3.5(@types/node@18.16.9)(jiti@2.4.2)(terser@5.43.1)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 4.0.18 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 6.3.5(@types/node@18.16.9)(jiti@2.4.2)(terser@5.43.1)(yaml@2.8.1) + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 + '@vitest/pretty-format@4.0.18': + dependencies: + tinyrainbow: 3.0.3 + '@vitest/runner@3.2.4': dependencies: '@vitest/utils': 3.2.4 pathe: 2.0.3 strip-literal: 3.1.0 + '@vitest/runner@4.0.18': + dependencies: + '@vitest/utils': 4.0.18 + pathe: 2.0.3 + '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 magic-string: 0.30.17 pathe: 2.0.3 + '@vitest/snapshot@4.0.18': + dependencies: + '@vitest/pretty-format': 4.0.18 + magic-string: 0.30.21 + pathe: 2.0.3 + '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.4 + '@vitest/spy@4.0.18': {} + '@vitest/ui@3.2.4(vitest@3.2.4)': dependencies: '@vitest/utils': 3.2.4 @@ -17316,6 +17442,11 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 + '@vitest/utils@4.0.18': + dependencies: + '@vitest/pretty-format': 4.0.18 + tinyrainbow: 3.0.3 + '@volar/language-core@2.4.17': dependencies: '@volar/source-map': 2.4.17 @@ -18062,6 +18193,8 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chai@6.2.2: {} + chalk@3.0.0: dependencies: ansi-styles: 4.3.0 @@ -19378,9 +19511,9 @@ snapshots: dependencies: pend: 1.2.0 - fdir@6.4.6(picomatch@4.0.2): + fdir@6.4.6(picomatch@4.0.3): optionalDependencies: - picomatch: 4.0.2 + picomatch: 4.0.3 fdir@6.5.0(picomatch@4.0.3): optionalDependencies: @@ -21117,6 +21250,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.4 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + make-dir@2.1.0: dependencies: pify: 4.0.1 @@ -22497,6 +22634,8 @@ snapshots: object-path@0.11.8: {} + obug@2.1.1: {} + on-exit-leak-free@2.1.2: {} on-finished@2.3.0: @@ -24203,6 +24342,8 @@ snapshots: statuses@2.0.2: {} + std-env@3.10.0: {} + std-env@3.9.0: {} stream-chain@2.2.5: {} @@ -24529,10 +24670,12 @@ snapshots: tinyexec@1.0.1: {} + tinyexec@1.0.2: {} + tinyglobby@0.2.14: dependencies: - fdir: 6.4.6(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 tinyglobby@0.2.15: dependencies: @@ -24548,6 +24691,8 @@ snapshots: tinyrainbow@2.0.0: {} + tinyrainbow@3.0.3: {} + tinyspy@4.0.4: {} tmp@0.2.5: {} @@ -24998,6 +25143,45 @@ snapshots: - tsx - yaml + vitest@4.0.18(@types/node@22.17.0)(@vitest/ui@3.2.4(vitest@3.2.4))(jiti@2.6.1)(jsdom@22.1.0)(terser@5.43.1)(yaml@2.8.1): + dependencies: + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(vite@6.3.5(@types/node@18.16.9)(jiti@2.4.2)(terser@5.43.1)(yaml@2.8.1)) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 6.3.5(@types/node@22.17.0)(jiti@2.6.1)(terser@5.43.1)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.17.0 + '@vitest/ui': 3.2.4(vitest@3.2.4) + jsdom: 22.1.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + vlq@0.2.3: {} vlq@1.0.1: {}