rooklang 0.3.4 → 0.4.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/README.md +2 -0
- package/README.ru.md +2 -0
- package/dist/ast/declarations.d.ts +19 -3
- package/dist/ast/expressions.d.ts +4 -8
- package/dist/ast/statements.d.ts +3 -1
- package/dist/checker/assignments.d.ts +2 -0
- package/dist/checker/assignments.js +9 -3
- package/dist/checker/base.d.ts +8 -2
- package/dist/checker/base.js +3 -3
- package/dist/checker/builtins.js +7 -3
- package/dist/checker/channels.js +1 -2
- package/dist/checker/classes.js +7 -1
- package/dist/checker/closing.js +1 -1
- package/dist/checker/completion.d.ts +1 -1
- package/dist/checker/completion.js +2 -1
- package/dist/checker/component-bindings.d.ts +7 -0
- package/dist/checker/component-bindings.js +45 -0
- package/dist/checker/component-values.js +10 -7
- package/dist/checker/components.d.ts +2 -2
- package/dist/checker/components.js +8 -3
- package/dist/checker/context.d.ts +28 -5
- package/dist/checker/context.js +23 -0
- package/dist/checker/control-flow.d.ts +6 -5
- package/dist/checker/control-flow.js +48 -40
- package/dist/checker/declarations.d.ts +2 -2
- package/dist/checker/declarations.js +3 -2
- package/dist/checker/decorator-exports.js +4 -0
- package/dist/checker/decorator-includes.d.ts +9 -0
- package/dist/checker/decorator-includes.js +71 -0
- package/dist/checker/decorator-members.d.ts +8 -10
- package/dist/checker/decorator-members.js +28 -31
- package/dist/checker/decorator-wrappers.d.ts +1 -1
- package/dist/checker/decorator-wrappers.js +3 -2
- package/dist/checker/decorators.d.ts +14 -5
- package/dist/checker/decorators.js +52 -25
- package/dist/checker/expressions.d.ts +1 -1
- package/dist/checker/expressions.js +7 -6
- package/dist/checker/flow.d.ts +25 -3
- package/dist/checker/flow.js +48 -8
- package/dist/checker/functions.d.ts +2 -2
- package/dist/checker/functions.js +9 -5
- package/dist/checker/helpers.d.ts +4 -1
- package/dist/checker/helpers.js +7 -2
- package/dist/checker/imports.js +5 -2
- package/dist/checker/loops.d.ts +2 -0
- package/dist/checker/loops.js +27 -2
- package/dist/checker/markup.js +14 -2
- package/dist/checker/members.js +3 -1
- package/dist/checker/shared-state.d.ts +11 -0
- package/dist/checker/shared-state.js +37 -0
- package/dist/checker/statements.d.ts +5 -2
- package/dist/checker/statements.js +51 -22
- package/dist/checker/tagged-unions.d.ts +37 -0
- package/dist/checker/tagged-unions.js +132 -0
- package/dist/checker/untyped-globals.d.ts +1 -0
- package/dist/checker/untyped-globals.js +3 -1
- package/dist/codegen/attributes.d.ts +4 -0
- package/dist/codegen/attributes.js +24 -2
- package/dist/codegen/component-helpers.d.ts +6 -1
- package/dist/codegen/component-helpers.js +6 -1
- package/dist/codegen/component-props.d.ts +11 -2
- package/dist/codegen/component-props.js +50 -5
- package/dist/codegen/components.d.ts +6 -5
- package/dist/codegen/components.js +44 -21
- package/dist/codegen/control-flow.d.ts +2 -0
- package/dist/codegen/control-flow.js +23 -9
- package/dist/codegen/creation.js +5 -2
- package/dist/codegen/early-returns.d.ts +4 -3
- package/dist/codegen/early-returns.js +17 -5
- package/dist/codegen/emitter.d.ts +3 -0
- package/dist/codegen/emitter.js +18 -4
- package/dist/codegen/expressions.js +0 -2
- package/dist/codegen/functions.d.ts +11 -1
- package/dist/codegen/functions.js +31 -3
- package/dist/codegen/live.d.ts +60 -0
- package/dist/codegen/live.js +29 -0
- package/dist/codegen/mutations.d.ts +7 -0
- package/dist/codegen/mutations.js +43 -2
- package/dist/codegen/options.d.ts +2 -0
- package/dist/codegen/reactive.d.ts +4 -59
- package/dist/codegen/reactive.js +34 -72
- package/dist/codegen/runtime.js +6 -1
- package/dist/codegen/shared.d.ts +22 -0
- package/dist/codegen/shared.js +93 -0
- package/dist/codegen/simple-statements.d.ts +1 -1
- package/dist/codegen/statements.d.ts +5 -0
- package/dist/codegen/statements.js +35 -0
- package/dist/codegen/updates.d.ts +11 -3
- package/dist/codegen/updates.js +52 -4
- package/dist/codegen/web-components.d.ts +2 -2
- package/dist/codegen/web-components.js +2 -2
- package/dist/compile.js +6 -2
- package/dist/decorator-copy.d.ts +1 -1
- package/dist/decorator-copy.js +1 -0
- package/dist/decorator-names.d.ts +14 -0
- package/dist/decorator-names.js +56 -0
- package/dist/decorators.d.ts +2 -3
- package/dist/decorators.js +36 -14
- package/dist/inline-packages.d.ts +2 -1
- package/dist/inline-packages.js +9 -4
- package/dist/names.d.ts +4 -0
- package/dist/names.js +29 -0
- package/dist/parser/base.d.ts +1 -0
- package/dist/parser/base.js +1 -1
- package/dist/parser/control-flow.d.ts +5 -0
- package/dist/parser/control-flow.js +28 -2
- package/dist/parser/declarations.d.ts +5 -15
- package/dist/parser/declarations.js +15 -119
- package/dist/parser/decorators.d.ts +6 -2
- package/dist/parser/decorators.js +43 -19
- package/dist/parser/params.d.ts +21 -0
- package/dist/parser/params.js +143 -0
- package/dist/parser/simple-statements.d.ts +1 -0
- package/dist/parser/simple-statements.js +22 -6
- package/dist/parser/statements.d.ts +5 -2
- package/dist/parser/statements.js +18 -11
- package/dist/runtime/sources.js +3 -2
- package/dist/walk.js +3 -3
- package/package.json +2 -2
- package/src/runtime/decode.js +41 -10
- package/src/runtime/index.js +1 -0
- package/src/runtime/markup.js +27 -17
- package/src/runtime/shared.js +17 -0
package/README.md
CHANGED
|
@@ -58,6 +58,8 @@ close(results)
|
|
|
58
58
|
The guide with examples and a playground: <https://qwern-9876r.github.io/RookLang/>.
|
|
59
59
|
Specification: [docs/spec.md](docs/spec.md), design of components:
|
|
60
60
|
[docs/components.md](docs/components.md), an example: [examples/hello.rk](examples/hello.rk).
|
|
61
|
+
Where Rook stands and the road to 1.0: [docs/roadmap.md](docs/roadmap.md); what changed in each
|
|
62
|
+
version: [CHANGELOG.md](CHANGELOG.md).
|
|
61
63
|
|
|
62
64
|
## Installation
|
|
63
65
|
|
package/README.ru.md
CHANGED
|
@@ -58,6 +58,8 @@ close(results)
|
|
|
58
58
|
Руководство с примерами и песочницей: <https://qwern-9876r.github.io/RookLang/ru/>.
|
|
59
59
|
Спецификация: [docs/spec.ru.md](docs/spec.ru.md), концепция компонентов:
|
|
60
60
|
[docs/components.ru.md](docs/components.ru.md), пример: [examples/hello.rk](examples/hello.rk).
|
|
61
|
+
Где Rook сейчас и путь к 1.0: [docs/roadmap.ru.md](docs/roadmap.ru.md); что изменилось в каждой
|
|
62
|
+
версии: [CHANGELOG.ru.md](CHANGELOG.ru.md).
|
|
61
63
|
|
|
62
64
|
## Установка
|
|
63
65
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Expression, Identifier, StringLiteral } from './expressions.ts';
|
|
1
|
+
import type { Expression, FuncExpression, Identifier, StringLiteral } from './expressions.ts';
|
|
2
2
|
import type { TypeMember, TypeNode, TypeReference } from './markup.ts';
|
|
3
3
|
import type { BlockStatement, Statement } from './statements.ts';
|
|
4
4
|
export interface NodeBase {
|
|
@@ -57,6 +57,11 @@ export interface Parameter extends NodeBase {
|
|
|
57
57
|
* properties, which `{...attrs}` puts on one of its elements.
|
|
58
58
|
*/
|
|
59
59
|
rest?: true;
|
|
60
|
+
/**
|
|
61
|
+
* `bind active string`: a property of a component that it may change. `bind:active={tab}`
|
|
62
|
+
* writes the changes into the parent's `tab`; without it the property is the component's state.
|
|
63
|
+
*/
|
|
64
|
+
bind?: true;
|
|
60
65
|
/**
|
|
61
66
|
* `constructor(private name string)`: the parameter is a property too, which the constructor
|
|
62
67
|
* assigns, as in TypeScript.
|
|
@@ -97,6 +102,11 @@ export interface DecoratorDeclaration extends NodeBase {
|
|
|
97
102
|
params: Parameter[];
|
|
98
103
|
/** `needs cart, auth`: the decorators whose public members it uses; they are applied above it. */
|
|
99
104
|
needs: Identifier[];
|
|
105
|
+
/**
|
|
106
|
+
* `includes visible(margin), cart`: decorators applied with it, above its own body, whose
|
|
107
|
+
* arguments see its parameters. Their public members are its own for the component, `@shop.count`.
|
|
108
|
+
*/
|
|
109
|
+
includes: DecoratorUse[];
|
|
100
110
|
body: BlockStatement;
|
|
101
111
|
}
|
|
102
112
|
/** `@visible("300px")` or `@visible` before `comp`; the arguments are positional. */
|
|
@@ -118,14 +128,20 @@ export interface VariableDeclaration extends NodeBase {
|
|
|
118
128
|
exported: boolean;
|
|
119
129
|
keyword: 'let' | 'const' | 'state' | 'derived' | 'ref';
|
|
120
130
|
/**
|
|
121
|
-
* `public state`, `public derived` or `public const` in a decorator: the component reads it
|
|
122
|
-
*
|
|
131
|
+
* `public state`, `public derived` or `public const` in a decorator: the component reads it as
|
|
132
|
+
* `@name.x`.
|
|
123
133
|
*/
|
|
124
134
|
isPublic: boolean;
|
|
125
135
|
/** `_` is an ordinary identifier here; the checker treats it as "skip this value". */
|
|
126
136
|
names: Identifier[];
|
|
127
137
|
type: TypeNode | null;
|
|
128
138
|
values: Expression[];
|
|
139
|
+
/**
|
|
140
|
+
* `derived summary string { ...; return text }`: the block that computes a derived value, a
|
|
141
|
+
* function without parameters, so that what looks for the `return`s, `defer`s and `break`s of a
|
|
142
|
+
* component stops at it, as at other functions. Its result type is `type`; `values` is empty.
|
|
143
|
+
*/
|
|
144
|
+
body: FuncExpression | null;
|
|
129
145
|
}
|
|
130
146
|
/** `class Admin extends User implements Shape { ... }` */
|
|
131
147
|
export interface ClassDeclaration extends NodeBase {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NodeBase, Parameter } from './declarations.ts';
|
|
2
2
|
import type { ElementExpression, TypeNode } from './markup.ts';
|
|
3
3
|
import type { BlockStatement } from './statements.ts';
|
|
4
|
-
export type Expression = Identifier | NumberLiteral | BigIntLiteral | StringLiteral | RegExpLiteral | TemplateLiteral | BooleanLiteral | NullLiteral | ThisExpression | SuperExpression | ArrayLiteral | ObjectLiteral | FuncExpression | ArrowFunction | UnaryExpression | BinaryExpression | ConditionalExpression | CallExpression | NewExpression | MemberExpression | IndexExpression | ElementExpression | DecoratorMember |
|
|
4
|
+
export type Expression = Identifier | NumberLiteral | BigIntLiteral | StringLiteral | RegExpLiteral | TemplateLiteral | BooleanLiteral | NullLiteral | ThisExpression | SuperExpression | ArrayLiteral | ObjectLiteral | FuncExpression | ArrowFunction | UnaryExpression | BinaryExpression | ConditionalExpression | CallExpression | NewExpression | MemberExpression | IndexExpression | ElementExpression | DecoratorMember | AsExpression | GoExpression | ChanExpression | StreamChanExpression | QueueExpression | TopicExpression | DecodeExpression | TryExpression | ConversionExpression | MakeExpression;
|
|
5
5
|
/**
|
|
6
6
|
* `[]byte("rook")`: the UTF-8 bytes of a string. `string(data)`, the way back, is a call, since
|
|
7
7
|
* `string` may be the name of a value.
|
|
@@ -231,16 +231,12 @@ export interface IndexExpression extends NodeBase {
|
|
|
231
231
|
optional: boolean;
|
|
232
232
|
}
|
|
233
233
|
/**
|
|
234
|
-
* `@visible.shown`: a public member of a decorator applied to the component
|
|
235
|
-
* the decorator, not to any scope, so walkers do not visit them
|
|
234
|
+
* `@visible.shown` or `@visible.show()`: a public member of a decorator applied to the component,
|
|
235
|
+
* read or called. The names belong to the decorator, not to any scope, so walkers do not visit them
|
|
236
|
+
* as identifiers.
|
|
236
237
|
*/
|
|
237
238
|
export interface DecoratorMember extends NodeBase {
|
|
238
239
|
kind: 'DecoratorMember';
|
|
239
240
|
decorator: Identifier;
|
|
240
241
|
member: Identifier;
|
|
241
242
|
}
|
|
242
|
-
/** `get(@visible.shown)`: reads public state or a constant of a decorator. */
|
|
243
|
-
export interface DecoratorGet extends NodeBase {
|
|
244
|
-
kind: 'DecoratorGet';
|
|
245
|
-
target: DecoratorMember;
|
|
246
|
-
}
|
package/dist/ast/statements.d.ts
CHANGED
|
@@ -86,6 +86,8 @@ export interface ForInStatement extends NodeBase {
|
|
|
86
86
|
key: Identifier | null;
|
|
87
87
|
value: Identifier;
|
|
88
88
|
iterable: Expression;
|
|
89
|
+
/** `with key todo.id` of a `{for}` in markup: what its blocks are found again by. */
|
|
90
|
+
withKey?: Expression;
|
|
89
91
|
body: BlockStatement;
|
|
90
92
|
go?: boolean;
|
|
91
93
|
}
|
|
@@ -156,7 +158,7 @@ export interface DeferStatement extends NodeBase {
|
|
|
156
158
|
body: CallExpression | BlockStatement;
|
|
157
159
|
}
|
|
158
160
|
/**
|
|
159
|
-
* `mount
|
|
161
|
+
* `mount { ... }` at the top level of a component: runs once the component's markup is in the
|
|
160
162
|
* document. It may return a function, which runs when `{if}`, `{switch}` or `{for}` removes the
|
|
161
163
|
* markup.
|
|
162
164
|
*/
|
|
@@ -2,6 +2,8 @@ import type * as ast from '../ast.ts';
|
|
|
2
2
|
import { InitializerChecker } from './initializers.ts';
|
|
3
3
|
import { type Type } from './types.ts';
|
|
4
4
|
export declare abstract class AssignmentChecker extends InitializerChecker {
|
|
5
|
+
/** The name of the `derived` whose block is being checked: it computes, so it changes no state. */
|
|
6
|
+
protected derivedBlock: string | null;
|
|
5
7
|
/**
|
|
6
8
|
* `a, b = f()` or `return f()`: the call must return exactly `count` values. Returns their
|
|
7
9
|
* types.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { placeOf } from "./context.js";
|
|
1
2
|
import { guardedByError, unpackedResults } from "./error-results.js";
|
|
2
3
|
import { isUntyped, countValues, callName, isCall } from "./helpers.js";
|
|
3
4
|
import { InitializerChecker } from "./initializers.js";
|
|
@@ -5,6 +6,8 @@ import { BOOL, ANY, isAssignable, isNullable, nonNull, nullable, typeToString, U
|
|
|
5
6
|
// Where values go: assignments to variables, fields and elements, and `return`. Both unpack a call
|
|
6
7
|
// that returns several values.
|
|
7
8
|
export class AssignmentChecker extends InitializerChecker {
|
|
9
|
+
/** The name of the `derived` whose block is being checked: it computes, so it changes no state. */
|
|
10
|
+
derivedBlock = null;
|
|
8
11
|
/**
|
|
9
12
|
* `a, b = f()` or `return f()`: the call must return exactly `count` values. Returns their
|
|
10
13
|
* types.
|
|
@@ -66,13 +69,13 @@ export class AssignmentChecker extends InitializerChecker {
|
|
|
66
69
|
const binding = this.narrowableBinding(target);
|
|
67
70
|
const declared = binding?.type;
|
|
68
71
|
if (binding && declared && isNullable(declared) && !isNullable(valueTypes[i] ?? UNKNOWN)) {
|
|
69
|
-
this.flow.set(binding, nonNull(declared));
|
|
72
|
+
this.flow.set(placeOf(binding), nonNull(declared));
|
|
70
73
|
}
|
|
71
74
|
});
|
|
72
75
|
}
|
|
73
76
|
/** The type a value assigned to `target` must have. */
|
|
74
77
|
checkTarget(target) {
|
|
75
|
-
if (this.checkDecoratorWrite(target))
|
|
78
|
+
if (this.checkDecoratorWrite(target) || this.checkSharedWrite(target))
|
|
76
79
|
return UNKNOWN;
|
|
77
80
|
switch (target.kind) {
|
|
78
81
|
case 'Identifier': {
|
|
@@ -92,13 +95,16 @@ export class AssignmentChecker extends InitializerChecker {
|
|
|
92
95
|
else if (binding.kind === 'ref') {
|
|
93
96
|
this.error(`cannot assign to "${target.name}": a ref always points to its element`, target);
|
|
94
97
|
}
|
|
98
|
+
else if (binding.kind === 'state' && this.derivedBlock !== null) {
|
|
99
|
+
this.error(`derived "${this.derivedBlock}" cannot change the state "${target.name}": change it in an event handler or an effect`, target);
|
|
100
|
+
}
|
|
95
101
|
else if (binding.kind === 'loop') {
|
|
96
102
|
this.error(`cannot assign to loop variable "${target.name}"`, target);
|
|
97
103
|
}
|
|
98
104
|
else if (binding.kind === 'prop') {
|
|
99
105
|
this.error(this.decorator
|
|
100
106
|
? `cannot assign to "${target.name}": parameters of a decorator are read-only`
|
|
101
|
-
: `cannot assign to "${target.name}": component properties are read-only; to change
|
|
107
|
+
: `cannot assign to "${target.name}": component properties are read-only; to change it, declare it with bind, e.g. comp ${this.component?.node.name.name ?? 'Tabs'}(bind ${target.name} ...)`, target);
|
|
102
108
|
}
|
|
103
109
|
else if (binding.kind !== 'let' &&
|
|
104
110
|
binding.kind !== 'state' &&
|
package/dist/checker/base.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
2
|
import type { Diagnostic } from '../diagnostics.ts';
|
|
3
|
-
import { type CheckOptions, type BindingKind, type Binding, type ComponentInfo, type DecoratorInfo, type AliasEntry, Scope, type FunctionContext, type ClassContext, type Flow } from './context.ts';
|
|
3
|
+
import { type CheckOptions, type BindingKind, type Binding, type ComponentInfo, type DecoratorInfo, type AliasEntry, Scope, type FunctionContext, type ClassContext, type Flow, type Place } from './context.ts';
|
|
4
4
|
import { type ClassInfo, type FunctionType, type Member, type Type, type TypeParam } from './types.ts';
|
|
5
5
|
export declare abstract class CheckerBase {
|
|
6
6
|
protected abstract checkClassBodies(node: ast.ClassDeclaration, info: ClassInfo): void;
|
|
@@ -18,6 +18,13 @@ export declare abstract class CheckerBase {
|
|
|
18
18
|
protected abstract checkTry(node: ast.TryStatement): void;
|
|
19
19
|
protected abstract forget(target: ast.Expression): void;
|
|
20
20
|
protected abstract narrowableBinding(node: ast.Expression): Binding | undefined;
|
|
21
|
+
/** A variable or a path of data fields that conditions narrow, with its type at this point. */
|
|
22
|
+
protected abstract narrowable(node: ast.Expression): {
|
|
23
|
+
place: Place;
|
|
24
|
+
type: Type;
|
|
25
|
+
} | undefined;
|
|
26
|
+
/** Whether an assignment to `field` of `place` may change the member of a union it is. */
|
|
27
|
+
protected abstract isTagOf(place: Place, field: string): boolean;
|
|
21
28
|
/** The error for a value that may be null where it must not be. */
|
|
22
29
|
protected abstract nullError(node: ast.Expression): void;
|
|
23
30
|
protected abstract checkFunction(params: readonly ast.Parameter[], type: FunctionType, results: Type[] | null, body: ast.BlockStatement | ast.Expression, options?: {
|
|
@@ -44,7 +51,6 @@ export declare abstract class CheckerBase {
|
|
|
44
51
|
protected abstract orderByNeeds(decorators: readonly DecoratorInfo[]): DecoratorInfo[];
|
|
45
52
|
protected abstract wrappedType(info: ComponentInfo, own: Type, seen: Set<ComponentInfo>): Type;
|
|
46
53
|
protected abstract checkDecoratorMember(node: ast.DecoratorMember): Type;
|
|
47
|
-
protected abstract checkDecoratorGet(node: ast.DecoratorGet): Type;
|
|
48
54
|
protected abstract checkDecoratorWrite(target: ast.Expression): boolean;
|
|
49
55
|
protected abstract checkComponentUse(node: ast.ElementExpression, tag: ast.Identifier): Type;
|
|
50
56
|
protected abstract checkRefDeclaration(node: ast.VariableDeclaration): void;
|
package/dist/checker/base.js
CHANGED
|
@@ -4,9 +4,9 @@ import { Scope, globalScope, } from "./context.js";
|
|
|
4
4
|
import { DOCUMENT_FRAGMENT, eventType, HTML_ELEMENT, NODE } from "./dom.js";
|
|
5
5
|
import { explainMismatch, isAssignable, LazyMap, memberNames, nonNull, property, typeToString, UNKNOWN, } from "./types.js";
|
|
6
6
|
// The type checker is a chain of layers, one per area, like the code generator: base → imports →
|
|
7
|
-
// declarations → class members → classes → resolution → initializers → assignments → statements → control flow →
|
|
8
|
-
// functions → expressions → array literals → operators → markup → forwarding → components → component values →
|
|
9
|
-
// refs → styles → decorator wrappers → decorator exports → decorators → decorator members →
|
|
7
|
+
// shared state → declarations → class members → classes → resolution → initializers → assignments → statements → control flow →
|
|
8
|
+
// tagged unions → functions → expressions → array literals → operators → markup → forwarding → component bindings → components → component values →
|
|
9
|
+
// refs → styles → decorator wrappers → decorator exports → decorators → decorator includes → decorator members →
|
|
10
10
|
// members → calls → goroutines → channels → signals → queues → topics → decode → try → bytes → loops → go for →
|
|
11
11
|
// iterators → closing → completion → Checker (checker.ts). Statements, expressions and markup
|
|
12
12
|
// check each other recursively, so methods of later layers that earlier ones call are declared
|
package/dist/checker/builtins.js
CHANGED
|
@@ -6,7 +6,7 @@ import { chanOf } from "./channel.js";
|
|
|
6
6
|
import { CHANNEL_OBJECT, TOPIC_OBJECT } from "./channel-objects.js";
|
|
7
7
|
import { PROMISE } from "./promise.js";
|
|
8
8
|
import { BUFFERS } from "./transfer.js";
|
|
9
|
-
import { UNTYPED_GLOBALS } from "./untyped-globals.js";
|
|
9
|
+
import { UNTYPED_CLASSES, UNTYPED_GLOBALS } from "./untyped-globals.js";
|
|
10
10
|
import { ANY, arrayOf, BIGINT, BOOL, ERROR, ERROR_CLASS, func, method, NUMBER, nullable, property, STRING, topicOf, union, } from "./types.js";
|
|
11
11
|
// Types of the JS standard library that Rook programs use most. Anything not listed here
|
|
12
12
|
// can still be reached through the untyped globals below.
|
|
@@ -249,7 +249,10 @@ export const GLOBAL_VALUES = new Map([
|
|
|
249
249
|
['cancel', func([ANY], [])],
|
|
250
250
|
['Channel', CHANNEL_OBJECT],
|
|
251
251
|
['Topic', TOPIC_OBJECT],
|
|
252
|
-
...[...UNTYPED_GLOBALS, ...BUFFERS].map((name) => [
|
|
252
|
+
...[...UNTYPED_GLOBALS, ...UNTYPED_CLASSES, ...BUFFERS].map((name) => [
|
|
253
|
+
name,
|
|
254
|
+
ANY,
|
|
255
|
+
]),
|
|
253
256
|
]);
|
|
254
257
|
/** Built-in types that a library from TypeScript's lib files replaces: untyped ones and the DOM. */
|
|
255
258
|
export const LIBRARY_TYPES = new Set([
|
|
@@ -260,6 +263,7 @@ export const LIBRARY_TYPES = new Set([
|
|
|
260
263
|
'Promise',
|
|
261
264
|
'URL',
|
|
262
265
|
'Event',
|
|
266
|
+
...UNTYPED_CLASSES,
|
|
263
267
|
...BUFFERS,
|
|
264
268
|
...[...DOM_TYPES.keys()].filter((name) => name !== 'Content'),
|
|
265
269
|
...SVG_TYPES.keys(),
|
|
@@ -278,7 +282,7 @@ export const GLOBAL_TYPES = new Map([
|
|
|
278
282
|
['any', ANY],
|
|
279
283
|
['error', ERROR],
|
|
280
284
|
['Error', ERROR_CLASS.instance],
|
|
281
|
-
...['Date', 'RegExp', 'Map', 'Set', 'URL', ...BUFFERS].map((name) => [name, ANY]),
|
|
285
|
+
...['Date', 'RegExp', 'Map', 'Set', 'URL', ...UNTYPED_CLASSES, ...BUFFERS].map((name) => [name, ANY]),
|
|
282
286
|
['Promise', PROMISE],
|
|
283
287
|
...DOM_TYPES,
|
|
284
288
|
...SVG_TYPES,
|
package/dist/checker/channels.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { assignedNames } from "../names.js";
|
|
2
1
|
import { cannotWait } from "../parser/syntax.js";
|
|
3
2
|
import { chanDirection, chanOf, elementOf, isChan } from "./channel.js";
|
|
4
3
|
import { GoroutineChecker } from "./goroutines.js";
|
|
@@ -115,7 +114,7 @@ export class ChannelChecker extends GoroutineChecker {
|
|
|
115
114
|
: ANY;
|
|
116
115
|
}
|
|
117
116
|
checkSelect(node) {
|
|
118
|
-
this.dropNarrowing(
|
|
117
|
+
this.dropNarrowing(node);
|
|
119
118
|
const entry = this.flow;
|
|
120
119
|
for (const { names, operation, body } of node.cases) {
|
|
121
120
|
this.flow = new Map(entry);
|
package/dist/checker/classes.js
CHANGED
|
@@ -195,6 +195,7 @@ export class ClassChecker extends ClassMemberChecker {
|
|
|
195
195
|
types: new Map(),
|
|
196
196
|
decorators: new Map(),
|
|
197
197
|
components: new Map(),
|
|
198
|
+
shared: new Set(),
|
|
198
199
|
};
|
|
199
200
|
const declarations = { values: new Map(), types: new Map() };
|
|
200
201
|
const valueOf = (name) => this.moduleScope.values.get(name)?.type ?? UNKNOWN;
|
|
@@ -217,6 +218,8 @@ export class ClassChecker extends ClassMemberChecker {
|
|
|
217
218
|
case 'VariableDeclaration':
|
|
218
219
|
for (const name of statement.names) {
|
|
219
220
|
add(statement.exported, 'values', name.name, valueOf(name.name));
|
|
221
|
+
if (statement.exported && statement.keyword === 'state')
|
|
222
|
+
exports.shared.add(name.name);
|
|
220
223
|
}
|
|
221
224
|
break;
|
|
222
225
|
case 'ClassDeclaration':
|
|
@@ -230,7 +233,10 @@ export class ClassChecker extends ClassMemberChecker {
|
|
|
230
233
|
case 'ComponentDeclaration': {
|
|
231
234
|
const info = this.moduleScope.values.get(statement.name.name)?.component;
|
|
232
235
|
if (statement.exported && info?.node === statement) {
|
|
233
|
-
|
|
236
|
+
const found = this.modulePackages().components.get(info.node.name.name) ?? null;
|
|
237
|
+
// Code that reads state another module shares listens to it here, so it stays here.
|
|
238
|
+
const shared = found?.moduleNames.some((name) => this.moduleScope.values.get(name)?.shared !== undefined);
|
|
239
|
+
info.inline = shared ? null : found;
|
|
234
240
|
info.expressionTypes = this.checkedTypes;
|
|
235
241
|
exports.components.set(statement.name.name, info);
|
|
236
242
|
}
|
package/dist/checker/closing.js
CHANGED
|
@@ -4,7 +4,7 @@ import { IteratorChecker } from "./iterators.js";
|
|
|
4
4
|
const OPERATIONS = new Set(['send', 'receive', 'range', 'stream', 'publish', 'listen', 'try']);
|
|
5
5
|
const MESSAGES = {
|
|
6
6
|
chan: (name) => `"${name}" is never closed: close it when nothing more is sent, e.g. close(${name})`,
|
|
7
|
-
topic: (name) => `"${name}" is never closed, and a topic listens until it is closed: close it when it is no longer needed, e.g. close(${name})
|
|
7
|
+
topic: (name) => `"${name}" is never closed, and a topic listens until it is closed: close it when it is no longer needed, e.g. close(${name}); a component closes it with defer close(${name}) in mount`,
|
|
8
8
|
tick: (name) => `"${name}" is never closed, and a tick runs until it is closed: e.g. close(${name})`,
|
|
9
9
|
signal: (name) => `"${name}" is never closed, and it keeps the program running until it is: close it once the signal has come, e.g. close(${name})`,
|
|
10
10
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
2
|
import { ClosingChecker } from './closing.ts';
|
|
3
|
-
import type
|
|
3
|
+
import { type CompletionResult } from './context.ts';
|
|
4
4
|
import { type Type } from './types.ts';
|
|
5
5
|
export declare abstract class CompletionChecker extends ClosingChecker {
|
|
6
6
|
protected completion: CompletionResult | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ClosingChecker } from "./closing.js";
|
|
2
2
|
import { ARRAY_MEMBERS, ATTRIBUTE_NAMES, BIGINT_MEMBERS, BOOL_MEMBERS, EVENT_ATTRIBUTES, HTML_TAGS, NUMBER_MEMBERS, STRING_MEMBERS, } from "./completion-names.js";
|
|
3
|
+
import { placeOf, } from "./context.js";
|
|
3
4
|
import { isSubclass, literalBase, memberNames, memberTypeOf, nonNull, typeToString, union, UNKNOWN, } from "./types.js";
|
|
4
5
|
// Completion in editors (src/completion.ts): the editor writes the name `options.completion` where
|
|
5
6
|
// the cursor is, and this layer lists what can be written there when the check reaches that name:
|
|
@@ -79,7 +80,7 @@ export class CompletionChecker extends ClosingChecker {
|
|
|
79
80
|
if (component) {
|
|
80
81
|
return { name, kind: 'component', detail: typeToString(this.componentValue(component)) };
|
|
81
82
|
}
|
|
82
|
-
const type = binding.type && (this.flow.get(binding) ?? binding.type);
|
|
83
|
+
const type = binding.type && (this.flow.get(placeOf(binding)) ?? binding.type);
|
|
83
84
|
const detail = type ? { detail: typeToString(type) } : {};
|
|
84
85
|
if (kind === 'function' || type?.kind === 'function')
|
|
85
86
|
return { name, kind: 'function', ...detail };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import type { ComponentInfo } from './context.ts';
|
|
3
|
+
import { ForwardingChecker } from './forwarding.ts';
|
|
4
|
+
export declare abstract class ComponentBindingChecker extends ForwardingChecker {
|
|
5
|
+
/** `bind:active={tab}` on a component; adds the name of the property to `given`. */
|
|
6
|
+
protected checkPropBinding(attribute: ast.JsxAttribute, info: ComponentInfo, component: string, given: Set<string>): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { attrsParam } from "../attrs.js";
|
|
2
|
+
import { ForwardingChecker } from "./forwarding.js";
|
|
3
|
+
import { isUntyped } from "./helpers.js";
|
|
4
|
+
import { isAssignable, typeToString } from "./types.js";
|
|
5
|
+
// Bound properties: `comp Tabs(bind active string)` may change `active`, and
|
|
6
|
+
// `<Tabs bind:active={tab} />` writes those changes into the parent's `tab`, as `bind:value` of an
|
|
7
|
+
// input does. The value goes both ways, so the variable and the property have the same type.
|
|
8
|
+
export class ComponentBindingChecker extends ForwardingChecker {
|
|
9
|
+
/** `bind:active={tab}` on a component; adds the name of the property to `given`. */
|
|
10
|
+
checkPropBinding(attribute, info, component, given) {
|
|
11
|
+
const name = attribute.name.name.slice('bind:'.length);
|
|
12
|
+
const prop = info.props.get(name);
|
|
13
|
+
const { value } = attribute;
|
|
14
|
+
if (given.has(name))
|
|
15
|
+
this.error(`duplicate property "${name}"`, attribute.name);
|
|
16
|
+
given.add(name);
|
|
17
|
+
if (!prop) {
|
|
18
|
+
this.error(attrsParam(info.node)
|
|
19
|
+
? `${attribute.name.name} does not go through the attributes of <${component}>`
|
|
20
|
+
: `<${component}> has no property "${name}" to bind`, attribute.name);
|
|
21
|
+
}
|
|
22
|
+
else if (!prop.bind) {
|
|
23
|
+
this.error(`"${name}" of <${component}> is not bound: the component declares it with bind to change it, e.g. comp ${component}(bind ${name} ...)`, attribute.name);
|
|
24
|
+
}
|
|
25
|
+
if (value === null || value.kind === 'StringLiteral' || value.kind === 'EventHandler') {
|
|
26
|
+
this.error(`"bind:${name}" needs a variable in braces, e.g. bind:${name}={${name}}`, attribute);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const bindable = (value.kind === 'Identifier' && value.name !== '_') ||
|
|
30
|
+
((value.kind === 'MemberExpression' || value.kind === 'IndexExpression') && !value.optional);
|
|
31
|
+
if (!bindable) {
|
|
32
|
+
this.checkValue(value);
|
|
33
|
+
this.error(`"bind:${name}" needs a variable or a field to write to`, value);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const type = this.checkTarget(value);
|
|
37
|
+
this.checkedTypes.set(value, type);
|
|
38
|
+
if (!prop?.bind || isUntyped(type) || isUntyped(prop.type))
|
|
39
|
+
return;
|
|
40
|
+
// The parent gives its value, and the component writes its own back.
|
|
41
|
+
if (!isAssignable(type, prop.type) || !isAssignable(prop.type, type)) {
|
|
42
|
+
this.error(`cannot bind ${typeToString(type)} to "${name}" of <${component}>, which is ${typeToString(prop.type)}`, value);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ComponentChecker } from "./components.js";
|
|
2
2
|
import { isUntyped } from "./helpers.js";
|
|
3
|
-
const upperFirst = (name) => name.charAt(0).toUpperCase() + name.slice(1);
|
|
4
3
|
import { isNullable, typeToString, UNKNOWN, } from "./types.js";
|
|
5
4
|
// Components as values: `content: Intro` has the type `comp()`, and a tag takes the component
|
|
6
5
|
// from a value, `<section.content />`, or `<Page />` when `Page` is a variable. Such a tag passes
|
|
@@ -20,12 +19,8 @@ export class ComponentValueChecker extends ComponentChecker {
|
|
|
20
19
|
const name = tag.name;
|
|
21
20
|
if (isUntyped(type) || type.kind !== 'component') {
|
|
22
21
|
if (isNullable(type)) {
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
const show = `{if ${variable} != null { <${variable} /> }}`;
|
|
26
|
-
this.error(name === variable
|
|
27
|
-
? `<${name}> may be null: show it with {if}, e.g. ${show}`
|
|
28
|
-
: `<${name}> may be null: put it into a variable and show it with {if}, e.g. const ${variable} = ${name}, then ${show}`, tag);
|
|
22
|
+
// A path of fields is narrowed like a variable: `{if page.other != null { <page.other /> }}`.
|
|
23
|
+
this.error(`<${name}> may be null: show it with {if}, e.g. {if ${name} != null { <${name} /> }}`, tag);
|
|
29
24
|
}
|
|
30
25
|
else if (!isUntyped(type)) {
|
|
31
26
|
this.error(`<${name}> is ${typeToString(type)}, not a component: give it a type like comp(title string)`, tag);
|
|
@@ -58,6 +53,14 @@ export class ComponentValueChecker extends ComponentChecker {
|
|
|
58
53
|
this.error(`ref cannot point to <${name}>: its component is a value, so its element is not known in advance`, attribute);
|
|
59
54
|
continue;
|
|
60
55
|
}
|
|
56
|
+
// The type of the value does not say which properties are bound.
|
|
57
|
+
if (attributeName.startsWith('bind:')) {
|
|
58
|
+
const { value } = attribute;
|
|
59
|
+
if (value && value.kind !== 'EventHandler')
|
|
60
|
+
this.checkValue(value);
|
|
61
|
+
this.error(`<${name}> takes its component from a value, so it cannot bind its properties; pass a function, e.g. onChange={...}`, attribute.name);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
61
64
|
if (given.has(attributeName)) {
|
|
62
65
|
this.error(`duplicate property "${attributeName}"`, attribute.name);
|
|
63
66
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
2
|
import type { ComponentInfo } from './context.ts';
|
|
3
|
-
import {
|
|
3
|
+
import { ComponentBindingChecker } from './component-bindings.ts';
|
|
4
4
|
import { type Type } from './types.ts';
|
|
5
|
-
export declare abstract class ComponentChecker extends
|
|
5
|
+
export declare abstract class ComponentChecker extends ComponentBindingChecker {
|
|
6
6
|
protected resolveProps(info: ComponentInfo): void;
|
|
7
7
|
/** The body runs once where the component is used and must end with `return <markup>`. */
|
|
8
8
|
protected checkComponentBody(info: ComponentInfo): void;
|
|
@@ -3,10 +3,11 @@ import { freeNames } from "../names.js";
|
|
|
3
3
|
import { endlessRecursion } from "../recursion.js";
|
|
4
4
|
import { CONTENT } from "./dom.js";
|
|
5
5
|
import { lookupIn, ownStatements, isUntyped, callSignature } from "./helpers.js";
|
|
6
|
-
import {
|
|
6
|
+
import { ComponentBindingChecker } from "./component-bindings.js";
|
|
7
|
+
import { ATTRIBUTES } from "./forwarding.js";
|
|
7
8
|
import { BOOL, func, hasZeroValue, isAssignable, isNullable, nonNull, spreadFields, typesEqual, typeToString, UNKNOWN, } from "./types.js";
|
|
8
9
|
// Components: their bodies and uses, properties and spread, names they use from the module, recursion.
|
|
9
|
-
export class ComponentChecker extends
|
|
10
|
+
export class ComponentChecker extends ComponentBindingChecker {
|
|
10
11
|
resolveProps(info) {
|
|
11
12
|
for (const param of info.node.params) {
|
|
12
13
|
// `...attrs` takes the attributes that are not properties.
|
|
@@ -20,7 +21,7 @@ export class ComponentChecker extends ForwardingChecker {
|
|
|
20
21
|
if (info.props.has(name))
|
|
21
22
|
this.error(`duplicate property "${name}"`, param.name);
|
|
22
23
|
const optional = param.defaultValue !== null || isNullable(type) || name === 'children';
|
|
23
|
-
info.props.set(name, { type, optional });
|
|
24
|
+
info.props.set(name, { type, optional, ...(param.bind ? { bind: true } : {}) });
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
/** The body runs once where the component is used and must end with `return <markup>`. */
|
|
@@ -96,6 +97,10 @@ export class ComponentChecker extends ForwardingChecker {
|
|
|
96
97
|
this.checkRefAttribute(attribute, node);
|
|
97
98
|
continue;
|
|
98
99
|
}
|
|
100
|
+
if (name.startsWith('bind:')) {
|
|
101
|
+
this.checkPropBinding(attribute, info, tag.name, given);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
99
104
|
if (given.has(name))
|
|
100
105
|
this.error(`duplicate property "${name}"`, attribute.name);
|
|
101
106
|
given.add(name);
|
|
@@ -11,6 +11,8 @@ export interface ModuleExports {
|
|
|
11
11
|
decorators?: Map<string, DecoratorInfo>;
|
|
12
12
|
/** `export comp`: components are used as tags, not as values. */
|
|
13
13
|
components?: Map<string, ComponentInfo>;
|
|
14
|
+
/** `export state`: other modules read it, and change it only with the functions of its module. */
|
|
15
|
+
shared?: Set<string>;
|
|
14
16
|
}
|
|
15
17
|
/** What `importModule` found: the module's exports, an error for the import, or nothing (untyped). */
|
|
16
18
|
export type ImportResult = {
|
|
@@ -100,6 +102,8 @@ export interface Binding {
|
|
|
100
102
|
type: Type | null;
|
|
101
103
|
/** For components, which exist only at compile time and are used as tags. */
|
|
102
104
|
component?: ComponentInfo;
|
|
105
|
+
/** State of another module, `state items` there, which is read-only here: its module's path. */
|
|
106
|
+
shared?: string;
|
|
103
107
|
/**
|
|
104
108
|
* `const user, err = loadUser(id)` of `(User, error)`: the error has the values that are not null
|
|
105
109
|
* once it is null, and each of them has the error (error-results.ts).
|
|
@@ -113,6 +117,7 @@ export interface ComponentInfo {
|
|
|
113
117
|
props: Map<string, {
|
|
114
118
|
type: Type;
|
|
115
119
|
optional: boolean;
|
|
120
|
+
bind?: true;
|
|
116
121
|
}>;
|
|
117
122
|
/** Names from the module that the component's code uses, computed when first needed. */
|
|
118
123
|
freeNames: Set<string> | null;
|
|
@@ -160,6 +165,8 @@ export interface DecoratorInfo {
|
|
|
160
165
|
} | null;
|
|
161
166
|
/** The decorators after `needs` that exist; they are applied above it. */
|
|
162
167
|
needed: DecoratorInfo[];
|
|
168
|
+
/** The decorators after `includes` that exist: applied with it, above its own body. */
|
|
169
|
+
included: DecoratorInfo[];
|
|
163
170
|
/**
|
|
164
171
|
* For an exported decorator: the names of its module that its code uses, which other modules get
|
|
165
172
|
* through hidden exports, and the types of its expressions, for the code generator there.
|
|
@@ -170,9 +177,15 @@ export interface DecoratorInfo {
|
|
|
170
177
|
inline: InlinePackage | null;
|
|
171
178
|
}
|
|
172
179
|
export interface PublicMember {
|
|
173
|
-
kind: 'state' | 'derived' | 'const' | 'func';
|
|
180
|
+
kind: 'state' | 'derived' | 'const' | 'ref' | 'func';
|
|
174
181
|
type: Type;
|
|
175
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* The decorators applied with those they include, which come before them: `@shop` of
|
|
185
|
+
* `dec shop() includes visible, cart` gives visible, cart, shop. A cycle of includes is reported
|
|
186
|
+
* where the decorators are declared; here it stops.
|
|
187
|
+
*/
|
|
188
|
+
export declare function withIncluded(decorators: readonly DecoratorInfo[], seen?: ReadonlySet<DecoratorInfo>): DecoratorInfo[];
|
|
176
189
|
/** A `type` alias, resolved when first used. */
|
|
177
190
|
export interface AliasEntry {
|
|
178
191
|
kind: 'alias';
|
|
@@ -205,11 +218,21 @@ export interface ClassContext {
|
|
|
205
218
|
isStatic: boolean;
|
|
206
219
|
}
|
|
207
220
|
/**
|
|
208
|
-
*
|
|
209
|
-
*
|
|
221
|
+
* A variable, or a path of data fields from it that is narrowed like one: `order.delivery` is the
|
|
222
|
+
* binding of `order` with the path `"delivery"`. The path of the variable itself is empty.
|
|
223
|
+
*/
|
|
224
|
+
export interface Place {
|
|
225
|
+
binding: Binding;
|
|
226
|
+
path: string;
|
|
227
|
+
}
|
|
228
|
+
/** The same object for the same variable and path, so that it is a key of a `Flow`. */
|
|
229
|
+
export declare function placeOf(binding: Binding, path?: string): Place;
|
|
230
|
+
/**
|
|
231
|
+
* Types of variables and paths narrowed by the code before the current point, e.g. `?User` →
|
|
232
|
+
* `User` after `if user == null { return }`.
|
|
210
233
|
*/
|
|
211
|
-
export type Flow = Map<
|
|
212
|
-
export type Narrowing = [
|
|
234
|
+
export type Flow = Map<Place, Type>;
|
|
235
|
+
export type Narrowing = [Place, Type][];
|
|
213
236
|
export declare const NARROWABLE: ReadonlySet<BindingKind>;
|
|
214
237
|
/** Built-in globals. With a library, the untyped ones and the DOM types come from it instead. */
|
|
215
238
|
export declare function globalScope(library: Library | undefined): Scope;
|
package/dist/checker/context.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { GLOBAL_TYPES, GLOBAL_VALUES, LIBRARY_TYPES } from "./builtins.js";
|
|
2
2
|
import {} from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The decorators applied with those they include, which come before them: `@shop` of
|
|
5
|
+
* `dec shop() includes visible, cart` gives visible, cart, shop. A cycle of includes is reported
|
|
6
|
+
* where the decorators are declared; here it stops.
|
|
7
|
+
*/
|
|
8
|
+
export function withIncluded(decorators, seen = new Set()) {
|
|
9
|
+
return decorators.flatMap((each) => seen.has(each) ? [] : [...withIncluded(each.included, new Set([...seen, each])), each]);
|
|
10
|
+
}
|
|
3
11
|
export class Scope {
|
|
4
12
|
parent;
|
|
5
13
|
values = new Map();
|
|
@@ -8,6 +16,21 @@ export class Scope {
|
|
|
8
16
|
this.parent = parent;
|
|
9
17
|
}
|
|
10
18
|
}
|
|
19
|
+
const places = new WeakMap();
|
|
20
|
+
/** The same object for the same variable and path, so that it is a key of a `Flow`. */
|
|
21
|
+
export function placeOf(binding, path = '') {
|
|
22
|
+
let byPath = places.get(binding);
|
|
23
|
+
if (!byPath) {
|
|
24
|
+
byPath = new Map();
|
|
25
|
+
places.set(binding, byPath);
|
|
26
|
+
}
|
|
27
|
+
let place = byPath.get(path);
|
|
28
|
+
if (!place) {
|
|
29
|
+
place = { binding, path };
|
|
30
|
+
byPath.set(path, place);
|
|
31
|
+
}
|
|
32
|
+
return place;
|
|
33
|
+
}
|
|
11
34
|
export const NARROWABLE = new Set([
|
|
12
35
|
'let',
|
|
13
36
|
'const',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
|
-
import { type Binding, type
|
|
2
|
+
import { type Binding, type Narrowing } from './context.ts';
|
|
3
3
|
import { StatementChecker } from './statements.ts';
|
|
4
4
|
export declare abstract class ControlFlowChecker extends StatementChecker {
|
|
5
5
|
protected checkIf(node: ast.IfStatement): void;
|
|
@@ -15,16 +15,17 @@ export declare abstract class ControlFlowChecker extends StatementChecker {
|
|
|
15
15
|
* `switch filter { case "all": ... default: ... }`: in a case the discriminant has the literals
|
|
16
16
|
* of its tests, in `default` the literals of no case.
|
|
17
17
|
*/
|
|
18
|
-
protected narrowCase(node: ast.SwitchStatement, switchCase: ast.SwitchCase
|
|
18
|
+
protected narrowCase(node: ast.SwitchStatement, switchCase: ast.SwitchCase): Narrowing;
|
|
19
19
|
protected checkTry(node: ast.TryStatement): void;
|
|
20
20
|
protected checkCondition(node: ast.Expression): void;
|
|
21
|
-
/** Variables whose type is narrowed when `node` evaluates to `assumeTrue`. */
|
|
21
|
+
/** Variables and paths whose type is narrowed when `node` evaluates to `assumeTrue`. */
|
|
22
22
|
protected narrow(node: ast.Expression, assumeTrue: boolean): Narrowing;
|
|
23
23
|
/** `x instanceof Date`: an instance of the class when true, the rest of a union when false. */
|
|
24
24
|
protected narrowInstanceof(left: ast.Expression, right: ast.Expression, assumeTrue: boolean): Narrowing;
|
|
25
25
|
protected nullError(node: ast.Expression): void;
|
|
26
26
|
protected narrowableBinding(node: ast.Expression): Binding | undefined;
|
|
27
|
-
/** An assignment to a variable ends its narrowing. */
|
|
27
|
+
/** An assignment to a variable ends its narrowing; one to a path, that of the paths it changes. */
|
|
28
28
|
protected forget(target: ast.Expression): void;
|
|
29
|
-
|
|
29
|
+
/** Ends the narrowing of what `node` assigns: before a loop, or a statement with several ways. */
|
|
30
|
+
protected dropNarrowing(node: ast.Node): void;
|
|
30
31
|
}
|