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.
Files changed (314) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -2
  3. package/README.ru.md +178 -0
  4. package/dist/ast/declarations.d.ts +185 -0
  5. package/dist/ast/declarations.js +1 -0
  6. package/dist/ast/expressions.d.ts +201 -0
  7. package/dist/ast/expressions.js +1 -0
  8. package/dist/ast/markup.d.ts +141 -0
  9. package/dist/ast/markup.js +1 -0
  10. package/dist/ast/statements.d.ts +173 -0
  11. package/dist/ast/statements.js +1 -0
  12. package/dist/ast.d.ts +18 -0
  13. package/dist/ast.js +18 -0
  14. package/dist/attrs.d.ts +8 -0
  15. package/dist/attrs.js +23 -0
  16. package/dist/browser.d.ts +30 -0
  17. package/dist/browser.js +36 -0
  18. package/dist/build.d.ts +48 -0
  19. package/dist/build.js +112 -0
  20. package/dist/checker/assignments.d.ts +14 -0
  21. package/dist/checker/assignments.js +197 -0
  22. package/dist/checker/base.d.ts +124 -0
  23. package/dist/checker/base.js +180 -0
  24. package/dist/checker/bigint.d.ts +15 -0
  25. package/dist/checker/bigint.js +85 -0
  26. package/dist/checker/builtins.d.ts +9 -0
  27. package/dist/checker/builtins.js +307 -0
  28. package/dist/checker/bytes.d.ts +11 -0
  29. package/dist/checker/bytes.js +72 -0
  30. package/dist/checker/calls.d.ts +23 -0
  31. package/dist/checker/calls.js +210 -0
  32. package/dist/checker/channel.d.ts +14 -0
  33. package/dist/checker/channel.js +44 -0
  34. package/dist/checker/channels.d.ts +9 -0
  35. package/dist/checker/channels.js +95 -0
  36. package/dist/checker/checker.d.ts +4 -0
  37. package/dist/checker/checker.js +22 -0
  38. package/dist/checker/classes.d.ts +24 -0
  39. package/dist/checker/classes.js +223 -0
  40. package/dist/checker/completion-names.d.ts +11 -0
  41. package/dist/checker/completion-names.js +100 -0
  42. package/dist/checker/completion.d.ts +25 -0
  43. package/dist/checker/completion.js +222 -0
  44. package/dist/checker/component-values.d.ts +11 -0
  45. package/dist/checker/component-values.js +88 -0
  46. package/dist/checker/components.d.ts +37 -0
  47. package/dist/checker/components.js +287 -0
  48. package/dist/checker/context.d.ts +196 -0
  49. package/dist/checker/context.js +35 -0
  50. package/dist/checker/control-flow.d.ts +24 -0
  51. package/dist/checker/control-flow.js +237 -0
  52. package/dist/checker/declarations.d.ts +26 -0
  53. package/dist/checker/declarations.js +259 -0
  54. package/dist/checker/decorator-exports.d.ts +5 -0
  55. package/dist/checker/decorator-exports.js +40 -0
  56. package/dist/checker/decorator-members.d.ts +28 -0
  57. package/dist/checker/decorator-members.js +140 -0
  58. package/dist/checker/decorator-wrappers.d.ts +20 -0
  59. package/dist/checker/decorator-wrappers.js +132 -0
  60. package/dist/checker/decorators.d.ts +28 -0
  61. package/dist/checker/decorators.js +265 -0
  62. package/dist/checker/dom-svg.d.ts +7 -0
  63. package/dist/checker/dom-svg.js +19 -0
  64. package/dist/checker/dom.d.ts +22 -0
  65. package/dist/checker/dom.js +361 -0
  66. package/dist/checker/expressions.d.ts +32 -0
  67. package/dist/checker/expressions.js +244 -0
  68. package/dist/checker/flow.d.ts +18 -0
  69. package/dist/checker/flow.js +134 -0
  70. package/dist/checker/forwarding.d.ts +15 -0
  71. package/dist/checker/forwarding.js +78 -0
  72. package/dist/checker/functions.d.ts +24 -0
  73. package/dist/checker/functions.js +154 -0
  74. package/dist/checker/go-for.d.ts +12 -0
  75. package/dist/checker/go-for.js +93 -0
  76. package/dist/checker/goroutines.d.ts +19 -0
  77. package/dist/checker/goroutines.js +110 -0
  78. package/dist/checker/helpers.d.ts +35 -0
  79. package/dist/checker/helpers.js +162 -0
  80. package/dist/checker/imports.d.ts +7 -0
  81. package/dist/checker/imports.js +83 -0
  82. package/dist/checker/initializers.d.ts +22 -0
  83. package/dist/checker/initializers.js +104 -0
  84. package/dist/checker/iterables.d.ts +5 -0
  85. package/dist/checker/iterables.js +17 -0
  86. package/dist/checker/iterators.d.ts +13 -0
  87. package/dist/checker/iterators.js +95 -0
  88. package/dist/checker/loops.d.ts +10 -0
  89. package/dist/checker/loops.js +82 -0
  90. package/dist/checker/markup.d.ts +28 -0
  91. package/dist/checker/markup.js +220 -0
  92. package/dist/checker/members.d.ts +29 -0
  93. package/dist/checker/members.js +233 -0
  94. package/dist/checker/operators.d.ts +13 -0
  95. package/dist/checker/operators.js +177 -0
  96. package/dist/checker/promise.d.ts +17 -0
  97. package/dist/checker/promise.js +49 -0
  98. package/dist/checker/refs.d.ts +26 -0
  99. package/dist/checker/refs.js +136 -0
  100. package/dist/checker/resolution.d.ts +23 -0
  101. package/dist/checker/resolution.js +173 -0
  102. package/dist/checker/statements.d.ts +28 -0
  103. package/dist/checker/statements.js +212 -0
  104. package/dist/checker/styles.d.ts +14 -0
  105. package/dist/checker/styles.js +49 -0
  106. package/dist/checker/transfer.d.ts +5 -0
  107. package/dist/checker/transfer.js +83 -0
  108. package/dist/checker/types/bytes.d.ts +4 -0
  109. package/dist/checker/types/bytes.js +51 -0
  110. package/dist/checker/types/classes.d.ts +22 -0
  111. package/dist/checker/types/classes.js +93 -0
  112. package/dist/checker/types/constructors.d.ts +27 -0
  113. package/dist/checker/types/constructors.js +129 -0
  114. package/dist/checker/types/generics.d.ts +20 -0
  115. package/dist/checker/types/generics.js +75 -0
  116. package/dist/checker/types/lazy.d.ts +30 -0
  117. package/dist/checker/types/lazy.js +92 -0
  118. package/dist/checker/types/model.d.ts +149 -0
  119. package/dist/checker/types/model.js +15 -0
  120. package/dist/checker/types/params.d.ts +7 -0
  121. package/dist/checker/types/params.js +220 -0
  122. package/dist/checker/types/printing.d.ts +6 -0
  123. package/dist/checker/types/printing.js +109 -0
  124. package/dist/checker/types/relations.d.ts +16 -0
  125. package/dist/checker/types/relations.js +294 -0
  126. package/dist/checker/types.d.ts +9 -0
  127. package/dist/checker/types.js +10 -0
  128. package/dist/cli.d.ts +2 -0
  129. package/dist/cli.js +193 -0
  130. package/dist/codegen/analysis.d.ts +18 -0
  131. package/dist/codegen/analysis.js +144 -0
  132. package/dist/codegen/attributes.d.ts +40 -0
  133. package/dist/codegen/attributes.js +227 -0
  134. package/dist/codegen/calls.d.ts +22 -0
  135. package/dist/codegen/calls.js +106 -0
  136. package/dist/codegen/chan-helpers.d.ts +2 -0
  137. package/dist/codegen/chan-helpers.js +253 -0
  138. package/dist/codegen/component-helpers.d.ts +33 -0
  139. package/dist/codegen/component-helpers.js +18 -0
  140. package/dist/codegen/component-props.d.ts +42 -0
  141. package/dist/codegen/component-props.js +173 -0
  142. package/dist/codegen/component-values.d.ts +27 -0
  143. package/dist/codegen/component-values.js +146 -0
  144. package/dist/codegen/components.d.ts +53 -0
  145. package/dist/codegen/components.js +268 -0
  146. package/dist/codegen/control-flow.d.ts +25 -0
  147. package/dist/codegen/control-flow.js +152 -0
  148. package/dist/codegen/control-statements.d.ts +28 -0
  149. package/dist/codegen/control-statements.js +226 -0
  150. package/dist/codegen/creation.d.ts +31 -0
  151. package/dist/codegen/creation.js +113 -0
  152. package/dist/codegen/early-returns.d.ts +59 -0
  153. package/dist/codegen/early-returns.js +244 -0
  154. package/dist/codegen/element-helpers.d.ts +12 -0
  155. package/dist/codegen/element-helpers.js +54 -0
  156. package/dist/codegen/elements.d.ts +71 -0
  157. package/dist/codegen/elements.js +293 -0
  158. package/dist/codegen/emitter.d.ts +135 -0
  159. package/dist/codegen/emitter.js +296 -0
  160. package/dist/codegen/expressions.d.ts +49 -0
  161. package/dist/codegen/expressions.js +262 -0
  162. package/dist/codegen/functions.d.ts +28 -0
  163. package/dist/codegen/functions.js +170 -0
  164. package/dist/codegen/go-helpers.d.ts +9 -0
  165. package/dist/codegen/go-helpers.js +307 -0
  166. package/dist/codegen/helpers.d.ts +10 -0
  167. package/dist/codegen/helpers.js +356 -0
  168. package/dist/codegen/imported.d.ts +38 -0
  169. package/dist/codegen/imported.js +71 -0
  170. package/dist/codegen/js.d.ts +20 -0
  171. package/dist/codegen/js.js +113 -0
  172. package/dist/codegen/mutations.d.ts +10 -0
  173. package/dist/codegen/mutations.js +102 -0
  174. package/dist/codegen/parallel.d.ts +7 -0
  175. package/dist/codegen/parallel.js +63 -0
  176. package/dist/codegen/reactive.d.ts +95 -0
  177. package/dist/codegen/reactive.js +280 -0
  178. package/dist/codegen/refs.d.ts +62 -0
  179. package/dist/codegen/refs.js +160 -0
  180. package/dist/codegen/select-helpers.d.ts +2 -0
  181. package/dist/codegen/select-helpers.js +135 -0
  182. package/dist/codegen/simple-statements.d.ts +42 -0
  183. package/dist/codegen/simple-statements.js +253 -0
  184. package/dist/codegen/statements.d.ts +22 -0
  185. package/dist/codegen/statements.js +142 -0
  186. package/dist/codegen/styles.d.ts +54 -0
  187. package/dist/codegen/styles.js +157 -0
  188. package/dist/codegen/syntax.d.ts +18 -0
  189. package/dist/codegen/syntax.js +70 -0
  190. package/dist/codegen/thread-helpers.d.ts +2 -0
  191. package/dist/codegen/thread-helpers.js +198 -0
  192. package/dist/codegen/updates.d.ts +51 -0
  193. package/dist/codegen/updates.js +201 -0
  194. package/dist/codegen/web-components.d.ts +20 -0
  195. package/dist/codegen/web-components.js +149 -0
  196. package/dist/compile-file.d.ts +11 -0
  197. package/dist/compile-file.js +24 -0
  198. package/dist/compile.d.ts +53 -0
  199. package/dist/compile.js +187 -0
  200. package/dist/completion.d.ts +10 -0
  201. package/dist/completion.js +134 -0
  202. package/dist/config.d.ts +2 -0
  203. package/dist/config.js +44 -0
  204. package/dist/css/hash.d.ts +1 -0
  205. package/dist/css/hash.js +10 -0
  206. package/dist/css/parse.d.ts +42 -0
  207. package/dist/css/parse.js +138 -0
  208. package/dist/css/print.d.ts +9 -0
  209. package/dist/css/print.js +35 -0
  210. package/dist/css/selectors.d.ts +29 -0
  211. package/dist/css/selectors.js +168 -0
  212. package/dist/declarations.d.ts +3 -0
  213. package/dist/declarations.js +207 -0
  214. package/dist/decorator-copy.d.ts +18 -0
  215. package/dist/decorator-copy.js +158 -0
  216. package/dist/decorators.d.ts +61 -0
  217. package/dist/decorators.js +241 -0
  218. package/dist/diagnostics.d.ts +17 -0
  219. package/dist/diagnostics.js +22 -0
  220. package/dist/go-for.d.ts +26 -0
  221. package/dist/go-for.js +155 -0
  222. package/dist/goroutines.d.ts +37 -0
  223. package/dist/goroutines.js +253 -0
  224. package/dist/html-tag.d.ts +21 -0
  225. package/dist/html-tag.js +103 -0
  226. package/dist/index.d.ts +33 -0
  227. package/dist/index.js +55 -0
  228. package/dist/inline-packages.d.ts +32 -0
  229. package/dist/inline-packages.js +129 -0
  230. package/dist/iterators.d.ts +10 -0
  231. package/dist/iterators.js +33 -0
  232. package/dist/lexer/base.d.ts +24 -0
  233. package/dist/lexer/base.js +87 -0
  234. package/dist/lexer/lexer.d.ts +8 -0
  235. package/dist/lexer/lexer.js +134 -0
  236. package/dist/lexer/literals.d.ts +18 -0
  237. package/dist/lexer/literals.js +203 -0
  238. package/dist/lexer/markup.d.ts +10 -0
  239. package/dist/lexer/markup.js +93 -0
  240. package/dist/lexer/style.d.ts +17 -0
  241. package/dist/lexer/style.js +97 -0
  242. package/dist/lexer/tables.d.ts +53 -0
  243. package/dist/lexer/tables.js +101 -0
  244. package/dist/lexer/token.d.ts +42 -0
  245. package/dist/lexer/token.js +108 -0
  246. package/dist/loader.d.ts +1 -0
  247. package/dist/loader.js +50 -0
  248. package/dist/methods.d.ts +5 -0
  249. package/dist/methods.js +81 -0
  250. package/dist/modules.d.ts +3 -0
  251. package/dist/modules.js +49 -0
  252. package/dist/names.d.ts +23 -0
  253. package/dist/names.js +267 -0
  254. package/dist/parser/base.d.ts +96 -0
  255. package/dist/parser/base.js +202 -0
  256. package/dist/parser/classes.d.ts +8 -0
  257. package/dist/parser/classes.js +164 -0
  258. package/dist/parser/control-flow.d.ts +29 -0
  259. package/dist/parser/control-flow.js +232 -0
  260. package/dist/parser/declarations.d.ts +27 -0
  261. package/dist/parser/declarations.js +238 -0
  262. package/dist/parser/decorators.d.ts +30 -0
  263. package/dist/parser/decorators.js +176 -0
  264. package/dist/parser/expressions.d.ts +15 -0
  265. package/dist/parser/expressions.js +255 -0
  266. package/dist/parser/literals.d.ts +27 -0
  267. package/dist/parser/literals.js +206 -0
  268. package/dist/parser/markup.d.ts +22 -0
  269. package/dist/parser/markup.js +193 -0
  270. package/dist/parser/parser.d.ts +14 -0
  271. package/dist/parser/parser.js +22 -0
  272. package/dist/parser/simple-statements.d.ts +22 -0
  273. package/dist/parser/simple-statements.js +156 -0
  274. package/dist/parser/statements.d.ts +37 -0
  275. package/dist/parser/statements.js +267 -0
  276. package/dist/parser/syntax.d.ts +51 -0
  277. package/dist/parser/syntax.js +173 -0
  278. package/dist/parser/types.d.ts +23 -0
  279. package/dist/parser/types.js +218 -0
  280. package/dist/recursion.d.ts +25 -0
  281. package/dist/recursion.js +170 -0
  282. package/dist/refs.d.ts +49 -0
  283. package/dist/refs.js +210 -0
  284. package/dist/run.d.ts +1 -0
  285. package/dist/run.js +21 -0
  286. package/dist/source.d.ts +15 -0
  287. package/dist/source.js +34 -0
  288. package/dist/style-modules.d.ts +17 -0
  289. package/dist/style-modules.js +49 -0
  290. package/dist/styles.d.ts +36 -0
  291. package/dist/styles.js +160 -0
  292. package/dist/svg.d.ts +8 -0
  293. package/dist/svg.js +77 -0
  294. package/dist/ts-types.d.ts +11 -0
  295. package/dist/ts-types.js +133 -0
  296. package/dist/typed-arrays.d.ts +8 -0
  297. package/dist/typed-arrays.js +43 -0
  298. package/dist/typescript/convert-base.d.ts +42 -0
  299. package/dist/typescript/convert-base.js +135 -0
  300. package/dist/typescript/convert-objects.d.ts +28 -0
  301. package/dist/typescript/convert-objects.js +245 -0
  302. package/dist/typescript/convert.d.ts +14 -0
  303. package/dist/typescript/convert.js +67 -0
  304. package/dist/typescript/importer.d.ts +37 -0
  305. package/dist/typescript/importer.js +152 -0
  306. package/dist/typescript/library.d.ts +23 -0
  307. package/dist/typescript/library.js +81 -0
  308. package/dist/vite.d.ts +11 -0
  309. package/dist/vite.js +86 -0
  310. package/dist/walk-types.d.ts +4 -0
  311. package/dist/walk-types.js +56 -0
  312. package/dist/walk.d.ts +5 -0
  313. package/dist/walk.js +315 -0
  314. package/package.json +104 -4
