rooklang 0.0.0-stage → 0.3.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/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 +193 -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 +104 -4
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { arrayName, isGoFor, loopParams, partFunction } from "./go-for.js";
|
|
2
|
+
import { declaredNames, freeNames } from "./names.js";
|
|
3
|
+
import { forEachChild } from "./walk.js";
|
|
4
|
+
/** Every `go` expression and `go for` loop of the program, nested ones too. */
|
|
5
|
+
export function goroutines(program) {
|
|
6
|
+
const found = [];
|
|
7
|
+
const visit = (node) => {
|
|
8
|
+
if (node.kind === 'GoExpression' || isGoFor(node))
|
|
9
|
+
found.push(node);
|
|
10
|
+
forEachChild(node, visit);
|
|
11
|
+
};
|
|
12
|
+
visit(program);
|
|
13
|
+
return found;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The names of the functions of the literals of `go` and of the `go for` loops in the worker
|
|
17
|
+
* module, numbered in the module as written. The worker module has the same nodes, and keeps them.
|
|
18
|
+
*/
|
|
19
|
+
const literalNames = new WeakMap();
|
|
20
|
+
/** The programs whose literals have names. */
|
|
21
|
+
const named = new WeakSet();
|
|
22
|
+
/** The local names that a literal of `go` or a `go for` uses, as the checker finds them. */
|
|
23
|
+
const captures = new WeakMap();
|
|
24
|
+
/** A function literal of `go`, or a `go for`: code that becomes a function of the worker module. */
|
|
25
|
+
function isLiteral(node) {
|
|
26
|
+
return node.kind === 'ForInStatement' || node.call.callee.kind === 'FuncExpression';
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The function that a goroutine calls in the worker module: a name, or the name given to a
|
|
30
|
+
* function literal or a `go for`; anything else is reported by the checker.
|
|
31
|
+
*/
|
|
32
|
+
export function goTarget(program, node) {
|
|
33
|
+
if (node.kind === 'GoExpression' && node.call.callee.kind === 'Identifier') {
|
|
34
|
+
return node.call.callee.name;
|
|
35
|
+
}
|
|
36
|
+
if (!isLiteral(node))
|
|
37
|
+
return null;
|
|
38
|
+
if (!named.has(program) && !literalNames.has(node)) {
|
|
39
|
+
named.add(program);
|
|
40
|
+
goroutines(program)
|
|
41
|
+
.filter(isLiteral)
|
|
42
|
+
.forEach((each, i) => literalNames.set(each, `$$goroutine${i}`));
|
|
43
|
+
}
|
|
44
|
+
return literalNames.get(node) ?? null;
|
|
45
|
+
}
|
|
46
|
+
export function recordCaptures(node, names) {
|
|
47
|
+
captures.set(node, names);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The local names that the literal of a `go` or a `go for` gets first: those the checker found.
|
|
51
|
+
* Code that was not checked passes the names that the code uses and the declaration around it
|
|
52
|
+
* declares, but not the array of `go for`, which goes in parts.
|
|
53
|
+
*/
|
|
54
|
+
export function goCaptures(program, node) {
|
|
55
|
+
const recorded = captures.get(node);
|
|
56
|
+
if (recorded)
|
|
57
|
+
return recorded;
|
|
58
|
+
const literal = node.kind === 'ForInStatement'
|
|
59
|
+
? { params: loopParams(node), body: node.body }
|
|
60
|
+
: node.call.callee;
|
|
61
|
+
const array = node.kind === 'ForInStatement' ? arrayName(node) : null;
|
|
62
|
+
const outer = program.body.find((each) => each.start <= node.start && node.end <= each.end);
|
|
63
|
+
const module = new Set(program.body.flatMap(declaredBy));
|
|
64
|
+
const local = outer
|
|
65
|
+
? declaredNames({ kind: 'BlockStatement', body: [outer], start: outer.start, end: outer.end })
|
|
66
|
+
: new Set();
|
|
67
|
+
return [...freeNames(literal)].filter((name) => local.has(name) && !module.has(name) && name !== array);
|
|
68
|
+
}
|
|
69
|
+
/** Parameters for the local names that a literal of `go` gets first. */
|
|
70
|
+
export function goParams(literal, names) {
|
|
71
|
+
const at = { start: literal.start, end: literal.start };
|
|
72
|
+
return names.map((name) => ({
|
|
73
|
+
kind: 'Parameter',
|
|
74
|
+
name: { kind: 'Identifier', name, ...at },
|
|
75
|
+
type: null,
|
|
76
|
+
defaultValue: null,
|
|
77
|
+
...at,
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The functions of the literals of `go`, with the local names they use as their first parameters,
|
|
82
|
+
* and of the `go for` loops.
|
|
83
|
+
*/
|
|
84
|
+
function literalFunctions(program) {
|
|
85
|
+
return goroutines(program).flatMap((node) => {
|
|
86
|
+
if (node.kind === 'ForInStatement') {
|
|
87
|
+
return [partFunction(node, goTarget(program, node), goCaptures(program, node))];
|
|
88
|
+
}
|
|
89
|
+
const literal = node.call.callee;
|
|
90
|
+
if (literal.kind !== 'FuncExpression')
|
|
91
|
+
return [];
|
|
92
|
+
const at = { start: literal.start, end: literal.start };
|
|
93
|
+
const params = goParams(literal, goCaptures(program, node));
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
kind: 'FuncDeclaration',
|
|
97
|
+
exported: false,
|
|
98
|
+
isPublic: false,
|
|
99
|
+
...(literal.async ? { async: true } : {}),
|
|
100
|
+
name: { kind: 'Identifier', name: goTarget(program, node), ...at },
|
|
101
|
+
typeParams: [],
|
|
102
|
+
params: [...params, ...literal.params],
|
|
103
|
+
results: literal.results,
|
|
104
|
+
body: literal.body,
|
|
105
|
+
start: literal.start,
|
|
106
|
+
end: literal.end,
|
|
107
|
+
},
|
|
108
|
+
];
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/** What the code of the functions `names` reaches in the module, through the names it uses. */
|
|
112
|
+
export function reach(program, names) {
|
|
113
|
+
const declarations = new Map();
|
|
114
|
+
const importsByName = new Map();
|
|
115
|
+
for (const statement of program.body) {
|
|
116
|
+
for (const name of declaredBy(statement))
|
|
117
|
+
declarations.set(name, statement);
|
|
118
|
+
}
|
|
119
|
+
const result = { statements: [], imports: new Map(), lets: [], markup: null };
|
|
120
|
+
const reached = new Set();
|
|
121
|
+
const queue = [...names];
|
|
122
|
+
const seen = new Set(queue);
|
|
123
|
+
for (const statement of program.body) {
|
|
124
|
+
if (statement.kind !== 'ImportDeclaration')
|
|
125
|
+
continue;
|
|
126
|
+
for (const name of importedNames(statement))
|
|
127
|
+
importsByName.set(name, statement);
|
|
128
|
+
}
|
|
129
|
+
for (let i = 0; i < queue.length; i++) {
|
|
130
|
+
const name = queue[i];
|
|
131
|
+
const imported = importsByName.get(name);
|
|
132
|
+
if (imported) {
|
|
133
|
+
const used = result.imports.get(imported) ?? new Set();
|
|
134
|
+
used.add(name);
|
|
135
|
+
result.imports.set(imported, used);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const statement = declarations.get(name);
|
|
139
|
+
if (!statement)
|
|
140
|
+
continue;
|
|
141
|
+
if (statement.kind === 'VariableDeclaration' && statement.keyword !== 'const') {
|
|
142
|
+
result.lets.push(name);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (statement.kind === 'ComponentDeclaration' || statement.kind === 'DecoratorDeclaration') {
|
|
146
|
+
result.markup ??= `the ${statement.kind === 'ComponentDeclaration' ? 'component' : 'decorator'} "${name}"`;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (reached.has(statement))
|
|
150
|
+
continue;
|
|
151
|
+
reached.add(statement);
|
|
152
|
+
if (hasMarkup(statement))
|
|
153
|
+
result.markup ??= `the markup in "${name}"`;
|
|
154
|
+
for (const used of usedNames(statement)) {
|
|
155
|
+
if (!seen.has(used)) {
|
|
156
|
+
seen.add(used);
|
|
157
|
+
queue.push(used);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
result.statements = program.body.filter((statement) => reached.has(statement));
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The program of the worker module: the imports and the declarations that the functions `targets`
|
|
166
|
+
* reach, unexported, since the worker module is only loaded by the worker.
|
|
167
|
+
*/
|
|
168
|
+
export function workerProgram(program, targets) {
|
|
169
|
+
const lifted = { ...program, body: [...program.body, ...literalFunctions(program)] };
|
|
170
|
+
const { statements, imports } = reach(lifted, targets);
|
|
171
|
+
const body = [];
|
|
172
|
+
for (const [statement, names] of imports) {
|
|
173
|
+
body.push({
|
|
174
|
+
...statement,
|
|
175
|
+
defaultImport: statement.defaultImport && keep(statement.defaultImport, names),
|
|
176
|
+
namespaceImport: statement.namespaceImport && keep(statement.namespaceImport, names),
|
|
177
|
+
namedImports: statement.namedImports.filter((specifier) => names.has(specifier.local.name)),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
for (const statement of statements) {
|
|
181
|
+
body.push('exported' in statement ? { ...statement, exported: false } : statement);
|
|
182
|
+
}
|
|
183
|
+
return { ...program, body };
|
|
184
|
+
}
|
|
185
|
+
function keep(name, names) {
|
|
186
|
+
return names.has(name.name) ? name : null;
|
|
187
|
+
}
|
|
188
|
+
function declaredBy(statement) {
|
|
189
|
+
switch (statement.kind) {
|
|
190
|
+
case 'VariableDeclaration':
|
|
191
|
+
return statement.names.map((name) => name.name);
|
|
192
|
+
case 'FuncDeclaration':
|
|
193
|
+
case 'ClassDeclaration':
|
|
194
|
+
case 'ComponentDeclaration':
|
|
195
|
+
case 'DecoratorDeclaration':
|
|
196
|
+
return [statement.name.name];
|
|
197
|
+
default:
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function importedNames(statement) {
|
|
202
|
+
return [
|
|
203
|
+
...(statement.defaultImport ? [statement.defaultImport.name] : []),
|
|
204
|
+
...(statement.namespaceImport ? [statement.namespaceImport.name] : []),
|
|
205
|
+
...statement.namedImports.map((specifier) => specifier.local.name),
|
|
206
|
+
];
|
|
207
|
+
}
|
|
208
|
+
/** The names that the code of a top-level declaration uses; not those of members or keys. */
|
|
209
|
+
function usedNames(statement) {
|
|
210
|
+
const block = (body) => ({
|
|
211
|
+
kind: 'BlockStatement',
|
|
212
|
+
body,
|
|
213
|
+
start: statement.start,
|
|
214
|
+
end: statement.end,
|
|
215
|
+
});
|
|
216
|
+
switch (statement.kind) {
|
|
217
|
+
case 'FuncDeclaration':
|
|
218
|
+
return freeNames(statement);
|
|
219
|
+
case 'VariableDeclaration':
|
|
220
|
+
return freeNames({ params: [], body: block([statement]) });
|
|
221
|
+
case 'ClassDeclaration': {
|
|
222
|
+
const names = new Set();
|
|
223
|
+
const add = (found) => found.forEach((name) => names.add(name));
|
|
224
|
+
if (statement.superClass) {
|
|
225
|
+
add(freeNames({ params: [], body: block([expression(statement.superClass)]) }));
|
|
226
|
+
}
|
|
227
|
+
for (const member of statement.members) {
|
|
228
|
+
if (member.kind === 'FieldDeclaration') {
|
|
229
|
+
if (member.value)
|
|
230
|
+
add(freeNames({ params: [], body: block([expression(member.value)]) }));
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
add(freeNames(member));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return names;
|
|
237
|
+
}
|
|
238
|
+
default:
|
|
239
|
+
return new Set();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function expression(node) {
|
|
243
|
+
return { kind: 'ExpressionStatement', expression: node, start: node.start, end: node.end };
|
|
244
|
+
}
|
|
245
|
+
function hasMarkup(node) {
|
|
246
|
+
if (node.kind === 'ElementExpression')
|
|
247
|
+
return true;
|
|
248
|
+
let found = false;
|
|
249
|
+
forEachChild(node, (child) => {
|
|
250
|
+
found ||= hasMarkup(child);
|
|
251
|
+
});
|
|
252
|
+
return found;
|
|
253
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type * as ast from './ast.ts';
|
|
2
|
+
/** `mainPage` and `MainPage` → `main-page`, `HTMLView` → `html-view`. */
|
|
3
|
+
export declare function kebabCase(name: string): string;
|
|
4
|
+
/** The tag of a web component: given by `@html-tag("app-page")`, or its name in kebab case. */
|
|
5
|
+
export declare function htmlTagName(component: ast.ComponentDeclaration): string;
|
|
6
|
+
/** The attribute of a property: `itemCount` → `item-count` (attributes do not keep case). */
|
|
7
|
+
export declare function attributeName(property: string): string;
|
|
8
|
+
/** Why HTML does not accept a name of a custom element, or `null` if it does. */
|
|
9
|
+
export declare function tagNameProblem(tag: string): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* Properties of HTMLElement that a web component's properties would replace, breaking the
|
|
12
|
+
* element: `id`, `style`, `hidden`...
|
|
13
|
+
*/
|
|
14
|
+
export declare const RESERVED_PROPERTIES: ReadonlySet<string>;
|
|
15
|
+
/** How an attribute sets a property: as text, as a number, or by its presence. */
|
|
16
|
+
export type AttributeKind = 'string' | 'number' | 'bool';
|
|
17
|
+
/**
|
|
18
|
+
* The attribute kind of a property by its type: `string`, `?number`, `"s" | "m"` have attributes;
|
|
19
|
+
* arrays, objects and functions are set only as JS properties (`null`).
|
|
20
|
+
*/
|
|
21
|
+
export declare function attributeKind(type: ast.TypeNode | null, aliases: ReadonlyMap<string, ast.TypeNode>, seen?: Set<string>): AttributeKind | null;
|
package/dist/html-tag.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Web components from `@html-tag` components: their tags, attributes and the names they cannot use.
|
|
2
|
+
/** `mainPage` and `MainPage` → `main-page`, `HTMLView` → `html-view`. */
|
|
3
|
+
export function kebabCase(name) {
|
|
4
|
+
return name
|
|
5
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
6
|
+
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
|
|
7
|
+
.toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
/** The tag of a web component: given by `@html-tag("app-page")`, or its name in kebab case. */
|
|
10
|
+
export function htmlTagName(component) {
|
|
11
|
+
return component.htmlTag?.name?.value ?? kebabCase(component.name.name);
|
|
12
|
+
}
|
|
13
|
+
/** The attribute of a property: `itemCount` → `item-count` (attributes do not keep case). */
|
|
14
|
+
export function attributeName(property) {
|
|
15
|
+
return kebabCase(property);
|
|
16
|
+
}
|
|
17
|
+
const RESERVED_TAGS = new Set([
|
|
18
|
+
'annotation-xml',
|
|
19
|
+
'color-profile',
|
|
20
|
+
'font-face',
|
|
21
|
+
'font-face-src',
|
|
22
|
+
'font-face-uri',
|
|
23
|
+
'font-face-format',
|
|
24
|
+
'font-face-name',
|
|
25
|
+
'missing-glyph',
|
|
26
|
+
]);
|
|
27
|
+
/** Why HTML does not accept a name of a custom element, or `null` if it does. */
|
|
28
|
+
export function tagNameProblem(tag) {
|
|
29
|
+
if (!/^[a-z][a-z0-9._-]*$/.test(tag)) {
|
|
30
|
+
return 'it must start with a lowercase letter and have only lowercase letters, digits, "-", "." and "_"';
|
|
31
|
+
}
|
|
32
|
+
if (!tag.includes('-'))
|
|
33
|
+
return 'it must have a hyphen';
|
|
34
|
+
if (RESERVED_TAGS.has(tag))
|
|
35
|
+
return 'HTML reserves this name';
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Properties of HTMLElement that a web component's properties would replace, breaking the
|
|
40
|
+
* element: `id`, `style`, `hidden`...
|
|
41
|
+
*/
|
|
42
|
+
export const RESERVED_PROPERTIES = new Set([
|
|
43
|
+
'id',
|
|
44
|
+
'className',
|
|
45
|
+
'classList',
|
|
46
|
+
'style',
|
|
47
|
+
'slot',
|
|
48
|
+
'hidden',
|
|
49
|
+
'dir',
|
|
50
|
+
'lang',
|
|
51
|
+
'tabIndex',
|
|
52
|
+
'part',
|
|
53
|
+
'dataset',
|
|
54
|
+
'attributes',
|
|
55
|
+
'shadowRoot',
|
|
56
|
+
'innerHTML',
|
|
57
|
+
'outerHTML',
|
|
58
|
+
'innerText',
|
|
59
|
+
'textContent',
|
|
60
|
+
'tagName',
|
|
61
|
+
'localName',
|
|
62
|
+
'nodeName',
|
|
63
|
+
'nodeType',
|
|
64
|
+
'isConnected',
|
|
65
|
+
'ownerDocument',
|
|
66
|
+
'parentNode',
|
|
67
|
+
'parentElement',
|
|
68
|
+
'childNodes',
|
|
69
|
+
'firstChild',
|
|
70
|
+
'lastChild',
|
|
71
|
+
]);
|
|
72
|
+
/**
|
|
73
|
+
* The attribute kind of a property by its type: `string`, `?number`, `"s" | "m"` have attributes;
|
|
74
|
+
* arrays, objects and functions are set only as JS properties (`null`).
|
|
75
|
+
*/
|
|
76
|
+
export function attributeKind(type, aliases, seen = new Set()) {
|
|
77
|
+
if (type === null)
|
|
78
|
+
return null;
|
|
79
|
+
switch (type.kind) {
|
|
80
|
+
case 'NullableType':
|
|
81
|
+
return attributeKind(type.type, aliases, seen);
|
|
82
|
+
case 'LiteralType':
|
|
83
|
+
return type.value.kind === 'StringLiteral'
|
|
84
|
+
? 'string'
|
|
85
|
+
: type.value.kind === 'NumberLiteral'
|
|
86
|
+
? 'number'
|
|
87
|
+
: 'bool';
|
|
88
|
+
case 'UnionType': {
|
|
89
|
+
const kinds = new Set(type.types.map((member) => attributeKind(member, aliases, seen)));
|
|
90
|
+
const [kind] = kinds;
|
|
91
|
+
return kinds.size === 1 && kind !== undefined ? kind : null;
|
|
92
|
+
}
|
|
93
|
+
case 'TypeReference': {
|
|
94
|
+
const name = type.name.name;
|
|
95
|
+
const alias = aliases.get(name);
|
|
96
|
+
if (alias && !seen.has(name))
|
|
97
|
+
return attributeKind(alias, aliases, seen.add(name));
|
|
98
|
+
return name === 'string' || name === 'number' || name === 'bool' ? name : null;
|
|
99
|
+
}
|
|
100
|
+
default:
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type CompileResult } from './compile.ts';
|
|
2
|
+
import { type CompletionList } from './completion.ts';
|
|
3
|
+
export type { CompileResult } from './compile.ts';
|
|
4
|
+
export type { Completion, CompletionList } from './completion.ts';
|
|
5
|
+
export type { Diagnostic } from './diagnostics.ts';
|
|
6
|
+
export { formatDiagnostic } from './diagnostics.ts';
|
|
7
|
+
export { SourceFile } from './source.ts';
|
|
8
|
+
export interface CompileOptions {
|
|
9
|
+
/** Path of the source file; needed to check the types of imports. */
|
|
10
|
+
filename?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Replace `.rk` with `.js` in relative import paths, for output written next to the sources.
|
|
13
|
+
* Defaults to `true`.
|
|
14
|
+
*/
|
|
15
|
+
rewriteImports?: boolean;
|
|
16
|
+
/** Check types before generating code. Defaults to `true`. */
|
|
17
|
+
typeCheck?: boolean;
|
|
18
|
+
/** Also print a TypeScript declaration file (`.d.ts`); needs type checking. */
|
|
19
|
+
declarations?: boolean;
|
|
20
|
+
/** Where the worker module of `go` is, relative to the module; see workerUrl() in compile.ts. */
|
|
21
|
+
workerUrl?: string;
|
|
22
|
+
/** Markup and components; by default as the package.json of the file says (src/config.ts). */
|
|
23
|
+
markup?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function compile(source: string, options?: CompileOptions): CompileResult;
|
|
26
|
+
/**
|
|
27
|
+
* What can be written at `offset` in a module, for editors: names, members, types, tags and
|
|
28
|
+
* attributes. The imports are read from disk, like those of `compile`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function complete(source: string, offset: number, options?: {
|
|
31
|
+
filename?: string;
|
|
32
|
+
markup?: boolean;
|
|
33
|
+
}): CompletionList;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { compileModule, workerUrl, } from "./compile.js";
|
|
2
|
+
import { completeModule } from "./completion.js";
|
|
3
|
+
import { markupFor } from "./config.js";
|
|
4
|
+
import { loadModuleExports } from "./modules.js";
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { dirname, join, resolve } from 'node:path';
|
|
7
|
+
import { importDeclarations, libraryFor } from "./typescript/importer.js";
|
|
8
|
+
export { formatDiagnostic } from "./diagnostics.js";
|
|
9
|
+
export { SourceFile } from "./source.js";
|
|
10
|
+
export function compile(source, options = {}) {
|
|
11
|
+
const { filename } = options;
|
|
12
|
+
const rewriteImports = options.rewriteImports ?? true;
|
|
13
|
+
const markup = options.markup ?? markupFor(filename);
|
|
14
|
+
return compileModule(source, {
|
|
15
|
+
...filesOf(filename, options.typeCheck ?? true, options.markup),
|
|
16
|
+
markup,
|
|
17
|
+
rewriteImports,
|
|
18
|
+
workerUrl: options.workerUrl ?? workerUrl(filename ?? 'main.rk', rewriteImports),
|
|
19
|
+
typeCheck: options.typeCheck ?? true,
|
|
20
|
+
declarations: options.declarations ?? false,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* What can be written at `offset` in a module, for editors: names, members, types, tags and
|
|
25
|
+
* attributes. The imports are read from disk, like those of `compile`.
|
|
26
|
+
*/
|
|
27
|
+
export function complete(source, offset, options = {}) {
|
|
28
|
+
return completeModule(source, offset, {
|
|
29
|
+
...filesOf(options.filename, true, options.markup),
|
|
30
|
+
markup: options.markup ?? markupFor(options.filename),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** Reading what a module imports: other modules, declarations, CSS modules and the library. */
|
|
34
|
+
function filesOf(filename, typeCheck, markup) {
|
|
35
|
+
return {
|
|
36
|
+
importModule: filename
|
|
37
|
+
? (specifier) => loadModuleExports(filename, specifier, markup)
|
|
38
|
+
: undefined,
|
|
39
|
+
importDeclarations: filename
|
|
40
|
+
? (specifier) => importDeclarations(filename, specifier)
|
|
41
|
+
: undefined,
|
|
42
|
+
readStyleModule: filename ? (specifier) => readNear(filename, specifier) : undefined,
|
|
43
|
+
// Without a file name, `@types` packages are looked up from the current directory.
|
|
44
|
+
library: typeCheck ? libraryFor(filename ?? join(process.cwd(), 'main.rk')) : undefined,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** A file next to `filename`, or null if it cannot be read. */
|
|
48
|
+
function readNear(filename, specifier) {
|
|
49
|
+
try {
|
|
50
|
+
return readFileSync(resolve(dirname(filename), specifier), 'utf8');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type * as ast from './ast.ts';
|
|
2
|
+
import { type DecoratorSource } from './decorators.ts';
|
|
3
|
+
export interface InlinePackage {
|
|
4
|
+
/** The exported component first, then the components of its module it reaches: inlined too. */
|
|
5
|
+
components: Map<string, ast.ComponentDeclaration>;
|
|
6
|
+
/** Reached components that are functions in their module (recursive, web or exported): called. */
|
|
7
|
+
functions: Map<string, ast.ComponentDeclaration>;
|
|
8
|
+
/** The decorators of the module applied to them, with the names of the module their code uses. */
|
|
9
|
+
decorators: Map<string, DecoratorSource>;
|
|
10
|
+
/** The names of the module that the components' own code uses. */
|
|
11
|
+
moduleNames: string[];
|
|
12
|
+
/** Names the code uses that the module does not declare: they must mean the same elsewhere. */
|
|
13
|
+
globals: string[];
|
|
14
|
+
}
|
|
15
|
+
/** The packages that a module exports for others. */
|
|
16
|
+
export interface ModulePackages {
|
|
17
|
+
/** By exported component; a component that cannot be inlined has none. */
|
|
18
|
+
components: Map<string, InlinePackage>;
|
|
19
|
+
/**
|
|
20
|
+
* By exported decorator: the components its code uses, which go with it into the components it
|
|
21
|
+
* is applied to. Null when they cannot go elsewhere.
|
|
22
|
+
*/
|
|
23
|
+
decorators: Map<string, InlinePackage | null>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The packages of the exported components and decorators of a module. A component that is
|
|
27
|
+
* recursive or a web component cannot be inlined, and neither can one that reaches components or
|
|
28
|
+
* decorators the module imports: those would bring the names of a third module. Nor can code that
|
|
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.
|
|
31
|
+
*/
|
|
32
|
+
export declare function inlinePackages(program: ast.Program): ModulePackages;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { decorate, moduleNamesOf } from "./decorators.js";
|
|
2
|
+
import { assignedNames, componentValues, freeNames, topLevelNames } from "./names.js";
|
|
3
|
+
import { recursiveComponents } from "./recursion.js";
|
|
4
|
+
import { forEachChild } from "./walk.js";
|
|
5
|
+
/**
|
|
6
|
+
* The packages of the exported components and decorators of a module. A component that is
|
|
7
|
+
* recursive or a web component cannot be inlined, and neither can one that reaches components or
|
|
8
|
+
* decorators the module imports: those would bring the names of a third module. Nor can code that
|
|
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.
|
|
11
|
+
*/
|
|
12
|
+
export function inlinePackages(program) {
|
|
13
|
+
const components = new Map();
|
|
14
|
+
const decorators = new Map();
|
|
15
|
+
for (const statement of program.body) {
|
|
16
|
+
if (statement.kind === 'ComponentDeclaration')
|
|
17
|
+
components.set(statement.name.name, statement);
|
|
18
|
+
if (statement.kind === 'DecoratorDeclaration')
|
|
19
|
+
decorators.set(statement.name.name, statement);
|
|
20
|
+
}
|
|
21
|
+
const sources = new Map([...decorators].map(([name, node]) => [
|
|
22
|
+
name,
|
|
23
|
+
{ node, moduleNames: moduleNamesOf(node, program) },
|
|
24
|
+
]));
|
|
25
|
+
const expanded = new Map([...components].map(([name, node]) => [name, decorate(node, sources)]));
|
|
26
|
+
const recursive = recursiveComponents(expanded);
|
|
27
|
+
const isFunction = (name) => {
|
|
28
|
+
const node = components.get(name);
|
|
29
|
+
return node.htmlTag !== null || node.exported || recursive.has(expanded.get(name));
|
|
30
|
+
};
|
|
31
|
+
const topLevel = topLevelNames(program);
|
|
32
|
+
const usesComponentValues = (node) => componentValues(node, (name) => components.has(name)).length > 0;
|
|
33
|
+
const assignsModuleNames = (node) => {
|
|
34
|
+
const assigned = assignedNames(node);
|
|
35
|
+
return [...freeNames(node)].some((name) => topLevel.has(name) && assigned.has(name));
|
|
36
|
+
};
|
|
37
|
+
/** The package of a component, or of the tags in the code of a decorator. */
|
|
38
|
+
const build = (root, tags) => {
|
|
39
|
+
const found = {
|
|
40
|
+
components: new Map(),
|
|
41
|
+
functions: new Map(),
|
|
42
|
+
decorators: new Map(),
|
|
43
|
+
moduleNames: [],
|
|
44
|
+
globals: [],
|
|
45
|
+
};
|
|
46
|
+
const own = new Set();
|
|
47
|
+
const globals = new Set();
|
|
48
|
+
const sort = (names, mine) => {
|
|
49
|
+
for (const used of names) {
|
|
50
|
+
if (!topLevel.has(used))
|
|
51
|
+
globals.add(used);
|
|
52
|
+
else if (mine)
|
|
53
|
+
own.add(used);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
// A component of the module is called or inlined; one the module imports cannot go anywhere.
|
|
57
|
+
const take = (tag) => {
|
|
58
|
+
if (!components.has(tag))
|
|
59
|
+
return !topLevel.has(tag);
|
|
60
|
+
if (!isFunction(tag))
|
|
61
|
+
return visit(tag);
|
|
62
|
+
found.functions.set(tag, components.get(tag));
|
|
63
|
+
return true;
|
|
64
|
+
};
|
|
65
|
+
const visit = (current) => {
|
|
66
|
+
if (found.components.has(current))
|
|
67
|
+
return true;
|
|
68
|
+
const component = components.get(current);
|
|
69
|
+
found.components.set(current, component);
|
|
70
|
+
if (usesComponentValues(expanded.get(current)))
|
|
71
|
+
return false;
|
|
72
|
+
if (assignsModuleNames(expanded.get(current)))
|
|
73
|
+
return false;
|
|
74
|
+
for (const use of component.decorators) {
|
|
75
|
+
const source = sources.get(use.name.name);
|
|
76
|
+
if (!source)
|
|
77
|
+
return false;
|
|
78
|
+
found.decorators.set(use.name.name, source);
|
|
79
|
+
sort(freeNames(source.node), false);
|
|
80
|
+
}
|
|
81
|
+
sort(freeNames(withArguments(component)), true);
|
|
82
|
+
return [...tagsIn(expanded.get(current))].every(take);
|
|
83
|
+
};
|
|
84
|
+
if (!(root === null ? [...tags].every(take) : visit(root)))
|
|
85
|
+
return null;
|
|
86
|
+
found.moduleNames = [...own].sort();
|
|
87
|
+
found.globals = [...globals].sort();
|
|
88
|
+
return found;
|
|
89
|
+
};
|
|
90
|
+
const packages = { components: new Map(), decorators: new Map() };
|
|
91
|
+
for (const [name, node] of components) {
|
|
92
|
+
if (!node.exported || node.htmlTag || recursive.has(expanded.get(name)))
|
|
93
|
+
continue;
|
|
94
|
+
const found = build(name, []);
|
|
95
|
+
if (found)
|
|
96
|
+
packages.components.set(name, found);
|
|
97
|
+
}
|
|
98
|
+
for (const [name, node] of decorators) {
|
|
99
|
+
if (!node.exported)
|
|
100
|
+
continue;
|
|
101
|
+
packages.decorators.set(name, usesComponentValues(node) ? null : build(null, tagsIn(node.body)));
|
|
102
|
+
}
|
|
103
|
+
return packages;
|
|
104
|
+
}
|
|
105
|
+
/** The component's own code with the arguments of its decorators, which are its code too. */
|
|
106
|
+
function withArguments(component) {
|
|
107
|
+
const statements = component.decorators.flatMap((use) => use.arguments.map((expression) => ({
|
|
108
|
+
kind: 'ExpressionStatement',
|
|
109
|
+
expression,
|
|
110
|
+
start: use.start,
|
|
111
|
+
end: use.end,
|
|
112
|
+
})));
|
|
113
|
+
return {
|
|
114
|
+
params: component.params,
|
|
115
|
+
body: { ...component.body, body: [...statements, ...component.body.body] },
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** The names of components in the markup: tags with a capital letter. */
|
|
119
|
+
function tagsIn(node) {
|
|
120
|
+
const tags = new Set();
|
|
121
|
+
const visit = (child) => {
|
|
122
|
+
if (child.kind === 'ElementExpression' && child.tag && /^[A-Z]/.test(child.tag.name)) {
|
|
123
|
+
tags.add(child.tag.name);
|
|
124
|
+
}
|
|
125
|
+
forEachChild(child, visit);
|
|
126
|
+
};
|
|
127
|
+
visit(node);
|
|
128
|
+
return tags;
|
|
129
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type * as ast from './ast.ts';
|
|
2
|
+
/** Whether a function with these parameters is an iterator. */
|
|
3
|
+
export declare function isIterator(params: readonly ast.Parameter[]): boolean;
|
|
4
|
+
/** `yield(v)` or `yield(k, v)`: a call of the parameter of an iterator. */
|
|
5
|
+
export declare function isYieldCall(node: ast.Node): node is ast.CallExpression;
|
|
6
|
+
/**
|
|
7
|
+
* Whether a body uses `this` of the code around it. A func literal is an arrow function, which
|
|
8
|
+
* has it; an iterator literal becomes a generator function, which needs it bound.
|
|
9
|
+
*/
|
|
10
|
+
export declare function usesThis(node: ast.Node): boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { forEachChild } from "./walk.js";
|
|
2
|
+
// Iterators, as in Go 1.23: a function whose one parameter is `yield`, `func(yield func(T) bool)`.
|
|
3
|
+
// It compiles to a JS generator: `yield(v)` yields `v`, and when the loop that goes over it stops,
|
|
4
|
+
// the generator is closed, so the iterator returns there, with its `defer`s run. The checker
|
|
5
|
+
// (src/checker/iterators.ts) and the code generator both tell iterators by this parameter.
|
|
6
|
+
/** Whether a function with these parameters is an iterator. */
|
|
7
|
+
export function isIterator(params) {
|
|
8
|
+
return params.length === 1 && params[0].name.name === 'yield';
|
|
9
|
+
}
|
|
10
|
+
/** `yield(v)` or `yield(k, v)`: a call of the parameter of an iterator. */
|
|
11
|
+
export function isYieldCall(node) {
|
|
12
|
+
return (node.kind === 'CallExpression' &&
|
|
13
|
+
node.callee.kind === 'Identifier' &&
|
|
14
|
+
node.callee.name === 'yield');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Whether a body uses `this` of the code around it. A func literal is an arrow function, which
|
|
18
|
+
* has it; an iterator literal becomes a generator function, which needs it bound.
|
|
19
|
+
*/
|
|
20
|
+
export function usesThis(node) {
|
|
21
|
+
if (node.kind === 'ThisExpression')
|
|
22
|
+
return true;
|
|
23
|
+
if (node.kind === 'FuncDeclaration' ||
|
|
24
|
+
node.kind === 'ClassDeclaration' ||
|
|
25
|
+
(node.kind === 'FuncExpression' && isIterator(node.params))) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
let found = false;
|
|
29
|
+
forEachChild(node, (child) => {
|
|
30
|
+
found ||= usesThis(child);
|
|
31
|
+
});
|
|
32
|
+
return found;
|
|
33
|
+
}
|