@cplieger/deadset-ts 1.1.0 → 2.0.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.
@@ -0,0 +1,101 @@
1
+ import { API } from "@typescript/native/unstable/sync";
2
+ import { isIdentifier } from "@typescript/native/unstable/ast";
3
+ /**
4
+ * Opens the compiler client the analysis reads through.
5
+ *
6
+ * Closing twice releases once: a run that fails before its snapshot opens closes
7
+ * the client where it failed, and the outer release runs whatever happened, so the
8
+ * second call has nothing left to do.
9
+ */
10
+ export function openEngine(options) {
11
+ const api = new API({ collectTiming: options.collectTiming });
12
+ let closed = false;
13
+ return {
14
+ parseConfigFile: (file) => api.parseConfigFile(file),
15
+ updateSnapshot: (openProjects) => api.updateSnapshot({ openProjects: [...openProjects] }),
16
+ getTimingInfo: () => api.getTimingInfo(),
17
+ close: () => {
18
+ if (closed) {
19
+ return;
20
+ }
21
+ closed = true;
22
+ api.close();
23
+ },
24
+ };
25
+ }
26
+ function viewOf(project) {
27
+ // The snapshot is never updated, so a project's own file set is the same answer
28
+ // every time it is asked for. It is read once because the question is not free:
29
+ // deciding it consults the metadata of every file the program holds, the compiler's
30
+ // own library files included, and a run asks for the set once per pass.
31
+ let ownFiles;
32
+ return {
33
+ configFile: project.configFileName,
34
+ program: project.program,
35
+ checker: project.checker,
36
+ ownSourceFiles: () => {
37
+ ownFiles ??= project.program
38
+ .getSourceFileNames()
39
+ // The metadata decides which files belong to the target, and it is read by
40
+ // name. Fetching each file first and filtering afterwards costs one round
41
+ // trip per library file the answer then discards, and a program holds far
42
+ // more library files than target files.
43
+ .filter((name) => {
44
+ const metadata = project.program.getSourceFileMetadata(name);
45
+ return (metadata !== undefined && !metadata.isDefaultLibrary && !metadata.isFromExternalLibrary);
46
+ })
47
+ .map((name) => project.program.getSourceFile(name))
48
+ .filter((file) => file !== undefined);
49
+ return ownFiles;
50
+ },
51
+ handle: (node) => ({ node }),
52
+ symbolsAt: (handles) => project.checker.getSymbolAtLocation(handles.map(({ node }) => node)),
53
+ resolvedSymbolAt: ({ node }) => isIdentifier(node) ? project.checker.getResolvedSymbol(node) : undefined,
54
+ shorthandValueAt: ({ node }) => project.checker.getShorthandAssignmentValueSymbol(node),
55
+ aliasStepOf: (symbol) => {
56
+ const held = project.checker.getImmediateAliasedSymbol(symbol);
57
+ // An alias that resolves to nothing answers with the checker's own unknown
58
+ // symbol, which declares nothing; a symbol that is no alias answers with
59
+ // nothing at all.
60
+ return held === undefined || held.declarations.length === 0 ? undefined : held;
61
+ },
62
+ declarationAt: (handle) => {
63
+ const node = handle.resolve(project);
64
+ return node === undefined ? undefined : { node };
65
+ },
66
+ };
67
+ }
68
+ /** The three diagnostic sets one project carries, read before any analysis. */
69
+ export function diagnosticsOf(project) {
70
+ return {
71
+ configFile: project.configFile,
72
+ syntactic: project.program.getSyntacticDiagnostics(),
73
+ semantic: project.program.getSemanticDiagnostics(),
74
+ configParsing: project.program.getConfigFileParsingDiagnostics(),
75
+ };
76
+ }
77
+ /**
78
+ * Runs `visit` over every project the compiler configurations name, inside one
79
+ * snapshot of one client.
80
+ *
81
+ * The snapshot is opened once with every configuration, so each becomes a project
82
+ * with its own program and checker; it is never updated, because the analysis
83
+ * reads and the tree does not change under it. It is disposed once when the last
84
+ * project has been visited, and the client is released after it, whether the visit
85
+ * completed or threw.
86
+ */
87
+ export function runSession(engine, configFiles, visit) {
88
+ try {
89
+ const snapshot = engine.updateSnapshot(configFiles);
90
+ try {
91
+ const projects = snapshot.getProjects().map((project) => visit(viewOf(project)));
92
+ return { projects, timing: engine.getTimingInfo() };
93
+ }
94
+ finally {
95
+ snapshot.dispose();
96
+ }
97
+ }
98
+ finally {
99
+ engine.close();
100
+ }
101
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The Contract version this analyzer implements. It is the resolved value of
3
+ * `contract_version` when no configuration source supplies one, and it moves
4
+ * independently of the analyzer's own version, which is the one the host answers
5
+ * from the package the analyzer was installed from.
6
+ */
7
+ export const CONTRACT_VERSION = "1.7.0";
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "license": "GPL-3.0-or-later",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
package/package.json CHANGED
@@ -1,20 +1,25 @@
1
1
  {
2
2
  "name": "@cplieger/deadset-ts",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Deterministic dead-code analysis for TypeScript, class and type members included",
7
7
  "license": "GPL-3.0-or-later",
8
8
  "bin": {
9
- "deadset-ts": "./bin/deadset-ts.ts"
9
+ "deadset-ts": "./dist/bin/deadset-ts.js"
10
10
  },
11
11
  "exports": {
12
- ".": "./src/index.ts"
12
+ ".": {
13
+ "types": "./src/index.ts",
14
+ "default": "./dist/src/index.js"
15
+ }
13
16
  },
14
17
  "engines": {
15
18
  "node": ">=24"
16
19
  },
17
20
  "scripts": {
21
+ "build": "tsc -p tsconfig.build.json",
22
+ "prepack": "npm run build",
18
23
  "typecheck": "tsc -p tsconfig.json",
19
24
  "typecheck:tests": "tsc -p tsconfig.test.json",
20
25
  "test": "vitest --run",
@@ -29,6 +34,7 @@
29
34
  "@eslint/js": "10.0.1",
30
35
  "@types/node": "24.13.5",
31
36
  "eslint": "10.10.0",
37
+ "fast-check": "4.10.1",
32
38
  "prettier": "3.9.7",
33
39
  "typescript": "npm:@typescript/typescript6@6.0.2",
34
40
  "typescript-eslint": "8.70.0",
@@ -57,7 +63,7 @@
57
63
  "files": [
58
64
  "LICENSE",
59
65
  "NOTICE",
60
- "bin/",
66
+ "dist/",
61
67
  "src/",
62
68
  "!src/**/*.test.ts",
63
69
  "jsr.json"
package/src/host.ts CHANGED
@@ -8,8 +8,9 @@ export interface DirectoryEntry {
8
8
  }
9
9
 
10
10
  /**
11
- * The filesystem the analysis reads, and the directory it reads relative paths
12
- * against.
11
+ * The platform one run reads: the filesystem, the directory relative paths
12
+ * resolve against, and the version of the package this analyzer was installed
13
+ * from.
13
14
  *
14
15
  * It is a parameter rather than an import because a published module that reads
15
16
  * the filesystem directly carries the whole platform's type surface into every
@@ -26,4 +27,11 @@ export interface Host {
26
27
  readDirectory(path: string): readonly DirectoryEntry[];
27
28
  /** What one path names, `"absent"` where nothing is there. */
28
29
  kindOf(path: string): PathKind;
30
+ /**
31
+ * The version of the package this analyzer was installed from, or a throw
32
+ * naming why it cannot be known. It is a method rather than a value because
33
+ * locating that package is a filesystem walk, and a host that cannot do it
34
+ * says so at the one moment the value is asked for.
35
+ */
36
+ analyzerVersion(): string;
29
37
  }
package/src/index.ts CHANGED
@@ -1,3 +1,41 @@
1
1
  export { run, SETTING_OPTIONS, type Writer } from "./run.ts";
2
2
  export type { DirectoryEntry, Host, PathKind } from "./host.ts";
3
- export { CONTRACT_VERSION, version } from "./version.ts";
3
+ export {
4
+ inventory,
5
+ nodeKey,
6
+ type Inventory,
7
+ type InventoryCost,
8
+ type InventorySymbol,
9
+ type SymbolKind,
10
+ type Visibility,
11
+ } from "./inventory.ts";
12
+ export {
13
+ DEFAULT_BATCH_CAP,
14
+ references,
15
+ type Reference,
16
+ type ReferenceCost,
17
+ type ReferenceOptions,
18
+ type References,
19
+ type Resolution,
20
+ type TestFileRule,
21
+ type Use,
22
+ } from "./references.ts";
23
+ export {
24
+ byPosition,
25
+ positionKey,
26
+ PositionError,
27
+ renderPosition,
28
+ type Position,
29
+ } from "./position.ts";
30
+ export {
31
+ computedComponent,
32
+ isRef,
33
+ nameComponent,
34
+ REF_EXPRESSIONS,
35
+ renderRef,
36
+ type Component,
37
+ type DependencySection,
38
+ type Fragment,
39
+ type Module,
40
+ } from "./ref.ts";
41
+ export { CONTRACT_VERSION } from "./version.ts";