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,187 @@
1
+ import { check, } from "./checker/checker.js";
2
+ import { property, STRING } from "./checker/types.js";
3
+ import { generateJs } from "./codegen/js.js";
4
+ import { printDeclarations } from "./declarations.js";
5
+ import { goroutines, goTarget, workerProgram } from "./goroutines.js";
6
+ import { parse } from "./parser/parser.js";
7
+ import { isStyleModule, styleModule, unusedClasses } from "./style-modules.js";
8
+ /**
9
+ * Where the worker module of a module is: a `.worker.js` next to its `.js`, or, where `.rk`
10
+ * files are imported as they are (`rook run`), the module itself with `?goroutines`. Vite drops
11
+ * the query of a worker's URL, so its plugin gives another path (vite.ts).
12
+ */
13
+ export function workerUrl(file, rewriteImports) {
14
+ const base = file.replace(/^.*[\\/]/, '').replace(/\.rk$/, '');
15
+ return rewriteImports ? `./${base}.worker.js` : `./${base}.rk?goroutines`;
16
+ }
17
+ export function compileModule(source, options = {}) {
18
+ const { program, diagnostics } = parse(source, { markup: options.markup ?? true });
19
+ if (diagnostics.length > 0)
20
+ return { code: '', diagnostics, warnings: [], dependencies: [] };
21
+ const dependencies = localImports(program);
22
+ const { styleModules, problems, checkOptions } = prepareCheck(program, options);
23
+ if (problems.length > 0)
24
+ return { code: '', diagnostics: problems, warnings: [], dependencies };
25
+ let types;
26
+ let declarations;
27
+ let warnings = [];
28
+ if (options.typeCheck ?? true) {
29
+ const checked = check(program, checkOptions);
30
+ warnings = [...checked.warnings, ...unusedStyleClasses(program, styleModules)].sort((a, b) => a.start - b.start);
31
+ if (checked.diagnostics.length > 0) {
32
+ return { code: '', diagnostics: checked.diagnostics, warnings, dependencies };
33
+ }
34
+ types = checked.types;
35
+ if (options.declarations)
36
+ declarations = printDeclarations(program, checked);
37
+ }
38
+ const rewriteImports = options.rewriteImports ?? true;
39
+ const jsOptions = {
40
+ source,
41
+ rewriteImports,
42
+ types,
43
+ styleModules,
44
+ ...importedCode(program, options.importModule),
45
+ };
46
+ const code = generateJs(program, {
47
+ ...jsOptions,
48
+ goroutines: { workerUrl: options.workerUrl ?? workerUrl('main.rk', rewriteImports) },
49
+ });
50
+ const targets = [
51
+ ...new Set(goroutines(program).flatMap((node) => goTarget(program, node) ?? [])),
52
+ ];
53
+ const worker = targets.length > 0
54
+ ? generateJs(workerProgram(program, targets), { ...jsOptions, goroutines: { targets } })
55
+ : undefined;
56
+ return {
57
+ code,
58
+ diagnostics: [],
59
+ warnings,
60
+ dependencies,
61
+ ...(declarations ? { declarations } : {}),
62
+ ...(worker === undefined ? {} : { worker }),
63
+ };
64
+ }
65
+ /**
66
+ * The options of the checker for a program: its imports, with the CSS modules it imports, which
67
+ * are read and parsed here (or what is wrong with them), and the library.
68
+ */
69
+ export function prepareCheck(program, options) {
70
+ const { styleModules, problems } = readStyleModules(program, options.readStyleModule);
71
+ const { importModule, importDeclarations, library } = options;
72
+ // A CSS module has a default export: an object with a string for each of its classes.
73
+ const importStyles = (specifier) => {
74
+ const module = styleModules.get(specifier);
75
+ return module
76
+ ? { exports: styleModuleExports(specifier, module) }
77
+ : importDeclarations?.(specifier);
78
+ };
79
+ return {
80
+ styleModules,
81
+ problems,
82
+ checkOptions: {
83
+ ...(importModule ? { importModule } : {}),
84
+ importDeclarations: importStyles,
85
+ ...(library ? { library } : {}),
86
+ },
87
+ };
88
+ }
89
+ /**
90
+ * What the module imports from other `.rk` modules for the generator: decorators, whose code is
91
+ * inlined, and components, which are called or inlined.
92
+ */
93
+ function importedCode(program, importModule) {
94
+ const found = new Map();
95
+ const components = new Map();
96
+ for (const statement of program.body) {
97
+ if (statement.kind !== 'ImportDeclaration' || !statement.source.value.endsWith('.rk')) {
98
+ continue;
99
+ }
100
+ const result = importModule?.(statement.source.value);
101
+ const exports = result && 'exports' in result ? result.exports : undefined;
102
+ const decorators = exports?.decorators;
103
+ for (const specifier of statement.namedImports) {
104
+ const component = exports?.components?.get(specifier.imported.name);
105
+ if (component) {
106
+ components.set(specifier.local.name, {
107
+ node: component.node,
108
+ inline: component.inline,
109
+ types: component.expressionTypes,
110
+ root: component.root ?? 'is not known in advance',
111
+ });
112
+ }
113
+ const info = decorators?.get(specifier.imported.name);
114
+ if (!info)
115
+ continue;
116
+ found.set(specifier.local.name, {
117
+ node: info.node,
118
+ moduleNames: info.moduleNames,
119
+ types: info.expressionTypes,
120
+ inline: info.inline,
121
+ });
122
+ }
123
+ }
124
+ return { decorators: found, components };
125
+ }
126
+ /** The CSS modules that the program imports, read and parsed, or what is wrong with them. */
127
+ function readStyleModules(program, read) {
128
+ const styleModules = new Map();
129
+ const problems = [];
130
+ for (const statement of program.body) {
131
+ if (statement.kind !== 'ImportDeclaration' || !isStyleModule(statement.source.value))
132
+ continue;
133
+ const { source } = statement;
134
+ const text = read?.(source.value) ?? null;
135
+ const problem = (message) => problems.push({ message, start: source.start, end: source.end });
136
+ if (text === null) {
137
+ problem(read
138
+ ? `cannot read the CSS module "${source.value}"`
139
+ : 'CSS modules need files: build the program with rook build or Vite');
140
+ continue;
141
+ }
142
+ const module = styleModule(text);
143
+ for (const message of module.problems)
144
+ problem(`${source.value}: ${message}`);
145
+ styleModules.set(source.value, module);
146
+ }
147
+ return { styleModules, problems };
148
+ }
149
+ function styleModuleExports(specifier, module) {
150
+ const members = new Map([...module.classes.keys()].map((name) => [name, property(STRING)]));
151
+ const name = specifier.split('/').at(-1);
152
+ return {
153
+ values: new Map([['default', { kind: 'object', name, members, call: null }]]),
154
+ types: new Map(),
155
+ };
156
+ }
157
+ function unusedStyleClasses(program, styleModules) {
158
+ const found = [];
159
+ for (const statement of program.body) {
160
+ if (statement.kind !== 'ImportDeclaration' || !statement.defaultImport)
161
+ continue;
162
+ const module = styleModules.get(statement.source.value);
163
+ if (!module)
164
+ continue;
165
+ for (const name of unusedClasses(program, statement.defaultImport.name, module)) {
166
+ found.push({
167
+ message: `class "${name}" of ${statement.source.value} is never used`,
168
+ start: statement.source.start,
169
+ end: statement.source.end,
170
+ });
171
+ }
172
+ }
173
+ return found;
174
+ }
175
+ /** The relative imports of `.rk` modules and CSS modules: what the module depends on. */
176
+ function localImports(program) {
177
+ const specifiers = new Set();
178
+ for (const statement of program.body) {
179
+ if (statement.kind !== 'ImportDeclaration')
180
+ continue;
181
+ const path = statement.source.value;
182
+ if (/^\.{1,2}\//.test(path) && (path.endsWith('.rk') || isStyleModule(path))) {
183
+ specifiers.add(path);
184
+ }
185
+ }
186
+ return [...specifiers];
187
+ }
@@ -0,0 +1,10 @@
1
+ import { type Completion } from './checker/checker.ts';
2
+ import { type CoreCompileOptions } from './compile.ts';
3
+ export type { Completion, CompletionContext } from './checker/checker.ts';
4
+ export interface CompletionList {
5
+ /** Offsets of what the chosen item replaces: the part of the name before the cursor. */
6
+ start: number;
7
+ end: number;
8
+ items: Completion[];
9
+ }
10
+ export declare function completeModule(source: string, offset: number, options?: CoreCompileOptions): CompletionList;
@@ -0,0 +1,134 @@
1
+ import { check } from "./checker/checker.js";
2
+ import { prepareCheck } from "./compile.js";
3
+ import { tokenize } from "./lexer/lexer.js";
4
+ import { KEYWORDS } from "./lexer/token.js";
5
+ import { parse } from "./parser/parser.js";
6
+ import { RESERVED } from "./parser/syntax.js";
7
+ import { forEachChild } from "./walk.js";
8
+ const CURSOR = '$$cursor';
9
+ const STRINGS = new Set([
10
+ 'String',
11
+ 'Template',
12
+ 'TemplateHead',
13
+ 'TemplateMiddle',
14
+ 'TemplateTail',
15
+ 'JsxText',
16
+ 'JsxString',
17
+ 'CssText',
18
+ ]);
19
+ const VALUE_KEYWORDS = KEYWORDS.filter((keyword) => !RESERVED.has(keyword));
20
+ const TYPE_KEYWORDS = ['func', 'comp', 'chan'];
21
+ /** Keywords only at the start of a statement at the top level of a component or a decorator. */
22
+ const COMPONENT_KEYWORDS = ['state', 'derived', 'ref', 'style', 'effect', 'mount'];
23
+ export function completeModule(source, offset, options = {}) {
24
+ const { tokens } = tokenize(source);
25
+ const empty = { start: offset, end: offset, items: [] };
26
+ const token = tokens.find((each) => each.start < offset && offset <= each.end);
27
+ if (token && STRINGS.has(token.kind) && offset < token.end)
28
+ return empty;
29
+ if (token?.kind === 'Number' || (!token && inComment(source, tokens, offset)))
30
+ return empty;
31
+ // The name being typed, `ti|tle`, is replaced as a whole.
32
+ const word = token && /^[\p{ID_Start}$_]/u.test(token.text) ? token : null;
33
+ const start = word?.start ?? offset;
34
+ const end = word?.end ?? offset;
35
+ const before = source.slice(0, start) + CURSOR;
36
+ const after = source.slice(end);
37
+ const nextLines = after.replace(/^.*/, '');
38
+ const line = source.slice(source.lastIndexOf('\n', start - 1) + 1, start);
39
+ const open = unclosed(source.slice(0, start));
40
+ const attempts = [
41
+ ...new Set([
42
+ before + after,
43
+ before + unclosed(line) + nextLines,
44
+ before + open + nextLines,
45
+ `${before + open} {}${nextLines}`,
46
+ ]),
47
+ ];
48
+ // `<Ca|` without the rest of the element yet.
49
+ if (word?.kind === 'JsxName' ||
50
+ tokens.some((each) => each.kind === 'JsxTagOpen' && each.end === start)) {
51
+ attempts.splice(1, 0, `${before} />${after}`);
52
+ }
53
+ for (const text of attempts) {
54
+ const { program } = parse(text, { markup: options.markup ?? true });
55
+ const result = completeIn(program, options);
56
+ if (result) {
57
+ return { start, end: offset, items: withKeywords(result, statementKeywords(program)) };
58
+ }
59
+ // The cursor is where a name is declared, `let ti|`: nothing to suggest.
60
+ if (pathTo(program, CURSOR).length > 0)
61
+ return empty;
62
+ }
63
+ return { start, end: offset, items: VALUE_KEYWORDS.map(keyword) };
64
+ }
65
+ function completeIn(program, options) {
66
+ const { checkOptions } = prepareCheck(program, options);
67
+ try {
68
+ return check(program, { ...checkOptions, completion: CURSOR }).completion ?? null;
69
+ }
70
+ catch {
71
+ // The tree that the parser recovered from errors may surprise the checker.
72
+ return null;
73
+ }
74
+ }
75
+ function withKeywords({ context, items }, statement) {
76
+ if (context === 'value')
77
+ return [...items, ...[...statement, ...VALUE_KEYWORDS].map(keyword)];
78
+ if (context === 'type')
79
+ return [...items, ...TYPE_KEYWORDS.map(keyword)];
80
+ return items;
81
+ }
82
+ function keyword(name) {
83
+ return { name, kind: 'keyword' };
84
+ }
85
+ /** Whether the offset is in a comment: comments are the only text between tokens. */
86
+ function inComment(source, tokens, offset) {
87
+ const previous = tokens.findLast((each) => each.end <= offset && each.text !== '');
88
+ const gap = source.slice(previous?.end ?? 0, offset);
89
+ const block = gap.lastIndexOf('/*');
90
+ return gap.slice(gap.lastIndexOf('\n') + 1).includes('//') || block > gap.lastIndexOf('*/');
91
+ }
92
+ /**
93
+ * Words that are keywords only at the start of a statement (see src/parser/statements.ts), when the
94
+ * cursor starts one: `select` anywhere, `dec` at the top level of the module and `state` and the
95
+ * like at the top level of a component or a decorator.
96
+ */
97
+ function statementKeywords(program) {
98
+ const path = pathTo(program, CURSOR);
99
+ const [statement, block, owner] = [path.at(-2), path.at(-3), path.at(-4)];
100
+ if (statement?.kind !== 'ExpressionStatement')
101
+ return [];
102
+ if (block?.kind === 'Program')
103
+ return ['select', 'dec'];
104
+ const component = owner?.kind === 'ComponentDeclaration' || owner?.kind === 'DecoratorDeclaration';
105
+ return component && block?.kind === 'BlockStatement'
106
+ ? ['select', ...COMPONENT_KEYWORDS]
107
+ : ['select'];
108
+ }
109
+ /** The nodes from `node` down to the identifier `name`, both included; empty if it is not there. */
110
+ function pathTo(node, name) {
111
+ if (node.kind === 'Identifier' && node.name === name)
112
+ return [node];
113
+ let path = [];
114
+ forEachChild(node, (child) => {
115
+ if (path.length === 0) {
116
+ const below = pathTo(child, name);
117
+ if (below.length > 0)
118
+ path = [node, ...below];
119
+ }
120
+ });
121
+ return path;
122
+ }
123
+ /** The brackets that `text` opens and does not close, closed. */
124
+ function unclosed(text) {
125
+ const open = [];
126
+ const pairs = { '(': ')', '[': ']', '{': '}' };
127
+ for (const { kind } of tokenize(text).tokens) {
128
+ if (kind in pairs)
129
+ open.push(pairs[kind]);
130
+ else if (kind === ')' || kind === ']' || kind === '}')
131
+ open.pop();
132
+ }
133
+ return open.reverse().join('');
134
+ }
@@ -0,0 +1,2 @@
1
+ /** Whether markup and components are on for a file. */
2
+ export declare function markupFor(filename: string | undefined): boolean;
package/dist/config.js ADDED
@@ -0,0 +1,44 @@
1
+ import { readFileSync, statSync } from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ const DEFAULTS = { markup: false };
4
+ /** package.json files read, with the time they were changed: the language server runs long. */
5
+ const read = new Map();
6
+ /** Whether markup and components are on for a file. */
7
+ export function markupFor(filename) {
8
+ return settingsFor(resolve(filename ? dirname(filename) : '.')).markup;
9
+ }
10
+ function settingsFor(start) {
11
+ let dir = start;
12
+ while (true) {
13
+ const settings = readSettings(join(dir, 'package.json'));
14
+ if (settings)
15
+ return settings;
16
+ if (dirname(dir) === dir)
17
+ return DEFAULTS;
18
+ dir = dirname(dir);
19
+ }
20
+ }
21
+ /** The settings in a package.json, or null if there is none. */
22
+ function readSettings(path) {
23
+ let changed;
24
+ try {
25
+ changed = statSync(path).mtimeMs;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ const cached = read.get(path);
31
+ if (cached?.changed === changed)
32
+ return cached.settings;
33
+ let field;
34
+ try {
35
+ field = JSON.parse(readFileSync(path, 'utf8')).rook;
36
+ }
37
+ catch {
38
+ field = undefined;
39
+ }
40
+ const markup = typeof field === 'object' && field !== null && 'markup' in field && field.markup;
41
+ const settings = { markup: markup === true };
42
+ read.set(path, { changed, settings });
43
+ return settings;
44
+ }
@@ -0,0 +1 @@
1
+ export declare function hash(text: string): string;
@@ -0,0 +1,10 @@
1
+ // A short hash of CSS text for class names: FNV-1a, the same in Node and in the browser, so the
2
+ // names do not depend on where or when the code is compiled.
3
+ export function hash(text) {
4
+ let value = 0x811c9dc5;
5
+ for (let i = 0; i < text.length; i++) {
6
+ value ^= text.charCodeAt(i);
7
+ value = Math.imul(value, 0x01000193);
8
+ }
9
+ return (value >>> 0).toString(36);
10
+ }
@@ -0,0 +1,42 @@
1
+ /** Stands for a value from code in the text of a style block. */
2
+ export declare const SLOT = "\uE000";
3
+ export interface CssRule {
4
+ kind: 'rule';
5
+ selector: string;
6
+ /** Offsets in the parsed text. */
7
+ start: number;
8
+ end: number;
9
+ body: CssNode[];
10
+ }
11
+ export interface CssAtRule {
12
+ kind: 'at-rule';
13
+ /** `media` for `@media (min-width: 600px) { ... }`. */
14
+ name: string;
15
+ prelude: string;
16
+ start: number;
17
+ end: number;
18
+ /** Null for a statement such as `@import url(a.css);`. */
19
+ body: CssNode[] | null;
20
+ }
21
+ export interface CssDeclaration {
22
+ kind: 'declaration';
23
+ property: string;
24
+ /** Text and the numbers of the values from code, in order: `0 {gap}px` → ["0 ", 0, "px"]. */
25
+ value: (string | number)[];
26
+ start: number;
27
+ end: number;
28
+ }
29
+ export type CssNode = CssRule | CssAtRule | CssDeclaration;
30
+ export interface CssProblem {
31
+ message: string;
32
+ start: number;
33
+ end: number;
34
+ }
35
+ export interface CssSheet {
36
+ nodes: CssNode[];
37
+ problems: CssProblem[];
38
+ }
39
+ /** Parses CSS; values from code are SLOT characters, numbered in order. */
40
+ export declare function parseCss(text: string): CssSheet;
41
+ /** At-rules whose blocks hold rules with selectors of the page, such as `@media`. */
42
+ export declare function holdsRules(rule: CssAtRule): boolean;
@@ -0,0 +1,138 @@
1
+ // CSS as a tree of rules, at-rules and declarations: enough to rewrite selectors and to put values
2
+ // from code into declarations. The text of selectors, at-rule preludes and values is kept as it is
3
+ // written, without comments. A value from code is SLOT in the text given to the parser.
4
+ /** Stands for a value from code in the text of a style block. */
5
+ export const SLOT = '';
6
+ /** Parses CSS; values from code are SLOT characters, numbered in order. */
7
+ export function parseCss(text) {
8
+ const problems = [];
9
+ let pos = 0;
10
+ let slots = 0;
11
+ const skipSpace = () => {
12
+ while (pos < text.length) {
13
+ if (/\s/.test(text[pos])) {
14
+ pos++;
15
+ }
16
+ else if (text.startsWith('/*', pos)) {
17
+ const end = text.indexOf('*/', pos + 2);
18
+ pos = end === -1 ? text.length : end + 2;
19
+ }
20
+ else {
21
+ break;
22
+ }
23
+ }
24
+ };
25
+ /** Reads up to `{`, `;` or `}` outside strings and brackets; comments are left out. */
26
+ const readItem = () => {
27
+ let item = '';
28
+ let depth = 0;
29
+ while (pos < text.length) {
30
+ const c = text[pos];
31
+ if (text.startsWith('/*', pos)) {
32
+ const end = text.indexOf('*/', pos + 2);
33
+ pos = end === -1 ? text.length : end + 2;
34
+ item += ' ';
35
+ continue;
36
+ }
37
+ if (c === '"' || c === "'") {
38
+ const start = pos;
39
+ pos++;
40
+ while (pos < text.length && text[pos] !== c && text[pos] !== '\n') {
41
+ pos += text[pos] === '\\' ? 2 : 1;
42
+ }
43
+ pos++;
44
+ item += text.slice(start, pos);
45
+ continue;
46
+ }
47
+ if (c === '(' || c === '[')
48
+ depth++;
49
+ if (c === ')' || c === ']')
50
+ depth--;
51
+ if (depth <= 0 && (c === '{' || c === ';' || c === '}'))
52
+ break;
53
+ item += c;
54
+ pos++;
55
+ }
56
+ return item;
57
+ };
58
+ const block = (inRule) => {
59
+ const nodes = [];
60
+ while (true) {
61
+ skipSpace();
62
+ if (pos >= text.length || text[pos] === '}')
63
+ return nodes;
64
+ const start = pos;
65
+ const item = readItem().trim();
66
+ const end = pos;
67
+ const next = text[pos];
68
+ if (next === '{') {
69
+ pos++;
70
+ const body = block(true);
71
+ if (text[pos] === '}')
72
+ pos++;
73
+ nodes.push(item.startsWith('@')
74
+ ? { kind: 'at-rule', ...atRule(item), start, end, body }
75
+ : { kind: 'rule', selector: item, start, end, body });
76
+ continue;
77
+ }
78
+ if (next === ';')
79
+ pos++;
80
+ if (item === '')
81
+ continue;
82
+ if (item.startsWith('@')) {
83
+ nodes.push({ kind: 'at-rule', ...atRule(item), start, end, body: null });
84
+ continue;
85
+ }
86
+ const colon = item.indexOf(':');
87
+ if (colon <= 0) {
88
+ problems.push({
89
+ message: `expected a declaration such as "color: red", found "${item}"`,
90
+ start,
91
+ end,
92
+ });
93
+ }
94
+ else if (!inRule) {
95
+ problems.push({
96
+ message: `a declaration needs a rule around it, e.g. .card { ${item} }`,
97
+ start,
98
+ end,
99
+ });
100
+ }
101
+ if (colon > 0) {
102
+ nodes.push({
103
+ kind: 'declaration',
104
+ property: item.slice(0, colon).trim(),
105
+ value: valueParts(item.slice(colon + 1).trim()),
106
+ start,
107
+ end,
108
+ });
109
+ }
110
+ }
111
+ };
112
+ const valueParts = (value) => {
113
+ const parts = [];
114
+ value.split(SLOT).forEach((piece, i) => {
115
+ if (i > 0)
116
+ parts.push(slots++);
117
+ if (piece !== '')
118
+ parts.push(piece);
119
+ });
120
+ return parts;
121
+ };
122
+ const nodes = block(false);
123
+ while (pos < text.length) {
124
+ // A `}` without its `{`: report it and go on after it.
125
+ problems.push({ message: 'unexpected "}" in CSS', start: pos, end: pos + 1 });
126
+ pos++;
127
+ nodes.push(...block(false));
128
+ }
129
+ return { nodes, problems };
130
+ }
131
+ function atRule(item) {
132
+ const name = /^@([\w-]*)/.exec(item)[1];
133
+ return { name, prelude: item.slice(name.length + 1).trim() };
134
+ }
135
+ /** At-rules whose blocks hold rules with selectors of the page, such as `@media`. */
136
+ export function holdsRules(rule) {
137
+ return ['media', 'supports', 'container', 'layer', 'scope', 'document'].includes(rule.name);
138
+ }
@@ -0,0 +1,9 @@
1
+ import { type CssDeclaration, type CssNode } from './parse.ts';
2
+ export interface PrintOptions {
3
+ /** The selector of a rule; not called inside `@keyframes`, whose selectors are steps. */
4
+ selector: (selector: string) => string;
5
+ value: (declaration: CssDeclaration) => string;
6
+ }
7
+ export declare function printCss(nodes: readonly CssNode[], options: PrintOptions): string;
8
+ /** The text of a value with its values from code given by `slot`. */
9
+ export declare function valueText(declaration: CssDeclaration, slot: (index: number) => string): string;
@@ -0,0 +1,35 @@
1
+ import { holdsRules } from "./parse.js";
2
+ export function printCss(nodes, options) {
3
+ const lines = [];
4
+ const print = (list, indent, steps) => {
5
+ for (const node of list) {
6
+ switch (node.kind) {
7
+ case 'declaration':
8
+ lines.push(`${indent}${node.property}: ${options.value(node)};`);
9
+ break;
10
+ case 'rule':
11
+ lines.push(`${indent}${steps ? node.selector : options.selector(node.selector)} {`);
12
+ print(node.body, `${indent} `, steps);
13
+ lines.push(`${indent}}`);
14
+ break;
15
+ case 'at-rule': {
16
+ const head = `${indent}@${node.name}${node.prelude ? ` ${node.prelude}` : ''}`;
17
+ if (node.body === null) {
18
+ lines.push(`${head};`);
19
+ break;
20
+ }
21
+ lines.push(`${head} {`);
22
+ // The rules of @media keep their selectors; @keyframes has steps, @font-face none.
23
+ print(node.body, `${indent} `, steps || !holdsRules(node));
24
+ lines.push(`${indent}}`);
25
+ }
26
+ }
27
+ }
28
+ };
29
+ print(nodes, '', false);
30
+ return lines.join('\n');
31
+ }
32
+ /** The text of a value with its values from code given by `slot`. */
33
+ export function valueText(declaration, slot) {
34
+ return declaration.value.map((part) => (typeof part === 'number' ? slot(part) : part)).join('');
35
+ }
@@ -0,0 +1,29 @@
1
+ /** A selector list split at the commas outside brackets. */
2
+ export declare function splitList(selector: string): string[];
3
+ /**
4
+ * A complex selector as compound selectors and the combinators between them:
5
+ * `ul > li.item a` → ["ul", " > ", "li.item", " ", "a"]. A relative selector starts with a
6
+ * combinator: `> a` → ["", "> ", "a"].
7
+ */
8
+ export declare function splitComplex(selector: string): string[];
9
+ /**
10
+ * Gives every compound selector the class of a component, before its pseudo-elements, so that the
11
+ * rule applies only to its elements. Compounds with `&` refer to the rule around them, and those
12
+ * in `:global(...)` are left to the page.
13
+ */
14
+ export declare function scopeSelector(selector: string, scope: string): string;
15
+ /** `:global(.a) .b` → `.a .b`. */
16
+ export declare function unwrapGlobal(selector: string): string;
17
+ /**
18
+ * The classes of a CSS module get its names: `.button:hover` → `.button_k3f9:hover`. Classes in
19
+ * `:global(...)` keep their names. Calls `found` for every local class.
20
+ */
21
+ export declare function renameClasses(selector: string, rename: (name: string) => string, found: (name: string) => void): string;
22
+ /** What a compound selector asks of an element, for checking that some element can match it. */
23
+ export interface CompoundRequirements {
24
+ tag: string | null;
25
+ classes: string[];
26
+ ids: string[];
27
+ }
28
+ /** The tag, classes and ids a compound requires; those inside `:not(...)` and others are not. */
29
+ export declare function requirements(compound: string): CompoundRequirements;