@amxts/core 0.1.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 (247) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +162 -0
  3. package/README.ru.md +162 -0
  4. package/amxmodx/base/include/amxconst.inc +594 -0
  5. package/amxmodx/base/include/amxmisc.inc +902 -0
  6. package/amxmodx/base/include/amxmodx.inc +3447 -0
  7. package/amxmodx/base/include/amxmodx_version.inc +20 -0
  8. package/amxmodx/base/include/cellarray.inc +525 -0
  9. package/amxmodx/base/include/cellstack.inc +166 -0
  10. package/amxmodx/base/include/celltrie.inc +412 -0
  11. package/amxmodx/base/include/core.inc +202 -0
  12. package/amxmodx/base/include/csstats.inc +298 -0
  13. package/amxmodx/base/include/csstats_const.inc +29 -0
  14. package/amxmodx/base/include/cstrike.inc +1287 -0
  15. package/amxmodx/base/include/cstrike_const.inc +521 -0
  16. package/amxmodx/base/include/csx.inc +259 -0
  17. package/amxmodx/base/include/cvars.inc +547 -0
  18. package/amxmodx/base/include/datapack.inc +161 -0
  19. package/amxmodx/base/include/dbi.inc +154 -0
  20. package/amxmodx/base/include/dodconst.inc +152 -0
  21. package/amxmodx/base/include/dodfun.inc +152 -0
  22. package/amxmodx/base/include/dodstats.inc +69 -0
  23. package/amxmodx/base/include/dodx.inc +161 -0
  24. package/amxmodx/base/include/engine.inc +1343 -0
  25. package/amxmodx/base/include/engine_const.inc +310 -0
  26. package/amxmodx/base/include/engine_stocks.inc +248 -0
  27. package/amxmodx/base/include/esf.inc +70 -0
  28. package/amxmodx/base/include/esf_const.inc +74 -0
  29. package/amxmodx/base/include/fakemeta.inc +1127 -0
  30. package/amxmodx/base/include/fakemeta_const.inc +793 -0
  31. package/amxmodx/base/include/fakemeta_stocks.inc +395 -0
  32. package/amxmodx/base/include/fakemeta_util.inc +882 -0
  33. package/amxmodx/base/include/file.inc +666 -0
  34. package/amxmodx/base/include/float.inc +422 -0
  35. package/amxmodx/base/include/fun.inc +333 -0
  36. package/amxmodx/base/include/gameconfig.inc +93 -0
  37. package/amxmodx/base/include/geoip.inc +264 -0
  38. package/amxmodx/base/include/ham_const.inc +4352 -0
  39. package/amxmodx/base/include/hamsandwich.inc +439 -0
  40. package/amxmodx/base/include/hlsdk_const.inc +808 -0
  41. package/amxmodx/base/include/json.inc +777 -0
  42. package/amxmodx/base/include/lang.inc +119 -0
  43. package/amxmodx/base/include/message_const.inc +1130 -0
  44. package/amxmodx/base/include/message_stocks.inc +161 -0
  45. package/amxmodx/base/include/messages.inc +604 -0
  46. package/amxmodx/base/include/newmenus.inc +387 -0
  47. package/amxmodx/base/include/ns.inc +754 -0
  48. package/amxmodx/base/include/ns_const.inc +196 -0
  49. package/amxmodx/base/include/nvault.inc +154 -0
  50. package/amxmodx/base/include/regex.inc +348 -0
  51. package/amxmodx/base/include/sockets.inc +183 -0
  52. package/amxmodx/base/include/sorting.inc +107 -0
  53. package/amxmodx/base/include/sqlx.inc +582 -0
  54. package/amxmodx/base/include/string.inc +786 -0
  55. package/amxmodx/base/include/string_const.inc +156 -0
  56. package/amxmodx/base/include/string_stocks.inc +325 -0
  57. package/amxmodx/base/include/textparse_ini.inc +214 -0
  58. package/amxmodx/base/include/textparse_smc.inc +261 -0
  59. package/amxmodx/base/include/tfcconst.inc +84 -0
  60. package/amxmodx/base/include/tfcstats.inc +62 -0
  61. package/amxmodx/base/include/tfcx.inc +148 -0
  62. package/amxmodx/base/include/time.inc +118 -0
  63. package/amxmodx/base/include/tsconst.inc +154 -0
  64. package/amxmodx/base/include/tsfun.inc +198 -0
  65. package/amxmodx/base/include/tsstats.inc +65 -0
  66. package/amxmodx/base/include/tsx.inc +88 -0
  67. package/amxmodx/base/include/vault.inc +60 -0
  68. package/amxmodx/base/include/vector.inc +131 -0
  69. package/amxmodx/base/include/xs.inc +1570 -0
  70. package/as/amxts.d.ts +153 -0
  71. package/as/constants.ts +4730 -0
  72. package/as/effects.ts +1449 -0
  73. package/as/entities.ts +5482 -0
  74. package/as/events.ts +3054 -0
  75. package/as/facade.ts +4639 -0
  76. package/as/flags.ts +299 -0
  77. package/as/fs.ts +287 -0
  78. package/as/hooks.ts +18536 -0
  79. package/as/kit.ts +25 -0
  80. package/as/lib/check.ts +86 -0
  81. package/as/modules/http.ts +184 -0
  82. package/as/natives.ts +11845 -0
  83. package/as/os.ts +30 -0
  84. package/as/promise.ts +1392 -0
  85. package/as/promise.types.d.ts +82 -0
  86. package/as/remote.ts +288 -0
  87. package/as/tsconfig.json +22 -0
  88. package/as/vector.ts +65 -0
  89. package/bin/amxts.mjs +10 -0
  90. package/includes/menu_core.inc +355 -0
  91. package/includes/order.txt +54 -0
  92. package/includes/sources.json +27 -0
  93. package/includes/universal_config.inc +355 -0
  94. package/lint/oxlint-plugin.mjs +77 -0
  95. package/node_modules/assemblyscript/LICENSE +201 -0
  96. package/node_modules/assemblyscript/NOTICE +93 -0
  97. package/node_modules/assemblyscript/package.json +63 -0
  98. package/node_modules/assemblyscript/std/README.md +6 -0
  99. package/node_modules/assemblyscript/std/assembly/array.ts +526 -0
  100. package/node_modules/assemblyscript/std/assembly/arraybuffer.ts +77 -0
  101. package/node_modules/assemblyscript/std/assembly/atomics.ts +127 -0
  102. package/node_modules/assemblyscript/std/assembly/bindings/asyncify.ts +16 -0
  103. package/node_modules/assemblyscript/std/assembly/bindings/dom.ts +291 -0
  104. package/node_modules/assemblyscript/std/assembly/bindings/node.ts +6 -0
  105. package/node_modules/assemblyscript/std/assembly/builtins.ts +2631 -0
  106. package/node_modules/assemblyscript/std/assembly/compat.ts +2 -0
  107. package/node_modules/assemblyscript/std/assembly/console.ts +42 -0
  108. package/node_modules/assemblyscript/std/assembly/crypto.ts +9 -0
  109. package/node_modules/assemblyscript/std/assembly/dataview.ts +181 -0
  110. package/node_modules/assemblyscript/std/assembly/date.ts +373 -0
  111. package/node_modules/assemblyscript/std/assembly/diagnostics.ts +11 -0
  112. package/node_modules/assemblyscript/std/assembly/error.ts +44 -0
  113. package/node_modules/assemblyscript/std/assembly/function.ts +38 -0
  114. package/node_modules/assemblyscript/std/assembly/index.d.ts +2822 -0
  115. package/node_modules/assemblyscript/std/assembly/iterator.ts +35 -0
  116. package/node_modules/assemblyscript/std/assembly/map.ts +260 -0
  117. package/node_modules/assemblyscript/std/assembly/math.ts +3289 -0
  118. package/node_modules/assemblyscript/std/assembly/memory.ts +123 -0
  119. package/node_modules/assemblyscript/std/assembly/number.ts +388 -0
  120. package/node_modules/assemblyscript/std/assembly/object.ts +36 -0
  121. package/node_modules/assemblyscript/std/assembly/performance.ts +9 -0
  122. package/node_modules/assemblyscript/std/assembly/polyfills.ts +27 -0
  123. package/node_modules/assemblyscript/std/assembly/process.ts +50 -0
  124. package/node_modules/assemblyscript/std/assembly/reference.ts +48 -0
  125. package/node_modules/assemblyscript/std/assembly/regexp.ts +12 -0
  126. package/node_modules/assemblyscript/std/assembly/rt/README.md +83 -0
  127. package/node_modules/assemblyscript/std/assembly/rt/common.ts +81 -0
  128. package/node_modules/assemblyscript/std/assembly/rt/index-incremental.ts +2 -0
  129. package/node_modules/assemblyscript/std/assembly/rt/index-minimal.ts +2 -0
  130. package/node_modules/assemblyscript/std/assembly/rt/index-stub.ts +1 -0
  131. package/node_modules/assemblyscript/std/assembly/rt/index.d.ts +37 -0
  132. package/node_modules/assemblyscript/std/assembly/rt/itcms.ts +419 -0
  133. package/node_modules/assemblyscript/std/assembly/rt/rtrace.ts +15 -0
  134. package/node_modules/assemblyscript/std/assembly/rt/stub.ts +133 -0
  135. package/node_modules/assemblyscript/std/assembly/rt/tcms.ts +254 -0
  136. package/node_modules/assemblyscript/std/assembly/rt/tlsf.ts +592 -0
  137. package/node_modules/assemblyscript/std/assembly/rt.ts +87 -0
  138. package/node_modules/assemblyscript/std/assembly/set.ts +220 -0
  139. package/node_modules/assemblyscript/std/assembly/shared/feature.ts +65 -0
  140. package/node_modules/assemblyscript/std/assembly/shared/runtime.ts +11 -0
  141. package/node_modules/assemblyscript/std/assembly/shared/target.ts +11 -0
  142. package/node_modules/assemblyscript/std/assembly/shared/tsconfig.json +11 -0
  143. package/node_modules/assemblyscript/std/assembly/shared/typeinfo.ts +72 -0
  144. package/node_modules/assemblyscript/std/assembly/staticarray.ts +401 -0
  145. package/node_modules/assemblyscript/std/assembly/string.ts +847 -0
  146. package/node_modules/assemblyscript/std/assembly/symbol.ts +114 -0
  147. package/node_modules/assemblyscript/std/assembly/table.ts +16 -0
  148. package/node_modules/assemblyscript/std/assembly/tsconfig.json +6 -0
  149. package/node_modules/assemblyscript/std/assembly/typedarray.ts +1945 -0
  150. package/node_modules/assemblyscript/std/assembly/uri.ts +17 -0
  151. package/node_modules/assemblyscript/std/assembly/util/bytes.ts +107 -0
  152. package/node_modules/assemblyscript/std/assembly/util/casemap.ts +497 -0
  153. package/node_modules/assemblyscript/std/assembly/util/error.ts +54 -0
  154. package/node_modules/assemblyscript/std/assembly/util/hash.ts +117 -0
  155. package/node_modules/assemblyscript/std/assembly/util/math.ts +1922 -0
  156. package/node_modules/assemblyscript/std/assembly/util/memory.ts +290 -0
  157. package/node_modules/assemblyscript/std/assembly/util/number.ts +873 -0
  158. package/node_modules/assemblyscript/std/assembly/util/sort.ts +313 -0
  159. package/node_modules/assemblyscript/std/assembly/util/string.ts +1202 -0
  160. package/node_modules/assemblyscript/std/assembly/util/uri.ts +275 -0
  161. package/node_modules/assemblyscript/std/assembly/vector.ts +4 -0
  162. package/node_modules/assemblyscript/std/assembly.json +16 -0
  163. package/node_modules/assemblyscript/std/portable/index.d.ts +462 -0
  164. package/node_modules/assemblyscript/std/portable/index.js +416 -0
  165. package/node_modules/assemblyscript/std/portable.json +11 -0
  166. package/node_modules/assemblyscript/std/types/assembly/index.d.ts +1 -0
  167. package/node_modules/assemblyscript/std/types/assembly/package.json +3 -0
  168. package/node_modules/assemblyscript/std/types/portable/index.d.ts +1 -0
  169. package/node_modules/assemblyscript/std/types/portable/package.json +3 -0
  170. package/node_modules/assemblyscript/tsconfig-base.json +13 -0
  171. package/package.json +112 -0
  172. package/runtime/deps/assemblyscript/LICENSE +201 -0
  173. package/runtime/deps/assemblyscript/NOTICE +93 -0
  174. package/runtime/deps/assemblyscript/bin/asc.js +35 -0
  175. package/runtime/deps/assemblyscript/dist/asc.js +25035 -0
  176. package/runtime/deps/assemblyscript/dist/assemblyscript.js +337 -0
  177. package/runtime/deps/assemblyscript/node_modules/binaryen/LICENSE +201 -0
  178. package/runtime/deps/assemblyscript/node_modules/binaryen/index.js +28905 -0
  179. package/runtime/deps/assemblyscript/node_modules/binaryen/package.json +50 -0
  180. package/runtime/deps/assemblyscript/node_modules/long/LICENSE +202 -0
  181. package/runtime/deps/assemblyscript/node_modules/long/index.js +1581 -0
  182. package/runtime/deps/assemblyscript/node_modules/long/package.json +58 -0
  183. package/runtime/deps/assemblyscript/package.json +114 -0
  184. package/runtime/host/amxts.inc +85 -0
  185. package/runtime/licenses/AMXX-LICENSE.txt +34 -0
  186. package/runtime/licenses/AssemblyScript-LICENSE +201 -0
  187. package/runtime/licenses/AssemblyScript-NOTICE +93 -0
  188. package/runtime/licenses/Binaryen-LICENSE +201 -0
  189. package/runtime/licenses/Bun-LICENSE.md +84 -0
  190. package/runtime/licenses/GPL-3.0.txt +674 -0
  191. package/runtime/licenses/LLVM-LICENSE.TXT +279 -0
  192. package/runtime/licenses/README.md +25 -0
  193. package/runtime/licenses/WAMR-LICENSE +219 -0
  194. package/runtime/natives.txt +1095 -0
  195. package/runtime/patches/README.md +59 -0
  196. package/runtime/patches/assemblyscript-0.28.20-amxts.patch +14174 -0
  197. package/runtime/patches/wamr-2.4.5-amxts.patch +364 -0
  198. package/scripts/apply-docs.ts +508 -0
  199. package/scripts/asc.ts +27 -0
  200. package/scripts/auto-imports.ts +258 -0
  201. package/scripts/build-wasm.ts +595 -0
  202. package/scripts/check.ts +93 -0
  203. package/scripts/compile-cache.ts +179 -0
  204. package/scripts/compile.ts +401 -0
  205. package/scripts/docker-server.ts +73 -0
  206. package/scripts/docs/as/amxts.ts +210 -0
  207. package/scripts/docs/as/effects.ts +1684 -0
  208. package/scripts/docs/as/facade.ts +3302 -0
  209. package/scripts/docs/as/fs.ts +173 -0
  210. package/scripts/docs/as/lib/check.ts +68 -0
  211. package/scripts/docs/as/modules/http.ts +66 -0
  212. package/scripts/docs/as/os.ts +16 -0
  213. package/scripts/docs/as/promise.ts +132 -0
  214. package/scripts/docs/as/promise.types.ts +63 -0
  215. package/scripts/docs/as/vector.ts +48 -0
  216. package/scripts/docs/entities.ts +2338 -0
  217. package/scripts/docs/events.ts +245 -0
  218. package/scripts/docs/game.ts +497 -0
  219. package/scripts/docs/messages.ts +148 -0
  220. package/scripts/file-contents.ts +74 -0
  221. package/scripts/includes.ts +134 -0
  222. package/scripts/player-fields.ts +866 -0
  223. package/scripts/plugin-cache.ts +92 -0
  224. package/scripts/plugin-natives.ts +1157 -0
  225. package/scripts/prebuilt.ts +268 -0
  226. package/scripts/prepare.ts +134 -0
  227. package/scripts/project.ts +973 -0
  228. package/scripts/run.ts +28 -0
  229. package/scripts/shared-modules.ts +906 -0
  230. package/scripts/system.ts +33 -0
  231. package/scripts/tracked-fs.ts +115 -0
  232. package/scripts/typed-configs.ts +788 -0
  233. package/scripts/ui.ts +77 -0
  234. package/src/cli-api.mjs +175 -0
  235. package/src/parser/include-parser.ts +506 -0
  236. package/src/system.mjs +155 -0
  237. package/src/testing/compile-cache.ts +31 -0
  238. package/src/testing/compile.ts +222 -0
  239. package/src/testing/coroutines.ts +191 -0
  240. package/src/testing/index.ts +39 -0
  241. package/src/testing/kits.ts +55 -0
  242. package/src/testing/memory.ts +125 -0
  243. package/src/testing/natives.ts +925 -0
  244. package/src/testing/server.ts +1874 -0
  245. package/src/testing/setup.ts +63 -0
  246. package/src/testing/tables.ts +139 -0
  247. package/src/types.ts +51 -0
