tsc-ts 0.0.4 → 0.0.6

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 (584) hide show
  1. package/README.md +34 -2
  2. package/dist/api/compiler/ast-utilities.d.ts +82 -0
  3. package/dist/api/compiler/ast-utilities.js +462 -0
  4. package/dist/api/compiler/ast.d.ts +2351 -0
  5. package/dist/api/compiler/ast.js +11212 -0
  6. package/dist/api/compiler/astnav.d.ts +37 -0
  7. package/dist/api/compiler/astnav.js +767 -0
  8. package/dist/api/compiler/binder-utilities.d.ts +182 -0
  9. package/dist/api/compiler/binder-utilities.js +1241 -0
  10. package/dist/api/compiler/binder.d.ts +33 -0
  11. package/dist/api/compiler/binder.js +2359 -0
  12. package/dist/api/compiler/character-codes.d.ts +161 -0
  13. package/dist/api/compiler/character-codes.js +160 -0
  14. package/dist/api/compiler/checker-calls.d.ts +46 -0
  15. package/dist/api/compiler/checker-calls.js +1641 -0
  16. package/dist/api/compiler/checker-classes.d.ts +30 -0
  17. package/dist/api/compiler/checker-classes.js +582 -0
  18. package/dist/api/compiler/checker-contextual.d.ts +77 -0
  19. package/dist/api/compiler/checker-contextual.js +1136 -0
  20. package/dist/api/compiler/checker-declaration-order.d.ts +12 -0
  21. package/dist/api/compiler/checker-declaration-order.js +357 -0
  22. package/dist/api/compiler/checker-declarations.d.ts +33 -0
  23. package/dist/api/compiler/checker-declarations.js +888 -0
  24. package/dist/api/compiler/checker-declared-types.d.ts +48 -0
  25. package/dist/api/compiler/checker-declared-types.js +788 -0
  26. package/dist/api/compiler/checker-decorators.d.ts +26 -0
  27. package/dist/api/compiler/checker-decorators.js +562 -0
  28. package/dist/api/compiler/checker-deprecation.d.ts +33 -0
  29. package/dist/api/compiler/checker-deprecation.js +314 -0
  30. package/dist/api/compiler/checker-destructuring.d.ts +34 -0
  31. package/dist/api/compiler/checker-destructuring.js +584 -0
  32. package/dist/api/compiler/checker-elaboration.d.ts +15 -0
  33. package/dist/api/compiler/checker-elaboration.js +291 -0
  34. package/dist/api/compiler/checker-emit-helpers.d.ts +58 -0
  35. package/dist/api/compiler/checker-emit-helpers.js +154 -0
  36. package/dist/api/compiler/checker-emit-resolver.d.ts +75 -0
  37. package/dist/api/compiler/checker-emit-resolver.js +580 -0
  38. package/dist/api/compiler/checker-evaluator.d.ts +27 -0
  39. package/dist/api/compiler/checker-evaluator.js +188 -0
  40. package/dist/api/compiler/checker-expressions.d.ts +67 -0
  41. package/dist/api/compiler/checker-expressions.js +1351 -0
  42. package/dist/api/compiler/checker-features.d.ts +6 -0
  43. package/dist/api/compiler/checker-features.js +354 -0
  44. package/dist/api/compiler/checker-flow.d.ts +86 -0
  45. package/dist/api/compiler/checker-flow.js +169 -0
  46. package/dist/api/compiler/checker-functions.d.ts +43 -0
  47. package/dist/api/compiler/checker-functions.js +1002 -0
  48. package/dist/api/compiler/checker-generated-code.d.ts +25 -0
  49. package/dist/api/compiler/checker-generated-code.js +168 -0
  50. package/dist/api/compiler/checker-globals.d.ts +14 -0
  51. package/dist/api/compiler/checker-globals.js +37 -0
  52. package/dist/api/compiler/checker-grammar.d.ts +35 -0
  53. package/dist/api/compiler/checker-grammar.js +2587 -0
  54. package/dist/api/compiler/checker-heritage.d.ts +28 -0
  55. package/dist/api/compiler/checker-heritage.js +901 -0
  56. package/dist/api/compiler/checker-inference.d.ts +179 -0
  57. package/dist/api/compiler/checker-inference.js +2105 -0
  58. package/dist/api/compiler/checker-iteration.d.ts +115 -0
  59. package/dist/api/compiler/checker-iteration.js +857 -0
  60. package/dist/api/compiler/checker-javascript.d.ts +42 -0
  61. package/dist/api/compiler/checker-javascript.js +442 -0
  62. package/dist/api/compiler/checker-jsdoc.d.ts +30 -0
  63. package/dist/api/compiler/checker-jsdoc.js +320 -0
  64. package/dist/api/compiler/checker-jsx.d.ts +94 -0
  65. package/dist/api/compiler/checker-jsx.js +1433 -0
  66. package/dist/api/compiler/checker-literals.d.ts +6 -0
  67. package/dist/api/compiler/checker-literals.js +43 -0
  68. package/dist/api/compiler/checker-members.d.ts +200 -0
  69. package/dist/api/compiler/checker-members.js +2148 -0
  70. package/dist/api/compiler/checker-modules.d.ts +85 -0
  71. package/dist/api/compiler/checker-modules.js +2141 -0
  72. package/dist/api/compiler/checker-narrowing.d.ts +120 -0
  73. package/dist/api/compiler/checker-narrowing.js +2797 -0
  74. package/dist/api/compiler/checker-object-literals.d.ts +14 -0
  75. package/dist/api/compiler/checker-object-literals.js +483 -0
  76. package/dist/api/compiler/checker-operators.d.ts +33 -0
  77. package/dist/api/compiler/checker-operators.js +1040 -0
  78. package/dist/api/compiler/checker-overloads.d.ts +7 -0
  79. package/dist/api/compiler/checker-overloads.js +271 -0
  80. package/dist/api/compiler/checker-property-access.d.ts +34 -0
  81. package/dist/api/compiler/checker-property-access.js +672 -0
  82. package/dist/api/compiler/checker-services.d.ts +49 -0
  83. package/dist/api/compiler/checker-services.js +995 -0
  84. package/dist/api/compiler/checker-signatures.d.ts +55 -0
  85. package/dist/api/compiler/checker-signatures.js +789 -0
  86. package/dist/api/compiler/checker-source-file.d.ts +27 -0
  87. package/dist/api/compiler/checker-source-file.js +619 -0
  88. package/dist/api/compiler/checker-statements.d.ts +18 -0
  89. package/dist/api/compiler/checker-statements.js +226 -0
  90. package/dist/api/compiler/checker-super.d.ts +13 -0
  91. package/dist/api/compiler/checker-super.js +241 -0
  92. package/dist/api/compiler/checker-symbol-types.d.ts +51 -0
  93. package/dist/api/compiler/checker-symbol-types.js +955 -0
  94. package/dist/api/compiler/checker-symbols.d.ts +181 -0
  95. package/dist/api/compiler/checker-symbols.js +1784 -0
  96. package/dist/api/compiler/checker-type-factory.d.ts +142 -0
  97. package/dist/api/compiler/checker-type-factory.js +2542 -0
  98. package/dist/api/compiler/checker-type-nodes.d.ts +26 -0
  99. package/dist/api/compiler/checker-type-nodes.js +828 -0
  100. package/dist/api/compiler/checker-type-operators.d.ts +109 -0
  101. package/dist/api/compiler/checker-type-operators.js +1842 -0
  102. package/dist/api/compiler/checker-types.d.ts +1093 -0
  103. package/dist/api/compiler/checker-types.js +1874 -0
  104. package/dist/api/compiler/checker-unused.d.ts +19 -0
  105. package/dist/api/compiler/checker-unused.js +524 -0
  106. package/dist/api/compiler/checker-utilities.d.ts +33 -0
  107. package/dist/api/compiler/checker-utilities.js +141 -0
  108. package/dist/api/compiler/checker.d.ts +512 -0
  109. package/dist/api/compiler/checker.js +932 -0
  110. package/dist/api/compiler/command-line.d.ts +32 -0
  111. package/dist/api/compiler/command-line.js +383 -0
  112. package/dist/api/compiler/compile-program.d.ts +118 -0
  113. package/dist/api/compiler/compile-program.js +384 -0
  114. package/dist/api/compiler/diagnostic-locales.d.ts +22 -0
  115. package/dist/api/compiler/diagnostic-locales.js +64 -0
  116. package/dist/api/compiler/diagnostic-messages.d.ts +2166 -0
  117. package/dist/api/compiler/diagnostic-messages.js +184 -0
  118. package/dist/api/compiler/diagnostics.d.ts +161 -0
  119. package/dist/api/compiler/diagnostics.js +784 -0
  120. package/dist/api/compiler/emitter.d.ts +71 -0
  121. package/dist/api/compiler/emitter.js +327 -0
  122. package/dist/api/compiler/file-include.d.ts +144 -0
  123. package/dist/api/compiler/file-include.js +599 -0
  124. package/dist/api/compiler/file-loader.d.ts +290 -0
  125. package/dist/api/compiler/file-loader.js +1323 -0
  126. package/dist/api/compiler/file-system.d.ts +51 -0
  127. package/dist/api/compiler/file-system.js +132 -0
  128. package/dist/api/compiler/flow.d.ts +60 -0
  129. package/dist/api/compiler/flow.js +45 -0
  130. package/dist/api/compiler/instantiation.d.ts +74 -0
  131. package/dist/api/compiler/instantiation.js +835 -0
  132. package/dist/api/compiler/jsdoc.d.ts +75 -0
  133. package/dist/api/compiler/jsdoc.js +1229 -0
  134. package/dist/api/compiler/link-tables.d.ts +105 -0
  135. package/dist/api/compiler/link-tables.js +319 -0
  136. package/dist/api/compiler/locale-data.d.ts +24 -0
  137. package/dist/api/compiler/locale-data.js +183 -0
  138. package/dist/api/compiler/locale.d.ts +4 -0
  139. package/dist/api/compiler/locale.js +317 -0
  140. package/dist/api/compiler/locales/cs-CZ.d.ts +2 -0
  141. package/dist/api/compiler/locales/cs-CZ.js +3 -0
  142. package/dist/api/compiler/locales/de-DE.d.ts +2 -0
  143. package/dist/api/compiler/locales/de-DE.js +3 -0
  144. package/dist/api/compiler/locales/es-ES.d.ts +2 -0
  145. package/dist/api/compiler/locales/es-ES.js +3 -0
  146. package/dist/api/compiler/locales/fr-FR.d.ts +2 -0
  147. package/dist/api/compiler/locales/fr-FR.js +3 -0
  148. package/dist/api/compiler/locales/it-IT.d.ts +2 -0
  149. package/dist/api/compiler/locales/it-IT.js +3 -0
  150. package/dist/api/compiler/locales/ja-JP.d.ts +2 -0
  151. package/dist/api/compiler/locales/ja-JP.js +3 -0
  152. package/dist/api/compiler/locales/ko-KR.d.ts +2 -0
  153. package/dist/api/compiler/locales/ko-KR.js +3 -0
  154. package/dist/api/compiler/locales/pl-PL.d.ts +2 -0
  155. package/dist/api/compiler/locales/pl-PL.js +3 -0
  156. package/dist/api/compiler/locales/pt-BR.d.ts +2 -0
  157. package/dist/api/compiler/locales/pt-BR.js +3 -0
  158. package/dist/api/compiler/locales/ru-RU.d.ts +2 -0
  159. package/dist/api/compiler/locales/ru-RU.js +3 -0
  160. package/dist/api/compiler/locales/tr-TR.d.ts +2 -0
  161. package/dist/api/compiler/locales/tr-TR.js +3 -0
  162. package/dist/api/compiler/locales/zh-CN.d.ts +2 -0
  163. package/dist/api/compiler/locales/zh-CN.js +3 -0
  164. package/dist/api/compiler/locales/zh-TW.d.ts +2 -0
  165. package/dist/api/compiler/locales/zh-TW.js +3 -0
  166. package/dist/api/compiler/module-resolution-types.d.ts +83 -0
  167. package/dist/api/compiler/module-resolution-types.js +176 -0
  168. package/dist/api/compiler/module-resolution.d.ts +71 -0
  169. package/dist/api/compiler/module-resolution.js +1598 -0
  170. package/dist/api/compiler/module-specifiers.d.ts +64 -0
  171. package/dist/api/compiler/module-specifiers.js +1101 -0
  172. package/dist/api/compiler/name-resolver.d.ts +43 -0
  173. package/dist/api/compiler/name-resolver.js +509 -0
  174. package/dist/api/compiler/node-builder-names.d.ts +32 -0
  175. package/dist/api/compiler/node-builder-names.js +854 -0
  176. package/dist/api/compiler/node-builder-pseudo.d.ts +36 -0
  177. package/dist/api/compiler/node-builder-pseudo.js +745 -0
  178. package/dist/api/compiler/node-builder-reuse.d.ts +4 -0
  179. package/dist/api/compiler/node-builder-reuse.js +647 -0
  180. package/dist/api/compiler/node-builder.d.ts +270 -0
  181. package/dist/api/compiler/node-builder.js +2645 -0
  182. package/dist/api/compiler/option-declarations.d.ts +90 -0
  183. package/dist/api/compiler/option-declarations.js +1460 -0
  184. package/dist/api/compiler/options.d.ts +289 -0
  185. package/dist/api/compiler/options.js +1197 -0
  186. package/dist/api/compiler/output-paths.d.ts +42 -0
  187. package/dist/api/compiler/output-paths.js +195 -0
  188. package/dist/api/compiler/package-json.d.ts +175 -0
  189. package/dist/api/compiler/package-json.js +1015 -0
  190. package/dist/api/compiler/parallel-parse.d.ts +221 -0
  191. package/dist/api/compiler/parallel-parse.js +541 -0
  192. package/dist/api/compiler/parser.d.ts +604 -0
  193. package/dist/api/compiler/parser.js +6426 -0
  194. package/dist/api/compiler/path.d.ts +91 -0
  195. package/dist/api/compiler/path.js +705 -0
  196. package/dist/api/compiler/printer/async-helpers.d.ts +22 -0
  197. package/dist/api/compiler/printer/async-helpers.js +91 -0
  198. package/dist/api/compiler/printer/auto-generate.d.ts +13 -0
  199. package/dist/api/compiler/printer/auto-generate.js +20 -0
  200. package/dist/api/compiler/printer/decorator-helpers.d.ts +24 -0
  201. package/dist/api/compiler/printer/decorator-helpers.js +118 -0
  202. package/dist/api/compiler/printer/emit-context.d.ts +157 -0
  203. package/dist/api/compiler/printer/emit-context.js +582 -0
  204. package/dist/api/compiler/printer/emit-flags.d.ts +60 -0
  205. package/dist/api/compiler/printer/emit-flags.js +2 -0
  206. package/dist/api/compiler/printer/factory-base.d.ts +171 -0
  207. package/dist/api/compiler/printer/factory-base.js +1505 -0
  208. package/dist/api/compiler/printer/factory.d.ts +147 -0
  209. package/dist/api/compiler/printer/factory.js +542 -0
  210. package/dist/api/compiler/printer/helpers.d.ts +27 -0
  211. package/dist/api/compiler/printer/helpers.js +164 -0
  212. package/dist/api/compiler/printer/name-generator.d.ts +41 -0
  213. package/dist/api/compiler/printer/name-generator.js +283 -0
  214. package/dist/api/compiler/printer/printer.d.ts +411 -0
  215. package/dist/api/compiler/printer/printer.js +4517 -0
  216. package/dist/api/compiler/printer/text-writer.d.ts +46 -0
  217. package/dist/api/compiler/printer/text-writer.js +172 -0
  218. package/dist/api/compiler/printer/utilities.d.ts +82 -0
  219. package/dist/api/compiler/printer/utilities.js +817 -0
  220. package/dist/api/compiler/program-checker.d.ts +156 -0
  221. package/dist/api/compiler/program-checker.js +529 -0
  222. package/dist/api/compiler/program-utilities.d.ts +52 -0
  223. package/dist/api/compiler/program-utilities.js +365 -0
  224. package/dist/api/compiler/program.d.ts +180 -0
  225. package/dist/api/compiler/program.js +1019 -0
  226. package/dist/api/compiler/pseudo-bigint.d.ts +5 -0
  227. package/dist/api/compiler/pseudo-bigint.js +61 -0
  228. package/dist/api/compiler/regexp-checker.d.ts +38 -0
  229. package/dist/api/compiler/regexp-checker.js +942 -0
  230. package/dist/api/compiler/relations.d.ts +263 -0
  231. package/dist/api/compiler/relations.js +4002 -0
  232. package/dist/api/compiler/reparser.d.ts +48 -0
  233. package/dist/api/compiler/reparser.js +1323 -0
  234. package/dist/api/compiler/scanner.d.ts +256 -0
  235. package/dist/api/compiler/scanner.js +2853 -0
  236. package/dist/api/compiler/side-state.d.ts +16 -0
  237. package/dist/api/compiler/side-state.js +89 -0
  238. package/dist/api/compiler/sourcemap.d.ts +55 -0
  239. package/dist/api/compiler/sourcemap.js +340 -0
  240. package/dist/api/compiler/spelling.d.ts +14 -0
  241. package/dist/api/compiler/spelling.js +128 -0
  242. package/dist/api/compiler/symbol-accessibility.d.ts +57 -0
  243. package/dist/api/compiler/symbol-accessibility.js +991 -0
  244. package/dist/api/compiler/symbol.d.ts +282 -0
  245. package/dist/api/compiler/symbol.js +339 -0
  246. package/dist/api/compiler/symlinks.d.ts +32 -0
  247. package/dist/api/compiler/symlinks.js +114 -0
  248. package/dist/api/compiler/syntax-kind.d.ts +389 -0
  249. package/dist/api/compiler/syntax-kind.js +2 -0
  250. package/dist/api/compiler/timeline.d.ts +9 -0
  251. package/dist/api/compiler/timeline.js +54 -0
  252. package/dist/api/compiler/transformers/alias-references.d.ts +39 -0
  253. package/dist/api/compiler/transformers/alias-references.js +677 -0
  254. package/dist/api/compiler/transformers/async-utilities.d.ts +51 -0
  255. package/dist/api/compiler/transformers/async-utilities.js +260 -0
  256. package/dist/api/compiler/transformers/async.d.ts +53 -0
  257. package/dist/api/compiler/transformers/async.js +645 -0
  258. package/dist/api/compiler/transformers/class-fields.d.ts +206 -0
  259. package/dist/api/compiler/transformers/class-fields.js +2418 -0
  260. package/dist/api/compiler/transformers/class-utilities.d.ts +46 -0
  261. package/dist/api/compiler/transformers/class-utilities.js +261 -0
  262. package/dist/api/compiler/transformers/commonjs-module.d.ts +103 -0
  263. package/dist/api/compiler/transformers/commonjs-module.js +1180 -0
  264. package/dist/api/compiler/transformers/const-enum-inliner.d.ts +11 -0
  265. package/dist/api/compiler/transformers/const-enum-inliner.js +36 -0
  266. package/dist/api/compiler/transformers/declarations-diagnostics.d.ts +52 -0
  267. package/dist/api/compiler/transformers/declarations-diagnostics.js +539 -0
  268. package/dist/api/compiler/transformers/declarations-pseudo.d.ts +160 -0
  269. package/dist/api/compiler/transformers/declarations-pseudo.js +799 -0
  270. package/dist/api/compiler/transformers/declarations-resolver.d.ts +152 -0
  271. package/dist/api/compiler/transformers/declarations-resolver.js +35 -0
  272. package/dist/api/compiler/transformers/declarations-syntactic.d.ts +119 -0
  273. package/dist/api/compiler/transformers/declarations-syntactic.js +1582 -0
  274. package/dist/api/compiler/transformers/declarations-tracker.d.ts +46 -0
  275. package/dist/api/compiler/transformers/declarations-tracker.js +198 -0
  276. package/dist/api/compiler/transformers/declarations.d.ts +175 -0
  277. package/dist/api/compiler/transformers/declarations.js +2381 -0
  278. package/dist/api/compiler/transformers/decorator-metadata.d.ts +54 -0
  279. package/dist/api/compiler/transformers/decorator-metadata.js +554 -0
  280. package/dist/api/compiler/transformers/decorator-utilities.d.ts +38 -0
  281. package/dist/api/compiler/transformers/decorator-utilities.js +238 -0
  282. package/dist/api/compiler/transformers/destructuring.d.ts +25 -0
  283. package/dist/api/compiler/transformers/destructuring.js +626 -0
  284. package/dist/api/compiler/transformers/downlevel.d.ts +6 -0
  285. package/dist/api/compiler/transformers/downlevel.js +45 -0
  286. package/dist/api/compiler/transformers/emit-resolver.d.ts +61 -0
  287. package/dist/api/compiler/transformers/emit-resolver.js +197 -0
  288. package/dist/api/compiler/transformers/es-decorators.d.ts +118 -0
  289. package/dist/api/compiler/transformers/es-decorators.js +1901 -0
  290. package/dist/api/compiler/transformers/exponentiation.d.ts +11 -0
  291. package/dist/api/compiler/transformers/exponentiation.js +76 -0
  292. package/dist/api/compiler/transformers/external-module-info.d.ts +35 -0
  293. package/dist/api/compiler/transformers/external-module-info.js +351 -0
  294. package/dist/api/compiler/transformers/for-await.d.ts +43 -0
  295. package/dist/api/compiler/transformers/for-await.js +469 -0
  296. package/dist/api/compiler/transformers/import-elision.d.ts +16 -0
  297. package/dist/api/compiler/transformers/import-elision.js +124 -0
  298. package/dist/api/compiler/transformers/jsx-entities.d.ts +1 -0
  299. package/dist/api/compiler/transformers/jsx-entities.js +256 -0
  300. package/dist/api/compiler/transformers/jsx.d.ts +71 -0
  301. package/dist/api/compiler/transformers/jsx.js +700 -0
  302. package/dist/api/compiler/transformers/legacy-decorators.d.ts +56 -0
  303. package/dist/api/compiler/transformers/legacy-decorators.js +564 -0
  304. package/dist/api/compiler/transformers/logical-assignment.d.ts +14 -0
  305. package/dist/api/compiler/transformers/logical-assignment.js +62 -0
  306. package/dist/api/compiler/transformers/module-utilities.d.ts +9 -0
  307. package/dist/api/compiler/transformers/module-utilities.js +36 -0
  308. package/dist/api/compiler/transformers/module.d.ts +49 -0
  309. package/dist/api/compiler/transformers/module.js +296 -0
  310. package/dist/api/compiler/transformers/named-evaluation.d.ts +26 -0
  311. package/dist/api/compiler/transformers/named-evaluation.js +266 -0
  312. package/dist/api/compiler/transformers/nullish-coalescing.d.ts +9 -0
  313. package/dist/api/compiler/transformers/nullish-coalescing.js +26 -0
  314. package/dist/api/compiler/transformers/object-rest-spread.d.ts +34 -0
  315. package/dist/api/compiler/transformers/object-rest-spread.js +411 -0
  316. package/dist/api/compiler/transformers/optional-catch.d.ts +9 -0
  317. package/dist/api/compiler/transformers/optional-catch.js +20 -0
  318. package/dist/api/compiler/transformers/optional-chain.d.ts +14 -0
  319. package/dist/api/compiler/transformers/optional-chain.js +188 -0
  320. package/dist/api/compiler/transformers/reference-resolver.d.ts +129 -0
  321. package/dist/api/compiler/transformers/reference-resolver.js +1157 -0
  322. package/dist/api/compiler/transformers/runtime-syntax.d.ts +71 -0
  323. package/dist/api/compiler/transformers/runtime-syntax.js +753 -0
  324. package/dist/api/compiler/transformers/subtree-facts.d.ts +10 -0
  325. package/dist/api/compiler/transformers/subtree-facts.js +205 -0
  326. package/dist/api/compiler/transformers/syntax-facts.d.ts +52 -0
  327. package/dist/api/compiler/transformers/syntax-facts.js +258 -0
  328. package/dist/api/compiler/transformers/tagged-template.d.ts +17 -0
  329. package/dist/api/compiler/transformers/tagged-template.js +148 -0
  330. package/dist/api/compiler/transformers/transformer.d.ts +17 -0
  331. package/dist/api/compiler/transformers/transformer.js +54 -0
  332. package/dist/api/compiler/transformers/type-eraser.d.ts +21 -0
  333. package/dist/api/compiler/transformers/type-eraser.js +346 -0
  334. package/dist/api/compiler/transformers/type-serialization.d.ts +20 -0
  335. package/dist/api/compiler/transformers/type-serialization.js +347 -0
  336. package/dist/api/compiler/transformers/using.d.ts +56 -0
  337. package/dist/api/compiler/transformers/using.js +453 -0
  338. package/dist/api/compiler/transformers/utilities.d.ts +50 -0
  339. package/dist/api/compiler/transformers/utilities.js +347 -0
  340. package/dist/api/compiler/transformers/visitor-base.d.ts +6 -0
  341. package/dist/api/compiler/transformers/visitor-base.js +817 -0
  342. package/dist/api/compiler/transformers/visitor-hooks.d.ts +29 -0
  343. package/dist/api/compiler/transformers/visitor-hooks.js +189 -0
  344. package/dist/api/compiler/tsconfig.d.ts +177 -0
  345. package/dist/api/compiler/tsconfig.js +1311 -0
  346. package/dist/api/compiler/type-printer.d.ts +15 -0
  347. package/dist/api/compiler/type-printer.js +74 -0
  348. package/dist/api/compiler/types.d.ts +87 -0
  349. package/dist/api/compiler/types.js +23 -0
  350. package/dist/api/compiler/unicode-identifiers.d.ts +2 -0
  351. package/dist/api/compiler/unicode-identifiers.js +209 -0
  352. package/dist/api/compiler/unicode-properties.d.ts +8 -0
  353. package/dist/api/compiler/unicode-properties.js +531 -0
  354. package/dist/api/compiler/vfs-match.d.ts +24 -0
  355. package/dist/api/compiler/vfs-match.js +587 -0
  356. package/dist/api/library/ast.d.ts +3 -2
  357. package/dist/api/library/ast.generated.d.ts +127 -139
  358. package/dist/api/library/checker.d.ts +51 -0
  359. package/dist/api/library/checker.js +318 -0
  360. package/dist/api/library/diagnostics.d.ts +13 -0
  361. package/dist/api/library/diagnostics.js +50 -0
  362. package/dist/api/library/disk.d.ts +13 -0
  363. package/dist/api/library/disk.js +66 -0
  364. package/dist/api/library/errors.d.ts +13 -0
  365. package/dist/api/library/errors.js +24 -0
  366. package/dist/api/library/host.d.ts +24 -0
  367. package/dist/api/library/host.js +52 -0
  368. package/dist/api/library/index.d.ts +5 -1
  369. package/dist/api/library/index.js +4 -0
  370. package/dist/api/library/nodes.d.ts +51 -0
  371. package/dist/api/library/nodes.js +196 -0
  372. package/dist/api/library/options.d.ts +144 -0
  373. package/dist/api/library/options.js +308 -0
  374. package/dist/api/library/parse.d.ts +19 -0
  375. package/dist/api/library/parse.js +41 -0
  376. package/dist/api/library/program.d.ts +21 -0
  377. package/dist/api/library/program.js +234 -0
  378. package/dist/api/library/types.d.ts +14 -1
  379. package/package.json +12 -8
  380. package/src/compiler/ast-utilities.ts +548 -0
  381. package/src/compiler/ast.ts +12870 -0
  382. package/src/compiler/astnav.ts +799 -0
  383. package/src/compiler/binder-utilities.ts +1392 -0
  384. package/src/compiler/binder.ts +2707 -0
  385. package/src/compiler/build-info.ts +1197 -0
  386. package/src/compiler/character-codes.ts +323 -0
  387. package/src/compiler/checker-calls.ts +2155 -0
  388. package/src/compiler/checker-classes.ts +706 -0
  389. package/src/compiler/checker-contextual.ts +1457 -0
  390. package/src/compiler/checker-declaration-order.ts +438 -0
  391. package/src/compiler/checker-declarations.ts +1201 -0
  392. package/src/compiler/checker-declared-types.ts +919 -0
  393. package/src/compiler/checker-decorators.ts +694 -0
  394. package/src/compiler/checker-deprecation.ts +355 -0
  395. package/src/compiler/checker-destructuring.ts +805 -0
  396. package/src/compiler/checker-elaboration.ts +477 -0
  397. package/src/compiler/checker-emit-helpers.ts +224 -0
  398. package/src/compiler/checker-emit-resolver.ts +769 -0
  399. package/src/compiler/checker-evaluator.ts +243 -0
  400. package/src/compiler/checker-expressions.ts +1831 -0
  401. package/src/compiler/checker-features.ts +361 -0
  402. package/src/compiler/checker-flow.ts +394 -0
  403. package/src/compiler/checker-functions.ts +1296 -0
  404. package/src/compiler/checker-generated-code.ts +227 -0
  405. package/src/compiler/checker-globals.ts +52 -0
  406. package/src/compiler/checker-grammar.ts +3596 -0
  407. package/src/compiler/checker-heritage.ts +1240 -0
  408. package/src/compiler/checker-inference.ts +2584 -0
  409. package/src/compiler/checker-iteration.ts +1206 -0
  410. package/src/compiler/checker-javascript.ts +502 -0
  411. package/src/compiler/checker-jsdoc.ts +360 -0
  412. package/src/compiler/checker-jsx.ts +1928 -0
  413. package/src/compiler/checker-literals.ts +61 -0
  414. package/src/compiler/checker-members.ts +2480 -0
  415. package/src/compiler/checker-modules.ts +2896 -0
  416. package/src/compiler/checker-narrowing.ts +3230 -0
  417. package/src/compiler/checker-object-literals.ts +591 -0
  418. package/src/compiler/checker-operators.ts +1422 -0
  419. package/src/compiler/checker-overloads.ts +328 -0
  420. package/src/compiler/checker-property-access.ts +906 -0
  421. package/src/compiler/checker-services.ts +1149 -0
  422. package/src/compiler/checker-signatures.ts +1032 -0
  423. package/src/compiler/checker-source-file.ts +732 -0
  424. package/src/compiler/checker-statements.ts +292 -0
  425. package/src/compiler/checker-super.ts +261 -0
  426. package/src/compiler/checker-symbol-types.ts +1187 -0
  427. package/src/compiler/checker-symbols.ts +2157 -0
  428. package/src/compiler/checker-type-factory.ts +2616 -0
  429. package/src/compiler/checker-type-nodes.ts +1033 -0
  430. package/src/compiler/checker-type-operators.ts +2510 -0
  431. package/src/compiler/checker-types.ts +2520 -0
  432. package/src/compiler/checker-unused.ts +642 -0
  433. package/src/compiler/checker-utilities.ts +161 -0
  434. package/src/compiler/checker.ts +1138 -0
  435. package/src/compiler/command-line.ts +436 -0
  436. package/src/compiler/compile-program.ts +439 -0
  437. package/src/compiler/decoder.ts +1209 -0
  438. package/src/compiler/diagnostic-locales.ts +86 -0
  439. package/src/compiler/diagnostic-messages.ts +2349 -0
  440. package/src/compiler/diagnostics.ts +858 -0
  441. package/src/compiler/emitter.ts +434 -0
  442. package/src/compiler/encoder.ts +1945 -0
  443. package/src/compiler/file-include.ts +741 -0
  444. package/src/compiler/file-loader.ts +1597 -0
  445. package/src/compiler/file-system.ts +143 -0
  446. package/src/compiler/flow.ts +89 -0
  447. package/src/compiler/help.ts +473 -0
  448. package/src/compiler/incremental.ts +2818 -0
  449. package/src/compiler/init.ts +141 -0
  450. package/src/compiler/instantiation.ts +938 -0
  451. package/src/compiler/jsdoc.ts +1386 -0
  452. package/src/compiler/link-tables.ts +343 -0
  453. package/src/compiler/locale-data.ts +203 -0
  454. package/src/compiler/locale.ts +307 -0
  455. package/src/compiler/locales/cs-CZ.ts +5 -0
  456. package/src/compiler/locales/de-DE.ts +5 -0
  457. package/src/compiler/locales/es-ES.ts +5 -0
  458. package/src/compiler/locales/fr-FR.ts +5 -0
  459. package/src/compiler/locales/it-IT.ts +5 -0
  460. package/src/compiler/locales/ja-JP.ts +5 -0
  461. package/src/compiler/locales/ko-KR.ts +5 -0
  462. package/src/compiler/locales/pl-PL.ts +5 -0
  463. package/src/compiler/locales/pt-BR.ts +5 -0
  464. package/src/compiler/locales/ru-RU.ts +5 -0
  465. package/src/compiler/locales/tr-TR.ts +5 -0
  466. package/src/compiler/locales/zh-CN.ts +5 -0
  467. package/src/compiler/locales/zh-TW.ts +5 -0
  468. package/src/compiler/module-resolution-types.ts +243 -0
  469. package/src/compiler/module-resolution.ts +2072 -0
  470. package/src/compiler/module-specifiers.ts +1491 -0
  471. package/src/compiler/name-resolver.ts +646 -0
  472. package/src/compiler/node-builder-names.ts +1071 -0
  473. package/src/compiler/node-builder-pseudo.ts +892 -0
  474. package/src/compiler/node-builder-reuse.ts +764 -0
  475. package/src/compiler/node-builder.ts +3433 -0
  476. package/src/compiler/option-declarations.ts +1560 -0
  477. package/src/compiler/options.ts +1576 -0
  478. package/src/compiler/output-paths.ts +334 -0
  479. package/src/compiler/package-json.ts +1083 -0
  480. package/src/compiler/parallel-check.ts +1954 -0
  481. package/src/compiler/parallel-parse.ts +595 -0
  482. package/src/compiler/parse-thread.ts +136 -0
  483. package/src/compiler/parser.ts +7766 -0
  484. package/src/compiler/path.ts +717 -0
  485. package/src/compiler/printer/async-helpers.ts +183 -0
  486. package/src/compiler/printer/auto-generate.ts +27 -0
  487. package/src/compiler/printer/decorator-helpers.ts +218 -0
  488. package/src/compiler/printer/emit-context.ts +684 -0
  489. package/src/compiler/printer/emit-flags.ts +64 -0
  490. package/src/compiler/printer/factory-base.ts +2577 -0
  491. package/src/compiler/printer/factory.ts +764 -0
  492. package/src/compiler/printer/helpers.ts +291 -0
  493. package/src/compiler/printer/name-generator.ts +372 -0
  494. package/src/compiler/printer/printer.ts +5072 -0
  495. package/src/compiler/printer/text-writer.ts +183 -0
  496. package/src/compiler/printer/utilities.ts +884 -0
  497. package/src/compiler/program-checker.ts +594 -0
  498. package/src/compiler/program-utilities.ts +435 -0
  499. package/src/compiler/program.ts +1426 -0
  500. package/src/compiler/pseudo-bigint.ts +62 -0
  501. package/src/compiler/regexp-checker.ts +1142 -0
  502. package/src/compiler/relations.ts +5149 -0
  503. package/src/compiler/reparser.ts +1566 -0
  504. package/src/compiler/scanner.ts +2951 -0
  505. package/src/compiler/show-config.ts +239 -0
  506. package/src/compiler/side-state.ts +125 -0
  507. package/src/compiler/sourcemap.ts +366 -0
  508. package/src/compiler/spelling.ts +122 -0
  509. package/src/compiler/symbol-accessibility.ts +1166 -0
  510. package/src/compiler/symbol.ts +557 -0
  511. package/src/compiler/symlinks.ts +137 -0
  512. package/src/compiler/syntax-kind.ts +391 -0
  513. package/src/compiler/thread-entry.ts +191 -0
  514. package/src/compiler/timeline.ts +53 -0
  515. package/src/compiler/transformers/alias-references.ts +757 -0
  516. package/src/compiler/transformers/async-utilities.ts +335 -0
  517. package/src/compiler/transformers/async.ts +838 -0
  518. package/src/compiler/transformers/class-fields.ts +2967 -0
  519. package/src/compiler/transformers/class-utilities.ts +324 -0
  520. package/src/compiler/transformers/commonjs-module.ts +1621 -0
  521. package/src/compiler/transformers/const-enum-inliner.ts +50 -0
  522. package/src/compiler/transformers/declarations-diagnostics.ts +833 -0
  523. package/src/compiler/transformers/declarations-pseudo.ts +969 -0
  524. package/src/compiler/transformers/declarations-resolver.ts +249 -0
  525. package/src/compiler/transformers/declarations-syntactic.ts +1867 -0
  526. package/src/compiler/transformers/declarations-tracker.ts +269 -0
  527. package/src/compiler/transformers/declarations.ts +3120 -0
  528. package/src/compiler/transformers/decorator-metadata.ts +697 -0
  529. package/src/compiler/transformers/decorator-utilities.ts +283 -0
  530. package/src/compiler/transformers/destructuring.ts +708 -0
  531. package/src/compiler/transformers/downlevel.ts +50 -0
  532. package/src/compiler/transformers/emit-resolver.ts +258 -0
  533. package/src/compiler/transformers/es-decorators.ts +2682 -0
  534. package/src/compiler/transformers/exponentiation.ts +80 -0
  535. package/src/compiler/transformers/external-module-info.ts +412 -0
  536. package/src/compiler/transformers/for-await.ts +687 -0
  537. package/src/compiler/transformers/import-elision.ts +152 -0
  538. package/src/compiler/transformers/jsx-entities.ts +256 -0
  539. package/src/compiler/transformers/jsx.ts +824 -0
  540. package/src/compiler/transformers/legacy-decorators.ts +726 -0
  541. package/src/compiler/transformers/logical-assignment.ts +75 -0
  542. package/src/compiler/transformers/module-utilities.ts +42 -0
  543. package/src/compiler/transformers/module.ts +474 -0
  544. package/src/compiler/transformers/named-evaluation.ts +389 -0
  545. package/src/compiler/transformers/nullish-coalescing.ts +35 -0
  546. package/src/compiler/transformers/object-rest-spread.ts +577 -0
  547. package/src/compiler/transformers/optional-catch.ts +29 -0
  548. package/src/compiler/transformers/optional-chain.ts +246 -0
  549. package/src/compiler/transformers/reference-resolver.ts +1213 -0
  550. package/src/compiler/transformers/runtime-syntax.ts +942 -0
  551. package/src/compiler/transformers/subtree-facts.ts +253 -0
  552. package/src/compiler/transformers/syntax-facts.ts +358 -0
  553. package/src/compiler/transformers/tagged-template.ts +167 -0
  554. package/src/compiler/transformers/transformer.ts +56 -0
  555. package/src/compiler/transformers/type-eraser.ts +520 -0
  556. package/src/compiler/transformers/type-serialization.ts +374 -0
  557. package/src/compiler/transformers/using.ts +613 -0
  558. package/src/compiler/transformers/utilities.ts +419 -0
  559. package/src/compiler/transformers/visitor-base.ts +1601 -0
  560. package/src/compiler/transformers/visitor-hooks.ts +252 -0
  561. package/src/compiler/tsc-build.ts +1422 -0
  562. package/src/compiler/tsc-cli.ts +669 -0
  563. package/src/compiler/tsc-compile.ts +446 -0
  564. package/src/compiler/tsc-watch.ts +922 -0
  565. package/src/compiler/tsconfig.ts +1574 -0
  566. package/src/compiler/type-printer.ts +110 -0
  567. package/src/compiler/types.ts +115 -0
  568. package/src/compiler/unicode-identifiers.ts +212 -0
  569. package/src/compiler/unicode-properties.ts +537 -0
  570. package/src/compiler/vfs-match.ts +597 -0
  571. package/src/compiler/xxh3.ts +650 -0
  572. package/src/library/ast.generated.ts +141 -167
  573. package/src/library/ast.ts +4 -1
  574. package/src/library/checker.ts +384 -0
  575. package/src/library/diagnostics.ts +72 -0
  576. package/src/library/disk.ts +73 -0
  577. package/src/library/errors.ts +26 -0
  578. package/src/library/host.ts +63 -0
  579. package/src/library/index.ts +21 -0
  580. package/src/library/nodes.ts +232 -0
  581. package/src/library/options.ts +449 -0
  582. package/src/library/parse.ts +63 -0
  583. package/src/library/program.ts +293 -0
  584. package/src/library/types.ts +14 -1
