11#!/usr/bin/env node
22
33import path from "node:path" ;
4- import { resolveExtensionBatchPlan } from "./lib/extension-test-plan.mjs" ;
4+ import {
5+ listTrackedTestFilesForRoots ,
6+ resolveExtensionBatchPlan ,
7+ } from "./lib/extension-test-plan.mjs" ;
58import { isDirectScriptRun , runVitestBatch } from "./lib/vitest-batch-runner.mjs" ;
69
710const FS_MODULE_CACHE_PATH_ENV_KEY = "OPENCLAW_VITEST_FS_MODULE_CACHE_PATH" ;
@@ -87,9 +90,62 @@ function orderPlanGroups(planGroups, parallelism) {
8790 } ) ;
8891}
8992
93+ function normalizeRelativePath ( inputPath ) {
94+ return path
95+ . relative ( process . cwd ( ) , path . resolve ( process . cwd ( ) , inputPath ) )
96+ . split ( path . sep )
97+ . join ( "/" ) ;
98+ }
99+
100+ function isExactExcludePath ( inputPath ) {
101+ return ! / [ * ! ? [ \] { } ] / u. test ( inputPath ) ;
102+ }
103+
104+ export function parseExactVitestExcludePaths ( vitestArgs ) {
105+ const excludePaths = new Set ( ) ;
106+ for ( let index = 0 ; index < vitestArgs . length ; index += 1 ) {
107+ const arg = vitestArgs [ index ] ;
108+ if ( arg === "--exclude" ) {
109+ const value = vitestArgs [ index + 1 ] ;
110+ if ( value && isExactExcludePath ( value ) ) {
111+ excludePaths . add ( normalizeRelativePath ( value ) ) ;
112+ }
113+ index += 1 ;
114+ continue ;
115+ }
116+ const prefix = "--exclude=" ;
117+ if ( arg . startsWith ( prefix ) ) {
118+ const value = arg . slice ( prefix . length ) ;
119+ if ( value && isExactExcludePath ( value ) ) {
120+ excludePaths . add ( normalizeRelativePath ( value ) ) ;
121+ }
122+ }
123+ }
124+ return excludePaths ;
125+ }
126+
127+ function resolveGroupTargets ( group , exactExcludePaths ) {
128+ if ( exactExcludePaths . size === 0 ) {
129+ return group . roots ;
130+ }
131+
132+ const testFiles = listTrackedTestFilesForRoots ( group . roots ) ;
133+ if ( ! testFiles ) {
134+ return group . roots ;
135+ }
136+
137+ return testFiles . filter ( ( file ) => ! exactExcludePaths . has ( file ) ) ;
138+ }
139+
90140async function runPlanGroup ( group , params ) {
141+ const targets = resolveGroupTargets ( group , params . exactExcludePaths ) ;
142+ if ( targets . length === 0 ) {
143+ console . log ( `[test-extension-batch] ${ group . config } : no test files remain after excludes` ) ;
144+ return 0 ;
145+ }
146+
91147 console . log (
92- `[test-extension-batch] ${ group . config } : ${ group . extensionIds . join ( ", " ) } (${ group . testFileCount } files )` ,
148+ `[test-extension-batch] ${ group . config } : ${ group . extensionIds . join ( ", " ) } (${ targets . length } targets )` ,
93149 ) ;
94150 return await params . runGroup ( {
95151 args : params . vitestArgs ,
@@ -100,13 +156,14 @@ async function runPlanGroup(group, params) {
100156 groupIndex : params . groupIndex ,
101157 useDedicatedCache : params . useDedicatedCache ,
102158 } ) ,
103- targets : group . roots ,
159+ targets,
104160 } ) ;
105161}
106162
107163export async function runExtensionBatchPlan ( batchPlan , params = { } ) {
108164 const env = params . env ?? process . env ;
109165 const vitestArgs = params . vitestArgs ?? [ ] ;
166+ const exactExcludePaths = parseExactVitestExcludePaths ( vitestArgs ) ;
110167 const runGroup = params . runGroup ?? runVitestBatch ;
111168 const parallelism = resolveExtensionBatchParallelism ( batchPlan . planGroups . length , env ) ;
112169 const orderedGroups = orderPlanGroups ( batchPlan . planGroups , parallelism ) ;
@@ -130,6 +187,7 @@ export async function runExtensionBatchPlan(batchPlan, params = {}) {
130187 env,
131188 groupIndex,
132189 runGroup,
190+ exactExcludePaths,
133191 useDedicatedCache,
134192 vitestArgs,
135193 } ) ;
0 commit comments