@@ -0,0 +1,218 @@
1
+ import { ClassParser } from "./classes.js";
2
+ import { describe } from "./syntax.js";
3
+ // Types: `?T`, `[]T`, unions, literal types, function, component and object types, type arguments.
4
+ /** `iterator: true` for the parameters of an iterator: one, named `yield`. */
5
+ function iterator(names) {
6
+ return names.length === 1 && names[0] === 'yield' ? { iterator: true } : {};
7
+ }
8
+ export class TypeParser extends ClassParser {
9
+ /** `<number, string>` after the name of a generic type. */
10
+ parseTypeArgs() {
11
+ if (!this.accept('<'))
12
+ return [];
13
+ const args = [this.parseType()];
14
+ while (this.accept(','))
15
+ args.push(this.parseType());
16
+ this.expectTypeClose('">" after the type arguments');
17
+ return args;
18
+ }
19
+ canStartType() {
20
+ switch (this.peek().kind) {
21
+ case 'Identifier':
22
+ case '[':
23
+ case '?':
24
+ case 'func':
25
+ case 'comp':
26
+ case 'chan':
27
+ case '<-':
28
+ case '{':
29
+ case '(':
30
+ case 'String':
31
+ case 'Number':
32
+ case 'true':
33
+ case 'false':
34
+ return true;
35
+ default:
36
+ return false;
37
+ }
38
+ }
39
+ /** A type, possibly a union: `string | number`. */
40
+ parseType() {
41
+ const start = this.peek().start;
42
+ const first = this.parsePrimaryType();
43
+ if (!this.check('|'))
44
+ return first;
45
+ const types = [first];
46
+ while (this.accept('|'))
47
+ types.push(this.parsePrimaryType());
48
+ return { kind: 'UnionType', types, ...this.span(start) };
49
+ }
50
+ parsePrimaryType() {
51
+ const start = this.peek().start;
52
+ switch (this.peek().kind) {
53
+ case '(':
54
+ return this.parseParenthesizedType();
55
+ case 'String': {
56
+ const value = this.parseStringLiteral();
57
+ return { kind: 'LiteralType', value, ...this.span(start) };
58
+ }
59
+ case 'Number':
60
+ case 'true':
61
+ case 'false': {
62
+ const value = this.parsePrimary();
63
+ return { kind: 'LiteralType', value, ...this.span(start) };
64
+ }
65
+ case '?': {
66
+ this.next();
67
+ const type = this.parsePrimaryType();
68
+ return { kind: 'NullableType', type, ...this.span(start) };
69
+ }
70
+ case '[': {
71
+ this.next();
72
+ this.expect(']', '"]" (array types are written as []T)');
73
+ const element = this.parsePrimaryType();
74
+ return { kind: 'ArrayType', element, ...this.span(start) };
75
+ }
76
+ // The parameters may have names, as in Go; `func(yield func(T) bool)` is an iterator.
77
+ case 'func': {
78
+ this.next();
79
+ this.expect('(');
80
+ const names = [];
81
+ const params = this.parseList(')', () => this.parseParamType(names));
82
+ const results = this.parseResults();
83
+ return { kind: 'FuncType', params, results, ...iterator(names), ...this.span(start) };
84
+ }
85
+ // `chan T`, `chan<- T` that only sends, `<-chan T` that only receives.
86
+ case 'chan': {
87
+ this.next();
88
+ const direction = this.accept('<-') ? 'send' : 'both';
89
+ const element = this.parsePrimaryType();
90
+ return { kind: 'ChanType', element, direction, ...this.span(start) };
91
+ }
92
+ case '<-': {
93
+ this.next();
94
+ this.expect('chan', '"chan" after "<-": a channel that only receives is <-chan T');
95
+ const element = this.parsePrimaryType();
96
+ return { kind: 'ChanType', element, direction: 'receive', ...this.span(start) };
97
+ }
98
+ // `comp(title string)`: the names are those of the attributes, so they are required.
99
+ case 'comp': {
100
+ this.requireMarkup('components are');
101
+ this.next();
102
+ const params = this.parseParams(false);
103
+ return { kind: 'ComponentType', params, ...this.span(start) };
104
+ }
105
+ case '{': {
106
+ const members = this.parseTypeMembers();
107
+ return { kind: 'ObjectType', members, ...this.span(start) };
108
+ }
109
+ default: {
110
+ const name = this.parseIdentifier('type');
111
+ const typeArgs = this.parseTypeArgs();
112
+ return { kind: 'TypeReference', name, typeArgs, ...this.span(start) };
113
+ }
114
+ }
115
+ }
116
+ /**
117
+ * Grouping, `[](string | number)`, or a function type written as an arrow,
118
+ * `(paths []string) => Shape`, the same as `func([]string) Shape`. Its parameters may have
119
+ * names, which only document them.
120
+ */
121
+ parseParenthesizedType() {
122
+ const start = this.next().start;
123
+ const names = [];
124
+ const params = this.parseList(')', () => this.parseParamType(names));
125
+ if (this.accept('=>')) {
126
+ const result = this.parseType();
127
+ const voids = result.kind === 'TypeReference' && result.name.name === 'void';
128
+ return {
129
+ kind: 'FuncType',
130
+ params,
131
+ results: voids ? [] : [result],
132
+ ...iterator(names),
133
+ ...this.span(start),
134
+ };
135
+ }
136
+ if (params.length !== 1) {
137
+ this.error('types in parentheses are the parameters of a function type: (a A, b B) => R', start, this.span(start).end);
138
+ }
139
+ return (params[0] ?? {
140
+ kind: 'TypeReference',
141
+ name: { kind: 'Identifier', name: 'any', start, end: start },
142
+ typeArgs: [],
143
+ start,
144
+ end: start,
145
+ });
146
+ }
147
+ /** The type of a parameter of a function type, after its name if it has one, kept in `names`. */
148
+ parseParamType(names) {
149
+ names.push(this.check('Identifier') && this.startsNamedParam() ? this.next().text : '');
150
+ return this.parseType();
151
+ }
152
+ /** `paths []string` in `(paths []string) => Shape`: a name before the type of a parameter. */
153
+ startsNamedParam() {
154
+ const next = this.peek(1);
155
+ switch (next.kind) {
156
+ case 'Identifier':
157
+ case '?':
158
+ case '{':
159
+ case '(':
160
+ case 'func':
161
+ case 'chan':
162
+ case '<-':
163
+ case 'String':
164
+ case 'Number':
165
+ return true;
166
+ // `x []number` is named.
167
+ case '[':
168
+ return this.peek(2).kind === ']';
169
+ default:
170
+ return false;
171
+ }
172
+ }
173
+ /** `{ name string; x, y number; area() number }`, separated by newlines, `;` or `,`. */
174
+ parseTypeMembers() {
175
+ this.expect('{');
176
+ const members = [];
177
+ this.parseSeparated(() => this.check('}'), () => {
178
+ members.push(...this.parseTypeMember());
179
+ if (!this.accept(',') && !this.accept(';') && !this.check('}')) {
180
+ this.fail(`expected newline, ";" or "}" after member, found ${describe(this.peek())}`);
181
+ }
182
+ });
183
+ this.expect('}');
184
+ return members;
185
+ }
186
+ parseTypeMember() {
187
+ const start = this.peek().start;
188
+ // `[string]: Shape`: an object used as a map.
189
+ if (this.accept('[')) {
190
+ const key = this.parseType();
191
+ if (key.kind !== 'TypeReference' || !['string', 'number'].includes(key.name.name)) {
192
+ this.error('the key of [key]: T is string or number, e.g. [string]: number', key.start, key.end);
193
+ }
194
+ this.expect(']');
195
+ this.expect(':', '":" and the type of the values, e.g. [string]: number');
196
+ const type = this.parseType();
197
+ return [{ kind: 'IndexSignature', key, type, ...this.span(start) }];
198
+ }
199
+ const name = this.parsePropertyName('member name');
200
+ if (this.check('(')) {
201
+ const params = this.parseParams(false);
202
+ const results = this.parseResults();
203
+ return [{ kind: 'MethodSignature', name, params, results, ...this.span(start) }];
204
+ }
205
+ // `x, y number` declares several properties of the same type.
206
+ const names = [name];
207
+ while (this.accept(','))
208
+ names.push(this.parsePropertyName('member name'));
209
+ const type = this.parseType();
210
+ return names.map((memberName) => ({
211
+ kind: 'PropertySignature',
212
+ name: memberName,
213
+ type,
214
+ start: memberName.start,
215
+ end: type.end,
216
+ }));
217
+ }
218
+ }
@@ -0,0 +1,25 @@
1
+ import type * as ast from './ast.ts';
2
+ export interface ComponentUse {
3
+ tag: ast.Identifier;
4
+ /**
5
+ * Inside if/for/switch, a branch of `?:`, the right side of `&&`, `||` or `??`, a function, or
6
+ * a default value of a property: the use may not run every time the component is created.
7
+ */
8
+ conditional: boolean;
9
+ }
10
+ /** Tags with a capital letter inside a component: uses of other components, or of itself. */
11
+ export declare function componentUses(component: ast.ComponentDeclaration): ComponentUse[];
12
+ /** Whether a statement has a `return` of its own, not one of a nested function. */
13
+ export declare function containsReturn(statement: ast.Statement): boolean;
14
+ /** Components that can reach themselves through their uses. */
15
+ export declare function recursiveComponents(components: ReadonlyMap<string, ast.ComponentDeclaration>): Set<ast.ComponentDeclaration>;
16
+ export interface EndlessRecursion {
17
+ /** Names of the components on the cycle, starting and ending with the same one. */
18
+ cycle: string[];
19
+ /** The use that closes the cycle. */
20
+ tag: ast.Identifier;
21
+ }
22
+ /** Cycles of uses where every use runs each time: creating such a component never ends. */
23
+ export declare function endlessRecursion(components: ReadonlyMap<string, ast.ComponentDeclaration>): EndlessRecursion[];
24
+ /** What a component returns: the markup of every `return` of its own, in order. */
25
+ export declare function returnedMarkup(component: ast.ComponentDeclaration): ast.Expression[];
@@ -0,0 +1,170 @@
1
+ import { forEachChild } from "./walk.js";
2
+ /** Tags with a capital letter inside a component: uses of other components, or of itself. */
3
+ export function componentUses(component) {
4
+ const uses = [];
5
+ const visit = (node, conditional) => {
6
+ const visitChildren = (inside) => forEachChild(node, (child) => visit(child, inside));
7
+ switch (node.kind) {
8
+ case 'ElementExpression':
9
+ if (node.tag && /^[A-Z]/.test(node.tag.name))
10
+ uses.push({ tag: node.tag, conditional });
11
+ for (const attribute of node.attributes)
12
+ visit(attribute, conditional);
13
+ for (const child of node.children)
14
+ visit(child, conditional);
15
+ return;
16
+ case 'IfStatement':
17
+ visit(node.condition, conditional);
18
+ visit(node.consequent, true);
19
+ if (node.alternate)
20
+ visit(node.alternate, true);
21
+ return;
22
+ case 'ConditionalExpression':
23
+ visit(node.test, conditional);
24
+ visit(node.consequent, true);
25
+ visit(node.alternate, true);
26
+ return;
27
+ case 'BinaryExpression':
28
+ if (node.operator === '&&' || node.operator === '||' || node.operator === '??') {
29
+ visit(node.left, conditional);
30
+ visit(node.right, true);
31
+ return;
32
+ }
33
+ visitChildren(conditional);
34
+ return;
35
+ case 'ForStatement':
36
+ case 'ForInStatement':
37
+ case 'SwitchStatement':
38
+ case 'CatchClause':
39
+ case 'FuncDeclaration':
40
+ case 'FuncExpression':
41
+ case 'ArrowFunction':
42
+ case 'EventHandler':
43
+ case 'MountStatement':
44
+ case 'EffectStatement':
45
+ case 'ClassDeclaration':
46
+ case 'Parameter':
47
+ visitChildren(true);
48
+ return;
49
+ default:
50
+ visitChildren(conditional);
51
+ }
52
+ };
53
+ for (const param of component.params)
54
+ visit(param, true);
55
+ // After an early return the rest of the body may not run.
56
+ let returned = false;
57
+ for (const statement of component.body.body) {
58
+ visit(statement, returned);
59
+ returned ||= containsReturn(statement);
60
+ }
61
+ return uses;
62
+ }
63
+ /** Whether a statement has a `return` of its own, not one of a nested function. */
64
+ export function containsReturn(statement) {
65
+ let found = false;
66
+ const visit = (node) => {
67
+ if (found)
68
+ return;
69
+ switch (node.kind) {
70
+ case 'ReturnStatement':
71
+ found = true;
72
+ return;
73
+ case 'FuncDeclaration':
74
+ case 'FuncExpression':
75
+ case 'ArrowFunction':
76
+ case 'EventHandler':
77
+ case 'ClassDeclaration':
78
+ case 'MountStatement':
79
+ case 'EffectStatement':
80
+ return;
81
+ default:
82
+ forEachChild(node, visit);
83
+ }
84
+ };
85
+ visit(statement);
86
+ return found;
87
+ }
88
+ /** Components that can reach themselves through their uses. */
89
+ export function recursiveComponents(components) {
90
+ const edges = new Map();
91
+ for (const component of components.values()) {
92
+ const used = componentUses(component)
93
+ .map((use) => components.get(use.tag.name))
94
+ .filter((other) => other !== undefined);
95
+ edges.set(component, used);
96
+ }
97
+ const recursive = new Set();
98
+ for (const component of components.values()) {
99
+ const seen = new Set();
100
+ const stack = [...(edges.get(component) ?? [])];
101
+ while (stack.length > 0) {
102
+ const next = stack.pop();
103
+ if (next === component) {
104
+ recursive.add(component);
105
+ break;
106
+ }
107
+ if (seen.has(next))
108
+ continue;
109
+ seen.add(next);
110
+ stack.push(...(edges.get(next) ?? []));
111
+ }
112
+ }
113
+ return recursive;
114
+ }
115
+ /** Cycles of uses where every use runs each time: creating such a component never ends. */
116
+ export function endlessRecursion(components) {
117
+ const edges = new Map();
118
+ for (const component of components.values()) {
119
+ const list = [];
120
+ for (const use of componentUses(component)) {
121
+ const to = components.get(use.tag.name);
122
+ if (to && !use.conditional)
123
+ list.push({ to, tag: use.tag });
124
+ }
125
+ edges.set(component, list);
126
+ }
127
+ const found = [];
128
+ const state = new Map();
129
+ const visit = (component, path) => {
130
+ state.set(component, 'visiting');
131
+ for (const edge of edges.get(component) ?? []) {
132
+ if (state.get(edge.to) === 'visiting') {
133
+ const cycle = [...path.slice(path.indexOf(edge.to)), edge.to];
134
+ found.push({ cycle: cycle.map((c) => c.name.name), tag: edge.tag });
135
+ }
136
+ else if (!state.has(edge.to)) {
137
+ visit(edge.to, [...path, edge.to]);
138
+ }
139
+ }
140
+ state.set(component, 'done');
141
+ };
142
+ for (const component of components.values()) {
143
+ if (!state.has(component))
144
+ visit(component, [component]);
145
+ }
146
+ return found;
147
+ }
148
+ /** What a component returns: the markup of every `return` of its own, in order. */
149
+ export function returnedMarkup(component) {
150
+ const found = [];
151
+ const visit = (node) => {
152
+ switch (node.kind) {
153
+ case 'ReturnStatement':
154
+ found.push(...node.values);
155
+ return;
156
+ case 'FuncDeclaration':
157
+ case 'FuncExpression':
158
+ case 'ArrowFunction':
159
+ case 'EventHandler':
160
+ case 'ClassDeclaration':
161
+ case 'MountStatement':
162
+ case 'EffectStatement':
163
+ return;
164
+ default:
165
+ forEachChild(node, visit);
166
+ }
167
+ };
168
+ visit(component.body);
169
+ return found;
170
+ }
package/dist/refs.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ import type * as ast from './ast.ts';
2
+ /** Why markup may be created more than once, so that a ref cannot point into it. */
3
+ export type Repeated = 'for' | 'if' | 'switch' | 'expression' | 'function' | 'block';
4
+ /** An attribute of markup, the element it is on, and whether that markup is created once. */
5
+ export interface MarkupBinding<A> {
6
+ attribute: A;
7
+ element: ast.ElementExpression;
8
+ /** Null when the markup is created exactly once. */
9
+ repeated: Repeated | null;
10
+ }
11
+ export type RefBinding = MarkupBinding<ast.JsxAttribute>;
12
+ /** `ref={input}` of an element or a component use. */
13
+ export declare function refAttribute(node: ast.ElementExpression): ast.JsxAttribute | undefined;
14
+ /** The ref an element is bound to: `input` in `<input ref={input} />`. */
15
+ export declare function refName(node: ast.ElementExpression): string | null;
16
+ /**
17
+ * The attributes that are set where the ref is declared: strings and attributes without a value,
18
+ * before the first spread, which may override them. Values in braces, handlers and `bind:` are
19
+ * set by the markup.
20
+ */
21
+ export declare function constantAttributes(node: ast.ElementExpression): ast.JsxAttribute[];
22
+ /** The `ref={...}` attributes in the markup of a body; see markupBindings. */
23
+ export declare function refBindings(body: ast.BlockStatement): RefBinding[];
24
+ /**
25
+ * The attributes that `wanted` picks in the markup of a body, and whether that markup is created
26
+ * exactly once: what a `return` gives (also through the wrapper a decorator returns),
27
+ * `const x = <markup>` at the top level, and the elements and children of components inside them.
28
+ */
29
+ export declare function markupBindings<A extends ast.JsxAttribute | ast.JsxSpreadAttribute>(body: ast.BlockStatement, wanted: (attribute: ast.JsxAttribute | ast.JsxSpreadAttribute) => attribute is A): MarkupBinding<A>[];
30
+ /**
31
+ * For each ref of a checked body, the element its markup binds it to. The wrapper of a decorator
32
+ * is copied to every `return` of a component, so there may be several, all alike.
33
+ */
34
+ export declare function refTargets(body: ast.BlockStatement): Map<string, ast.ElementExpression>;
35
+ /** Why the element a component creates is not known in advance; e.g. `has several returns`. */
36
+ export type Root = ast.ElementExpression | string;
37
+ /**
38
+ * What a component returns, if it is known in advance: an element or the use of a component in
39
+ * its own markup. Through `const content = <markup>`, which is how the wrapper of a decorator gets
40
+ * it. Otherwise why it is not known, as a phrase after "it".
41
+ */
42
+ export declare function ownRoot(component: ast.ComponentDeclaration): Root;
43
+ /** `<Card />`, or a component from a value: `<section.content />`, `<Page />`. */
44
+ export declare function isComponentUse(node: ast.ElementExpression): boolean;
45
+ /**
46
+ * The element at the top of what a component creates, through the components it returns.
47
+ * `rootOf` gives that of another component by its tag.
48
+ */
49
+ export declare function componentRoot(component: ast.ComponentDeclaration, rootOf: (tag: string) => Root | undefined): Root;
package/dist/refs.js ADDED
@@ -0,0 +1,210 @@
1
+ import { forEachChild } from "./walk.js";
2
+ /** `ref={input}` of an element or a component use. */
3
+ export function refAttribute(node) {
4
+ return node.attributes.find((attribute) => attribute.kind === 'JsxAttribute' && attribute.name.name === 'ref');
5
+ }
6
+ /** The ref an element is bound to: `input` in `<input ref={input} />`. */
7
+ export function refName(node) {
8
+ const value = refAttribute(node)?.value;
9
+ return value?.kind === 'Identifier' ? value.name : null;
10
+ }
11
+ /**
12
+ * The attributes that are set where the ref is declared: strings and attributes without a value,
13
+ * before the first spread, which may override them. Values in braces, handlers and `bind:` are
14
+ * set by the markup.
15
+ */
16
+ export function constantAttributes(node) {
17
+ const found = [];
18
+ for (const attribute of node.attributes) {
19
+ if (attribute.kind === 'JsxSpreadAttribute')
20
+ break;
21
+ const { name, value } = attribute;
22
+ if (name.name === 'ref' || name.name.startsWith('bind:') || /^on[A-Z]/.test(name.name)) {
23
+ continue;
24
+ }
25
+ if (value === null || value.kind === 'StringLiteral')
26
+ found.push(attribute);
27
+ }
28
+ return found;
29
+ }
30
+ /** The `ref={...}` attributes in the markup of a body; see markupBindings. */
31
+ export function refBindings(body) {
32
+ return markupBindings(body, (attribute) => attribute.kind === 'JsxAttribute' && attribute.name.name === 'ref');
33
+ }
34
+ /**
35
+ * The attributes that `wanted` picks in the markup of a body, and whether that markup is created
36
+ * exactly once: what a `return` gives (also through the wrapper a decorator returns),
37
+ * `const x = <markup>` at the top level, and the elements and children of components inside them.
38
+ */
39
+ export function markupBindings(body, wanted) {
40
+ const found = [];
41
+ const markup = (node, repeated) => {
42
+ for (const attribute of node.attributes) {
43
+ if (wanted(attribute)) {
44
+ found.push({ attribute, element: node, repeated });
45
+ }
46
+ else {
47
+ visit(attribute, repeated ?? 'expression');
48
+ }
49
+ }
50
+ for (const child of node.children) {
51
+ // The children of a component are created by the markup that uses it, once.
52
+ if (child.kind === 'ElementExpression')
53
+ markup(child, repeated);
54
+ else if (child.kind !== 'JsxStatementContainer')
55
+ visit(child, repeated ?? 'expression');
56
+ else
57
+ visit(child.statement, flowOf(child.statement));
58
+ }
59
+ };
60
+ const visit = (node, repeated) => {
61
+ switch (node.kind) {
62
+ case 'ElementExpression':
63
+ markup(node, repeated);
64
+ return;
65
+ case 'ReturnStatement':
66
+ // An early return of the body runs once, wherever it is.
67
+ if (repeated === 'block')
68
+ returned(node);
69
+ else
70
+ forEachChild(node, (child) => visit(child, repeated));
71
+ return;
72
+ case 'IfStatement':
73
+ case 'ForStatement':
74
+ case 'ForInStatement':
75
+ case 'SwitchStatement': {
76
+ // Control flow in markup; that of the body is a block, which runs once or in a loop.
77
+ const inner = repeated === 'block' || repeated === 'function' ? repeated : flowOf(node);
78
+ forEachChild(node, (child) => visit(child, inner));
79
+ return;
80
+ }
81
+ case 'FuncDeclaration':
82
+ case 'FuncExpression':
83
+ case 'ArrowFunction':
84
+ case 'EventHandler':
85
+ case 'MountStatement':
86
+ case 'EffectStatement':
87
+ case 'ClassDeclaration':
88
+ forEachChild(node, (child) => visit(child, 'function'));
89
+ return;
90
+ default:
91
+ forEachChild(node, (child) => visit(child, repeated));
92
+ }
93
+ };
94
+ const returned = (node) => {
95
+ for (const value of node.values) {
96
+ if (value.kind === 'ElementExpression')
97
+ markup(value, null);
98
+ else if (value.kind === 'ArrowFunction' || value.kind === 'FuncExpression')
99
+ wrapper(value);
100
+ else
101
+ visit(value, 'expression');
102
+ }
103
+ };
104
+ // The wrapper of a decorator runs once, where the component returns.
105
+ const wrapper = (fn) => {
106
+ for (const param of fn.params)
107
+ visit(param, 'function');
108
+ if (fn.body.kind === 'BlockStatement')
109
+ statements(fn.body.body);
110
+ else if (fn.body.kind === 'ElementExpression')
111
+ markup(fn.body, null);
112
+ else
113
+ visit(fn.body, 'expression');
114
+ };
115
+ const statements = (list) => {
116
+ for (const statement of list) {
117
+ const [value] = statement.kind === 'VariableDeclaration' ? statement.values : [];
118
+ if (statement.kind === 'ReturnStatement') {
119
+ returned(statement);
120
+ }
121
+ else if (value?.kind === 'ElementExpression' && statement.kind === 'VariableDeclaration') {
122
+ if (statement.names.length === 1)
123
+ markup(value, null);
124
+ else
125
+ visit(statement, 'block');
126
+ }
127
+ else {
128
+ visit(statement, 'block');
129
+ }
130
+ }
131
+ };
132
+ statements(body.body);
133
+ return found;
134
+ }
135
+ function flowOf(node) {
136
+ return node.kind === 'IfStatement' ? 'if' : node.kind === 'SwitchStatement' ? 'switch' : 'for';
137
+ }
138
+ /**
139
+ * For each ref of a checked body, the element its markup binds it to. The wrapper of a decorator
140
+ * is copied to every `return` of a component, so there may be several, all alike.
141
+ */
142
+ export function refTargets(body) {
143
+ const targets = new Map();
144
+ const visit = (node) => {
145
+ const name = node.kind === 'ElementExpression' ? refName(node) : null;
146
+ if (name !== null && !targets.has(name))
147
+ targets.set(name, node);
148
+ forEachChild(node, visit);
149
+ };
150
+ visit(body);
151
+ return targets;
152
+ }
153
+ /**
154
+ * What a component returns, if it is known in advance: an element or the use of a component in
155
+ * its own markup. Through `const content = <markup>`, which is how the wrapper of a decorator gets
156
+ * it. Otherwise why it is not known, as a phrase after "it".
157
+ */
158
+ export function ownRoot(component) {
159
+ const returns = [];
160
+ const visit = (node) => {
161
+ if (node.kind === 'ReturnStatement')
162
+ returns.push(node);
163
+ if (node.kind !== 'FuncDeclaration' &&
164
+ node.kind !== 'FuncExpression' &&
165
+ node.kind !== 'ArrowFunction' &&
166
+ node.kind !== 'ClassDeclaration' &&
167
+ node.kind !== 'MountStatement' &&
168
+ node.kind !== 'EffectStatement') {
169
+ forEachChild(node, visit);
170
+ }
171
+ };
172
+ forEachChild(component.body, visit);
173
+ if (returns.length > 1)
174
+ return 'has several returns, so its element is not known in advance';
175
+ const constants = new Map();
176
+ for (const statement of component.body.body) {
177
+ const [name] = statement.kind === 'VariableDeclaration' ? statement.names : [];
178
+ if (statement.kind === 'VariableDeclaration' && statement.keyword === 'const' && name) {
179
+ if (statement.names.length === 1 && statement.values.length === 1) {
180
+ constants.set(name.name, statement.values[0]);
181
+ }
182
+ }
183
+ }
184
+ let [value] = returns[0]?.values ?? [];
185
+ const seen = new Set();
186
+ while (value?.kind === 'Identifier' && constants.has(value.name) && !seen.has(value.name)) {
187
+ seen.add(value.name);
188
+ value = constants.get(value.name);
189
+ }
190
+ if (value?.kind !== 'ElementExpression') {
191
+ return 'does not return markup of its own, so its element is not known in advance';
192
+ }
193
+ return value.tag === null ? 'returns a fragment, which is empty once inserted' : value;
194
+ }
195
+ /** `<Card />`, or a component from a value: `<section.content />`, `<Page />`. */
196
+ export function isComponentUse(node) {
197
+ return node.value !== null || (node.tag !== null && /^[A-Z]/.test(node.tag.name));
198
+ }
199
+ /**
200
+ * The element at the top of what a component creates, through the components it returns.
201
+ * `rootOf` gives that of another component by its tag.
202
+ */
203
+ export function componentRoot(component, rootOf) {
204
+ const own = ownRoot(component);
205
+ if (typeof own === 'string' || !isComponentUse(own))
206
+ return own;
207
+ const tag = own.tag.name;
208
+ const inner = rootOf(tag) ?? 'is not known in advance';
209
+ return typeof inner === 'string' ? `returns <${tag}>, which ${inner}` : inner;
210
+ }
package/dist/run.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};