@@ -0,0 +1,1954 @@
1
+ /**
2
+ * Checking on several threads (`--checkers N`).
3
+ *
4
+ * In scriptc builds the main thread starts N checker threads and only collects their results
5
+ * (`mainThreadChecks`); under Node.js it is checker 0 and starts N-1. Every checker creates its own
6
+ * checker over the program and takes files until none are left (`planSchedule`): the costliest files first,
7
+ * and groups of files that share costly declarations as a whole (`checkGroups`), then a range of
8
+ * neighboring files of its own, then the end of other checkers' ranges where that makes them finish
9
+ * earlier (`takePosition`). Checkers beyond tsgo's four stop when the process's memory nears its
10
+ * limit, and checkers 1-3 when it is about to run out; the others take over their files
11
+ * (`retires`). The threads send back the semantic
12
+ * diagnostics of their files (and under `noEmit` with declarations, their declaration diagnostics)
13
+ * and their global diagnostics, and the main thread reports them like a single checker would. Which
14
+ * checker checks a file does not change its diagnostics. When the program emits, each checker also
15
+ * emits the files it checks, right after checking each one, as tsgo emits on its checkers, and sends
16
+ * back what it wrote (`checkersEmit`).
17
+ *
18
+ * In scriptc builds, threads share one program: the main thread computes the program's lazy caches,
19
+ * publishes it with `@scriptc/threads` (the program becomes immutable and immortal, and threads
20
+ * receive it by reference), and keeps the JSDoc that checking parses lazily in tables of each thread.
21
+ * Checker threads publish their diagnostics back. Under Node.js, which copies what it posts, every
22
+ * checker thread builds its own copy of the program from the command line instead, and the main
23
+ * thread revives the diagnostics it receives against its own files.
24
+ */
25
+ import { mark, markAt, timeline } from './timeline.js';
26
+ import type { Worker } from 'node:worker_threads';
27
+ import { publish, sharesPublishedGraphs } from '@scriptc/threads';
28
+ import { lastNodeIdValue } from './ast.js';
29
+ import {
30
+ declarationDiagnosticsOfFile,
31
+ EmitIndexes,
32
+ HoldingWriter,
33
+ type OutputWriter,
34
+ ProgramEmitter,
35
+ } from './compile-program.js';
36
+ import { Diagnostic, DiagnosticFile, RepopulateInfo } from './diagnostics.js';
37
+ import { EmitOnly, EmitResult } from './emitter.js';
38
+ import { getEmitDeclarations, isIncremental } from './options.js';
39
+ import { DeclarationSignatureWriter } from './incremental.js';
40
+ import { useLazyJSDocTable } from './parser.js';
41
+ import type { CheckedProgram } from './program-checker.js';
42
+ import type { Program } from './program.js';
43
+ import type { SourceFile } from './ast.js';
44
+ import { lastSymbolIdValue } from './symbol.js';
45
+ import { shareMergedGlobals } from './checker-symbols.js';
46
+ import { readFileSync } from 'node:fs';
47
+ import { availableParallelism, totalmem } from 'node:os';
48
+
49
+ /** With TSC_TS_CHECKERS_TRACE: the process's resident and peak resident set in MB (Linux), else 0. */
50
+ export function residentMB(): number[] {
51
+ try {
52
+ const status = readFileSync('/proc/self/status', 'utf8');
53
+ const rss = /VmRSS:\s+(\d+)/.exec(status);
54
+ const hwm = /VmHWM:\s+(\d+)/.exec(status);
55
+ return [rss === null ? 0 : Math.round(Number(rss[1]) / 1024), hwm === null ? 0 : Math.round(Number(hwm[1]) / 1024)];
56
+ } catch {
57
+ return [0, 0];
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Starts checker thread `index` (main.ts owns the worker entry point). `program` is the shared,
63
+ * published program in scriptc builds and `undefined` under Node.js; ids continue above `nodeIds` and
64
+ * `symbolIds`. `reserved` are the positions of a group the thread's predecessor on `index` left
65
+ * (`CheckerResult.reserved`). With `emits` false the thread doesn't emit the files it checks, where
66
+ * the main thread emits the program (`checkersEmit`). `emitKinds`, by file index, is what the emit of
67
+ * each file includes (an `EmitOnly`, or -1 for none) where not every output of every file is emitted
68
+ * (an incremental program's rebuild); `undefined` for all of them.
69
+ */
70
+ export type CheckerSpawn = (
71
+ program: Program | undefined,
72
+ index: number,
73
+ counter: SharedArrayBuffer,
74
+ order: number[],
75
+ nodeIds: number,
76
+ symbolIds: number,
77
+ reserved: number[],
78
+ emits: boolean,
79
+ emitKinds: number[] | undefined,
80
+ checking: boolean,
81
+ ) => Worker;
82
+
83
+ export class ParallelCheck {
84
+ /** Set where checker threads can be started. */
85
+ spawn: CheckerSpawn | undefined = undefined;
86
+ /** Where checkers write the files they emit (`checkersEmit`); set where they can write files. */
87
+ writer: OutputWriter | undefined = undefined;
88
+ }
89
+
90
+ export const parallelCheck = new ParallelCheck();
91
+
92
+ /**
93
+ * What a checker thread sends back: the files it checked (indices) with their semantic diagnostics,
94
+ * their declaration diagnostics when the program reports them without emitting, and the results of
95
+ * emitting them when it emits them (`checkersEmit`).
96
+ */
97
+ export class CheckerResult {
98
+ constructor(
99
+ readonly index: number,
100
+ readonly files: number[],
101
+ readonly diagnostics: Diagnostic[][],
102
+ readonly declarations: Diagnostic[][],
103
+ readonly globals: Diagnostic[],
104
+ readonly checkTime: number,
105
+ /**
106
+ * With TSC_TS_CHECKERS_TRACE: wall clock (ms since the epoch) when the thread started, when its
107
+ * checker was ready and when it finished; types and instantiations it created; thread CPU ms
108
+ * (user, system); process RSS MB when it finished.
109
+ */
110
+ readonly stats: number[],
111
+ /** Whether the thread stopped at its file limit (`recycleFiles`) with files possibly left. */
112
+ readonly more: boolean = false,
113
+ /** The positions of a group the thread took and had not checked when it stopped at its file limit. */
114
+ readonly reserved: number[] = [],
115
+ readonly emits: EmitResult[] = [],
116
+ ) {}
117
+ }
118
+
119
+ /**
120
+ * Whether checker threads send their results as plain data (`PackedResult`), which the message
121
+ * copies, instead of publishing them (TSC_TS_REGION, default on in scriptc builds). Then nothing a
122
+ * checker thread allocated is reachable from another thread, and the scriptc runtime discards the
123
+ * thread's whole heap when it ends: a finished or retired checker returns its memory.
124
+ */
125
+ export const packsResults: boolean = sharesPublishedGraphs && process.env.TSC_TS_REGION !== '0';
126
+
127
+ /**
128
+ * The number of files after which a checker thread ends and a new one takes over its schedule
129
+ * index (TSC_TS_RECYCLE=n; 0, the default, for none). A new checker starts empty, so with packed
130
+ * results the old one's types, links and caches go back to the system.
131
+ */
132
+ export const recycleFiles: number = Math.max(0, Number(process.env.TSC_TS_RECYCLE ?? '0') | 0);
133
+
134
+ /**
135
+ * A `CheckerResult` as plain data. `nums` holds, in order, the diagnostic lists of `files`, the
136
+ * declaration diagnostic lists, the global diagnostics and the emit results, each list as a count
137
+ * followed by that many items. A diagnostic is file name (an index into `strs`, -1 for none), file
138
+ * text (index, -1 when the program has the file), pos, end, code, category, message,
139
+ * skippedOnNoEmit, the argument count and indices, its repopulate kind (0 for none) followed by the
140
+ * module reference (index), mode and package name (index) of other kinds, then its message chain and
141
+ * related information as lists. An emit result is emitSkipped, the emitted files, errors and
142
+ * unsupported files as lists of indices, then its diagnostics and its declaration signatures as a list
143
+ * of indices. `files` and `nums` are typed arrays, which postMessage copies as one block of memory
144
+ * each; it copies an array of numbers element by element.
145
+ */
146
+ export type PackedResult = {
147
+ readonly index: number;
148
+ readonly files: Int32Array;
149
+ readonly nums: Int32Array;
150
+ readonly strs: string[];
151
+ readonly checkTime: number;
152
+ readonly stats: number[];
153
+ readonly more: boolean;
154
+ readonly reserved: number[];
155
+ };
156
+
157
+ class ResultPacker {
158
+ readonly nums: number[] = [];
159
+ readonly strs: string[] = [];
160
+ private readonly indices = new Map<string, number>();
161
+
162
+ constructor(private readonly program: Program) {}
163
+
164
+ private str(text: string): number {
165
+ let k = this.indices.get(text);
166
+ if (k === undefined) {
167
+ k = this.strs.length;
168
+ this.strs.push(text);
169
+ this.indices.set(text, k);
170
+ }
171
+ return k;
172
+ }
173
+
174
+ list(diagnostics: readonly Diagnostic[]): void {
175
+ this.nums.push(diagnostics.length);
176
+ for (const d of diagnostics) this.diagnostic(d);
177
+ }
178
+
179
+ private strList(texts: readonly string[]): void {
180
+ this.nums.push(texts.length);
181
+ for (const text of texts) this.nums.push(this.str(text));
182
+ }
183
+
184
+ emit(result: EmitResult): void {
185
+ this.nums.push((result.emitSkipped ? 1 : 0) | (result.declarationTransformed ? 2 : 0));
186
+ this.strList(result.emittedFiles);
187
+ this.strList(result.errors);
188
+ this.strList(result.unsupported);
189
+ this.list(result.diagnostics);
190
+ this.strList(result.declarationSignatures);
191
+ this.strList(result.heldOutputs);
192
+ }
193
+
194
+ private diagnostic(d: Diagnostic): void {
195
+ const nums = this.nums;
196
+ const file = d.file;
197
+ if (file === undefined) nums.push(-1, -1);
198
+ else
199
+ nums.push(
200
+ this.str(file.fileName),
201
+ this.program.getSourceFile(file.fileName) === undefined ? this.str(file.text) : -1,
202
+ );
203
+ nums.push(d.pos, d.end, d.code, d.category, d.message, d.skippedOnNoEmit ? 1 : 0, d.args.length);
204
+ for (const arg of d.args) nums.push(this.str(arg));
205
+ const repopulate = d.repopulateInfo;
206
+ if (repopulate === undefined) nums.push(0);
207
+ else
208
+ nums.push(
209
+ repopulate.kind,
210
+ this.str(repopulate.moduleReference),
211
+ repopulate.mode,
212
+ this.str(repopulate.packageName),
213
+ );
214
+ this.list(d.messageChain);
215
+ this.list(d.relatedInformation);
216
+ }
217
+ }
218
+
219
+ /** Packs `result` (on the checker thread) for `postMessage`. */
220
+ export function packResult(result: CheckerResult, program: Program): PackedResult {
221
+ const packer = new ResultPacker(program);
222
+ for (const list of result.diagnostics) packer.list(list);
223
+ packer.nums.push(result.declarations.length);
224
+ for (const list of result.declarations) packer.list(list);
225
+ packer.list(result.globals);
226
+ packer.nums.push(result.emits.length);
227
+ for (const emit of result.emits) packer.emit(emit);
228
+ return {
229
+ index: result.index,
230
+ files: Int32Array.from(result.files),
231
+ nums: Int32Array.from(packer.nums),
232
+ strs: packer.strs,
233
+ checkTime: result.checkTime,
234
+ stats: result.stats,
235
+ more: result.more,
236
+ reserved: result.reserved,
237
+ };
238
+ }
239
+
240
+ /** Rebuilds a packed result on the main thread against `program`'s files. */
241
+ class ResultUnpacker {
242
+ private at = 0;
243
+
244
+ constructor(
245
+ private readonly packed: PackedResult,
246
+ private readonly program: Program,
247
+ private readonly files: Map<string, DiagnosticFile>,
248
+ ) {}
249
+
250
+ private next(): number {
251
+ return this.packed.nums[this.at++]!;
252
+ }
253
+
254
+ count(): number {
255
+ return this.next();
256
+ }
257
+
258
+ list(): Diagnostic[] {
259
+ const n = this.next();
260
+ const result: Diagnostic[] = [];
261
+ for (let k = 0; k < n; k++) result.push(this.diagnostic());
262
+ return result;
263
+ }
264
+
265
+ private diagnostic(): Diagnostic {
266
+ const strs = this.packed.strs;
267
+ const name = this.next();
268
+ const text = this.next();
269
+ let file: DiagnosticFile | undefined = undefined;
270
+ if (name >= 0) {
271
+ const fileName = strs[name]!;
272
+ file = this.files.get(fileName);
273
+ if (file === undefined) {
274
+ file =
275
+ text >= 0
276
+ ? new DiagnosticFile(fileName, strs[text]!)
277
+ : this.program.getSourceFile(fileName)!.diagnosticFile();
278
+ this.files.set(fileName, file);
279
+ }
280
+ }
281
+ const pos = this.next();
282
+ const end = this.next();
283
+ const code = this.next();
284
+ const category = this.next();
285
+ const message = this.next();
286
+ const skipped = this.next() === 1;
287
+ const argc = this.next();
288
+ const args: string[] = [];
289
+ for (let k = 0; k < argc; k++) args.push(strs[this.next()]!);
290
+ const result = new Diagnostic(file, pos, end, message, args);
291
+ result.code = code;
292
+ result.category = category;
293
+ result.skippedOnNoEmit = skipped;
294
+ const kind = this.next();
295
+ if (kind !== 0) {
296
+ const moduleReference = strs[this.next()]!;
297
+ const mode = this.next();
298
+ result.repopulateInfo = new RepopulateInfo(kind, moduleReference, mode, strs[this.next()]!);
299
+ }
300
+ result.messageChain = this.list();
301
+ result.relatedInformation = this.list();
302
+ return result;
303
+ }
304
+
305
+ private strList(into: string[]): void {
306
+ const n = this.next();
307
+ for (let k = 0; k < n; k++) into.push(this.packed.strs[this.next()]!);
308
+ }
309
+
310
+ emit(): EmitResult {
311
+ const result = new EmitResult();
312
+ const flags = this.next();
313
+ result.emitSkipped = (flags & 1) !== 0;
314
+ result.declarationTransformed = (flags & 2) !== 0;
315
+ this.strList(result.emittedFiles);
316
+ this.strList(result.errors);
317
+ this.strList(result.unsupported);
318
+ for (const d of this.list()) result.diagnostics.push(d);
319
+ this.strList(result.declarationSignatures);
320
+ this.strList(result.heldOutputs);
321
+ return result;
322
+ }
323
+ }
324
+
325
+ export function unpackResult(
326
+ packed: PackedResult,
327
+ program: Program,
328
+ files: Map<string, DiagnosticFile>,
329
+ ): CheckerResult {
330
+ const unpacker = new ResultUnpacker(packed, program, files);
331
+ const diagnostics: Diagnostic[][] = [];
332
+ for (let k = 0; k < packed.files.length; k++) diagnostics.push(unpacker.list());
333
+ const declarations: Diagnostic[][] = [];
334
+ const nd = unpacker.count();
335
+ for (let k = 0; k < nd; k++) declarations.push(unpacker.list());
336
+ const globals = unpacker.list();
337
+ const emits: EmitResult[] = [];
338
+ const ne = unpacker.count();
339
+ for (let k = 0; k < ne; k++) emits.push(unpacker.emit());
340
+ return new CheckerResult(
341
+ packed.index,
342
+ Array.from(packed.files),
343
+ diagnostics,
344
+ declarations,
345
+ globals,
346
+ packed.checkTime,
347
+ packed.stats,
348
+ packed.more,
349
+ packed.reserved,
350
+ emits,
351
+ );
352
+ }
353
+
354
+ /**
355
+ * Whether checkers also compute declaration diagnostics: under `noEmit` with declarations, reporting
356
+ * collects them for every file (getDiagnosticsOfProgram), and tsgo computes them per file on its
357
+ * checkers too. Otherwise they come from emitting.
358
+ */
359
+ function checkersReportDeclarations(checked: CheckedProgram): boolean {
360
+ const options = checked.program.options;
361
+ return options.noEmit === true && getEmitDeclarations(options);
362
+ }
363
+
364
+ /**
365
+ * Records in the shared schedule that a checked file has diagnostics, which under `noEmitOnError`
366
+ * keeps the program from emitting: checkers stop emitting the files they check.
367
+ */
368
+ function blockEmit(schedule: Int32Array): void {
369
+ Atomics.store(schedule, schedule.length - 1, 1);
370
+ }
371
+
372
+ function blocksEmit(schedule: Int32Array): boolean {
373
+ return Atomics.load(schedule, schedule.length - 1) !== 0;
374
+ }
375
+
376
+ /**
377
+ * Whether checkers emit the files they check, each right after checking it (where they can write
378
+ * files): unless the program doesn't emit. The checker that checked a file has its types at hand, and
379
+ * its declarations print as on the checker tsgo emits the file with. Under `noEmitOnError`, which
380
+ * makes emit wait for every file's diagnostics, they hold the outputs (`EmitResult.heldOutputs`) for
381
+ * the main thread, which writes them if there are no errors. The main thread emits the files no
382
+ * checker emitted. With `emits` false checkers don't emit.
383
+ */
384
+ function checkersEmit(checked: CheckedProgram, emits: boolean): boolean {
385
+ const options = checked.program.options;
386
+ return emits && parallelCheck.writer !== undefined && options.noEmit !== true && options.listFilesOnly !== true;
387
+ }
388
+
389
+ /**
390
+ * How the number of checkers is chosen without `--checkers`: `auto` (the default) uses every CPU
391
+ * this process may run on (`os.availableParallelism()`, at most `maxAutoCheckers`), and at least
392
+ * tsgo's four; fewer than the CPUs, but never fewer than four, when the estimated peak memory of more
393
+ * checkers would not fit (`memoryCheckers`). `fixed` (TSC_TS_CHECKERS=fixed) uses tsgo's four.
394
+ */
395
+ export const checkerPolicy: 'fixed' | 'auto' = process.env.TSC_TS_CHECKERS === 'fixed' ? 'fixed' : 'auto';
396
+
397
+ /** tsgo's number of checkers, and the fewest `auto` starts. */
398
+ const tsgoCheckers = 4;
399
+
400
+ /** The most checkers `auto` starts (measured up to 16 CPUs). */
401
+ const maxAutoCheckers = 16;
402
+
403
+ /**
404
+ * The number of checker threads for `checkers` (`--checkers`), or 1 to check on this thread. Without
405
+ * the option, native builds follow `checkerPolicy` (with `program`, the memory estimate of `auto`
406
+ * applies); under Node.js, where every thread builds its own copy of the program, tsc-ts checks on
407
+ * one thread.
408
+ */
409
+ export function checkerThreads(
410
+ checkers: number | undefined,
411
+ singleThreaded: boolean | undefined,
412
+ program?: Program,
413
+ ): number {
414
+ if (parallelCheck.spawn === undefined || singleThreaded === true) return 1;
415
+ const count = checkers ?? defaultCheckers(program);
416
+ return count > 1 ? Math.min(Math.floor(count), 256) : 1;
417
+ }
418
+
419
+ function defaultCheckers(program: Program | undefined): number {
420
+ if (!sharesPublishedGraphs) return 1;
421
+ if (program !== undefined && checksLittle(program)) return 1;
422
+ if (checkerPolicy === 'fixed') return tsgoCheckers;
423
+ const cpus = Math.min(availableParallelism(), maxAutoCheckers);
424
+ return autoCheckers(cpus, program === undefined ? Number.MAX_SAFE_INTEGER : memoryCheckers(program));
425
+ }
426
+
427
+ /**
428
+ * The most text of the files to check, in characters, of a program checked on this thread without
429
+ * `--checkers`: starting checker threads and sharing the program with them takes about 10 ms on 16
430
+ * CPUs, more than checking that much.
431
+ */
432
+ const littleText = 20_000;
433
+
434
+ /**
435
+ * Whether `program` has less than `littleText` characters to check, not counting the declaration
436
+ * files `skipLibCheck` skips.
437
+ */
438
+ function checksLittle(program: Program): boolean {
439
+ const options = program.options;
440
+ let text = 0;
441
+ for (const file of program.getSourceFiles()) {
442
+ if (file.isDeclarationFile && options.skipLibCheck === true) continue;
443
+ text += file.end;
444
+ if (text >= littleText) return false;
445
+ }
446
+ return true;
447
+ }
448
+
449
+ /** `auto` with `cpus` CPUs when the memory estimate fits `fit` checkers: tsgo's four at least. */
450
+ export function autoCheckers(cpus: number, fit: number): number {
451
+ return Math.max(tsgoCheckers, Math.min(cpus, fit));
452
+ }
453
+
454
+ /**
455
+ * The number of threads that parse ahead of a program checked by `checkers` checkers: every CPU this
456
+ * process may run on, as tsgo parses on every core whatever its checker count (parse threads hand
457
+ * their files to the shared program; what else they allocate is reused by the checker threads), but
458
+ * one. The main thread hands out the chunks they load: with a thread on every CPU, it waits for a CPU
459
+ * each time a thread returns a chunk, and so does that thread for its next chunk (VS Code's parse took
460
+ * 18-57 ms longer with 16 threads on 16 CPUs than with 15).
461
+ */
462
+ export function parseThreads(checkers: number): number {
463
+ const cpus = availableParallelism();
464
+ return Math.max(1, Math.min(cpus - 1, Math.max(checkers, Math.min(cpus, maxAutoCheckers))));
465
+ }
466
+
467
+ /**
468
+ * The program's memory per character of source text, where it can't be measured (`residentBytes`):
469
+ * 23-28 bytes on the benchmark apps when checking starts (parse threads' arenas included).
470
+ */
471
+ const programBytesPerChar = 32;
472
+
473
+ /**
474
+ * What the first checker adds, as a share of the program's memory: measured 0.24 (tRPC), 0.7-0.9
475
+ * (TypeORM, Playwright, Excalidraw), 1.0 (VS Code) and 1.4 (Sentry). The first checker needs this
476
+ * whatever the count, so the estimate mostly decides how many further checkers fit; programs above
477
+ * it stay within memory by retiring checkers (`retires`).
478
+ */
479
+ const firstCheckerShare = 1;
480
+
481
+ /**
482
+ * What each further checker adds, per character of declaration files, for a typical program:
483
+ * measured 6 bytes for VS Code, 9 for Excalidraw, 15 for Sentry and up to 31 for T3 Code's
484
+ * Effect-based projects (every checker resolves the declarations its files use; under 2 for tRPC).
485
+ */
486
+ const checkerBytesPerDeclarationChar = 24;
487
+
488
+ /** The share of the memory limit that checking may use (`checkersFittingMemory`, `retires`). */
489
+ const memoryShare = 0.85;
490
+
491
+ /**
492
+ * The share of the memory limit at which even checkers 1-3 retire, to finish on fewer threads rather
493
+ * than run out of memory (`retires`).
494
+ */
495
+ const lastResortShare = 0.95;
496
+
497
+ /**
498
+ * The most checkers whose estimated peak memory fits `memoryShare` of the memory limit, of `memory`
499
+ * bytes, for a program of `programBytes` (`firstCheckerShare`, `checkerBytesPerDeclarationChar`);
500
+ * at least 1.
501
+ */
502
+ export function checkersFittingMemory(programBytes: number, declarationChars: number, memory: number): number {
503
+ const peak = programBytes * (1 + firstCheckerShare);
504
+ const perChecker = Math.max(1, declarationChars * checkerBytesPerDeclarationChar);
505
+ return Math.max(1, 1 + Math.floor((memoryShare * memory - peak) / perChecker));
506
+ }
507
+
508
+ /**
509
+ * The memory this process may use, in bytes: the smallest of the total, its constrained memory, and
510
+ * the limits of its cgroup (`cgroupMemoryLimit`).
511
+ */
512
+ export function memoryLimit(): number {
513
+ let limit = totalmem();
514
+ const constrained = process.constrainedMemory();
515
+ if (constrained > 0 && constrained < limit) limit = constrained;
516
+ const cgroup = cgroupMemoryLimit();
517
+ if (cgroup > 0 && cgroup < limit) limit = cgroup;
518
+ return limit;
519
+ }
520
+
521
+ /**
522
+ * The smallest memory limit of this process's cgroup and its ancestors (Linux), or 0 where there is
523
+ * none or it is unknown: `memory.max` and `memory.high` under cgroup v2, `memory.limit_in_bytes`
524
+ * under v1. `process.constrainedMemory()` does not always see these: libuv reads only the process's
525
+ * own cgroup, and scriptc's runtime only the root of the cgroup mount, which is the process's own
526
+ * cgroup only inside a cgroup namespace.
527
+ */
528
+ export function cgroupMemoryLimit(): number {
529
+ let membership: string;
530
+ try {
531
+ membership = readFileSync('/proc/self/cgroup', 'utf8');
532
+ } catch {
533
+ return 0;
534
+ }
535
+ let limit = 0;
536
+ const lower = (value: number): void => {
537
+ if (value > 0 && (limit === 0 || value < limit)) limit = value;
538
+ };
539
+ for (const line of membership.split('\n')) {
540
+ // "0::/path" (v2), "N:memory:/path" or "N:cpu,memory:/path" (v1).
541
+ const first = line.indexOf(':');
542
+ const second = line.indexOf(':', first + 1);
543
+ if (first < 0 || second < 0) continue;
544
+ const controllers = line.substring(first + 1, second);
545
+ const path = line.substring(second + 1);
546
+ if (controllers === '') {
547
+ for (let dir = path; ; dir = dir.substring(0, dir.lastIndexOf('/'))) {
548
+ const base = '/sys/fs/cgroup' + (dir === '/' ? '' : dir);
549
+ lower(cgroupLimitValue(base + '/memory.max'));
550
+ lower(cgroupLimitValue(base + '/memory.high'));
551
+ if (dir === '/' || dir === '' || dir.lastIndexOf('/') < 0) break;
552
+ }
553
+ } else if (controllers.split(',').indexOf('memory') >= 0) {
554
+ lower(cgroupLimitValue('/sys/fs/cgroup/memory' + (path === '/' ? '' : path) + '/memory.limit_in_bytes'));
555
+ }
556
+ }
557
+ return limit;
558
+ }
559
+
560
+ /** A cgroup memory limit file's value in bytes, or 0 for `max`, an unreadable file or no number. */
561
+ function cgroupLimitValue(file: string): number {
562
+ let text: string;
563
+ try {
564
+ text = readFileSync(file, 'utf8').trim();
565
+ } catch {
566
+ return 0;
567
+ }
568
+ if (text === '' || text === 'max') return 0;
569
+ const value = Number(text);
570
+ // cgroup v1 reports no limit as a page-aligned value near 2^63.
571
+ return Number.isFinite(value) && value > 0 && value < 2 ** 62 ? value : 0;
572
+ }
573
+
574
+ /** The program whose text lengths `textLengths` holds. */
575
+ let lengthsProgram: Program | undefined = undefined;
576
+ let lengthsOfProgram: number[] = [];
577
+
578
+ /**
579
+ * The text length of each of `program`'s files, by index, computed once per program: a file's `end`,
580
+ * the length the parser found. In scriptc builds a thread caches the UTF-16 length of only the few
581
+ * non-ASCII strings it read last, so reading a non-ASCII file's `text.length` scans its text.
582
+ */
583
+ export function textLengths(program: Program): number[] {
584
+ if (lengthsProgram !== program) {
585
+ const lengths: number[] = [];
586
+ for (const file of program.getSourceFiles()) lengths.push(file.end);
587
+ lengthsProgram = program;
588
+ lengthsOfProgram = lengths;
589
+ }
590
+ return lengthsOfProgram;
591
+ }
592
+
593
+ /**
594
+ * The most checkers whose estimated peak memory fits (`checkersFittingMemory`), for `program`: its
595
+ * memory is this process's resident memory now (it is parsed and bound, and nothing is checked yet),
596
+ * or an estimate from its text where that is unknown.
597
+ */
598
+ export function memoryCheckers(program: Program): number {
599
+ let chars = 0;
600
+ let declarationChars = 0;
601
+ const files = program.getSourceFiles();
602
+ const length = textLengths(program);
603
+ for (let i = 0; i < files.length; i++) {
604
+ chars += length[i]!;
605
+ if (files[i]!.isDeclarationFile) declarationChars += length[i]!;
606
+ }
607
+ const resident = residentBytes();
608
+ const programBytes = resident > 0 ? resident : chars * programBytesPerChar;
609
+ const memory = memoryLimit();
610
+ const fit = checkersFittingMemory(programBytes, declarationChars, memory);
611
+ if (process.env.TSC_TS_CHECKERS_TRACE !== undefined)
612
+ process.stderr.write(
613
+ `checkers: ${chars} source chars (${declarationChars} in declaration files), program ${Math.round(programBytes / 1048576)}MB${resident > 0 ? '' : ' (estimated)'}, memory ${Math.round(memory / 1048576)}MB fits ${fit} checkers\n`,
614
+ );
615
+ return fit;
616
+ }
617
+
618
+ /**
619
+ * The files to check, costliest first, ties in program order. A file's estimated cost is its text
620
+ * length plus a share of the declaration files it makes its checker resolve: for each unchecked
621
+ * declaration file it imports, the text length of everything that file reaches through imports,
622
+ * divided by the number of checked files importing it (each checker resolves a library once, so a
623
+ * library many files import costs little per file, while one only a single small file imports, like
624
+ * a server framework's types under an adapter, makes that file one of the most expensive).
625
+ */
626
+ export function checkOrder(checked: CheckedProgram): number[] {
627
+ return costOrder(checked).order;
628
+ }
629
+
630
+ /** The estimated costs of checking a program's files (`checkOrder`). */
631
+ class CheckCosts {
632
+ constructor(
633
+ /** The files to check (indices into the program files), costliest first. */
634
+ readonly order: number[],
635
+ /** The estimated cost of each file, by index. */
636
+ readonly cost: number[],
637
+ /** The libraries (indices) each file of `order` imports, by index. */
638
+ readonly libraries: number[][],
639
+ /** What resolving a library costs a checker the first time (`libraryFirstUseShare` of the text it reaches), by index. */
640
+ readonly libraryCost: number[],
641
+ /** The program files (indices) each file of `order` imports, by index. */
642
+ readonly imports: (number[] | undefined)[],
643
+ ) {}
644
+ }
645
+
646
+ function costOrder(checked: CheckedProgram): CheckCosts {
647
+ const files = checked.program.getSourceFiles();
648
+ const only = checked.checkOnly;
649
+ const order: number[] = [];
650
+ for (let i = 0; i < files.length; i++)
651
+ if (!checked.skipTypeChecking(files[i]!) && (only === undefined || only.has(files[i]!))) order.push(i);
652
+ const costs = checkCosts(checked, order);
653
+ const cost = costs.cost;
654
+ order.sort((a, b) => cost[b]! - cost[a]! || a - b);
655
+ return costs;
656
+ }
657
+
658
+ /** How much of a library's text counts as checking cost of the files importing it. */
659
+ const libraryCostShare = 0.25;
660
+
661
+ /**
662
+ * How much of a library's text counts as the cost of a checker resolving it for the first time, in
663
+ * the steals of `takeCheapestEnd`: all of it. A checker pays that once for all the files importing
664
+ * the library, so the estimate of each file counts only a share (`libraryCostShare`); but a thief
665
+ * that takes files whose libraries it has not resolved pays for them all at once. Counted at a
666
+ * quarter, the end of a range was often taken in pieces by checkers that each paid for the
667
+ * library again: Playwright's 41 MCP tool files (zod, its MCP SDK and Playwright's own types)
668
+ * went to up to seven checkers, each paying 100-260 ms before its first tool file checked, often
669
+ * at the very end.
670
+ */
671
+ const libraryFirstUseShare = 1;
672
+
673
+ /** The estimated cost of checking each file of `order` (indices into the program files). */
674
+ function checkCosts(checked: CheckedProgram, order: number[]): CheckCosts {
675
+ const program = checked.program;
676
+ const files = program.getSourceFiles();
677
+ const cost: number[] = [];
678
+ const librariesOf: number[][] = [];
679
+ const libraryCost: number[] = [];
680
+ for (let i = 0; i < files.length; i++) {
681
+ cost.push(0);
682
+ librariesOf.push([]);
683
+ libraryCost.push(0);
684
+ }
685
+ const length = textLengths(program);
686
+ for (const i of order) cost[i] = length[i]!;
687
+ const indexOf = new Map<SourceFile, number>();
688
+ for (let i = 0; i < files.length; i++) indexOf.set(files[i]!, i);
689
+ // A file resolved by its own file name is that file (its path is the path of that name).
690
+ const byFileName = new Map<string, SourceFile>();
691
+ for (const file of files) byFileName.set(file.fileName, file);
692
+ const checkedFile: boolean[] = [];
693
+ for (let i = 0; i < files.length; i++) checkedFile.push(false);
694
+ for (const i of order) checkedFile[i] = true;
695
+ // Libraries: unchecked modules that are not default libraries (globals every checker has).
696
+ const isLibrary = (i: number): boolean =>
697
+ !checkedFile[i]! && !program.isSourceFileDefaultLibrary(files[i]!.path) && isExternalModuleFile(files[i]!);
698
+ const edges: (number[] | undefined)[] = [];
699
+ const edgesOf = (i: number): number[] => {
700
+ let result = edges[i];
701
+ if (result === undefined) {
702
+ result = [];
703
+ for (const target of program.getResolvedFilesOf(files[i]!, byFileName)) {
704
+ const k = indexOf.get(target);
705
+ if (k !== undefined && k !== i) result.push(k);
706
+ }
707
+ edges[i] = result;
708
+ }
709
+ return result;
710
+ };
711
+ const importers = new Map<number, number>();
712
+ for (const i of order) {
713
+ const libraries: number[] = [];
714
+ for (const k of edgesOf(i)) if (isLibrary(k) && libraries.indexOf(k) < 0) libraries.push(k);
715
+ librariesOf[i] = libraries;
716
+ for (const k of libraries) importers.set(k, (importers.get(k) ?? 0) + 1);
717
+ }
718
+ // The text length of the libraries each library reaches (itself included).
719
+ const reach = new Map<number, number>();
720
+ const reachOf = (root: number): number => {
721
+ let total = reach.get(root);
722
+ if (total !== undefined) return total;
723
+ total = 0;
724
+ const seen = new Set<number>([root]);
725
+ const stack = [root];
726
+ while (stack.length > 0) {
727
+ const k = stack.pop()!;
728
+ total += length[k]!;
729
+ for (const t of edgesOf(k))
730
+ if (!seen.has(t) && isLibrary(t)) {
731
+ seen.add(t);
732
+ stack.push(t);
733
+ }
734
+ }
735
+ reach.set(root, total);
736
+ return total;
737
+ };
738
+ for (let n = 0; n < order.length; n++) {
739
+ const i = order[n]!;
740
+ let share = 0;
741
+ for (const k of librariesOf[i]!) {
742
+ libraryCost[k] = libraryFirstUseShare * reachOf(k);
743
+ share += reachOf(k) / importers.get(k)!;
744
+ }
745
+ cost[i] = cost[i]! + libraryCostShare * share;
746
+ }
747
+ return new CheckCosts(order, cost, librariesOf, libraryCost, edges);
748
+ }
749
+
750
+ function isExternalModuleFile(file: SourceFile): boolean {
751
+ return file.externalModuleIndicator !== undefined;
752
+ }
753
+
754
+ /**
755
+ * The shared schedule (an Int32Array over a SharedArrayBuffer). Header: [0] the cursor of the
756
+ * shared queue, [1] a lock, [2] the number of leading positions of the order that every checker
757
+ * takes from the shared queue first, or -1 when the whole order is one shared queue, [3] the number
758
+ * of checkers, [4] the number of positions with costs (0 for none), [5] the number of libraries, [6]
759
+ * the words of a set of libraries, [7] the number of library references. Then, for each checker, the
760
+ * start and end of its range of positions; with costs, for each position the text length of its file
761
+ * and the start of its libraries in the library references (one more start at the end), the library
762
+ * references, for each library what resolving it costs, and for each checker the set of libraries it
763
+ * has resolved (one bit per library), and for each position the number of files of the group starting
764
+ * there (0 or 1 for a file of its own). Between the ranges and the costs, the time of the latest
765
+ * retirement (`Date.now()` modulo 2^31, 0 for none) and for each checker whether it retired
766
+ * (`retires`). The last word is 1 once a checked file has diagnostics (`blocksEmit`).
767
+ *
768
+ * The leading positions are the shared queue: the costliest files, and groups of files that a
769
+ * checker takes as a whole (`checkGroups`), which no other checker takes from it.
770
+ *
771
+ * With ranges, each checker checks a contiguous run of the program's files (files next to each
772
+ * other in program order tend to use the same declarations, so its checker resolves fewer of them),
773
+ * and a checker whose range is empty takes the end of another checker's range: the part that brings
774
+ * the later of the two checkers' estimated finishing times furthest forward, counting for itself the
775
+ * libraries it has not resolved yet. It takes nothing when that would not finish earlier: the end of
776
+ * a range is often a few files that are cheap only to the checker that resolved their libraries.
777
+ * Without costs, it takes the back half of the largest remaining range.
778
+ */
779
+ const scheduleHeader = 8;
780
+
781
+ export function newSchedule(count: number, positions = 0, libraries = 0, references = 0): SharedArrayBuffer {
782
+ const words = (libraries + 31) >> 5;
783
+ const plan = positions === 0 ? 0 : 3 * positions + 1 + references + libraries + count * words;
784
+ const buffer = new SharedArrayBuffer(4 * (scheduleHeader + 3 * count + 1 + plan + 1));
785
+ const schedule = new Int32Array(buffer);
786
+ schedule[3] = count;
787
+ if (positions > 0) {
788
+ schedule[4] = positions;
789
+ schedule[5] = libraries;
790
+ schedule[6] = words;
791
+ schedule[7] = references;
792
+ }
793
+ return buffer;
794
+ }
795
+
796
+ /** Where the parts of a schedule with costs start (`newSchedule`). */
797
+ class PlanLayout {
798
+ readonly lengths: number;
799
+ readonly starts: number;
800
+ readonly references: number;
801
+ readonly costs: number;
802
+ readonly groups: number;
803
+ readonly resolved: number;
804
+ readonly words: number;
805
+
806
+ constructor(schedule: Int32Array) {
807
+ const positions = schedule[4]!;
808
+ this.lengths = scheduleHeader + 3 * schedule[3]! + 1;
809
+ this.starts = this.lengths + positions;
810
+ this.references = this.starts + positions + 1;
811
+ this.costs = this.references + schedule[7]!;
812
+ this.groups = this.costs + schedule[5]!;
813
+ this.resolved = this.groups + positions;
814
+ this.words = schedule[6]!;
815
+ }
816
+ }
817
+
818
+ /**
819
+ * The costs of a schedule, copied out of the shared buffer once per checker: they do not change, and
820
+ * in scriptc builds every access to a shared buffer takes a process-wide lock, which the checkers
821
+ * waiting for the schedule's own lock also take.
822
+ */
823
+ export class SchedulePlan {
824
+ readonly layout: PlanLayout;
825
+ /** The schedule from its first length up to the sets of resolved libraries. */
826
+ private readonly data: Int32Array;
827
+
828
+ constructor(schedule: Int32Array) {
829
+ this.layout = new PlanLayout(schedule);
830
+ this.data = schedule.slice(this.layout.lengths, this.layout.resolved);
831
+ }
832
+
833
+ length(position: number): number {
834
+ return this.data[position]!;
835
+ }
836
+
837
+ /** The start of the libraries of `position` in the references (`referenceStart(position + 1)` is its end). */
838
+ referenceStart(position: number): number {
839
+ return this.data[this.layout.starts - this.layout.lengths + position]!;
840
+ }
841
+
842
+ reference(k: number): number {
843
+ return this.data[this.layout.references - this.layout.lengths + k]!;
844
+ }
845
+
846
+ cost(library: number): number {
847
+ return this.data[this.layout.costs - this.layout.lengths + library]!;
848
+ }
849
+
850
+ /** The number of files of the group starting at `position` (0 or 1 for a file of its own). */
851
+ group(position: number): number {
852
+ return this.data[this.layout.groups - this.layout.lengths + position]!;
853
+ }
854
+
855
+ /** A copy of the set of libraries `checker` has resolved. */
856
+ resolvedBy(schedule: Int32Array, checker: number): number[] {
857
+ const words: number[] = [];
858
+ const at = this.layout.resolved + checker * this.layout.words;
859
+ for (let w = 0; w < this.layout.words; w++) words.push(Atomics.load(schedule, at + w));
860
+ return words;
861
+ }
862
+ }
863
+
864
+ /** The costs of `schedule`, or `undefined` if it has none. */
865
+ export function schedulePlan(schedule: Int32Array): SchedulePlan | undefined {
866
+ return Atomics.load(schedule, 4) > 0 ? new SchedulePlan(schedule) : undefined;
867
+ }
868
+
869
+ function isResolved(words: readonly number[], library: number): boolean {
870
+ return (words[library >> 5]! & (1 << (library & 31))) !== 0;
871
+ }
872
+
873
+ /**
874
+ * Fills the costs of a schedule from `newSchedule(count, lengths.length, libraryCosts.length, R)`:
875
+ * the text length of the file at each position, the libraries (0 to `libraryCosts.length - 1`) it
876
+ * imports, R in total, what resolving each library costs, and the number of files of the group
877
+ * starting at each position (`groups`, by default none).
878
+ */
879
+ export function fillScheduleCosts(
880
+ schedule: Int32Array,
881
+ lengths: readonly number[],
882
+ libraries: readonly (readonly number[])[],
883
+ libraryCosts: readonly number[],
884
+ groups: readonly number[] = [],
885
+ ): void {
886
+ const layout = new PlanLayout(schedule);
887
+ for (let p = 0; p < groups.length; p++) schedule[layout.groups + p] = groups[p]!;
888
+ let reference = 0;
889
+ for (let p = 0; p < lengths.length; p++) {
890
+ schedule[layout.lengths + p] = lengths[p]!;
891
+ schedule[layout.starts + p] = reference;
892
+ for (const library of libraries[p]!) schedule[layout.references + reference++] = library;
893
+ }
894
+ schedule[layout.starts + lengths.length] = reference;
895
+ for (let k = 0; k < libraryCosts.length; k++) schedule[layout.costs + k] = Math.round(libraryCosts[k]!);
896
+ }
897
+
898
+ /** Records that `checker` resolves the libraries of the file at `position`. */
899
+ function markResolved(schedule: Int32Array, plan: SchedulePlan | undefined, checker: number, position: number): void {
900
+ if (plan === undefined) return;
901
+ const set = plan.layout.resolved + checker * plan.layout.words;
902
+ const end = plan.referenceStart(position + 1);
903
+ for (let r = plan.referenceStart(position); r < end; r++) {
904
+ const library = plan.reference(r);
905
+ Atomics.or(schedule, set + (library >> 5), 1 << (library & 31));
906
+ }
907
+ }
908
+
909
+ /**
910
+ * The next position of the order for checker `index`, or -1 when every file is taken. `plan` is
911
+ * `schedulePlan(schedule)`, which a checker reads once.
912
+ */
913
+ export function takePosition(
914
+ schedule: Int32Array,
915
+ length: number,
916
+ index: number,
917
+ plan: SchedulePlan | undefined = schedulePlan(schedule),
918
+ ): number {
919
+ const reserved = reservedBy(index);
920
+ if (reserved.length > 0) return reserved.shift()!;
921
+ const heavy = Atomics.load(schedule, 2);
922
+ if (heavy < 0) {
923
+ const k = Atomics.add(schedule, 0, 1);
924
+ return k < length ? k : -1;
925
+ }
926
+ for (;;) {
927
+ // The shared queue: a group goes to one checker as a whole.
928
+ const k = Atomics.load(schedule, 0);
929
+ if (k >= heavy) break;
930
+ const files = plan === undefined ? 1 : Math.max(1, plan.group(k));
931
+ if (Atomics.compareExchange(schedule, 0, k, k + files) !== k) continue;
932
+ for (let j = k; j < k + files; j++) {
933
+ markResolved(schedule, plan, index, j);
934
+ if (j > k) reserved.push(j);
935
+ }
936
+ return k;
937
+ }
938
+ while (Atomics.compareExchange(schedule, 1, 0, 1) !== 0) {
939
+ // Another checker is taking a file; this takes nanoseconds, or microseconds to take a range.
940
+ }
941
+ const own = scheduleHeader + 2 * index;
942
+ let lo = Atomics.load(schedule, own);
943
+ let hi = Atomics.load(schedule, own + 1);
944
+ if (lo >= hi) {
945
+ const taken = plan !== undefined ? takeCheapestEnd(schedule, plan, index) : takeBackHalf(schedule);
946
+ if (taken === undefined) {
947
+ Atomics.store(schedule, 1, 0);
948
+ return -1;
949
+ }
950
+ lo = taken[0]!;
951
+ hi = taken[1]!;
952
+ }
953
+ Atomics.store(schedule, own, lo + 1);
954
+ Atomics.store(schedule, own + 1, hi);
955
+ markResolved(schedule, plan, index, lo);
956
+ Atomics.store(schedule, 1, 0);
957
+ return lo;
958
+ }
959
+
960
+ /** The rest of a group that checker `index` took from the shared queue, by checker index. */
961
+ const reservedPositions = new Map<number, number[]>();
962
+
963
+ function reservedBy(index: number): number[] {
964
+ let reserved = reservedPositions.get(index);
965
+ if (reserved === undefined) {
966
+ reserved = [];
967
+ reservedPositions.set(index, reserved);
968
+ }
969
+ return reserved;
970
+ }
971
+
972
+ /** Where the retirement state starts in `schedule` (`newSchedule`). */
973
+ function retirementAt(schedule: Int32Array): number {
974
+ return scheduleHeader + 2 * Atomics.load(schedule, 3);
975
+ }
976
+
977
+ /** For checker `thief`, the whole remaining range of a checker that retired (its positions), or `undefined`. */
978
+ function takeRetired(schedule: Int32Array, thief: number): number[] | undefined {
979
+ const count = Atomics.load(schedule, 3);
980
+ const flags = retirementAt(schedule) + 1;
981
+ for (let v = 0; v < count; v++) {
982
+ if (v === thief || Atomics.load(schedule, flags + v) === 0) continue;
983
+ const at = scheduleHeader + 2 * v;
984
+ const lo = Atomics.load(schedule, at);
985
+ const hi = Atomics.load(schedule, at + 1);
986
+ if (lo >= hi) continue;
987
+ Atomics.store(schedule, at + 1, lo);
988
+ if (traceSteals)
989
+ process.stderr.write(`checkers: steal ${thief} from retired ${v} [${lo},${hi}) at ${Date.now()}\n`);
990
+ return [lo, hi];
991
+ }
992
+ return undefined;
993
+ }
994
+
995
+ /** How long after a retirement the next checker may retire: the first one's memory returns as its thread ends. */
996
+ const retirementSpacing = 1000;
997
+
998
+ /**
999
+ * Whether checker `index` stops taking files: it is not checker 0 and holds no group, the process's
1000
+ * resident memory has reached `limit` bytes (`retirementMemory`), and no other checker retired in the
1001
+ * last `retirementSpacing` ms. Its thread then ends, which returns its memory (with ProgramLifetime
1002
+ * classes, only its other objects: it still stops adding to it), and idle checkers take the rest of
1003
+ * its range.
1004
+ */
1005
+ export function retires(schedule: Int32Array, index: number, limit: number, resident = residentBytes()): boolean {
1006
+ if (index === 0 || reservedBy(index).length > 0 || resident < limit) return false;
1007
+ const at = retirementAt(schedule);
1008
+ const now = (Date.now() % 0x7fffffff) + 1;
1009
+ const last = Atomics.load(schedule, at);
1010
+ if (last !== 0 && (now - last + 0x7fffffff) % 0x7fffffff < retirementSpacing) return false;
1011
+ if (Atomics.compareExchange(schedule, at, last, now) !== last) return false;
1012
+ while (Atomics.compareExchange(schedule, 1, 0, 1) !== 0) {
1013
+ // Idle checkers read the flags under the schedule's lock.
1014
+ }
1015
+ Atomics.store(schedule, at + 1 + index, 1);
1016
+ Atomics.store(schedule, 1, 0);
1017
+ if (process.env.TSC_TS_CHECKERS_TRACE !== undefined)
1018
+ process.stderr.write(`checkers: thread ${index} retires at rss ${Math.round(resident / 1048576)}MB\n`);
1019
+ return true;
1020
+ }
1021
+
1022
+ /** The process's resident memory in bytes (Linux), or 0 where it is unknown. */
1023
+ function residentBytes(): number {
1024
+ try {
1025
+ // VmRSS is in kB, so this holds on kernels with any page size (statm counts pages).
1026
+ const rss = /VmRSS:\s+(\d+)/.exec(readFileSync('/proc/self/status', 'utf8'));
1027
+ return rss === null ? 0 : Number(rss[1]) * 1024;
1028
+ } catch {
1029
+ return 0;
1030
+ }
1031
+ }
1032
+
1033
+ /**
1034
+ * The resident memory, in bytes, at which checker `index` retires: `memoryShare` of the memory limit
1035
+ * beyond tsgo's four, `lastResortShare` for checkers 1-3; checker 0 never retires.
1036
+ */
1037
+ export function retirementMemory(index: number, memory = memoryLimit()): number {
1038
+ if (index === 0) return 0;
1039
+ return (index >= tsgoCheckers ? memoryShare : lastResortShare) * memory;
1040
+ }
1041
+
1042
+ /** Takes the back half of the largest remaining range: its positions, or `undefined` if none is left. */
1043
+ function takeBackHalf(schedule: Int32Array): number[] | undefined {
1044
+ const count = Atomics.load(schedule, 3);
1045
+ const retired = takeRetired(schedule, -1);
1046
+ if (retired !== undefined) return retired;
1047
+ let victim = -1;
1048
+ let most = 0;
1049
+ for (let v = 0; v < count; v++) {
1050
+ const at = scheduleHeader + 2 * v;
1051
+ const left = Atomics.load(schedule, at + 1) - Atomics.load(schedule, at);
1052
+ if (left > most) {
1053
+ most = left;
1054
+ victim = v;
1055
+ }
1056
+ }
1057
+ if (victim < 0) return undefined;
1058
+ const at = scheduleHeader + 2 * victim;
1059
+ const hi = Atomics.load(schedule, at + 1);
1060
+ const lo = Atomics.load(schedule, at) + (most >> 1);
1061
+ Atomics.store(schedule, at + 1, lo);
1062
+ return [lo, hi];
1063
+ }
1064
+
1065
+ /**
1066
+ * For checker `thief`: takes the end of another checker's range that brings the later of the two
1067
+ * estimated finishing times furthest forward (its positions), or nothing (`undefined`) if every part
1068
+ * would make it later. The owner's remaining cost counts its files and, once, the libraries they import that it
1069
+ * has not resolved; the thief's cost of an end counts its files and the libraries they import that
1070
+ * the thief has not resolved.
1071
+ */
1072
+ /** With TSC_TS_CHECKERS_TRACE=3: what each idle checker takes. */
1073
+ const traceSteals = process.env.TSC_TS_CHECKERS_TRACE === '3';
1074
+
1075
+ function takeCheapestEnd(schedule: Int32Array, plan: SchedulePlan, thief: number): number[] | undefined {
1076
+ const count = Atomics.load(schedule, 3);
1077
+ const retired = takeRetired(schedule, thief);
1078
+ if (retired !== undefined) return retired;
1079
+ const thiefResolved = plan.resolvedBy(schedule, thief);
1080
+ let bestVictim = -1;
1081
+ let bestStart = 0;
1082
+ // Even taking what changes nothing helps when the owner is busy with a long file, or never starts.
1083
+ let bestGain = -1;
1084
+ let bestRemaining = 0;
1085
+ let bestThiefCost = 0;
1086
+ for (let v = 0; v < count; v++) {
1087
+ if (v === thief) continue;
1088
+ const at = scheduleHeader + 2 * v;
1089
+ const lo = Atomics.load(schedule, at);
1090
+ const hi = Atomics.load(schedule, at + 1);
1091
+ if (lo >= hi) continue;
1092
+ const ownerResolved = plan.resolvedBy(schedule, v);
1093
+ let remaining = 0;
1094
+ const counted = new Set<number>();
1095
+ for (let p = lo; p < hi; p++) {
1096
+ remaining += plan.length(p);
1097
+ const end = plan.referenceStart(p + 1);
1098
+ for (let r = plan.referenceStart(p); r < end; r++) {
1099
+ const library = plan.reference(r);
1100
+ if (!counted.has(library) && !isResolved(ownerResolved, library)) {
1101
+ counted.add(library);
1102
+ remaining += plan.cost(library);
1103
+ }
1104
+ }
1105
+ }
1106
+ // The thief takes [start, hi): the owner keeps the rest, the thief pays for libraries it lacks.
1107
+ let taken = 0;
1108
+ let thiefCost = 0;
1109
+ const lacking = new Set<number>();
1110
+ for (let start = hi - 1; start >= lo; start--) {
1111
+ const length = plan.length(start);
1112
+ taken += length;
1113
+ thiefCost += length;
1114
+ const end = plan.referenceStart(start + 1);
1115
+ for (let r = plan.referenceStart(start); r < end; r++) {
1116
+ const library = plan.reference(r);
1117
+ if (!lacking.has(library) && !isResolved(thiefResolved, library)) {
1118
+ lacking.add(library);
1119
+ thiefCost += plan.cost(library);
1120
+ }
1121
+ }
1122
+ const gain = remaining - Math.max(remaining - taken, thiefCost);
1123
+ if (gain > bestGain) {
1124
+ bestGain = gain;
1125
+ bestVictim = v;
1126
+ bestStart = start;
1127
+ bestRemaining = remaining;
1128
+ bestThiefCost = thiefCost;
1129
+ }
1130
+ // Taking more only makes the thief finish later.
1131
+ if (thiefCost >= remaining - taken) break;
1132
+ }
1133
+ }
1134
+ if (bestVictim < 0) {
1135
+ if (traceSteals) process.stderr.write(`checkers: steal ${thief} none at ${Date.now()}\n`);
1136
+ return undefined;
1137
+ }
1138
+ const at = scheduleHeader + 2 * bestVictim;
1139
+ const hi = Atomics.load(schedule, at + 1);
1140
+ if (traceSteals)
1141
+ process.stderr.write(
1142
+ `checkers: steal ${thief} from ${bestVictim} [${bestStart},${hi}) of [${Atomics.load(schedule, at)},${hi}) remaining ${bestRemaining} thief ${bestThiefCost} at ${Date.now()}\n`,
1143
+ );
1144
+ Atomics.store(schedule, at + 1, bestStart);
1145
+ return [bestStart, hi];
1146
+ }
1147
+
1148
+ /**
1149
+ * A plan for checking a program with several checkers (`planSchedule`): the order of its files, the
1150
+ * number of leading positions every checker takes from the shared queue (-1 for all), the start and
1151
+ * end of each checker's range, and the costs of each position (`fillScheduleCosts`).
1152
+ */
1153
+ export class CheckPlan {
1154
+ constructor(
1155
+ readonly order: number[],
1156
+ readonly heavy: number,
1157
+ readonly ranges: number[],
1158
+ readonly lengths: number[],
1159
+ readonly libraries: number[][],
1160
+ readonly libraryCosts: number[],
1161
+ readonly references: number,
1162
+ /** For each position, the number of files of the group starting there (0 elsewhere). */
1163
+ readonly groups: number[] = [],
1164
+ ) {}
1165
+ }
1166
+
1167
+ /** The shared schedule of `plan` for `count` checkers. */
1168
+ export function scheduleOf(plan: CheckPlan, count: number): SharedArrayBuffer {
1169
+ const buffer = newSchedule(count, plan.lengths.length, plan.libraryCosts.length, plan.references);
1170
+ const schedule = new Int32Array(buffer);
1171
+ if (plan.lengths.length > 0)
1172
+ fillScheduleCosts(schedule, plan.lengths, plan.libraries, plan.libraryCosts, plan.groups);
1173
+ Atomics.store(schedule, 2, plan.heavy);
1174
+ for (let k = 0; k < plan.ranges.length; k++) Atomics.store(schedule, scheduleHeader + k, plan.ranges[k]!);
1175
+ return buffer;
1176
+ }
1177
+
1178
+ /** The most checked files that may import a file that starts a group (`checkGroups`), and the most files joining it from its importers. */
1179
+ const groupImporters = 16;
1180
+
1181
+ /** The least load (`checkGroups`) of a file that starts a group, in characters. */
1182
+ const groupLoad = 1_000_000;
1183
+
1184
+ /**
1185
+ * Groups of checked files that one of `count` checkers should check together, each costliest first
1186
+ * (`costs`, the estimate of `checkOrder`, also has the files' imports). A checker resolving the declarations of a file pays for much
1187
+ * of what the files it imports declare, and so does every checker checking a file that imports it:
1188
+ * a large generated schema and the files importing it, or a module that composes a server from
1189
+ * everything else, can cost each checker seconds. So a file whose load, its own text and the text
1190
+ * of the checked files it imports directly, is at least a sixteenth of all checked text and
1191
+ * `least` characters, and that 1 to `groupImporters` checked files import, starts a group. Such files
1192
+ * join the groups of those they import or that import them, and the files importing one directly
1193
+ * or indirectly join its group when there are at most `groupImporters` of them. Only groups of small
1194
+ * files around one large file form: the text of the others may be at most a quarter of a checker's
1195
+ * share, so that checking them together costs little more than their shared declarations (a group
1196
+ * of large components, each mostly its own work, would only make its checker finish last).
1197
+ */
1198
+ export function checkGroups(
1199
+ checked: CheckedProgram,
1200
+ count: number,
1201
+ least: number = groupLoad,
1202
+ costs: CheckCosts = costOrder(checked),
1203
+ ): number[][] {
1204
+ const files = checked.program.getSourceFiles();
1205
+ const n = files.length;
1206
+ const length = textLengths(checked.program);
1207
+ const order = costs.order;
1208
+ const cost = costs.cost;
1209
+ const isChecked: boolean[] = [];
1210
+ const isStart: boolean[] = [];
1211
+ const mark: number[] = [];
1212
+ const parent: number[] = [];
1213
+ const imports: (number[] | undefined)[] = [];
1214
+ const importers: (number[] | undefined)[] = [];
1215
+ for (let i = 0; i < n; i++) {
1216
+ isChecked.push(false);
1217
+ isStart.push(false);
1218
+ mark.push(-1);
1219
+ parent.push(-1);
1220
+ imports.push(undefined);
1221
+ importers.push(undefined);
1222
+ }
1223
+ for (const i of order) isChecked[i] = true;
1224
+ // The checked files that each checked file imports, once each, and their importers.
1225
+ let total = 0;
1226
+ for (const i of order) {
1227
+ total += length[i]!;
1228
+ const own: number[] = [];
1229
+ for (const k of costs.imports[i] ?? []) {
1230
+ if (!isChecked[k]! || mark[k] === i) continue;
1231
+ mark[k] = i;
1232
+ own.push(k);
1233
+ let by = importers[k];
1234
+ if (by === undefined) importers[k] = by = [];
1235
+ by.push(i);
1236
+ }
1237
+ imports[i] = own;
1238
+ }
1239
+ const threshold = Math.max(least, total / 16);
1240
+ const starts: number[] = [];
1241
+ for (const i of order) {
1242
+ const by = importers[i]?.length ?? 0;
1243
+ if (by < 1 || by > groupImporters) continue;
1244
+ let load = length[i]!;
1245
+ for (const k of imports[i]!) load += length[k]!;
1246
+ if (load < threshold) continue;
1247
+ starts.push(i);
1248
+ isStart[i] = true;
1249
+ }
1250
+ if (starts.length === 0) return [];
1251
+ // Union-find over the files of groups (`parent` -1: in none).
1252
+ const find = (x: number): number => {
1253
+ if (parent[x]! < 0) parent[x] = x;
1254
+ let root = x;
1255
+ while (parent[root] !== root) root = parent[root]!;
1256
+ while (parent[x] !== root) {
1257
+ const next = parent[x]!;
1258
+ parent[x] = root;
1259
+ x = next;
1260
+ }
1261
+ return root;
1262
+ };
1263
+ const union = (a: number, b: number): void => {
1264
+ const ra = find(a);
1265
+ const rb = find(b);
1266
+ if (ra !== rb) parent[ra] = rb;
1267
+ };
1268
+ for (let i = 0; i < n; i++) mark[i] = -1;
1269
+ for (const start of starts) {
1270
+ find(start);
1271
+ for (const k of imports[start]!) if (isStart[k]!) union(start, k);
1272
+ for (const k of importers[start]!) if (isStart[k]!) union(start, k);
1273
+ // The files importing it directly or indirectly, if they are few.
1274
+ const closure = [start];
1275
+ mark[start] = start;
1276
+ let complete = true;
1277
+ for (let q = 0; q < closure.length && complete; q++)
1278
+ for (const k of importers[closure[q]!] ?? []) {
1279
+ if (mark[k] === start) continue;
1280
+ mark[k] = start;
1281
+ closure.push(k);
1282
+ if (closure.length > groupImporters + 1) {
1283
+ complete = false;
1284
+ break;
1285
+ }
1286
+ }
1287
+ if (complete) for (const k of closure) union(start, k);
1288
+ }
1289
+ const members = new Map<number, number[]>();
1290
+ for (const i of order) {
1291
+ if (parent[i]! < 0) continue;
1292
+ const root = find(i);
1293
+ let group = members.get(root);
1294
+ if (group === undefined) members.set(root, (group = []));
1295
+ group.push(i);
1296
+ }
1297
+ const groups: number[][] = [];
1298
+ for (const group of members.values()) {
1299
+ if (group.length < 2) continue;
1300
+ let largest = 0;
1301
+ let text = 0;
1302
+ for (const i of group) {
1303
+ text += length[i]!;
1304
+ largest = Math.max(largest, length[i]!);
1305
+ }
1306
+ if (text - largest > total / count / 4) continue;
1307
+ group.sort((a, b) => cost[b]! - cost[a]! || a - b);
1308
+ groups.push(group);
1309
+ }
1310
+ return groups;
1311
+ }
1312
+
1313
+ /**
1314
+ * Plans checking with `count` checkers: the order to check files in, the costliest files and the
1315
+ * groups (`checkGroups`; at least `count` entries, and every file costing more than a 32nd of a
1316
+ * checker's share), costliest first, from the shared queue; then the others split, in program
1317
+ * order, into one range of about equal cost per checker, each costliest first.
1318
+ */
1319
+ export function planSchedule(checked: CheckedProgram, count: number, costs = costOrder(checked)): CheckPlan {
1320
+ const order = costs.order;
1321
+ const cost = costs.cost;
1322
+ if (order.length <= count) return new CheckPlan(order, -1, [], [], [], [], 0);
1323
+ let all = 0;
1324
+ for (const i of order) all += cost[i]!;
1325
+ // Files that cost more than a 32nd of a checker's share go first, costliest first: at the end of
1326
+ // a range they would make its checker finish last.
1327
+ let heavyCount = count;
1328
+ while (heavyCount < order.length && cost[order[heavyCount]!]! * count * 32 >= all) heavyCount++;
1329
+ // The groups go to the shared queue as one entry each, among the costliest files, by total cost.
1330
+ const groups = checkGroups(checked, count, groupLoad, costs);
1331
+ const grouped = new Set<number>();
1332
+ for (const group of groups) for (const i of group) grouped.add(i);
1333
+ const entries: number[][] = groups.slice();
1334
+ for (const i of order.slice(0, heavyCount)) if (!grouped.has(i)) entries.push([i]);
1335
+ const entryCost = (entry: number[]): number => {
1336
+ let sum = 0;
1337
+ for (const i of entry) sum += cost[i]!;
1338
+ return sum;
1339
+ };
1340
+ entries.sort((a, b) => entryCost(b) - entryCost(a) || a[0]! - b[0]!);
1341
+ const heavy: number[] = [];
1342
+ const groupAt: number[] = [];
1343
+ for (const entry of entries)
1344
+ for (let k = 0; k < entry.length; k++) {
1345
+ heavy.push(entry[k]!);
1346
+ groupAt.push(k === 0 && entry.length > 1 ? entry.length : 0);
1347
+ }
1348
+ const rest = order
1349
+ .slice(heavyCount)
1350
+ .filter((i) => !grouped.has(i))
1351
+ .sort((a, b) => a - b);
1352
+ heavyCount = heavy.length;
1353
+ let total = 0;
1354
+ for (const i of rest) total += cost[i]!;
1355
+ const planned = heavy.concat(rest);
1356
+ const ranges: number[] = [];
1357
+ let position = heavyCount;
1358
+ let sum = 0;
1359
+ for (let c = 0; c < count; c++) {
1360
+ const start = position;
1361
+ const target = (total * (c + 1)) / count;
1362
+ while (position < planned.length && (c === count - 1 || sum + cost[planned[position]!]! / 2 <= target)) {
1363
+ sum += cost[planned[position]!]!;
1364
+ position++;
1365
+ }
1366
+ ranges.push(start, position);
1367
+ // A range is the files of a checker; within it, costliest first, so that the files other
1368
+ // checkers take from its end, and the last files anyone checks, are cheap.
1369
+ const range = planned.slice(start, position).sort((a, b) => cost[b]! - cost[a]! || a - b);
1370
+ for (let k = 0; k < range.length; k++) planned[start + k] = range[k]!;
1371
+ }
1372
+ // The costs of each position, with the libraries numbered in order of first use.
1373
+ const files = checked.program.getSourceFiles();
1374
+ const textLength = textLengths(checked.program);
1375
+ const libraryIds = new Map<number, number>();
1376
+ const libraryCosts: number[] = [];
1377
+ const lengths: number[] = [];
1378
+ const libraries: number[][] = [];
1379
+ let references = 0;
1380
+ for (const i of planned) {
1381
+ lengths.push(textLength[i]!);
1382
+ const ids: number[] = [];
1383
+ for (const k of costs.libraries[i]!) {
1384
+ let id = libraryIds.get(k);
1385
+ if (id === undefined) {
1386
+ id = libraryCosts.length;
1387
+ libraryIds.set(k, id);
1388
+ libraryCosts.push(costs.libraryCost[k]!);
1389
+ }
1390
+ ids.push(id);
1391
+ }
1392
+ libraries.push(ids);
1393
+ references += ids.length;
1394
+ }
1395
+ while (groupAt.length < planned.length) groupAt.push(0);
1396
+ if (process.env.TSC_TS_CHECKERS_TRACE !== undefined)
1397
+ for (const group of groups)
1398
+ process.stderr.write(
1399
+ `checkers: group of ${group.length}: ${group.map((i) => files[i]!.fileName.split('/').slice(-2).join('/')).join(' ')}\n`,
1400
+ );
1401
+ return new CheckPlan(planned, heavyCount, ranges, lengths, libraries, libraryCosts, references, groupAt);
1402
+ }
1403
+
1404
+ /** With TSC_TS_CHECKERS_TRACE: the single-threaded checker's statistics, like a checker thread's. */
1405
+ export function traceSingleChecker(checked: CheckedProgram): void {
1406
+ if (process.env.TSC_TS_CHECKERS_TRACE === undefined) return;
1407
+ const c = checked.checker;
1408
+ const relations =
1409
+ c.subtypeRelation.size() +
1410
+ c.strictSubtypeRelation.size() +
1411
+ c.assignableRelation.size() +
1412
+ c.comparableRelation.size() +
1413
+ c.identityRelation.size();
1414
+ const cpu = process.threadCpuUsage();
1415
+ process.stderr.write(
1416
+ `checkers: single types ${c.typeCount} inst ${c.totalInstantiations} user ${Math.round(cpu.user / 1000)} sys ${Math.round(cpu.system / 1000)} rss ${residentMB().join('/')}MB symlinks ${c.symbolLinkTable.values.size} nodelinks ${c.nodeLinkTable.values.size} sigs ${c.signatureCount} rel ${relations}\n`,
1417
+ );
1418
+ }
1419
+
1420
+ /**
1421
+ * Checks files from the shared queue until it is empty; the main thread and checker threads alike.
1422
+ * A checker thread (`onMainThread` false) also sends back its global diagnostics. At `limit` files
1423
+ * it stops; `reserved` are the positions of a group its predecessor on `index` had not checked then.
1424
+ * With `emit` false it doesn't emit the files it checks (`checkersEmit`). With `checking` false it
1425
+ * only emits them (`startParallelCheck`).
1426
+ */
1427
+ export function checkFromQueue(
1428
+ checked: CheckedProgram,
1429
+ index: number,
1430
+ counter: Int32Array,
1431
+ order: number[],
1432
+ startedAt: number = Date.now(),
1433
+ onMainThread: boolean = true,
1434
+ limit: number = 0,
1435
+ reserved: readonly number[] = [],
1436
+ emit: boolean = true,
1437
+ emitKinds: readonly number[] | undefined = undefined,
1438
+ checking: boolean = true,
1439
+ ): CheckerResult {
1440
+ const start = performance.now();
1441
+ const readyAt = Date.now();
1442
+ const verbose = process.env.TSC_TS_CHECKERS_TRACE === '3';
1443
+ mark('checking');
1444
+ const files = checked.program.getSourceFiles();
1445
+ const done: number[] = [];
1446
+ const diagnostics: Diagnostic[][] = [];
1447
+ const declarations: Diagnostic[][] = [];
1448
+ const emits: EmitResult[] = [];
1449
+ // Emit (or declaration emit) of a file right after checking it, with this checker.
1450
+ const emitting = checkersEmit(checked, emit);
1451
+ // Outputs are held for the main thread under `noEmitOnError`, where emit waits for every file's diagnostics,
1452
+ // and when a composite program's rebuild emits on the threads (`emitKinds`): its main thread decides which
1453
+ // unchanged declaration files not to write (`EmitFilesHandler.skipDtsOutputOfComposite`).
1454
+ // Under `noEmitOnError` a file with diagnostics keeps the program from emitting (`blockEmit`).
1455
+ const blocksOnErrors = emitting && checked.program.options.noEmitOnError === true;
1456
+ const holding =
1457
+ blocksOnErrors || (emitting && checked.program.options.composite === true && emitKinds !== undefined)
1458
+ ? new HoldingWriter()
1459
+ : undefined;
1460
+ const writer = emitting ? (holding ?? parallelCheck.writer) : undefined;
1461
+ const emitter = writer !== undefined || checkersReportDeclarations(checked) ? new ProgramEmitter(checked) : undefined;
1462
+ // An incremental program records the signatures of the declaration files on the main thread.
1463
+ const options = checked.program.options;
1464
+ const signatures =
1465
+ writer !== undefined && isIncremental(options) && getEmitDeclarations(options)
1466
+ ? new DeclarationSignatureWriter(writer, options)
1467
+ : undefined;
1468
+ const plan = schedulePlan(counter);
1469
+ const retirement = retirementMemory(index);
1470
+ let memoryReadAt = 0;
1471
+ let more = false;
1472
+ let left: number[] = [];
1473
+ for (const k of reserved) reservedBy(index).push(k);
1474
+ for (;;) {
1475
+ // At its file limit the thread ends; a new one continues its schedule index, and the rest of the
1476
+ // group it took, which no other checker takes.
1477
+ if (limit > 0 && done.length >= limit) {
1478
+ more = true;
1479
+ left = reservedBy(index).splice(0);
1480
+ break;
1481
+ }
1482
+ // Except on checker 0, read the process's memory every 20 ms at most.
1483
+ if (retirement > 0 && performance.now() - memoryReadAt >= 20) {
1484
+ memoryReadAt = performance.now();
1485
+ if (retires(counter, index, retirement)) break;
1486
+ }
1487
+ const k = takePosition(counter, order.length, index, plan);
1488
+ if (k < 0) break;
1489
+ const i = order[k]!;
1490
+ done.push(i);
1491
+ const fileStart = verbose || timeline ? performance.now() : 0;
1492
+ // Without `checking` the file is only emitted: declaration emit checks what it needs.
1493
+ const fileDiagnostics = checking ? checked.unfilteredDiagnosticsOfFile(files[i]!) : [];
1494
+ diagnostics.push(fileDiagnostics);
1495
+ if (blocksOnErrors && fileDiagnostics.length > 0) blockEmit(counter);
1496
+ if (timeline)
1497
+ markAt(
1498
+ fileStart,
1499
+ `file ${(performance.now() - fileStart).toFixed(1)} pos ${k} ${files[i]!.text.length} ${files[i]!.fileName}`,
1500
+ );
1501
+ if (verbose)
1502
+ process.stderr.write(
1503
+ `checkers: file ${index} ${(performance.now() - fileStart).toFixed(1)}ms ${files[i]!.text.length} ${files[i]!.fileName}\n`,
1504
+ );
1505
+ if (blocksOnErrors && blocksEmit(counter)) {
1506
+ // The program won't emit (`handleNoEmitOnError`).
1507
+ const skipped = new EmitResult();
1508
+ skipped.emitSkipped = true;
1509
+ emits.push(skipped);
1510
+ } else if (writer !== undefined) {
1511
+ const emitStart = timeline ? performance.now() : 0;
1512
+ const kind = (emitKinds === undefined ? EmitOnly.All : emitKinds[i]!) as EmitOnly;
1513
+ if (kind < 0) emits.push(new EmitResult());
1514
+ else if (signatures !== undefined) {
1515
+ signatures.file = files[i]!;
1516
+ const result = emitter!.emit([files[i]!], kind, signatures);
1517
+ for (const s of signatures.take()) result.declarationSignatures.push(s);
1518
+ if (holding !== undefined) for (const o of holding.take()) result.heldOutputs.push(o);
1519
+ emits.push(result);
1520
+ } else {
1521
+ const result = emitter!.emit([files[i]!], kind, writer);
1522
+ if (holding !== undefined) for (const o of holding.take()) result.heldOutputs.push(o);
1523
+ emits.push(result);
1524
+ }
1525
+ if (timeline) markAt(emitStart, `emit ${(performance.now() - emitStart).toFixed(1)} ${files[i]!.fileName}`);
1526
+ } else if (emitter !== undefined) declarations.push(declarationDiagnosticsOfFile(emitter, files[i]!));
1527
+ }
1528
+ mark(`checked ${done.length} files`);
1529
+ // The main thread reports its own global diagnostics (CheckedProgram.getGlobalDiagnostics).
1530
+ const globals = onMainThread ? [] : checked.getGlobalDiagnostics().slice();
1531
+ let stats: number[] = [];
1532
+ if (process.env.TSC_TS_CHECKERS_TRACE !== undefined) {
1533
+ const cpu = process.threadCpuUsage();
1534
+ const checker = checked.checker;
1535
+ const relations =
1536
+ checker.subtypeRelation.size() +
1537
+ checker.strictSubtypeRelation.size() +
1538
+ checker.assignableRelation.size() +
1539
+ checker.comparableRelation.size() +
1540
+ checker.identityRelation.size();
1541
+ stats = [
1542
+ startedAt,
1543
+ readyAt,
1544
+ Date.now(),
1545
+ checker.typeCount,
1546
+ checker.totalInstantiations,
1547
+ cpu.user / 1000,
1548
+ cpu.system / 1000,
1549
+ residentMB()[0]!,
1550
+ checker.symbolLinkTable.values.size,
1551
+ checker.nodeLinkTable.values.size,
1552
+ checker.signatureCount,
1553
+ relations,
1554
+ ];
1555
+ }
1556
+ return new CheckerResult(
1557
+ index,
1558
+ done,
1559
+ diagnostics,
1560
+ declarations,
1561
+ globals,
1562
+ performance.now() - start,
1563
+ stats,
1564
+ more,
1565
+ left,
1566
+ emits,
1567
+ );
1568
+ }
1569
+
1570
+ /**
1571
+ * Whether the main thread is one of the checkers, given whether threads share the published program.
1572
+ * In scriptc builds it is not: the main thread carries the memory state of loading the program, and
1573
+ * checking there is slower than on a new thread (on Playwright the same files took 1.5 times as long
1574
+ * on the main thread, its cycle collector took twice the share of a checker thread's, and a checker
1575
+ * created on it after publishing was no faster), so the main thread often finished last. Starting
1576
+ * one more thread costs well under a millisecond. Under Node.js, where every checker thread builds
1577
+ * its own copy of the program, the main thread checks too.
1578
+ */
1579
+ export function mainThreadChecks(shared: boolean): boolean {
1580
+ return !shared;
1581
+ }
1582
+
1583
+ /**
1584
+ * When a checker's own files are a small part of what it resolves (`libraryBoundCheckers`): the least
1585
+ * text, in characters, of the libraries the files of a checker's range reach, on average over the
1586
+ * ranges, and the least multiple of the text of the range's own files it is.
1587
+ */
1588
+ const libraryBoundText = 6_000_000;
1589
+ const libraryBoundShare = 5;
1590
+
1591
+ /**
1592
+ * The number of checkers for `count` checkers whose ranges reach `library` characters of libraries
1593
+ * and check `own` characters each, on average: three quarters of them (tsgo's four at least) where
1594
+ * every checker spends most of its time resolving the same large libraries (`libraryBoundText`,
1595
+ * `libraryBoundShare`), else `count`.
1596
+ *
1597
+ * Each checker resolves the declarations its files reach, whatever else it checks. Where they are
1598
+ * millions of characters, such as an Effect-based project's, and much more than the checker's own
1599
+ * files, a checker costs seconds of CPU and hundreds of MB before its files get cheap, and the last
1600
+ * quarter of the checkers (on 16 CPUs of 8 cores, the ones sharing a core with another) repeat that
1601
+ * work rather than save time: T3 Code's web and server projects ran 3% and 1% faster with 12
1602
+ * checkers than with 16, and used 16% and 8% less memory. Where checkers resolve less, or mostly
1603
+ * check their own files, 12 of 16 checkers were slower: Excalidraw (1.4M library and 0.15M own
1604
+ * characters per range) by 6%, Sentry (5.7M and 3.0M) and the tsc-ts sources by about 11%.
1605
+ */
1606
+ export function libraryBoundCount(count: number, library: number, own: number): number {
1607
+ return library >= libraryBoundText && library >= libraryBoundShare * own
1608
+ ? Math.max(tsgoCheckers, Math.ceil((count * 3) / 4))
1609
+ : count;
1610
+ }
1611
+
1612
+ /**
1613
+ * The number of checkers for `plan`, made with `count` checkers that `auto` chose
1614
+ * (`libraryBoundCount` of what its ranges reach and check).
1615
+ */
1616
+ function libraryBoundCheckers(checked: CheckedProgram, plan: CheckPlan, costs: CheckCosts, count: number): number {
1617
+ if (checkerPolicy !== 'auto' || checked.program.options.checkers !== undefined || count <= tsgoCheckers) return count;
1618
+ const program = checked.program;
1619
+ const files = program.getSourceFiles();
1620
+ const length = textLengths(program);
1621
+ const isChecked: boolean[] = [];
1622
+ for (let i = 0; i < files.length; i++) isChecked.push(false);
1623
+ for (const i of costs.order) isChecked[i] = true;
1624
+ const ranges = plan.ranges.length >> 1;
1625
+ if (ranges === 0) return count;
1626
+ let library = 0;
1627
+ let own = 0;
1628
+ for (let r = 0; r < ranges; r++) {
1629
+ // The libraries the range's files import, and what they reach through other libraries (as checkCosts counts them).
1630
+ const reached = new Set<number>();
1631
+ const stack: number[] = [];
1632
+ for (let p = plan.ranges[2 * r]!; p < plan.ranges[2 * r + 1]!; p++) {
1633
+ const i = plan.order[p]!;
1634
+ own += length[i]!;
1635
+ for (const k of costs.libraries[i]!)
1636
+ if (!reached.has(k)) {
1637
+ reached.add(k);
1638
+ stack.push(k);
1639
+ }
1640
+ }
1641
+ while (stack.length > 0) {
1642
+ const k = stack.pop()!;
1643
+ library += length[k]!;
1644
+ for (const t of costs.imports[k] ?? [])
1645
+ if (
1646
+ !reached.has(t) &&
1647
+ !isChecked[t]! &&
1648
+ !program.isSourceFileDefaultLibrary(files[t]!.path) &&
1649
+ isExternalModuleFile(files[t]!)
1650
+ ) {
1651
+ reached.add(t);
1652
+ stack.push(t);
1653
+ }
1654
+ }
1655
+ }
1656
+ const fewer = libraryBoundCount(count, library / ranges, own / ranges);
1657
+ if (process.env.TSC_TS_CHECKERS_TRACE !== undefined)
1658
+ process.stderr.write(
1659
+ `checkers: ranges reach ${Math.round(library / ranges / 1000)}K library chars and check ${Math.round(own / ranges / 1000)}K on average: ${fewer} checkers\n`,
1660
+ );
1661
+ return fewer;
1662
+ }
1663
+
1664
+ /**
1665
+ * Checks `checked` with `checkers` checkers (fewer where `auto` chose them and they would mostly
1666
+ * resolve the same libraries: `libraryBoundCheckers`) and calls `done` with the wall time once
1667
+ * `checked` holds every file's diagnostics.
1668
+ */
1669
+ /**
1670
+ * Shares the program of `checked` with checker threads (scriptc builds): this thread's checker merges the
1671
+ * globals and module augmentations, which checker threads start from, and the program is published, so
1672
+ * immutable from then on. `startParallelCheck` does this first unless it was done before.
1673
+ */
1674
+ export function shareWithCheckers(checked: CheckedProgram): void {
1675
+ const program = checked.program;
1676
+ program.prepareForSharing();
1677
+ mark('prepared for sharing');
1678
+ program.mergedGlobals = shareMergedGlobals(checked.checker);
1679
+ mark('globals merged');
1680
+ // Checker threads emit, or report declaration errors, with the program's indexes.
1681
+ const options = program.options;
1682
+ if (options.noEmit !== true || getEmitDeclarations(options)) {
1683
+ program.emitIndexes = new EmitIndexes(program);
1684
+ mark('emit indexes built');
1685
+ }
1686
+ mark('libraries verified');
1687
+ publish(program);
1688
+ mark('program published');
1689
+ // Lazily parsed JSDoc goes to a table of this thread.
1690
+ useLazyJSDocTable();
1691
+ checked.sharedWithCheckers = true;
1692
+ }
1693
+
1694
+ /**
1695
+ * Checks `checked` on `checkers` threads and calls `done` once they are done. With `emits`, each thread
1696
+ * emits the files it checks (`emitKinds` limits what). With `checking` false, the threads only emit:
1697
+ * for a program whose report has no semantic diagnostics, so that no file is checked in full.
1698
+ */
1699
+ export function startParallelCheck(
1700
+ checked: CheckedProgram,
1701
+ checkers: number,
1702
+ done: (checkTime: number) => void,
1703
+ emits: boolean = true,
1704
+ emitKinds: number[] | undefined = undefined,
1705
+ checking: boolean = true,
1706
+ ): void {
1707
+ let count = checkers;
1708
+ const start = performance.now();
1709
+ const program = checked.program;
1710
+ const files = program.getSourceFiles();
1711
+ const trace = process.env.TSC_TS_CHECKERS_TRACE !== undefined;
1712
+ const shared = sharesPublishedGraphs;
1713
+ const epoch = Date.now();
1714
+ if (trace) process.stderr.write(`checkers: start at ${epoch} rss ${residentMB().join('/')}MB\n`);
1715
+ if (shared && !checked.sharedWithCheckers) {
1716
+ shareWithCheckers(checked);
1717
+ if (trace)
1718
+ process.stderr.write(
1719
+ `checkers: published the program in ${(performance.now() - start).toFixed(1)}ms rss ${residentMB().join('/')}MB\n`,
1720
+ );
1721
+ }
1722
+ const orderStart = performance.now();
1723
+ const costs = costOrder(checked);
1724
+ let plan = planSchedule(checked, count, costs);
1725
+ const fewer = libraryBoundCheckers(checked, plan, costs, count);
1726
+ if (fewer < count) {
1727
+ count = fewer;
1728
+ plan = planSchedule(checked, count, costs);
1729
+ }
1730
+ const order = plan.order;
1731
+ const counter = scheduleOf(plan, count);
1732
+ if (trace) {
1733
+ let top = '';
1734
+ for (let k = 0; k < Math.min(5, order.length); k++) top += ' ' + files[order[k]!]!.fileName;
1735
+ process.stderr.write(
1736
+ `checkers: ordered ${order.length} files in ${(performance.now() - orderStart).toFixed(1)}ms, first:${top}\n`,
1737
+ );
1738
+ }
1739
+ if (shared) {
1740
+ publish(order);
1741
+ if (emitKinds !== undefined) publish(emitKinds);
1742
+ }
1743
+ mark(`planned ${order.length} files, ${plan.heavy} heavy`);
1744
+ const perFile: (Diagnostic[] | undefined)[] = [];
1745
+ const perFileDeclarations: (Diagnostic[] | undefined)[] = [];
1746
+ const perFileEmits: (EmitResult | undefined)[] = [];
1747
+ for (let i = 0; i < files.length; i++) {
1748
+ perFile.push(undefined);
1749
+ perFileDeclarations.push(undefined);
1750
+ perFileEmits.push(undefined);
1751
+ }
1752
+ const revival = shared ? undefined : new Revival(program);
1753
+ const mainChecks = mainThreadChecks(shared);
1754
+ const firstThread = mainChecks ? 1 : 0;
1755
+ let remaining = count - firstThread;
1756
+ let ownDone = false;
1757
+ const finish = (): void => {
1758
+ if (checking) {
1759
+ // A thread that failed may have taken files it never checked: check them here.
1760
+ for (const i of order) if (perFile[i] === undefined) perFile[i] = checked.unfilteredDiagnosticsOfFile(files[i]!);
1761
+ checked.checkedInParallel = perFile;
1762
+ }
1763
+ mark('all checked');
1764
+ if (trace)
1765
+ process.stderr.write(
1766
+ `checkers: finished at ${(performance.now() - start).toFixed(1)}ms rss ${residentMB().join('/')}MB\n`,
1767
+ );
1768
+ // Files without declaration diagnostics or emit results here get them on this thread (PlainProgram).
1769
+ if (checkersReportDeclarations(checked)) checked.declarationsInParallel = perFileDeclarations;
1770
+ for (const [i, result] of emittedHere) perFileEmits[i] = result;
1771
+ if (checkersEmit(checked, emits)) {
1772
+ checked.emittedInParallel = perFileEmits;
1773
+ // A file whose declarations were emitted has its declaration diagnostics in the emit result, so
1774
+ // the program doesn't transform it again for them (handleNoEmitOnError).
1775
+ if (!checkersReportDeclarations(checked) && getEmitDeclarations(checked.program.options)) {
1776
+ for (let i = 0; i < perFileEmits.length; i++) {
1777
+ const emitted = perFileEmits[i];
1778
+ if (emitted !== undefined && emitted.declarationTransformed) perFileDeclarations[i] = emitted.diagnostics;
1779
+ }
1780
+ checked.declarationsInParallel = perFileDeclarations;
1781
+ }
1782
+ }
1783
+ done(performance.now() - start);
1784
+ };
1785
+ const record = (result: CheckerResult): void => {
1786
+ for (let k = 0; k < result.files.length; k++) perFile[result.files[k]!] = result.diagnostics[k]!;
1787
+ for (let k = 0; k < result.declarations.length; k++)
1788
+ perFileDeclarations[result.files[k]!] = result.declarations[k]!;
1789
+ for (let k = 0; k < result.emits.length; k++) perFileEmits[result.files[k]!] = result.emits[k]!;
1790
+ for (const d of result.globals) checked.parallelGlobalDiagnostics.push(d);
1791
+ if (trace) {
1792
+ const st = result.stats;
1793
+ const rel = (t: number | undefined): string => (t === undefined ? '?' : String(t - epoch));
1794
+ process.stderr.write(
1795
+ `checkers: thread ${result.index} checked ${result.files.length} files in ${result.checkTime.toFixed(1)}ms, done at ${(performance.now() - start).toFixed(1)}ms` +
1796
+ ` [start ${rel(st[0])} ready ${rel(st[1])} end ${rel(st[2])} types ${st[3]} inst ${st[4]} user ${Math.round(st[5] ?? 0)} sys ${Math.round(st[6] ?? 0)} rss ${st[7]}MB symlinks ${st[8]} nodelinks ${st[9]} sigs ${st[10]} rel ${st[11]}]\n`,
1797
+ );
1798
+ }
1799
+ };
1800
+ const threadDone = (): void => {
1801
+ remaining--;
1802
+ if (remaining === 0 && ownDone) finish();
1803
+ };
1804
+ const diagnosticFiles = new Map<string, DiagnosticFile>();
1805
+ const spawn = (i: number, reserved: number[]): void => {
1806
+ const worker = parallelCheck.spawn!(
1807
+ shared ? program : undefined,
1808
+ i,
1809
+ counter,
1810
+ order,
1811
+ lastNodeIdValue(),
1812
+ lastSymbolIdValue(),
1813
+ reserved,
1814
+ emits,
1815
+ emitKinds,
1816
+ checking,
1817
+ );
1818
+ worker.on('message', (message: unknown) => {
1819
+ mark(`result from c${i}`);
1820
+ let result: CheckerResult;
1821
+ if (packsResults) {
1822
+ result = unpackResult(message as PackedResult, program, diagnosticFiles);
1823
+ record(result);
1824
+ } else {
1825
+ result = (message as { result: CheckerResult }).result;
1826
+ record(revival === undefined ? publish(result) : revival.result(result));
1827
+ }
1828
+ // A thread at its file limit hands its schedule index to a new one.
1829
+ if (result.more) spawn(i, result.reserved);
1830
+ else threadDone();
1831
+ });
1832
+ worker.on('error', (error: unknown) => {
1833
+ if (trace) process.stderr.write(`checkers: thread ${i} failed: ${String(error)}\n`);
1834
+ threadDone();
1835
+ });
1836
+ };
1837
+ for (let i = firstThread; i < count; i++) spawn(i, []);
1838
+ mark('checkers spawned');
1839
+ // Without a shared program, this thread's checker (`CheckedProgram.checker`) merges the globals
1840
+ // while the threads start.
1841
+ void checked.checker;
1842
+ mark('reporting checker created');
1843
+ const emittedHere =
1844
+ !mainChecks && emitKinds === undefined ? emitUncheckedFiles(checked, order, emits) : new Map<number, EmitResult>();
1845
+ if (mainChecks)
1846
+ record(checkFromQueue(checked, 0, new Int32Array(counter), order, epoch, true, 0, [], emits, emitKinds, checking));
1847
+ ownDone = true;
1848
+ if (remaining === 0) finish();
1849
+ }
1850
+
1851
+ /**
1852
+ * Emits, on this thread and into memory, the files that checker threads emit nothing for because
1853
+ * they don't check them (`skipTypeChecking`, such as a file with `// @ts-nocheck`), while the threads
1854
+ * check the others: the program would emit them here after the threads finish. This thread's checker
1855
+ * hasn't checked anything yet either way, so the outputs are the same. Returns the results by file
1856
+ * index; the program writes them in order with the threads' (`writeHeldOutputs`).
1857
+ */
1858
+ function emitUncheckedFiles(
1859
+ checked: CheckedProgram,
1860
+ order: readonly number[],
1861
+ emits: boolean,
1862
+ ): Map<number, EmitResult> {
1863
+ const results = new Map<number, EmitResult>();
1864
+ const program = checked.program;
1865
+ const options = program.options;
1866
+ if (!checkersEmit(checked, emits) || options.noEmitOnError === true || isIncremental(options)) return results;
1867
+ const files = program.getSourceFiles();
1868
+ const inOrder: boolean[] = [];
1869
+ for (let i = 0; i < files.length; i++) inOrder.push(false);
1870
+ for (const i of order) inOrder[i] = true;
1871
+ let emitter: ProgramEmitter | undefined = undefined;
1872
+ let holding: HoldingWriter | undefined = undefined;
1873
+ for (let i = 0; i < files.length; i++) {
1874
+ const file = files[i]!;
1875
+ if (inOrder[i]! || file.isDeclarationFile || !program.sourceFileMayBeEmitted(file, false)) continue;
1876
+ if (emitter === undefined) {
1877
+ emitter = new ProgramEmitter(checked);
1878
+ // The program reports the global diagnostics this checker has before emitting, as it would
1879
+ // collect them before emitting these files.
1880
+ checked.ownGlobalDiagnostics = checked.checker.getGlobalDiagnostics().slice();
1881
+ }
1882
+ if (holding === undefined) holding = new HoldingWriter();
1883
+ const start = timeline ? performance.now() : 0;
1884
+ const result = emitter.emit([file], EmitOnly.All, holding);
1885
+ for (const output of holding.take()) result.heldOutputs.push(output);
1886
+ results.set(i, result);
1887
+ if (timeline) markAt(start, `emit ${(performance.now() - start).toFixed(1)} ${file.fileName}`);
1888
+ }
1889
+ return results;
1890
+ }
1891
+
1892
+ /** Diagnostics copied from another thread (Node.js), attached to this program's files again. */
1893
+ class Revival {
1894
+ private readonly files = new Map<string, DiagnosticFile>();
1895
+
1896
+ constructor(program: Program) {
1897
+ for (const file of program.getSourceFiles()) this.files.set(file.fileName, file.diagnosticFile());
1898
+ }
1899
+
1900
+ result(copy: CheckerResult): CheckerResult {
1901
+ return new CheckerResult(
1902
+ copy.index,
1903
+ copy.files,
1904
+ copy.diagnostics.map((diagnostics) => diagnostics.map((d) => this.diagnostic(d))),
1905
+ copy.declarations.map((diagnostics) => diagnostics.map((d) => this.diagnostic(d))),
1906
+ copy.globals.map((d) => this.diagnostic(d)),
1907
+ copy.checkTime,
1908
+ copy.stats,
1909
+ copy.more,
1910
+ copy.reserved,
1911
+ copy.emits.map((emit) => this.emit(emit)),
1912
+ );
1913
+ }
1914
+
1915
+ private emit(copy: EmitResult): EmitResult {
1916
+ const result = new EmitResult();
1917
+ result.emitSkipped = copy.emitSkipped;
1918
+ result.declarationTransformed = copy.declarationTransformed;
1919
+ for (const f of copy.emittedFiles) result.emittedFiles.push(f);
1920
+ for (const e of copy.errors) result.errors.push(e);
1921
+ for (const u of copy.unsupported) result.unsupported.push(u);
1922
+ for (const d of copy.diagnostics) result.diagnostics.push(this.diagnostic(d));
1923
+ for (const s of copy.declarationSignatures) result.declarationSignatures.push(s);
1924
+ for (const o of copy.heldOutputs) result.heldOutputs.push(o);
1925
+ return result;
1926
+ }
1927
+
1928
+ private diagnostic(copy: Diagnostic): Diagnostic {
1929
+ let file: DiagnosticFile | undefined = undefined;
1930
+ const copied = copy.file;
1931
+ if (copied !== undefined) {
1932
+ file = this.files.get(copied.fileName);
1933
+ if (file === undefined) {
1934
+ file = new DiagnosticFile(copied.fileName, copied.text);
1935
+ this.files.set(copied.fileName, file);
1936
+ }
1937
+ }
1938
+ const result = new Diagnostic(file, copy.pos, copy.end, copy.message, copy.args);
1939
+ result.code = copy.code;
1940
+ result.category = copy.category;
1941
+ result.skippedOnNoEmit = copy.skippedOnNoEmit;
1942
+ const repopulate = copy.repopulateInfo;
1943
+ if (repopulate !== undefined)
1944
+ result.repopulateInfo = new RepopulateInfo(
1945
+ repopulate.kind,
1946
+ repopulate.moduleReference,
1947
+ repopulate.mode,
1948
+ repopulate.packageName,
1949
+ );
1950
+ result.messageChain = copy.messageChain.map((d) => this.diagnostic(d));
1951
+ result.relatedInformation = copy.relatedInformation.map((d) => this.diagnostic(d));
1952
+ return result;
1953
+ }
1954
+ }