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,51 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { type Type } from '../checker/types.ts';
|
|
3
|
+
import type { Reactive, Source, Write } from './reactive.ts';
|
|
4
|
+
import { StatementEmitter } from './statements.ts';
|
|
5
|
+
/**
|
|
6
|
+
* Updates after changes of component state. Each state variable gets an update function; every
|
|
7
|
+
* place that changes the variable calls it. Those places are written before it is known what the
|
|
8
|
+
* update needs, so they get a marker that is replaced at the end of the component (see
|
|
9
|
+
* resolveMarkers): by the call, by the only update statement, or by nothing.
|
|
10
|
+
*/
|
|
11
|
+
export declare abstract class UpdateEmitter extends StatementEmitter {
|
|
12
|
+
/** The component whose code is being written, if it has state or reactive properties. */
|
|
13
|
+
protected reactive: Reactive | null;
|
|
14
|
+
/** Inside event handlers, which cannot run before the markup exists. */
|
|
15
|
+
protected inHandler: number;
|
|
16
|
+
/**
|
|
17
|
+
* Inside code that runs while live markup is created or updated: its expressions, the functions
|
|
18
|
+
* they call and the callbacks they pass (`items.map(item => ...)`). Changes there do not update
|
|
19
|
+
* the markup, which would update it again; code that runs later (handlers) is not included.
|
|
20
|
+
*/
|
|
21
|
+
protected rendering: number;
|
|
22
|
+
/** After a statement that changes state, the update of what depends on it. */
|
|
23
|
+
protected statement(node: ast.Statement): void;
|
|
24
|
+
protected changesState(body: ast.Expression): boolean;
|
|
25
|
+
protected withRendering<T>(rendering: number, emit: () => T): T;
|
|
26
|
+
/** Records a place that changes a source and returns its marker; see resolveMarkers. */
|
|
27
|
+
protected write(source: Source, skip?: number | null): string;
|
|
28
|
+
/** The type from the checker; literal types count as their base types here (`"all"` → string). */
|
|
29
|
+
protected typeOf(node: ast.Expression): Type | undefined;
|
|
30
|
+
/** State that a statement changes, when it runs after the component's markup is created. */
|
|
31
|
+
private changedBy;
|
|
32
|
+
private updates;
|
|
33
|
+
/** Marker statements for the start of a branch, at `position` in the source. */
|
|
34
|
+
private markers;
|
|
35
|
+
/** `() => value`, evaluated by a helper when the markup is created and on updates. */
|
|
36
|
+
protected liveFunction(node: ast.Expression): string;
|
|
37
|
+
/**
|
|
38
|
+
* An expression that is evaluated again on updates: elements inside it are created in place,
|
|
39
|
+
* so that each evaluation creates new ones.
|
|
40
|
+
*/
|
|
41
|
+
protected liveExpression(node: ast.Expression, precedence?: number): string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Replaces the markers of a component's sources: by the call of the update function, by the only
|
|
45
|
+
* update statement, or by nothing when no markup depends on the source.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveMarkers(block: string, reactive: Reactive): string;
|
|
48
|
+
/** The update statements that a place changing the source needs, the computations first. */
|
|
49
|
+
export declare function needed(source: Source, write: Write): string[];
|
|
50
|
+
/** One place changes the source, after the markup exists, and needs one update statement. */
|
|
51
|
+
export declare function inlinesUpdate(source: Source): boolean;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { widenLiterals } from "../checker/types.js";
|
|
2
|
+
import { StatementEmitter } from "./statements.js";
|
|
3
|
+
import { startsWithObjectLiteral } from "./analysis.js";
|
|
4
|
+
import { ARROW } from "./syntax.js";
|
|
5
|
+
/**
|
|
6
|
+
* Updates after changes of component state. Each state variable gets an update function; every
|
|
7
|
+
* place that changes the variable calls it. Those places are written before it is known what the
|
|
8
|
+
* update needs, so they get a marker that is replaced at the end of the component (see
|
|
9
|
+
* resolveMarkers): by the call, by the only update statement, or by nothing.
|
|
10
|
+
*/
|
|
11
|
+
export class UpdateEmitter extends StatementEmitter {
|
|
12
|
+
/** The component whose code is being written, if it has state or reactive properties. */
|
|
13
|
+
reactive = null;
|
|
14
|
+
/** Inside event handlers, which cannot run before the markup exists. */
|
|
15
|
+
inHandler = 0;
|
|
16
|
+
/**
|
|
17
|
+
* Inside code that runs while live markup is created or updated: its expressions, the functions
|
|
18
|
+
* they call and the callbacks they pass (`items.map(item => ...)`). Changes there do not update
|
|
19
|
+
* the markup, which would update it again; code that runs later (handlers) is not included.
|
|
20
|
+
*/
|
|
21
|
+
rendering = 0;
|
|
22
|
+
/** After a statement that changes state, the update of what depends on it. */
|
|
23
|
+
statement(node) {
|
|
24
|
+
const { reactive } = this;
|
|
25
|
+
if (reactive && this.fn === reactive.setup && reactive.declaresRenderFunction(node)) {
|
|
26
|
+
this.withRendering(this.rendering + 1, () => super.statement(node));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// The body itself runs before the markup exists, but the derived values it changes stay right.
|
|
30
|
+
if (reactive && this.fn === reactive.setup && this.rendering === 0) {
|
|
31
|
+
const changed = reactive.writtenBy(node, (expression) => this.typeOf(expression));
|
|
32
|
+
super.statement(node);
|
|
33
|
+
for (const source of changed) {
|
|
34
|
+
for (const computation of source.computations)
|
|
35
|
+
this.line(computation);
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const written = this.changedBy(node);
|
|
40
|
+
if (written.length === 0) {
|
|
41
|
+
super.statement(node);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
switch (node.kind) {
|
|
45
|
+
case 'ReturnStatement':
|
|
46
|
+
case 'ThrowStatement': {
|
|
47
|
+
// The update goes between computing the value and leaving the function.
|
|
48
|
+
const values = node.kind === 'ThrowStatement' ? [node.argument] : node.values;
|
|
49
|
+
const temp = this.temp();
|
|
50
|
+
this.withHoisting(true, () => {
|
|
51
|
+
const rendered = values.map((value) => this.expression(value, ARROW));
|
|
52
|
+
const value = rendered.length === 1 ? rendered[0] : `[${rendered.join(', ')}]`;
|
|
53
|
+
this.line(`const ${temp} = ${value};`);
|
|
54
|
+
});
|
|
55
|
+
this.updates(written);
|
|
56
|
+
this.line(`${node.kind === 'ThrowStatement' ? 'throw' : 'return'} ${temp};`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
case 'IfStatement':
|
|
60
|
+
case 'ForStatement':
|
|
61
|
+
case 'ForInStatement':
|
|
62
|
+
case 'SwitchStatement':
|
|
63
|
+
// The change is in a condition: update at the start of every branch, before anything in
|
|
64
|
+
// it can leave the function, and after the statement.
|
|
65
|
+
super.statement(withFirst(node, (position) => this.markers(written, position)));
|
|
66
|
+
this.updates(written);
|
|
67
|
+
return;
|
|
68
|
+
default:
|
|
69
|
+
super.statement(node);
|
|
70
|
+
this.updates(written);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
changesState(body) {
|
|
74
|
+
return this.changedBy(body).length > 0;
|
|
75
|
+
}
|
|
76
|
+
withRendering(rendering, emit) {
|
|
77
|
+
const saved = this.rendering;
|
|
78
|
+
this.rendering = rendering;
|
|
79
|
+
try {
|
|
80
|
+
return emit();
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
this.rendering = saved;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Records a place that changes a source and returns its marker; see resolveMarkers. */
|
|
87
|
+
write(source, skip = null) {
|
|
88
|
+
source.writes.push({ early: this.inHandler === 0, skip });
|
|
89
|
+
return `\uE000${source.id}${skip === null ? '' : `:${skip}`}\uE001`;
|
|
90
|
+
}
|
|
91
|
+
/** The type from the checker; literal types count as their base types here (`"all"` → string). */
|
|
92
|
+
typeOf(node) {
|
|
93
|
+
const type = this.options.types?.get(node);
|
|
94
|
+
return type && widenLiterals(type);
|
|
95
|
+
}
|
|
96
|
+
/** State that a statement changes, when it runs after the component's markup is created. */
|
|
97
|
+
changedBy(node) {
|
|
98
|
+
const { reactive } = this;
|
|
99
|
+
if (!reactive || this.fn === reactive.setup || this.rendering > 0)
|
|
100
|
+
return [];
|
|
101
|
+
return [...reactive.writtenBy(node, (expression) => this.typeOf(expression))];
|
|
102
|
+
}
|
|
103
|
+
updates(sources) {
|
|
104
|
+
for (const source of sources)
|
|
105
|
+
this.line(`${this.write(source)};`);
|
|
106
|
+
}
|
|
107
|
+
/** Marker statements for the start of a branch, at `position` in the source. */
|
|
108
|
+
markers(sources, position) {
|
|
109
|
+
return sources.map((source) => {
|
|
110
|
+
const expression = {
|
|
111
|
+
kind: 'Identifier',
|
|
112
|
+
name: this.write(source),
|
|
113
|
+
start: position,
|
|
114
|
+
end: position,
|
|
115
|
+
};
|
|
116
|
+
return { kind: 'ExpressionStatement', expression, start: position, end: position };
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/** `() => value`, evaluated by a helper when the markup is created and on updates. */
|
|
120
|
+
liveFunction(node) {
|
|
121
|
+
const text = this.liveExpression(node);
|
|
122
|
+
return `() => ${startsWithObjectLiteral(node) ? `(${text})` : text}`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* An expression that is evaluated again on updates: elements inside it are created in place,
|
|
126
|
+
* so that each evaluation creates new ones.
|
|
127
|
+
*/
|
|
128
|
+
liveExpression(node, precedence = ARROW) {
|
|
129
|
+
return this.withRendering(this.rendering + 1, () => this.withHoisting(false, () => this.expression(node, precedence)));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Replaces the markers of a component's sources: by the call of the update function, by the only
|
|
134
|
+
* update statement, or by nothing when no markup depends on the source.
|
|
135
|
+
*/
|
|
136
|
+
export function resolveMarkers(block, reactive) {
|
|
137
|
+
const sources = new Map();
|
|
138
|
+
for (const source of reactive.sources.values())
|
|
139
|
+
sources.set(source.id, source);
|
|
140
|
+
return block.replace(/^([ \t]*)\uE000(\d+)(?::(\d+))?\uE001;\n/gm, (line, indent, id, skip) => {
|
|
141
|
+
const source = sources.get(Number(id));
|
|
142
|
+
if (!source)
|
|
143
|
+
return line;
|
|
144
|
+
const dependents = needed(source, { early: false, skip: skip ? Number(skip) : null });
|
|
145
|
+
if (dependents.length === 0)
|
|
146
|
+
return '';
|
|
147
|
+
const text = inlinesUpdate(source) ? dependents[0] : `${source.update}();`;
|
|
148
|
+
return `${indent}${text}\n`;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/** The update statements that a place changing the source needs, the computations first. */
|
|
152
|
+
export function needed(source, write) {
|
|
153
|
+
return [...source.computations, ...source.dependents.filter((_, i) => i !== write.skip)];
|
|
154
|
+
}
|
|
155
|
+
/** One place changes the source, after the markup exists, and needs one update statement. */
|
|
156
|
+
export function inlinesUpdate(source) {
|
|
157
|
+
const [write] = source.writes;
|
|
158
|
+
if (source.writes.length !== 1 || write.early)
|
|
159
|
+
return false;
|
|
160
|
+
const dependents = needed(source, write);
|
|
161
|
+
return dependents.length === 1 && !dependents[0].includes('\n');
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* A copy of an if/for/switch statement with statements added at the start of every branch or
|
|
165
|
+
* loop body; `first` gets the position where they go.
|
|
166
|
+
*/
|
|
167
|
+
function withFirst(node, first) {
|
|
168
|
+
const prepend = (body, end) => [
|
|
169
|
+
...first(body[0]?.start ?? end),
|
|
170
|
+
...body,
|
|
171
|
+
];
|
|
172
|
+
const block = (node) => ({
|
|
173
|
+
...node,
|
|
174
|
+
body: prepend(node.body, node.end - 1),
|
|
175
|
+
});
|
|
176
|
+
switch (node.kind) {
|
|
177
|
+
case 'IfStatement': {
|
|
178
|
+
const { alternate } = node;
|
|
179
|
+
return {
|
|
180
|
+
...node,
|
|
181
|
+
consequent: block(node.consequent),
|
|
182
|
+
alternate: alternate === null
|
|
183
|
+
? null
|
|
184
|
+
: alternate.kind === 'IfStatement'
|
|
185
|
+
? withFirst(alternate, first)
|
|
186
|
+
: block(alternate),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
case 'ForStatement':
|
|
190
|
+
case 'ForInStatement':
|
|
191
|
+
return { ...node, body: block(node.body) };
|
|
192
|
+
case 'SwitchStatement':
|
|
193
|
+
return {
|
|
194
|
+
...node,
|
|
195
|
+
cases: node.cases.map((switchCase) => ({
|
|
196
|
+
...switchCase,
|
|
197
|
+
body: prepend(switchCase.body, switchCase.end),
|
|
198
|
+
})),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { ComponentEmitter } from './components.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Web components: `@html-tag comp Card(count number) { ... }` is a function component, and also the
|
|
5
|
+
* class of a custom element `<card>`, defined at the end of the module, when the names it uses
|
|
6
|
+
* have their values.
|
|
7
|
+
*
|
|
8
|
+
* The element renders the component into its shadow root when it is connected. When it is
|
|
9
|
+
* disconnected and not put back in the same task, the component is removed: the functions
|
|
10
|
+
* returned by `mount()` run. Moving the element keeps its markup.
|
|
11
|
+
*
|
|
12
|
+
* Every property except `children` is a JS property of the element; its setter updates the markup.
|
|
13
|
+
* Strings, numbers and bools also have attributes: `item-count="3"` sets `itemCount` to 3, and a
|
|
14
|
+
* bool attribute sets true by its presence. A removed attribute leaves the default value.
|
|
15
|
+
* `children` is a `<slot>`: what is inside the element is shown there.
|
|
16
|
+
*/
|
|
17
|
+
export declare abstract class WebComponentEmitter extends ComponentEmitter {
|
|
18
|
+
protected webComponentClasses(): void;
|
|
19
|
+
protected webComponentClass(component: ast.ComponentDeclaration): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { attributeKind, attributeName, htmlTagName } from "../html-tag.js";
|
|
2
|
+
import { ComponentEmitter } from "./components.js";
|
|
3
|
+
/**
|
|
4
|
+
* Web components: `@html-tag comp Card(count number) { ... }` is a function component, and also the
|
|
5
|
+
* class of a custom element `<card>`, defined at the end of the module, when the names it uses
|
|
6
|
+
* have their values.
|
|
7
|
+
*
|
|
8
|
+
* The element renders the component into its shadow root when it is connected. When it is
|
|
9
|
+
* disconnected and not put back in the same task, the component is removed: the functions
|
|
10
|
+
* returned by `mount()` run. Moving the element keeps its markup.
|
|
11
|
+
*
|
|
12
|
+
* Every property except `children` is a JS property of the element; its setter updates the markup.
|
|
13
|
+
* Strings, numbers and bools also have attributes: `item-count="3"` sets `itemCount` to 3, and a
|
|
14
|
+
* bool attribute sets true by its presence. A removed attribute leaves the default value.
|
|
15
|
+
* `children` is a `<slot>`: what is inside the element is shown there.
|
|
16
|
+
*/
|
|
17
|
+
export class WebComponentEmitter extends ComponentEmitter {
|
|
18
|
+
webComponentClasses() {
|
|
19
|
+
for (const component of this.components.values()) {
|
|
20
|
+
if (component.htmlTag)
|
|
21
|
+
this.webComponentClass(component);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
webComponentClass(component) {
|
|
25
|
+
this.helpers.add('owner');
|
|
26
|
+
const name = component.name.name;
|
|
27
|
+
const className = `$$${name}Element`;
|
|
28
|
+
const props = component.params.filter((param) => param.name.name !== 'children');
|
|
29
|
+
const kinds = props.map((param) => attributeKind(param.type, this.typeAliases));
|
|
30
|
+
const observed = props
|
|
31
|
+
.filter((_, i) => kinds[i] !== null)
|
|
32
|
+
.map((param) => JSON.stringify(attributeName(param.name.name)));
|
|
33
|
+
// A missing property: undefined gives the default value, others get their zero value.
|
|
34
|
+
const value = (param) => param.defaultValue
|
|
35
|
+
? `values.${param.name.name}`
|
|
36
|
+
: `values.${param.name.name} ?? ${(param.type && this.zeroValue(param.type)) ?? 'undefined'}`;
|
|
37
|
+
// The properties come from `#args()`, in the order of the setter; `children` is a slot.
|
|
38
|
+
const slot = component.params.findIndex((param) => param.name.name === 'children');
|
|
39
|
+
let prepare = null;
|
|
40
|
+
const args = props.length > 0 ? ['...this.#args()'] : [];
|
|
41
|
+
if (slot === props.length) {
|
|
42
|
+
args.push('document.createElement("slot")');
|
|
43
|
+
}
|
|
44
|
+
else if (slot >= 0) {
|
|
45
|
+
prepare = `args.splice(${slot}, 0, document.createElement("slot"));`;
|
|
46
|
+
args[0] = '...args';
|
|
47
|
+
}
|
|
48
|
+
const names = props.map((param) => JSON.stringify(param.name.name)).join(', ');
|
|
49
|
+
const body = this.capture(() => {
|
|
50
|
+
if (observed.length > 0)
|
|
51
|
+
this.line(`static observedAttributes = [${observed.join(', ')}];`);
|
|
52
|
+
this.line('#values = {};');
|
|
53
|
+
this.line('#update = null;');
|
|
54
|
+
this.line('#remove = null;');
|
|
55
|
+
this.lines.push('');
|
|
56
|
+
this.line('constructor() {');
|
|
57
|
+
this.line(' super();');
|
|
58
|
+
this.line(' this.attachShadow({ mode: "open" });');
|
|
59
|
+
if (props.length > 0) {
|
|
60
|
+
// A property set before the class was defined hides its setter.
|
|
61
|
+
this.line(` for (const name of [${names}]) {`);
|
|
62
|
+
this.line(' if (!Object.hasOwn(this, name)) continue;');
|
|
63
|
+
this.line(' const value = this[name];');
|
|
64
|
+
this.line(' delete this[name];');
|
|
65
|
+
this.line(' this[name] = value;');
|
|
66
|
+
this.line(' }');
|
|
67
|
+
}
|
|
68
|
+
this.line('}');
|
|
69
|
+
this.lines.push('');
|
|
70
|
+
this.line('connectedCallback() {');
|
|
71
|
+
this.line(' if (this.#remove) return;');
|
|
72
|
+
if (prepare) {
|
|
73
|
+
this.line(' const args = this.#args();');
|
|
74
|
+
this.line(` ${prepare}`);
|
|
75
|
+
}
|
|
76
|
+
this.line(` const [[node, update], remove] = $$owned(() => $$${name}(${args.join(', ')}));`);
|
|
77
|
+
this.line(' this.#update = update ?? null;');
|
|
78
|
+
this.line(' this.#remove = remove;');
|
|
79
|
+
// The styles of the page do not reach a shadow root: it gets the CSS of the module.
|
|
80
|
+
if (this.usedStyles.size > 0) {
|
|
81
|
+
this.line(' const style = document.createElement("style");');
|
|
82
|
+
this.line(' style.textContent = $$styles.join("\\n");');
|
|
83
|
+
this.line(' this.shadowRoot.append(style, node);');
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
this.line(' this.shadowRoot.append(node);');
|
|
87
|
+
}
|
|
88
|
+
this.line('}');
|
|
89
|
+
this.lines.push('');
|
|
90
|
+
this.line('disconnectedCallback() {');
|
|
91
|
+
this.line(' queueMicrotask(() => {');
|
|
92
|
+
this.line(' if (this.isConnected || !this.#remove) return;');
|
|
93
|
+
this.line(' this.#remove();');
|
|
94
|
+
this.line(' this.#update = this.#remove = null;');
|
|
95
|
+
this.line(' this.shadowRoot.replaceChildren();');
|
|
96
|
+
this.line(' });');
|
|
97
|
+
this.line('}');
|
|
98
|
+
if (observed.length > 0) {
|
|
99
|
+
this.lines.push('');
|
|
100
|
+
this.line('attributeChangedCallback(name, old, value) {');
|
|
101
|
+
this.line(' switch (name) {');
|
|
102
|
+
props.forEach((param, i) => {
|
|
103
|
+
const kind = kinds[i];
|
|
104
|
+
if (!kind)
|
|
105
|
+
return;
|
|
106
|
+
const converted = kind === 'string'
|
|
107
|
+
? 'value ?? undefined'
|
|
108
|
+
: kind === 'number'
|
|
109
|
+
? 'value === null ? undefined : Number(value)'
|
|
110
|
+
: 'value === null ? undefined : true';
|
|
111
|
+
this.line(` case ${JSON.stringify(attributeName(param.name.name))}:`);
|
|
112
|
+
this.line(` this.${param.name.name} = ${converted};`);
|
|
113
|
+
this.line(' break;');
|
|
114
|
+
});
|
|
115
|
+
this.line(' }');
|
|
116
|
+
this.line('}');
|
|
117
|
+
}
|
|
118
|
+
for (const param of props) {
|
|
119
|
+
const prop = param.name.name;
|
|
120
|
+
this.lines.push('');
|
|
121
|
+
this.line(`get ${prop}() {`);
|
|
122
|
+
this.line(` return this.#values.${prop};`);
|
|
123
|
+
this.line('}');
|
|
124
|
+
this.lines.push('');
|
|
125
|
+
this.line(`set ${prop}(value) {`);
|
|
126
|
+
this.line(` this.#values.${prop} = value;`);
|
|
127
|
+
this.line(' this.#update?.(...this.#args());');
|
|
128
|
+
this.line('}');
|
|
129
|
+
}
|
|
130
|
+
if (props.length > 0) {
|
|
131
|
+
this.lines.push('');
|
|
132
|
+
this.line('#args() {');
|
|
133
|
+
this.line(' const values = this.#values;');
|
|
134
|
+
this.line(' return [');
|
|
135
|
+
for (const param of props)
|
|
136
|
+
this.line(` ${value(param)},`);
|
|
137
|
+
this.line(' ];');
|
|
138
|
+
this.line('}');
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
this.blankLine();
|
|
142
|
+
this.line(`class ${className} extends HTMLElement {`);
|
|
143
|
+
this.lines.push(...body);
|
|
144
|
+
this.line('}');
|
|
145
|
+
this.lines.push('');
|
|
146
|
+
const tag = JSON.stringify(htmlTagName(component));
|
|
147
|
+
this.line(`customElements.define(${tag}, ${className});`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CompileResult } from './compile.ts';
|
|
2
|
+
/** Compile errors, already formatted for the terminal. */
|
|
3
|
+
export declare class CompileError extends Error {
|
|
4
|
+
}
|
|
5
|
+
/** Compiles a file; prints its warnings unless `quiet`, throws a CompileError on errors. */
|
|
6
|
+
export declare function compileFile(path: string, options: {
|
|
7
|
+
rewriteImports: boolean;
|
|
8
|
+
typeCheck: boolean;
|
|
9
|
+
markup?: boolean;
|
|
10
|
+
quiet?: boolean;
|
|
11
|
+
}): CompileResult;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { formatDiagnostic } from "./diagnostics.js";
|
|
3
|
+
import { compile } from "./index.js";
|
|
4
|
+
import { SourceFile } from "./source.js";
|
|
5
|
+
// Compiling a file for the CLI: errors become one exception with the messages for the terminal.
|
|
6
|
+
/** Compile errors, already formatted for the terminal. */
|
|
7
|
+
export class CompileError extends Error {
|
|
8
|
+
}
|
|
9
|
+
/** Compiles a file; prints its warnings unless `quiet`, throws a CompileError on errors. */
|
|
10
|
+
export function compileFile(path, options) {
|
|
11
|
+
const source = readFileSync(path, 'utf8');
|
|
12
|
+
const { quiet, ...compileOptions } = options;
|
|
13
|
+
const result = compile(source, { filename: path, ...compileOptions });
|
|
14
|
+
const file = new SourceFile(path, source);
|
|
15
|
+
if (!quiet) {
|
|
16
|
+
for (const warning of result.warnings) {
|
|
17
|
+
console.warn(`${formatDiagnostic(file, warning, 'warning')}\n`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (result.diagnostics.length > 0) {
|
|
21
|
+
throw new CompileError(result.diagnostics.map((d) => formatDiagnostic(file, d)).join('\n\n'));
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type * as ast from './ast.ts';
|
|
2
|
+
import { type CheckOptions, type ImportResult, type Library } from './checker/checker.ts';
|
|
3
|
+
import type { Diagnostic } from './diagnostics.ts';
|
|
4
|
+
import { type StyleModule } from './style-modules.ts';
|
|
5
|
+
export interface CoreCompileOptions {
|
|
6
|
+
/** Replace `.rk` with `.js` in relative import paths. Defaults to `true`. */
|
|
7
|
+
rewriteImports?: boolean;
|
|
8
|
+
/** Check types before generating code. Defaults to `true`. */
|
|
9
|
+
typeCheck?: boolean;
|
|
10
|
+
/** Markup and components; read unless false (the entries decide it, see src/config.ts). */
|
|
11
|
+
markup?: boolean;
|
|
12
|
+
/** The exports of an imported `.rk` module; without it, imports are untyped (`any`). */
|
|
13
|
+
importModule?: ((specifier: string) => ImportResult) | undefined;
|
|
14
|
+
/** The exports of other imports, from TypeScript declarations; without it, they are untyped. */
|
|
15
|
+
importDeclarations?: ((specifier: string) => ImportResult) | undefined;
|
|
16
|
+
/** Types of the standard library and the DOM beyond the built-in ones. */
|
|
17
|
+
library?: Library | undefined;
|
|
18
|
+
/** Also write a TypeScript declaration file; needs type checking. */
|
|
19
|
+
declarations?: boolean;
|
|
20
|
+
/** The text of an imported CSS module, `./button.module.css`, or null if it cannot be read. */
|
|
21
|
+
readStyleModule?: ((specifier: string) => string | null) | undefined;
|
|
22
|
+
/** Where the worker module of `go` is, relative to the module; see workerUrl(). */
|
|
23
|
+
workerUrl?: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Where the worker module of a module is: a `.worker.js` next to its `.js`, or, where `.rk`
|
|
27
|
+
* files are imported as they are (`rook run`), the module itself with `?goroutines`. Vite drops
|
|
28
|
+
* the query of a worker's URL, so its plugin gives another path (vite.ts).
|
|
29
|
+
*/
|
|
30
|
+
export declare function workerUrl(file: string, rewriteImports: boolean): string;
|
|
31
|
+
export interface CompileResult {
|
|
32
|
+
/** The generated ES module; empty when there are diagnostics. */
|
|
33
|
+
code: string;
|
|
34
|
+
diagnostics: Diagnostic[];
|
|
35
|
+
/** Problems that do not stop compilation, sorted by position. */
|
|
36
|
+
warnings: Diagnostic[];
|
|
37
|
+
/** Relative paths of the `.rk` modules and CSS modules this file imports, as written. */
|
|
38
|
+
dependencies: string[];
|
|
39
|
+
/** The `.d.ts` for the module, with the `declarations` option. */
|
|
40
|
+
declarations?: string;
|
|
41
|
+
/** The worker module that runs the functions `go` calls, when the module has `go`. */
|
|
42
|
+
worker?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare function compileModule(source: string, options?: CoreCompileOptions): CompileResult;
|
|
45
|
+
/**
|
|
46
|
+
* The options of the checker for a program: its imports, with the CSS modules it imports, which
|
|
47
|
+
* are read and parsed here (or what is wrong with them), and the library.
|
|
48
|
+
*/
|
|
49
|
+
export declare function prepareCheck(program: ast.Program, options: CoreCompileOptions): {
|
|
50
|
+
styleModules: Map<string, StyleModule>;
|
|
51
|
+
problems: Diagnostic[];
|
|
52
|
+
checkOptions: CheckOptions;
|
|
53
|
+
};
|