diff --git a/.eslintignore b/.eslintignore index ecb0c8801995..ceee5217c471 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,4 +5,4 @@ node_modules *.d.ts coverage !.vitepress -test/core/src/self \ No newline at end of file +test/core/src/self diff --git a/.gitignore b/.gitignore index 5168f474f52b..6c53c3a54331 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ yarn-debug.log* yarn-error.log* lib-cov coverage +!**/integrations/coverage node_modules .env .cache diff --git a/README.md b/README.md index d860d50ed09b..2608108ebd6c 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ A blazing fast unit test framework powered by Vite. - [Jest Snapshot](https://jestjs.io/docs/snapshot-testing) - [Chai](https://www.chaijs.com/) built-in for assertions, with [Jest expect](https://jestjs.io/docs/expect) compatible APIs. - [Smart & instant watch mode](https://vitest.dev/guide/features.html#watch-mode), like HMR for tests! -- [Native code coverage](https://vitest.dev/guide/features.html#coverage) via [c8](https://github.com/bcoe/c8) +- [Native code coverage](https://vitest.dev/guide/features.html#coverage) via [c8](https://github.com/bcoe/c8) or [`istanbul`](https://istanbul.js.org/). - [Tinyspy](https://github.com/Aslemammad/tinyspy) built-in for mocking, stubbing, and spies. - [JSDOM](https://github.com/jsdom/jsdom) and [happy-dom](https://github.com/capricorn86/happy-dom) for DOM and browser API mocking - Components testing ([Vue](./examples/vue), [React](./examples/react), [Svelte](./examples/svelte), [Lit](./examples/lit), [Vitesse](./examples/vitesse)) diff --git a/docs/.vitepress/components/FeaturesList.vue b/docs/.vitepress/components/FeaturesList.vue index 277590a3b702..ea64c7de68c3 100644 --- a/docs/.vitepress/components/FeaturesList.vue +++ b/docs/.vitepress/components/FeaturesList.vue @@ -20,7 +20,7 @@ Chai built-in for assertions + Jest expect compatible APIs Tinyspy built-in for mocking happy-dom or jsdom for DOM mocking - Native code coverage via c8 + Code coverage via c8 or istanbul Rust-like in-source testing diff --git a/docs/config/index.md b/docs/config/index.md index fb668aad8ffa..e10422b5c36b 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -420,10 +420,103 @@ Isolate environment for each test file. Does not work if you disable [`--threads ### coverage -- **Type:** `C8Options` +- **Type:** `CoverageC8Options | CoverageIstanbulOptions` - **Default:** `undefined` -Coverage options passed to [C8](https://github.com/bcoe/c8). +You can use [`c8`](https://github.com/bcoe/c8) or [`istanbul`](https://istanbul.js.org/) for coverage collection. + +#### provider + +- **Type:** `'c8' | 'istanbul'` +- **Default:** `'c8'` + +Use `provider` to select the tool for coverage collection. + +#### CoverageC8Options + +Used when `provider: 'c8'` is set. Coverage options are passed to [`c8`](https://github.com/bcoe/c8). + +#### CoverageIstanbulOptions + +Used when `provider: 'istanbul'` is set. + +##### exclude + +- **Type:** `string[]` +- **Default:** `[]` + +List of files excluded from coverage as glob patterns. + +##### skipFull + +- **Type:** `boolean` +- **Default:** `false` + +Do not show files with 100% statement, branch, and function coverage. + +##### perFile + +- **Type:** `boolean` +- **Default:** `false` + +Check thresholds per file. + +##### lines + +- **Type:** `number` + +Threshold for lines. + +##### functions + +- **Type:** `number` + +Threshold for functions. + +##### branches + +- **Type:** `number` + +Threshold for branches. + +##### statements + +- **Type:** `number` + +Threshold for statements. + +##### ignoreClassMethods + +- **Type:** `string[]` +- **Default:** [] + +Set to array of class method names to ignore for coverage. + +##### watermarks + +- **Type:** + +```ts +{ + statements?: [number, number], + functions?: [number, number], + branches?: [number, number], + lines?: [number, number] +} +``` + +- **Default:** + +```ts +{ + statements: [50, 80], + functions: [50, 80], + branches: [50, 80], + lines: [50, 80] +} +``` + +Watermarks for statements, lines, branches and functions. ### testNamePattern diff --git a/docs/guide/cli.md b/docs/guide/cli.md index 63cea34b266b..ec8b87c29cf2 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -61,7 +61,7 @@ vitest related /src/index.ts /src/hello-world.js | `--outputTruncateLength ` | Truncate output diff lines up to `` number of characters. | | `--outputDiffLines ` | Limit number of output diff lines up to ``. | | `--outputFile ` | Write test results to a file when the `--reporter=json` or `--reporter=junit` option is also specified
Via [cac's dot notation] you can specify individual outputs for multiple reporters | -| `--coverage` | Use c8 for coverage | +| `--coverage` | Enable coverage report | | `--run` | Do not watch | | `--mode` | Override Vite mode (default: `test`) | | `--mode ` | Override Vite mode (default: `test`) | diff --git a/docs/guide/coverage.md b/docs/guide/coverage.md index b65fac2cdded..5e808c10cd33 100644 --- a/docs/guide/coverage.md +++ b/docs/guide/coverage.md @@ -4,13 +4,46 @@ title: Coverage | Guide # Coverage -Vitest supports Native code coverage via [`c8`](https://github.com/bcoe/c8). `c8` is an optional peer dependency, to use the coverage feature you will need to install it first by: +Vitest supports Native code coverage via [`c8`](https://github.com/bcoe/c8) and instrumented code coverage via [`istanbul`](https://istanbul.js.org/). + +## Coverage Providers + +:::tip +Since Vitest v0.22.0 +::: + +Both `c8` and `istanbul` support are optional. By default, `c8` will be used. + +You can select the coverage tool by setting `test.coverage.provider` to either `c8` or `istanbul`: + +```ts +// vite.config.ts +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + coverage: { + provider: 'istanbul' // or 'c8' + }, + }, +}) +``` + +When you start the Vitest process, it will prompt you to install the corresponding support package automatically. + +Or if you prefer to install them manually: ```bash -npm i -D c8 +# For c8 +npm i -D @vitest/coverage-c8 + +# For istanbul +npm i -D @vitest/coverage-istanbul ``` -Then you could get the coverage by passing the `--coverage` flag in CLI. +## Coverage Setup + +To test with coverage enabled, you can pass the `--coverage` flag in CLI. ```json { @@ -35,3 +68,23 @@ export default defineConfig({ }, }) ``` + +## Custom Coverage Provider + +It's also possible to provide your custom coverage provider by passing an object to the `test.coverage.provider`: + +```ts +// vite.config.ts +import { defineConfig } from 'vitest/config' +import CustomCoverageProvider from 'my-custom-coverage-provider' + +export default defineConfig({ + test: { + coverage: { + provider: CustomCoverageProvider() + }, + }, +}) +``` + +Please refer to the type definition for more details. diff --git a/docs/guide/features.md b/docs/guide/features.md index 16b3c7419fc2..b7ae12f1071a 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -136,7 +136,7 @@ Learn more at [Mocking](/guide/mocking). ## Coverage -Vitest supports Native code coverage via [c8](https://github.com/bcoe/c8). +Vitest supports Native code coverage via [`c8`](https://github.com/bcoe/c8) and instrumented code coverage via [`istanbul`](https://istanbul.js.org/). ```json { @@ -147,20 +147,7 @@ Vitest supports Native code coverage via [c8](https://github.com/bcoe/c8). } ``` -To configure it, set `test.coverage` options in your config file: - -```ts -// vite.config.ts -import { defineConfig } from 'vitest/config' - -export default defineConfig({ - test: { - coverage: { - reporter: ['text', 'json', 'html'], - }, - }, -}) -``` +Learn more at [Coverage](/guide/coverage). ## In-source testing diff --git a/package.json b/package.json index 3287a091299b..dc0f612eff65 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,8 @@ "@types/node": "^18.7.3", "@types/ws": "^8.5.3", "@vitest/browser": "workspace:*", + "@vitest/coverage-c8": "workspace:*", + "@vitest/coverage-istanbul": "workspace:*", "@vitest/ui": "workspace:*", "bumpp": "^8.2.1", "c8": "^7.12.0", @@ -64,6 +66,7 @@ "rollup-plugin-esbuild": "^4.9.3", "rollup-plugin-license": "^2.8.1", "simple-git-hooks": "^2.8.0", + "tsup": "^6.2.2", "typescript": "^4.7.4", "vite": "^3.0.7", "vitest": "workspace:*", diff --git a/packages/coverage-c8/package.json b/packages/coverage-c8/package.json new file mode 100644 index 000000000000..d1ebaeb23f48 --- /dev/null +++ b/packages/coverage-c8/package.json @@ -0,0 +1,50 @@ +{ + "name": "@vitest/coverage-c8", + "type": "module", + "version": "0.0.0", + "description": "C8 coverage provider for Vitest", + "author": "Anthony Fu ", + "license": "MIT", + "funding": "https://github.com/sponsors/antfu", + "homepage": "https://github.com/vitest-dev/vitest#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/vitest-dev/vitest.git", + "directory": "packages/coverage-c8" + }, + "bugs": { + "url": "https://github.com/vitest-dev/vitest/issues" + }, + "keywords": [ + "vite", + "vitest", + "test", + "coverage", + "c8" + ], + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./*": "./*" + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "rimraf dist && rollup -c", + "dev": "rollup -c --watch --watch.include=src/**" + }, + "dependencies": { + "c8": "^7.12.0", + "vitest": "workspace:*" + }, + "devDependencies": { + "vite-node": "workspace:*" + } +} diff --git a/packages/coverage-c8/rollup.config.js b/packages/coverage-c8/rollup.config.js new file mode 100644 index 000000000000..50cfe07d43f2 --- /dev/null +++ b/packages/coverage-c8/rollup.config.js @@ -0,0 +1,62 @@ +import { builtinModules } from 'module' +import esbuild from 'rollup-plugin-esbuild' +import dts from 'rollup-plugin-dts' +import commonjs from '@rollup/plugin-commonjs' +import json from '@rollup/plugin-json' +import alias from '@rollup/plugin-alias' +import nodeResolve from '@rollup/plugin-node-resolve' +import { join } from 'pathe' +import pkg from './package.json' + +const entries = { + index: 'src/index.ts', +} + +const external = [ + ...builtinModules, + ...Object.keys(pkg.dependencies || {}), + ...Object.keys(pkg.peerDependencies || {}), + 'c8/lib/report.js', + 'c8/lib/commands/check-coverage.js', + 'vitest', + 'vitest/node', + 'vitest/config', +] + +const plugins = [ + alias({ + entries: [ + { find: /^node:(.+)$/, replacement: '$1' }, + ], + }), + nodeResolve(), + json(), + commonjs(), + esbuild({ + target: 'node14', + }), +] + +export default () => [ + { + input: entries, + output: { + dir: 'dist', + format: 'esm', + }, + external, + plugins, + }, + { + input: entries, + output: { + dir: join(process.cwd(), 'dist'), + entryFileNames: '[name].d.ts', + format: 'esm', + }, + external, + plugins: [ + dts({ respectExternal: true }), + ], + }, +] diff --git a/packages/coverage-c8/src/index.ts b/packages/coverage-c8/src/index.ts new file mode 100644 index 000000000000..ddc09359a394 --- /dev/null +++ b/packages/coverage-c8/src/index.ts @@ -0,0 +1,6 @@ +export * from './takeCoverage' + +export async function getProvider() { + const { C8CoverageProvider } = await import('./provider') + return new C8CoverageProvider() +} diff --git a/packages/coverage-c8/src/provider.ts b/packages/coverage-c8/src/provider.ts new file mode 100644 index 000000000000..3c05da7b9fd2 --- /dev/null +++ b/packages/coverage-c8/src/provider.ts @@ -0,0 +1,126 @@ +import { existsSync, promises as fs } from 'fs' +import _url from 'url' +import type { Profiler } from 'inspector' +import { takeCoverage } from 'v8' +import { resolve } from 'pathe' +import type { RawSourceMap } from 'vite-node' +import { configDefaults } from 'vitest/config' +// eslint-disable-next-line no-restricted-imports +import type { CoverageC8Options, CoverageProvider, ResolvedCoverageOptions } from 'vitest' +import type { Vitest } from 'vitest/node' +// @ts-expect-error missing types +import createReport from 'c8/lib/report.js' +// @ts-expect-error missing types +import { checkCoverages } from 'c8/lib/commands/check-coverage.js' + +export class C8CoverageProvider implements CoverageProvider { + name = 'c8' + + ctx!: Vitest + options!: ResolvedCoverageOptions & { provider: 'c8' } + + initialize(ctx: Vitest) { + this.ctx = ctx + this.options = resolveC8Options(ctx.config.coverage, ctx.config.root) + } + + resolveOptions() { + return this.options + } + + onBeforeFilesRun() { + process.env.NODE_V8_COVERAGE ||= this.options.tempDirectory + } + + async clean(clean = true) { + if (clean && existsSync(this.options.reportsDirectory)) + await fs.rm(this.options.reportsDirectory, { recursive: true, force: true }) + + if (!existsSync(this.options.tempDirectory)) + await fs.mkdir(this.options.tempDirectory, { recursive: true }) + } + + onAfterSuiteRun() { + takeCoverage() + } + + async reportCoverage() { + takeCoverage() + const report = createReport(this.ctx.config.coverage) + + // add source maps + const sourceMapMeta: Record = {} + await Promise.all(Array + .from(this.ctx.vitenode.fetchCache.entries()) + .filter(i => !i[0].includes('/node_modules/')) + .map(async ([file, { result }]) => { + const map = result.map + if (!map) + return + + const url = _url.pathToFileURL(file).href + + let code: string | undefined + try { + code = (await fs.readFile(file)).toString() + } + catch { } + + // Vite does not report full path in sourcemap sources + // so use an actual file path + const sources = [url] + + sourceMapMeta[url] = { + source: result.code, + map: { + sourcesContent: code ? [code] : undefined, + ...map, + sources, + }, + } + })) + + // This is a magic number. It corresponds to the amount of code + // that we add in packages/vite-node/src/client.ts:114 (vm.runInThisContext) + // TODO: Include our transformations in sourcemaps + const offset = 224 + + report._getSourceMap = (coverage: Profiler.ScriptCoverage) => { + const path = _url.pathToFileURL(coverage.url).href + const data = sourceMapMeta[path] + + if (!data) + return {} + + return { + sourceMap: { + sourcemap: data.map, + }, + source: Array(offset).fill('.').join('') + data.source, + } + } + + await report.run() + await checkCoverages(this.options, report) + } +} +function resolveC8Options(options: CoverageC8Options, root: string) { + const resolved = { + ...configDefaults.coverage, + ...options as any, + } + + if (options['100']) { + resolved.lines = 100 + resolved.functions = 100 + resolved.branches = 100 + resolved.statements = 100 + } + + resolved.reporter = resolved.reporter || [] + resolved.reporter = Array.isArray(resolved.reporter) ? resolved.reporter : [resolved.reporter] + resolved.reportsDirectory = resolve(root, resolved.reportsDirectory) + resolved.tempDirectory = process.env.NODE_V8_COVERAGE || resolve(resolved.reportsDirectory, 'tmp') + + return resolved +} diff --git a/packages/coverage-c8/src/takeCoverage.ts b/packages/coverage-c8/src/takeCoverage.ts new file mode 100644 index 000000000000..5669b1088671 --- /dev/null +++ b/packages/coverage-c8/src/takeCoverage.ts @@ -0,0 +1,10 @@ +import v8 from 'v8' + +// Flush coverage to disk + +export function takeCoverage() { + if (v8.takeCoverage == null) + console.warn('[Vitest] takeCoverage is not available in this NodeJs version.\nCoverage could be incomplete. Update to NodeJs 14.18.') + else + v8.takeCoverage() +} diff --git a/packages/coverage-istanbul/package.json b/packages/coverage-istanbul/package.json new file mode 100644 index 000000000000..1c6e0d2e8c1d --- /dev/null +++ b/packages/coverage-istanbul/package.json @@ -0,0 +1,60 @@ +{ + "name": "@vitest/coverage-istanbul", + "type": "module", + "version": "0.0.0", + "description": "Istanbul coverage provider for Vitest", + "author": "Anthony Fu ", + "license": "MIT", + "funding": "https://github.com/sponsors/antfu", + "homepage": "https://github.com/vitest-dev/vitest#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/vitest-dev/vitest.git", + "directory": "packages/coverage-istanbul" + }, + "bugs": { + "url": "https://github.com/vitest-dev/vitest/issues" + }, + "keywords": [ + "vite", + "vitest", + "test", + "coverage", + "istanbul" + ], + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./*": "./*" + }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "rimraf dist && rollup -c", + "dev": "rollup -c --watch --watch.include=src/**" + }, + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.2.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.1.5", + "test-exclude": "^6.0.0", + "vitest": "workspace:*" + }, + "devDependencies": { + "@types/istanbul-lib-coverage": "^2.0.4", + "@types/istanbul-lib-instrument": "^1.7.4", + "@types/istanbul-lib-report": "^3.0.0", + "@types/istanbul-lib-source-maps": "^4.0.1", + "@types/istanbul-reports": "^3.0.1", + "pathe": "^0.2.0" + } +} diff --git a/packages/coverage-istanbul/rollup.config.js b/packages/coverage-istanbul/rollup.config.js new file mode 100644 index 000000000000..66fbbd080629 --- /dev/null +++ b/packages/coverage-istanbul/rollup.config.js @@ -0,0 +1,60 @@ +import { builtinModules } from 'module' +import esbuild from 'rollup-plugin-esbuild' +import dts from 'rollup-plugin-dts' +import commonjs from '@rollup/plugin-commonjs' +import json from '@rollup/plugin-json' +import alias from '@rollup/plugin-alias' +import nodeResolve from '@rollup/plugin-node-resolve' +import { join } from 'pathe' +import pkg from './package.json' + +const entries = { + index: 'src/index.ts', +} + +const external = [ + ...builtinModules, + ...Object.keys(pkg.dependencies || {}), + ...Object.keys(pkg.peerDependencies || {}), + 'vitest', + 'vitest/node', + 'vitest/config', +] + +const plugins = [ + alias({ + entries: [ + { find: /^node:(.+)$/, replacement: '$1' }, + ], + }), + nodeResolve(), + json(), + commonjs(), + esbuild({ + target: 'node14', + }), +] + +export default () => [ + { + input: entries, + output: { + dir: 'dist', + format: 'esm', + }, + external, + plugins, + }, + { + input: entries, + output: { + dir: join(process.cwd(), 'dist'), + entryFileNames: '[name].d.ts', + format: 'esm', + }, + external, + plugins: [ + dts({ respectExternal: true }), + ], + }, +] diff --git a/packages/coverage-istanbul/src/constants.ts b/packages/coverage-istanbul/src/constants.ts new file mode 100644 index 000000000000..1f81d8ad9fd9 --- /dev/null +++ b/packages/coverage-istanbul/src/constants.ts @@ -0,0 +1 @@ +export const COVERAGE_STORE_KEY = '__VITEST_COVERAGE__' diff --git a/packages/coverage-istanbul/src/index.ts b/packages/coverage-istanbul/src/index.ts new file mode 100644 index 000000000000..a88aae227082 --- /dev/null +++ b/packages/coverage-istanbul/src/index.ts @@ -0,0 +1,11 @@ +import { COVERAGE_STORE_KEY } from './constants' + +export async function getProvider() { + const { IstanbulCoverageProvider } = await import('./provider') + return new IstanbulCoverageProvider() +} + +export function takeCoverage() { + // @ts-expect-error -- untyped global + return globalThis[COVERAGE_STORE_KEY] +} diff --git a/packages/coverage-istanbul/src/provider.ts b/packages/coverage-istanbul/src/provider.ts new file mode 100644 index 000000000000..285a0fc108b4 --- /dev/null +++ b/packages/coverage-istanbul/src/provider.ts @@ -0,0 +1,190 @@ +/* eslint-disable no-restricted-imports */ +import { existsSync, promises as fs } from 'fs' +import { relative, resolve } from 'pathe' +import type { TransformPluginContext } from 'rollup' +import type { AfterSuiteRunMeta, CoverageIstanbulOptions, CoverageProvider, ResolvedCoverageOptions, Vitest } from 'vitest' +import { configDefaults, defaultExclude, defaultInclude } from 'vitest/config' +import libReport from 'istanbul-lib-report' +import reports from 'istanbul-reports' +import type { CoverageMap } from 'istanbul-lib-coverage' +import libCoverage from 'istanbul-lib-coverage' +import libSourceMaps from 'istanbul-lib-source-maps' +import { type Instrumenter, createInstrumenter } from 'istanbul-lib-instrument' +// @ts-expect-error missing types +import _TestExclude from 'test-exclude' +import { COVERAGE_STORE_KEY } from './constants' + +type Threshold = 'lines' | 'functions' | 'statements' | 'branches' + +interface TestExclude { + new(opts: { + cwd?: string | string[] + include?: string | string[] + exclude?: string | string[] + extension?: string | string[] + excludeNodeModules?: boolean + }): { shouldInstrument(filePath: string): boolean } +} + +export class IstanbulCoverageProvider implements CoverageProvider { + name = 'istanbul' + + ctx!: Vitest + options!: ResolvedCoverageOptions & CoverageIstanbulOptions & { provider: 'istanbul' } + instrumenter!: Instrumenter + testExclude!: InstanceType + + /** + * Coverage objects collected from workers. + * Some istanbul utilizers write these into file system instead of storing in memory. + * If storing in memory causes issues, we can simply write these into fs in `onAfterSuiteRun` + * and read them back when merging coverage objects in `onAfterAllFilesRun`. + */ + coverages: any[] = [] + + initialize(ctx: Vitest) { + this.ctx = ctx + this.options = resolveIstanbulOptions(ctx.config.coverage, ctx.config.root) + + this.instrumenter = createInstrumenter({ + produceSourceMap: true, + autoWrap: false, + esModules: true, + coverageVariable: COVERAGE_STORE_KEY, + // @ts-expect-error missing type + coverageGlobalScope: 'globalThis', + coverageGlobalScopeFunc: false, + ignoreClassMethods: this.options.ignoreClassMethods, + }) + + this.testExclude = new _TestExclude({ + cwd: ctx.config.root, + exclude: [...defaultExclude, ...defaultInclude, ...this.options.exclude], + excludeNodeModules: true, + extension: configDefaults.coverage.extension, + }) + } + + resolveOptions(): ResolvedCoverageOptions { + return this.options + } + + onFileTransform(sourceCode: string, id: string, pluginCtx: TransformPluginContext) { + if (!this.testExclude.shouldInstrument(id)) + return + + // eslint-disable-next-line @typescript-eslint/no-unused-vars -- ignoreRestSiblings should be enabled + const { sourcesContent, ...sourceMap } = pluginCtx.getCombinedSourcemap() + const code = this.instrumenter.instrumentSync(sourceCode, id, sourceMap as any) + const map = this.instrumenter.lastSourceMap() as any + + return { code, map } + } + + onAfterSuiteRun({ coverage }: AfterSuiteRunMeta) { + this.coverages.push(coverage) + } + + async clean(clean = true) { + if (clean && existsSync(this.options.reportsDirectory)) + await fs.rm(this.options.reportsDirectory, { recursive: true, force: true }) + + this.coverages = [] + } + + async reportCoverage() { + const mergedCoverage = this.coverages.reduce((coverage, previousCoverageMap) => { + const map = libCoverage.createCoverageMap(coverage) + map.merge(previousCoverageMap) + return map + }, {}) + + const sourceMapStore = libSourceMaps.createSourceMapStore() + const coverageMap: CoverageMap = await sourceMapStore.transformCoverage(mergedCoverage) + + const context = libReport.createContext({ + dir: this.options.reportsDirectory, + coverageMap, + sourceFinder: sourceMapStore.sourceFinder, + watermarks: this.options.watermarks, + }) + + for (const reporter of this.options.reporter) { + reports.create(reporter as any, { + skipFull: this.options.skipFull, + }).execute(context) + } + + if (this.options.branches + || this.options.functions + || this.options.lines + || this.options.statements) { + this.checkThresholds(coverageMap, { + branches: this.options.branches, + functions: this.options.functions, + lines: this.options.lines, + statements: this.options.statements, + }) + } + } + + checkThresholds(coverageMap: CoverageMap, thresholds: Record) { + // Construct list of coverage summaries where thresholds are compared against + const summaries = this.options.perFile + ? coverageMap.files() + .map((file: string) => ({ + file, + summary: coverageMap.fileCoverageFor(file).toSummary(), + })) + : [{ + file: null, + summary: coverageMap.getCoverageSummary(), + }] + + // Check thresholds of each summary + for (const { summary, file } of summaries) { + for (const thresholdKey of ['lines', 'functions', 'statements', 'branches'] as const) { + const threshold = thresholds[thresholdKey] + + if (threshold !== undefined) { + const coverage = summary.data[thresholdKey].pct + + if (coverage < threshold) { + process.exitCode = 1 + + /* + * Generate error message based on perFile flag: + * - ERROR: Coverage for statements (33.33%) does not meet threshold (85%) for src/math.ts + * - ERROR: Coverage for statements (50%) does not meet global threshold (85%) + */ + let errorMessage = `ERROR: Coverage for ${thresholdKey} (${coverage}%) does not meet` + + if (!this.options.perFile) + errorMessage += ' global' + + errorMessage += ` threshold (${threshold}%)` + + if (this.options.perFile && file) + errorMessage += ` for ${relative('./', file).replace(/\\/g, '/')}` + + console.error(errorMessage) + } + } + } + } + } +} + +function resolveIstanbulOptions(options: CoverageIstanbulOptions, root: string) { + const reportsDirectory = resolve(root, options.reportsDirectory || configDefaults.coverage.reportsDirectory!) + + const resolved = { + ...configDefaults.coverage, + ...options, + provider: 'istanbul', + reportsDirectory, + tempDirectory: resolve(reportsDirectory, 'tmp'), + } + + return resolved as ResolvedCoverageOptions & { provider: 'istanbul' } +} diff --git a/packages/ui/client/composables/summary.ts b/packages/ui/client/composables/summary.ts index 534b01c8e172..07f1518ac836 100644 --- a/packages/ui/client/composables/summary.ts +++ b/packages/ui/client/composables/summary.ts @@ -52,6 +52,7 @@ function toArray(array?: Nullable>): Array { return array return [array] } + function getTests(suite: Arrayable): Test[] { return toArray(suite).flatMap(s => s.type === 'test' ? [s] : s.tasks.flatMap(c => c.type === 'test' ? [c] : getTests(c))) } diff --git a/packages/vite-node/src/client.ts b/packages/vite-node/src/client.ts index 308f3fa4f232..f53e97a3218a 100644 --- a/packages/vite-node/src/client.ts +++ b/packages/vite-node/src/client.ts @@ -216,7 +216,7 @@ export class ViteNodeRunner { // Be careful when changing this // changing context will change amount of code added on line :114 (vm.runInThisContext) // this messes up sourcemaps for coverage - // adjust `offset` variable in packages/vitest/src/integrations/coverage.ts#L100 if you do change this + // adjust `offset` variable in packages/vitest/src/integrations/coverage/c8.ts#86 if you do change this const context = this.prepareContext({ // esm transformed by Vite __vite_ssr_import__: request, diff --git a/packages/vitest/LICENSE.md b/packages/vitest/LICENSE.md index bb9148d8ade8..f4efaa8ab7a6 100644 --- a/packages/vitest/LICENSE.md +++ b/packages/vitest/LICENSE.md @@ -898,7 +898,7 @@ Repository: ehmicky/human-signals > same "printed page" as the copyright notice for easier > identification within third-party archives. > -> Copyright 2019 ehmicky +> Copyright 2021 ehmicky > > Licensed under the Apache License, Version 2.0 (the "License"); > you may not use this file except in compliance with the License. @@ -1310,7 +1310,7 @@ Repository: sindresorhus/npm-run-path > MIT License > -> Copyright (c) Sindre Sorhus (sindresorhus.com) +> Copyright (c) Sindre Sorhus (https://sindresorhus.com) > > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: > @@ -1395,7 +1395,7 @@ Repository: sindresorhus/path-key > MIT License > -> Copyright (c) Sindre Sorhus (sindresorhus.com) +> Copyright (c) Sindre Sorhus (https://sindresorhus.com) > > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: > @@ -1911,7 +1911,7 @@ Repository: sindresorhus/strip-final-newline > MIT License > -> Copyright (c) Sindre Sorhus (sindresorhus.com) +> Copyright (c) Sindre Sorhus (https://sindresorhus.com) > > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: > diff --git a/packages/vitest/package.json b/packages/vitest/package.json index 146639cdf925..27996eba8c1f 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -16,6 +16,7 @@ "keywords": [ "vite", "vite-node", + "vitest", "test", "jest" ], @@ -70,7 +71,6 @@ "@edge-runtime/vm": "*", "@vitest/browser": "*", "@vitest/ui": "*", - "c8": "*", "happy-dom": "*", "jsdom": "*" }, @@ -81,9 +81,6 @@ "@vitest/browser": { "optional": true }, - "c8": { - "optional": true - }, "happy-dom": { "optional": true }, @@ -117,7 +114,6 @@ "@types/sinonjs__fake-timers": "^8.1.2", "@vitest/ui": "workspace:*", "birpc": "^0.2.3", - "c8": "^7.12.0", "cac": "^6.7.12", "chai-subset": "^1.6.0", "cli-truncate": "^3.1.0", diff --git a/packages/vitest/src/config.ts b/packages/vitest/src/config.ts index 5b783a22fed8..dc18055c82e0 100644 --- a/packages/vitest/src/config.ts +++ b/packages/vitest/src/config.ts @@ -5,7 +5,7 @@ export interface UserConfig extends ViteUserConfig { } // will import vitest declare test in module 'vite' -export { configDefaults } from './defaults' +export { configDefaults, defaultInclude, defaultExclude } from './defaults' export type { ConfigEnv } export type UserConfigFn = (env: ConfigEnv) => UserConfig | Promise diff --git a/packages/vitest/src/defaults.ts b/packages/vitest/src/defaults.ts index 63add24a37dd..54d16961b713 100644 --- a/packages/vitest/src/defaults.ts +++ b/packages/vitest/src/defaults.ts @@ -1,4 +1,4 @@ -import type { ResolvedC8Options, UserConfig } from './types' +import type { ResolvedCoverageOptions, UserConfig } from './types' export const defaultInclude = ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'] export const defaultExclude = ['**/node_modules/**', '**/dist/**', '**/cypress/**', '**/.{idea,git,cache,output,temp}/**'] @@ -19,6 +19,7 @@ const defaultCoverageExcludes = [ ] const coverageConfigDefaults = { + provider: 'c8', enabled: false, clean: true, cleanOnRerun: false, @@ -30,7 +31,7 @@ const coverageConfigDefaults = { // default extensions used by c8, plus '.vue' and '.svelte' // see https://github.com/istanbuljs/schema/blob/master/default-extension.js extension: ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx', '.vue', '.svelte'], -} as ResolvedC8Options +} as ResolvedCoverageOptions export const fakeTimersDefaults = { loopLimit: 10_000, diff --git a/packages/vitest/src/integrations/coverage.ts b/packages/vitest/src/integrations/coverage.ts index 76c95a476621..a14b4d5dc9f9 100644 --- a/packages/vitest/src/integrations/coverage.ts +++ b/packages/vitest/src/integrations/coverage.ts @@ -1,115 +1,34 @@ -import { existsSync, promises as fs } from 'fs' -import { createRequire } from 'module' -import _url from 'url' -import type { Profiler } from 'inspector' -import { resolve } from 'pathe' -import type { RawSourceMap } from 'vite-node' -import type { Vitest } from '../node' -import { toArray } from '../utils' -import type { C8Options, ResolvedC8Options } from '../types' -import { configDefaults } from '../defaults' +import { importModule } from 'local-pkg' +import type { CoverageOptions, CoverageProvider, CoverageProviderModule } from '../types' -export function resolveC8Options(options: C8Options, root: string): ResolvedC8Options { - const resolved: ResolvedC8Options = { - ...configDefaults.coverage, - ...options as any, - } - - resolved.reporter = toArray(resolved.reporter) - resolved.reportsDirectory = resolve(root, resolved.reportsDirectory) - resolved.tempDirectory = process.env.NODE_V8_COVERAGE || resolve(resolved.reportsDirectory, 'tmp') - - return resolved as ResolvedC8Options -} - -export async function cleanCoverage(options: ResolvedC8Options, clean = true) { - if (clean && existsSync(options.reportsDirectory)) - await fs.rm(options.reportsDirectory, { recursive: true, force: true }) - - if (!existsSync(options.tempDirectory)) - await fs.mkdir(options.tempDirectory, { recursive: true }) +export const CoverageProviderMap = { + c8: '@vitest/coverage-c8', + istanbul: '@vitest/coverage-istanbul', } -const require = createRequire(import.meta.url) - -// Flush coverage to disk -export function takeCoverage() { - const v8 = require('v8') - if (v8.takeCoverage == null) - console.warn('[Vitest] takeCoverage is not available in this NodeJs version.\nCoverage could be incomplete. Update to NodeJs 14.18.') - else - v8.takeCoverage() +export async function resolveCoverageProvider(provider: NonNullable) { + if (typeof provider === 'string') { + const pkg = CoverageProviderMap[provider] + if (!pkg) + throw new Error(`Unknown coverage provider: ${provider}`) + return await importModule(pkg) + } + else { + return provider + } } -export async function reportCoverage(ctx: Vitest) { - takeCoverage() - - const createReport = require('c8/lib/report') - const report = createReport(ctx.config.coverage) - - // add source maps - const sourceMapMeta: Record = {} - await Promise.all(Array - .from(ctx.vitenode.fetchCache.entries()) - .filter(i => !i[0].includes('/node_modules/')) - .map(async ([file, { result }]) => { - const map = result.map - if (!map) - return - - const url = _url.pathToFileURL(file).href - - let code: string | undefined - try { - code = (await fs.readFile(file)).toString() - } - catch {} - - // Vite does not report full path in sourcemap sources - // so use an actual file path - const sources = [url] - - sourceMapMeta[url] = { - source: result.code, - map: { - sourcesContent: code ? [code] : undefined, - ...map, - sources, - }, - } - })) - - // This is a magic number. It corresponds to the amount of code - // that we add in packages/vite-node/src/client.ts:114 (vm.runInThisContext) - // TODO: Include our transformations in sourcemaps - const offset = 224 - - report._getSourceMap = (coverage: Profiler.ScriptCoverage) => { - const path = _url.pathToFileURL(coverage.url).href - const data = sourceMapMeta[path] - - if (!data) - return {} - - return { - sourceMap: { - sourcemap: data.map, - }, - source: Array(offset).fill('.').join('') + data.source, - } +export async function getCoverageProvider(options?: CoverageOptions): Promise { + if (options?.enabled && options?.provider) { + const { getProvider } = await resolveCoverageProvider(options.provider) + return await getProvider() } + return null +} - await report.run() - - if (ctx.config.coverage.enabled) { - if (ctx.config.coverage['100']) { - ctx.config.coverage.lines = 100 - ctx.config.coverage.functions = 100 - ctx.config.coverage.branches = 100 - ctx.config.coverage.statements = 100 - } - - const { checkCoverages } = require('c8/lib/commands/check-coverage') - await checkCoverages(ctx.config.coverage, report) +export async function takeCoverageInsideWorker(options: CoverageOptions) { + if (options.enabled && options.provider) { + const { takeCoverage } = await resolveCoverageProvider(options.provider) + return await takeCoverage?.() } } diff --git a/packages/vitest/src/node/cli-api.ts b/packages/vitest/src/node/cli-api.ts index db60a81381b3..6106cdb609f8 100644 --- a/packages/vitest/src/node/cli-api.ts +++ b/packages/vitest/src/node/cli-api.ts @@ -1,5 +1,6 @@ import { resolve } from 'pathe' import type { UserConfig as ViteUserConfig } from 'vite' +import { CoverageProviderMap } from '../integrations/coverage' import { envPackageNames } from '../integrations/env' import type { UserConfig } from '../types' import { ensurePackageInstalled } from '../utils' @@ -37,9 +38,14 @@ export async function startVitest(cliFilters: string[], options: CliOptions, vit const ctx = await createVitest(options, viteOverrides) if (ctx.config.coverage.enabled) { - if (!await ensurePackageInstalled('c8', root)) { - process.exitCode = 1 - return false + const provider = ctx.config.coverage.provider || 'c8' + if (typeof provider === 'string') { + const requiredPackages = CoverageProviderMap[provider] + + if (!await ensurePackageInstalled(requiredPackages, root)) { + process.exitCode = 1 + return false + } } } diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index ba72347d602f..c42b4fd6dd5b 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -25,7 +25,7 @@ cli .option('--outputTruncateLength ', 'diff output length (default: 80)') .option('--outputDiffLines ', 'number of diff output lines (default: 15)') .option('--outputFile ', 'write test results to a file when the --reporter=json or --reporter=junit option is also specified, use cac\'s dot notation for individual outputs of mutliple reporters') - .option('--coverage', 'use c8 for coverage') + .option('--coverage', 'enable coverage report') .option('--run', 'do not watch') .option('--mode ', 'override Vite mode (default: test)') .option('--globals', 'inject apis globally') diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index e2543ac6ca7c..4e1b6ddba6cc 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -6,7 +6,6 @@ import type { ResolvedConfig as ResolvedViteConfig } from 'vite' import type { ApiConfig, ResolvedConfig, UserConfig } from '../types' import { defaultPort } from '../constants' import { configDefaults } from '../defaults' -import { resolveC8Options } from '../integrations/coverage' import { toArray } from '../utils' import { VitestCache } from './cache' import { BaseSequencer } from './sequencers/BaseSequencer' @@ -93,8 +92,6 @@ export function resolveConfig( if (viteConfig.base !== '/') resolved.base = viteConfig.base - resolved.coverage = resolveC8Options(options.coverage || {}, resolved.root) - if (options.shard) { if (resolved.watch) throw new Error('You cannot use --shard option with enabled watch') diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index 49b7e361e9b6..ac8aa7da9ad1 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -6,10 +6,10 @@ import mm from 'micromatch' import c from 'picocolors' import { ViteNodeRunner } from 'vite-node/client' import { ViteNodeServer } from 'vite-node/server' -import type { ArgumentsType, Reporter, ResolvedConfig, UserConfig } from '../types' +import type { ArgumentsType, CoverageProvider, Reporter, ResolvedConfig, UserConfig } from '../types' import { SnapshotManager } from '../integrations/snapshot/manager' import { clearTimeout, deepMerge, hasFailed, noop, setTimeout, slash } from '../utils' -import { cleanCoverage, reportCoverage } from '../integrations/coverage' +import { getCoverageProvider } from '../integrations/coverage' import { createPool } from './pool' import type { WorkerPool } from './pool' import { createReporters } from './reporters/utils' @@ -29,6 +29,7 @@ export class Vitest { snapshot: SnapshotManager = undefined! cache: VitestCache = undefined! reporters: Reporter[] = undefined! + coverageProvider: CoverageProvider | null | undefined logger: Logger pool: WorkerPool | undefined @@ -86,8 +87,7 @@ export class Vitest { this._onRestartListeners.forEach(fn => fn()) - if (resolved.coverage.enabled) - await cleanCoverage(resolved.coverage, resolved.coverage.clean) + await this.coverageProvider?.clean(this.config.coverage.clean) this.cache.results.setConfig(resolved.root, resolved.cache) try { @@ -98,6 +98,17 @@ export class Vitest { } } + async initCoverageProvider() { + if (this.coverageProvider !== undefined) + return + this.coverageProvider = await getCoverageProvider(this.config.coverage) + if (this.coverageProvider) { + await this.coverageProvider.initialize(this) + this.config.coverage = this.coverageProvider.resolveOptions() + } + return this.coverageProvider + } + getSerializableConfig() { return deepMerge({ ...this.config, @@ -117,6 +128,14 @@ export class Vitest { } async start(filters?: string[]) { + try { + await this.initCoverageProvider() + } + catch (e) { + this.logger.error(e) + process.exit(1) + } + await this.report('onInit', this) const files = await this.filterTestsBySource( @@ -136,8 +155,10 @@ export class Vitest { await this.runFiles(files) - if (this.config.coverage.enabled) - await reportCoverage(this) + if (this.coverageProvider) { + this.logger.log(c.blue(' % ') + c.dim('Coverage report from ') + c.yellow(this.coverageProvider.name)) + await this.coverageProvider.reportCoverage() + } if (this.config.watch && !this.config.browser) await this.report('onWatcherStart') @@ -321,15 +342,14 @@ export class Vitest { const files = Array.from(this.changedTests) this.changedTests.clear() - if (this.config.coverage.enabled && this.config.coverage.cleanOnRerun) - await cleanCoverage(this.config.coverage) + if (this.coverageProvider && this.config.coverage.cleanOnRerun) + await this.coverageProvider.clean() await this.report('onWatcherRerun', files, triggerId) await this.runFiles(files) - if (this.config.coverage.enabled) - await reportCoverage(this) + await this.coverageProvider?.reportCoverage() if (!this.config.browser) await this.report('onWatcherStart') diff --git a/packages/vitest/src/node/logger.ts b/packages/vitest/src/node/logger.ts index c4a613522006..5509c161c2cd 100644 --- a/packages/vitest/src/node/logger.ts +++ b/packages/vitest/src/node/logger.ts @@ -102,6 +102,9 @@ export class Logger { else if (this.ctx.config.api) this.log(c.dim(c.green(` API started at http://${this.ctx.config.api?.host || 'localhost'}:${c.bold(`${this.ctx.config.api.port}`)}`))) + if (this.ctx.coverageProvider) + this.log(c.dim(' Coverage enabled with ') + c.yellow(this.ctx.coverageProvider.name)) + this.log() } diff --git a/packages/vitest/src/node/plugins/coverageTransform.ts b/packages/vitest/src/node/plugins/coverageTransform.ts new file mode 100644 index 000000000000..f31d19762e87 --- /dev/null +++ b/packages/vitest/src/node/plugins/coverageTransform.ts @@ -0,0 +1,12 @@ +import type { Plugin as VitePlugin } from 'vite' + +import type { Vitest } from '../core' + +export function CoverageTransform(ctx: Vitest): VitePlugin | null { + return { + name: 'vitest:coverage-transform', + transform(srcCode, id) { + return ctx.coverageProvider?.onFileTransform?.(srcCode, id, this) + }, + } +} diff --git a/packages/vitest/src/node/plugins/index.ts b/packages/vitest/src/node/plugins/index.ts index d722baa94939..a2a8266151e6 100644 --- a/packages/vitest/src/node/plugins/index.ts +++ b/packages/vitest/src/node/plugins/index.ts @@ -8,6 +8,7 @@ import { EnvReplacerPlugin } from './envRelacer' import { GlobalSetupPlugin } from './globalSetup' import { MocksPlugin } from './mock' import { CSSEnablerPlugin } from './cssEnabler' +import { CoverageTransform } from './coverageTransform' export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()): Promise { let haveStarted = false @@ -169,6 +170,7 @@ export async function VitestPlugin(options: UserConfig = {}, ctx = new Vitest()) ? await BrowserPlugin() : []), CSSEnablerPlugin(ctx), + CoverageTransform(ctx), options.ui ? await UIPlugin() : null, diff --git a/packages/vitest/src/node/pool.ts b/packages/vitest/src/node/pool.ts index 0f1b116b8755..6b0d6e07d9d0 100644 --- a/packages/vitest/src/node/pool.ts +++ b/packages/vitest/src/node/pool.ts @@ -64,8 +64,7 @@ export function createPool(ctx: Vitest): WorkerPool { options.minThreads = 1 } - if (ctx.config.coverage.enabled) - process.env.NODE_V8_COVERAGE ||= ctx.config.coverage.tempDirectory + ctx.coverageProvider?.onBeforeFilesRun?.() options.env = { TEST: 'true', @@ -171,6 +170,9 @@ function createChannel(ctx: Vitest) { ctx.state.collectFiles(files) ctx.report('onCollected', files) }, + onAfterSuiteRun(meta) { + ctx.coverageProvider?.onAfterSuiteRun(meta) + }, onTaskUpdate(packs) { ctx.state.updateTasks(packs) ctx.report('onTaskUpdate', packs) diff --git a/packages/vitest/src/runtime/run.ts b/packages/vitest/src/runtime/run.ts index 9dcb5601ebe1..fb019a302fc9 100644 --- a/packages/vitest/src/runtime/run.ts +++ b/packages/vitest/src/runtime/run.ts @@ -2,9 +2,9 @@ import limit from 'p-limit' import type { File, HookCleanupCallback, HookListener, ResolvedConfig, Suite, SuiteHooks, Task, TaskResult, TaskState, Test } from '../types' import { vi } from '../integrations/vi' import { clearTimeout, getFullName, getWorkerState, hasFailed, hasTests, isBrowser, isNode, partitionSuiteChildren, setTimeout, shuffle } from '../utils' -import { takeCoverage } from '../integrations/coverage' import { getState, setState } from '../integrations/chai/jest-expect' import { GLOBAL_EXPECT } from '../integrations/chai/constants' +import { takeCoverageInsideWorker } from '../integrations/coverage' import { getFn, getHooks } from './map' import { rpc } from './rpc' import { collectTests } from './collect' @@ -316,7 +316,8 @@ async function startTestsNode(paths: string[], config: ResolvedConfig) { await runFiles(files, config) - takeCoverage() + const coverage = await takeCoverageInsideWorker(config.coverage) + rpc().onAfterSuiteRun({ coverage }) await getSnapshotClient().saveCurrent() diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index 8258f24262f7..aa63e8274e8f 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -3,7 +3,7 @@ import type { PrettyFormatOptions } from 'pretty-format' import type { FakeTimerInstallOpts } from '@sinonjs/fake-timers' import type { BuiltinReporters } from '../node/reporters' import type { TestSequencerConstructor } from '../node/sequencers/types' -import type { C8Options, ResolvedC8Options } from './coverage' +import type { CoverageOptions, ResolvedCoverageOptions } from './coverage' import type { JSDOMOptions } from './jsdom-options' import type { Reporter } from './reporter' import type { SnapshotStateOptions } from './snapshot' @@ -242,7 +242,7 @@ export interface InlineConfig { /** * Coverage options */ - coverage?: C8Options + coverage?: CoverageOptions /** * run test names with the specified pattern @@ -478,7 +478,7 @@ export interface ResolvedConfig extends Omit, 'config' | 'f testNamePattern?: RegExp related?: string[] - coverage: ResolvedC8Options + coverage: ResolvedCoverageOptions snapshotOptions: SnapshotStateOptions reporters: (Reporter | BuiltinReporters)[] diff --git a/packages/vitest/src/types/coverage.ts b/packages/vitest/src/types/coverage.ts index f65cfac9a44c..d2c14d7e66b7 100644 --- a/packages/vitest/src/types/coverage.ts +++ b/packages/vitest/src/types/coverage.ts @@ -1,4 +1,37 @@ +import type { TransformPluginContext, TransformResult } from 'rollup' +import type { Vitest } from '../node' import type { Arrayable } from './general' +import type { AfterSuiteRunMeta } from './worker' + +export interface CoverageProvider { + name: string + initialize(ctx: Vitest): Promise | void + + resolveOptions(): ResolvedCoverageOptions + clean(clean?: boolean): void | Promise + + onBeforeFilesRun?(): void | Promise + onAfterSuiteRun(meta: AfterSuiteRunMeta): void | Promise + + reportCoverage(): void | Promise + + onFileTransform?( + sourceCode: string, + id: string, + pluginCtx: TransformPluginContext + ): TransformResult | Promise +} + +export interface CoverageProviderModule { + /** + * Factory for creating a new coverage provider + */ + getProvider(): CoverageProvider | Promise + /** + * Executed on after each run in the worker thread. Possible to return a payload passed to the provider + */ + takeCoverage?(): unknown | Promise +} export type CoverageReporter = | 'clover' @@ -15,68 +48,122 @@ export type CoverageReporter = | 'text-summary' | 'text' -export interface C8Options { +export type CoverageOptions = + | BaseCoverageOptions & { provider?: null | CoverageProviderModule } + | CoverageC8Options & { provider?: 'c8' } + | CoverageIstanbulOptions & { provider?: 'istanbul' } + +export type ResolvedCoverageOptions = + & { tempDirectory: string } + & Required + +export interface BaseCoverageOptions { /** * Enable coverage, pass `--coverage` to enable * * @default false */ enabled?: boolean - /** - * Directory to write coverage report to - */ - reportsDirectory?: string + /** * Clean coverage before running tests * * @default true */ clean?: boolean + /** * Clean coverage report on watch rerun * * @default false */ cleanOnRerun?: boolean + + /** + * Directory to write coverage report to + */ + reportsDirectory?: string + + /** + * Reporters + * + * @default 'text' + */ + reporter?: Arrayable + + /** + * List of files excluded from coverage as glob patterns + */ + exclude?: string[] + + /** + * Do not show files with 100% statement, branch, and function coverage + */ + skipFull?: boolean + /** * Check thresholds per file * * @default false */ perFile?: boolean + + /** + * Threshold for lines + */ + lines?: number + + /** + * Threshold for functions + */ + functions?: number + + /** + * Threshold for branches + */ + branches?: number + + /** + * Threshold for statements + */ + statements?: number + + /** + * Extensions for files to be included in coverage + */ + extension?: string | string[] +} + +export interface CoverageIstanbulOptions extends BaseCoverageOptions { + /* Set to array of class method names to ignore for coverage */ + ignoreClassMethods?: string[] + + /* Watermarks for statements, lines, branches and functions */ + watermarks?: { + statements?: [number, number] + functions?: [number, number] + branches?: [number, number] + lines?: [number, number] + } +} + +export interface CoverageC8Options extends BaseCoverageOptions { /** * Allow files from outside of your cwd. * * @default false */ allowExternal?: any - /** - * Reporters - * - * @default 'text' - */ - reporter?: Arrayable /** * Exclude coverage under /node_modules/ * * @default true */ excludeNodeModules?: boolean - exclude?: string[] include?: string[] - skipFull?: boolean - extension?: string | string[] all?: boolean src?: string[] 100?: boolean - lines?: number - functions?: number - branches?: number - statements?: number -} - -export interface ResolvedC8Options extends Required { - tempDirectory: string } diff --git a/packages/vitest/src/types/worker.ts b/packages/vitest/src/types/worker.ts index 2e49a77c1866..4b4b984cf7cf 100644 --- a/packages/vitest/src/types/worker.ts +++ b/packages/vitest/src/types/worker.ts @@ -17,6 +17,10 @@ export interface WorkerContext { export type ResolveIdFunction = (id: string, importer?: string) => Promise +export interface AfterSuiteRunMeta { + coverage?: unknown +} + export interface WorkerRPC { fetch: FetchFunction resolveId: ResolveIdFunction @@ -28,6 +32,7 @@ export interface WorkerRPC { onUserConsoleLog: (log: UserConsoleLog) => void onUnhandledRejection: (err: unknown) => void onCollected: (files: File[]) => void + onAfterSuiteRun: (meta: AfterSuiteRunMeta) => void onTaskUpdate: (pack: TaskResultPack[]) => void snapshotSaved: (snapshot: SnapshotResult) => void diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 590ce36f327e..3a1a76c486e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,6 +21,8 @@ importers: '@types/node': ^18.7.3 '@types/ws': ^8.5.3 '@vitest/browser': workspace:* + '@vitest/coverage-c8': workspace:* + '@vitest/coverage-istanbul': workspace:* '@vitest/ui': workspace:* bumpp: ^8.2.1 c8: ^7.12.0 @@ -43,6 +45,7 @@ importers: rollup-plugin-esbuild: ^4.9.3 rollup-plugin-license: ^2.8.1 simple-git-hooks: ^2.8.0 + tsup: ^6.2.2 typescript: ^4.7.4 vite: ^3.0.7 vitest: workspace:* @@ -59,6 +62,8 @@ importers: '@types/node': 18.7.3 '@types/ws': 8.5.3 '@vitest/browser': link:packages/browser + '@vitest/coverage-c8': link:packages/coverage-c8 + '@vitest/coverage-istanbul': link:packages/coverage-istanbul '@vitest/ui': link:packages/ui bumpp: 8.2.1 c8: 7.12.0 @@ -81,6 +86,7 @@ importers: rollup-plugin-esbuild: 4.9.3_2xvlthrg5p5laozpkponua2u2i rollup-plugin-license: 2.8.1_rollup@2.77.3 simple-git-hooks: 2.8.0 + tsup: 6.2.2_typescript@4.7.4 typescript: 4.7.4 vite: 3.0.7 vitest: link:packages/vitest @@ -210,7 +216,7 @@ importers: react-dom: 18.0.0_react@18.0.0 devDependencies: '@testing-library/react': 13.2.0_zpnidt7m3osuk7shl3s4oenomq - '@types/node': 18.7.3 + '@types/node': 18.7.4 '@types/react': 18.0.17 '@vitejs/plugin-react': 2.0.1 jsdom: 20.0.0 @@ -608,6 +614,48 @@ importers: rollup: 2.77.3 vitest: link:../vitest + packages/coverage-c8: + specifiers: + c8: ^7.12.0 + vite-node: workspace:* + vitest: workspace:* + dependencies: + c8: 7.12.0 + vitest: link:../vitest + devDependencies: + vite-node: link:../vite-node + + packages/coverage-istanbul: + specifiers: + '@types/istanbul-lib-coverage': ^2.0.4 + '@types/istanbul-lib-instrument': ^1.7.4 + '@types/istanbul-lib-report': ^3.0.0 + '@types/istanbul-lib-source-maps': ^4.0.1 + '@types/istanbul-reports': ^3.0.1 + istanbul-lib-coverage: ^3.2.0 + istanbul-lib-instrument: ^5.2.0 + istanbul-lib-report: ^3.0.0 + istanbul-lib-source-maps: ^4.0.1 + istanbul-reports: ^3.1.5 + pathe: ^0.2.0 + test-exclude: ^6.0.0 + vitest: workspace:* + dependencies: + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 5.2.0 + istanbul-lib-report: 3.0.0 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.5 + test-exclude: 6.0.0 + vitest: link:../vitest + devDependencies: + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-lib-instrument': 1.7.4 + '@types/istanbul-lib-report': 3.0.0 + '@types/istanbul-lib-source-maps': 4.0.1 + '@types/istanbul-reports': 3.0.1 + pathe: 0.2.0 + packages/ui: specifiers: '@faker-js/faker': ^7.4.0 @@ -710,7 +758,6 @@ importers: '@types/sinonjs__fake-timers': ^8.1.2 '@vitest/ui': workspace:* birpc: ^0.2.3 - c8: ^7.12.0 cac: ^6.7.12 chai: ^4.3.6 chai-subset: ^1.6.0 @@ -766,7 +813,6 @@ importers: '@types/sinonjs__fake-timers': 8.1.2 '@vitest/ui': link:../ui birpc: 0.2.3 - c8: 7.12.0 cac: 6.7.12 chai-subset: 1.6.0 cli-truncate: 3.1.0 @@ -1111,7 +1157,6 @@ packages: dependencies: '@jridgewell/gen-mapping': 0.1.1 '@jridgewell/trace-mapping': 0.3.13 - dev: true /@antfu/eslint-config-basic/0.26.1_yy5c4q6zhpajzhorvwhnwvctfe: resolution: {integrity: sha512-MPos0lb8VJztb2tBf8ZYWopXkXQ/PM3Ub9k+8fRTt3nHiVo5UvkxUGV2dMvhJC1XkUE4Ym6yr1wZZz9BSdlkpQ==} @@ -1295,7 +1340,6 @@ packages: /@babel/compat-data/7.18.8: resolution: {integrity: sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==} engines: {node: '>=6.9.0'} - dev: true /@babel/core/7.12.9: resolution: {integrity: sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==} @@ -1342,7 +1386,6 @@ packages: semver: 6.3.0 transitivePeerDependencies: - supports-color - dev: true /@babel/core/7.18.2: resolution: {integrity: sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==} @@ -1397,7 +1440,6 @@ packages: '@babel/types': 7.18.10 '@jridgewell/gen-mapping': 0.3.2 jsesc: 2.5.2 - dev: true /@babel/generator/7.18.2: resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==} @@ -1463,7 +1505,6 @@ packages: '@babel/helper-validator-option': 7.18.6 browserslist: 4.20.3 semver: 6.3.0 - dev: true /@babel/helper-compilation-targets/7.18.9_@babel+core@7.18.2: resolution: {integrity: sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==} @@ -1647,7 +1688,6 @@ packages: /@babel/helper-environment-visitor/7.18.9: resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-explode-assignable-expression/7.16.7: resolution: {integrity: sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==} @@ -1670,7 +1710,6 @@ packages: dependencies: '@babel/template': 7.18.10 '@babel/types': 7.18.10 - dev: true /@babel/helper-hoist-variables/7.16.7: resolution: {integrity: sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==} @@ -1684,7 +1723,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.10 - dev: true /@babel/helper-member-expression-to-functions/7.17.7: resolution: {integrity: sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==} @@ -1718,7 +1756,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.10 - dev: true /@babel/helper-module-transforms/7.18.0: resolution: {integrity: sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==} @@ -1750,7 +1787,6 @@ packages: '@babel/types': 7.18.10 transitivePeerDependencies: - supports-color - dev: true /@babel/helper-optimise-call-expression/7.16.7: resolution: {integrity: sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==} @@ -1828,7 +1864,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.10 - dev: true /@babel/helper-skip-transparent-expression-wrappers/7.16.0: resolution: {integrity: sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==} @@ -1849,7 +1884,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.18.10 - dev: true /@babel/helper-string-parser/7.18.10: resolution: {integrity: sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==} @@ -1871,7 +1905,6 @@ packages: /@babel/helper-validator-option/7.18.6: resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-wrap-function/7.16.8: resolution: {integrity: sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==} @@ -1905,7 +1938,6 @@ packages: '@babel/types': 7.18.10 transitivePeerDependencies: - supports-color - dev: true /@babel/highlight/7.17.12: resolution: {integrity: sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==} @@ -1938,14 +1970,6 @@ packages: dependencies: '@babel/types': 7.18.4 - /@babel/parser/7.18.9: - resolution: {integrity: sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.18.9 - dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.16.7_@babel+core@7.18.2: resolution: {integrity: sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==} engines: {node: '>=6.9.0'} @@ -3918,7 +3942,6 @@ packages: '@babel/code-frame': 7.18.6 '@babel/parser': 7.18.11 '@babel/types': 7.18.10 - dev: true /@babel/template/7.18.6: resolution: {integrity: sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==} @@ -3945,7 +3968,6 @@ packages: globals: 11.12.0 transitivePeerDependencies: - supports-color - dev: true /@babel/traverse/7.18.2: resolution: {integrity: sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==} @@ -4020,7 +4042,6 @@ packages: /@bcoe/v8-coverage/0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - dev: true /@cnakazawa/watch/1.0.4: resolution: {integrity: sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==} @@ -4480,7 +4501,6 @@ packages: /@istanbuljs/schema/0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - dev: true /@jest/transform/26.6.2: resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==} @@ -4536,7 +4556,6 @@ packages: dependencies: '@jridgewell/set-array': 1.1.0 '@jridgewell/sourcemap-codec': 1.4.13 - dev: true /@jridgewell/gen-mapping/0.3.1: resolution: {integrity: sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==} @@ -4554,28 +4573,23 @@ packages: '@jridgewell/set-array': 1.1.0 '@jridgewell/sourcemap-codec': 1.4.13 '@jridgewell/trace-mapping': 0.3.13 - dev: true /@jridgewell/resolve-uri/3.0.7: resolution: {integrity: sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==} engines: {node: '>=6.0.0'} - dev: true /@jridgewell/set-array/1.1.0: resolution: {integrity: sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg==} engines: {node: '>=6.0.0'} - dev: true /@jridgewell/sourcemap-codec/1.4.13: resolution: {integrity: sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==} - dev: true /@jridgewell/trace-mapping/0.3.13: resolution: {integrity: sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==} dependencies: '@jridgewell/resolve-uri': 3.0.7 '@jridgewell/sourcemap-codec': 1.4.13 - dev: true /@jsdevtools/ez-spawn/3.0.4: resolution: {integrity: sha512-f5DRIOZf7wxogefH03RjMPMdBF7ADTWUMoOs9kaJo06EfwF+aFhMZMDZxHg/Xe12hptN9xoZjGso2fdjapBRIA==} @@ -7084,6 +7098,10 @@ packages: resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} dev: true + /@types/babel-types/7.0.11: + resolution: {integrity: sha512-pkPtJUUY+Vwv6B1inAz55rQvivClHJxc9aVEPPmaq2cbyeMLCiDpbKpcKyX4LAwpNGi+SHBv0tHv6+0gXv0P2A==} + dev: true + /@types/braces/3.0.1: resolution: {integrity: sha512-+euflG6ygo4bn0JHtn4pYqcXwRtLvElQ7/nnjDu7iYG56H0+OhCd7d6Ug0IE3WcFpZozBKW2+80FUbv5QGk5AQ==} dev: true @@ -7113,7 +7131,7 @@ packages: /@types/concat-stream/1.6.1: resolution: {integrity: sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==} dependencies: - '@types/node': 18.7.3 + '@types/node': 18.7.4 dev: true /@types/cookie/0.4.1: @@ -7155,7 +7173,7 @@ packages: /@types/form-data/0.0.33: resolution: {integrity: sha1-yayFsqX9GENbjIXZ7LUObWyJP/g=} dependencies: - '@types/node': 18.7.3 + '@types/node': 18.7.4 dev: true /@types/fs-extra/9.0.13: @@ -7193,6 +7211,13 @@ packages: /@types/istanbul-lib-coverage/2.0.4: resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + + /@types/istanbul-lib-instrument/1.7.4: + resolution: {integrity: sha512-1i1VVkU2KrpZCmti+t5J/zBb2KLKxHgU1EYL+0QtnDnVyZ59aSKcpnG6J0I6BZGDON566YzPNIlNfk7m+9l1JA==} + dependencies: + '@types/babel-types': 7.0.11 + '@types/istanbul-lib-coverage': 2.0.4 + source-map: 0.6.1 dev: true /@types/istanbul-lib-report/3.0.0: @@ -7201,6 +7226,13 @@ packages: '@types/istanbul-lib-coverage': 2.0.4 dev: true + /@types/istanbul-lib-source-maps/4.0.1: + resolution: {integrity: sha512-WH6e5naLXI3vB2Px3whNeYxzDgm6S6sk3Ht8e3/BiWwEnzZi72wja3bWzWwcgbFTFp8hBLB7NT2p3lNJgxCxvA==} + dependencies: + '@types/istanbul-lib-coverage': 2.0.4 + source-map: 0.6.1 + dev: true + /@types/istanbul-reports/3.0.1: resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} dependencies: @@ -7293,6 +7325,10 @@ packages: resolution: {integrity: sha512-LJgzOEwWuMTBxHzgBR/fhhBOWrvBjvO+zPteUgbbuQi80rYIZHrk1mNbRUqPZqSLP2H7Rwt1EFLL/tNLD1Xx/w==} dev: true + /@types/node/18.7.4: + resolution: {integrity: sha512-RzRcw8c0B8LzryWOR4Wj7YOTFXvdYKwvrb6xQQyuDfnlTxwYXGCV5RZ/TEbq5L5kn+w3rliHAUyRcG1RtbmTFg==} + dev: true + /@types/node/8.10.66: resolution: {integrity: sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==} dev: true @@ -8559,7 +8595,6 @@ packages: /ansi-regex/5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true /ansi-regex/6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} @@ -8582,7 +8617,6 @@ packages: engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - dev: true /ansi-styles/5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} @@ -8610,6 +8644,10 @@ packages: entities: 2.2.0 dev: true + /any-promise/1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: true + /anymatch/2.0.0: resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==} dependencies: @@ -9369,7 +9407,6 @@ packages: escalade: 3.1.1 node-releases: 2.0.3 picocolors: 1.0.0 - dev: true /bser/2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} @@ -9432,6 +9469,16 @@ packages: semver: 7.3.7 dev: true + /bundle-require/3.0.4_esbuild@0.14.47: + resolution: {integrity: sha512-VXG6epB1yrLAvWVQpl92qF347/UXmncQj7J3U8kZEbdVZ1ZkQyr4hYeL/9RvcE8vVVdp53dY78Fd/3pqfRqI1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.13' + dependencies: + esbuild: 0.14.47 + load-tsconfig: 0.2.3 + dev: true + /bytes/3.0.0: resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} engines: {node: '>= 0.8'} @@ -9472,13 +9519,12 @@ packages: foreground-child: 2.0.0 istanbul-lib-coverage: 3.2.0 istanbul-lib-report: 3.0.0 - istanbul-reports: 3.1.4 + istanbul-reports: 3.1.5 rimraf: 3.0.2 test-exclude: 6.0.0 v8-to-istanbul: 9.0.0 yargs: 16.2.0 yargs-parser: 20.2.9 - dev: true /cac/6.7.12: resolution: {integrity: sha512-rM7E2ygtMkJqD9c7WnFU6fruFcN3xe4FM5yUmgxhZzIKJk4uHl9U/fhwdajGFQbQuv43FAUo1Fe8gX/oIKDeSA==} @@ -9903,7 +9949,6 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true /clone-deep/4.0.1: resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} @@ -9959,14 +10004,12 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - dev: true /color-name/1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} /color-name/1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true /color-support/1.1.3: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} @@ -10296,7 +10339,6 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true /crypto-browserify/3.12.0: resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} @@ -11088,7 +11130,6 @@ packages: /electron-to-chromium/1.4.118: resolution: {integrity: sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==} - dev: true /elliptic/6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} @@ -11104,7 +11145,6 @@ packages: /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==} @@ -11488,7 +11528,6 @@ packages: /escalade/3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} - dev: true /escape-html/1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} @@ -12380,7 +12419,6 @@ packages: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true /find-up/6.3.0: resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} @@ -12443,7 +12481,6 @@ packages: dependencies: cross-spawn: 7.0.3 signal-exit: 3.0.7 - dev: true /forever-agent/0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} @@ -12686,12 +12723,10 @@ packages: /gensync/1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - dev: true /get-caller-file/2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - dev: true /get-func-name/2.0.0: resolution: {integrity: sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=} @@ -12835,6 +12870,17 @@ packages: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} dev: true + /glob/7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + /glob/7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -12862,7 +12908,6 @@ packages: /globals/11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - dev: true /globals/13.15.0: resolution: {integrity: sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==} @@ -12997,7 +13042,6 @@ packages: /has-flag/4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - dev: true /has-glob/1.0.0: resolution: {integrity: sha512-D+8A457fBShSEI3tFCj65PAbT++5sKiFtdCdOam0gnfBgw9D277OERk+HM9qYJXmdVLZ/znez10SqHN0BBQ50g==} @@ -13217,7 +13261,6 @@ packages: /html-escaper/2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - dev: true /html-minifier-terser/5.1.1: resolution: {integrity: sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==} @@ -13732,7 +13775,6 @@ packages: /is-fullwidth-code-point/3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true /is-fullwidth-code-point/4.0.0: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} @@ -13998,7 +14040,6 @@ packages: /isexe/2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true /isobject/2.1.0: resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} @@ -14033,20 +14074,18 @@ packages: /istanbul-lib-coverage/3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} - dev: true /istanbul-lib-instrument/5.2.0: resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.18.9 - '@babel/parser': 7.18.9 + '@babel/core': 7.18.10 + '@babel/parser': 7.18.11 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 transitivePeerDependencies: - supports-color - dev: true /istanbul-lib-report/3.0.0: resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} @@ -14055,7 +14094,17 @@ packages: istanbul-lib-coverage: 3.2.0 make-dir: 3.1.0 supports-color: 7.2.0 - dev: true + + /istanbul-lib-source-maps/4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4 + istanbul-lib-coverage: 3.2.0 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: false /istanbul-reports/3.1.4: resolution: {integrity: sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==} @@ -14065,6 +14114,13 @@ packages: istanbul-lib-report: 3.0.0 dev: true + /istanbul-reports/3.1.5: + resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.0 + /iterate-iterator/1.0.2: resolution: {integrity: sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==} dev: true @@ -14293,7 +14349,6 @@ packages: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} hasBin: true - dev: true /json-parse-better-errors/1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} @@ -14333,7 +14388,6 @@ packages: resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} engines: {node: '>=6'} hasBin: true - dev: true /jsonc-eslint-parser/2.1.0: resolution: {integrity: sha512-qCRJWlbP2v6HbmKW7R3lFbeiVWHo+oMJ0j+MizwvauqnCV/EvtAeEeuCgoc/ErtsuoKgYB8U4Ih8AxJbXoE6/g==} @@ -14594,6 +14648,11 @@ packages: strip-bom: 3.0.0 dev: true + /load-tsconfig/0.2.3: + resolution: {integrity: sha512-iyT2MXws+dc2Wi6o3grCFtGXpeMvHmJqS27sMPGtV2eUu4PeFnG+33I8BlFK1t1NWMjOpcx9bridn5yxLDX2gQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /loader-runner/2.4.0: resolution: {integrity: sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==} engines: {node: '>=4.3.0 <5.0.0 || >=5.10'} @@ -14654,7 +14713,6 @@ packages: engines: {node: '>=10'} dependencies: p-locate: 5.0.0 - dev: true /locate-path/7.1.0: resolution: {integrity: sha512-HNx5uOnYeK4SxEoid5qnhRfprlJeGMzFRKPLCf/15N3/B4AiofNwC/yq7VBKdVk9dx7m+PiYCJOGg55JYTAqoQ==} @@ -14811,7 +14869,6 @@ packages: engines: {node: '>=8'} dependencies: semver: 6.3.0 - dev: true /makeerror/1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} @@ -15306,6 +15363,14 @@ packages: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} dev: true + /mz/2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: true + /nan/2.15.0: resolution: {integrity: sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==} requiresBuild: true @@ -15484,7 +15549,6 @@ packages: /node-releases/2.0.3: resolution: {integrity: sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==} - dev: true /nopt/5.0.0: resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} @@ -15883,7 +15947,6 @@ packages: engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 - dev: true /p-limit/4.0.0: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} @@ -15918,7 +15981,6 @@ packages: engines: {node: '>=10'} dependencies: p-limit: 3.1.0 - dev: true /p-locate/6.0.0: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} @@ -16105,7 +16167,6 @@ packages: /path-exists/4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - dev: true /path-exists/5.0.0: resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} @@ -16124,7 +16185,6 @@ packages: /path-key/3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - dev: true /path-key/4.0.0: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} @@ -16358,6 +16418,22 @@ packages: postcss: 7.0.39 dev: true + /postcss-load-config/3.1.4: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.0.5 + yaml: 1.10.2 + dev: true + /postcss-loader/4.3.0_gzaxsinx64nntyd3vmdqwl7coe: resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==} engines: {node: '>= 10.13.0'} @@ -17462,7 +17538,6 @@ packages: /require-directory/2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - dev: true /require-from-string/2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} @@ -17811,7 +17886,6 @@ packages: /semver/6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true - dev: true /semver/7.0.0: resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==} @@ -17935,7 +18009,6 @@ packages: engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 - dev: true /shebang-regex/1.0.0: resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} @@ -17945,7 +18018,6 @@ packages: /shebang-regex/3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - dev: true /shell-quote/1.7.3: resolution: {integrity: sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==} @@ -17973,7 +18045,6 @@ packages: /signal-exit/3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true /simple-git-hooks/2.8.0: resolution: {integrity: sha512-ocmZQORwa6x9mxg+gVIAp5o4wXiWOHGXyrDBA0+UxGKIEKOyFtL4LWNKkP/2ornQPdlnlDGDteVeYP5FjhIoWA==} @@ -18354,7 +18425,6 @@ packages: 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==} @@ -18454,7 +18524,6 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - dev: true /strip-ansi/7.0.1: resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==} @@ -18560,6 +18629,19 @@ packages: resolution: {integrity: sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==} dev: false + /sucrase/3.25.0: + resolution: {integrity: sha512-WxTtwEYXSmZArPGStGBicyRsg5TBEFhT5b7N+tF+zauImP0Acy+CoUK0/byJ8JNPK/5lbpWIVuFagI4+0l85QQ==} + engines: {node: '>=8'} + hasBin: true + dependencies: + commander: 4.1.1 + glob: 7.1.6 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.5 + ts-interface-checker: 0.1.13 + dev: true + /supports-color/2.0.0: resolution: {integrity: sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=} engines: {node: '>=0.8.0'} @@ -18576,7 +18658,6 @@ packages: engines: {node: '>=8'} dependencies: has-flag: 4.0.0 - dev: true /supports-color/8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} @@ -18798,7 +18879,6 @@ packages: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 - dev: true /text-table/0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -18821,6 +18901,19 @@ packages: qs: 6.10.3 dev: true + /thenify-all/1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: true + + /thenify/3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: true + /throttleit/1.0.0: resolution: {integrity: sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==} dev: true @@ -18958,6 +19051,11 @@ packages: punycode: 2.1.1 dev: true + /tree-kill/1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + dev: true + /trim-newlines/1.0.0: resolution: {integrity: sha1-WIeWa7WCpFA6QetST301ARgVphM=} engines: {node: '>=0.10.0'} @@ -18986,6 +19084,10 @@ packages: engines: {node: '>=14'} dev: true + /ts-interface-checker/0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: true + /ts-invariant/0.10.3: resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} engines: {node: '>=8'} @@ -19025,6 +19127,42 @@ packages: /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + /tsup/6.2.2_typescript@4.7.4: + resolution: {integrity: sha512-vJ9IAdif4GKAz2XMZzjX1hNqhBezJWXjm0qeQEoI7y//a64cxgCF8178eTMV4jBu7YNKnfAPpPSuyXW4mN+9rA==} + engines: {node: '>=14'} + hasBin: true + peerDependencies: + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: ^4.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + dependencies: + bundle-require: 3.0.4_esbuild@0.14.47 + cac: 6.7.12 + chokidar: 3.5.3 + debug: 4.3.4 + esbuild: 0.14.47 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 3.1.4 + resolve-from: 5.0.0 + rollup: 2.77.3 + source-map: 0.8.0-beta.0 + sucrase: 3.25.0 + tree-kill: 1.2.2 + typescript: 4.7.4 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + /tsutils/3.21.0_typescript@4.7.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -19743,7 +19881,6 @@ packages: '@jridgewell/trace-mapping': 0.3.13 '@types/istanbul-lib-coverage': 2.0.4 convert-source-map: 1.8.0 - dev: true /validate-npm-package-license/3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -20281,7 +20418,6 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: true /wide-align/1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} @@ -20490,7 +20626,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrap-ansi/8.0.1: resolution: {integrity: sha512-QFF+ufAqhoYHvoHdajT/Po7KoXVBPXS2bgjIam5isfWJPfIOnQZ50JtUiVvCv/sjgacf3yRrt2ZKUZ/V4itN4g==} @@ -20579,7 +20714,6 @@ packages: /y18n/5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - dev: true /yallist/2.1.2: resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} @@ -20614,7 +20748,6 @@ packages: /yargs-parser/20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} - dev: true /yargs-parser/21.0.1: resolution: {integrity: sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==} @@ -20632,7 +20765,6 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 - dev: true /yargs/17.4.1: resolution: {integrity: sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==} @@ -20657,7 +20789,6 @@ packages: /yocto-queue/0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - dev: true /yocto-queue/1.0.0: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} diff --git a/test/core/vitest.config.ts b/test/core/vitest.config.ts index abbb0fa1c691..a0acc89392e5 100644 --- a/test/core/vitest.config.ts +++ b/test/core/vitest.config.ts @@ -43,6 +43,7 @@ export default defineConfig({ ], testNamePattern: '^((?!does not include test that).)*$', coverage: { + provider: 'istanbul', reporter: ['text', 'html'], }, env: { diff --git a/test/coverage-test/coverage-test/coverage.test.ts b/test/coverage-test/coverage-test/coverage.c8.test.ts similarity index 90% rename from test/coverage-test/coverage-test/coverage.test.ts rename to test/coverage-test/coverage-test/coverage.c8.test.ts index 1cf39650795f..75b544526efb 100644 --- a/test/coverage-test/coverage-test/coverage.test.ts +++ b/test/coverage-test/coverage-test/coverage.c8.test.ts @@ -2,7 +2,7 @@ import fs from 'fs' import { resolve } from 'pathe' import { expect, test } from 'vitest' -test('coverage', async () => { +test('coverage c8', async () => { const coveragePath = resolve('./coverage/tmp/') const stat = fs.statSync(coveragePath) expect(stat.isDirectory()).toBe(true) diff --git a/test/coverage-test/coverage-test/coverage.istanbul.test.ts b/test/coverage-test/coverage-test/coverage.istanbul.test.ts new file mode 100644 index 000000000000..c2188e16ec90 --- /dev/null +++ b/test/coverage-test/coverage-test/coverage.istanbul.test.ts @@ -0,0 +1,24 @@ +import fs from 'fs' +import { resolve } from 'pathe' +import { expect, test } from 'vitest' + +test('istanbul html report', async () => { + const coveragePath = resolve('./coverage') + const files = fs.readdirSync(coveragePath) + + expect(files).toContain('index.html') + expect(files).toContain('index.ts.html') + expect(files).toContain('Hello.vue.html') +}) + +test('istanbul lcov report', async () => { + const coveragePath = resolve('./coverage') + const files = fs.readdirSync(coveragePath) + + expect(files).toContain('lcov.info') + + const lcovReport = resolve('./coverage/lcov-report') + const lcovReportFiles = fs.readdirSync(lcovReport) + + expect(lcovReportFiles).toContain('index.html') +}) diff --git a/test/coverage-test/package.json b/test/coverage-test/package.json index a2f285024b6b..5c5e123f7435 100644 --- a/test/coverage-test/package.json +++ b/test/coverage-test/package.json @@ -2,12 +2,19 @@ "name": "@vitest/test-coverage", "private": true, "scripts": { - "test": "npm run test:thread && npm run test:nothread", - "coverage": "vitest run --coverage", - "coverage:thread": "cross-env THREAD=true vitest run --coverage", - "coverage-test": "vitest -c vitest.config-coverage.ts run", - "test:thread": "npm run coverage:thread && npm run coverage-test", - "test:nothread": "npm run coverage && npm run coverage-test" + "test": "npm run test:c8 && npm run test:istanbul", + "test:c8": "npm run test:thread:c8 && npm run test:nothread:c8", + "coverage:c8": "vitest run --coverage.provider c8", + "coverage:thread:c8": "cross-env THREAD=true vitest run --coverage.provider c8", + "coverage-test:c8": "vitest -c vitest.config-c8-coverage.ts run", + "test:thread:c8": "npm run coverage:thread:c8 && npm run coverage-test:c8", + "test:nothread:c8": "npm run coverage:c8 && npm run coverage-test:c8", + "test:istanbul": "npm run test:thread:istanbul && npm run test:nothread:istanbul", + "coverage:istanbul": "vitest run --coverage.provider istanbul", + "coverage:thread:istanbul": "cross-env THREAD=true vitest run --coverage.provider istanbul", + "coverage-test:istanbul": "vitest -c vitest.config-istanbul-coverage.ts run", + "test:thread:istanbul": "npm run coverage:thread:istanbul && npm run coverage-test:istanbul", + "test:nothread:istanbul": "npm run coverage:istanbul && npm run coverage-test:istanbul" }, "devDependencies": { "@vitejs/plugin-vue": "latest", diff --git a/test/coverage-test/vitest.config-coverage.ts b/test/coverage-test/vitest.config-c8-coverage.ts similarity index 73% rename from test/coverage-test/vitest.config-coverage.ts rename to test/coverage-test/vitest.config-c8-coverage.ts index cf8df9d72998..0e9e69239b76 100644 --- a/test/coverage-test/vitest.config-coverage.ts +++ b/test/coverage-test/vitest.config-c8-coverage.ts @@ -3,7 +3,7 @@ import { defineConfig } from 'vite' export default defineConfig({ test: { include: [ - './coverage-test/*.test.ts', + './coverage-test/*.c8.test.ts', ], }, }) diff --git a/test/coverage-test/vitest.config-istanbul-coverage.ts b/test/coverage-test/vitest.config-istanbul-coverage.ts new file mode 100644 index 000000000000..620af8701b8b --- /dev/null +++ b/test/coverage-test/vitest.config-istanbul-coverage.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + test: { + include: [ + './coverage-test/*.istanbul.test.ts', + ], + }, +}) diff --git a/test/coverage-test/vitest.config.ts b/test/coverage-test/vitest.config.ts index 3944cd39f032..3d4e9cf640f3 100644 --- a/test/coverage-test/vitest.config.ts +++ b/test/coverage-test/vitest.config.ts @@ -16,5 +16,10 @@ export default defineConfig({ exclude: [ 'coverage-test/*.test.ts', ], + coverage: { + enabled: true, + clean: true, + reporter: ['html', 'text', 'lcov'], + }, }, }) diff --git a/tsconfig.json b/tsconfig.json index d301f57e8fbf..972ba657fdbd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,6 +30,7 @@ "vite-node/utils": ["./packages/vite-node/src/utils.ts"] }, "types": [ + "node", "vite/client" ] },