| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -479,3 +479,36 @@ export default { | |||
| 479 | 479 | If module runner is disabled, Vitest uses a native [Node.js module loader](https://nodejs.org/api/module.html#customization-hooks) to transform files to support `import.meta.vitest`, `vi.mock` and `vi.hoisted`. | |
| 480 | 480 | ||
| 481 | 481 | If you don't use these features, you can disable this to improve performance. | |
| 482 | + | ||
| 483 | + ## experimental.preParse <Version type="experimental">4.1.3</Version> {#experimental-preparse} | ||
| 484 | + | ||
| 485 | + - **Type:** `boolean` | ||
| 486 | + - **Default:** `false` | ||
| 487 | + | ||
| 488 | + Parses test specifications before running them. This applies the [`.only`](/api/test#test-only) modifier, the [`-t`](/config/testnamepattern) test name pattern, [`--tags-filter`](/guide/test-tags#syntax), [test lines](/api/advanced/test-specification#testlines), and [test IDs](/api/advanced/test-specification#testids) across all files without executing them. For example, if only a single test is marked with `.only`, Vitest will skip all other tests in all files. | ||
| 489 | + | ||
| 490 | + ::: tip | ||
| 491 | + This option is recommended when using [`.only`](/api/test#test-only), the [`-t`](/config/testnamepattern) flag, or [`--tags-filter`](/guide/test-tags#syntax). | ||
| 492 | + | ||
| 493 | + Enabling it unconditionally may slow down your test runs due to the additional parsing step. | ||
| 494 | + ::: | ||
| 495 | + | ||
| 496 | + ::: warning | ||
| 497 | + Pre-parsing uses static analysis (AST parsing) instead of executing your test files. This means that test names, tags, and modifiers (`.only`, `.skip`, `.todo`) must be statically analyzable. Dynamic test names (e.g., names stored in variables or returned from function calls) and non-literal tags will not be resolved correctly. | ||
| 498 | + | ||
| 499 | + ```ts | ||
| 500 | + // ✅ works — static string literal | ||
| 501 | + test('adds numbers', () => {}) | ||
| 502 | + | ||
| 503 | + // ✅ works — static tags | ||
| 504 | + test('my test', { tags: ['unit'] }, () => {}) | ||
| 505 | + | ||
| 506 | + // ❌ won't match correctly — dynamic name | ||
| 507 | + const name = getName() | ||
| 508 | + test(name, () => {}) | ||
| 509 | + | ||
| 510 | + // ❌ won't match correctly — dynamic tags | ||
| 511 | + const tags = getTags() | ||
| 512 | + test('my test', { tags }, () => {}) | ||
| 513 | + ``` | ||
| 514 | + ::: | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -963,3 +963,10 @@ Controls whether Vitest will use Node.js Loader API to process in-source or mock | |||
| 963 | 963 | - **Config:** [experimental.vcsProvider](/config/experimental#experimental-vcsprovider) | |
| 964 | 964 | ||
| 965 | 965 | Custom provider for detecting changed files. (default: `git`) | |
| 966 | + | ||
| 967 | + ### experimental.preParse | ||
| 968 | + | ||
| 969 | + - **CLI:** `--experimental.preParse` | ||
| 970 | + - **Config:** [experimental.preParse](/config/experimental#experimental-preparse) | ||
| 971 | + | ||
| 972 | + Parse test specifications before running them. This will apply `.only` flag and test name pattern across all files without running them. (default: `false`) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,8 +8,6 @@ import { | |||
| 8 | 8 | calculateSuiteHash, | |
| 9 | 9 | createTaskName, | |
| 10 | 10 | generateHash, | |
| 11 | - interpretTaskModes, | ||
| 12 | - someTasksAreOnly, | ||
| 13 | 11 | validateTags, | |
| 14 | 12 | } from '@vitest/runner/utils' | |
| 15 | 13 | import { unique } from '@vitest/utils/helpers' | |
@@ -472,17 +470,6 @@ function createFileTask( | |||
| 472 | 470 | latestSuite.tasks.push(task) | |
| 473 | 471 | }) | |
| 474 | 472 | calculateSuiteHash(file) | |
| 475 | - const hasOnly = someTasksAreOnly(file) | ||
| 476 | - interpretTaskModes( | ||
| 477 | - file, | ||
| 478 | - config.testNamePattern, | ||
| 479 | - undefined, | ||
| 480 | - undefined, | ||
| 481 | - undefined, | ||
| 482 | - hasOnly, | ||
| 483 | - false, | ||
| 484 | - config.allowOnly, | ||
| 485 | - ) | ||
| 486 | 473 | markDynamicTests(file.tasks) | |
| 487 | 474 | if (!file.tasks.length) { | |
| 488 | 475 | file.result = { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -920,6 +920,9 @@ export const cliOptionsConfig: VitestCLIOptions = { | |||
| 920 | 920 | description: 'Custom provider for detecting changed files. (default: `git`)', | |
| 921 | 921 | subcommands: null, | |
| 922 | 922 | }, | |
| 923 | + preParse: { | ||
| 924 | + description: 'Parse test specifications before running them. This will apply `.only` flag and test name pattern across all files without running them. (default: `false`)', | ||
| 925 | + }, | ||
| 923 | 926 | }, | |
| 924 | 927 | }, | |
| 925 | 928 | // disable CLI options | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,7 +17,7 @@ import type { Reporter } from './types/reporter' | |||
| 17 | 17 | import type { TestRunResult } from './types/tests' | |
| 18 | 18 | import type { VCSProvider } from './vcs/vcs' | |
| 19 | 19 | import os, { tmpdir } from 'node:os' | |
| 20 | - import { getTasks, hasFailed, limitConcurrency } from '@vitest/runner/utils' | ||
| 20 | + import { createTagsFilter, getTasks, hasFailed, interpretTaskModes, limitConcurrency, someTasksAreOnly } from '@vitest/runner/utils' | ||
| 21 | 21 | import { SnapshotManager } from '@vitest/snapshot/manager' | |
| 22 | 22 | import { deepClone, deepMerge, nanoid, toArray } from '@vitest/utils/helpers' | |
| 23 | 23 | import { serializeValue } from '@vitest/utils/serialize' | |
@@ -749,7 +749,7 @@ export class Vitest { | |||
| 749 | 749 | ||
| 750 | 750 | this.filenamePattern = filters && filters?.length > 0 ? filters : undefined | |
| 751 | 751 | startSpan.setAttribute('vitest.start.filters', this.filenamePattern || []) | |
| 752 | - const specifications = await this._traces.$( | ||
| 752 | + let specifications = await this._traces.$( | ||
| 753 | 753 | 'vitest.config.resolve_include_glob', | |
| 754 | 754 | async () => { | |
| 755 | 755 | const specifications = await this.specifications.getRelevantTestSpecifications(filters) | |
@@ -767,6 +767,14 @@ export class Vitest { | |||
| 767 | 767 | }, | |
| 768 | 768 | ) | |
| 769 | 769 | ||
| 770 | + if (this.config.experimental.preParse) { | ||
| 771 | + // This populates specification.testModule with parsed information | ||
| 772 | + await this.experimental_parseSpecifications(specifications) | ||
| 773 | + specifications = specifications.filter(({ testModule }) => { | ||
| 774 | + return !testModule || testModule.task.mode !== 'skip' | ||
| 775 | + }) | ||
| 776 | + } | ||
| 777 | + | ||
| 770 | 778 | // if run with --changed, don't exit if no tests are found | |
| 771 | 779 | if (!specifications.length) { | |
| 772 | 780 | await this._traces.$('vitest.test_run', async () => { | |
@@ -1032,10 +1040,38 @@ export class Vitest { | |||
| 1032 | 1040 | ? os.availableParallelism() | |
| 1033 | 1041 | : os.cpus().length) | |
| 1034 | 1042 | const limit = limitConcurrency(concurrency) | |
| 1035 | - const promises = specifications.map(specification => | ||
| 1036 | - limit(() => this.experimental_parseSpecification(specification)), | ||
| 1037 | - ) | ||
| 1038 | - return Promise.all(promises) | ||
| 1043 | + | ||
| 1044 | + // Phase 1: parse all files in parallel (without mode interpretation) | ||
| 1045 | + const results = await Promise.all(specifications.map(specification => | ||
| 1046 | + limit(async () => { | ||
| 1047 | + const file = await astCollectTests(specification.project, specification.moduleId).catch((error) => { | ||
| 1048 | + return createFailedFileTask(specification.project, specification.moduleId, error) | ||
| 1049 | + }) | ||
| 1050 | + return { file, specification } | ||
| 1051 | + }), | ||
| 1052 | + )) | ||
| 1053 | + | ||
| 1054 | + const tagsFilter = this.config.tagsFilter | ||
| 1055 | + ? createTagsFilter(this.config.tagsFilter, this.config.tags) | ||
| 1056 | + : undefined | ||
| 1057 | + // Phase 2: cross-file .only resolution | ||
| 1058 | + const globalHasOnly = results.some(({ file }) => someTasksAreOnly(file)) | ||
| 1059 | + for (const { file, specification } of results) { | ||
| 1060 | + const config = specification.project.config | ||
| 1061 | + interpretTaskModes( | ||
| 1062 | + file, | ||
| 1063 | + config.testNamePattern, | ||
| 1064 | + specification.testLines, | ||
| 1065 | + specification.testIds, | ||
| 1066 | + tagsFilter, | ||
| 1067 | + globalHasOnly, | ||
| 1068 | + false, | ||
| 1069 | + config.allowOnly, | ||
| 1070 | + ) | ||
| 1071 | + this.state.collectFiles(specification.project, [file]) | ||
| 1072 | + } | ||
| 1073 | + | ||
| 1074 | + return results.map(({ file }) => this.state.getReportedEntity(file) as TestModule) | ||
| 1039 | 1075 | } | |
| 1040 | 1076 | ||
| 1041 | 1077 | public async experimental_parseSpecification(specification: TestSpecification): Promise<TestModule> { | |
@@ -1045,6 +1081,21 @@ export class Vitest { | |||
| 1045 | 1081 | const file = await astCollectTests(specification.project, specification.moduleId).catch((error) => { | |
| 1046 | 1082 | return createFailedFileTask(specification.project, specification.moduleId, error) | |
| 1047 | 1083 | }) | |
| 1084 | + const config = specification.project.config | ||
| 1085 | + const hasOnly = someTasksAreOnly(file) | ||
| 1086 | + const tagsFilter = this.config.tagsFilter | ||
| 1087 | + ? createTagsFilter(this.config.tagsFilter, this.config.tags) | ||
| 1088 | + : undefined | ||
| 1089 | + interpretTaskModes( | ||
| 1090 | + file, | ||
| 1091 | + config.testNamePattern, | ||
| 1092 | + specification.testLines, | ||
| 1093 | + specification.testIds, | ||
| 1094 | + tagsFilter, | ||
| 1095 | + hasOnly, | ||
| 1096 | + false, | ||
| 1097 | + config.allowOnly, | ||
| 1098 | + ) | ||
| 1048 | 1099 | // register in state, so it can be retrieved by "getReportedEntity" | |
| 1049 | 1100 | this.state.collectFiles(specification.project, [file]) | |
| 1050 | 1101 | return this.state.getReportedEntity(file) as TestModule | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -947,6 +947,12 @@ export interface InlineConfig { | |||
| 947 | 947 | * implementation of the `VCSProvider` interface to use a different version control system. | |
| 948 | 948 | */ | |
| 949 | 949 | vcsProvider?: VCSProvider | string | |
| 950 | + | ||
| 951 | + /** | ||
| 952 | + * Parse test specifications before running them. | ||
| 953 | + * This will apply `.only` flag and test name pattern across all files without running them. | ||
| 954 | + */ | ||
| 955 | + preParse?: boolean | ||
| 950 | 956 | } | |
| 951 | 957 | ||
| 952 | 958 | /** | |
| Back | FazBrowse Home | New Git URL |
0 commit comments