@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.
package/src/session.ts CHANGED
@@ -3,14 +3,15 @@ import type {
3
3
  Checker,
4
4
  Diagnostic,
5
5
  DocumentIdentifier,
6
+ NodeHandle,
6
7
  Project,
7
8
  Program,
8
9
  Snapshot,
9
10
  Symbol as TSSymbol,
10
11
  TimingInfo,
11
12
  } from "@typescript/native/unstable/sync";
12
- import { SyntaxKind } from "@typescript/native/unstable/ast";
13
- import type { Identifier, Node, SourceFile } from "@typescript/native/unstable/ast";
13
+ import { isIdentifier } from "@typescript/native/unstable/ast";
14
+ import type { Node, SourceFile } from "@typescript/native/unstable/ast";
14
15
 
15
16
  /**
16
17
  * The compiler the analysis reads through. It is a client over a protocol to a
@@ -101,6 +102,36 @@ export interface ProjectView<Brand> {
101
102
  * resolve.
102
103
  */
103
104
  symbolsAt(handles: readonly Handle<Brand>[]): readonly (TSSymbol | undefined)[];
105
+ /**
106
+ * The symbol one node resolves to, asked for that node alone. The accessor behind
107
+ * it takes one identifier and offers no array overload, so it is one round trip per
108
+ * node and it answers only for the residue a batch left unresolved. A node that is
109
+ * not an identifier has no such answer.
110
+ */
111
+ resolvedSymbolAt(handle: Handle<Brand>): TSSymbol | undefined;
112
+ /**
113
+ * The symbol the value of one shorthand property assignment names, which is the
114
+ * declaration `{ a }` reads; the symbol the property's own name resolves to is the
115
+ * property the literal declares. The handle names the assignment, not its name.
116
+ */
117
+ shorthandValueAt(handle: Handle<Brand>): TSSymbol | undefined;
118
+ /**
119
+ * The symbol one alias names, one link along: an import or export specifier
120
+ * resolves to the declaration it carries forward, or to the next alias of a chain.
121
+ * `undefined` where the symbol is no alias, or where the alias resolves to nothing.
122
+ */
123
+ aliasStepOf(symbol: TSSymbol): TSSymbol | undefined;
124
+ /**
125
+ * The node one symbol's declaration handle names, read in this project's program,
126
+ * or `undefined` where this project's program does not hold that file.
127
+ *
128
+ * A symbol is shared by every project of the snapshot and remembers the project it
129
+ * was first seen in, so resolving its handle without saying which program to read
130
+ * would answer out of whichever project reached the symbol first. The project is
131
+ * named here instead, and the answer becomes a handle of this project like any
132
+ * other. The read is local once the file has been fetched.
133
+ */
134
+ declarationAt(handle: NodeHandle): Handle<Brand> | undefined;
104
135
  }
105
136
 
106
137
  /**
@@ -110,30 +141,18 @@ export interface ProjectView<Brand> {
110
141
  */
111
142
  export type ProjectVisitor<Result> = <Brand>(project: ProjectView<Brand>) => Result;
112
143
 
113
- /**
114
- * The identifiers of one source file, in source order. A file is the unit a batch
115
- * resolves: the walk is local to this process and the resolution is not, so one
116
- * file's identifiers are gathered here and handed to the checker together.
117
- */
118
- export function identifiersOf(file: SourceFile): Identifier[] {
119
- const found: Identifier[] = [];
120
- const visit = (node: Node): void => {
121
- if (node.kind === SyntaxKind.Identifier) {
122
- found.push(node as Identifier);
123
- }
124
- node.forEachChild(visit);
125
- };
126
- file.forEachChild(visit);
127
- return found;
128
- }
129
-
130
144
  function viewOf<Brand>(project: Project): ProjectView<Brand> {
145
+ // The snapshot is never updated, so a project's own file set is the same answer
146
+ // every time it is asked for. It is read once because the question is not free:
147
+ // deciding it consults the metadata of every file the program holds, the compiler's
148
+ // own library files included, and a run asks for the set once per pass.
149
+ let ownFiles: readonly SourceFile[] | undefined;
131
150
  return {
132
151
  configFile: project.configFileName,
133
152
  program: project.program,
134
153
  checker: project.checker,
135
- ownSourceFiles: () =>
136
- project.program
154
+ ownSourceFiles: () => {
155
+ ownFiles ??= project.program
137
156
  .getSourceFileNames()
138
157
  // The metadata decides which files belong to the target, and it is read by
139
158
  // name. Fetching each file first and filtering afterwards costs one round
@@ -146,9 +165,25 @@ function viewOf<Brand>(project: Project): ProjectView<Brand> {
146
165
  );
147
166
  })
148
167
  .map((name) => project.program.getSourceFile(name))
149
- .filter((file): file is SourceFile => file !== undefined),
168
+ .filter((file): file is SourceFile => file !== undefined);
169
+ return ownFiles;
170
+ },
150
171
  handle: (node) => ({ node }),
