@vitest-agent/plugin 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -223,6 +223,11 @@ declare const CURRENT_PLUGIN_VERSION: string;
223
223
  */
224
224
  declare function AgentPlugin(options?: AgentPluginConstructorOptions, _layer?: Layer.Layer<EnvironmentDetector>): {
225
225
  name: "vitest-agent";
226
+ configResolved: (resolvedConfig: {
227
+ logger: {
228
+ warn: (msg: string, options?: unknown) => void;
229
+ };
230
+ }) => void;
226
231
  configureVitest(ctx: VitestPluginContext): Promise<void>;
227
232
  transform?: (code: string, id: string) => InjectTagsResult | null;
228
233
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitest-agent/plugin",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "private": false,
5
5
  "description": "Vitest plugin for the vitest-agent ecosystem: owns persistence, classification, baselines, trends, and dispatches rendering to a configurable reporter.",
6
6
  "keywords": [
@@ -54,7 +54,7 @@
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@vitest-agent/cli": "1.0.2",
57
- "@vitest-agent/mcp": "1.2.0",
57
+ "@vitest-agent/mcp": "1.3.0",
58
58
  "@vitest/coverage-istanbul": "^4.1.0",
59
59
  "@vitest/coverage-v8": "^4.1.0",
60
60
  "vitest": "^4.1.0"
package/plugin.js CHANGED
@@ -6,6 +6,7 @@ import { buildModuleInfo } from "./utils/build-module-info.js";
6
6
  import { DefaultDiscoverStrategy } from "./utils/discover-strategy.js";
7
7
  import { discoverProjects } from "./utils/discover-projects.js";
8
8
  import { injectTags } from "./utils/inject-tags.js";
9
+ import { isBenignViteSourceMapWarning } from "./utils/is-benign-vite-source-map-warning.js";
9
10
  import { stripConsoleReporters } from "./utils/strip-console-reporters.js";
10
11
  import { execSync } from "node:child_process";
11
12
  import { AgentConsoleMode, CiConsoleMode, CoverageLevel, EnvironmentDetector, EnvironmentDetectorLive, HumanConsoleMode, formatFatalError, resolveLogLevel } from "@vitest-agent/sdk";
@@ -97,7 +98,7 @@ const aggregatedReporterByVitest = /* @__PURE__ */ new WeakSet();
97
98
  *
98
99
  * @public
99
100
  */
100
- const CURRENT_PLUGIN_VERSION = "1.1.1";
101
+ const CURRENT_PLUGIN_VERSION = "1.1.2";
101
102
  const TEST_FILE_SUFFIX_RE = /\.(?:test|spec)\.(?:ts|tsx|js|jsx)$/;
102
103
  const TEST_FILE_DIR_RE = /\/(?:src|__test__)\//;
103
104
  const isTestFile = (id) => TEST_FILE_SUFFIX_RE.test(id) && TEST_FILE_DIR_RE.test(id);
@@ -115,6 +116,31 @@ function envToExecutor(env) {
115
116
  return "ci";
116
117
  }
117
118
  /**
119
+ * Wraps `resolvedConfig.logger.warn` in place so the benign Vite
120
+ * "Failed to load source map" / ENOENT `.js.map` noise (GitHub issue #110)
121
+ * never reaches stdout, while every other warning still passes through
122
+ * untouched.
123
+ *
124
+ * Every Vite per-environment logger (`environment.logger`) delegates its
125
+ * `warn` calls to the single root `resolvedConfig.logger.warn` function
126
+ * reference (see Vite's `PartialEnvironment` constructor), so mutating that
127
+ * one function here intercepts the warning regardless of which environment
128
+ * (`client`, `ssr`, ...) triggered it. Wrapping happens in `configResolved`
129
+ * (rather than returning a `customLogger` from the plugin's `config` hook)
130
+ * because Vite's own config-resolution pipeline can construct or replace
131
+ * the logger between `config` and `configResolved` — mutating the already-
132
+ * resolved logger instance in place is the wiring that survives.
133
+ *
134
+ * @internal
135
+ */
136
+ function installViteSourceMapWarningFilter(resolvedConfig) {
137
+ const originalWarn = resolvedConfig.logger.warn.bind(resolvedConfig.logger);
138
+ resolvedConfig.logger.warn = (msg, options) => {
139
+ if (isBenignViteSourceMapWarning(msg)) return;
140
+ originalWarn(msg, options);
141
+ };
142
+ }
143
+ /**
118
144
  * Vitest plugin that injects `AgentReporter` into the reporter chain.
119
145
  *
120
146
  * @param options - Plugin configuration options
@@ -130,6 +156,9 @@ function AgentPlugin(options = {}, _layer) {
130
156
  const discoverStrategyResolved = options.discoverStrategy === false ? null : options.discoverStrategy ?? new DefaultDiscoverStrategy();
131
157
  const pluginObj = {
132
158
  name: "vitest-agent",
159
+ configResolved(resolvedConfig) {
160
+ installViteSourceMapWarningFilter(resolvedConfig);
161
+ },
133
162
  async configureVitest(ctx) {
134
163
  try {
135
164
  const { vitest, project } = ctx;
@@ -1,11 +1,65 @@
1
1
  import { toPosixPath } from "./to-posix-path.js";
2
2
  import { DefaultDiscoverStrategy } from "./discover-strategy.js";
3
3
  import { isAbsolute, join, normalize, relative } from "node:path";
4
+ import { readdir, stat } from "node:fs/promises";
4
5
  import { findWorkspaceRootSync, getWorkspacePackagesSync } from "workspaces-effect";
5
6
 
6
7
  //#region src/utils/discover-projects.ts
8
+ const DISCOVERY_LAST_SCAN_SYMBOL = Symbol.for("vitest-agent:discovery:last-scan-at");
9
+ function recordDiscoveryScanTimestamp() {
10
+ globalThis[DISCOVERY_LAST_SCAN_SYMBOL] = (/* @__PURE__ */ new Date()).toISOString();
11
+ }
7
12
  const _cache = /* @__PURE__ */ new Map();
