diff --git a/packages/coverage-istanbul/package.json b/packages/coverage-istanbul/package.json index 1c9040307c46..bbb16b4da43b 100644 --- a/packages/coverage-istanbul/package.json +++ b/packages/coverage-istanbul/package.json @@ -51,7 +51,7 @@ "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^4.0.1", "istanbul-reports": "^3.1.6", - "magicast": "^0.3.2", + "magicast": "^0.3.3", "picocolors": "^1.0.0", "test-exclude": "^6.0.0" }, diff --git a/packages/coverage-istanbul/src/provider.ts b/packages/coverage-istanbul/src/provider.ts index c25a147f7e73..f869bb4b57a6 100644 --- a/packages/coverage-istanbul/src/provider.ts +++ b/packages/coverage-istanbul/src/provider.ts @@ -4,6 +4,7 @@ import type { AfterSuiteRunMeta, CoverageIstanbulOptions, CoverageProvider, Repo import { coverageConfigDefaults, defaultExclude, defaultInclude } from 'vitest/config' import { BaseCoverageProvider } from 'vitest/coverage' import c from 'picocolors' +import type { ProxifiedModule } from 'magicast' import { parseModule } from 'magicast' import createDebug from 'debug' import libReport from 'istanbul-lib-report' @@ -238,9 +239,7 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co perFile: this.options.thresholds.perFile, configurationFile: { write: () => writeFileSync(configFilePath, configModule.generate().code, 'utf-8'), - read: () => configModule.exports.default.$type === 'function-call' - ? configModule.exports.default.$args[0] - : configModule.exports.default, + read: () => resolveConfig(configModule), }, }) } @@ -360,3 +359,29 @@ function toSlices(array: T[], size: number): T[][] { return chunks }, []) } + +function resolveConfig(configModule: ProxifiedModule) { + const mod = configModule.exports.default + + try { + // Check for "export default { test: {...} }" + if (mod.$type === 'object') + return mod + + if (mod.$type === 'function-call') { + // "export default defineConfig({ test: {...} })" + if (mod.$args[0].$type === 'object') + return mod.$args[0] + + // "export default defineConfig(() => ({ test: {...} }))" + if (mod.$args[0].$type === 'arrow-function-expression' && mod.$args[0].$body.$type === 'object') + return mod.$args[0].$body + } + } + catch (error) { + // Reduce magicast's verbose errors to readable ones + throw new Error(error instanceof Error ? error.message : String(error)) + } + + throw new Error('Failed to update coverage thresholds. Configuration file is too complex.') +} diff --git a/packages/coverage-v8/package.json b/packages/coverage-v8/package.json index 310a9b9e2f7b..1d7d787d2eb7 100644 --- a/packages/coverage-v8/package.json +++ b/packages/coverage-v8/package.json @@ -53,7 +53,7 @@ "istanbul-lib-source-maps": "^4.0.1", "istanbul-reports": "^3.1.6", "magic-string": "^0.30.5", - "magicast": "^0.3.2", + "magicast": "^0.3.3", "picocolors": "^1.0.0", "std-env": "^3.5.0", "test-exclude": "^6.0.0", diff --git a/packages/coverage-v8/src/provider.ts b/packages/coverage-v8/src/provider.ts index 19b80986fcf0..e0e34b40bea8 100644 --- a/packages/coverage-v8/src/provider.ts +++ b/packages/coverage-v8/src/provider.ts @@ -9,6 +9,7 @@ import type { CoverageMap } from 'istanbul-lib-coverage' import libCoverage from 'istanbul-lib-coverage' import libSourceMaps from 'istanbul-lib-source-maps' import MagicString from 'magic-string' +import type { ProxifiedModule } from 'magicast' import { parseModule } from 'magicast' import remapping from '@ampproject/remapping' import { normalize, resolve } from 'pathe' @@ -230,9 +231,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage perFile: this.options.thresholds.perFile, configurationFile: { write: () => writeFileSync(configFilePath, configModule.generate().code, 'utf-8'), - read: () => configModule.exports.default.$type === 'function-call' - ? configModule.exports.default.$args[0] - : configModule.exports.default, + read: () => resolveConfig(configModule), }, }) } @@ -432,3 +431,29 @@ function toSlices(array: T[], size: number): T[][] { return chunks }, []) } + +function resolveConfig(configModule: ProxifiedModule) { + const mod = configModule.exports.default + + try { + // Check for "export default { test: {...} }" + if (mod.$type === 'object') + return mod + + if (mod.$type === 'function-call') { + // "export default defineConfig({ test: {...} })" + if (mod.$args[0].$type === 'object') + return mod.$args[0] + + // "export default defineConfig(() => ({ test: {...} }))" + if (mod.$args[0].$type === 'arrow-function-expression' && mod.$args[0].$body.$type === 'object') + return mod.$args[0].$body + } + } + catch (error) { + // Reduce magicast's verbose errors to readable ones + throw new Error(error instanceof Error ? error.message : String(error)) + } + + throw new Error('Failed to update coverage thresholds. Configuration file is too complex.') +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd6205391183..909396663e6a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -930,8 +930,8 @@ importers: specifier: ^3.1.6 version: 3.1.6 magicast: - specifier: ^0.3.2 - version: 0.3.2 + specifier: ^0.3.3 + version: 0.3.3 picocolors: specifier: ^1.0.0 version: 1.0.0 @@ -991,8 +991,8 @@ importers: specifier: ^0.30.5 version: 0.30.5 magicast: - specifier: ^0.3.2 - version: 0.3.2 + specifier: ^0.3.3 + version: 0.3.3 picocolors: specifier: ^1.0.0 version: 1.0.0 @@ -1621,8 +1621,8 @@ importers: specifier: ^3.0.1 version: 3.0.1 magicast: - specifier: ^0.3.2 - version: 0.3.2 + specifier: ^0.3.3 + version: 0.3.3 vite: specifier: ^5.0.0 version: 5.0.2(@types/node@18.18.9)(less@4.1.3) @@ -2335,7 +2335,7 @@ packages: '@babel/generator': 7.23.3 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.12.9) '@babel/helpers': 7.23.2 - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@babel/template': 7.22.15 '@babel/traverse': 7.23.3 '@babel/types': 7.23.3 @@ -2361,7 +2361,7 @@ packages: '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.3) '@babel/helpers': 7.23.2 - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@babel/template': 7.22.15 '@babel/traverse': 7.23.3 '@babel/types': 7.23.3 @@ -2411,7 +2411,7 @@ packages: resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.3 + '@babel/types': 7.23.6 dev: true /@babel/helper-compilation-targets@7.22.15: @@ -2669,6 +2669,10 @@ packages: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} engines: {node: '>=6.9.0'} + /@babel/helper-string-parser@7.23.4: + resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + engines: {node: '>=6.9.0'} + /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} @@ -2700,7 +2704,7 @@ packages: dependencies: '@babel/helper-function-name': 7.23.0 '@babel/template': 7.22.15 - '@babel/types': 7.23.3 + '@babel/types': 7.23.6 dev: true /@babel/helpers@7.23.2: @@ -2726,7 +2730,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.3 + dev: false /@babel/parser@7.23.3: resolution: {integrity: sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==} @@ -2740,8 +2745,7 @@ packages: engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.23.3 - dev: true + '@babel/types': 7.23.6 /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.23.3): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} @@ -4476,7 +4480,7 @@ packages: '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.23.3) '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.23.3) '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.3) - '@babel/types': 7.23.3 + '@babel/types': 7.23.6 babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.3) babel-plugin-polyfill-corejs3: 0.8.5(@babel/core@7.23.3) babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.3) @@ -4518,7 +4522,7 @@ packages: dependencies: '@babel/core': 7.23.3 '@babel/helper-plugin-utils': 7.22.5 - '@babel/types': 7.23.3 + '@babel/types': 7.23.6 esutils: 2.0.3 dev: true @@ -4598,7 +4602,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.22.13 - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@babel/types': 7.23.3 /@babel/traverse@7.23.3: @@ -4611,7 +4615,7 @@ packages: '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@babel/types': 7.23.3 debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 @@ -4625,6 +4629,7 @@ packages: '@babel/helper-string-parser': 7.22.5 '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 + dev: true /@babel/types@7.23.3: resolution: {integrity: sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==} @@ -4634,6 +4639,14 @@ packages: '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 + /@babel/types@7.23.6: + resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.23.4 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + /@base2/pretty-print-object@1.0.1: resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} dev: true @@ -8195,7 +8208,7 @@ packages: dependencies: '@babel/core': 7.23.3 '@babel/generator': 7.23.3 - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@babel/plugin-transform-react-jsx': 7.19.0(@babel/core@7.23.3) '@babel/preset-env': 7.18.10(@babel/core@7.23.3) '@babel/traverse': 7.23.3 @@ -8324,7 +8337,7 @@ packages: resolution: {integrity: sha512-4biZIWWzoWlCarMZmTpqcJNgo/RBesYZwGFbQeXiGYsswuvfWARZnW9RE9aUEMZ4XPn7B1N3EKkWcdcWe/K2tg==} dependencies: '@babel/generator': 7.23.3 - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@babel/preset-env': 7.18.10(@babel/core@7.23.3) '@babel/types': 7.23.3 '@mdx-js/mdx': 1.6.22 @@ -8343,7 +8356,7 @@ packages: resolution: {integrity: sha512-xxUEMy0D+0G1aSYxbeVNbs+XBU5nCqW4I7awpBYSTywXDv/MJWeC6FDRpj5P1pgfq8j8jWDD5ZDvBQ7syFg0LQ==} dependencies: '@babel/generator': 7.23.3 - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@babel/preset-env': 7.18.10(@babel/core@7.23.3) '@babel/types': 7.23.3 '@mdx-js/mdx': 1.6.22 @@ -9104,8 +9117,8 @@ packages: /@types/babel__core@7.20.3: resolution: {integrity: sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==} dependencies: - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 + '@babel/parser': 7.23.6 + '@babel/types': 7.23.3 '@types/babel__generator': 7.6.6 '@types/babel__template': 7.4.3 '@types/babel__traverse': 7.20.3 @@ -9130,7 +9143,7 @@ packages: /@types/babel__template@7.4.3: resolution: {integrity: sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==} dependencies: - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@babel/types': 7.23.3 dev: true @@ -10375,7 +10388,7 @@ packages: /@vue/compiler-core@3.3.8: resolution: {integrity: sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==} dependencies: - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@vue/shared': 3.3.8 estree-walker: 2.0.2 source-map-js: 1.0.2 @@ -10406,7 +10419,7 @@ packages: /@vue/compiler-sfc@2.7.10: resolution: {integrity: sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q==} dependencies: - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 postcss: 8.4.31 source-map: 0.6.1 dev: true @@ -10478,7 +10491,7 @@ packages: /@vue/reactivity-transform@3.3.8: resolution: {integrity: sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==} dependencies: - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@vue/compiler-core': 3.3.8 '@vue/shared': 3.3.8 estree-walker: 2.0.2 @@ -12050,7 +12063,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@babel/template': 7.22.15 - '@babel/types': 7.23.3 + '@babel/types': 7.23.6 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.3 dev: true @@ -18435,7 +18448,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.23.3 - '@babel/parser': 7.23.3 + '@babel/parser': 7.23.6 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -18983,7 +18996,7 @@ packages: '@babel/generator': 7.23.3 '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.3) '@babel/traverse': 7.23.3 - '@babel/types': 7.23.3 + '@babel/types': 7.23.6 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 '@types/babel__traverse': 7.20.3 @@ -19996,11 +20009,11 @@ packages: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 - /magicast@0.3.2: - resolution: {integrity: sha512-Fjwkl6a0syt9TFN0JSYpOybxiMCkYNEeOTnOTNRbjphirLakznZXAqrXgj/7GG3D1dvETONNwrBfinvAbpunDg==} + /magicast@0.3.3: + resolution: {integrity: sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==} dependencies: - '@babel/parser': 7.23.3 - '@babel/types': 7.23.3 + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 source-map-js: 1.0.2 /make-dir@2.1.0: @@ -23942,7 +23955,7 @@ packages: dependencies: '@babel/generator': 7.23.0 '@babel/helper-module-imports': 7.22.15 - '@babel/types': 7.23.0 + '@babel/types': 7.23.3 solid-js: 1.8.3 dev: true diff --git a/test/coverage-test/coverage-report-tests/generic.report.test.ts b/test/coverage-test/coverage-report-tests/generic.report.test.ts index d7e5821b6d3d..cbff56cc9ee0 100644 --- a/test/coverage-test/coverage-report-tests/generic.report.test.ts +++ b/test/coverage-test/coverage-report-tests/generic.report.test.ts @@ -90,7 +90,7 @@ test('files should not contain a setup file', () => { test('thresholds.autoUpdate updates thresholds', async () => { const configFilename = resolve('./vitest.config.ts') const mod = parseModule(fs.readFileSync(configFilename, 'utf-8')) - const thresholds = mod.exports.default.$args[0].test.coverage.thresholds + const thresholds = mod.exports.default.$args[0].$body.test.coverage.thresholds // Configuration has fixed value of 1.01 and 0 set for each threshold expect(Number.parseInt(thresholds.functions)).toBeGreaterThan(1.01) diff --git a/test/coverage-test/package.json b/test/coverage-test/package.json index 48f5f21b4b95..1393431bb3c6 100644 --- a/test/coverage-test/package.json +++ b/test/coverage-test/package.json @@ -23,7 +23,7 @@ "happy-dom": "latest", "istanbul-lib-coverage": "^3.2.0", "istanbul-lib-report": "^3.0.1", - "magicast": "^0.3.2", + "magicast": "^0.3.3", "vite": "latest", "vitest": "workspace:*", "vue": "latest", diff --git a/test/coverage-test/vitest.config.ts b/test/coverage-test/vitest.config.ts index a6aae64bead2..01cf624633c4 100644 --- a/test/coverage-test/vitest.config.ts +++ b/test/coverage-test/vitest.config.ts @@ -6,7 +6,7 @@ import remapping from '@ampproject/remapping' const provider = process.argv[1 + process.argv.indexOf('--provider')] -export default defineConfig({ +export default defineConfig(() => ({ plugins: [ vue(), /* @@ -104,4 +104,4 @@ export default defineConfig({ './src/another-setup.ts', ], }, -}) +}))