@specific.dev/spectest 0.120.0 → 0.120.1

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.
@@ -0,0 +1 @@
1
+ export declare const JAVASCRIPT_GENERATED_SOURCES: readonly ["webpack/bootstrap", "*/webpack/bootstrap", "webpack/runtime/*", "*/webpack/runtime/*", "webpack://*/", "webpack://*/?*", "webpack://*/external *"];
@@ -0,0 +1,11 @@
1
+ // Bundler-generated source identities shared by browser and Node coverage.
2
+ // Do not match arbitrary webpack:// sources: those include authored modules.
3
+ export const JAVASCRIPT_GENERATED_SOURCES = [
4
+ "webpack/bootstrap",
5
+ "*/webpack/bootstrap",
6
+ "webpack/runtime/*",
7
+ "*/webpack/runtime/*",
8
+ "webpack://*/", // Loader-only modules with an empty source identity.
9
+ "webpack://*/?*", // Loader-only modules without a resource path.
10
+ "webpack://*/external *", // Webpack external-module wrappers.
11
+ ];
@@ -1,6 +1,7 @@
1
1
  // Next policy lives in the SDK. The collector/indexer only see ordinary
2
2
  // Node/browser reports and generic, report-scoped source metadata.
3
3
  import path from "node:path";
4
+ import { JAVASCRIPT_GENERATED_SOURCES } from "./coverage-javascript.js";
4
5
  import { node, browser } from "./coverage.js";
5
6
  export const NEXT_COVERAGE_HELPER_PATH = "/spectest/next-coverage.cjs";
6
7
  // Also preloaded before the Node hook. Only an identified standalone launcher
@@ -46,9 +47,6 @@ if (require.main === module) {
46
47
  }
47
48
  `;
48
49
  const GENERATED_SOURCES = [
49
- "webpack://*/", // Loader-only modules with an empty source identity.
50
- "webpack://*/?*", // Loader-only modules without a resource path.
51
- "webpack://*/external *", // Webpack external-module wrappers.
52
50
  "*/data:* <text/javascript>", // Turbopack virtual modules, including action proxies.
53
51
  "*/.next-internal/server/app/*/actions.js (server actions loader)",
54
52
  "*/app/*--route-entry.js",
@@ -78,6 +76,7 @@ export function next(options = {}) {
78
76
  return value;
79
77
  };
80
78
  return {
79
+ defaultExcludeSources: JAVASCRIPT_GENERATED_SOURCES,
81
80
  name: "next", reports: "delta", explains: server.explains,
82
81
  ownsReport: name => server.ownsReport(name) || client.ownsReport(name),
83
82
  configure(svc, info) {
@@ -88,7 +88,9 @@ export interface CoverageAdapter {
88
88
  name: string;
89
89
  /** Archived on this adapter's reports; other adapters retain their own policy. */
90
90
  sourceOptions?: CoverageSourceOptions | ((ctx: CoverageSourceContext) => Promise<CoverageSourceOptions>);
91
- /** Required with sourceOptions, including reports written directly by a runtime. */
91
+ /** Generated scaffolding excluded in addition to explicit or legacy source policy. */
92
+ defaultExcludeSources?: readonly string[];
93
+ /** Required with sourceOptions/defaultExcludeSources, including runtime reports. */
92
94
  ownsReport?(name: string): boolean;
93
95
  /**
94
96
  * How the reports this adapter writes (through `ctx.writeReport`)
@@ -172,7 +174,7 @@ export declare function coverageAdapters(cov: ServiceCoverage | undefined): read
172
174
  export declare function applyCoverageAdapters<S extends ServiceConfig>(key: string, svc: S): S;
173
175
  /** Resolve adapter policies once per capture. Invalid/overlapping ownership is
174
176
  * an error, so one adapter cannot silently reinterpret another's reports. */
175
- export declare function resolveCoverageSourceOptions(adapters: readonly CoverageAdapter[], ctx: CoverageSourceContext): Promise<{
177
+ export declare function resolveCoverageSourceOptions(adapters: readonly CoverageAdapter[], ctx: CoverageSourceContext, fallback?: CoverageSourceOptions): Promise<{
176
178
  adapter: string;
177
179
  ownsReport: (name: string) => boolean;
178
180
  options: CoverageSourceOptions | undefined;
package/dist/coverage.js CHANGED
@@ -27,6 +27,7 @@
27
27
  // import * as coverage from "@specific.dev/spectest/coverage";
28
28
  // coverage: { adapters: [coverage.node(), coverage.browser()] }
29
29
  import { createHash } from "node:crypto";
30
+ import { JAVASCRIPT_GENERATED_SOURCES } from "./coverage-javascript.js";
30
31
  import { harvestAllBrowserCoverage, takeBrowserCoverageReport } from "./browser-coverage.js";
31
32
  import { COVERAGE_CONTAINER_DIR } from "./harness/coverage.js";
32
33
  export { COVERAGE_CONTAINER_DIR };
@@ -89,13 +90,17 @@ export function validateCoverage(service, cov) {
89
90
  ["configure", "load", "capture", "inventory", "ownsReport"].every((k) => a[k] === undefined ||
90
91
  typeof a[k] === "function");
91
92
  const sourceOptions = a?.sourceOptions;
93
+ const defaults = a?.defaultExcludeSources;
94
+ const defaultShape = defaults === undefined || typeof a.ownsReport === "function";
92
95
  const sourceShape = sourceOptions === undefined || (typeof a.ownsReport === "function" &&
93
96
  (typeof sourceOptions === "function" || (typeof sourceOptions === "object" && sourceOptions !== null && !Array.isArray(sourceOptions))));
94
- if (!ok || !sourceShape) {
97
+ if (!ok || !sourceShape || !defaultShape) {
95
98
  throw new Error(`service "${service}" has an invalid coverage adapter — an adapter is \`{ name, reports?, explains?, configure?, load?, capture?, inventory? }\` (see \`defineAdapter\` in @specific.dev/spectest/coverage)`);
