@cplieger/deadset-ts 4.0.1 → 5.1.0

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.
Files changed (138) hide show
  1. package/README.md +4 -4
  2. package/dist/bin/component-mapper.js +5 -0
  3. package/dist/bin/mapper-connection.js +101 -0
  4. package/dist/bin/node-host.js +24 -2
  5. package/dist/src/analysis.js +301 -41
  6. package/dist/src/component-files.js +612 -0
  7. package/dist/src/component-layer.js +132 -0
  8. package/dist/src/config.js +9 -2
  9. package/dist/src/configuration-files.js +290 -0
  10. package/dist/src/conformance-record.js +3 -9
  11. package/dist/src/contextual-uses.js +240 -0
  12. package/dist/src/convention-rows.js +389 -0
  13. package/dist/src/conventions.js +565 -0
  14. package/dist/src/dependencies.js +36 -3
  15. package/dist/src/destructuring.js +6 -6
  16. package/dist/src/discover.js +10 -3
  17. package/dist/src/emit-map.js +72 -0
  18. package/dist/src/entry-point-gaps.js +81 -35
  19. package/dist/src/entry-points.js +50 -32
  20. package/dist/src/exempt-classes.js +6 -1
  21. package/dist/src/exempt.js +32 -20
  22. package/dist/src/findings/completion.js +46 -32
  23. package/dist/src/findings/emitters.js +22 -1
  24. package/dist/src/findings/interfaces.js +6 -2
  25. package/dist/src/findings/reads-and-writes.js +53 -9
  26. package/dist/src/findings/unused-declarations.js +47 -3
  27. package/dist/src/generated-file.js +24 -0
  28. package/dist/src/graph.js +13 -5
  29. package/dist/src/interface-satisfaction.js +2 -68
  30. package/dist/src/inventory.js +8 -3
  31. package/dist/src/kinds.js +6 -5
  32. package/dist/src/manifest.js +37 -11
  33. package/dist/src/matrix.js +21 -13
  34. package/dist/src/partial.js +3 -1
  35. package/dist/src/pnpm-workspace.js +120 -0
  36. package/dist/src/position.js +17 -2
  37. package/dist/src/print.js +2 -0
  38. package/dist/src/query.js +15 -0
  39. package/dist/src/references.js +404 -36
  40. package/dist/src/report.js +26 -6
  41. package/dist/src/reporters/annotations.js +10 -3
  42. package/dist/src/reporters/template.js +3 -0
  43. package/dist/src/resolve.js +37 -11
  44. package/dist/src/roots.js +37 -8
  45. package/dist/src/run.js +13 -3
  46. package/dist/src/schema.js +2 -0
  47. package/dist/src/session.js +141 -34
  48. package/dist/src/setup-failure.js +23 -0
  49. package/dist/src/source-files.js +4 -61
  50. package/dist/src/stores.js +3 -0
  51. package/dist/src/sweep.js +4 -2
  52. package/dist/src/template-field.js +38 -6
  53. package/dist/src/test-support.js +59 -0
  54. package/dist/src/type-errors.js +252 -0
  55. package/dist/src/type-query-alias.js +92 -0
  56. package/dist/src/value-flow.js +74 -0
  57. package/dist/src/verbs/analyze.js +24 -4
  58. package/dist/src/verbs/explain.js +4 -1
  59. package/dist/src/verbs/print-projects.js +17 -56
  60. package/dist/src/verbs/print-retained.js +7 -2
  61. package/dist/src/verbs/print-roots.js +7 -2
  62. package/dist/src/version-range.js +255 -0
  63. package/dist/src/version.js +1 -1
  64. package/dist/src/workspace-manifests.js +68 -0
  65. package/dist/src/workspace-programs.js +342 -0
  66. package/dist/src/workspace-resolution.js +307 -0
  67. package/dist/src/workspace-run.js +75 -0
  68. package/dist/src/workspace.js +358 -0
  69. package/jsr.json +1 -1
  70. package/package.json +3 -2
  71. package/src/analysis.ts +475 -72
  72. package/src/component-files.ts +718 -0
  73. package/src/component-layer.ts +160 -0
  74. package/src/config.ts +11 -2
  75. package/src/configuration-files.ts +370 -0
  76. package/src/conformance-record.ts +3 -10
  77. package/src/contextual-uses.ts +354 -0
  78. package/src/convention-rows.ts +448 -0
  79. package/src/conventions.ts +788 -0
  80. package/src/dependencies.ts +49 -2
  81. package/src/destructuring.ts +16 -6
  82. package/src/discover.ts +10 -6
  83. package/src/emit-map.ts +93 -0
  84. package/src/entry-point-gaps.ts +92 -38
  85. package/src/entry-points.ts +79 -36
  86. package/src/exempt-classes.ts +6 -1
  87. package/src/exempt.ts +69 -20
  88. package/src/file-facts.ts +2 -0
  89. package/src/findings/completion.ts +55 -36
  90. package/src/findings/emitters.ts +32 -1
  91. package/src/findings/interfaces.ts +9 -2
  92. package/src/findings/reads-and-writes.ts +70 -9
  93. package/src/findings/unused-declarations.ts +60 -3
  94. package/src/generated-file.ts +27 -0
  95. package/src/graph.ts +15 -4
  96. package/src/host.ts +23 -0
  97. package/src/index.ts +1 -1
  98. package/src/interface-satisfaction.ts +1 -92
  99. package/src/inventory.ts +17 -3
  100. package/src/kinds.ts +7 -5
  101. package/src/manifest.ts +47 -14
  102. package/src/matrix.ts +43 -15
  103. package/src/partial.ts +6 -1
  104. package/src/pnpm-workspace.ts +135 -0
  105. package/src/position.ts +23 -2
  106. package/src/print.ts +2 -0
  107. package/src/query.ts +26 -0
  108. package/src/references.ts +472 -40
  109. package/src/report.ts +56 -6
  110. package/src/reporters/annotations.ts +12 -3
  111. package/src/reporters/template.ts +3 -0
  112. package/src/resolve.ts +65 -11
  113. package/src/roots.ts +70 -9
  114. package/src/run.ts +15 -5
  115. package/src/schema.ts +2 -0
  116. package/src/session.ts +218 -37
  117. package/src/setup-failure.ts +39 -0
  118. package/src/source-files.ts +4 -66
  119. package/src/stores.ts +9 -0
  120. package/src/sweep.ts +9 -2
  121. package/src/template-field.ts +42 -7
  122. package/src/test-support.ts +77 -0
  123. package/src/type-errors.ts +340 -0
  124. package/src/type-query-alias.ts +133 -0
  125. package/src/value-flow.ts +104 -0
  126. package/src/verbs/analyze.ts +38 -4
  127. package/src/verbs/explain.ts +4 -1
  128. package/src/verbs/print-projects.ts +17 -69
  129. package/src/verbs/print-retained.ts +7 -2
  130. package/src/verbs/print-roots.ts +7 -2
  131. package/src/verbs/verb.ts +6 -3
  132. package/src/version-range.ts +309 -0
  133. package/src/version.ts +1 -1
  134. package/src/workspace-manifests.ts +100 -0
  135. package/src/workspace-programs.ts +486 -0
  136. package/src/workspace-resolution.ts +393 -0
  137. package/src/workspace-run.ts +148 -0
  138. package/src/workspace.ts +463 -0
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  deadset-ts finds dead code in TypeScript projects, down to class members, type members and dead stores inside functions, and writes a report your CI can fail on.
6
6
 