8
13
  /**
14
+ * Computes a cheap signature (relative path + mtimeMs pairs, sorted) for every
15
+ * file nested under `dirPath`. Used to detect added/removed/moved/renamed test
16
+ * files between `discoverProjects` calls without re-walking test-file globs.
17
+ * Returns an empty string when `dirPath` does not exist — this still produces
18
+ * a stable, comparable signature contribution.
19
+ */
20
+ async function computeDirSignature(dirPath) {
21
+ let entries;
22
+ try {
23
+ entries = await readdir(dirPath, {
24
+ withFileTypes: true,
25
+ recursive: true
26
+ });
27
+ } catch {
28
+ return "";
29
+ }
30
+ const parts = [];
31
+ for (const ent of entries) {
32
+ if (!ent.isFile()) continue;
33
+ const fullPath = join(ent.parentPath ?? dirPath, ent.name);
34
+ let mtimeMs;
35
+ try {
36
+ mtimeMs = (await stat(fullPath)).mtimeMs;
37
+ } catch {
38
+ continue;
39
+ }
40
+ const relPath = toPosixPath(relative(dirPath, fullPath));
41
+ parts.push(`${relPath}:${mtimeMs}`);
42
+ }
43
+ parts.sort();
44
+ return parts.join("|");
45
+ }
46
+ /**
47
+ * Computes a cheap whole-workspace directory signature by combining each
48
+ * package's `src/` and `__test__/` signatures (issue #100). Only entries +
49
+ * mtimes are read — no file content — so this stays fast even for large
50
+ * monorepos. A changed signature means a test file was added, removed,
51
+ * moved, or renamed since the cached result was computed.
52
+ */
53
+ async function computeWorkspaceSignature(packages) {
54
+ const parts = [];
55
+ for (const pkg of packages) {
56
+ const srcSig = await computeDirSignature(join(pkg.path, "src"));
57
+ const testDirSig = await computeDirSignature(join(pkg.path, "__test__"));
58
+ parts.push(`${pkg.path}::src=${srcSig}::__test__=${testDirSig}`);
59
+ }
60
+ return parts.join("\n");
61
+ }
62
+ /**
9
63
  * Scan all workspace packages and additional entries through the active strategy and return projects + tags.
10
64
  * @param options - Optional strategy, working directory, and extra project entries
11
65
  * @returns Resolved projects and tag definitions
@@ -18,12 +72,14 @@ async function discoverProjects(options) {
18
72
  const root = findWorkspaceRootSync(cwd ?? process.cwd());
19
73
  if (!root) throw new Error(`[vitest-agent] Could not find workspace root from ${cwd ?? process.cwd()}. Ensure a pnpm-workspace.yaml or package.json with "workspaces" exists.`);
20
74
  const useCache = strategy === void 0 && additionalEntries.length === 0;
75
+ const resolvedStrategy = strategy ?? new DefaultDiscoverStrategy();
76
+ const packages = getWorkspacePackagesSync(root);
77
+ let signature;
21
78
  if (useCache) {
79
+ signature = await computeWorkspaceSignature(packages);
22
80
  const cached = _cache.get(root);
23
- if (cached) return cached;
81
+ if (cached && cached.signature === signature) return cached.result;
24
82
  }
25
- const resolvedStrategy = strategy ?? new DefaultDiscoverStrategy();
26
- const packages = getWorkspacePackagesSync(root);
27
83
  const configs = [];
28
84
  const workspaceNames = /* @__PURE__ */ new Set();
29
85
  const workspacePaths = /* @__PURE__ */ new Set();
@@ -60,7 +116,11 @@ async function discoverProjects(options) {
60
116
  projects: configs.length > 0 ? configs : void 0,
61
117
  tags
62
118
  };
63
- if (useCache) _cache.set(root, result);
119
+ if (useCache && signature !== void 0) _cache.set(root, {
120
+ result,
121
+ signature
122
+ });
123
+ recordDiscoveryScanTimestamp();
64
124
  return result;
65
125
  }
66
126
 
@@ -0,0 +1,21 @@
1
+ //#region src/utils/is-benign-vite-source-map-warning.ts
2
+ /**
3
+ * Matches the benign Vite core warning emitted when a dependency's shipped
4
+ * `.js` file references a `.js.map` sibling that was never published in the
5
+ * npm tarball (the canonical example: `typescript/lib/typescript.js` /
6
+ * `typescript.js.map`). Vite core's `loadAndTransform` logs this through
7
+ * `environment.logger.warn` — not through per-test console output — so it
8
+ * cannot be filtered by the console-leak path. See GitHub issue #110.
9
+ *
10
+ * Only matches the specific "Failed to load source map" + ENOENT `.js.map`
11
+ * shape; every other warning (including unrelated ENOENT errors against
12
+ * other file extensions) returns `false` so it still surfaces.
13
+ * @public
14
+ */
15
+ function isBenignViteSourceMapWarning(message) {
16
+ if (!message) return false;
17
+ return /Failed to load source map/.test(message) && /ENOENT:.*\.js\.map/.test(message);
18
+ }
19
+
20
+ //#endregion
21
+ export { isBenignViteSourceMapWarning };