rooklang 0.0.0-stage → 0.3.1
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/LICENSE +21 -0
- package/README.md +177 -2
- package/README.ru.md +178 -0
- package/dist/ast/declarations.d.ts +185 -0
- package/dist/ast/declarations.js +1 -0
- package/dist/ast/expressions.d.ts +201 -0
- package/dist/ast/expressions.js +1 -0
- package/dist/ast/markup.d.ts +141 -0
- package/dist/ast/markup.js +1 -0
- package/dist/ast/statements.d.ts +173 -0
- package/dist/ast/statements.js +1 -0
- package/dist/ast.d.ts +18 -0
- package/dist/ast.js +18 -0
- package/dist/attrs.d.ts +8 -0
- package/dist/attrs.js +23 -0
- package/dist/browser.d.ts +30 -0
- package/dist/browser.js +36 -0
- package/dist/build.d.ts +48 -0
- package/dist/build.js +112 -0
- package/dist/checker/assignments.d.ts +14 -0
- package/dist/checker/assignments.js +197 -0
- package/dist/checker/base.d.ts +124 -0
- package/dist/checker/base.js +180 -0
- package/dist/checker/bigint.d.ts +15 -0
- package/dist/checker/bigint.js +85 -0
- package/dist/checker/builtins.d.ts +9 -0
- package/dist/checker/builtins.js +307 -0
- package/dist/checker/bytes.d.ts +11 -0
- package/dist/checker/bytes.js +72 -0
- package/dist/checker/calls.d.ts +23 -0
- package/dist/checker/calls.js +210 -0
- package/dist/checker/channel.d.ts +14 -0
- package/dist/checker/channel.js +44 -0
- package/dist/checker/channels.d.ts +9 -0
- package/dist/checker/channels.js +95 -0
- package/dist/checker/checker.d.ts +4 -0
- package/dist/checker/checker.js +22 -0
- package/dist/checker/classes.d.ts +24 -0
- package/dist/checker/classes.js +223 -0
- package/dist/checker/completion-names.d.ts +11 -0
- package/dist/checker/completion-names.js +100 -0
- package/dist/checker/completion.d.ts +25 -0
- package/dist/checker/completion.js +222 -0
- package/dist/checker/component-values.d.ts +11 -0
- package/dist/checker/component-values.js +88 -0
- package/dist/checker/components.d.ts +37 -0
- package/dist/checker/components.js +287 -0
- package/dist/checker/context.d.ts +196 -0
- package/dist/checker/context.js +35 -0
- package/dist/checker/control-flow.d.ts +24 -0
- package/dist/checker/control-flow.js +237 -0
- package/dist/checker/declarations.d.ts +26 -0
- package/dist/checker/declarations.js +259 -0
- package/dist/checker/decorator-exports.d.ts +5 -0
- package/dist/checker/decorator-exports.js +40 -0
- package/dist/checker/decorator-members.d.ts +28 -0
- package/dist/checker/decorator-members.js +140 -0
- package/dist/checker/decorator-wrappers.d.ts +20 -0
- package/dist/checker/decorator-wrappers.js +132 -0
- package/dist/checker/decorators.d.ts +28 -0
- package/dist/checker/decorators.js +265 -0
- package/dist/checker/dom-svg.d.ts +7 -0
- package/dist/checker/dom-svg.js +19 -0
- package/dist/checker/dom.d.ts +22 -0
- package/dist/checker/dom.js +361 -0
- package/dist/checker/expressions.d.ts +32 -0
- package/dist/checker/expressions.js +244 -0
- package/dist/checker/flow.d.ts +18 -0
- package/dist/checker/flow.js +134 -0
- package/dist/checker/forwarding.d.ts +15 -0
- package/dist/checker/forwarding.js +78 -0
- package/dist/checker/functions.d.ts +24 -0
- package/dist/checker/functions.js +154 -0
- package/dist/checker/go-for.d.ts +12 -0
- package/dist/checker/go-for.js +93 -0
- package/dist/checker/goroutines.d.ts +19 -0
- package/dist/checker/goroutines.js +110 -0
- package/dist/checker/helpers.d.ts +35 -0
- package/dist/checker/helpers.js +162 -0
- package/dist/checker/imports.d.ts +7 -0
- package/dist/checker/imports.js +83 -0
- package/dist/checker/initializers.d.ts +22 -0
- package/dist/checker/initializers.js +104 -0
- package/dist/checker/iterables.d.ts +5 -0
- package/dist/checker/iterables.js +17 -0
- package/dist/checker/iterators.d.ts +13 -0
- package/dist/checker/iterators.js +95 -0
- package/dist/checker/loops.d.ts +10 -0
- package/dist/checker/loops.js +82 -0
- package/dist/checker/markup.d.ts +28 -0
- package/dist/checker/markup.js +220 -0
- package/dist/checker/members.d.ts +29 -0
- package/dist/checker/members.js +233 -0
- package/dist/checker/operators.d.ts +13 -0
- package/dist/checker/operators.js +177 -0
- package/dist/checker/promise.d.ts +17 -0
- package/dist/checker/promise.js +49 -0
- package/dist/checker/refs.d.ts +26 -0
- package/dist/checker/refs.js +136 -0
- package/dist/checker/resolution.d.ts +23 -0
- package/dist/checker/resolution.js +173 -0
- package/dist/checker/statements.d.ts +28 -0
- package/dist/checker/statements.js +212 -0
- package/dist/checker/styles.d.ts +14 -0
- package/dist/checker/styles.js +49 -0
- package/dist/checker/transfer.d.ts +5 -0
- package/dist/checker/transfer.js +83 -0
- package/dist/checker/types/bytes.d.ts +4 -0
- package/dist/checker/types/bytes.js +51 -0
- package/dist/checker/types/classes.d.ts +22 -0
- package/dist/checker/types/classes.js +93 -0
- package/dist/checker/types/constructors.d.ts +27 -0
- package/dist/checker/types/constructors.js +129 -0
- package/dist/checker/types/generics.d.ts +20 -0
- package/dist/checker/types/generics.js +75 -0
- package/dist/checker/types/lazy.d.ts +30 -0
- package/dist/checker/types/lazy.js +92 -0
- package/dist/checker/types/model.d.ts +149 -0
- package/dist/checker/types/model.js +15 -0
- package/dist/checker/types/params.d.ts +7 -0
- package/dist/checker/types/params.js +220 -0
- package/dist/checker/types/printing.d.ts +6 -0
- package/dist/checker/types/printing.js +109 -0
- package/dist/checker/types/relations.d.ts +16 -0
- package/dist/checker/types/relations.js +294 -0
- package/dist/checker/types.d.ts +9 -0
- package/dist/checker/types.js +10 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +195 -0
- package/dist/codegen/analysis.d.ts +18 -0
- package/dist/codegen/analysis.js +144 -0
- package/dist/codegen/attributes.d.ts +40 -0
- package/dist/codegen/attributes.js +227 -0
- package/dist/codegen/calls.d.ts +22 -0
- package/dist/codegen/calls.js +106 -0
- package/dist/codegen/chan-helpers.d.ts +2 -0
- package/dist/codegen/chan-helpers.js +253 -0
- package/dist/codegen/component-helpers.d.ts +33 -0
- package/dist/codegen/component-helpers.js +18 -0
- package/dist/codegen/component-props.d.ts +42 -0
- package/dist/codegen/component-props.js +173 -0
- package/dist/codegen/component-values.d.ts +27 -0
- package/dist/codegen/component-values.js +146 -0
- package/dist/codegen/components.d.ts +53 -0
- package/dist/codegen/components.js +268 -0
- package/dist/codegen/control-flow.d.ts +25 -0
- package/dist/codegen/control-flow.js +152 -0
- package/dist/codegen/control-statements.d.ts +28 -0
- package/dist/codegen/control-statements.js +226 -0
- package/dist/codegen/creation.d.ts +31 -0
- package/dist/codegen/creation.js +113 -0
- package/dist/codegen/early-returns.d.ts +59 -0
- package/dist/codegen/early-returns.js +244 -0
- package/dist/codegen/element-helpers.d.ts +12 -0
- package/dist/codegen/element-helpers.js +54 -0
- package/dist/codegen/elements.d.ts +71 -0
- package/dist/codegen/elements.js +293 -0
- package/dist/codegen/emitter.d.ts +135 -0
- package/dist/codegen/emitter.js +296 -0
- package/dist/codegen/expressions.d.ts +49 -0
- package/dist/codegen/expressions.js +262 -0
- package/dist/codegen/functions.d.ts +28 -0
- package/dist/codegen/functions.js +170 -0
- package/dist/codegen/go-helpers.d.ts +9 -0
- package/dist/codegen/go-helpers.js +307 -0
- package/dist/codegen/helpers.d.ts +10 -0
- package/dist/codegen/helpers.js +356 -0
- package/dist/codegen/imported.d.ts +38 -0
- package/dist/codegen/imported.js +71 -0
- package/dist/codegen/js.d.ts +20 -0
- package/dist/codegen/js.js +113 -0
- package/dist/codegen/mutations.d.ts +10 -0
- package/dist/codegen/mutations.js +102 -0
- package/dist/codegen/parallel.d.ts +7 -0
- package/dist/codegen/parallel.js +63 -0
- package/dist/codegen/reactive.d.ts +95 -0
- package/dist/codegen/reactive.js +280 -0
- package/dist/codegen/refs.d.ts +62 -0
- package/dist/codegen/refs.js +160 -0
- package/dist/codegen/select-helpers.d.ts +2 -0
- package/dist/codegen/select-helpers.js +135 -0
- package/dist/codegen/simple-statements.d.ts +42 -0
- package/dist/codegen/simple-statements.js +253 -0
- package/dist/codegen/statements.d.ts +22 -0
- package/dist/codegen/statements.js +142 -0
- package/dist/codegen/styles.d.ts +54 -0
- package/dist/codegen/styles.js +157 -0
- package/dist/codegen/syntax.d.ts +18 -0
- package/dist/codegen/syntax.js +70 -0
- package/dist/codegen/thread-helpers.d.ts +2 -0
- package/dist/codegen/thread-helpers.js +198 -0
- package/dist/codegen/updates.d.ts +51 -0
- package/dist/codegen/updates.js +201 -0
- package/dist/codegen/web-components.d.ts +20 -0
- package/dist/codegen/web-components.js +149 -0
- package/dist/compile-file.d.ts +11 -0
- package/dist/compile-file.js +24 -0
- package/dist/compile.d.ts +53 -0
- package/dist/compile.js +187 -0
- package/dist/completion.d.ts +10 -0
- package/dist/completion.js +134 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +44 -0
- package/dist/css/hash.d.ts +1 -0
- package/dist/css/hash.js +10 -0
- package/dist/css/parse.d.ts +42 -0
- package/dist/css/parse.js +138 -0
- package/dist/css/print.d.ts +9 -0
- package/dist/css/print.js +35 -0
- package/dist/css/selectors.d.ts +29 -0
- package/dist/css/selectors.js +168 -0
- package/dist/declarations.d.ts +3 -0
- package/dist/declarations.js +207 -0
- package/dist/decorator-copy.d.ts +18 -0
- package/dist/decorator-copy.js +158 -0
- package/dist/decorators.d.ts +61 -0
- package/dist/decorators.js +241 -0
- package/dist/diagnostics.d.ts +17 -0
- package/dist/diagnostics.js +22 -0
- package/dist/go-for.d.ts +26 -0
- package/dist/go-for.js +155 -0
- package/dist/goroutines.d.ts +37 -0
- package/dist/goroutines.js +253 -0
- package/dist/html-tag.d.ts +21 -0
- package/dist/html-tag.js +103 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +55 -0
- package/dist/inline-packages.d.ts +32 -0
- package/dist/inline-packages.js +129 -0
- package/dist/iterators.d.ts +10 -0
- package/dist/iterators.js +33 -0
- package/dist/lexer/base.d.ts +24 -0
- package/dist/lexer/base.js +87 -0
- package/dist/lexer/lexer.d.ts +8 -0
- package/dist/lexer/lexer.js +134 -0
- package/dist/lexer/literals.d.ts +18 -0
- package/dist/lexer/literals.js +203 -0
- package/dist/lexer/markup.d.ts +10 -0
- package/dist/lexer/markup.js +93 -0
- package/dist/lexer/style.d.ts +17 -0
- package/dist/lexer/style.js +97 -0
- package/dist/lexer/tables.d.ts +53 -0
- package/dist/lexer/tables.js +101 -0
- package/dist/lexer/token.d.ts +42 -0
- package/dist/lexer/token.js +108 -0
- package/dist/loader.d.ts +1 -0
- package/dist/loader.js +50 -0
- package/dist/methods.d.ts +5 -0
- package/dist/methods.js +81 -0
- package/dist/modules.d.ts +3 -0
- package/dist/modules.js +49 -0
- package/dist/names.d.ts +23 -0
- package/dist/names.js +267 -0
- package/dist/parser/base.d.ts +96 -0
- package/dist/parser/base.js +202 -0
- package/dist/parser/classes.d.ts +8 -0
- package/dist/parser/classes.js +164 -0
- package/dist/parser/control-flow.d.ts +29 -0
- package/dist/parser/control-flow.js +232 -0
- package/dist/parser/declarations.d.ts +27 -0
- package/dist/parser/declarations.js +238 -0
- package/dist/parser/decorators.d.ts +30 -0
- package/dist/parser/decorators.js +176 -0
- package/dist/parser/expressions.d.ts +15 -0
- package/dist/parser/expressions.js +255 -0
- package/dist/parser/literals.d.ts +27 -0
- package/dist/parser/literals.js +206 -0
- package/dist/parser/markup.d.ts +22 -0
- package/dist/parser/markup.js +193 -0
- package/dist/parser/parser.d.ts +14 -0
- package/dist/parser/parser.js +22 -0
- package/dist/parser/simple-statements.d.ts +22 -0
- package/dist/parser/simple-statements.js +156 -0
- package/dist/parser/statements.d.ts +37 -0
- package/dist/parser/statements.js +267 -0
- package/dist/parser/syntax.d.ts +51 -0
- package/dist/parser/syntax.js +173 -0
- package/dist/parser/types.d.ts +23 -0
- package/dist/parser/types.js +218 -0
- package/dist/recursion.d.ts +25 -0
- package/dist/recursion.js +170 -0
- package/dist/refs.d.ts +49 -0
- package/dist/refs.js +210 -0
- package/dist/run.d.ts +1 -0
- package/dist/run.js +21 -0
- package/dist/source.d.ts +15 -0
- package/dist/source.js +34 -0
- package/dist/style-modules.d.ts +17 -0
- package/dist/style-modules.js +49 -0
- package/dist/styles.d.ts +36 -0
- package/dist/styles.js +160 -0
- package/dist/svg.d.ts +8 -0
- package/dist/svg.js +77 -0
- package/dist/ts-types.d.ts +11 -0
- package/dist/ts-types.js +133 -0
- package/dist/typed-arrays.d.ts +8 -0
- package/dist/typed-arrays.js +43 -0
- package/dist/typescript/convert-base.d.ts +42 -0
- package/dist/typescript/convert-base.js +135 -0
- package/dist/typescript/convert-objects.d.ts +28 -0
- package/dist/typescript/convert-objects.js +245 -0
- package/dist/typescript/convert.d.ts +14 -0
- package/dist/typescript/convert.js +67 -0
- package/dist/typescript/importer.d.ts +37 -0
- package/dist/typescript/importer.js +152 -0
- package/dist/typescript/library.d.ts +23 -0
- package/dist/typescript/library.js +81 -0
- package/dist/vite.d.ts +11 -0
- package/dist/vite.js +86 -0
- package/dist/walk-types.d.ts +4 -0
- package/dist/walk-types.js +56 -0
- package/dist/walk.d.ts +5 -0
- package/dist/walk.js +315 -0
- package/package.json +105 -4
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { ANY, BOOL, literal, NEVER, NULL, nullable, NUMBER, BIGINT, STRING, union, VOID, } from "../checker/types.js";
|
|
2
|
+
// The converter is a chain of layers: base (this one: primitives, unions, type parameters) → objects
|
|
3
|
+
// (interfaces, classes, signatures) → TypeConverter (convert.ts: the exports of modules).
|
|
4
|
+
export class ConverterBase {
|
|
5
|
+
ts;
|
|
6
|
+
program;
|
|
7
|
+
checker;
|
|
8
|
+
types = new Map();
|
|
9
|
+
/** Types being converted: a function type that refers to itself becomes `any` inside. */
|
|
10
|
+
converting = new Set();
|
|
11
|
+
params = new Map();
|
|
12
|
+
classes = new Map();
|
|
13
|
+
shared;
|
|
14
|
+
arrayInterface;
|
|
15
|
+
constructor(ts, program, shared = new Map()) {
|
|
16
|
+
this.ts = ts;
|
|
17
|
+
this.program = program;
|
|
18
|
+
this.checker = program.getTypeChecker();
|
|
19
|
+
this.shared = shared;
|
|
20
|
+
}
|
|
21
|
+
/** The key of a declaration that does not change between programs, or `null`. */
|
|
22
|
+
sharedKey(symbol, kind) {
|
|
23
|
+
const declarations = symbol.declarations ?? [];
|
|
24
|
+
if (declarations.length === 0)
|
|
25
|
+
return null;
|
|
26
|
+
const stable = declarations.every((declaration) => {
|
|
27
|
+
const file = declaration.getSourceFile();
|
|
28
|
+
return (this.program.isSourceFileDefaultLibrary(file) ||
|
|
29
|
+
this.program.isSourceFileFromExternalLibrary(file));
|
|
30
|
+
});
|
|
31
|
+
if (!stable)
|
|
32
|
+
return null;
|
|
33
|
+
const file = declarations[0].getSourceFile().fileName;
|
|
34
|
+
return `${kind} ${file} ${this.checker.getFullyQualifiedName(symbol)}`;
|
|
35
|
+
}
|
|
36
|
+
convert(type) {
|
|
37
|
+
const known = this.types.get(type);
|
|
38
|
+
if (known)
|
|
39
|
+
return known;
|
|
40
|
+
if (this.converting.has(type))
|
|
41
|
+
return ANY;
|
|
42
|
+
this.converting.add(type);
|
|
43
|
+
try {
|
|
44
|
+
const converted = this.convertType(type);
|
|
45
|
+
this.types.set(type, converted);
|
|
46
|
+
return converted;
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
this.converting.delete(type);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
convertType(type) {
|
|
53
|
+
const { TypeFlags } = this.ts;
|
|
54
|
+
const { flags } = type;
|
|
55
|
+
if (flags & (TypeFlags.Any | TypeFlags.Unknown))
|
|
56
|
+
return ANY;
|
|
57
|
+
// `boolean` is the union `true | false` in TypeScript.
|
|
58
|
+
if (flags & TypeFlags.Boolean)
|
|
59
|
+
return BOOL;
|
|
60
|
+
if (flags & TypeFlags.String)
|
|
61
|
+
return STRING;
|
|
62
|
+
if (flags & TypeFlags.Number)
|
|
63
|
+
return NUMBER;
|
|
64
|
+
if (flags & (TypeFlags.BigInt | TypeFlags.BigIntLiteral))
|
|
65
|
+
return BIGINT;
|
|
66
|
+
if (flags & TypeFlags.StringLiteral)
|
|
67
|
+
return literal(type.value);
|
|
68
|
+
if (flags & TypeFlags.NumberLiteral)
|
|
69
|
+
return literal(type.value);
|
|
70
|
+
if (flags & TypeFlags.BooleanLiteral)
|
|
71
|
+
return literal(this.checker.typeToString(type) === 'true');
|
|
72
|
+
if (flags & (TypeFlags.TemplateLiteral | TypeFlags.StringMapping))
|
|
73
|
+
return STRING;
|
|
74
|
+
if (flags & (TypeFlags.Void | TypeFlags.Undefined))
|
|
75
|
+
return VOID;
|
|
76
|
+
if (flags & TypeFlags.Null)
|
|
77
|
+
return NULL;
|
|
78
|
+
if (flags & TypeFlags.Never)
|
|
79
|
+
return NEVER;
|
|
80
|
+
if (flags & TypeFlags.TypeParameter)
|
|
81
|
+
return this.typeParamOrThis(type);
|
|
82
|
+
if (flags & TypeFlags.Union)
|
|
83
|
+
return this.union(type.types);
|
|
84
|
+
if (flags & TypeFlags.Intersection)
|
|
85
|
+
return this.object(type, null);
|
|
86
|
+
if (flags & TypeFlags.Object)
|
|
87
|
+
return this.objectType(type);
|
|
88
|
+
return ANY;
|
|
89
|
+
}
|
|
90
|
+
/** `undefined`, `null` and `void` in a union make it nullable: `string | undefined` is `?string`. */
|
|
91
|
+
union(types) {
|
|
92
|
+
const { TypeFlags } = this.ts;
|
|
93
|
+
const empty = (type) => (type.flags & (TypeFlags.Null | TypeFlags.Undefined | TypeFlags.Void)) !== 0;
|
|
94
|
+
const rest = types.filter((type) => !empty(type)).map((type) => this.convert(type));
|
|
95
|
+
if (rest.length === 0)
|
|
96
|
+
return NULL;
|
|
97
|
+
const result = union(rest);
|
|
98
|
+
return types.some(empty) ? nullable(result) : result;
|
|
99
|
+
}
|
|
100
|
+
typeParamOrThis(type) {
|
|
101
|
+
// `this` in an interface or a class is the type itself.
|
|
102
|
+
if (type.isThisType) {
|
|
103
|
+
const constraint = type.getConstraint();
|
|
104
|
+
return constraint ? this.convert(constraint) : ANY;
|
|
105
|
+
}
|
|
106
|
+
return this.typeParam(type);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Constraints are not kept: they only matter in the bodies of generic functions, which in a
|
|
110
|
+
* `.d.ts` are not checked, and many have no counterpart. Defaults are kept: `Buffer` is
|
|
111
|
+
* `Buffer<ArrayBufferLike>`.
|
|
112
|
+
*/
|
|
113
|
+
typeParam(type) {
|
|
114
|
+
let param = this.params.get(type);
|
|
115
|
+
if (!param) {
|
|
116
|
+
param = { kind: 'param', name: type.symbol?.name ?? 'T', constraint: null };
|
|
117
|
+
this.params.set(type, param);
|
|
118
|
+
const fallback = this.checker.getDefaultFromTypeParameter(type);
|
|
119
|
+
if (fallback)
|
|
120
|
+
param.default = this.convert(fallback);
|
|
121
|
+
}
|
|
122
|
+
return param;
|
|
123
|
+
}
|
|
124
|
+
/** Declared in TypeScript's lib files. */
|
|
125
|
+
isLibrary(symbol) {
|
|
126
|
+
return (symbol.declarations ?? []).some((declaration) => this.program.isSourceFileDefaultLibrary(declaration.getSourceFile()));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** Members that Rook code can name: not `[Symbol.iterator]` and the like. */
|
|
130
|
+
export function propertyNames(checker, type) {
|
|
131
|
+
return checker
|
|
132
|
+
.getPropertiesOfType(type)
|
|
133
|
+
.map((property) => property.name)
|
|
134
|
+
.filter((name) => /^[A-Za-z_$][\w$]*$/.test(name));
|
|
135
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ClassInfo, type FunctionType, type Member, type ObjectType, type Type, type TypeParam } from '../checker/types.ts';
|
|
2
|
+
import type * as TS from 'typescript';
|
|
3
|
+
import { ConverterBase } from './convert-base.ts';
|
|
4
|
+
export declare abstract class ObjectConverter extends ConverterBase {
|
|
5
|
+
protected objectType(type: TS.ObjectType): Type;
|
|
6
|
+
/**
|
|
7
|
+
* `Array<T>` as an interface, for the members of arrays that the built-in types do not list.
|
|
8
|
+
* Elsewhere it is converted to `[]T`.
|
|
9
|
+
*/
|
|
10
|
+
arrayTemplate(symbol: TS.Symbol): ObjectType;
|
|
11
|
+
/** A class or an interface; a generic one is the template its instances are made from. */
|
|
12
|
+
protected declared(type: TS.InterfaceType): Type;
|
|
13
|
+
/**
|
|
14
|
+
* An object type with members converted when used. It is registered before its signatures are
|
|
15
|
+
* converted, since they may refer to it.
|
|
16
|
+
*/
|
|
17
|
+
protected object(type: TS.Type, name: string | null, typeParams?: TypeParam[]): ObjectType;
|
|
18
|
+
/**
|
|
19
|
+
* What `for v in x` gives: the `value` of what `next()` of `[Symbol.iterator]()` returns while
|
|
20
|
+
* it is not `done`. Found when it is first needed, as most types are never looped over.
|
|
21
|
+
*/
|
|
22
|
+
private iterates;
|
|
23
|
+
protected member(type: TS.Type, name: string, owner: ClassInfo | null): Member | undefined;
|
|
24
|
+
/** The first signature, with the others as its overloads. */
|
|
25
|
+
protected signatures(list: readonly TS.Signature[]): FunctionType | null;
|
|
26
|
+
protected signature(signature: TS.Signature): FunctionType;
|
|
27
|
+
protected classInfo(symbol: TS.Symbol): ClassInfo;
|
|
28
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { ANY, arrayOf, classInstance, createClass, func, instantiate, LazyMap, union, } from "../checker/types.js";
|
|
2
|
+
import { ConverterBase, propertyNames } from "./convert-base.js";
|
|
3
|
+
// Object types: arrays and tuples, interfaces and their generic templates, classes, functions with their overloads.
|
|
4
|
+
export class ObjectConverter extends ConverterBase {
|
|
5
|
+
objectType(type) {
|
|
6
|
+
const { checker } = this;
|
|
7
|
+
const { ObjectFlags, SymbolFlags, SignatureKind } = this.ts;
|
|
8
|
+
if (checker.isArrayType(type)) {
|
|
9
|
+
const [element] = checker.getTypeArguments(type);
|
|
10
|
+
return arrayOf(element ? this.convert(element) : ANY);
|
|
11
|
+
}
|
|
12
|
+
// A tuple is an array of any of its elements: `[string, number]` is `[](string | number)`.
|
|
13
|
+
if (checker.isTupleType(type)) {
|
|
14
|
+
const elements = checker.getTypeArguments(type);
|
|
15
|
+
return arrayOf(elements.length > 0 ? union(elements.map((each) => this.convert(each))) : ANY);
|
|
16
|
+
}
|
|
17
|
+
if (type.objectFlags & ObjectFlags.Reference) {
|
|
18
|
+
const reference = type;
|
|
19
|
+
const { target } = reference;
|
|
20
|
+
if (target !== reference) {
|
|
21
|
+
// `Box<number>`: the generic declaration with type arguments (without the `this` one).
|
|
22
|
+
const generic = this.declared(target);
|
|
23
|
+
const count = target.typeParameters?.length ?? 0;
|
|
24
|
+
const args = checker
|
|
25
|
+
.getTypeArguments(reference)
|
|
26
|
+
.slice(0, count)
|
|
27
|
+
.map((arg) => this.convert(arg));
|
|
28
|
+
if (generic.kind === 'class')
|
|
29
|
+
return classInstance(generic.info, args);
|
|
30
|
+
if (generic.kind === 'object' && generic.typeParams)
|
|
31
|
+
return instantiate(generic, args);
|
|
32
|
+
return generic;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (type.objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) {
|
|
36
|
+
return this.declared(type);
|
|
37
|
+
}
|
|
38
|
+
// `typeof Foo` of a class: the class itself.
|
|
39
|
+
const symbol = type.getSymbol();
|
|
40
|
+
if (symbol && symbol.flags & SymbolFlags.Class && type.objectFlags & ObjectFlags.Anonymous) {
|
|
41
|
+
return this.classInfo(symbol).value;
|
|
42
|
+
}
|
|
43
|
+
// A function: call signatures and nothing else.
|
|
44
|
+
const calls = checker.getSignaturesOfType(type, SignatureKind.Call);
|
|
45
|
+
if (calls.length > 0 &&
|
|
46
|
+
propertyNames(checker, type).length === 0 &&
|
|
47
|
+
checker.getSignaturesOfType(type, SignatureKind.Construct).length === 0) {
|
|
48
|
+
return this.signatures(calls);
|
|
49
|
+
}
|
|
50
|
+
return this.object(type, null);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* `Array<T>` as an interface, for the members of arrays that the built-in types do not list.
|
|
54
|
+
* Elsewhere it is converted to `[]T`.
|
|
55
|
+
*/
|
|
56
|
+
arrayTemplate(symbol) {
|
|
57
|
+
if (!this.arrayInterface) {
|
|
58
|
+
const { checker } = this;
|
|
59
|
+
const type = checker.getDeclaredTypeOfSymbol(symbol);
|
|
60
|
+
this.arrayInterface = {
|
|
61
|
+
kind: 'object',
|
|
62
|
+
name: 'Array',
|
|
63
|
+
members: new LazyMap(() => propertyNames(checker, type), (name) => this.member(type, name, null)),
|
|
64
|
+
call: null,
|
|
65
|
+
typeParams: (type.typeParameters ?? []).map((param) => this.typeParam(param)),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return this.arrayInterface;
|
|
69
|
+
}
|
|
70
|
+
/** A class or an interface; a generic one is the template its instances are made from. */
|
|
71
|
+
declared(type) {
|
|
72
|
+
const { symbol } = type;
|
|
73
|
+
// A value of type `Function` can be called with anything, as in TypeScript.
|
|
74
|
+
if (symbol.name === 'Function' && this.isLibrary(symbol))
|
|
75
|
+
return ANY;
|
|
76
|
+
if (symbol.flags & this.ts.SymbolFlags.Class)
|
|
77
|
+
return this.classInfo(symbol).instance;
|
|
78
|
+
const key = this.sharedKey(symbol, 'interface');
|
|
79
|
+
const known = key === null ? undefined : this.shared.get(key);
|
|
80
|
+
if (known && !('instance' in known)) {
|
|
81
|
+
this.types.set(type, known);
|
|
82
|
+
return known;
|
|
83
|
+
}
|
|
84
|
+
const params = (type.typeParameters ?? []).map((param) => this.typeParam(param));
|
|
85
|
+
const object = this.object(type, symbol.name, params);
|
|
86
|
+
if (key !== null)
|
|
87
|
+
this.shared.set(key, object);
|
|
88
|
+
return object;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* An object type with members converted when used. It is registered before its signatures are
|
|
92
|
+
* converted, since they may refer to it.
|
|
93
|
+
*/
|
|
94
|
+
object(type, name, typeParams = []) {
|
|
95
|
+
const known = this.types.get(type);
|
|
96
|
+
if (known?.kind === 'object')
|
|
97
|
+
return known;
|
|
98
|
+
const { checker } = this;
|
|
99
|
+
const { SignatureKind, IndexKind } = this.ts;
|
|
100
|
+
const object = {
|
|
101
|
+
kind: 'object',
|
|
102
|
+
name,
|
|
103
|
+
members: new LazyMap(() => propertyNames(checker, type), (member) => this.member(type, member, null)),
|
|
104
|
+
call: null,
|
|
105
|
+
};
|
|
106
|
+
if (typeParams.length > 0)
|
|
107
|
+
object.typeParams = typeParams;
|
|
108
|
+
this.types.set(type, object);
|
|
109
|
+
object.call = this.signatures(checker.getSignaturesOfType(type, SignatureKind.Call));
|
|
110
|
+
const construct = this.signatures(checker.getSignaturesOfType(type, SignatureKind.Construct));
|
|
111
|
+
if (construct)
|
|
112
|
+
object.construct = construct;
|
|
113
|
+
const index = checker.getIndexTypeOfType(type, IndexKind.String);
|
|
114
|
+
const numberIndex = checker.getIndexTypeOfType(type, IndexKind.Number);
|
|
115
|
+
if (index)
|
|
116
|
+
object.index = this.convert(index);
|
|
117
|
+
if (numberIndex && numberIndex !== index)
|
|
118
|
+
object.numberIndex = this.convert(numberIndex);
|
|
119
|
+
this.iterates(object, type);
|
|
120
|
+
return object;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* What `for v in x` gives: the `value` of what `next()` of `[Symbol.iterator]()` returns while
|
|
124
|
+
* it is not `done`. Found when it is first needed, as most types are never looped over.
|
|
125
|
+
*/
|
|
126
|
+
iterates(object, type) {
|
|
127
|
+
const { checker } = this;
|
|
128
|
+
const iterator = type
|
|
129
|
+
.getProperties()
|
|
130
|
+
.find((property) => String(property.escapedName).startsWith('__@iterator@'));
|
|
131
|
+
if (!iterator)
|
|
132
|
+
return;
|
|
133
|
+
let found;
|
|
134
|
+
let done = false;
|
|
135
|
+
Object.defineProperty(object, 'iterates', {
|
|
136
|
+
enumerable: true,
|
|
137
|
+
get: () => {
|
|
138
|
+
if (done)
|
|
139
|
+
return found;
|
|
140
|
+
done = true;
|
|
141
|
+
const next = checker
|
|
142
|
+
.getTypeOfSymbol(iterator)
|
|
143
|
+
.getCallSignatures()[0]
|
|
144
|
+
?.getReturnType()
|
|
145
|
+
.getProperty('next');
|
|
146
|
+
const result = next && checker.getTypeOfSymbol(next).getCallSignatures()[0]?.getReturnType();
|
|
147
|
+
const results = result?.isUnion() ? result.types : result ? [result] : [];
|
|
148
|
+
const yielded = results.find((each) => {
|
|
149
|
+
const flag = each.getProperty('done');
|
|
150
|
+
return !flag || checker.typeToString(checker.getTypeOfSymbol(flag)) !== 'true';
|
|
151
|
+
});
|
|
152
|
+
const value = yielded?.getProperty('value');
|
|
153
|
+
found = value && this.convert(checker.getTypeOfSymbol(value));
|
|
154
|
+
return found;
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
member(type, name, owner) {
|
|
159
|
+
const property = this.checker.getPropertyOfType(type, name);
|
|
160
|
+
if (!property)
|
|
161
|
+
return undefined;
|
|
162
|
+
const { SymbolFlags, ModifierFlags } = this.ts;
|
|
163
|
+
const modifiers = property.valueDeclaration
|
|
164
|
+
? this.ts.getCombinedModifierFlags(property.valueDeclaration)
|
|
165
|
+
: 0;
|
|
166
|
+
const visibility = modifiers & ModifierFlags.Private
|
|
167
|
+
? 'private'
|
|
168
|
+
: modifiers & ModifierFlags.Protected
|
|
169
|
+
? 'protected'
|
|
170
|
+
: 'public';
|
|
171
|
+
return {
|
|
172
|
+
// An optional property already includes `undefined`: `name?: string` is `?string`.
|
|
173
|
+
type: this.convert(this.checker.getTypeOfSymbol(property)),
|
|
174
|
+
method: (property.flags & SymbolFlags.Method) !== 0,
|
|
175
|
+
visibility,
|
|
176
|
+
owner,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/** The first signature, with the others as its overloads. */
|
|
180
|
+
signatures(list) {
|
|
181
|
+
const [first, ...rest] = list.map((signature) => this.signature(signature));
|
|
182
|
+
if (!first)
|
|
183
|
+
return null;
|
|
184
|
+
if (rest.length > 0)
|
|
185
|
+
first.overloads = rest;
|
|
186
|
+
return first;
|
|
187
|
+
}
|
|
188
|
+
signature(signature) {
|
|
189
|
+
const { ts, checker } = this;
|
|
190
|
+
const params = [];
|
|
191
|
+
let required = 0;
|
|
192
|
+
let rest = null;
|
|
193
|
+
for (const parameter of signature.getParameters()) {
|
|
194
|
+
const declaration = parameter.valueDeclaration;
|
|
195
|
+
const type = checker.getTypeOfSymbol(parameter);
|
|
196
|
+
const isParameter = declaration !== undefined && ts.isParameter(declaration);
|
|
197
|
+
if (isParameter && declaration.dotDotDotToken) {
|
|
198
|
+
const [element] = checker.isArrayType(type)
|
|
199
|
+
? checker.getTypeArguments(type)
|
|
200
|
+
: [];
|
|
201
|
+
rest = element ? this.convert(element) : ANY;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
params.push(this.convert(type));
|
|
205
|
+
if (!isParameter || !checker.isOptionalParameter(declaration))
|
|
206
|
+
required = params.length;
|
|
207
|
+
}
|
|
208
|
+
const result = this.convert(checker.getReturnTypeOfSignature(signature));
|
|
209
|
+
return func(params, result.kind === 'void' ? [] : [result], {
|
|
210
|
+
required,
|
|
211
|
+
...(rest ? { rest } : {}),
|
|
212
|
+
typeParams: (signature.getTypeParameters() ?? []).map((param) => this.typeParam(param)),
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
classInfo(symbol) {
|
|
216
|
+
const known = this.classes.get(symbol);
|
|
217
|
+
if (known)
|
|
218
|
+
return known;
|
|
219
|
+
const key = this.sharedKey(symbol, 'class');
|
|
220
|
+
const shared = key === null ? undefined : this.shared.get(key);
|
|
221
|
+
if (shared && 'instance' in shared) {
|
|
222
|
+
this.classes.set(symbol, shared);
|
|
223
|
+
return shared;
|
|
224
|
+
}
|
|
225
|
+
const { checker } = this;
|
|
226
|
+
const { SymbolFlags, SignatureKind } = this.ts;
|
|
227
|
+
const info = createClass(symbol.name);
|
|
228
|
+
this.classes.set(symbol, info);
|
|
229
|
+
if (key !== null)
|
|
230
|
+
this.shared.set(key, info);
|
|
231
|
+
const instance = checker.getDeclaredTypeOfSymbol(symbol);
|
|
232
|
+
info.typeParams = (instance.typeParameters ?? []).map((param) => this.typeParam(param));
|
|
233
|
+
// TypeScript lists inherited members too, already with the base class's type arguments.
|
|
234
|
+
info.members = new LazyMap(() => propertyNames(checker, instance), (name) => this.member(instance, name, info));
|
|
235
|
+
const statics = checker.getTypeOfSymbol(symbol);
|
|
236
|
+
info.statics = new LazyMap(() => propertyNames(checker, statics).filter((name) => name !== 'prototype'), (name) => this.member(statics, name, info));
|
|
237
|
+
const base = checker
|
|
238
|
+
.getBaseTypes(instance)
|
|
239
|
+
.find((type) => type.symbol && type.symbol.flags & SymbolFlags.Class);
|
|
240
|
+
if (base)
|
|
241
|
+
info.superClass = this.classInfo(base.symbol);
|
|
242
|
+
info.ctor = this.signatures(checker.getSignaturesOfType(statics, SignatureKind.Construct));
|
|
243
|
+
return info;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ModuleExports } from '../checker/checker.ts';
|
|
2
|
+
import { type Type } from '../checker/types.ts';
|
|
3
|
+
import { ObjectConverter } from './convert-objects.ts';
|
|
4
|
+
import type * as TS from 'typescript';
|
|
5
|
+
export type { SharedTypes } from './convert-base.ts';
|
|
6
|
+
/** Converts the types of a TypeScript program; one converter per program. */
|
|
7
|
+
export declare class TypeConverter extends ObjectConverter {
|
|
8
|
+
/** The values and types a module exports, converted when they are imported. */
|
|
9
|
+
moduleExports(module: TS.Symbol): ModuleExports;
|
|
10
|
+
/** The type of a value: a class is the class itself. */
|
|
11
|
+
valueType(symbol: TS.Symbol): Type;
|
|
12
|
+
/** The type that a declaration names: an interface, a class instance, an alias. */
|
|
13
|
+
typeOf(symbol: TS.Symbol): Type;
|
|
14
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { LazyMap } from "../checker/types.js";
|
|
2
|
+
import { ObjectConverter } from "./convert-objects.js";
|
|
3
|
+
/** Converts the types of a TypeScript program; one converter per program. */
|
|
4
|
+
export class TypeConverter extends ObjectConverter {
|
|
5
|
+
/** The values and types a module exports, converted when they are imported. */
|
|
6
|
+
moduleExports(module) {
|
|
7
|
+
const { checker } = this;
|
|
8
|
+
const { SymbolFlags } = this.ts;
|
|
9
|
+
const exported = new Map();
|
|
10
|
+
for (const symbol of checker.getExportsOfModule(module))
|
|
11
|
+
exported.set(symbol.name, symbol);
|
|
12
|
+
// `export = value` (CommonJS packages, `node:path`): the default import is the value itself,
|
|
13
|
+
// and named imports are its properties.
|
|
14
|
+
const assigned = module.exports?.get(this.ts.InternalSymbolName.ExportEquals);
|
|
15
|
+
if (assigned) {
|
|
16
|
+
if (!exported.has('default'))
|
|
17
|
+
exported.set('default', assigned);
|
|
18
|
+
const value = assigned.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(assigned) : assigned;
|
|
19
|
+
if (value.flags & SymbolFlags.Value) {
|
|
20
|
+
for (const property of checker.getPropertiesOfType(checker.getTypeOfSymbol(value))) {
|
|
21
|
+
if (!exported.has(property.name))
|
|
22
|
+
exported.set(property.name, property);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const target = (name) => {
|
|
27
|
+
const symbol = exported.get(name);
|
|
28
|
+
return symbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
values: new LazyMap(() => exported.keys(), (name) => {
|
|
32
|
+
const symbol = target(name);
|
|
33
|
+
return symbol.flags & SymbolFlags.Value ? this.valueType(symbol) : undefined;
|
|
34
|
+
}),
|
|
35
|
+
types: new LazyMap(() => exported.keys(), (name) => {
|
|
36
|
+
const symbol = target(name);
|
|
37
|
+
return symbol.flags & SymbolFlags.Type ? this.typeOf(symbol) : undefined;
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** The type of a value: a class is the class itself. */
|
|
42
|
+
valueType(symbol) {
|
|
43
|
+
if (symbol.flags & this.ts.SymbolFlags.Class)
|
|
44
|
+
return this.classInfo(symbol).value;
|
|
45
|
+
return this.convert(this.checker.getTypeOfSymbol(symbol));
|
|
46
|
+
}
|
|
47
|
+
/** The type that a declaration names: an interface, a class instance, an alias. */
|
|
48
|
+
typeOf(symbol) {
|
|
49
|
+
const { SymbolFlags } = this.ts;
|
|
50
|
+
if (symbol.flags & SymbolFlags.Class)
|
|
51
|
+
return this.classInfo(symbol).instance;
|
|
52
|
+
const type = this.convert(this.checker.getDeclaredTypeOfSymbol(symbol));
|
|
53
|
+
// `type Pair<A, B> = { ... }` works like a generic interface: `Pair<number, string>`.
|
|
54
|
+
if (symbol.flags & SymbolFlags.TypeAlias && type.kind === 'object' && !type.typeParams) {
|
|
55
|
+
const declaration = symbol.declarations?.find(this.ts.isTypeAliasDeclaration);
|
|
56
|
+
const params = declaration?.typeParameters ?? [];
|
|
57
|
+
if (params.length > 0) {
|
|
58
|
+
return {
|
|
59
|
+
...type,
|
|
60
|
+
name: symbol.name,
|
|
61
|
+
typeParams: params.map((param) => this.typeParam(this.checker.getTypeAtLocation(param))),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return type;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ImportResult, Library } from '../checker/checker.ts';
|
|
2
|
+
export declare class DeclarationImporter {
|
|
3
|
+
private ts;
|
|
4
|
+
/** Files with declarations that imports resolved to; the program is rebuilt when one is added. */
|
|
5
|
+
private readonly roots;
|
|
6
|
+
/** `node_modules/@types` folders above the importing files: `@types/node` gives `node:fs`. */
|
|
7
|
+
private readonly typeRoots;
|
|
8
|
+
/** The packages in them; TypeScript 6 does not include them by itself. */
|
|
9
|
+
private readonly typePackages;
|
|
10
|
+
private program;
|
|
11
|
+
private stale;
|
|
12
|
+
private converter;
|
|
13
|
+
/** Types of lib files and packages, shared by the converters of every program. */
|
|
14
|
+
private readonly shared;
|
|
15
|
+
private libraryTypes;
|
|
16
|
+
private readonly results;
|
|
17
|
+
/** The exports of the module `specifier` imported from the file `fromFile`. */
|
|
18
|
+
import(fromFile: string, specifier: string): ImportResult;
|
|
19
|
+
/**
|
|
20
|
+
* The standard library and the DOM for a file: TypeScript's lib files and the `@types` packages
|
|
21
|
+
* above it. The globals come from the program as it is when they are used.
|
|
22
|
+
*/
|
|
23
|
+
library(fromFile: string): Library | undefined;
|
|
24
|
+
private loadTypeScript;
|
|
25
|
+
private resolve;
|
|
26
|
+
private addTypeRoots;
|
|
27
|
+
private options;
|
|
28
|
+
/**
|
|
29
|
+
* The program with every root so far. A new program reuses the files of the old one; types
|
|
30
|
+
* already imported keep coming from the old one.
|
|
31
|
+
*/
|
|
32
|
+
private current;
|
|
33
|
+
}
|
|
34
|
+
/** Imports with declarations, shared by every compilation in this process. */
|
|
35
|
+
export declare function importDeclarations(fromFile: string, specifier: string): ImportResult;
|
|
36
|
+
/** The standard library and the DOM for a file, shared like the imports. */
|
|
37
|
+
export declare function libraryFor(fromFile: string): Library | undefined;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { TypeConverter } from "./convert.js";
|
|
5
|
+
import { LibraryTypes } from "./library.js";
|
|
6
|
+
// Types of imports that are not `.rk` modules: npm packages, `node:` modules and `.ts` files.
|
|
7
|
+
// The TypeScript compiler finds and reads their declarations; it is loaded only when such an
|
|
8
|
+
// import is checked, and an import without declarations stays untyped (`any`).
|
|
9
|
+
const TYPESCRIPT_EXTENSIONS = ['.d.ts', '.d.mts', '.d.cts', '.ts', '.mts', '.cts', '.tsx'];
|
|
10
|
+
export class DeclarationImporter {
|
|
11
|
+
ts;
|
|
12
|
+
/** Files with declarations that imports resolved to; the program is rebuilt when one is added. */
|
|
13
|
+
roots = new Set();
|
|
14
|
+
/** `node_modules/@types` folders above the importing files: `@types/node` gives `node:fs`. */
|
|
15
|
+
typeRoots = new Set();
|
|
16
|
+
/** The packages in them; TypeScript 6 does not include them by itself. */
|
|
17
|
+
typePackages = new Set();
|
|
18
|
+
program;
|
|
19
|
+
stale = true;
|
|
20
|
+
converter;
|
|
21
|
+
/** Types of lib files and packages, shared by the converters of every program. */
|
|
22
|
+
shared = new Map();
|
|
23
|
+
libraryTypes;
|
|
24
|
+
results = new Map();
|
|
25
|
+
/** The exports of the module `specifier` imported from the file `fromFile`. */
|
|
26
|
+
import(fromFile, specifier) {
|
|
27
|
+
const key = `${dirname(fromFile)}\0${specifier}`;
|
|
28
|
+
if (this.results.has(key))
|
|
29
|
+
return this.results.get(key);
|
|
30
|
+
const ts = this.loadTypeScript();
|
|
31
|
+
const result = ts ? this.resolve(ts, fromFile, specifier) : undefined;
|
|
32
|
+
this.results.set(key, result);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The standard library and the DOM for a file: TypeScript's lib files and the `@types` packages
|
|
37
|
+
* above it. The globals come from the program as it is when they are used.
|
|
38
|
+
*/
|
|
39
|
+
library(fromFile) {
|
|
40
|
+
const ts = this.loadTypeScript();
|
|
41
|
+
if (!ts)
|
|
42
|
+
return undefined;
|
|
43
|
+
this.addTypeRoots(fromFile);
|
|
44
|
+
const current = () => this.current(ts).library;
|
|
45
|
+
return {
|
|
46
|
+
value: (name) => current().value(name),
|
|
47
|
+
type: (name) => current().type(name),
|
|
48
|
+
primitive: (kind) => current().primitive(kind),
|
|
49
|
+
array: (element) => current().array(element),
|
|
50
|
+
element: (tag, svg) => current().element(tag, svg),
|
|
51
|
+
event: (name) => current().event(name),
|
|
52
|
+
names: (of) => current().names(of),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
loadTypeScript() {
|
|
56
|
+
if (this.ts === undefined) {
|
|
57
|
+
try {
|
|
58
|
+
this.ts = createRequire(import.meta.url)('typescript');
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
this.ts = null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return this.ts;
|
|
65
|
+
}
|
|
66
|
+
resolve(ts, fromFile, specifier) {
|
|
67
|
+
this.addTypeRoots(fromFile);
|
|
68
|
+
const { resolvedModule } = ts.resolveModuleName(specifier, fromFile, this.options(ts), ts.sys);
|
|
69
|
+
if (resolvedModule) {
|
|
70
|
+
const file = resolvedModule.resolvedFileName;
|
|
71
|
+
// A JS file without declarations next to it.
|
|
72
|
+
if (!TYPESCRIPT_EXTENSIONS.some((extension) => file.endsWith(extension)))
|
|
73
|
+
return undefined;
|
|
74
|
+
if (!this.roots.has(file)) {
|
|
75
|
+
this.roots.add(file);
|
|
76
|
+
this.stale = true;
|
|
77
|
+
}
|
|
78
|
+
const { program, converter } = this.current(ts);
|
|
79
|
+
const source = program.getSourceFile(file);
|
|
80
|
+
const module = source && program.getTypeChecker().getSymbolAtLocation(source);
|
|
81
|
+
return module ? { exports: converter.moduleExports(module) } : undefined;
|
|
82
|
+
}
|
|
83
|
+
// `node:fs`, `fs`: modules declared with `declare module "..."`, as in @types/node.
|
|
84
|
+
const { program, converter } = this.current(ts);
|
|
85
|
+
const name = JSON.stringify(specifier);
|
|
86
|
+
const ambient = program
|
|
87
|
+
.getTypeChecker()
|
|
88
|
+
.getAmbientModules()
|
|
89
|
+
.find((module) => module.name === name);
|
|
90
|
+
return ambient ? { exports: converter.moduleExports(ambient) } : undefined;
|
|
91
|
+
}
|
|
92
|
+
addTypeRoots(fromFile) {
|
|
93
|
+
for (let directory = dirname(fromFile);; directory = dirname(directory)) {
|
|
94
|
+
const types = join(directory, 'node_modules', '@types');
|
|
95
|
+
if (!this.typeRoots.has(types) && existsSync(types)) {
|
|
96
|
+
this.typeRoots.add(types);
|
|
97
|
+
for (const name of readdirSync(types)) {
|
|
98
|
+
if (!name.startsWith('.'))
|
|
99
|
+
this.typePackages.add(name);
|
|
100
|
+
}
|
|
101
|
+
this.stale = true;
|
|
102
|
+
}
|
|
103
|
+
if (dirname(directory) === directory)
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
options(ts) {
|
|
108
|
+
return {
|
|
109
|
+
// ES2025 has the helpers of iterators, `take`, `map` and the like, which iterators use.
|
|
110
|
+
target: ts.ScriptTarget.ES2025,
|
|
111
|
+
module: ts.ModuleKind.ESNext,
|
|
112
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
113
|
+
allowImportingTsExtensions: true,
|
|
114
|
+
esModuleInterop: true,
|
|
115
|
+
strict: true,
|
|
116
|
+
skipLibCheck: true,
|
|
117
|
+
noEmit: true,
|
|
118
|
+
typeRoots: [...this.typeRoots],
|
|
119
|
+
types: [...this.typePackages],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The program with every root so far. A new program reuses the files of the old one; types
|
|
124
|
+
* already imported keep coming from the old one.
|
|
125
|
+
*/
|
|
126
|
+
current(ts) {
|
|
127
|
+
if (this.stale || !this.program || !this.converter || !this.libraryTypes) {
|
|
128
|
+
const options = this.options(ts);
|
|
129
|
+
this.program = ts.createProgram({
|
|
130
|
+
// Without a root file, a program reads neither the lib nor the `types` packages.
|
|
131
|
+
rootNames: [ts.getDefaultLibFilePath(options), ...this.roots],
|
|
132
|
+
options,
|
|
133
|
+
...(this.program ? { oldProgram: this.program } : {}),
|
|
134
|
+
});
|
|
135
|
+
this.converter = new TypeConverter(ts, this.program, this.shared);
|
|
136
|
+
this.libraryTypes = new LibraryTypes(ts, this.program, this.converter);
|
|
137
|
+
this.stale = false;
|
|
138
|
+
}
|
|
139
|
+
return { program: this.program, converter: this.converter, library: this.libraryTypes };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
let shared;
|
|
143
|
+
/** Imports with declarations, shared by every compilation in this process. */
|
|
144
|
+
export function importDeclarations(fromFile, specifier) {
|
|
145
|
+
shared ??= new DeclarationImporter();
|
|
146
|
+
return shared.import(fromFile, specifier);
|
|
147
|
+
}
|
|
148
|
+
/** The standard library and the DOM for a file, shared like the imports. */
|
|
149
|
+
export function libraryFor(fromFile) {
|
|
150
|
+
shared ??= new DeclarationImporter();
|
|
151
|
+
return shared.library(fromFile);
|
|
152
|
+
}
|