@@ -0,0 +1,508 @@
1
+ // The tooltips of the hand-written API, in the language the developer reads.
2
+ //
3
+ // bun scripts/apply-docs.ts the core's hood and the project's
4
+ // modules, in AMXTS_DOCS_LANG (run by
5
+ // `bun run generate` and `amxts prepare`)
6
+ // bun scripts/apply-docs.ts --lang ru that language, whatever .env says
7
+ // bun scripts/apply-docs.ts --format the tables rewritten in one form: both
8
+ // languages in backticks, laid out alike
9
+ // (code-style rule 29)
10
+ // bun scripts/apply-docs.ts --clean <f> git's clean filter: stdin in English
11
+ // to stdout, so a tree written in
12
+ // Russian commits - and diffs - as English
13
+ //
14
+ // The generated API (as/events.ts, hooks.ts, entities.ts) is written in the
15
+ // chosen language by its generator. The hood a person wrote - the facade,
16
+ // fs, os, vector, the Promise globals, the official modules' API - is code in
17
+ // git, with an editor landing in it on go-to-definition; so its words live
18
+ // beside it, both languages keyed by the element they describe, and this
19
+ // script writes the chosen one into the JSDoc above each element:
20
+ //
21
+ // scripts/docs/as/facade.ts for as/facade.ts (the core)
22
+ // scripts/docs/src/index.ts for src/index.ts (a module)
23
+ //
24
+ // export default {
25
+ // 'print': { en: 'Prints ...', ru: 'Печатает ...' },
26
+ // 'Player.health': { en: '...', ru: '...' },
27
+ // };
28
+ //
29
+ // A key is the element's path: `print`, `Player`, `Player.health`,
30
+ // `game.addEventListener` (a namespace's member), `HudOptions.x`; an
31
+ // overloaded function's signatures are `load`, `load#2`, ... in their order.
32
+ // A class and an interface of the same members share keys, so an options
33
+ // class turned into an interface keeps its words. The text is the comment's
34
+ // body without the ` * `: several lines where it has several, the ```ts
35
+ // examples the same in both languages. A multi-line template literal is
36
+ // written indented, as the code around it; the common indentation is dropped.
37
+ //
38
+ // The files stay English in git: the repository's .gitattributes names them
39
+ // with `filter=amxts-docs`, and this script registers that filter's clean
40
+ // command in the repository's .git/config when it applies - git then stores
41
+ // what the clean command gives (English) whatever the working tree holds,
42
+ // and `git status` is clean in Russian too. English is what the check
43
+ // (tests/code-style.test.ts, rule 28) compares with when AMXTS_DOCS_LANG is
44
+ // not set.
45
+ import { spawnSync } from 'node:child_process';
46
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
47
+ import { dirname, join, relative, resolve } from 'node:path';
48
+ import { fileURLToPath } from 'node:url';
49
+ import ts from 'typescript';
50
+
51
+ export type Lang = 'en' | 'ru';
52
+ export const LANGS: Lang[] = ['en', 'ru'];
53
+
54
+ /** One element's words in both languages. */
55
+ export interface DocText {
56
+ en: string;
57
+ ru: string;
58
+ }
59
+
60
+ export type DocTable = Record<string, DocText>;
61
+
62
+ /** The language AMXTS_DOCS_LANG picks: English unless it says "ru". */
63
+ export function docsLang(value = process.env.AMXTS_DOCS_LANG): Lang {
64
+ return value === 'ru' ? 'ru' : 'en';
65
+ }
66
+
67
+ // ---------------------------------------------------------------- elements
68
+
69
+ /** A declaration in a source file, as the tooltips see it. */
70
+ export interface Element {
71
+ /** Its path: `Player.health`. */
72
+ key: string;
73
+ /** A plugin author sees it: exported, global, not private, not `_`-named. */
74
+ public: boolean;
75
+ /** The JSDoc block above it, when there is one: where it is and its text. */
76
+ doc: { start: number; end: number; text: string } | null;
77
+ /** Where a new block goes: the start of the line it would take. */
78
+ insertAt: number;
79
+ /** The declaration shares its line with code before it: a new block goes in that line. */
80
+ inline: boolean;
81
+ /** The declaration's indentation. */
82
+ indent: string;
83
+ /** The line (0-based) the declaration starts on. */
84
+ line: number;
85
+ }
86
+
87
+ type Named = ts.Node & { name?: ts.Node };
88
+
89
+ function nameOf(node: Named): string | null {
90
+ const name = node.name;
91
+ if (!name) return null;
92
+ if (ts.isIdentifier(name) || ts.isPrivateIdentifier(name)) return name.text;
93
+ if (ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text;
94
+ return null;
95
+ }
96
+
97
+ function hasModifier(node: ts.Node, kind: ts.SyntaxKind): boolean {
98
+ return (ts.canHaveModifiers(node) ? ts.getModifiers(node) ?? [] : []).some(m => m.kind === kind);
99
+ }
100
+
101
+ /** AssemblyScript's `@global`: a declaration every file sees without an import. */
102
+ function isGlobalDecorated(node: ts.Node): boolean {
103
+ const decorators = ts.canHaveDecorators(node) ? ts.getDecorators(node) ?? [] : [];
104
+ return decorators.some(d => ts.isIdentifier(d.expression) && d.expression.text === 'global');
105
+ }
106
+
107
+ /** The comment's body: `/** a *\/` is "a"; a block's lines lose their ` * `. */
108
+ export function docBody(comment: string): string {
109
+ const inner = comment.slice(3, -2);
110
+ const lines = inner.split(/\r?\n/).map((line, i) => (i === 0 ? line.replace(/^\s+/, '') : line.replace(/^\s*\* ?/, '')).replace(/\s+$/, ''));
111
+ while (lines.length && lines[0] === '') lines.shift();
112
+ while (lines.length && lines.at(-1) === '') lines.pop();
113
+ return lines.join('\n').replace(/\*\\\//g, '*/');
114
+ }
115
+
116
+ /** A body as a comment at `indent`, its lines ended by `eol`: one line when it is one line. */
117
+ export function renderDoc(text: string, indent: string, eol = '\n'): string {
118
+ const safe = text.replace(/\*\//g, '*\\/');
119
+ if (!safe.includes('\n')) return `/** ${safe} */`;
120
+ return ['/**', ...safe.split('\n').map(line => (line ? `${indent} * ${line}` : `${indent} *`)), `${indent} */`].join(eol);
121
+ }
122
+
123
+ /**
124
+ * Every named declaration in a file - top-level ones, namespaces' members,
125
+ * class and interface members - with its key, whether a plugin author sees
126
+ * it, and its JSDoc block.
127
+ */
128
+ export function elements(fileName: string, source: string): Element[] {
129
+ const file = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
130
+ // A file with no import and no export is a script: what it declares is global.
131
+ const script = !file.statements.some(s => ts.isImportDeclaration(s) || ts.isExportDeclaration(s) || ts.isExportAssignment(s) || hasModifier(s, ts.SyntaxKind.ExportKeyword));
132
+ const found: Element[] = [];
133
+ const seen = new Set<string>();
134
+
135
+ /** The start of the `// @ts-ignore` lines right above `line`: they speak of the line below them. */
136
+ function aboveTsComments(line: number): number {
137
+ const previous = source.lastIndexOf('\n', line - 2) + 1;
138
+ return line > 0 && /^\s*\/\/\s*@ts-/.test(source.slice(previous, line)) ? aboveTsComments(previous) : line;
139
+ }
140
+
141
+ function add(node: ts.Node, key: string, visible: boolean) {
142
+ // A setter after its getter, a second overload: the first one carries the words.
143
+ if (seen.has(key)) return;
144
+ seen.add(key);
145
+ const start = node.getStart(file);
146
+ // The parser's own reading: the last /** */ before it, on its line or above.
147
+ const jsDoc = (node as { jsDoc?: ts.JSDoc[] }).jsDoc?.at(-1);
148
+ const block = jsDoc ? { pos: jsDoc.getStart(file), end: jsDoc.end } : undefined;
149
+ const lineStart = source.lastIndexOf('\n', start - 1) + 1;
150
+ const indent = /^[ \t]*/.exec(source.slice(lineStart, start))![0];
151
+ // `constructor(public player: Player)`: the block goes in the line, before it.
152
+ const inline = /\S/.test(source.slice(lineStart, start));
153
+ const insertAt = inline ? start : aboveTsComments(lineStart);
154
+ found.push({
155
+ key,
156
+ public: visible && !key.split('.').some(part => part.startsWith('_') || part.startsWith('#')),
157
+ doc: block ? { start: block.pos, end: block.end, text: docBody(source.slice(block.pos, block.end)) } : null,
158
+ insertAt,
159
+ inline,
160
+ indent,
161
+ line: file.getLineAndCharacterOfPosition(start).line,
162
+ });
163
+ }
164
+
165
+ function members(owner: ts.ClassLikeDeclaration | ts.InterfaceDeclaration | ts.TypeLiteralNode, prefix: string, visible: boolean) {
166
+ for (const member of owner.members) {
167
+ // `constructor(public player: Player)` declares a field.
168
+ if (ts.isConstructorDeclaration(member)) {
169
+ for (const parameter of member.parameters) {
170
+ if (!ts.isParameterPropertyDeclaration(parameter, member) || !ts.isIdentifier(parameter.name)) continue;
171
+ const hidden = hasModifier(parameter, ts.SyntaxKind.PrivateKeyword) || hasModifier(parameter, ts.SyntaxKind.ProtectedKeyword);
172
+ add(parameter, `${prefix}.${parameter.name.text}`, visible && !hidden);
173
+ }
174
+ continue;
175
+ }
176
+ if (ts.isIndexSignatureDeclaration(member) || ts.isClassStaticBlockDeclaration(member)) continue;
177
+ const name = nameOf(member as Named);
178
+ if (!name) continue;
179
+ const hidden = hasModifier(member, ts.SyntaxKind.PrivateKeyword) || hasModifier(member, ts.SyntaxKind.ProtectedKeyword);
180
+ add(member, `${prefix}.${name}`, visible && !hidden);
181
+ }
182
+ }
183
+
184
+ /** How many declarations a function of this name has in the list: overloads have several. */
185
+ function overloads(list: readonly ts.Statement[], name: string) {
186
+ return list.filter(each => ts.isFunctionDeclaration(each) && nameOf(each as Named) === name).length;
187
+ }
188
+ const overloadIndex = new Map<string, number>();
189
+
190
+ // 'all': everything declared is visible (a script, `declare global`, an
191
+ // ambient namespace); 'exported': what is exported or `@global`; 'none':
192
+ // inside what nobody sees.
193
+ function statements(list: readonly ts.Statement[], prefix: string, mode: 'all' | 'exported' | 'none') {
194
+ for (const statement of list) {
195
+ const visible = mode === 'all' || (mode === 'exported' && (hasModifier(statement, ts.SyntaxKind.ExportKeyword) || isGlobalDecorated(statement)));
196
+ const at = (name: string) => (prefix ? `${prefix}.${name}` : name);
197
+
198
+ if (ts.isVariableStatement(statement)) {
199
+ for (const declaration of statement.declarationList.declarations) {
200
+ if (ts.isIdentifier(declaration.name)) add(statement, at(declaration.name.text), visible);
201
+ }
202
+ continue;
203
+ }
204
+
205
+ if (ts.isModuleDeclaration(statement)) {
206
+ const body = statement.body && ts.isModuleBlock(statement.body) ? statement.body.statements : [];
207
+ if (statement.flags & ts.NodeFlags.GlobalAugmentation) {
208
+ statements(body, prefix, 'all');
209
+ continue;
210
+ }
211
+ // `declare module "./facade"`: members a file adds to the classes of a
212
+ // file beside it are its own API; the class has its words there.
213
+ if (ts.isStringLiteral(statement.name)) {
214
+ if (!statement.name.text.startsWith('./')) continue;
215
+ for (const each of body) {
216
+ if (ts.isInterfaceDeclaration(each)) members(each, at(each.name.text), true);
217
+ }
218
+ continue;
219
+ }
220
+ const name = statement.name.text;
221
+ add(statement, at(name), visible);
222
+ const ambient = mode === 'all' || hasModifier(statement, ts.SyntaxKind.DeclareKeyword);
223
+ statements(body, at(name), !visible ? 'none' : ambient ? 'all' : 'exported');
224
+ continue;
225
+ }
226
+
227
+ const name = nameOf(statement as Named);
228
+ if (!name) continue;
229
+ if (ts.isFunctionDeclaration(statement) && overloads(list, name) > 1) {
230
+ // Each overload signature has words of its own - the editor shows
231
+ // the one a call matches: `load`, `load#2`, ... The implementation
232
+ // under them is not one a caller sees.
233
+ if (statement.body && list.some(other => other !== statement && ts.isFunctionDeclaration(other) && nameOf(other) === name && !other.body)) continue;
234
+ const index = (overloadIndex.get(at(name)) ?? 0) + 1;
235
+ overloadIndex.set(at(name), index);
236
+ add(statement, index === 1 ? at(name) : `${at(name)}#${index}`, visible);
237
+ } else if (ts.isFunctionDeclaration(statement) || ts.isTypeAliasDeclaration(statement)) {
238
+ add(statement, at(name), visible);
239
+ } else if (ts.isClassDeclaration(statement) || ts.isInterfaceDeclaration(statement)) {
240
+ add(statement, at(name), visible);
241
+ members(statement, at(name), visible);
242
+ } else if (ts.isEnumDeclaration(statement)) {
243
+ add(statement, at(name), visible);
244
+ for (const member of statement.members) {
245
+ const memberName = nameOf(member as Named);
246
+ if (memberName) add(member, `${at(name)}.${memberName}`, visible);
247
+ }
248
+ }
249
+ }
250
+ }
251
+
252
+ statements(file.statements, '', script ? 'all' : 'exported');
253
+ return found;
254
+ }
255
+
256
+ // ---------------------------------------------------------------- tables
257
+
258
+ /** A template literal written indented, as the code around it: without that indentation. */
259
+ export function dedent(text: string): string {
260
+ const lines = text.replace(/\r\n/g, '\n').split('\n');
261
+ while (lines.length && lines[0].trim() === '') lines.shift();
262
+ while (lines.length && lines.at(-1)!.trim() === '') lines.pop();
263
+ const indents = lines.filter(line => line.trim()).map(line => /^[ \t]*/.exec(line)![0].length);
264
+ const common = indents.length ? Math.min(...indents) : 0;
265
+ return lines.map(line => line.slice(common).replace(/\s+$/, '')).join('\n');
266
+ }
267
+
268
+ /** A docs file's table, every text dedented. */
269
+ export async function loadTable(path: string): Promise<DocTable> {
270
+ const module = await import(`${resolve(path).replace(/\\/g, '/')}?t=${Date.now()}`);
271
+ const raw = (module.default ?? {}) as Record<string, Partial<DocText>>;
272
+ const table: DocTable = {};
273
+ for (const [key, text] of Object.entries(raw)) table[key] = { en: dedent(text.en ?? ''), ru: dedent(text.ru ?? '') };
274
+ return table;
275
+ }
276
+
277
+ /** A text as a table writes it: in backticks, on the key's line or indented under it. */
278
+ function literal(text: string, multiline: boolean, indent: string): string {
279
+ const escaped = text.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
280
+ if (!multiline) return `\`${escaped}\``;
281
+ return `\`\n${escaped.split('\n').map(line => (line ? `${indent}\t${line}` : '')).join('\n')}\n${indent}\``;
282
+ }
283
+
284
+ /**
285
+ * A table as its file, the way code-style rule 29 has it: both
286
+ * languages in backticks and laid out alike - on one line each, or both
287
+ * indented under the key when either has more than one line.
288
+ */
289
+ export function formatTable(table: DocTable, header: string[], quote: '\'' | '"'): string {
290
+ const keys = Object.keys(table);
291
+ const quoted = keys.some(key => !/^[A-Z_$][\w$]*$/i.test(key));
292
+ const lines = [...header, 'export default {'];
293
+ for (const key of keys) {
294
+ const { en, ru } = table[key];
295
+ const multiline = en.includes('\n') || ru.includes('\n');
296
+ lines.push(`\t${quoted ? `${quote}${key}${quote}` : key}: {`, `\t\ten: ${literal(en, multiline, '\t\t')},`, `\t\tru: ${literal(ru, multiline, '\t\t')},`, '\t},');
297
+ }
298
+ return [...lines, '};', ''].join('\n');
299
+ }
300
+
301
+ /** Rewrites the tables of `sources` in the form formatTable gives; the ones it changed. */
302
+ export async function formatSources(sources: DocSource[]): Promise<string[]> {
303
+ const files = [...new Set(sources.map(source => source.docs).filter(existsSync))];
304
+ const tables = await Promise.all(files.map(loadTable));
305
+ const changed: string[] = [];
306
+ for (const [i, docs] of files.entries()) {
307
+ const before = readFileSync(docs, 'utf8');
308
+ const header = before.split(/\r?\n/).filter((line, i, all) => all.slice(0, i + 1).every(l => l.startsWith('//')));
309
+ const quote = /^\t"/m.test(before) ? '"' : '\'';
310
+ const after = formatTable(tables[i], header, quote);
311
+ if (after !== before) {
312
+ writeFileSync(docs, after);
313
+ changed.push(docs);
314
+ }
315
+ }
316
+ return changed;
317
+ }
318
+
319
+ /** The text in `lang`, English when that one is not written. */
320
+ export function pick(text: DocText, lang: Lang): string {
321
+ return text[lang] || text.en;
322
+ }
323
+
324
+ /** Text safe inside a JSDoc comment, on one line. */
325
+ export function docText(text: string) {
326
+ return text.replace(/\*\//g, '* /').replace(/\s+/g, ' ').trim();
327
+ }
328
+
329
+ /** `source` with the words of `table` in `lang` above every element it names. */
330
+ export function applyTable(fileName: string, source: string, table: DocTable, lang: Lang): string {
331
+ const edits: { start: number; end: number; text: string }[] = [];
332
+ // A file checked out with CRLF gets its comments with CRLF too.
333
+ const eol = source.includes('\r\n') ? '\r\n' : '\n';
334
+ for (const element of elements(fileName, source)) {
335
+ const entry = table[element.key];
336
+ if (!entry) continue;
337
+ const text = pick(entry, lang);
338
+ if (!text) continue;
339
+ const block = renderDoc(text, element.indent, eol);
340
+ if (element.doc) {
341
+ if (source.slice(element.doc.start, element.doc.end) !== block) edits.push({ start: element.doc.start, end: element.doc.end, text: block });
342
+ } else {
343
+ edits.push({ start: element.insertAt, end: element.insertAt, text: element.inline ? `${block} ` : `${element.indent}${block}${eol}` });
344
+ }
345
+ }
346
+ let out = source;
347
+ for (const edit of edits.sort((a, b) => b.start - a.start)) out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
348
+ return out;
349
+ }
350
+
351
+ // ---------------------------------------------------------------- packages
352
+
353
+ /** A source file of the public API and the file its words are in. */
354
+ export interface DocSource {
355
+ /** The source, absolute. */
356
+ file: string;
357
+ /** Its words: <package>/scripts/docs/<the source's path in the package>. */
358
+ docs: string;
359
+ /** The package's folder. */
360
+ root: string;
361
+ }
362
+
363
+ /** The core's hand-written API: what `~/facade` and the globals give a plugin. */
364
+ export const CORE_API = [
365
+ 'as/facade.ts',
366
+ 'as/promise.ts',
367
+ 'as/promise.types.d.ts',
368
+ 'as/vector.ts',
369
+ 'as/effects.ts',
370
+ 'as/fs.ts',
371
+ 'as/os.ts',
372
+ 'as/amxts.d.ts',
373
+ 'as/lib/check.ts',
374
+ 'as/modules/http.ts',
375
+ ];
376
+
377
+ export function docsFileOf(root: string, file: string): string {
378
+ return join(root, 'scripts', 'docs', relative(root, file).replace(/\.d\.ts$/, '.ts'));
379
+ }
380
+
381
+ /**
382
+ * A module package's API files: its module file and every file that file
383
+ * re-exports (`export * from "./types"`), followed through.
384
+ */
385
+ export function moduleApi(moduleFile: string): string[] {
386
+ const files: string[] = [];
387
+ const visit = (file: string) => {
388
+ if (files.includes(file) || !existsSync(file)) return;
389
+ files.push(file);
390
+ const text = readFileSync(file, 'utf8');
391
+ for (const match of text.matchAll(/^export\s+(?:type\s+)?(?:\*|\{[^}]*\})\s+from\s+["'](\.{1,2}\/[^"']+)["']/gm)) {
392
+ const target = resolve(dirname(file), match[1]);
393
+ visit(existsSync(`${target}.ts`) ? `${target}.ts` : target);
394
+ }
395
+ };
396
+ visit(resolve(moduleFile));
397
+ return files;
398
+ }
399
+
400
+ export function coreSources(core: string): DocSource[] {
401
+ return CORE_API.map(path => join(core, path)).map(file => ({ file, docs: docsFileOf(core, file), root: core }));
402
+ }
403
+
404
+ export function moduleSources(pkg: { dir: string; module: string }): DocSource[] {
405
+ return moduleApi(pkg.module).map(file => ({ file, docs: docsFileOf(pkg.dir, file), root: pkg.dir }));
406
+ }
407
+
408
+ /** Writes `lang` into every source that has a docs file; the ones it changed. */
409
+ export async function applySources(sources: DocSource[], lang: Lang): Promise<string[]> {
410
+ const present = sources.filter(source => existsSync(source.docs) && existsSync(source.file));
411
+ const tables = await Promise.all(present.map(source => loadTable(source.docs)));
412
+ const changed: string[] = [];
413
+ for (const [i, source] of present.entries()) {
414
+ const before = readFileSync(source.file, 'utf8');
415
+ const after = applyTable(source.file, before, tables[i], lang);
416
+ if (after !== before) {
417
+ writeFileSync(source.file, after);
418
+ changed.push(source.file);
419
+ }
420
+ }
421
+ return changed;
422
+ }
423
+
424
+ // ---------------------------------------------------------------- git
425
+
426
+ export const FILTER = 'amxts-docs';
427
+
428
+ /**
429
+ * Registers the clean filter in `root`'s repository, when `root` is the top
430
+ * of one: .gitattributes names the files, .git/config the command. Whether
431
+ * it is one.
432
+ */
433
+ export function registerFilter(root: string): boolean {
434
+ const top = spawnSync('git', ['-C', root, 'rev-parse', '--show-toplevel'], { encoding: 'utf8' });
435
+ if (top.status !== 0 || resolve(top.stdout.trim()) !== resolve(root)) return false;
436
+ const script = fileURLToPath(import.meta.url).replace(/\\/g, '/');
437
+ const command = `bun "${script}" --clean %f`;
438
+ const current = spawnSync('git', ['-C', root, 'config', '--local', `filter.${FILTER}.clean`], { encoding: 'utf8' });
439
+ if (current.stdout.trim() !== command) spawnSync('git', ['-C', root, 'config', '--local', `filter.${FILTER}.clean`, command]);
440
+ return true;
441
+ }
442
+
443
+ /**
444
+ * A rewritten file whose content git sees unchanged - only the language of
445
+ * its comments moved - has its index entry refreshed. `git status` takes a
446
+ * file whose size changed for a modified one without running the filter;
447
+ * `git add` of content equal to the index stores nothing new and records the
448
+ * size. A file with changes of its own is left as it is.
449
+ */
450
+ function refreshIndex(root: string, file: string): void {
451
+ const path = relative(root, file);
452
+ const same = spawnSync('git', ['-C', root, 'diff', '--quiet', '--', path]);
453
+ const staged = spawnSync('git', ['-C', root, 'diff', '--quiet', '--cached', '--', path]);
454
+ if (same.status === 0 && staged.status === 0) spawnSync('git', ['-C', root, 'add', '--', path]);
455
+ }
456
+
457
+ /** What the filter does: the file git hands over, in English. */
458
+ async function clean(path: string): Promise<void> {
459
+ const chunks: Buffer[] = [];
460
+ for await (const chunk of process.stdin) chunks.push(chunk as Buffer);
461
+ const input = Buffer.concat(chunks).toString('utf8');
462
+ const root = process.cwd();
463
+ const file = resolve(root, path);
464
+ const docs = docsFileOf(root, file);
465
+ process.stdout.write(existsSync(docs) ? applyTable(file, input, await loadTable(docs), 'en') : input);
466
+ }
467
+
468
+ // ---------------------------------------------------------------- command
469
+
470
+ /**
471
+ * The core's hood and the modules of the project in `dir` - or, in a
472
+ * module's own folder, that module - in `lang`; the line that says so.
473
+ */
474
+ export async function applyProject(dir: string, lang: Lang): Promise<string> {
475
+ const { roots, sources } = await projectSources(dir);
476
+ const repositories = roots.filter(registerFilter);
477
+ const changed = await applySources(sources, lang);
478
+ for (const file of changed) {
479
+ const root = repositories.find(each => file.startsWith(each));
480
+ if (root) refreshIndex(root, file);
481
+ }
482
+ return `docs: ${lang}, ${changed.length ? `${changed.length} file(s) rewritten` : 'up to date'}`;
483
+ }
484
+
485
+ /** The core's API files and those of the modules the project in `dir` uses - or of the module `dir` is. */
486
+ export async function projectSources(dir: string): Promise<{ roots: string[]; sources: DocSource[] }> {
487
+ const { CORE_DIR, loadProject } = await import('./project');
488
+ const project = loadProject(dir);
489
+ const modules = project.modules.length ? project.modules : project.packages.filter(pkg => pkg.dir === project.dir);
490
+ return { roots: [CORE_DIR, ...modules.map(pkg => pkg.dir)], sources: [...coreSources(CORE_DIR), ...modules.flatMap(moduleSources)] };
491
+ }
492
+
493
+ async function main() {
494
+ const args = process.argv.slice(2);
495
+ const cleanAt = args.indexOf('--clean');
496
+ if (cleanAt >= 0) return clean(args[cleanAt + 1]);
497
+
498
+ if (args.includes('--format')) {
499
+ const changed = await formatSources((await projectSources(process.cwd())).sources);
500
+ console.log(changed.length ? changed.map(file => `formatted ${file}`).join('\n') : 'docs: every table is in form');
501
+ return;
502
+ }
503
+
504
+ const langAt = args.indexOf('--lang');
505
+ console.log(await applyProject(process.cwd(), langAt >= 0 ? docsLang(args[langAt + 1]) : docsLang()));
506
+ }
507
+
508
+ if (import.meta.main) await main();
package/scripts/asc.ts ADDED
@@ -0,0 +1,27 @@
1
+ // asc - the patched AssemblyScript compiler (runtime/patches explains what it
2
+ // carries and why) - as every build here runs it.
3
+ // @ts-ignore - bun's own module
4
+ import { fullGC } from 'bun:jsc';
5
+ // @ts-ignore - shipped as JavaScript, with types beside it we do not need here
6
+ import asc from '../runtime/deps/assemblyscript/dist/asc.js';
7
+
8
+ /**
9
+ * asc.main, and nothing of the compile kept alive after it.
10
+ *
11
+ * asc hands a transform its compile by writing the program onto the transform
12
+ * class's prototype, where it stays until that class's next compile: a class
13
+ * that lives on - a module's own, a test file's - would keep the whole
14
+ * program, a few hundred MB for a plugin. Each compile is given subclasses of
15
+ * its own, which go with it. The program is then garbage JavaScriptCore does
16
+ * not collect soon on its own, and one process compiling plugin after plugin
17
+ * - a build, the test suite from an empty cache - piles it up past 3 GB; a
18
+ * full collection after each compile keeps one program's worth.
19
+ */
20
+ export async function ascMain(args: string[], options: Record<string, any>): Promise<{ error: Error | null; stdout: any; stderr: any }> {
21
+ const transforms = options.transforms?.map((transform: any) => (typeof transform === 'function' ? class extends transform {} : transform));
22
+ try {
23
+ return await asc.main(args, { ...options, transforms });
24
+ } finally {
25
+ fullGC();
26
+ }
27
+ }