From d68a73908ef00f1b7c6584ef015657b3a11416bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ari=20Perkki=C3=B6?= Date: Thu, 22 Feb 2024 16:32:47 +0200 Subject: [PATCH] fix(coverage): expensive regexp hangs v8 report generation (#5259) --- examples/nestjs/package.json | 22 ++++ examples/nestjs/src/cats.controller.ts | 17 +++ examples/nestjs/src/cats.service.ts | 20 +++ examples/nestjs/test/nestjs.test.ts | 23 ++++ examples/nestjs/tsconfig.json | 6 + examples/nestjs/vitest.config.ts | 32 +++++ packages/coverage-v8/src/provider.ts | 2 +- pnpm-lock.yaml | 169 ++++++++++++++++++++++--- 8 files changed, 271 insertions(+), 20 deletions(-) create mode 100644 examples/nestjs/package.json create mode 100644 examples/nestjs/src/cats.controller.ts create mode 100644 examples/nestjs/src/cats.service.ts create mode 100644 examples/nestjs/test/nestjs.test.ts create mode 100644 examples/nestjs/tsconfig.json create mode 100644 examples/nestjs/vitest.config.ts diff --git a/examples/nestjs/package.json b/examples/nestjs/package.json new file mode 100644 index 000000000000..0332e13a9534 --- /dev/null +++ b/examples/nestjs/package.json @@ -0,0 +1,22 @@ +{ + "name": "@vitest/example-nestjs", + "type": "module", + "private": true, + "license": "MIT", + "main": "index.js", + "scripts": { + "test": "vitest", + "test:ui": "vitest --ui", + "test:run": "vitest run" + }, + "devDependencies": { + "@nestjs/common": "^10.3.3", + "@nestjs/testing": "^10.3.3", + "@vitest/coverage-v8": "^1.3.1", + "unplugin-swc": "^1.4.4", + "vitest": "1.3.1" + }, + "stackblitz": { + "startCommand": "npm run test:ui" + } +} diff --git a/examples/nestjs/src/cats.controller.ts b/examples/nestjs/src/cats.controller.ts new file mode 100644 index 000000000000..190c53696a52 --- /dev/null +++ b/examples/nestjs/src/cats.controller.ts @@ -0,0 +1,17 @@ +import { Body, Controller, Get, Post } from '@nestjs/common' +import type { Cat, CatsService } from './cats.service' + +@Controller('cats') +export class CatsController { + constructor(private catsService: CatsService) {} + + @Post() + async create(@Body() createCatDto: Cat) { + this.catsService.create(createCatDto) + } + + @Get() + async findAll(): Promise { + return this.catsService.findAll() + } +} diff --git a/examples/nestjs/src/cats.service.ts b/examples/nestjs/src/cats.service.ts new file mode 100644 index 000000000000..9a4986c366b9 --- /dev/null +++ b/examples/nestjs/src/cats.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@nestjs/common' + +export interface Cat { + name: string + age: number + breed: string +} + +@Injectable() +export class CatsService { + private readonly cats: Cat[] = [] + + create(cat: Cat) { + this.cats.push(cat) + } + + findAll(): Cat[] { + return this.cats + } +} diff --git a/examples/nestjs/test/nestjs.test.ts b/examples/nestjs/test/nestjs.test.ts new file mode 100644 index 000000000000..29ede5d6b208 --- /dev/null +++ b/examples/nestjs/test/nestjs.test.ts @@ -0,0 +1,23 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { CatsController } from '../src/cats.controller' +import type { Cat } from '../src/cats.service' +import { CatsService } from '../src/cats.service' + +describe('CatsController', () => { + let catsController: CatsController + let catsService: CatsService + + beforeEach(() => { + catsService = new CatsService() + catsController = new CatsController(catsService) + }) + + describe('findAll', () => { + it('should return an array of cats', async () => { + const result = ['test'] as unknown as Cat[] + vi.spyOn(catsService, 'findAll').mockImplementation(() => result) + + expect(await catsController.findAll()).toBe(result) + }) + }) +}) diff --git a/examples/nestjs/tsconfig.json b/examples/nestjs/tsconfig.json new file mode 100644 index 000000000000..0742e50cba97 --- /dev/null +++ b/examples/nestjs/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "experimentalDecorators": true + } +} diff --git a/examples/nestjs/vitest.config.ts b/examples/nestjs/vitest.config.ts new file mode 100644 index 000000000000..5d705275f771 --- /dev/null +++ b/examples/nestjs/vitest.config.ts @@ -0,0 +1,32 @@ +import swc from 'unplugin-swc' +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + plugins: [ + swc.vite({ + jsc: { + target: 'esnext', + parser: { + syntax: 'typescript', + decorators: true, + }, + transform: { + legacyDecorator: true, + decoratorMetadata: true, + }, + }, + }), + ], + test: { + coverage: { + enabled: true, + provider: 'v8', + thresholds: { + branches: 100, + functions: 57.14, + lines: 81.08, + statements: 81.08, + }, + }, + }, +}) diff --git a/packages/coverage-v8/src/provider.ts b/packages/coverage-v8/src/provider.ts index f01e99bbb8ae..517f446618cb 100644 --- a/packages/coverage-v8/src/provider.ts +++ b/packages/coverage-v8/src/provider.ts @@ -51,7 +51,7 @@ const WRAPPER_LENGTH = 185 // Note that this needs to match the line ending as well const VITE_EXPORTS_LINE_PATTERN = /Object\.defineProperty\(__vite_ssr_exports__.*\n/g -const DECORATOR_METADATA_PATTERN = /_ts_metadata\("design:paramtypes"(\s|.)+?]\),/g +const DECORATOR_METADATA_PATTERN = /_ts_metadata\("design:paramtypes", \[[^\]]*?\]\),*/g const DEFAULT_PROJECT = Symbol.for('default-project') const debug = createDebug('vitest:coverage') diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8eae3f79f5c..5d8191c17060 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -320,6 +320,24 @@ importers: specifier: ^4.1.1 version: 4.1.1(react@18.2.0) + examples/nestjs: + devDependencies: + '@nestjs/common': + specifier: ^10.3.3 + version: 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/testing': + specifier: ^10.3.3 + version: 10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) + '@vitest/coverage-v8': + specifier: ^1.3.1 + version: link:../../packages/coverage-v8 + unplugin-swc: + specifier: ^1.4.4 + version: 1.4.4(@swc/core@1.4.1)(rollup@4.9.6) + vitest: + specifier: workspace:* + version: link:../../packages/vitest + examples/nextjs: dependencies: next: @@ -6120,6 +6138,11 @@ packages: '@lit-labs/ssr-dom-shim': 1.1.2 dev: false + /@lukeed/csprng@1.1.0: + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + dev: true + /@marko/babel-utils@6.3.4: resolution: {integrity: sha512-qS0YEi3K25jUScTTHSkj2IqYTLpUJXT9RAkh8wmw2bxMP7T5wKDJlVpcbGlBt+bZSFTxAkM3eoRo8SMj4rmi3Q==} dependencies: @@ -6597,6 +6620,75 @@ packages: - react-dom dev: false + /@nestjs/common@10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1): + resolution: {integrity: sha512-LAkTe8/CF0uNWM0ecuDwUNTHCi1lVSITmmR4FQ6Ftz1E7ujQCnJ5pMRzd8JRN14vdBkxZZ8VbVF0BDUKoKNxMQ==} + peerDependencies: + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + iterare: 1.2.1 + reflect-metadata: 0.2.1 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + dev: true + + /@nestjs/core@10.3.3(@nestjs/common@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1): + resolution: {integrity: sha512-kxJWggQAPX3RuZx9JVec69eSLaYLNIox2emkZJpfBJ5Qq7cAq7edQIt1r4LGjTKq6kFubNTPsqhWf5y7yFRBPw==} + requiresBuild: true + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + dependencies: + '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nuxtjs/opencollective': 0.3.2 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 3.2.0 + reflect-metadata: 0.2.1 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + transitivePeerDependencies: + - encoding + dev: true + + /@nestjs/testing@10.3.3(@nestjs/common@10.3.3)(@nestjs/core@10.3.3): + resolution: {integrity: sha512-kX20GfjAImL5grd/i69uD/x7sc00BaqGcP2dRG3ilqshQUuy5DOmspLCr3a2C8xmVU7kzK4spT0oTxhe6WcCAA==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + dependencies: + '@nestjs/common': 10.3.3(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + tslib: 2.6.2 + dev: true + /@next/env@12.1.5: resolution: {integrity: sha512-+34yUJslfJi7Lyx6ELuN8nWcOzi27izfYnZIC1Dqv7kmmfiBVxgzR3BXhlvEMTKC2IRJhXVs2FkMY+buQe3k7Q==} dev: false @@ -6747,6 +6839,18 @@ packages: rimraf: 3.0.2 dev: true + /@nuxtjs/opencollective@0.3.2: + resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + consola: 2.15.3 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + dev: true + /@one-ini/wasm@0.1.1: resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} dev: true @@ -8386,7 +8490,7 @@ packages: flat-cache: 3.0.4 micromatch: 4.0.5 react-docgen-typescript: 2.2.2(typescript@5.2.2) - tslib: 2.5.3 + tslib: 2.6.2 typescript: 5.2.2 webpack: 5.74.0(esbuild@0.18.20) transitivePeerDependencies: @@ -11327,21 +11431,21 @@ packages: resolution: {integrity: sha512-LOmVnY1iTU2D8tv4Xf6MVMZZ+juIJ87Kt/plMijjN20NMAXGmH4u8bS1t0uT74cZ5gwpocYueV58YwyI8y+GKw==} engines: {node: '>=8'} dependencies: - tslib: 2.5.3 + tslib: 2.6.2 dev: false /@wry/equality@0.5.3: resolution: {integrity: sha512-avR+UXdSrsF2v8vIqIgmeTY0UR91UT+IyablCyKe/uk22uOJ8fusKZnH9JH9e1/EtLeNJBtagNmL3eJdnOV53g==} engines: {node: '>=8'} dependencies: - tslib: 2.5.3 + tslib: 2.6.2 dev: false /@wry/trie@0.3.2: resolution: {integrity: sha512-yRTyhWSls2OY/pYLfwff867r8ekooZ4UI+/gxot5Wj8EFwSf2rG+n+Mo/6LoLQm1TKA4GRj2+LCpbfS937dClQ==} engines: {node: '>=8'} dependencies: - tslib: 2.5.3 + tslib: 2.6.2 dev: false /@xmldom/xmldom@0.8.6: @@ -11980,14 +12084,14 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} dependencies: - tslib: 2.5.3 + tslib: 2.6.2 dev: true /ast-types@0.14.2: resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} engines: {node: '>=4'} dependencies: - tslib: 2.5.3 + tslib: 2.6.2 dev: true /astral-regex@2.0.0: @@ -12885,7 +12989,7 @@ packages: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} dependencies: pascal-case: 3.1.2 - tslib: 2.5.3 + tslib: 2.6.2 dev: true /camelcase-css@2.0.1: @@ -13517,6 +13621,10 @@ packages: proto-list: 1.2.4 dev: true + /consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + dev: true + /consola@3.2.3: resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} engines: {node: ^14.18.0 || >=16.10.0} @@ -14634,7 +14742,7 @@ packages: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: no-case: 3.0.4 - tslib: 2.5.3 + tslib: 2.6.2 dev: true /dotenv-expand@10.0.0: @@ -17392,7 +17500,7 @@ packages: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 16.6.0 - tslib: 2.5.3 + tslib: 2.6.2 dev: false /graphql-tag@2.12.6(graphql@16.8.1): @@ -17402,7 +17510,7 @@ packages: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 16.8.1 - tslib: 2.5.3 + tslib: 2.6.2 dev: false /graphql@16.6.0: @@ -18729,6 +18837,11 @@ packages: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 + /iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + dev: true + /iterate-iterator@1.0.2: resolution: {integrity: sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==} dev: true @@ -20174,7 +20287,7 @@ packages: /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.5.3 + tslib: 2.6.2 dev: true /lowercase-keys@3.0.0: @@ -21013,7 +21126,7 @@ packages: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: lower-case: 2.0.2 - tslib: 2.5.3 + tslib: 2.6.2 dev: true /node-abi@3.45.0: @@ -21640,7 +21753,7 @@ packages: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: dot-case: 3.0.4 - tslib: 2.5.3 + tslib: 2.6.2 dev: true /parent-module@1.0.1: @@ -21727,7 +21840,7 @@ packages: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} dependencies: no-case: 3.0.4 - tslib: 2.5.3 + tslib: 2.6.2 dev: true /pascalcase@0.1.1: @@ -21800,6 +21913,10 @@ packages: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} dev: true + /path-to-regexp@3.2.0: + resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} + dev: true + /path-to-regexp@6.2.1: resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} dev: true @@ -23029,6 +23146,10 @@ packages: postcss-value-parser: 3.3.1 dev: false + /reflect-metadata@0.2.1: + resolution: {integrity: sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==} + dev: true + /regenerate-unicode-properties@10.0.1: resolution: {integrity: sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==} engines: {node: '>=4'} @@ -23547,7 +23668,7 @@ packages: /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - tslib: 2.5.3 + tslib: 2.6.2 dev: true /sade@1.8.1: @@ -25003,7 +25124,7 @@ packages: resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==} engines: {node: '>=12.20'} dependencies: - tslib: 2.5.3 + tslib: 2.6.2 dev: true /tabbable@6.2.0: @@ -25502,7 +25623,7 @@ packages: resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} engines: {node: '>=8'} dependencies: - tslib: 2.5.3 + tslib: 2.6.2 dev: false /ts-pnp@1.2.0(typescript@5.2.2): @@ -25524,6 +25645,9 @@ packages: /tslib@2.5.3: resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} + /tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + /tsx@3.9.0: resolution: {integrity: sha512-ofxsE+qjqCYYq4UBt5khglvb+ESgxef1YpuNcdQI92kvcAT2tZVrnSK3g4bRXTUhLmKHcC5q8vIZA47os/stng==} hasBin: true @@ -25704,6 +25828,13 @@ packages: dev: true optional: true + /uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + dependencies: + '@lukeed/csprng': 1.1.0 + dev: true + /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -25793,7 +25924,7 @@ packages: /unimport@3.4.0(rollup@4.9.6): resolution: {integrity: sha512-M/lfFEgufIT156QAr/jWHLUn55kEmxBBiQsMxvRSIbquwmeJEyQYgshHDEvQDWlSJrVOOTAgnJ3FvlsrpGkanA==} dependencies: - '@rollup/pluginutils': 5.0.5(rollup@4.9.6) + '@rollup/pluginutils': 5.1.0(rollup@4.9.6) escape-string-regexp: 5.0.0 fast-glob: 3.3.2 local-pkg: 0.4.3 @@ -25803,7 +25934,7 @@ packages: pkg-types: 1.0.3 scule: 1.0.0 strip-literal: 1.3.0 - unplugin: 1.5.0 + unplugin: 1.7.1 transitivePeerDependencies: - rollup dev: true