rooklang 0.3.4 → 0.4.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/README.md +33 -2
- package/README.ru.md +33 -2
- 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/bun-register.d.ts +1 -0
- package/dist/bun-register.js +9 -0
- package/dist/bun.d.ts +34 -0
- package/dist/bun.js +99 -0
- 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 +24 -7
- 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-file.d.ts +4 -1
- package/dist/compile.d.ts +1 -1
- 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/index.d.ts +1 -1
- package/dist/inline-packages.d.ts +2 -1
- package/dist/inline-packages.js +9 -4
- package/dist/loader.js +6 -19
- 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/run-cache.d.ts +7 -0
- package/dist/run-cache.js +24 -0
- package/dist/run-file.js +6 -1
- package/dist/runtime/sources.js +4 -3
- package/dist/walk.js +3 -3
- package/package.json +10 -2
- package/src/runtime/decode.js +41 -10
- package/src/runtime/go.js +10 -4
- package/src/runtime/index.js +1 -0
- package/src/runtime/markup.js +27 -17
- package/src/runtime/shared.js +17 -0
package/dist/codegen/updates.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { widenLiterals } from "../checker/types.js";
|
|
2
|
+
import { updatesOf } from "./reactive.js";
|
|
2
3
|
import { StatementEmitter } from "./statements.js";
|
|
3
4
|
import { startsWithObjectLiteral } from "./analysis.js";
|
|
5
|
+
import { sharedWrites } from "./shared.js";
|
|
4
6
|
import { ARROW } from "./syntax.js";
|
|
5
7
|
/**
|
|
6
8
|
* Updates after changes of component state. Each state variable gets an update function; every
|
|
@@ -19,8 +21,37 @@ export class UpdateEmitter extends StatementEmitter {
|
|
|
19
21
|
* the markup, which would update it again; code that runs later (handlers) is not included.
|
|
20
22
|
*/
|
|
21
23
|
rendering = 0;
|
|
22
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* After a statement that changes state of the module, the updates of the components that read
|
|
26
|
+
* it; code that renders markup changes none, as with the state of a component.
|
|
27
|
+
*/
|
|
23
28
|
statement(node) {
|
|
29
|
+
const shared = this.rendering > 0 ? [] : sharedWrites(node, this.shared, (each) => this.typeOf(each));
|
|
30
|
+
if (shared.length === 0) {
|
|
31
|
+
this.statementWithUpdates(node);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const notify = () => {
|
|
35
|
+
for (const name of shared)
|
|
36
|
+
this.line(`$$changed(${this.shared.listeners.get(name)});`);
|
|
37
|
+
};
|
|
38
|
+
if (node.kind !== 'ReturnStatement' && node.kind !== 'ThrowStatement') {
|
|
39
|
+
this.statementWithUpdates(node);
|
|
40
|
+
notify();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// The value is computed first, then the updates run, then the function leaves.
|
|
44
|
+
const values = node.kind === 'ThrowStatement' ? [node.argument] : node.values;
|
|
45
|
+
const temp = this.temp();
|
|
46
|
+
this.withHoisting(true, () => {
|
|
47
|
+
const rendered = values.map((value) => this.expression(value, ARROW));
|
|
48
|
+
this.line(`const ${temp} = ${rendered.length === 1 ? rendered[0] : `[${rendered.join(', ')}]`};`);
|
|
49
|
+
});
|
|
50
|
+
notify();
|
|
51
|
+
this.line(`${node.kind === 'ThrowStatement' ? 'throw' : 'return'} ${temp};`);
|
|
52
|
+
}
|
|
53
|
+
/** After a statement that changes state, the update of what depends on it. */
|
|
54
|
+
statementWithUpdates(node) {
|
|
24
55
|
const { reactive } = this;
|
|
25
56
|
if (reactive && this.fn === reactive.setup && reactive.declaresRenderFunction(node)) {
|
|
26
57
|
this.withRendering(this.rendering + 1, () => super.statement(node));
|
|
@@ -33,6 +64,9 @@ export class UpdateEmitter extends StatementEmitter {
|
|
|
33
64
|
for (const source of changed) {
|
|
34
65
|
for (const computation of source.computations)
|
|
35
66
|
this.line(computation);
|
|
67
|
+
// The parent of a bound property gets the value the body gave it.
|
|
68
|
+
if (source.bound)
|
|
69
|
+
this.line(source.bound);
|
|
36
70
|
}
|
|
37
71
|
return;
|
|
38
72
|
}
|
|
@@ -137,7 +171,14 @@ export function resolveMarkers(block, reactive) {
|
|
|
137
171
|
const sources = new Map();
|
|
138
172
|
for (const source of reactive.sources.values())
|
|
139
173
|
sources.set(source.id, source);
|
|
140
|
-
|
|
174
|
+
// `$$watch(...)` of state of the module stays if something depends on it.
|
|
175
|
+
const listened = block.replace(/^([ \t]*)\uE004(\d+)\uE004(.*)\n/gm, (line, indent, id, text) => {
|
|
176
|
+
const source = sources.get(Number(id));
|
|
177
|
+
if (!source)
|
|
178
|
+
return line;
|
|
179
|
+
return updatesOf(source).length > 0 ? `${indent}${text}\n` : '';
|
|
180
|
+
});
|
|
181
|
+
return listened.replace(/^([ \t]*)\uE000(\d+)(?::(\d+))?\uE001;\n/gm, (line, indent, id, skip) => {
|
|
141
182
|
const source = sources.get(Number(id));
|
|
142
183
|
if (!source)
|
|
143
184
|
return line;
|
|
@@ -148,9 +189,16 @@ export function resolveMarkers(block, reactive) {
|
|
|
148
189
|
return `${indent}${text}\n`;
|
|
149
190
|
});
|
|
150
191
|
}
|
|
151
|
-
/**
|
|
192
|
+
/**
|
|
193
|
+
* The update statements that a place changing the source needs: the computations, the call that
|
|
194
|
+
* gives a bound property to the parent, then the updates of the nodes.
|
|
195
|
+
*/
|
|
152
196
|
export function needed(source, write) {
|
|
153
|
-
return [
|
|
197
|
+
return [
|
|
198
|
+
...source.computations,
|
|
199
|
+
...(source.bound ? [source.bound] : []),
|
|
200
|
+
...source.dependents.filter((_, i) => i !== write.skip),
|
|
201
|
+
];
|
|
154
202
|
}
|
|
155
203
|
/** One place changes the source, after the markup exists, and needs one update statement. */
|
|
156
204
|
export function inlinesUpdate(source) {
|
|
@@ -6,8 +6,8 @@ import { ComponentEmitter } from './components.ts';
|
|
|
6
6
|
* have their values.
|
|
7
7
|
*
|
|
8
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:
|
|
10
|
-
*
|
|
9
|
+
* disconnected and not put back in the same task, the component is removed: what its
|
|
10
|
+
* `mount` deferred runs. Moving the element keeps its markup.
|
|
11
11
|
*
|
|
12
12
|
* Every property except `children` is a JS property of the element; its setter updates the markup.
|
|
13
13
|
* Strings, numbers and bools also have attributes: `item-count="3"` sets `itemCount` to 3, and a
|
|
@@ -6,8 +6,8 @@ import { ComponentEmitter } from "./components.js";
|
|
|
6
6
|
* have their values.
|
|
7
7
|
*
|
|
8
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:
|
|
10
|
-
*
|
|
9
|
+
* disconnected and not put back in the same task, the component is removed: what its
|
|
10
|
+
* `mount` deferred runs. Moving the element keeps its markup.
|
|
11
11
|
*
|
|
12
12
|
* Every property except `children` is a JS property of the element; its setter updates the markup.
|
|
13
13
|
* Strings, numbers and bools also have attributes: `item-count="3"` sets `itemCount` to 3, and a
|
package/dist/compile-file.d.ts
CHANGED
|
@@ -6,6 +6,9 @@ export declare class CompileError extends Error {
|
|
|
6
6
|
export declare function compileFile(path: string, options: {
|
|
7
7
|
rewriteImports: boolean;
|
|
8
8
|
typeCheck: boolean;
|
|
9
|
-
markup?: boolean;
|
|
9
|
+
markup?: boolean | undefined;
|
|
10
10
|
quiet?: boolean;
|
|
11
|
+
/** Where the worker module of `go` is, and what its worker imports first (bun.ts). */
|
|
12
|
+
workerUrl?: string;
|
|
13
|
+
nodeLoader?: string;
|
|
11
14
|
}): CompileResult;
|
package/dist/compile.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface CoreCompileOptions {
|
|
|
23
23
|
readStyleModule?: ((specifier: string) => string | null) | undefined;
|
|
24
24
|
/** Where the worker module of `go` is, relative to the module; see workerUrl(). */
|
|
25
25
|
workerUrl?: string | undefined;
|
|
26
|
-
/** A module that a worker
|
|
26
|
+
/** A module that a worker of Node or Bun imports first, to load `.rk` files (vite.ts, bun.ts). */
|
|
27
27
|
nodeLoader?: string | undefined;
|
|
28
28
|
/**
|
|
29
29
|
* How the code gets the runtime of Rook: `import`, the default, imports it from
|
package/dist/compile.js
CHANGED
|
@@ -102,11 +102,12 @@ export function prepareCheck(program, options) {
|
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
104
|
* What the module imports from other `.rk` modules for the generator: decorators, whose code is
|
|
105
|
-
* inlined,
|
|
105
|
+
* inlined, components, which are called or inlined, and state, whose changes it listens to.
|
|
106
106
|
*/
|
|
107
107
|
function importedCode(program, importModule) {
|
|
108
108
|
const found = new Map();
|
|
109
109
|
const components = new Map();
|
|
110
|
+
const shared = new Map();
|
|
110
111
|
for (const statement of program.body) {
|
|
111
112
|
if (statement.kind !== 'ImportDeclaration' || !statement.source.value.endsWith('.rk')) {
|
|
112
113
|
continue;
|
|
@@ -116,6 +117,9 @@ function importedCode(program, importModule) {
|
|
|
116
117
|
const decorators = exports?.decorators;
|
|
117
118
|
for (const specifier of statement.namedImports) {
|
|
118
119
|
const component = exports?.components?.get(specifier.imported.name);
|
|
120
|
+
if (exports?.shared?.has(specifier.imported.name)) {
|
|
121
|
+
shared.set(specifier.local.name, specifier.imported.name);
|
|
122
|
+
}
|
|
119
123
|
if (component) {
|
|
120
124
|
components.set(specifier.local.name, {
|
|
121
125
|
node: component.node,
|
|
@@ -135,7 +139,7 @@ function importedCode(program, importModule) {
|
|
|
135
139
|
});
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
|
-
return { decorators: found, components };
|
|
142
|
+
return { decorators: found, components, shared };
|
|
139
143
|
}
|
|
140
144
|
/** The CSS modules that the program imports, read and parsed, or what is wrong with them. */
|
|
141
145
|
function readStyleModules(program, read) {
|
package/dist/decorator-copy.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type CopyListener = (copy: ast.Node, original: ast.Node) => void;
|
|
|
5
5
|
export declare function originOf(node: ast.Node): ast.Node;
|
|
6
6
|
/**
|
|
7
7
|
* Renames of the names that are not values: tags of components, `<Icon />`, and decorators,
|
|
8
|
-
*
|
|
8
|
+
* `@visible.shown`. Code of another module refers to those of its own module.
|
|
9
9
|
*/
|
|
10
10
|
export interface OtherNames {
|
|
11
11
|
tags?: ReadonlyMap<string, string>;
|
package/dist/decorator-copy.js
CHANGED
|
@@ -143,6 +143,7 @@ export function renamer(renames, copied, other = {}) {
|
|
|
143
143
|
return done({
|
|
144
144
|
...node,
|
|
145
145
|
iterable: copy(node.iterable, hidden),
|
|
146
|
+
...(node.withKey ? { withKey: copy(node.withKey, scoped(names, hidden)) } : {}),
|
|
146
147
|
body: copy(node.body, scoped(names, hidden)),
|
|
147
148
|
}, node);
|
|
148
149
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type * as ast from './ast.ts';
|
|
2
|
+
import type { DecoratorSource } from './decorators.ts';
|
|
3
|
+
/** The JS name of a member or a parameter of a decorator, in the components it is applied to. */
|
|
4
|
+
export declare function decoratedName(decorator: string, member: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* The public members that a decorator gives the components it is applied to: its own and those of
|
|
7
|
+
* the decorators it includes, each with whether it changes (state and derived values do).
|
|
8
|
+
*/
|
|
9
|
+
export declare function publicMembers(decorator: ast.DecoratorDeclaration, decorators: ReadonlyMap<string, DecoratorSource>, seen?: ReadonlySet<ast.DecoratorDeclaration>): Map<string, boolean>;
|
|
10
|
+
/**
|
|
11
|
+
* `@shop.count` of a member that `shop` gets from the `cart` it includes: a variable of `shop`
|
|
12
|
+
* that is that of `cart`, `derived` when it changes, so that what reads it is updated.
|
|
13
|
+
*/
|
|
14
|
+
export declare function aliases(included: DecoratorSource, use: ast.DecoratorUse, prefix: string, decorators: ReadonlyMap<string, DecoratorSource>, prefixes: ReadonlyMap<string, string> | undefined): ast.VariableDeclaration[];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// The names of the members of decorators in the components they are applied to, and the members
|
|
2
|
+
// that a decorator gives with those of the decorators it includes.
|
|
3
|
+
/** The JS name of a member or a parameter of a decorator, in the components it is applied to. */
|
|
4
|
+
export function decoratedName(decorator, member) {
|
|
5
|
+
return `$$${decorator}$${member}`;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The public members that a decorator gives the components it is applied to: its own and those of
|
|
9
|
+
* the decorators it includes, each with whether it changes (state and derived values do).
|
|
10
|
+
*/
|
|
11
|
+
export function publicMembers(decorator, decorators, seen = new Set()) {
|
|
12
|
+
const members = new Map();
|
|
13
|
+
const inner = new Set([...seen, decorator]);
|
|
14
|
+
for (const use of decorator.includes) {
|
|
15
|
+
const included = decorators.get(use.name.name);
|
|
16
|
+
if (!included || inner.has(included.node))
|
|
17
|
+
continue;
|
|
18
|
+
for (const [name, changes] of publicMembers(included.node, decorators, inner)) {
|
|
19
|
+
members.set(name, changes);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
for (const statement of decorator.body.body) {
|
|
23
|
+
if (statement.kind === 'FuncDeclaration' && statement.isPublic) {
|
|
24
|
+
members.set(statement.name.name, false);
|
|
25
|
+
}
|
|
26
|
+
else if (statement.kind === 'VariableDeclaration' && statement.isPublic) {
|
|
27
|
+
const changes = statement.keyword === 'state' || statement.keyword === 'derived';
|
|
28
|
+
for (const name of statement.names)
|
|
29
|
+
members.set(name.name, changes);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return members;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* `@shop.count` of a member that `shop` gets from the `cart` it includes: a variable of `shop`
|
|
36
|
+
* that is that of `cart`, `derived` when it changes, so that what reads it is updated.
|
|
37
|
+
*/
|
|
38
|
+
export function aliases(included, use, prefix, decorators, prefixes) {
|
|
39
|
+
const from = prefixes?.get(included.node.name.name) ?? included.node.name.name;
|
|
40
|
+
return [...publicMembers(included.node, decorators)].map(([member, changes]) => ({
|
|
41
|
+
kind: 'VariableDeclaration',
|
|
42
|
+
exported: false,
|
|
43
|
+
isPublic: false,
|
|
44
|
+
keyword: changes ? 'derived' : 'const',
|
|
45
|
+
names: [
|
|
46
|
+
{ kind: 'Identifier', name: decoratedName(prefix, member), start: use.start, end: use.end },
|
|
47
|
+
],
|
|
48
|
+
type: null,
|
|
49
|
+
values: [
|
|
50
|
+
{ kind: 'Identifier', name: decoratedName(from, member), start: use.start, end: use.end },
|
|
51
|
+
],
|
|
52
|
+
body: null,
|
|
53
|
+
start: use.start,
|
|
54
|
+
end: use.end,
|
|
55
|
+
}));
|
|
56
|
+
}
|
package/dist/decorators.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type * as ast from './ast.ts';
|
|
2
2
|
import { type CopyListener } from './decorator-copy.ts';
|
|
3
|
-
|
|
4
|
-
export declare function decoratedName(decorator: string, member: string): string;
|
|
3
|
+
export { decoratedName, publicMembers } from './decorator-names.ts';
|
|
5
4
|
/**
|
|
6
5
|
* Whether a declaration of an applied component gets an argument of a decorator. When the
|
|
7
6
|
* argument reads the component's properties, the declaration is a `state` that the properties
|
|
@@ -45,7 +44,7 @@ export declare function moduleNamesOf(decorator: ast.DecoratorDeclaration, progr
|
|
|
45
44
|
* The component with its decorators applied. Its body first declares the arguments of each
|
|
46
45
|
* decorator and runs its body, from the top decorator down, then runs the component's own body.
|
|
47
46
|
* The names of a decorator get its prefix, `$$visible$shown`, so they clash neither with each other
|
|
48
|
-
* nor with the component's names, and
|
|
47
|
+
* nor with the component's names, and `@visible.shown` reads that variable. The `mount` of
|
|
49
48
|
* the decorators runs after the component's own, from the inside out, so it goes before every
|
|
50
49
|
* `return` of the component's body.
|
|
51
50
|
*
|
package/dist/decorators.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { renamer } from "./decorator-copy.js";
|
|
2
|
+
import { aliases, decoratedName } from "./decorator-names.js";
|
|
2
3
|
import { declaredIn, freeNames, mentionsName, topLevelNames } from "./names.js";
|
|
3
4
|
import { containsReturn } from "./recursion.js";
|
|
4
5
|
import { forEachChild } from "./walk.js";
|
|
5
6
|
// Applying decorators: a component with decorators becomes one component whose body runs the
|
|
6
7
|
// bodies of its decorators first. The checker uses it to see what the inlined code uses, the
|
|
7
8
|
// generator to write it.
|
|
8
|
-
|
|
9
|
-
export function decoratedName(decorator, member) {
|
|
10
|
-
return `$$${decorator}$${member}`;
|
|
11
|
-
}
|
|
9
|
+
export { decoratedName, publicMembers } from "./decorator-names.js";
|
|
12
10
|
const argumentDeclarations = new WeakSet();
|
|
13
11
|
const wrappedContents = new WeakSet();
|
|
14
12
|
/**
|
|
@@ -69,7 +67,7 @@ export function moduleNamesOf(decorator, program) {
|
|
|
69
67
|
* The component with its decorators applied. Its body first declares the arguments of each
|
|
70
68
|
* decorator and runs its body, from the top decorator down, then runs the component's own body.
|
|
71
69
|
* The names of a decorator get its prefix, `$$visible$shown`, so they clash neither with each other
|
|
72
|
-
* nor with the component's names, and
|
|
70
|
+
* nor with the component's names, and `@visible.shown` reads that variable. The `mount` of
|
|
73
71
|
* the decorators runs after the component's own, from the inside out, so it goes before every
|
|
74
72
|
* `return` of the component's body.
|
|
75
73
|
*
|
|
@@ -83,37 +81,55 @@ export function moduleNamesOf(decorator, program) {
|
|
|
83
81
|
export function decorate(component, decorators, copied, prefixes) {
|
|
84
82
|
if (component.decorators.length === 0)
|
|
85
83
|
return component;
|
|
86
|
-
|
|
84
|
+
// Arguments that read the properties, or such arguments of a decorator that includes another,
|
|
85
|
+
// are state: they change with them.
|
|
86
|
+
const reactive = new Set(component.params.map((param) => param.name.name));
|
|
87
87
|
const setup = [];
|
|
88
88
|
const mounts = [];
|
|
89
89
|
const wrappers = [];
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
continue;
|
|
90
|
+
// `argument` copies an argument into the component: one of the component stays as it is, one of
|
|
91
|
+
// a decorator it includes is that decorator's code.
|
|
92
|
+
const apply = (source, args, argument, applying) => {
|
|
94
93
|
const decorator = source.node;
|
|
94
|
+
// A decorator that includes itself, through others too, is reported by the checker.
|
|
95
|
+
const inner = new Set([...applying, decorator]);
|
|
95
96
|
const prefix = prefixes?.get(decorator.name.name) ?? decorator.name.name;
|
|
96
97
|
const names = [...decoratorNames(decorator), ...source.moduleNames];
|
|
97
98
|
const renames = membersOf(prefix, names);
|
|
98
99
|
const copy = renamer(renames, copied, { decorators: prefixes, tags: source.tags });
|
|
99
100
|
decorator.params.forEach((param, i) => {
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
const given = args[i];
|
|
102
|
+
// A default value is the decorator's code.
|
|
103
|
+
const value = given
|
|
104
|
+
? argument(given)
|
|
105
|
+
: param.defaultValue && copy(param.defaultValue, new Set());
|
|
106
|
+
const name = renames.get(param.name.name);
|
|
107
|
+
const changes = value !== null && value !== undefined && mentionsName(value, reactive);
|
|
102
108
|
const declaration = {
|
|
103
109
|
kind: 'VariableDeclaration',
|
|
104
110
|
exported: false,
|
|
105
111
|
isPublic: false,
|
|
106
|
-
keyword:
|
|
107
|
-
names: [{ ...param.name, name
|
|
112
|
+
keyword: changes ? 'state' : 'const',
|
|
113
|
+
names: [{ ...param.name, name }],
|
|
108
114
|
type: param.type,
|
|
109
115
|
values: value ? [value] : [],
|
|
116
|
+
body: null,
|
|
110
117
|
start: param.start,
|
|
111
118
|
end: param.end,
|
|
112
119
|
};
|
|
120
|
+
if (changes)
|
|
121
|
+
reactive.add(name);
|
|
113
122
|
argumentDeclarations.add(declaration);
|
|
114
123
|
copied?.(declaration, param);
|
|
115
124
|
setup.push(declaration);
|
|
116
125
|
});
|
|
126
|
+
for (const use of decorator.includes) {
|
|
127
|
+
const included = decorators.get(use.name.name);
|
|
128
|
+
if (!included || inner.has(included.node))
|
|
129
|
+
continue;
|
|
130
|
+
apply(included, use.arguments, (value) => copy(value, new Set()), inner);
|
|
131
|
+
setup.push(...aliases(included, use, prefix, decorators, prefixes));
|
|
132
|
+
}
|
|
117
133
|
const wrapper = wrapperOf(decorator);
|
|
118
134
|
const own = [];
|
|
119
135
|
for (const statement of decorator.body.body) {
|
|
@@ -128,6 +144,11 @@ export function decorate(component, decorators, copied, prefixes) {
|
|
|
128
144
|
mounts.push(own);
|
|
129
145
|
if (wrapper)
|
|
130
146
|
wrappers.push(applyWrapper(source, wrapper, copied, prefix, prefixes));
|
|
147
|
+
};
|
|
148
|
+
for (const use of component.decorators) {
|
|
149
|
+
const source = decorators.get(use.name.name);
|
|
150
|
+
if (source)
|
|
151
|
+
apply(source, use.arguments, (value) => value, new Set());
|
|
131
152
|
}
|
|
132
153
|
const inside = mounts.reverse().flat();
|
|
133
154
|
const chain = wrappers.reverse();
|
|
@@ -186,6 +207,7 @@ function wrapped(node, chain) {
|
|
|
186
207
|
names: [{ ...wrapper.content }],
|
|
187
208
|
type: wrapper.type,
|
|
188
209
|
values: [value],
|
|
210
|
+
body: null,
|
|
189
211
|
start: node.start,
|
|
190
212
|
end: node.end,
|
|
191
213
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface CompileOptions {
|
|
|
20
20
|
declarations?: boolean;
|
|
21
21
|
/** Where the worker module of `go` is, relative to the module; see workerUrl() in compile.ts. */
|
|
22
22
|
workerUrl?: string;
|
|
23
|
-
/** A module that a worker
|
|
23
|
+
/** A module that a worker of Node or Bun imports first, to load `.rk` files (vite.ts, bun.ts). */
|
|
24
24
|
nodeLoader?: string;
|
|
25
25
|
/** Markup and components; by default as the package.json of the file says (src/config.ts). */
|
|
26
26
|
markup?: boolean;
|
|
@@ -27,6 +27,7 @@ export interface ModulePackages {
|
|
|
27
27
|
* recursive or a web component cannot be inlined, and neither can one that reaches components or
|
|
28
28
|
* decorators the module imports: those would bring the names of a third module. Nor can code that
|
|
29
29
|
* uses a component of the module as a value, which is the function in the module, or that assigns
|
|
30
|
-
* a variable of the module: another module could only read it.
|
|
30
|
+
* a variable of the module: another module could only read it. Nor can code that reads state of
|
|
31
|
+
* the module, `state items` at its top level: it listens to the changes there.
|
|
31
32
|
*/
|
|
32
33
|
export declare function inlinePackages(program: ast.Program): ModulePackages;
|
package/dist/inline-packages.js
CHANGED
|
@@ -7,7 +7,8 @@ import { forEachChild } from "./walk.js";
|
|
|
7
7
|
* recursive or a web component cannot be inlined, and neither can one that reaches components or
|
|
8
8
|
* decorators the module imports: those would bring the names of a third module. Nor can code that
|
|
9
9
|
* uses a component of the module as a value, which is the function in the module, or that assigns
|
|
10
|
-
* a variable of the module: another module could only read it.
|
|
10
|
+
* a variable of the module: another module could only read it. Nor can code that reads state of
|
|
11
|
+
* the module, `state items` at its top level: it listens to the changes there.
|
|
11
12
|
*/
|
|
12
13
|
export function inlinePackages(program) {
|
|
13
14
|
const components = new Map();
|
|
@@ -30,9 +31,13 @@ export function inlinePackages(program) {
|
|
|
30
31
|
};
|
|
31
32
|
const topLevel = topLevelNames(program);
|
|
32
33
|
const usesComponentValues = (node) => componentValues(node, (name) => components.has(name)).length > 0;
|
|
33
|
-
const
|
|
34
|
+
const states = new Set(program.body.flatMap((statement) => statement.kind === 'VariableDeclaration' && statement.keyword === 'state'
|
|
35
|
+
? statement.names.map((name) => name.name)
|
|
36
|
+
: []));
|
|
37
|
+
// It assigns a variable of the module or reads its state, which only the module can do.
|
|
38
|
+
const needsItsModule = (node) => {
|
|
34
39
|
const assigned = assignedNames(node);
|
|
35
|
-
return [...freeNames(node)].some((name) => topLevel.has(name) && assigned.has(name));
|
|
40
|
+
return [...freeNames(node)].some((name) => topLevel.has(name) && (assigned.has(name) || states.has(name)));
|
|
36
41
|
};
|
|
37
42
|
/** The package of a component, or of the tags in the code of a decorator. */
|
|
38
43
|
const build = (root, tags) => {
|
|
@@ -69,7 +74,7 @@ export function inlinePackages(program) {
|
|
|
69
74
|
found.components.set(current, component);
|
|
70
75
|
if (usesComponentValues(expanded.get(current)))
|
|
71
76
|
return false;
|
|
72
|
-
if (
|
|
77
|
+
if (needsItsModule(expanded.get(current)))
|
|
73
78
|
return false;
|
|
74
79
|
for (const use of component.decorators) {
|
|
75
80
|
const source = sources.get(use.name.name);
|
package/dist/loader.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { appendFileSync, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
2
|
import { createRequire, registerHooks } from 'node:module';
|
|
4
|
-
import { dirname, extname,
|
|
3
|
+
import { dirname, extname, resolve } from 'node:path';
|
|
5
4
|
import { fileURLToPath } from 'node:url';
|
|
6
5
|
import { isMainThread } from 'node:worker_threads';
|
|
7
6
|
import { RUNTIME_MODULE } from "./runtime-module.js";
|
|
8
|
-
import {
|
|
7
|
+
import { cachedFile, listFiles, withMap } from "./run-cache.js";
|
|
9
8
|
// `rook run` imports this module first (`node --import`): Node then imports `.rk` files,
|
|
10
9
|
// compiled when they load, and `app.rk?goroutines` is the worker module of `go` in app.rk.
|
|
11
10
|
// The worker threads of `go` import it too, since they get the `--import` of the process. The main
|
|
@@ -24,7 +23,7 @@ const markup = markupSetting === undefined ? undefined : markupSetting === '1';
|
|
|
24
23
|
const cache = process.env['ROOK_RUN_CACHE'];
|
|
25
24
|
/** The file of the cache for the code of a URL, query included. */
|
|
26
25
|
function cached(url) {
|
|
27
|
-
return cache ?
|
|
26
|
+
return cache ? cachedFile(cache, url) : null;
|
|
28
27
|
}
|
|
29
28
|
// The compiled code imports the runtime of this compiler, wherever the program is.
|
|
30
29
|
const runtime = import.meta.resolve(RUNTIME_MODULE);
|
|
@@ -50,7 +49,7 @@ registerHooks({
|
|
|
50
49
|
const { compileFile } = require(`./compile-file${extension}`);
|
|
51
50
|
const path = displayPath(fileURLToPath(moduleUrl));
|
|
52
51
|
if (isMainThread && cache)
|
|
53
|
-
|
|
52
|
+
listFiles(cache, [resolve(path)]);
|
|
54
53
|
const result = compileFile(path, {
|
|
55
54
|
rewriteImports: false,
|
|
56
55
|
typeCheck,
|
|
@@ -62,7 +61,7 @@ registerHooks({
|
|
|
62
61
|
if (isMainThread && cache) {
|
|
63
62
|
// The CSS modules it imports; the .rk modules are listed when they are loaded.
|
|
64
63
|
const read = result.dependencies.map((each) => resolve(dirname(path), each));
|
|
65
|
-
|
|
64
|
+
listFiles(cache, read.filter((each) => existsSync(each)));
|
|
66
65
|
writeFileSync(cached(moduleUrl), code);
|
|
67
66
|
if (worker !== undefined)
|
|
68
67
|
writeFileSync(cached(`${moduleUrl}?goroutines`), worker);
|
|
@@ -71,15 +70,3 @@ registerHooks({
|
|
|
71
70
|
return { format: 'module', source, shortCircuit: true };
|
|
72
71
|
},
|
|
73
72
|
});
|
|
74
|
-
/** The code with its source map in it. */
|
|
75
|
-
function withMap(code, map) {
|
|
76
|
-
if (map === undefined || code === '')
|
|
77
|
-
return code;
|
|
78
|
-
const data = Buffer.from(JSON.stringify(map)).toString('base64');
|
|
79
|
-
return `${code}${sourceMapComment(`data:application/json;base64,${data}`)}`;
|
|
80
|
-
}
|
|
81
|
-
/** Adds files to the list of the run: a line of the path and the time it was changed. */
|
|
82
|
-
function list(paths) {
|
|
83
|
-
const lines = paths.map((path) => `${path}\t${statSync(path).mtimeMs}\n`);
|
|
84
|
-
appendFileSync(join(cache, 'files'), lines.join(''));
|
|
85
|
-
}
|
package/dist/names.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type * as ast from './ast.ts';
|
|
2
2
|
/** Names assigned anywhere inside `node`: `x = ...`, `x += ...`, `x++`. */
|
|
3
3
|
export declare function assignedNames(node: ast.Node, names?: Set<string>): Set<string>;
|
|
4
|
+
/** `order.delivery.kind`: the variable and the fields of a path, or `null` for other expressions. */
|
|
5
|
+
export declare function pathOf(node: ast.Expression): string[] | null;
|
|
6
|
+
/** Paths of fields assigned anywhere inside `node`, e.g. `order.delivery` of `order.delivery = d`. */
|
|
7
|
+
export declare function assignedPaths(node: ast.Node, paths?: Set<string>): Set<string>;
|
|
4
8
|
/** Whether an identifier with one of `names` occurs in `node`, whatever it refers to. */
|
|
5
9
|
export declare function mentionsName(node: ast.Node, names: ReadonlySet<string>): boolean;
|
|
6
10
|
/** Every name declared inside `node`: parameters and all local declarations, nested ones too. */
|
package/dist/names.js
CHANGED
|
@@ -19,6 +19,30 @@ export function assignedNames(node, names = new Set()) {
|
|
|
19
19
|
forEachChild(node, (child) => assignedNames(child, names));
|
|
20
20
|
return names;
|
|
21
21
|
}
|
|
22
|
+
/** `order.delivery.kind`: the variable and the fields of a path, or `null` for other expressions. */
|
|
23
|
+
export function pathOf(node) {
|
|
24
|
+
if (node.kind === 'Identifier')
|
|
25
|
+
return [node.name];
|
|
26
|
+
if (node.kind !== 'MemberExpression' || node.optional)
|
|
27
|
+
return null;
|
|
28
|
+
const object = pathOf(node.object);
|
|
29
|
+
return object && [...object, node.property.name];
|
|
30
|
+
}
|
|
31
|
+
/** Paths of fields assigned anywhere inside `node`, e.g. `order.delivery` of `order.delivery = d`. */
|
|
32
|
+
export function assignedPaths(node, paths = new Set()) {
|
|
33
|
+
const targets = node.kind === 'AssignmentStatement'
|
|
34
|
+
? node.targets
|
|
35
|
+
: node.kind === 'IncDecStatement'
|
|
36
|
+
? [node.target]
|
|
37
|
+
: [];
|
|
38
|
+
for (const target of targets) {
|
|
39
|
+
const path = pathOf(target);
|
|
40
|
+
if (path && path.length > 1)
|
|
41
|
+
paths.add(path.join('.'));
|
|
42
|
+
}
|
|
43
|
+
forEachChild(node, (child) => assignedPaths(child, paths));
|
|
44
|
+
return paths;
|
|
45
|
+
}
|
|
22
46
|
/** Whether an identifier with one of `names` occurs in `node`, whatever it refers to. */
|
|
23
47
|
export function mentionsName(node, names) {
|
|
24
48
|
if (node.kind === 'Identifier')
|
|
@@ -99,6 +123,7 @@ export function freeNames(component) {
|
|
|
99
123
|
for (const name of node.names)
|
|
100
124
|
declared.add(name.name);
|
|
101
125
|
node.values.forEach(visit);
|
|
126
|
+
visit(node.body);
|
|
102
127
|
return;
|
|
103
128
|
case 'SelectCase':
|
|
104
129
|
for (const name of node.names)
|
|
@@ -120,6 +145,8 @@ export function freeNames(component) {
|
|
|
120
145
|
if (node.key)
|
|
121
146
|
declared.add(node.key.name);
|
|
122
147
|
visit(node.iterable);
|
|
148
|
+
if (node.withKey)
|
|
149
|
+
visit(node.withKey);
|
|
123
150
|
visit(node.body);
|
|
124
151
|
return;
|
|
125
152
|
case 'CatchClause':
|
|
@@ -195,6 +222,7 @@ export function collectValueNames(node, names, isComponent = () => true) {
|
|
|
195
222
|
return;
|
|
196
223
|
case 'VariableDeclaration':
|
|
197
224
|
node.values.forEach(visit);
|
|
225
|
+
visit(node.body);
|
|
198
226
|
return;
|
|
199
227
|
case 'FuncDeclaration':
|
|
200
228
|
case 'MethodDeclaration':
|
|
@@ -237,6 +265,7 @@ export function collectValueNames(node, names, isComponent = () => true) {
|
|
|
237
265
|
return;
|
|
238
266
|
case 'ForInStatement':
|
|
239
267
|
visit(node.iterable);
|
|
268
|
+
visit(node.withKey ?? null);
|
|
240
269
|
visit(node.body);
|
|
241
270
|
return;
|
|
242
271
|
case 'CatchClause':
|
package/dist/parser/base.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export declare abstract class ParserBase {
|
|
|
25
25
|
protected abstract parseClass(start: number, exported: boolean): ast.ClassDeclaration;
|
|
26
26
|
protected abstract parseInterface(start: number, exported: boolean): ast.InterfaceDeclaration;
|
|
27
27
|
protected abstract parseTypeAlias(start: number, exported: boolean): ast.TypeAliasDeclaration;
|
|
28
|
+
protected abstract parseFunctionBody(async?: boolean): ast.BlockStatement;
|
|
28
29
|
protected abstract canStartType(offset?: number): boolean;
|
|
29
30
|
protected abstract parseType(): ast.TypeNode;
|
|
30
31
|
protected abstract parseTypeArgs(): ast.TypeNode[];
|
package/dist/parser/base.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { STATEMENT_KEYWORDS, BRACKETS, ParseError, describe } from "./syntax.js";
|
|
2
2
|
// The parser is a chain of layers, one per area: base → simple statements → statements → control
|
|
3
|
-
// flow → jumps → declarations → decorators → classes → types → expressions → literals → markup → Parser
|
|
3
|
+
// flow → jumps → params → declarations → decorators → classes → types → expressions → literals → markup → Parser
|
|
4
4
|
// (parser.ts). Statements, expressions and markup contain each other, so methods of later layers
|
|
5
5
|
// that earlier ones call are declared here as abstract.
|
|
6
6
|
//
|
|
@@ -17,6 +17,11 @@ export declare abstract class ControlFlowParser extends StatementParser {
|
|
|
17
17
|
ast.SimpleStatement | null
|
|
18
18
|
];
|
|
19
19
|
protected parseForIn(start: number, go?: boolean): ast.ForInStatement;
|
|
20
|
+
/**
|
|
21
|
+
* `with key todo.id` after the items of a `{for}` in markup: its blocks are found again by the
|
|
22
|
+
* key instead of the item. `with` and `key` are words only here, not keywords.
|
|
23
|
+
*/
|
|
24
|
+
private parseWithKey;
|
|
20
25
|
/** `go for i, v in items { }`: the parts of the array run as goroutines, and the loop waits. */
|
|
21
26
|
protected parseGoFor(): ast.ForStatement | ast.ForInStatement;
|
|
22
27
|
protected parseLoopBody(): ast.BlockStatement;
|