151
172
  symbolsAt: (handles) => project.checker.getSymbolAtLocation(handles.map(({ node }) => node)),
173
+ resolvedSymbolAt: ({ node }) =>
174
+ isIdentifier(node) ? project.checker.getResolvedSymbol(node) : undefined,
175
+ shorthandValueAt: ({ node }) => project.checker.getShorthandAssignmentValueSymbol(node),
176
+ aliasStepOf: (symbol) => {
177
+ const held = project.checker.getImmediateAliasedSymbol(symbol);
178
+ // An alias that resolves to nothing answers with the checker's own unknown
179
+ // symbol, which declares nothing; a symbol that is no alias answers with
180
+ // nothing at all.
181
+ return held === undefined || held.declarations.length === 0 ? undefined : held;
182
+ },
183
+ declarationAt: (handle) => {
184
+ const node = handle.resolve(project);
185
+ return node === undefined ? undefined : { node };
186
+ },
152
187
  };
153
188
  }
154
189
 
package/src/version.ts CHANGED
@@ -1,22 +1,7 @@
1
- import manifest from "../package.json" with { type: "json" };
2
-
3
1
  /**
4
2
  * The Contract version this analyzer implements. It is the resolved value of
5
3
  * `contract_version` when no configuration source supplies one, and it moves
6
- * independently of {@link version}.
4
+ * independently of the analyzer's own version, which is the one the host answers
5
+ * from the package the analyzer was installed from.
7
6
  */
8
- export const CONTRACT_VERSION = "1.5.0";
9
-
10
- /**
11
- * This analyzer's own version, which the published package carries rather than
12
- * the source: the release pipeline writes the released version into the manifest
13
- * of the artifact it publishes, so an installed package reports the release it
14
- * came from while a checkout reports the version the manifest holds in git.
15
- *
16
- * It is one function because a version written in more than one place is two
17
- * facts that can disagree: the version verb, the report envelope and anything
18
- * else that names this analyzer read this one.
19
- */
20
- export function version(): string {
21
- return manifest.version;
22
- }
7
+ export const CONTRACT_VERSION = "1.7.0";
package/bin/node-host.ts DELETED
@@ -1,28 +0,0 @@
1
- import { readdirSync, readFileSync, statSync } from "node:fs";
2
- import type { DirectoryEntry, Host, PathKind } from "../src/host.ts";
3
-
4
- /**
5
- * The filesystem of the platform the command runs on.
6
- *
7
- * It is the one module that names the platform: everything the analysis is made of
8
- * takes the filesystem as a parameter, so a consumer compiling the published
9
- * sources needs the compiler's types and nothing else.
10
- */
11
- export function nodeHost(): Host {
12
- return {
13
- workingDirectory: () => process.cwd(),
14
- readFile: (path) => readFileSync(path, "utf8"),
15
- readDirectory: (path): readonly DirectoryEntry[] =>
16
- readdirSync(path, { withFileTypes: true }).map((entry) => ({
17
- name: entry.name,
18
- directory: entry.isDirectory(),
19
- })),
20
- kindOf: (path): PathKind => {
21
- const stat = statSync(path, { throwIfNoEntry: false });
22
- if (stat === undefined) {
23
- return "absent";
24
- }
25
- return stat.isDirectory() ? "directory" : "file";
26
- },
27
- };
28
- }