7
- It reads your `tsconfig.json` projects, `allowJs` JavaScript included, with the type information of TypeScript 7.1.0-dev.20261003.1, its one dependency. Your project keeps its own TypeScript version and must also compile under that version. deadset-ts reports and never edits your code. It is pre-release, tested only on Linux, needs Node.js 24 or later and is licensed under GPL-3.0-or-later.
7
+ It reads your `tsconfig.json` projects, `allowJs` JavaScript and Vue, Svelte and Astro scripts included, with the type information of TypeScript 7.1.0-dev.20261003.1, its one dependency. Your project keeps its own TypeScript version. A type error under that version skips the function or statement that holds it. deadset-ts reports and never edits your code. It is pre-release, tested only on Linux, needs Node.js 24 or later and is licensed under GPL-3.0-or-later.
8
8
 
9
9
  ## Why use it
10
10
 
@@ -17,7 +17,7 @@ deadset-ts is built for a CI gate on dead code. Each finding names code to delet
17
17
  - The same tree gives the same report, and a baseline keyed on code and symbol survives moved lines.
18
18
  - It writes text, JSON, GitHub annotations, SARIF 2.1.0 or a custom template.
19
19
 
20
- Consider [Knip](https://knip.dev) if you want issues fixed with `--fix`, plugins for over 100 tools, or Astro, MDX, Svelte and Vue files checked.
20
+ Consider [Knip](https://knip.dev) if you want issues fixed with `--fix`, plugins for over 100 tools, or MDX files checked.
21
21
 
22
22
  ## Install
23
23
 
@@ -90,7 +90,7 @@ A TypeScript caller that imports the source sets `allowImportingTsExtensions`, b
90
90
 
91
91
  ## Related projects
92
92
 
93
- deadset-ts implements the [deadset Contract](https://github.com/cplieger/deadset-spec), which fixes the issue codes, the report schema and the exit codes. It passes the Contract's conformance corpus except one reflective-lookup fixture, the gap [`conformance.json`](conformance.json) declares.
93
+ deadset-ts implements the [deadset Contract](https://github.com/cplieger/deadset-spec), which fixes the issue codes, the report schema and the exit codes. It passes every fixture of the Contract's conformance corpus except one reflective-lookup fixture, the gap [`conformance.json`](conformance.json) declares.
94
94
 
95
95
  - [deadset-go](https://github.com/cplieger/deadset-go) is the same analysis for Go modules.
96
96
  - [deadset](https://github.com/cplieger/deadset) runs both analyzers as one command and merges their reports, resolving the edges between Go and TypeScript code.
@@ -105,7 +105,7 @@ deadset-ts implements the [deadset Contract](https://github.com/cplieger/deadset
105
105
 
106
106
  ## Contributing
107
107
 
108
- See [CONTRIBUTING.md](https://github.com/cplieger/.github/blob/main/CONTRIBUTING.md).
108
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
109
109
 
110
110
  ## Disclaimer
111
111
 
@@ -0,0 +1,5 @@
1
+ import { mapperConnection } from "./mapper-connection.js";
2
+ const connection = mapperConnection((bytes) => process.stdout.write(bytes));
3
+ process.stdin.on("data", (chunk) => {
4
+ connection.receive(chunk);
5
+ });
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The content mapper the compiler runs for component files: JSON-RPC 2.0 messages, each
3
+ * framed by a `Content-Length` header, over the process's standard streams. It answers
4
+ * `transform` with {@link componentModule} and keeps no state between requests.
5
+ * The protocol is the compiler's own:
6
+ * https://github.com/microsoft/TypeScript/blob/f9f8d01292562242b6e7c7142e46ed7e470926f9/tsc/internal/contentmapper/hostimpl.go
7
+ */
8
+ import { componentModule } from "../src/component-files.js";
9
+ /** The JSON-RPC code of a method this mapper does not serve. */
10
+ const METHOD_NOT_FOUND = -32601;
11
+ /** The JSON-RPC code of a request whose parameters are not the protocol's. */
12
+ const INVALID_PARAMS = -32602;
13
+ const HEADER_END = new Uint8Array([0x0d, 0x0a, 0x0d, 0x0a]);
14
+ /** The offset of `needle` in `bytes` at or after `from`, or -1. */
15
+ function indexOf(bytes, needle, from) {
16
+ outer: for (let at = from; at + needle.length <= bytes.length; at += 1) {
17
+ for (let index = 0; index < needle.length; index += 1) {
18
+ if (bytes[at + index] !== needle[index]) {
19
+ continue outer;
20
+ }
21
+ }
22
+ return at;
23
+ }
24
+ return -1;
25
+ }
26
+ /** The answer to one message, or undefined for a notification, which takes none. */
27
+ function answer(message) {
28
+ if (message.id === undefined) {
29
+ return undefined;
30
+ }
31
+ const reply = (result) => ({ jsonrpc: "2.0", id: message.id, result });
32
+ const fail = (code, text) => ({
33
+ jsonrpc: "2.0",
34
+ id: message.id,
35
+ error: { code, message: text },
36
+ });
37
+ switch (message.method) {
38
+ case "initialize":
39
+ return reply({ positionEncoding: "utf-16", diagnosticSource: "deadset-ts" });
40
+ case "openProject":
41
+ return reply({});
42
+ case "closeProject":
43
+ return reply(null);
44
+ case "transform": {
45
+ const content = typeof message.params === "object" && message.params !== null
46
+ ? message.params.content
47
+ : undefined;
48
+ if (typeof content !== "string") {
49
+ return fail(INVALID_PARAMS, "transform: params.content is not a string");
50
+ }
51
+ const module = componentModule(content);
52
+ return reply({ text: module.text, extension: module.extension, mappings: module.mappings });
53
+ }
54
+ default:
55
+ return fail(METHOD_NOT_FOUND, `no method ${JSON.stringify(message.method)}`);
56
+ }
57
+ }
58
+ /**
59
+ * A connection over one byte stream. A message whose body is not JSON is answered with
60
+ * nothing, because it carries no id to answer to.
61
+ */
62
+ export function mapperConnection(write) {
63
+ const encoder = new TextEncoder();
64
+ const decoder = new TextDecoder();
65
+ let pending = new Uint8Array(0);
66
+ return {
67
+ receive(chunk) {
68
+ const joined = new Uint8Array(pending.length + chunk.length);
69
+ joined.set(pending);
70
+ joined.set(chunk, pending.length);
71
+ pending = joined;
72
+ for (;;) {
73
+ const headerEnd = indexOf(pending, HEADER_END, 0);
74
+ if (headerEnd < 0) {
75
+ return;
76
+ }
77
+ const header = decoder.decode(pending.subarray(0, headerEnd));
78
+ const length = Number(/content-length:\s*(\d+)/iu.exec(header)?.[1] ?? "0");
79
+ const bodyStart = headerEnd + HEADER_END.length;
80
+ if (pending.length < bodyStart + length) {
81
+ return;
82
+ }
83
+ const body = decoder.decode(pending.subarray(bodyStart, bodyStart + length));
84
+ pending = pending.slice(bodyStart + length);
85
+ let message;
86
+ try {
87
+ message = JSON.parse(body);
88
+ }
89
+ catch {
90
+ continue;
91
+ }
92
+ const reply = answer(message);
93
+ if (reply !== undefined) {
94
+ const bytes = encoder.encode(JSON.stringify(reply));
95
+ write(encoder.encode(`Content-Length: ${String(bytes.length)}\r\n\r\n`));
96
+ write(bytes);
97
+ }
98
+ }
99
+ },
100
+ };
101
+ }
@@ -1,6 +1,7 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { closeSync, fchmodSync, fsyncSync, openSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeSync, } from "node:fs";
3
- import { basename, dirname, join } from "node:path";
2
+ import { closeSync, fchmodSync, fsyncSync, mkdtempSync, openSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeSync, } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { basename, dirname, extname, join } from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
  /** The name the manifest of this package declares. */
6
7
  const PACKAGE_NAME = "@cplieger/deadset-ts";
@@ -75,6 +76,24 @@ function writeAtomically(path, pieces) {
75
76
  throw error;
76
77
  }
77
78
  }
79
+ /** A new directory under the platform's temporary directory. */
80
+ function temporaryDirectory() {
81
+ const path = mkdtempSync(join(tmpdir(), "deadset-ts-mapper-"));
82
+ return {
83
+ path,
84
+ remove: () => {
85
+ rmSync(path, { recursive: true, force: true });
86
+ },
87
+ };
88
+ }
89
+ /**
90
+ * The mapper beside this file, in the same form: the emitted tree runs the emitted
91
+ * mapper and a checkout runs the source through the runtime's own type stripping.
92
+ */
93
+ function componentMapperCommand() {
94
+ const here = fileURLToPath(import.meta.url);
95
+ return [process.execPath, join(dirname(here), `component-mapper${extname(here)}`)];
96
+ }
78
97
  /**
79
98
  * The filesystem of the platform the command runs on, and the version of the
80
99
  * package it was installed from.
@@ -98,10 +117,13 @@ export function nodeHost() {
98
117
  }
99
118
  return stat.isDirectory() ? "directory" : "file";
100
119
  },
120
+ realPath: (path) => realpathSync(path),
101
121
  analyzerVersion: () => {
102
122
  found ??= readVersion();
103
123
  return found;
104
124
  },
105
125
  writeDocument: writeAtomically,
126
+ temporaryDirectory,
127
+ componentMapperCommand,
106
128
  };
107
129
  }