@cplieger/deadset-ts 5.2.0 → 5.2.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.
@@ -31,6 +31,7 @@ const APP_DIRECTORY_ENTRIES = [
31
31
  ];
32
32
  const SOLIDSTART_PLUGIN = { module: "@solidjs/start/config", export: "solidStart" };
33
33
  const NUXT_CONFIG = [`nuxt.config.${ANY_MODULE}`];
34
+ const STRYKER_CONFIG = "{stryker,.stryker}.{conf,config}.{json,js,mjs,cjs}";
34
35
  /** Every row, ordered by name and then by range. */
35
36
  export const CONVENTION_ROWS = [
36
37
  {
@@ -258,6 +259,25 @@ export const CONVENTION_ROWS = [
258
259
  ],
259
260
  moves: [],
260
261
  },
262
+ {
263
+ name: "stryker",
264
+ package: "@stryker-mutator/core",
265
+ range: ">=7.0.0 <11.0.0",
266
+ entries: [],
267
+ moves: [],
268
+ shortNames: [
269
+ {
270
+ property: "testRunner",
271
+ files: [STRYKER_CONFIG],
272
+ package: "@stryker-mutator/{}-runner",
273
+ },
274
+ {
275
+ property: "checkers",
276
+ files: [STRYKER_CONFIG],
277
+ package: "@stryker-mutator/{}-checker",
278
+ },
279
+ ],
280
+ },
261
281
  {
262
282
  name: "sveltekit",
263
283
  package: "@sveltejs/kit",
@@ -11,6 +11,7 @@ import { CONVENTION_ROWS, } from "./convention-rows.js";
11
11
  import { globExpression } from "./glob.js";
12
12
  import { dirnamePath, joinPath, normalizePath, relativePath, resolvePath } from "./paths.js";
13
13
  import { satisfies } from "./version-range.js";
14
+ const JSON_EXTENSION = ".json";
14
15
  /** The sections an installed package is required by, so its absence fails the run. */
15
16
  const REQUIRED_SECTIONS = ["dependencies", "devDependencies"];
16
17
  const DEPENDENCY_SECTIONS = [
@@ -114,8 +115,14 @@ function isModuleExports(node) {
114
115
  node.expression.text === "module" &&
115
116
  node.name.text === "exports");
116
117
  }
117
- /** Every expression or declaration one file exports as its default, in any module syntax. */
118
+ /**
119
+ * Every expression or declaration one file exports as its default, in any module syntax.
120
+ * A JSON module's default export is its document's value.
121
+ */
118
122
  function defaultExports(file) {
123
+ if (file.fileName.endsWith(JSON_EXTENSION)) {
124
+ return file.statements.flatMap((statement) => isExpressionStatement(statement) ? [statement.expression] : []);
125
+ }
119
126
  const found = [];
120
127
  for (const statement of file.statements) {
121
128
  if (isExportAssignment(statement)) {
@@ -215,6 +215,12 @@ function nameNodeOf(node) {
215
215
  export function nodeKey(file, node) {
216
216
  return `${file.fileName}:${String(node.pos)}:${String(node.end)}:${String(node.kind)}`;
217
217
  }
218
+ /** Whether `node` is written inside `declaration`, compared by file and range. */
219
+ function writtenInside(declaration, node) {
220
+ return (node.getSourceFile().fileName === declaration.getSourceFile().fileName &&
221
+ declaration.pos <= node.pos &&
222
+ node.end <= declaration.end);
223
+ }
218
224
  /** `export default`, up to the keyword that stands where an unnamed default export's name would. */
219
225
  const DEFAULT_EXPORT = /^export\s+(?=default\b)/u;
220
226
  /**
@@ -360,6 +366,7 @@ export function inventory(project, host, targetRoot) {
360
366
  });
361
367
  containers.push({
362
368
  owner: record,
369
+ declaration: node,
363
370
  nameNode: node.name,
364
371
  members: node.members,
365
372
  memberKind: "class-member",
@@ -379,6 +386,7 @@ export function inventory(project, host, targetRoot) {
379
386
  });
380
387
  containers.push({
381
388
  owner: record,
389
+ declaration: node,
382
390
  nameNode: node.name,
383
391
  members: node.members,
384
392
  memberKind: "interface-method",
@@ -402,6 +410,7 @@ export function inventory(project, host, targetRoot) {
402
410
  if (members.length > 0) {
403
411
  containers.push({
404
412
  owner: record,
413
+ declaration: node,
405
414
  nameNode: undefined,
406
415
  members,
407
416
  memberKind: "type-member",
@@ -422,6 +431,7 @@ export function inventory(project, host, targetRoot) {
422
431
  });
423
432
  containers.push({
424
433
  owner: record,
434
+ declaration: node,
425
435
  nameNode: node.name,
426
436
  members: node.members,
427
437
  memberKind: "enum-member",
@@ -604,7 +614,9 @@ export function inventory(project, host, targetRoot) {
604
614
  outside += declarations.length;
605
615
  continue;
606
616
  }
607
- for (const held of own) {
617
+ // A merged symbol's table holds the members every one of its declarations writes,
618
+ // and each member is kept once, by the declaration that writes it.
619
+ for (const held of own.filter((one) => writtenInside(container.declaration, one.node))) {
608
620
  keepMember(container, held.node, seen, areStatic);
609
621
  }
610
622
  }
@@ -19,7 +19,7 @@ export function partialNotes(partial) {
19
19
  byProject.set(configFile, (byProject.get(configFile) ?? 0) + 1);
20
20
  }
21
21
  const where = [...byProject].map(([configFile, n]) => `${configFile}: ${String(n)}`).join(", ");
22
- notes.push(`the checker answered ${counted(partial.unanswered.length, "question", "questions")} with a failure (${where}); every declaration an answer could have kept live is kept live and reported by nothing`);
22
+ notes.push(`the checker answered ${counted(partial.unanswered.length, "question", "questions")} with a failure (${where}), so every declaration an answer could have kept live is kept live and reported by nothing`);
23
23
  }
24
24
  return notes;
25
25
  }
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "5.2.0",
3
+ "version": "5.2.2",
4
4
  "license": "GPL-3.0-or-later",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "5.2.0",
3
+ "version": "5.2.2",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Deterministic dead-code analysis for TypeScript, class and type members included",
@@ -34,7 +34,7 @@
34
34
  "devDependencies": {
35
35
  "@eslint/js": "10.0.1",
36
36
  "@types/node": "24.19.1",
37
- "eslint": "10.11.0",
37
+ "eslint": "10.12.0",
38
38
  "fast-check": "4.10.2",
39
39
  "prettier": "3.9.9",
40
40
  "typescript": "npm:@typescript/typescript6@6.0.2",
@@ -98,6 +98,8 @@ const SOLIDSTART_PLUGIN = { module: "@solidjs/start/config", export: "solidStart
98
98
 
99
99
  const NUXT_CONFIG = [`nuxt.config.${ANY_MODULE}`];
100
100
 
101
+ const STRYKER_CONFIG = "{stryker,.stryker}.{conf,config}.{json,js,mjs,cjs}";
102
+
101
103
  /** Every row, ordered by name and then by range. */
102
104
  export const CONVENTION_ROWS: readonly ConventionRow[] = [
103
105
  {
@@ -325,6 +327,25 @@ export const CONVENTION_ROWS: readonly ConventionRow[] = [
325
327
  ],
326
328
  moves: [],
327
329
  },
330
+ {
331
+ name: "stryker",
332
+ package: "@stryker-mutator/core",
333
+ range: ">=7.0.0 <11.0.0",
334
+ entries: [],
335
+ moves: [],
336
+ shortNames: [
337
+ {
338
+ property: "testRunner",
339
+ files: [STRYKER_CONFIG],
340
+ package: "@stryker-mutator/{}-runner",
341
+ },
342
+ {
343
+ property: "checkers",
344
+ files: [STRYKER_CONFIG],
345
+ package: "@stryker-mutator/{}-checker",
346
+ },
347
+ ],
348
+ },
328
349
  {
329
350
  name: "sveltekit",
330
351
  package: "@sveltejs/kit",
@@ -122,6 +122,8 @@ interface ConventionEntry {
122
122
  /** Parses one file's text with no program. */
123
123
  type ParseSource = (fileName: string, text: string) => SourceFile;
124
124
 
125
+ const JSON_EXTENSION = ".json";
126
+
125
127
  /** The sections an installed package is required by, so its absence fails the run. */
126
128
  const REQUIRED_SECTIONS = ["dependencies", "devDependencies"] as const;
127
129
 
@@ -248,8 +250,16 @@ function isModuleExports(node: Node): boolean {
248
250
  );
249
251
  }
250
252
 
251
- /** Every expression or declaration one file exports as its default, in any module syntax. */
253
+ /**
254
+ * Every expression or declaration one file exports as its default, in any module syntax.
255
+ * A JSON module's default export is its document's value.
256
+ */
252
257
  function defaultExports(file: SourceFile): readonly Node[] {
258
+ if (file.fileName.endsWith(JSON_EXTENSION)) {
259
+ return file.statements.flatMap((statement) =>
260
+ isExpressionStatement(statement) ? [statement.expression] : [],
261
+ );
262
+ }
253
263
  const found: Node[] = [];
254
264
  for (const statement of file.statements) {
255
265
  if (isExportAssignment(statement)) {
package/src/inventory.ts CHANGED
@@ -208,6 +208,8 @@ interface Building {
208
208
  /** A container whose members are read from a symbol table, from the tree, or both. */
209
209
  interface Container {
210
210
  readonly owner: Building;
211
+ /** The declaration that writes the container, which owns the members written inside it. */
212
+ readonly declaration: Node;
211
213
  /** The container's name node, which is what a batch resolves to its symbol. */
212
214
  readonly nameNode: Node | undefined;
213
215
  readonly members: readonly Node[];
@@ -473,6 +475,15 @@ export function nodeKey(file: SourceFile, node: Node): string {
473
475
  return `${file.fileName}:${String(node.pos)}:${String(node.end)}:${String(node.kind)}`;
474
476
  }
475
477
 
478
+ /** Whether `node` is written inside `declaration`, compared by file and range. */
479
+ function writtenInside(declaration: Node, node: Node): boolean {
480
+ return (
481
+ node.getSourceFile().fileName === declaration.getSourceFile().fileName &&
482
+ declaration.pos <= node.pos &&
483
+ node.end <= declaration.end
484
+ );
485
+ }
486
+
476
487
  /** `export default`, up to the keyword that stands where an unnamed default export's name would. */
477
488
  const DEFAULT_EXPORT = /^export\s+(?=default\b)/u;
478
489
 
@@ -642,6 +653,7 @@ export function inventory<Brand>(
642
653
  });
643
654
  containers.push({
644
655
  owner: record,
656
+ declaration: node,
645
657
  nameNode: node.name,
646
658
  members: node.members,
647
659
  memberKind: "class-member",
@@ -661,6 +673,7 @@ export function inventory<Brand>(
661
673
  });
662
674
  containers.push({
663
675
  owner: record,
676
+ declaration: node,
664
677
  nameNode: node.name,
665
678
  members: node.members,
666
679
  memberKind: "interface-method",
@@ -684,6 +697,7 @@ export function inventory<Brand>(
684
697
  if (members.length > 0) {
685
698
  containers.push({
686
699
  owner: record,
700
+ declaration: node,
687
701
  nameNode: undefined,
688
702
  members,
689
703
  memberKind: "type-member",
@@ -704,6 +718,7 @@ export function inventory<Brand>(
704
718
  });
705
719
  containers.push({
706
720
  owner: record,
721
+ declaration: node,
707
722
  nameNode: node.name,
708
723
  members: node.members,
709
724
  memberKind: "enum-member",
@@ -897,7 +912,9 @@ export function inventory<Brand>(
897
912
  outside += declarations.length;
898
913
  continue;
899
914
  }
900
- for (const held of own) {
915
+ // A merged symbol's table holds the members every one of its declarations writes,
916
+ // and each member is kept once, by the declaration that writes it.
917
+ for (const held of own.filter((one) => writtenInside(container.declaration, one.node))) {
901
918
  keepMember(container, held.node, seen, areStatic);
902
919
  }
903
920
  }
package/src/partial.ts CHANGED
@@ -38,7 +38,7 @@ export function partialNotes(partial: Partial): readonly string[] {
38
38
  }
39
39
  const where = [...byProject].map(([configFile, n]) => `${configFile}: ${String(n)}`).join(", ");
40
40
  notes.push(
41
- `the checker answered ${counted(partial.unanswered.length, "question", "questions")} with a failure (${where}); every declaration an answer could have kept live is kept live and reported by nothing`,
41
+ `the checker answered ${counted(partial.unanswered.length, "question", "questions")} with a failure (${where}), so every declaration an answer could have kept live is kept live and reported by nothing`,
42
42
  );
43
43
  }
44
44
  return notes;