fallow-type-aware 3.14.0 → 3.16.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/fallow-type-aware.mjs +3 -1
- package/package.json +1 -1
- package/src/backend-preflight.mjs +42 -0
- package/src/semantic-identity.mjs +25 -2
- package/src/semantic.mjs +2 -1
package/fallow-type-aware.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { assertTypescriptBackendResolvable } from "./src/backend-preflight.mjs";
|
|
4
4
|
|
|
5
5
|
try {
|
|
6
|
+
assertTypescriptBackendResolvable();
|
|
7
|
+
const { run } = await import("./src/cli.mjs");
|
|
6
8
|
await run({ input: process.stdin, output: process.stdout, args: process.argv.slice(2) });
|
|
7
9
|
} catch (error) {
|
|
8
10
|
const message = error instanceof Error ? error.message : String(error);
|
package/package.json
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { BACKEND_VERSION } from "./generated-protocol.mjs";
|
|
5
|
+
|
|
6
|
+
const REQUIRED_MAJOR = Number.parseInt(BACKEND_VERSION, 10);
|
|
7
|
+
const INSTALL_HINT =
|
|
8
|
+
"run `npm ci` in tools/type-aware-sidecar so the sidecar resolves its own typescript install";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Verify that module resolution hands the sidecar a typescript install that
|
|
12
|
+
* provides the `typescript/unstable/sync` backend entry point.
|
|
13
|
+
*
|
|
14
|
+
* The semantic modules import that entry point statically, so an incompatible
|
|
15
|
+
* resolution (for example a typescript 6 hoisted into an ancestor
|
|
16
|
+
* `node_modules`) fails at link time with a bare module path instead of the
|
|
17
|
+
* version conflict that caused it. This preflight runs before those imports
|
|
18
|
+
* and names the resolved version, its location, and the fix. Exact
|
|
19
|
+
* backend-version parity stays enforced in `protocol.mjs`.
|
|
20
|
+
*
|
|
21
|
+
* @param {string | URL} resolveFrom Module URL or file path to resolve from;
|
|
22
|
+
* defaults to this module so the check mirrors the semantic imports.
|
|
23
|
+
*/
|
|
24
|
+
export const assertTypescriptBackendResolvable = (resolveFrom = import.meta.url) => {
|
|
25
|
+
const sidecarRequire = createRequire(resolveFrom);
|
|
26
|
+
let manifestPath;
|
|
27
|
+
try {
|
|
28
|
+
manifestPath = sidecarRequire.resolve("typescript/package.json");
|
|
29
|
+
} catch {
|
|
30
|
+
throw new Error(`typescript is not installed; ${INSTALL_HINT}`);
|
|
31
|
+
}
|
|
32
|
+
const { version } = sidecarRequire(manifestPath);
|
|
33
|
+
const major = Number.parseInt(version, 10);
|
|
34
|
+
if (Number.isFinite(major) && major >= REQUIRED_MAJOR) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
throw new Error(
|
|
38
|
+
`resolved typescript ${version} from ${path.dirname(manifestPath)}, which lacks the ` +
|
|
39
|
+
`typescript/unstable/sync backend; fallow-type-aware needs typescript ${BACKEND_VERSION}; ` +
|
|
40
|
+
INSTALL_HINT,
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -12,9 +12,11 @@ import {
|
|
|
12
12
|
isMethodDeclaration,
|
|
13
13
|
isMethodSignatureDeclaration,
|
|
14
14
|
isModuleDeclaration,
|
|
15
|
+
isNamespaceExport,
|
|
15
16
|
isPropertyDeclaration,
|
|
16
17
|
isPropertySignatureDeclaration,
|
|
17
18
|
isSetAccessorDeclaration,
|
|
19
|
+
isSourceFile,
|
|
18
20
|
isTypeAliasDeclaration,
|
|
19
21
|
isVariableDeclaration,
|
|
20
22
|
} from "typescript/unstable/ast/is";
|
|
@@ -45,7 +47,12 @@ const TYPE_ONLY_DECLARATIONS = [
|
|
|
45
47
|
isTypeAliasDeclaration,
|
|
46
48
|
isPropertySignatureDeclaration,
|
|
47
49
|
];
|
|
48
|
-
const DUAL_NAMESPACE_DECLARATIONS = [
|
|
50
|
+
const DUAL_NAMESPACE_DECLARATIONS = [
|
|
51
|
+
isClassDeclaration,
|
|
52
|
+
isClassExpression,
|
|
53
|
+
isEnumDeclaration,
|
|
54
|
+
isSourceFile,
|
|
55
|
+
];
|
|
49
56
|
const DECLARATION_OWNER_NODES = [isClassDeclaration, isClassExpression, isInterfaceDeclaration];
|
|
50
57
|
|
|
51
58
|
const slash = (value) => value.split(path.sep).join("/");
|
|
@@ -101,6 +108,8 @@ export const ownerDeclaration = (node) => {
|
|
|
101
108
|
|
|
102
109
|
export const isDeclaration = (node) => declarationKind(node) !== undefined;
|
|
103
110
|
|
|
111
|
+
export const isExportAnchor = (node) => isExportSpecifier(node) || isNamespaceExport(node);
|
|
112
|
+
|
|
104
113
|
const visit = (node, callback) => {
|
|
105
114
|
callback(node);
|
|
106
115
|
node.forEachChild((child) => {
|
|
@@ -151,6 +160,16 @@ const indexExportSpecifier = (index, node) => {
|
|
|
151
160
|
);
|
|
152
161
|
};
|
|
153
162
|
|
|
163
|
+
const indexNamespaceExport = (index, node) => {
|
|
164
|
+
const exportedName = nodeText(node.name);
|
|
165
|
+
if (!exportedName) return;
|
|
166
|
+
[node.parent, node]
|
|
167
|
+
.flatMap((candidate) => positions(candidate))
|
|
168
|
+
.forEach((position) =>
|
|
169
|
+
indexExportPosition(index, node, { localName: exportedName, exportedName }, position),
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
154
173
|
const defaultModifierFor = (node) =>
|
|
155
174
|
node.modifiers?.find((modifier) => modifier.getText(node.getSourceFile()) === "default");
|
|
156
175
|
|
|
@@ -197,6 +216,10 @@ const declarationIndex = (sourceFile) => {
|
|
|
197
216
|
indexExportSpecifier(index, node);
|
|
198
217
|
return;
|
|
199
218
|
}
|
|
219
|
+
if (isNamespaceExport(node)) {
|
|
220
|
+
indexNamespaceExport(index, node);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
200
223
|
indexDeclarationNode(index, node);
|
|
201
224
|
});
|
|
202
225
|
declarationIndexes.set(sourceFile, index);
|
|
@@ -205,7 +228,7 @@ const declarationIndex = (sourceFile) => {
|
|
|
205
228
|
|
|
206
229
|
export const findDeclaration = (sourceFile, identity) => {
|
|
207
230
|
const anchor = declarationIndex(sourceFile).get(anchorKey(identity));
|
|
208
|
-
if (!anchor ||
|
|
231
|
+
if (!anchor || isExportAnchor(anchor)) return anchor;
|
|
209
232
|
return declarationNamespaces(anchor).has(identity.namespace) ? anchor : undefined;
|
|
210
233
|
};
|
|
211
234
|
|
package/src/semantic.mjs
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
declarationsForSymbol,
|
|
36
36
|
findDeclaration,
|
|
37
37
|
isDeclaration,
|
|
38
|
+
isExportAnchor,
|
|
38
39
|
isProjectSource,
|
|
39
40
|
nodeText,
|
|
40
41
|
ownerDeclaration,
|
|
@@ -735,7 +736,7 @@ const selectSymbolContext = (
|
|
|
735
736
|
|
|
736
737
|
const resolvedAnchorTarget = (project, anchor, requestedSymbol) => {
|
|
737
738
|
if (!anchor) return undefined;
|
|
738
|
-
if (!
|
|
739
|
+
if (!isExportAnchor(anchor)) {
|
|
739
740
|
return declarationNamespaces(anchor).has(requestedSymbol.namespace)
|
|
740
741
|
? { declaration: anchor, namespace: requestedSymbol.namespace }
|
|
741
742
|
: undefined;
|