96
99
  }
97
100
  if (sourceOptions !== undefined && typeof sourceOptions !== "function")
98
101
  validateCoverageSourceOptions(service, sourceOptions);
102
+ if (defaults !== undefined)
103
+ validateCoverageSourceOptions(service, { excludeSources: defaults });
99
104
  }
100
105
  return;
101
106
  }
@@ -146,13 +151,22 @@ function validateCoverageSourceOptions(service, options) {
146
151
  }
147
152
  /** Resolve adapter policies once per capture. Invalid/overlapping ownership is
148
153
  * an error, so one adapter cannot silently reinterpret another's reports. */
149
- export async function resolveCoverageSourceOptions(adapters, ctx) {
150
- return Promise.all(adapters.filter(a => a.sourceOptions !== undefined || a.ownsReport).map(async (a) => {
154
+ export async function resolveCoverageSourceOptions(adapters, ctx, fallback) {
155
+ return Promise.all(adapters.filter(a => a.sourceOptions !== undefined || a.defaultExcludeSources !== undefined || a.ownsReport).map(async (a) => {
151
156
  if (!a.ownsReport)
152
157
  throw new Error(`coverage adapter "${a.name}" needs ownsReport with sourceOptions`);
153
- const options = typeof a.sourceOptions === "function" ? await a.sourceOptions(ctx) : a.sourceOptions;
158
+ let options = typeof a.sourceOptions === "function" ? await a.sourceOptions(ctx) : a.sourceOptions;
154
159
  if (a.sourceOptions !== undefined)
155
160
  validateCoverageSourceOptions(ctx.service, options);
161
+ if (a.defaultExcludeSources !== undefined) {
162
+ validateCoverageSourceOptions(ctx.service, { excludeSources: a.defaultExcludeSources });
163
+ // Defaults must not discard the source root/exclusions of older configs.
164
+ // Explicit adapter options still replace that service-level fallback.
165
+ options ??= fallback;
166
+ if (options !== undefined)
167
+ validateCoverageSourceOptions(ctx.service, options);
168
+ options = { ...options, excludeSources: [...new Set([...a.defaultExcludeSources, ...(options?.excludeSources ?? [])])] };
169
+ }
156
170
  return { adapter: a.name, ownsReport: a.ownsReport, options };
157
171
  }));
158
172
  }
@@ -170,7 +184,7 @@ function withWireName(a) {
170
184
  if (Object.prototype.hasOwnProperty.call(a, "toJSON"))
171
185
  return a;
172
186
  return Object.assign(Object.create(Object.getPrototypeOf(a)), a, {
173
- toJSON: () => ({ adapter: a.name, ...(a.sourceOptions && typeof a.sourceOptions !== "function" ? { sourceOptions: a.sourceOptions } : {}) }),
187
+ toJSON: () => ({ adapter: a.name, ...(a.defaultExcludeSources ? { defaultExcludeSources: a.defaultExcludeSources } : {}), ...(a.sourceOptions && typeof a.sourceOptions !== "function" ? { sourceOptions: a.sourceOptions } : {}) }),
174
188
  });
175
189
  }
176
190
  /** `env` with `value` appended to `name` (space-separated), or set. */
@@ -725,6 +739,7 @@ export function node(options = {}) {
725
739
  }
726
740
  return {
727
741
  name: "node",
742
+ defaultExcludeSources: JAVASCRIPT_GENERATED_SOURCES,
728
743
  ...(options.sourceRoot !== undefined || options.excludeSources !== undefined ? { sourceOptions: { sourceRoot: options.sourceRoot, excludeSources: options.excludeSources } } : {}),
729
744
  ownsReport: name => isNodeDumpName(name) || name === NODE_COVERAGE_EMPTY_REPORT,
730
745
  reports: "delta",
@@ -929,6 +944,7 @@ export async function collectNodeReports(dir) {
929
944
  export function browser(options = {}) {
930
945
  return {
931
946
  name: "browser",
947
+ defaultExcludeSources: JAVASCRIPT_GENERATED_SOURCES,
932
948
  ...(options.sourceRoot !== undefined || options.excludeSources !== undefined ? { sourceOptions: options } : {}),
933
949
  ownsReport: name => name === "browser.lcov",
934
950
  reports: "delta",
package/dist/daemon.js CHANGED
@@ -3300,7 +3300,10 @@ async function captureServiceCoverage(baseline = false) {
3300
3300
  return null;
3301
3301
  }
3302
3302
  },
3303
- });
3303
+ }, typeof svc.coverage === "object" ? {
3304
+ sourceRoot: svc.coverage.sourceRoot,
3305
+ excludeSources: svc.coverage.excludeSources,
3306
+ } : undefined);
3304
3307
  }
3305
3308
  catch (err) {
3306
3309
  return { service: svc.name, reports: [], error: `coverage source metadata: ${err instanceof Error ? err.message : String(err)}` };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specific.dev/spectest",
3
- "version": "0.120.0",
3
+ "version": "0.120.1",
4
4
  "description": "Spectest SDK for defining test environments in TypeScript.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,11 @@
1
+ // Bundler-generated source identities shared by browser and Node coverage.
2
+ // Do not match arbitrary webpack:// sources: those include authored modules.
3
+ export const JAVASCRIPT_GENERATED_SOURCES = [
4
+ "webpack/bootstrap",
5
+ "*/webpack/bootstrap",
6
+ "webpack/runtime/*",
7
+ "*/webpack/runtime/*",
8
+ "webpack://*/", // Loader-only modules with an empty source identity.
9
+ "webpack://*/?*", // Loader-only modules without a resource path.
10
+ "webpack://*/external *", // Webpack external-module wrappers.
11
+ ] as const;
@@ -29,6 +29,9 @@ describe("Next adapter", () => {
29
29
  expect(browserLoaded).toBe(true);
30
30
  const policies = await resolveCoverageSourceOptions([adapter],context());
31
31
  expect(policies[0]!.options!.sourceRoot).toBe("packages");
32
+ expect(policies[0]!.options!.excludeSources).toContain("*/webpack/runtime/*");
33
+ expect(policies[0]!.options!.excludeSources).toContain("*/.next-internal/server/app/*/actions.js (server actions loader)");
34
+ expect(policies[0]!.options!.excludeSources!.some(p => p.includes("temporal"))).toBe(false);
32
35
  const capture = attachCoverageSourceOptions({service:"web",reports:[
33
36
  {name:"node-1.json",content:'{"result":[]}'}, {name:"browser.lcov",content:"TN:\n"},
34
37
  {name:"other.lcov",content:"TN:\n"},
@@ -1,6 +1,7 @@
1
1
  // Next policy lives in the SDK. The collector/indexer only see ordinary
2
2
  // Node/browser reports and generic, report-scoped source metadata.
3
3
  import path from "node:path";
4
+ import { JAVASCRIPT_GENERATED_SOURCES } from "./coverage-javascript.js";
4
5
  import { node, browser, type CoverageAdapter, type CoverageSourceContext, type CoverageSourceOptions } from "./coverage.js";
5
6
 
6
7
  export interface NextCoverageOptions extends CoverageSourceOptions {
@@ -65,9 +66,6 @@ export interface NextBuildMetadata {
65
66
  }
66
67
 
67
68
  const GENERATED_SOURCES = [
68
- "webpack://*/", // Loader-only modules with an empty source identity.
69
- "webpack://*/?*", // Loader-only modules without a resource path.
70
- "webpack://*/external *", // Webpack external-module wrappers.
71
69
  "*/data:* <text/javascript>", // Turbopack virtual modules, including action proxies.
72
70
  "*/.next-internal/server/app/*/actions.js (server actions loader)",
73
71
  "*/app/*--route-entry.js",
@@ -96,6 +94,7 @@ export function next(options: NextCoverageOptions = {}): CoverageAdapter {
96
94
  return value;
97
95
  };
98
96
  return {
97
+ defaultExcludeSources: JAVASCRIPT_GENERATED_SOURCES,
99
98
  name: "next", reports: "delta", explains: server.explains,
100
99
  ownsReport: name => server.ownsReport!(name) || client.ownsReport!(name),
101
100
  configure(svc, info) {
@@ -22,9 +22,12 @@ import {
22
22
  isNodeDumpName,
23
23
  node,
24
24
  resetNodeCoverageMemory,
25
+ resolveCoverageSourceOptions,
26
+ attachCoverageSourceOptions,
25
27
  validateCoverage,
26
28
  type CoverageCaptureContext,
27
29
  } from "./coverage.js";
30
+ import { JAVASCRIPT_GENERATED_SOURCES } from "./coverage-javascript.js";
28
31
  import type { ServiceConfig } from "./index.js";
29
32
  import { foldBrowserCoverage } from "./browser-coverage.js";
30
33
 
@@ -109,7 +112,7 @@ describe("applyCoverageAdapters", () => {
109
112
  expect(typeof list[0]?.capture).toBe("function");
110
113
  expect(typeof list[1]?.load).toBe("function");
111
114
  expect(JSON.parse(JSON.stringify(out)).coverage).toEqual({
112
- adapters: [{ adapter: "node" }, { adapter: "browser" }],
115
+ adapters: [{ adapter: "node", defaultExcludeSources: [...JAVASCRIPT_GENERATED_SOURCES] }, { adapter: "browser", defaultExcludeSources: [...JAVASCRIPT_GENERATED_SOURCES] }],
113
116
  explains: [...NODE_EXPLAINS],
114
117
  });
115
118
  });
@@ -614,3 +617,46 @@ describe.if(hasNode)("nodeCoverage hook (real node)", () => {
614
617
  expect((await fs.readdir(dir)).filter((n) => n.endsWith(".json"))).toEqual([NODE_COVERAGE_EMPTY_REPORT]);
615
618
  });
616
619
  });
620
+
621
+
622
+ describe("JavaScript generated-source defaults", () => {
623
+ const ctx = {
624
+ service: "app", signal: new AbortController().signal,
625
+ exec: async () => ({stdout: "", stderr: ""}),
626
+ projectFiles: async () => [], readProjectFile: async () => null,
627
+ };
628
+ test("defaults are archived only on owned reports and preserve legacy policy", async () => {
629
+ const legacy = {sourceRoot: "packages", excludeSources: ["virtual:///generated/*"]};
630
+ const policies = await resolveCoverageSourceOptions([node(), browser()], ctx, legacy);
631
+ const captured = attachCoverageSourceOptions({reports: [
632
+ {name: "node-1.json"}, {name: "browser.lcov"}, {name: "other.lcov"},
633
+ ]}, policies);
634
+ for (const report of captured.reports.slice(0, 2) as any[]) {
635
+ expect(report.sourceOptions.sourceRoot).toBe("packages");
636
+ expect(report.sourceOptions.excludeSources).toContain("*/webpack/bootstrap");
637
+ expect(report.sourceOptions.excludeSources).toContain("*/webpack/runtime/*");
638
+ expect(report.sourceOptions.excludeSources).toContain("virtual:///generated/*");
639
+ expect(report.sourceOptions.excludeSources.some((p: string) => p.includes("temporal"))).toBe(false);
640
+ expect(report.sourceOptions.excludeSources.some((p: string) => p.includes("server actions loader"))).toBe(false);
641
+ }
642
+ expect((captured.reports[2] as any).sourceOptions).toBeUndefined();
643
+ });
644
+ test("explicit user policy replaces legacy policy and extends defaults without duplicates", async () => {
645
+ const user = {sourceRoot: "workflows", excludeSources: [
646
+ "*/webpack/runtime/*", "*/temporal/workflows/index-autogenerated-entrypoint.cjs",
647
+ ]};
648
+ for (const adapter of [node(user), browser(user)]) {
649
+ const [policy] = await resolveCoverageSourceOptions([adapter], ctx, {
650
+ sourceRoot: "old", excludeSources: ["old/*"],
651
+ });
652
+ expect(policy!.options!.sourceRoot).toBe("workflows");
653
+ expect(policy!.options!.excludeSources).toContain(user.excludeSources[1]!);
654
+ expect(policy!.options!.excludeSources).not.toContain("old/*");
655
+ expect(policy!.options!.excludeSources!.filter(p => p === "*/webpack/runtime/*")).toHaveLength(1);
656
+ }
657
+ });
658
+ test("default policy requires report ownership and valid source patterns", () => {
659
+ expect(() => validateCoverage("app", {adapters: [{name: "bad", defaultExcludeSources: ["generated/*"]}]})).toThrow();
660
+ expect(() => validateCoverage("app", {adapters: [{name: "bad", ownsReport: () => true, defaultExcludeSources: [""]}]})).toThrow();
661
+ });
662
+ });
package/src/coverage.ts CHANGED
@@ -28,6 +28,7 @@
28
28
  // coverage: { adapters: [coverage.node(), coverage.browser()] }
29
29
 
30
30
  import { createHash } from "node:crypto";
31
+ import { JAVASCRIPT_GENERATED_SOURCES } from "./coverage-javascript.js";
31
32
 
32
33
  import type { ServiceConfig } from "./index.js";
33
34
  import { harvestAllBrowserCoverage, takeBrowserCoverageReport } from "./browser-coverage.js";
@@ -126,7 +127,9 @@ export interface CoverageAdapter {
126
127
  name: string;
127
128
  /** Archived on this adapter's reports; other adapters retain their own policy. */
128
129
  sourceOptions?: CoverageSourceOptions | ((ctx: CoverageSourceContext) => Promise<CoverageSourceOptions>);
129
- /** Required with sourceOptions, including reports written directly by a runtime. */
130
+ /** Generated scaffolding excluded in addition to explicit or legacy source policy. */
131
+ defaultExcludeSources?: readonly string[];
132
+ /** Required with sourceOptions/defaultExcludeSources, including runtime reports. */
130
133
  ownsReport?(name: string): boolean;
131
134
  /**
132
135
  * How the reports this adapter writes (through `ctx.writeReport`)
@@ -248,14 +251,17 @@ export function validateCoverage(service: string, cov: unknown): void {
248
251
  typeof (a as Record<string, unknown>)[k] === "function",
249
252
  );
250
253
  const sourceOptions = (a as CoverageAdapter | null)?.sourceOptions;
254
+ const defaults = (a as CoverageAdapter | null)?.defaultExcludeSources;
255
+ const defaultShape = defaults === undefined || typeof (a as CoverageAdapter).ownsReport === "function";
251
256
  const sourceShape = sourceOptions === undefined || (typeof (a as CoverageAdapter).ownsReport === "function" &&
252
257
  (typeof sourceOptions === "function" || (typeof sourceOptions === "object" && sourceOptions !== null && !Array.isArray(sourceOptions))));
253
- if (!ok || !sourceShape) {
258
+ if (!ok || !sourceShape || !defaultShape) {
254
259
  throw new Error(
255
260
  `service "${service}" has an invalid coverage adapter — an adapter is \`{ name, reports?, explains?, configure?, load?, capture?, inventory? }\` (see \`defineAdapter\` in @specific.dev/spectest/coverage)`,
256
261
  );
257
262
  }
258
263
  if (sourceOptions !== undefined && typeof sourceOptions !== "function") validateCoverageSourceOptions(service, sourceOptions);
264
+ if (defaults !== undefined) validateCoverageSourceOptions(service, { excludeSources: defaults });
259
265
  }
260
266
  return;
261
267
  }
@@ -310,11 +316,19 @@ function validateCoverageSourceOptions(service: string, options: unknown): asser
310
316
 
311
317
  /** Resolve adapter policies once per capture. Invalid/overlapping ownership is
312
318
  * an error, so one adapter cannot silently reinterpret another's reports. */
313
- export async function resolveCoverageSourceOptions(adapters: readonly CoverageAdapter[], ctx: CoverageSourceContext) {
314
- return Promise.all(adapters.filter(a => a.sourceOptions !== undefined || a.ownsReport).map(async a => {
319
+ export async function resolveCoverageSourceOptions(adapters: readonly CoverageAdapter[], ctx: CoverageSourceContext, fallback?: CoverageSourceOptions) {
320
+ return Promise.all(adapters.filter(a => a.sourceOptions !== undefined || a.defaultExcludeSources !== undefined || a.ownsReport).map(async a => {
315
321
  if (!a.ownsReport) throw new Error(`coverage adapter "${a.name}" needs ownsReport with sourceOptions`);
316
- const options = typeof a.sourceOptions === "function" ? await a.sourceOptions(ctx) : a.sourceOptions;
322
+ let options = typeof a.sourceOptions === "function" ? await a.sourceOptions(ctx) : a.sourceOptions;
317
323
  if (a.sourceOptions !== undefined) validateCoverageSourceOptions(ctx.service, options);
324
+ if (a.defaultExcludeSources !== undefined) {
325
+ validateCoverageSourceOptions(ctx.service, { excludeSources: a.defaultExcludeSources });
326
+ // Defaults must not discard the source root/exclusions of older configs.
327
+ // Explicit adapter options still replace that service-level fallback.
328
+ options ??= fallback;
329
+ if (options !== undefined) validateCoverageSourceOptions(ctx.service, options);
330
+ options = { ...options, excludeSources: [...new Set([...a.defaultExcludeSources, ...(options?.excludeSources ?? [])])] };
331
+ }
318
332
  return { adapter: a.name, ownsReport: a.ownsReport, options };
319
333
  }));
320
334
  }
@@ -333,7 +347,7 @@ export function attachCoverageSourceOptions<T extends { reports: { name: string;
333
347
  function withWireName(a: CoverageAdapter): CoverageAdapter {
334
348
  if (Object.prototype.hasOwnProperty.call(a, "toJSON")) return a;
335
349
  return Object.assign(Object.create(Object.getPrototypeOf(a) as object | null), a, {
336
- toJSON: () => ({ adapter: a.name, ...(a.sourceOptions && typeof a.sourceOptions !== "function" ? { sourceOptions: a.sourceOptions } : {}) }),
350
+ toJSON: () => ({ adapter: a.name, ...(a.defaultExcludeSources ? { defaultExcludeSources: a.defaultExcludeSources } : {}), ...(a.sourceOptions && typeof a.sourceOptions !== "function" ? { sourceOptions: a.sourceOptions } : {}) }),
337
351
  }) as CoverageAdapter;
338
352
  }
339
353
 
@@ -909,6 +923,7 @@ export function node(options: NodeCoverageOptions = {}): CoverageAdapter {
909
923
  }
910
924
  return {
911
925
  name: "node",
926
+ defaultExcludeSources: JAVASCRIPT_GENERATED_SOURCES,
912
927
  ...(options.sourceRoot !== undefined || options.excludeSources !== undefined ? { sourceOptions: { sourceRoot: options.sourceRoot, excludeSources: options.excludeSources } } : {}),
913
928
  ownsReport: name => isNodeDumpName(name) || name === NODE_COVERAGE_EMPTY_REPORT,
914
929
  reports: "delta",
@@ -1121,6 +1136,7 @@ export async function collectNodeReports(dir: string): Promise<NodeReportStats>
1121
1136
  export function browser(options: CoverageSourceOptions = {}): CoverageAdapter {
1122
1137
  return {
1123
1138
  name: "browser",
1139
+ defaultExcludeSources: JAVASCRIPT_GENERATED_SOURCES,
1124
1140
  ...(options.sourceRoot !== undefined || options.excludeSources !== undefined ? { sourceOptions: options } : {}),
1125
1141
  ownsReport: name => name === "browser.lcov",
1126
1142
  reports: "delta",
package/src/daemon.ts CHANGED
@@ -3894,7 +3894,10 @@ async function captureServiceCoverage(baseline = false): Promise<ServiceCoverage
3894
3894
  try { return await fs.readFile(resolveProjectPath(p, { record: false }), "utf8"); }
3895
3895
  catch { return null; }
3896
3896
  },
3897
- });
3897
+ }, typeof svc.coverage === "object" ? {
3898
+ sourceRoot: svc.coverage.sourceRoot,
3899
+ excludeSources: svc.coverage.excludeSources,
3900
+ } : undefined);
3898
3901
  } catch (err) {
3899
3902
  return { service: svc.name, reports: [], error: `coverage source metadata: ${err instanceof Error ? err.message : String(err)}` };
3900
3903
  }