@soundscript/soundscript 0.1.13 → 0.1.15

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 (441) hide show
  1. package/package.json +15 -6
  2. package/project-transform/index.js +2 -0
  3. package/project-transform/index.ts +8 -0
  4. package/project-transform/src/annotation_syntax.js +948 -0
  5. package/project-transform/src/annotation_syntax.ts +1217 -0
  6. package/project-transform/src/build_package.js +475 -0
  7. package/project-transform/src/build_package.ts +683 -0
  8. package/project-transform/src/bundled/portable-web-globals.d.ts +153 -0
  9. package/project-transform/src/bundled/runtime_externs.js +220 -0
  10. package/project-transform/src/bundled/runtime_externs.ts +237 -0
  11. package/project-transform/src/bundled/sound-libs/lib.decorators.d.ts +385 -0
  12. package/project-transform/src/bundled/sound-libs/lib.decorators.legacy.d.ts +22 -0
  13. package/project-transform/src/bundled/sound-libs/lib.dom.asynciterable.d.ts +42 -0
  14. package/project-transform/src/bundled/sound-libs/lib.dom.d.ts +39440 -0
  15. package/project-transform/src/bundled/sound-libs/lib.es2015.collection.d.ts +149 -0
  16. package/project-transform/src/bundled/sound-libs/lib.es2015.core.d.ts +657 -0
  17. package/project-transform/src/bundled/sound-libs/lib.es2015.d.ts +28 -0
  18. package/project-transform/src/bundled/sound-libs/lib.es2015.generator.d.ts +77 -0
  19. package/project-transform/src/bundled/sound-libs/lib.es2015.iterable.d.ts +616 -0
  20. package/project-transform/src/bundled/sound-libs/lib.es2015.promise.d.ts +80 -0
  21. package/project-transform/src/bundled/sound-libs/lib.es2015.proxy.d.ts +128 -0
  22. package/project-transform/src/bundled/sound-libs/lib.es2015.reflect.d.ts +144 -0
  23. package/project-transform/src/bundled/sound-libs/lib.es2015.symbol.d.ts +46 -0
  24. package/project-transform/src/bundled/sound-libs/lib.es2015.symbol.wellknown.d.ts +170 -0
  25. package/project-transform/src/bundled/sound-libs/lib.es2016.array.include.d.ts +116 -0
  26. package/project-transform/src/bundled/sound-libs/lib.es2016.d.ts +21 -0
  27. package/project-transform/src/bundled/sound-libs/lib.es2017.arraybuffer.d.ts +21 -0
  28. package/project-transform/src/bundled/sound-libs/lib.es2017.d.ts +26 -0
  29. package/project-transform/src/bundled/sound-libs/lib.es2017.date.d.ts +31 -0
  30. package/project-transform/src/bundled/sound-libs/lib.es2017.object.d.ts +49 -0
  31. package/project-transform/src/bundled/sound-libs/lib.es2017.string.d.ts +45 -0
  32. package/project-transform/src/bundled/sound-libs/lib.es2017.typedarrays.d.ts +53 -0
  33. package/project-transform/src/bundled/sound-libs/lib.es2018.asyncgenerator.d.ts +77 -0
  34. package/project-transform/src/bundled/sound-libs/lib.es2018.asynciterable.d.ts +57 -0
  35. package/project-transform/src/bundled/sound-libs/lib.es2018.d.ts +24 -0
  36. package/project-transform/src/bundled/sound-libs/lib.es2018.promise.d.ts +30 -0
  37. package/project-transform/src/bundled/sound-libs/lib.es2018.regexp.d.ts +37 -0
  38. package/project-transform/src/bundled/sound-libs/lib.es2019.array.d.ts +79 -0
  39. package/project-transform/src/bundled/sound-libs/lib.es2019.d.ts +24 -0
  40. package/project-transform/src/bundled/sound-libs/lib.es2019.object.d.ts +47 -0
  41. package/project-transform/src/bundled/sound-libs/lib.es2019.string.d.ts +37 -0
  42. package/project-transform/src/bundled/sound-libs/lib.es2019.symbol.d.ts +24 -0
  43. package/project-transform/src/bundled/sound-libs/lib.es2020.bigint.d.ts +765 -0
  44. package/project-transform/src/bundled/sound-libs/lib.es2020.d.ts +27 -0
  45. package/project-transform/src/bundled/sound-libs/lib.es2020.date.d.ts +42 -0
  46. package/project-transform/src/bundled/sound-libs/lib.es2020.number.d.ts +28 -0
  47. package/project-transform/src/bundled/sound-libs/lib.es2020.promise.d.ts +49 -0
  48. package/project-transform/src/bundled/sound-libs/lib.es2020.string.d.ts +44 -0
  49. package/project-transform/src/bundled/sound-libs/lib.es2020.symbol.wellknown.d.ts +41 -0
  50. package/project-transform/src/bundled/sound-libs/lib.es2021.d.ts +23 -0
  51. package/project-transform/src/bundled/sound-libs/lib.es2021.promise.d.ts +48 -0
  52. package/project-transform/src/bundled/sound-libs/lib.es2021.string.d.ts +33 -0
  53. package/project-transform/src/bundled/sound-libs/lib.es2021.weakref.d.ts +78 -0
  54. package/project-transform/src/bundled/sound-libs/lib.es2022.array.d.ts +121 -0
  55. package/project-transform/src/bundled/sound-libs/lib.es2022.d.ts +25 -0
  56. package/project-transform/src/bundled/sound-libs/lib.es2022.error.d.ts +75 -0
  57. package/project-transform/src/bundled/sound-libs/lib.es2022.object.d.ts +26 -0
  58. package/project-transform/src/bundled/sound-libs/lib.es2022.regexp.d.ts +39 -0
  59. package/project-transform/src/bundled/sound-libs/lib.es2022.string.d.ts +25 -0
  60. package/project-transform/src/bundled/sound-libs/lib.es2023.array.d.ts +924 -0
  61. package/project-transform/src/bundled/sound-libs/lib.es2023.collection.d.ts +21 -0
  62. package/project-transform/src/bundled/sound-libs/lib.es2023.d.ts +22 -0
  63. package/project-transform/src/bundled/sound-libs/lib.es2024.arraybuffer.d.ts +65 -0
  64. package/project-transform/src/bundled/sound-libs/lib.es2024.collection.d.ts +29 -0
  65. package/project-transform/src/bundled/sound-libs/lib.es2024.d.ts +26 -0
  66. package/project-transform/src/bundled/sound-libs/lib.es2024.object.d.ts +33 -0
  67. package/project-transform/src/bundled/sound-libs/lib.es2024.promise.d.ts +35 -0
  68. package/project-transform/src/bundled/sound-libs/lib.es2024.regexp.d.ts +25 -0
  69. package/project-transform/src/bundled/sound-libs/lib.es2024.string.d.ts +29 -0
  70. package/project-transform/src/bundled/sound-libs/lib.es5.d.ts +4924 -0
  71. package/project-transform/src/bundled/sound_stdlib.js +142 -0
  72. package/project-transform/src/bundled/sound_stdlib.ts +180 -0
  73. package/project-transform/src/checker/analyze_project.js +1361 -0
  74. package/project-transform/src/checker/analyze_project.ts +2246 -0
  75. package/project-transform/src/checker/diagnostics.js +112 -0
  76. package/project-transform/src/checker/diagnostics.ts +222 -0
  77. package/project-transform/src/checker/engine/context.js +235 -0
  78. package/project-transform/src/checker/engine/context.ts +340 -0
  79. package/project-transform/src/checker/engine/diagnostic_codes.js +72 -0
  80. package/project-transform/src/checker/engine/diagnostic_codes.ts +95 -0
  81. package/project-transform/src/checker/engine/facts.js +35 -0
  82. package/project-transform/src/checker/engine/facts.ts +48 -0
  83. package/project-transform/src/checker/engine/types.js +1 -0
  84. package/project-transform/src/checker/engine/types.ts +485 -0
  85. package/project-transform/src/checker/proof_escape_hatch_diagnostics.js +104 -0
  86. package/project-transform/src/checker/proof_escape_hatch_diagnostics.ts +173 -0
  87. package/project-transform/src/checker/rules/async_surface.js +231 -0
  88. package/project-transform/src/checker/rules/async_surface.ts +335 -0
  89. package/project-transform/src/checker/rules/class_lifecycle.js +798 -0
  90. package/project-transform/src/checker/rules/class_lifecycle.ts +1276 -0
  91. package/project-transform/src/checker/rules/directive_validation.js +571 -0
  92. package/project-transform/src/checker/rules/directive_validation.ts +938 -0
  93. package/project-transform/src/checker/rules/directives.js +23 -0
  94. package/project-transform/src/checker/rules/directives.ts +25 -0
  95. package/project-transform/src/checker/rules/flow.js +202 -0
  96. package/project-transform/src/checker/rules/flow.ts +333 -0
  97. package/project-transform/src/checker/rules/flow_facts.js +601 -0
  98. package/project-transform/src/checker/rules/flow_facts.ts +978 -0
  99. package/project-transform/src/checker/rules/flow_invalidation.js +1119 -0
  100. package/project-transform/src/checker/rules/flow_invalidation.ts +2150 -0
  101. package/project-transform/src/checker/rules/flow_shared.js +2822 -0
  102. package/project-transform/src/checker/rules/flow_shared.ts +4383 -0
  103. package/project-transform/src/checker/rules/foreign_boundary.js +120 -0
  104. package/project-transform/src/checker/rules/foreign_boundary.ts +196 -0
  105. package/project-transform/src/checker/rules/foreign_projection.js +279 -0
  106. package/project-transform/src/checker/rules/foreign_projection.ts +425 -0
  107. package/project-transform/src/checker/rules/generated_helpers.js +13 -0
  108. package/project-transform/src/checker/rules/generated_helpers.ts +18 -0
  109. package/project-transform/src/checker/rules/index.js +35 -0
  110. package/project-transform/src/checker/rules/index.ts +49 -0
  111. package/project-transform/src/checker/rules/namespace_object.js +845 -0
  112. package/project-transform/src/checker/rules/namespace_object.ts +1224 -0
  113. package/project-transform/src/checker/rules/non_ordinary_recovery.js +1328 -0
  114. package/project-transform/src/checker/rules/non_ordinary_recovery.ts +2391 -0
  115. package/project-transform/src/checker/rules/null_prototype.js +3 -0
  116. package/project-transform/src/checker/rules/null_prototype.ts +6 -0
  117. package/project-transform/src/checker/rules/overloads.js +181 -0
  118. package/project-transform/src/checker/rules/overloads.ts +317 -0
  119. package/project-transform/src/checker/rules/predicate_verification.js +691 -0
  120. package/project-transform/src/checker/rules/predicate_verification.ts +1088 -0
  121. package/project-transform/src/checker/rules/prototype_hardening.js +237 -0
  122. package/project-transform/src/checker/rules/prototype_hardening.ts +343 -0
  123. package/project-transform/src/checker/rules/receiver_discipline.js +263 -0
  124. package/project-transform/src/checker/rules/receiver_discipline.ts +356 -0
  125. package/project-transform/src/checker/rules/relations.js +6861 -0
  126. package/project-transform/src/checker/rules/relations.ts +12158 -0
  127. package/project-transform/src/checker/rules/resolved_builtins.js +274 -0
  128. package/project-transform/src/checker/rules/resolved_builtins.ts +438 -0
  129. package/project-transform/src/checker/rules/trust.js +217 -0
  130. package/project-transform/src/checker/rules/trust.ts +301 -0
  131. package/project-transform/src/checker/rules/type_guards.js +173 -0
  132. package/project-transform/src/checker/rules/type_guards.ts +257 -0
  133. package/project-transform/src/checker/rules/universal.js +17 -0
  134. package/project-transform/src/checker/rules/universal.ts +22 -0
  135. package/project-transform/src/checker/rules/unsafe_value_origin.js +80 -0
  136. package/project-transform/src/checker/rules/unsafe_value_origin.ts +125 -0
  137. package/project-transform/src/checker/rules/unsound_imports.js +218 -0
  138. package/project-transform/src/checker/rules/unsound_imports.ts +301 -0
  139. package/project-transform/src/checker/rules/unsound_syntax.js +1695 -0
  140. package/project-transform/src/checker/rules/unsound_syntax.ts +2540 -0
  141. package/project-transform/src/checker/rules/value_types.js +206 -0
  142. package/project-transform/src/checker/rules/value_types.ts +407 -0
  143. package/project-transform/src/checker/timing.js +43 -0
  144. package/project-transform/src/checker/timing.ts +78 -0
  145. package/project-transform/src/checker/unsupported_feature_messages.js +337 -0
  146. package/project-transform/src/checker/unsupported_feature_messages.ts +531 -0
  147. package/project-transform/src/cli.js +892 -0
  148. package/project-transform/src/cli.ts +1476 -0
  149. package/project-transform/src/compiler/compile_project.js +319 -0
  150. package/project-transform/src/compiler/compile_project.ts +508 -0
  151. package/project-transform/src/compiler/errors.js +10 -0
  152. package/project-transform/src/compiler/errors.ts +29 -0
  153. package/project-transform/src/compiler/ir.js +1 -0
  154. package/project-transform/src/compiler/ir.ts +1526 -0
  155. package/project-transform/src/compiler/lower.js +30550 -0
  156. package/project-transform/src/compiler/lower.ts +43645 -0
  157. package/project-transform/src/compiler/lower_arrays.js +140 -0
  158. package/project-transform/src/compiler/lower_arrays.ts +190 -0
  159. package/project-transform/src/compiler/lower_strings.js +121 -0
  160. package/project-transform/src/compiler/lower_strings.ts +198 -0
  161. package/project-transform/src/compiler/lower_tagged.js +329 -0
  162. package/project-transform/src/compiler/lower_tagged.ts +427 -0
  163. package/project-transform/src/compiler/lower_views.js +171 -0
  164. package/project-transform/src/compiler/lower_views.ts +251 -0
  165. package/project-transform/src/compiler/object_keys.js +25 -0
  166. package/project-transform/src/compiler/object_keys.ts +35 -0
  167. package/project-transform/src/compiler/runtime_ir.js +30 -0
  168. package/project-transform/src/compiler/runtime_ir.ts +727 -0
  169. package/project-transform/src/compiler/tagged_boundary.js +18 -0
  170. package/project-transform/src/compiler/tagged_boundary.ts +37 -0
  171. package/project-transform/src/compiler/toolchain.js +170 -0
  172. package/project-transform/src/compiler/toolchain.ts +229 -0
  173. package/project-transform/src/compiler/unicode_case_data.js +2102 -0
  174. package/project-transform/src/compiler/unicode_case_data.ts +2112 -0
  175. package/project-transform/src/compiler/wasm_js_host_runtime.js +656 -0
  176. package/project-transform/src/compiler/wasm_js_host_runtime.ts +762 -0
  177. package/project-transform/src/compiler/wat_arrays.js +3132 -0
  178. package/project-transform/src/compiler/wat_arrays.ts +3768 -0
  179. package/project-transform/src/compiler/wat_emitter.js +17952 -0
  180. package/project-transform/src/compiler/wat_emitter.ts +22812 -0
  181. package/project-transform/src/compiler/wat_strings.js +129 -0
  182. package/project-transform/src/compiler/wat_strings.ts +187 -0
  183. package/project-transform/src/compiler/wat_tagged.js +548 -0
  184. package/project-transform/src/compiler/wat_tagged.ts +674 -0
  185. package/project-transform/src/compiler_generator_runner.js +153 -0
  186. package/project-transform/src/compiler_generator_runner.ts +171 -0
  187. package/project-transform/src/compiler_object_test_helpers.js +69 -0
  188. package/project-transform/src/compiler_object_test_helpers.ts +96 -0
  189. package/project-transform/src/compiler_promise_runner.js +2116 -0
  190. package/project-transform/src/compiler_promise_runner.ts +2184 -0
  191. package/project-transform/src/compiler_test_helpers.js +854 -0
  192. package/project-transform/src/compiler_test_helpers.ts +1087 -0
  193. package/project-transform/src/config.js +568 -0
  194. package/project-transform/src/config.ts +892 -0
  195. package/project-transform/src/diagnostic_metadata.js +67 -0
  196. package/project-transform/src/diagnostic_metadata.ts +99 -0
  197. package/project-transform/src/diagnostic_reference.js +1368 -0
  198. package/project-transform/src/diagnostic_reference.ts +1523 -0
  199. package/project-transform/src/editor_diagnostics_worker.js +176 -0
  200. package/project-transform/src/editor_diagnostics_worker.ts +250 -0
  201. package/project-transform/src/editor_projection.js +224 -0
  202. package/project-transform/src/editor_projection.ts +421 -0
  203. package/project-transform/src/frontend/builtin_expanded_program_test_cleanup.js +47 -0
  204. package/project-transform/src/frontend/builtin_expanded_program_test_cleanup.ts +72 -0
  205. package/project-transform/src/frontend/builtin_macro_support.js +842 -0
  206. package/project-transform/src/frontend/builtin_macro_support.ts +1386 -0
  207. package/project-transform/src/frontend/builtin_macros.js +409 -0
  208. package/project-transform/src/frontend/builtin_macros.ts +542 -0
  209. package/project-transform/src/frontend/component_poc_runtime.js +279 -0
  210. package/project-transform/src/frontend/component_poc_runtime.ts +372 -0
  211. package/project-transform/src/frontend/css_macro.js +148 -0
  212. package/project-transform/src/frontend/css_macro.ts +222 -0
  213. package/project-transform/src/frontend/derive_macros.js +2072 -0
  214. package/project-transform/src/frontend/derive_macros.ts +3188 -0
  215. package/project-transform/src/frontend/embedded_fragment_support.js +106 -0
  216. package/project-transform/src/frontend/embedded_fragment_support.ts +172 -0
  217. package/project-transform/src/frontend/error_normalization.js +403 -0
  218. package/project-transform/src/frontend/error_normalization.ts +832 -0
  219. package/project-transform/src/frontend/error_stdlib_support.js +1 -0
  220. package/project-transform/src/frontend/error_stdlib_support.ts +6 -0
  221. package/project-transform/src/frontend/expand_project.js +169 -0
  222. package/project-transform/src/frontend/expand_project.ts +248 -0
  223. package/project-transform/src/frontend/format_soundscript.js +297 -0
  224. package/project-transform/src/frontend/format_soundscript.ts +582 -0
  225. package/project-transform/src/frontend/graphql_macro.js +174 -0
  226. package/project-transform/src/frontend/graphql_macro.ts +253 -0
  227. package/project-transform/src/frontend/hash_context.js +83 -0
  228. package/project-transform/src/frontend/hash_context.ts +113 -0
  229. package/project-transform/src/frontend/hkt_macro.js +448 -0
  230. package/project-transform/src/frontend/hkt_macro.ts +897 -0
  231. package/project-transform/src/frontend/import_binding_usage.js +190 -0
  232. package/project-transform/src/frontend/import_binding_usage.ts +277 -0
  233. package/project-transform/src/frontend/macro_advanced_backend_adapter.js +58 -0
  234. package/project-transform/src/frontend/macro_advanced_backend_adapter.ts +123 -0
  235. package/project-transform/src/frontend/macro_advanced_context.js +826 -0
  236. package/project-transform/src/frontend/macro_advanced_context.ts +1102 -0
  237. package/project-transform/src/frontend/macro_advanced_output.js +21 -0
  238. package/project-transform/src/frontend/macro_advanced_output.ts +41 -0
  239. package/project-transform/src/frontend/macro_api.js +353 -0
  240. package/project-transform/src/frontend/macro_api.ts +1722 -0
  241. package/project-transform/src/frontend/macro_api_internal.js +35 -0
  242. package/project-transform/src/frontend/macro_api_internal.ts +80 -0
  243. package/project-transform/src/frontend/macro_api_module_support.js +39 -0
  244. package/project-transform/src/frontend/macro_api_module_support.ts +65 -0
  245. package/project-transform/src/frontend/macro_backend_adapter.js +272 -0
  246. package/project-transform/src/frontend/macro_backend_adapter.ts +420 -0
  247. package/project-transform/src/frontend/macro_context.js +816 -0
  248. package/project-transform/src/frontend/macro_context.ts +1105 -0
  249. package/project-transform/src/frontend/macro_debug.js +99 -0
  250. package/project-transform/src/frontend/macro_debug.ts +157 -0
  251. package/project-transform/src/frontend/macro_definition_support.js +28 -0
  252. package/project-transform/src/frontend/macro_definition_support.ts +73 -0
  253. package/project-transform/src/frontend/macro_errors.js +40 -0
  254. package/project-transform/src/frontend/macro_errors.ts +70 -0
  255. package/project-transform/src/frontend/macro_expander.js +919 -0
  256. package/project-transform/src/frontend/macro_expander.ts +1611 -0
  257. package/project-transform/src/frontend/macro_factory_support.js +176 -0
  258. package/project-transform/src/frontend/macro_factory_support.ts +263 -0
  259. package/project-transform/src/frontend/macro_host_ast_internal.js +64 -0
  260. package/project-transform/src/frontend/macro_host_ast_internal.ts +109 -0
  261. package/project-transform/src/frontend/macro_index.js +27 -0
  262. package/project-transform/src/frontend/macro_index.ts +50 -0
  263. package/project-transform/src/frontend/macro_loader.js +281 -0
  264. package/project-transform/src/frontend/macro_loader.ts +506 -0
  265. package/project-transform/src/frontend/macro_operand_semantics.js +838 -0
  266. package/project-transform/src/frontend/macro_operand_semantics.ts +1489 -0
  267. package/project-transform/src/frontend/macro_output.js +54 -0
  268. package/project-transform/src/frontend/macro_output.ts +123 -0
  269. package/project-transform/src/frontend/macro_parser.js +611 -0
  270. package/project-transform/src/frontend/macro_parser.ts +832 -0
  271. package/project-transform/src/frontend/macro_resolver.js +69 -0
  272. package/project-transform/src/frontend/macro_resolver.ts +125 -0
  273. package/project-transform/src/frontend/macro_rewrite.js +285 -0
  274. package/project-transform/src/frontend/macro_rewrite.ts +442 -0
  275. package/project-transform/src/frontend/macro_runtime_support.js +232 -0
  276. package/project-transform/src/frontend/macro_runtime_support.ts +324 -0
  277. package/project-transform/src/frontend/macro_scanner.js +393 -0
  278. package/project-transform/src/frontend/macro_scanner.ts +455 -0
  279. package/project-transform/src/frontend/macro_semantic_backend_adapter.js +87 -0
  280. package/project-transform/src/frontend/macro_semantic_backend_adapter.ts +166 -0
  281. package/project-transform/src/frontend/macro_semantic_context.js +5 -0
  282. package/project-transform/src/frontend/macro_semantic_context.ts +12 -0
  283. package/project-transform/src/frontend/macro_semantic_output.js +24 -0
  284. package/project-transform/src/frontend/macro_semantic_output.ts +47 -0
  285. package/project-transform/src/frontend/macro_semantic_types.js +1 -0
  286. package/project-transform/src/frontend/macro_semantic_types.ts +98 -0
  287. package/project-transform/src/frontend/macro_semantics.js +1172 -0
  288. package/project-transform/src/frontend/macro_semantics.ts +1502 -0
  289. package/project-transform/src/frontend/macro_site_kind_support.js +164 -0
  290. package/project-transform/src/frontend/macro_site_kind_support.ts +255 -0
  291. package/project-transform/src/frontend/macro_syntax_internal.js +1950 -0
  292. package/project-transform/src/frontend/macro_syntax_internal.ts +3338 -0
  293. package/project-transform/src/frontend/macro_templates.js +57 -0
  294. package/project-transform/src/frontend/macro_templates.ts +143 -0
  295. package/project-transform/src/frontend/macro_test_helpers.js +82 -0
  296. package/project-transform/src/frontend/macro_test_helpers.ts +136 -0
  297. package/project-transform/src/frontend/macro_types.js +1 -0
  298. package/project-transform/src/frontend/macro_types.ts +103 -0
  299. package/project-transform/src/frontend/macro_vm.js +39 -0
  300. package/project-transform/src/frontend/macro_vm.ts +113 -0
  301. package/project-transform/src/frontend/match_macro.js +885 -0
  302. package/project-transform/src/frontend/match_macro.ts +1220 -0
  303. package/project-transform/src/frontend/numeric_normalization.js +824 -0
  304. package/project-transform/src/frontend/numeric_normalization.ts +1380 -0
  305. package/project-transform/src/frontend/numeric_prelude.js +278 -0
  306. package/project-transform/src/frontend/numeric_prelude.ts +370 -0
  307. package/project-transform/src/frontend/project_frontend.js +2396 -0
  308. package/project-transform/src/frontend/project_frontend.ts +3776 -0
  309. package/project-transform/src/frontend/project_macro_support.js +1401 -0
  310. package/project-transform/src/frontend/project_macro_support.ts +2137 -0
  311. package/project-transform/src/frontend/sql_macro.js +175 -0
  312. package/project-transform/src/frontend/sql_macro.ts +254 -0
  313. package/project-transform/src/frontend/sql_stdlib_support.js +1 -0
  314. package/project-transform/src/frontend/sql_stdlib_support.ts +6 -0
  315. package/project-transform/src/frontend/std_package_support.js +228 -0
  316. package/project-transform/src/frontend/std_package_support.ts +400 -0
  317. package/project-transform/src/frontend/value_normalization.js +306 -0
  318. package/project-transform/src/frontend/value_normalization.ts +599 -0
  319. package/project-transform/src/lsp/project_service.js +4771 -0
  320. package/project-transform/src/lsp/project_service.ts +7580 -0
  321. package/project-transform/src/lsp/protocol.js +9 -0
  322. package/project-transform/src/lsp/protocol.ts +38 -0
  323. package/project-transform/src/lsp/server.js +355 -0
  324. package/project-transform/src/lsp/server.ts +671 -0
  325. package/project-transform/src/lsp/session.js +49 -0
  326. package/project-transform/src/lsp/session.ts +48 -0
  327. package/project-transform/src/lsp/timing.js +43 -0
  328. package/project-transform/src/lsp/timing.ts +76 -0
  329. package/project-transform/src/lsp/transport.js +205 -0
  330. package/project-transform/src/lsp/transport.ts +253 -0
  331. package/project-transform/src/lsp_main.js +5 -0
  332. package/project-transform/src/lsp_main.ts +7 -0
  333. package/project-transform/src/macros.d.ts +1 -0
  334. package/project-transform/src/macros.js +1 -0
  335. package/project-transform/src/macros.ts +1 -0
  336. package/project-transform/src/main.js +24 -0
  337. package/project-transform/src/main.ts +28 -0
  338. package/project-transform/src/platform/host.js +264 -0
  339. package/project-transform/src/platform/host.ts +343 -0
  340. package/project-transform/src/platform/path.js +8 -0
  341. package/project-transform/src/platform/path.ts +20 -0
  342. package/project-transform/src/public_macro_api/macro_api.d.ts +1054 -0
  343. package/project-transform/src/public_macro_api/macro_semantic_types.d.ts +66 -0
  344. package/project-transform/src/public_macro_api/macro_types.d.ts +70 -0
  345. package/project-transform/src/run_program.js +14 -0
  346. package/project-transform/src/run_program.ts +33 -0
  347. package/project-transform/src/runtime/materialize.js +371 -0
  348. package/project-transform/src/runtime/materialize.ts +502 -0
  349. package/project-transform/src/runtime/on_demand.js +203 -0
  350. package/project-transform/src/runtime/on_demand.ts +305 -0
  351. package/project-transform/src/runtime/source_maps.js +205 -0
  352. package/project-transform/src/runtime/source_maps.ts +297 -0
  353. package/project-transform/src/runtime/transform.js +148 -0
  354. package/project-transform/src/runtime/transform.ts +295 -0
  355. package/project-transform/src/service/types.js +1 -0
  356. package/project-transform/src/service/types.ts +22 -0
  357. package/project-transform/src/soundscript_packages.js +477 -0
  358. package/project-transform/src/soundscript_packages.ts +754 -0
  359. package/project-transform/src/soundscript_runtime_specifiers.js +88 -0
  360. package/project-transform/src/soundscript_runtime_specifiers.ts +96 -0
  361. package/project-transform/src/stdlib/async.d.ts +81 -0
  362. package/project-transform/src/stdlib/async.js +213 -0
  363. package/project-transform/src/stdlib/async.ts +315 -0
  364. package/project-transform/src/stdlib/codec.d.ts +32 -0
  365. package/project-transform/src/stdlib/codec.js +30 -0
  366. package/project-transform/src/stdlib/codec.ts +76 -0
  367. package/project-transform/src/stdlib/compare.d.ts +28 -0
  368. package/project-transform/src/stdlib/compare.js +115 -0
  369. package/project-transform/src/stdlib/compare.ts +151 -0
  370. package/project-transform/src/stdlib/css.d.ts +16 -0
  371. package/project-transform/src/stdlib/css.js +9 -0
  372. package/project-transform/src/stdlib/css.ts +28 -0
  373. package/project-transform/src/stdlib/debug.d.ts +2 -0
  374. package/project-transform/src/stdlib/debug.js +9 -0
  375. package/project-transform/src/stdlib/debug.ts +10 -0
  376. package/project-transform/src/stdlib/decode.d.ts +86 -0
  377. package/project-transform/src/stdlib/decode.js +254 -0
  378. package/project-transform/src/stdlib/decode.ts +390 -0
  379. package/project-transform/src/stdlib/derive.d.ts +6 -0
  380. package/project-transform/src/stdlib/derive.js +7 -0
  381. package/project-transform/src/stdlib/derive.ts +7 -0
  382. package/project-transform/src/stdlib/encode.d.ts +100 -0
  383. package/project-transform/src/stdlib/encode.js +130 -0
  384. package/project-transform/src/stdlib/encode.ts +259 -0
  385. package/project-transform/src/stdlib/failures.d.ts +23 -0
  386. package/project-transform/src/stdlib/failures.js +41 -0
  387. package/project-transform/src/stdlib/failures.ts +64 -0
  388. package/project-transform/src/stdlib/fetch.d.ts +67 -0
  389. package/project-transform/src/stdlib/fetch.js +5 -0
  390. package/project-transform/src/stdlib/fetch.ts +11 -0
  391. package/project-transform/src/stdlib/graphql.d.ts +16 -0
  392. package/project-transform/src/stdlib/graphql.js +9 -0
  393. package/project-transform/src/stdlib/graphql.ts +28 -0
  394. package/project-transform/src/stdlib/hash.d.ts +34 -0
  395. package/project-transform/src/stdlib/hash.js +110 -0
  396. package/project-transform/src/stdlib/hash.ts +188 -0
  397. package/project-transform/src/stdlib/hkt.d.ts +40 -0
  398. package/project-transform/src/stdlib/hkt.js +3 -0
  399. package/project-transform/src/stdlib/hkt.ts +41 -0
  400. package/project-transform/src/stdlib/index.d.ts +9 -0
  401. package/project-transform/src/stdlib/index.js +15 -0
  402. package/project-transform/src/stdlib/index.ts +23 -0
  403. package/project-transform/src/stdlib/json.d.ts +125 -0
  404. package/project-transform/src/stdlib/json.js +764 -0
  405. package/project-transform/src/stdlib/json.ts +1034 -0
  406. package/project-transform/src/stdlib/match.d.ts +11 -0
  407. package/project-transform/src/stdlib/match.js +13 -0
  408. package/project-transform/src/stdlib/match.ts +26 -0
  409. package/project-transform/src/stdlib/numerics.d.ts +523 -0
  410. package/project-transform/src/stdlib/numerics.js +1356 -0
  411. package/project-transform/src/stdlib/numerics.ts +1937 -0
  412. package/project-transform/src/stdlib/random.d.ts +19 -0
  413. package/project-transform/src/stdlib/random.js +3 -0
  414. package/project-transform/src/stdlib/random.ts +5 -0
  415. package/project-transform/src/stdlib/result.d.ts +68 -0
  416. package/project-transform/src/stdlib/result.js +139 -0
  417. package/project-transform/src/stdlib/result.ts +248 -0
  418. package/project-transform/src/stdlib/sql.d.ts +22 -0
  419. package/project-transform/src/stdlib/sql.js +23 -0
  420. package/project-transform/src/stdlib/sql.ts +53 -0
  421. package/project-transform/src/stdlib/text.d.ts +24 -0
  422. package/project-transform/src/stdlib/text.js +3 -0
  423. package/project-transform/src/stdlib/text.ts +4 -0
  424. package/project-transform/src/stdlib/thunk.d.ts +2 -0
  425. package/project-transform/src/stdlib/thunk.js +9 -0
  426. package/project-transform/src/stdlib/thunk.ts +15 -0
  427. package/project-transform/src/stdlib/typeclasses.d.ts +57 -0
  428. package/project-transform/src/stdlib/typeclasses.js +78 -0
  429. package/project-transform/src/stdlib/typeclasses.ts +173 -0
  430. package/project-transform/src/stdlib/url.d.ts +37 -0
  431. package/project-transform/src/stdlib/url.js +3 -0
  432. package/project-transform/src/stdlib/url.ts +4 -0
  433. package/project-transform/src/stdlib/value.d.ts +9 -0
  434. package/project-transform/src/stdlib/value.js +104 -0
  435. package/project-transform/src/stdlib/value.ts +133 -0
  436. package/project-transform/src/test_installed_stdlib.js +147 -0
  437. package/project-transform/src/test_installed_stdlib.ts +245 -0
  438. package/project-transform/src/test_macro_package_fixture.js +50 -0
  439. package/project-transform/src/test_macro_package_fixture.ts +68 -0
  440. package/project-transform/src/value_deep_safe.js +191 -0
  441. package/project-transform/src/value_deep_safe.ts +273 -0
@@ -0,0 +1,2246 @@
1
+ import ts from 'typescript';
2
+ import { dirname, isAbsolute, join } from '../platform/path.ts';
3
+
4
+ import { createSoundStdlibCompilerHost } from '../bundled/sound_stdlib.ts';
5
+ import {
6
+ type BuiltinExpandedTsDiagnosticProgram,
7
+ createBuiltinExpandedProgram,
8
+ } from '../frontend/builtin_macro_support.ts';
9
+ import type {
10
+ MacroModuleCacheStats,
11
+ ProjectMacroEnvironment,
12
+ } from '../frontend/project_macro_support.ts';
13
+ import {
14
+ sourceTextLooksLikeMacroModule,
15
+ usesLegacyDefineMacroAuthoring,
16
+ } from '../frontend/macro_factory_support.ts';
17
+ import {
18
+ clearPreparedCompilerHostReuseState,
19
+ emitProjectedDeclarations,
20
+ getLineAndCharacterOfPosition,
21
+ getPositionOfLineAndCharacter,
22
+ isProjectedSoundscriptDeclarationFile,
23
+ isSoundscriptSourceFile,
24
+ mapProgramEnclosingRangeToSource,
25
+ mapProgramPositionToSource,
26
+ type PreparedCompilerHostReuseState,
27
+ type PreparedProgram,
28
+ type PreparedSourceFile,
29
+ toProjectedDeclarationFileName,
30
+ toProjectedDeclarationSourceFileName,
31
+ toSourceFileName,
32
+ } from '../frontend/project_frontend.ts';
33
+ import { collectSoundscriptRootNames, loadConfig } from '../config.ts';
34
+ import {
35
+ findNearestPackageJsonPath,
36
+ getSoundScriptPackageInfoForResolvedModule,
37
+ resolveSoundScriptAwareModule,
38
+ } from '../soundscript_packages.ts';
39
+ import {
40
+ hasErrorDiagnostics,
41
+ remapDiagnosticFilePaths,
42
+ toMergedDiagnostic,
43
+ } from './diagnostics.ts';
44
+ import { SOUND_DIAGNOSTIC_CODES } from './engine/diagnostic_codes.ts';
45
+ import { createAnalysisContext } from './engine/context.ts';
46
+ import { runSoundAnalysis } from './rules/index.ts';
47
+ import {
48
+ runSourceSupplementalPolicyAnalysis,
49
+ runUniversalPolicyAnalysis,
50
+ } from './rules/universal.ts';
51
+ import { measureCheckerTiming } from './timing.ts';
52
+
53
+ import type { AnalyzeProjectOptions, AnalyzeProjectResult } from '../service/types.ts';
54
+ import type {
55
+ DiagnosticRelatedInformation,
56
+ MergedDiagnostic,
57
+ SoundDiagnostic,
58
+ } from './diagnostics.ts';
59
+ import type { AnalysisContext } from './engine/types.ts';
60
+
61
+ export interface PreparedAnalysisView {
62
+ analysisContext: AnalysisContext;
63
+ analysisPreparedProgram: PreparedProgram;
64
+ diagnosticPreparedFiles: ReadonlyMap<string, PreparedSourceFile>;
65
+ frontendDiagnostics: readonly MergedDiagnostic[];
66
+ macroEnvironment: ProjectMacroEnvironment;
67
+ macroCacheStats: MacroModuleCacheStats;
68
+ preparedProgram: PreparedProgram;
69
+ program: ts.Program;
70
+ runSound: boolean;
71
+ tsDiagnosticPrograms: readonly BuiltinExpandedTsDiagnosticProgram[];
72
+ universalPolicyScope: 'full' | 'sourceSupplemental';
73
+ }
74
+
75
+ export interface PreparedAnalysisProject {
76
+ analyzeOptions: AnalyzeProjectOptions;
77
+ configReuseSignature: string;
78
+ configuredSoundscriptRootNames: readonly string[];
79
+ localProjectedDeclarationOverrides: ReadonlyMap<string, string> | undefined;
80
+ packageSourcePolicyContentSignature: string;
81
+ packageSourcePolicyCompilerHostReuseState: PreparedCompilerHostReuseState | undefined;
82
+ packageSourcePolicyView: PreparedAnalysisView | null;
83
+ soundscriptRootContentSignature: string;
84
+ soundscriptRootDiscoverySignature: string;
85
+ stsCompilerHostReuseState: PreparedCompilerHostReuseState | undefined;
86
+ soundscriptFileOverridesSignature: string;
87
+ soundscriptRootNames: readonly string[];
88
+ stsView: PreparedAnalysisView | null;
89
+ tsCompilerHostReuseState: PreparedCompilerHostReuseState | undefined;
90
+ tsView: PreparedAnalysisView | null;
91
+ }
92
+
93
+ interface AnalyzedProgramResult {
94
+ frontendDiagnostics: readonly MergedDiagnostic[];
95
+ soundDiagnostics: readonly SoundDiagnostic[];
96
+ tsDiagnostics: readonly MergedDiagnostic[];
97
+ }
98
+
99
+ const fileScopedAnalysisContextCache = new WeakMap<
100
+ PreparedAnalysisView,
101
+ Map<string, AnalysisContext | null>
102
+ >();
103
+ const IGNORED_GENERATED_TOP_LEVEL_IMPORT_SPECIFIERS = new Set(['sts:prelude']);
104
+
105
+ interface PrepareProjectAnalysisOptions {
106
+ deferTypescriptView?: boolean;
107
+ }
108
+
109
+ function combineRootNames(
110
+ rootNames: readonly string[],
111
+ additionalRootNames: readonly string[] = [],
112
+ ): string[] {
113
+ return [...new Set([...rootNames, ...additionalRootNames])];
114
+ }
115
+
116
+ function rootNamesEqual(left: readonly string[], right: readonly string[]): boolean {
117
+ if (left.length !== right.length) {
118
+ return false;
119
+ }
120
+
121
+ for (let index = 0; index < left.length; index += 1) {
122
+ if (left[index] !== right[index]) {
123
+ return false;
124
+ }
125
+ }
126
+
127
+ return true;
128
+ }
129
+
130
+ function createFileOverrideSignature(
131
+ fileOverrides: ReadonlyMap<string, string> | undefined,
132
+ includeFileName: (fileName: string) => boolean,
133
+ ): string {
134
+ if (!fileOverrides || fileOverrides.size === 0) {
135
+ return '';
136
+ }
137
+
138
+ return [...fileOverrides.entries()]
139
+ .filter(([fileName]) => includeFileName(fileName))
140
+ .sort(([left], [right]) => left.localeCompare(right))
141
+ .map(([fileName, text]) => `${fileName}:${text.length}:${text}`)
142
+ .join('|');
143
+ }
144
+
145
+ function createSoundscriptRootDiscoverySignature(
146
+ projectPath: string,
147
+ loadedConfig: ReturnType<typeof loadConfig>,
148
+ ): string {
149
+ const basePath = dirname(projectPath);
150
+ const rawConfig = loadedConfig.commandLine.raw as {
151
+ exclude?: readonly string[];
152
+ files?: readonly string[];
153
+ include?: readonly string[];
154
+ } | undefined;
155
+ const explicitFiles = (rawConfig?.files ?? [])
156
+ .map((fileName) => isAbsolute(fileName) ? fileName : join(basePath, fileName))
157
+ .map((fileName) => ts.sys.resolvePath(fileName))
158
+ .filter(isSoundscriptSourceFile)
159
+ .sort()
160
+ .join('\u0000');
161
+ const includePatterns = rawConfig?.include
162
+ ? [...rawConfig.include]
163
+ : rawConfig?.files
164
+ ? []
165
+ : ['**/*'];
166
+ const excludePatterns = rawConfig?.exclude
167
+ ? [...rawConfig.exclude]
168
+ : ['node_modules', 'bower_components', 'jspm_packages', '.git'];
169
+
170
+ return [
171
+ basePath,
172
+ explicitFiles,
173
+ includePatterns.join('\u0001'),
174
+ excludePatterns.join('\u0001'),
175
+ ].join('\u0002');
176
+ }
177
+
178
+ function stableConfigSignature(value: unknown): string {
179
+ return JSON.stringify(value, (_key, currentValue) => {
180
+ if (
181
+ currentValue !== null &&
182
+ typeof currentValue === 'object' &&
183
+ !Array.isArray(currentValue)
184
+ ) {
185
+ return Object.fromEntries(
186
+ Object.entries(currentValue as Record<string, unknown>).sort(([left], [right]) =>
187
+ left.localeCompare(right)
188
+ ),
189
+ );
190
+ }
191
+
192
+ return currentValue;
193
+ });
194
+ }
195
+
196
+ function createProjectConfigReuseSignature(
197
+ projectPath: string,
198
+ loadedConfig: ReturnType<typeof loadConfig>,
199
+ ): string {
200
+ return [
201
+ projectPath,
202
+ stableConfigSignature(loadedConfig.commandLine.raw),
203
+ stableConfigSignature(loadedConfig.commandLine.options),
204
+ stableConfigSignature(loadedConfig.commandLine.projectReferences ?? []),
205
+ stableConfigSignature(loadedConfig.runtime),
206
+ ].join('\u0003');
207
+ }
208
+
209
+ function createModuleResolutionHostWithOverrides(
210
+ fileOverrides: ReadonlyMap<string, string> | undefined,
211
+ ): ts.ModuleResolutionHost {
212
+ const normalizedOverrides = fileOverrides
213
+ ? new Map(
214
+ [...fileOverrides.entries()].map(([fileName, text]) => [ts.sys.resolvePath(fileName), text]),
215
+ )
216
+ : new Map<string, string>();
217
+
218
+ return {
219
+ directoryExists(directoryName) {
220
+ const normalizedDirectoryName = ts.sys.resolvePath(directoryName);
221
+ if (
222
+ [...normalizedOverrides.keys()].some((fileName) =>
223
+ fileName === normalizedDirectoryName ||
224
+ fileName.startsWith(`${normalizedDirectoryName}/`) ||
225
+ fileName.startsWith(`${normalizedDirectoryName}\\`)
226
+ )
227
+ ) {
228
+ return true;
229
+ }
230
+ return ts.sys.directoryExists?.(directoryName) ?? false;
231
+ },
232
+ fileExists(fileName) {
233
+ return normalizedOverrides.has(ts.sys.resolvePath(fileName)) || ts.sys.fileExists(fileName);
234
+ },
235
+ getCurrentDirectory: ts.sys.getCurrentDirectory,
236
+ getDirectories: ts.sys.getDirectories,
237
+ readFile(fileName) {
238
+ return normalizedOverrides.get(ts.sys.resolvePath(fileName)) ?? ts.sys.readFile(fileName);
239
+ },
240
+ realpath: ts.sys.realpath,
241
+ useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
242
+ };
243
+ }
244
+
245
+ function isRelativeOrAbsoluteModuleSpecifier(moduleSpecifier: string): boolean {
246
+ return moduleSpecifier.startsWith('.') ||
247
+ moduleSpecifier.startsWith('/') ||
248
+ /^[A-Za-z]:[/\\]/u.test(moduleSpecifier);
249
+ }
250
+
251
+ function resolveRelativeSoundscriptDependency(
252
+ containingFileName: string,
253
+ moduleSpecifier: string,
254
+ host: ts.ModuleResolutionHost,
255
+ ): string | undefined {
256
+ if (!isRelativeOrAbsoluteModuleSpecifier(moduleSpecifier)) {
257
+ return undefined;
258
+ }
259
+
260
+ const explicitNonSoundscriptExtensionPattern = /\.(?:[cm]?[jt]sx?|[cm]?js)$/u;
261
+ if (explicitNonSoundscriptExtensionPattern.test(moduleSpecifier)) {
262
+ return undefined;
263
+ }
264
+
265
+ const candidateBase = ts.sys.resolvePath(
266
+ isAbsolute(moduleSpecifier)
267
+ ? moduleSpecifier
268
+ : join(dirname(containingFileName), moduleSpecifier),
269
+ );
270
+ const candidates = moduleSpecifier.endsWith('.sts')
271
+ ? [candidateBase]
272
+ : [`${candidateBase}.sts`, join(candidateBase, 'index.sts')];
273
+
274
+ for (const candidate of candidates) {
275
+ if (host.fileExists(candidate)) {
276
+ return candidate;
277
+ }
278
+ }
279
+
280
+ return undefined;
281
+ }
282
+
283
+ function collectReachableSoundscriptDependencyFiles(
284
+ rootNames: readonly string[],
285
+ compilerOptions: ts.CompilerOptions,
286
+ fileOverrides: ReadonlyMap<string, string> | undefined,
287
+ ): readonly string[] {
288
+ const host = createModuleResolutionHostWithOverrides(fileOverrides);
289
+ const visited = new Set<string>();
290
+ const reachableFiles: string[] = [];
291
+
292
+ function visit(fileName: string): void {
293
+ const sourceFileName = ts.sys.resolvePath(toSourceFileName(fileName));
294
+ if (!isSoundscriptSourceFile(sourceFileName) || visited.has(sourceFileName)) {
295
+ return;
296
+ }
297
+
298
+ visited.add(sourceFileName);
299
+ reachableFiles.push(sourceFileName);
300
+
301
+ const sourceText = host.readFile(sourceFileName);
302
+ if (!sourceText) {
303
+ return;
304
+ }
305
+
306
+ for (const importedFile of ts.preProcessFile(sourceText, true, true).importedFiles) {
307
+ const resolvedDependency = resolveRelativeSoundscriptDependency(
308
+ sourceFileName,
309
+ importedFile.fileName,
310
+ host,
311
+ );
312
+ if (resolvedDependency) {
313
+ visit(resolvedDependency);
314
+ continue;
315
+ }
316
+
317
+ const resolvedModule = resolveSoundScriptAwareModule(
318
+ importedFile.fileName,
319
+ sourceFileName,
320
+ compilerOptions,
321
+ host,
322
+ );
323
+ if (resolvedModule) {
324
+ visit(resolvedModule.resolvedFileName);
325
+ }
326
+ }
327
+ }
328
+
329
+ for (const rootName of rootNames) {
330
+ visit(rootName);
331
+ }
332
+
333
+ reachableFiles.sort();
334
+ return reachableFiles;
335
+ }
336
+
337
+ function createSoundscriptRootContentSignature(
338
+ rootNames: readonly string[],
339
+ compilerOptions: ts.CompilerOptions,
340
+ fileOverrides: ReadonlyMap<string, string> | undefined,
341
+ ): string {
342
+ const host = createModuleResolutionHostWithOverrides(fileOverrides);
343
+ return collectReachableSoundscriptDependencyFiles(rootNames, compilerOptions, fileOverrides)
344
+ .map((fileName) => {
345
+ const text = host.readFile(fileName) ?? '';
346
+ return `${fileName}\u0001${text.length}\u0001${text}`;
347
+ })
348
+ .join('\u0002');
349
+ }
350
+
351
+ function getConfigFileParsingDiagnostics(
352
+ diagnostics: readonly ts.Diagnostic[],
353
+ additionalRootNames: readonly string[] = [],
354
+ ): readonly ts.Diagnostic[] {
355
+ if (additionalRootNames.length === 0) {
356
+ return diagnostics;
357
+ }
358
+
359
+ return diagnostics.filter((diagnostic) => diagnostic.code !== 18003);
360
+ }
361
+
362
+ function remapDiagnostics<T extends MergedDiagnostic>(diagnostics: readonly T[]): T[] {
363
+ return diagnostics.map((diagnostic) => remapDiagnosticFilePaths(diagnostic, toSourceFileName));
364
+ }
365
+
366
+ function remapPreparedSoundDiagnosticRange<
367
+ T extends MergedDiagnostic | DiagnosticRelatedInformation,
368
+ >(
369
+ diagnostic: T,
370
+ preparedFile: PreparedSourceFile | undefined,
371
+ ): T {
372
+ if (
373
+ !preparedFile ||
374
+ !diagnostic.filePath ||
375
+ diagnostic.line === undefined ||
376
+ diagnostic.column === undefined
377
+ ) {
378
+ return diagnostic;
379
+ }
380
+
381
+ const programStart = getPositionOfLineAndCharacter(
382
+ preparedFile.rewrittenText,
383
+ diagnostic.line - 1,
384
+ diagnostic.column - 1,
385
+ );
386
+ const programEnd = diagnostic.endLine !== undefined && diagnostic.endColumn !== undefined
387
+ ? getPositionOfLineAndCharacter(
388
+ preparedFile.rewrittenText,
389
+ diagnostic.endLine - 1,
390
+ diagnostic.endColumn - 1,
391
+ )
392
+ : programStart;
393
+ const mappedRange = mapProgramEnclosingRangeToSource(preparedFile, programStart, programEnd);
394
+ const mappedStart = getLineAndCharacterOfPosition(preparedFile.originalText, mappedRange.start);
395
+ const mappedEnd = getLineAndCharacterOfPosition(preparedFile.originalText, mappedRange.end);
396
+
397
+ return {
398
+ ...diagnostic,
399
+ line: mappedStart.line + 1,
400
+ column: mappedStart.character + 1,
401
+ endLine: mappedEnd.line + 1,
402
+ endColumn: mappedEnd.character + 1,
403
+ } as T;
404
+ }
405
+
406
+ function remapSoundDiagnostics(
407
+ diagnostics: readonly SoundDiagnostic[],
408
+ diagnosticPreparedFiles: ReadonlyMap<string, PreparedSourceFile>,
409
+ ): SoundDiagnostic[] {
410
+ return diagnostics.map((diagnostic) => {
411
+ const preparedFile = diagnostic.filePath
412
+ ? diagnosticPreparedFiles.get(toSourceFileName(diagnostic.filePath))
413
+ : undefined;
414
+ const remapped = remapPreparedSoundDiagnosticRange(diagnostic, preparedFile);
415
+ return {
416
+ ...remapped,
417
+ relatedInformation: remapped.relatedInformation?.map((relatedInformation) => {
418
+ const relatedPreparedFile = relatedInformation.filePath
419
+ ? diagnosticPreparedFiles.get(toSourceFileName(relatedInformation.filePath))
420
+ : undefined;
421
+ return remapPreparedSoundDiagnosticRange(relatedInformation, relatedPreparedFile);
422
+ }),
423
+ };
424
+ });
425
+ }
426
+
427
+ function remapMergedDiagnosticRange<T extends MergedDiagnostic | DiagnosticRelatedInformation>(
428
+ mergedDiagnostic: T,
429
+ diagnostic: ts.Diagnostic | ts.DiagnosticRelatedInformation,
430
+ preparedFile: PreparedSourceFile | undefined,
431
+ ): T {
432
+ if (!preparedFile || !diagnostic.file || diagnostic.start === undefined) {
433
+ return mergedDiagnostic;
434
+ }
435
+
436
+ const diagnosticLength = diagnostic.length ?? 0;
437
+ const diagnosticText = diagnostic.file.text.slice(
438
+ diagnostic.start,
439
+ diagnostic.start + diagnosticLength,
440
+ );
441
+ const rawLineStartsAt = diagnostic.file.text.lastIndexOf('\n', diagnostic.start - 1) + 1;
442
+ const rawNextNewline = diagnostic.file.text.indexOf('\n', diagnostic.start);
443
+ const rawLineEndsAt = rawNextNewline === -1 ? diagnostic.file.text.length : rawNextNewline;
444
+ const rawLineText = diagnostic.file.text.slice(rawLineStartsAt, rawLineEndsAt);
445
+ const mappedRange = mapProgramEnclosingRangeToSource(
446
+ preparedFile,
447
+ diagnostic.start,
448
+ diagnostic.start + diagnosticLength,
449
+ );
450
+ const refinedRange = refineMappedRangeToMatchingText(
451
+ preparedFile.originalText,
452
+ mappedRange,
453
+ diagnosticText,
454
+ rawLineText,
455
+ );
456
+ const mappedStart = getLineAndCharacterOfPosition(preparedFile.originalText, refinedRange.start);
457
+ const mappedEnd = getLineAndCharacterOfPosition(preparedFile.originalText, refinedRange.end);
458
+
459
+ return {
460
+ ...mergedDiagnostic,
461
+ line: mappedStart.line + 1,
462
+ column: mappedStart.character + 1,
463
+ endLine: mappedEnd.line + 1,
464
+ endColumn: mappedEnd.character + 1,
465
+ } as T;
466
+ }
467
+
468
+ function refineMappedRangeToMatchingText(
469
+ originalText: string,
470
+ mappedRange: { intersectsReplacement: boolean; start: number; end: number },
471
+ diagnosticText: string,
472
+ rawLineText: string,
473
+ ): { intersectsReplacement: boolean; start: number; end: number } {
474
+ if (
475
+ mappedRange.intersectsReplacement ||
476
+ diagnosticText.length === 0 ||
477
+ diagnosticText.includes('\n') ||
478
+ diagnosticText.includes('\r')
479
+ ) {
480
+ return mappedRange;
481
+ }
482
+
483
+ if (originalText.slice(mappedRange.start, mappedRange.end) === diagnosticText) {
484
+ return mappedRange;
485
+ }
486
+
487
+ const lineStart = originalText.lastIndexOf('\n', mappedRange.start - 1) + 1;
488
+ const nextNewline = originalText.indexOf('\n', mappedRange.start);
489
+ const lineEnd = nextNewline === -1 ? originalText.length : nextNewline;
490
+ const lineText = originalText.slice(lineStart, lineEnd);
491
+ if (lineText !== rawLineText) {
492
+ return mappedRange;
493
+ }
494
+ let bestStart: number | undefined;
495
+ let searchIndex = lineText.indexOf(diagnosticText);
496
+
497
+ while (searchIndex !== -1) {
498
+ const candidateStart = lineStart + searchIndex;
499
+ if (
500
+ bestStart === undefined ||
501
+ Math.abs(candidateStart - mappedRange.start) < Math.abs(bestStart - mappedRange.start)
502
+ ) {
503
+ bestStart = candidateStart;
504
+ }
505
+ searchIndex = lineText.indexOf(diagnosticText, searchIndex + 1);
506
+ }
507
+
508
+ if (bestStart === undefined) {
509
+ return mappedRange;
510
+ }
511
+
512
+ return {
513
+ intersectsReplacement: false,
514
+ start: bestStart,
515
+ end: bestStart + diagnosticText.length,
516
+ };
517
+ }
518
+
519
+ function toMappedMergedDiagnostic(
520
+ diagnostic: ts.Diagnostic,
521
+ diagnosticPreparedFiles: ReadonlyMap<string, PreparedSourceFile>,
522
+ ): MergedDiagnostic {
523
+ const mergedDiagnostic = toMergedDiagnostic(diagnostic);
524
+ const preparedFile = diagnostic.file
525
+ ? diagnosticPreparedFiles.get(toSourceFileName(diagnostic.file.fileName))
526
+ : undefined;
527
+ const remapped = remapMergedDiagnosticRange(mergedDiagnostic, diagnostic, preparedFile);
528
+ if (!mergedDiagnostic.relatedInformation || !diagnostic.relatedInformation) {
529
+ return remapped as MergedDiagnostic;
530
+ }
531
+
532
+ return {
533
+ ...(remapped as MergedDiagnostic),
534
+ relatedInformation: mergedDiagnostic.relatedInformation.map((relatedInformation, index) => {
535
+ const relatedDiagnostic = diagnostic.relatedInformation?.[index];
536
+ const relatedPreparedFile = relatedDiagnostic?.file
537
+ ? diagnosticPreparedFiles.get(toSourceFileName(relatedDiagnostic.file.fileName))
538
+ : undefined;
539
+ return remapMergedDiagnosticRange(
540
+ relatedInformation,
541
+ relatedDiagnostic ?? diagnostic,
542
+ relatedPreparedFile,
543
+ ) as DiagnosticRelatedInformation;
544
+ }),
545
+ };
546
+ }
547
+
548
+ function mergeProjectedDeclarationOverrides(
549
+ first: ReadonlyMap<string, string> | undefined,
550
+ second: ReadonlyMap<string, string> | undefined,
551
+ ): ReadonlyMap<string, string> | undefined {
552
+ if (!first && !second) {
553
+ return undefined;
554
+ }
555
+
556
+ if (!first) {
557
+ return second;
558
+ }
559
+ if (!second) {
560
+ return first;
561
+ }
562
+
563
+ const merged = new Map(first);
564
+ for (const [fileName, text] of second) {
565
+ merged.set(fileName, text);
566
+ }
567
+
568
+ return merged;
569
+ }
570
+
571
+ function filterProjectedDeclarationOverridesToRootNames(
572
+ projectedDeclarationOverrides: ReadonlyMap<string, string> | undefined,
573
+ rootNames: readonly string[],
574
+ ): ReadonlyMap<string, string> | undefined {
575
+ if (!projectedDeclarationOverrides) {
576
+ return undefined;
577
+ }
578
+
579
+ const normalizedRootNames = new Set(rootNames.map((rootName) => ts.sys.resolvePath(rootName)));
580
+ const filtered = new Map<string, string>();
581
+
582
+ for (const [fileName, text] of projectedDeclarationOverrides) {
583
+ if (normalizedRootNames.has(ts.sys.resolvePath(fileName))) {
584
+ filtered.set(fileName, text);
585
+ }
586
+ }
587
+
588
+ return filtered;
589
+ }
590
+
591
+ function projectedDeclarationOverridesDiffer(
592
+ first: ReadonlyMap<string, string> | undefined,
593
+ second: ReadonlyMap<string, string> | undefined,
594
+ ): boolean {
595
+ if (!first && !second) {
596
+ return false;
597
+ }
598
+
599
+ if (!first || !second) {
600
+ return true;
601
+ }
602
+
603
+ if (first.size !== second.size) {
604
+ return true;
605
+ }
606
+
607
+ for (const [fileName, text] of first) {
608
+ if (second.get(fileName) !== text) {
609
+ return true;
610
+ }
611
+ }
612
+
613
+ return false;
614
+ }
615
+
616
+ function collectProjectedDeclarationCandidateRootNames(
617
+ program: ts.Program,
618
+ existingOverrides: ReadonlyMap<string, string> | undefined,
619
+ projectPackageJsonPath: string | undefined,
620
+ ): readonly string[] {
621
+ const rootNames = new Set<string>();
622
+
623
+ for (const sourceFile of program.getSourceFiles()) {
624
+ const sourceFileName = toSourceFileName(sourceFile.fileName);
625
+ if (!isSoundscriptSourceFile(sourceFileName)) {
626
+ continue;
627
+ }
628
+ if (isInstalledSoundStdlibSourceFileName(sourceFileName)) {
629
+ continue;
630
+ }
631
+ if (existingOverrides?.has(sourceFileName)) {
632
+ continue;
633
+ }
634
+ if (!isSupplementalPackageSourceCandidate(sourceFileName, projectPackageJsonPath)) {
635
+ continue;
636
+ }
637
+
638
+ rootNames.add(sourceFileName);
639
+ }
640
+
641
+ return [...rootNames].sort();
642
+ }
643
+
644
+ function hasNonRootProjectedDeclarationCandidates(
645
+ program: ts.Program,
646
+ soundscriptRootNameSet: ReadonlySet<string>,
647
+ projectPackageJsonPath: string | undefined,
648
+ ): boolean {
649
+ return program.getSourceFiles().some((sourceFile) => {
650
+ const sourceFileName = toSourceFileName(sourceFile.fileName);
651
+ return isSoundscriptSourceFile(sourceFileName) &&
652
+ !isInstalledSoundStdlibSourceFileName(sourceFileName) &&
653
+ isSupplementalPackageSourceCandidate(sourceFileName, projectPackageJsonPath) &&
654
+ !soundscriptRootNameSet.has(ts.sys.resolvePath(sourceFileName));
655
+ });
656
+ }
657
+
658
+ function collectProjectedDeclarationCandidateRootNamesFromPrograms(
659
+ programs: readonly (ts.Program | null | undefined)[],
660
+ existingOverrides: ReadonlyMap<string, string> | undefined,
661
+ projectPackageJsonPath: string | undefined,
662
+ ): readonly string[] {
663
+ const rootNames = new Set<string>();
664
+
665
+ for (const program of programs) {
666
+ if (!program) {
667
+ continue;
668
+ }
669
+
670
+ for (
671
+ const rootName of collectProjectedDeclarationCandidateRootNames(
672
+ program,
673
+ existingOverrides,
674
+ projectPackageJsonPath,
675
+ )
676
+ ) {
677
+ rootNames.add(rootName);
678
+ }
679
+ }
680
+
681
+ return [...rootNames].sort();
682
+ }
683
+
684
+ function isInstalledSoundStdlibSourceFileName(fileName: string): boolean {
685
+ const normalizedFileName = toSourceFileName(fileName).replaceAll('\\', '/');
686
+ return normalizedFileName.includes('/node_modules/@soundscript/soundscript/soundscript/') &&
687
+ normalizedFileName.endsWith('.sts');
688
+ }
689
+
690
+ function isNodeModulesPath(fileName: string): boolean {
691
+ const normalizedFileName = toSourceFileName(fileName).replaceAll('\\', '/');
692
+ return normalizedFileName.includes('/node_modules/');
693
+ }
694
+
695
+ function shouldAnalyzeSoundscriptSourceFile(
696
+ sourceFile: ts.SourceFile,
697
+ preparedProgram: PreparedProgram,
698
+ ): boolean {
699
+ const sourceFileName = toSourceFileName(sourceFile.fileName);
700
+ return isSoundscriptSourceFile(sourceFileName) &&
701
+ !isInstalledSoundStdlibSourceFileName(sourceFileName) &&
702
+ !isMacroAuthoringSourceFile(sourceFile, preparedProgram);
703
+ }
704
+
705
+ function normalizeOptionalResolvedPath(path: string | undefined): string | undefined {
706
+ return path ? ts.sys.resolvePath(path) : undefined;
707
+ }
708
+
709
+ function isSupplementalPackageSourceCandidate(
710
+ fileName: string,
711
+ projectPackageJsonPath: string | undefined,
712
+ ): boolean {
713
+ if (!isSoundscriptSourceFile(fileName) || isInstalledSoundStdlibSourceFileName(fileName)) {
714
+ return false;
715
+ }
716
+
717
+ const packageInfo = getSoundScriptPackageInfoForResolvedModule(fileName, ts.sys);
718
+ if (!packageInfo) {
719
+ return false;
720
+ }
721
+
722
+ const normalizedProjectPackageJsonPath = normalizeOptionalResolvedPath(projectPackageJsonPath);
723
+ const normalizedFilePackageJsonPath = normalizeOptionalResolvedPath(packageInfo.packageJsonPath);
724
+ return normalizedProjectPackageJsonPath === undefined ||
725
+ normalizedFilePackageJsonPath !== normalizedProjectPackageJsonPath;
726
+ }
727
+
728
+ function shouldAnalyzeProjectSoundscriptSourceFile(
729
+ sourceFile: ts.SourceFile,
730
+ preparedProgram: PreparedProgram,
731
+ projectPackageJsonPath: string | undefined,
732
+ ): boolean {
733
+ const sourceFileName = toSourceFileName(sourceFile.fileName);
734
+ return shouldAnalyzeSoundscriptSourceFile(sourceFile, preparedProgram) &&
735
+ !isSupplementalPackageSourceCandidate(sourceFileName, projectPackageJsonPath);
736
+ }
737
+
738
+ function shouldAnalyzeTypescriptViewSourceFile(sourceFile: ts.SourceFile): boolean {
739
+ return !isSoundscriptSourceFile(toSourceFileName(sourceFile.fileName));
740
+ }
741
+
742
+ function isIgnorableGeneratedTopLevelStatement(statement: ts.Statement): boolean {
743
+ return ts.isImportDeclaration(statement) &&
744
+ ts.isStringLiteralLike(statement.moduleSpecifier) &&
745
+ IGNORED_GENERATED_TOP_LEVEL_IMPORT_SPECIFIERS.has(statement.moduleSpecifier.text);
746
+ }
747
+
748
+ function hasGeneratedTopLevelStatements(
749
+ sourceFile: ts.SourceFile,
750
+ isGeneratedNode: (node: ts.Node) => boolean,
751
+ ): boolean {
752
+ return sourceFile.statements.some((statement) =>
753
+ isGeneratedNode(statement) && !isIgnorableGeneratedTopLevelStatement(statement)
754
+ );
755
+ }
756
+
757
+ function createOriginalSourceFileForPreparedSource(
758
+ fileName: string,
759
+ preparedSource: PreparedSourceFile,
760
+ ): ts.SourceFile {
761
+ return ts.createSourceFile(
762
+ fileName,
763
+ preparedSource.originalText,
764
+ ts.ScriptTarget.Latest,
765
+ true,
766
+ /\.(?:[cm]?tsx|jsx|sts)$/iu.test(fileName) ? ts.ScriptKind.TSX : ts.ScriptKind.TS,
767
+ );
768
+ }
769
+
770
+ function findInnermostNodeContainingPosition(
771
+ root: ts.Node,
772
+ position: number,
773
+ ): ts.Node | undefined {
774
+ if (position < root.getFullStart() || position >= root.getEnd()) {
775
+ return undefined;
776
+ }
777
+
778
+ let best: ts.Node = root;
779
+ const visit = (node: ts.Node): void => {
780
+ if (position < node.getFullStart() || position >= node.getEnd()) {
781
+ return;
782
+ }
783
+ best = node;
784
+ ts.forEachChild(node, visit);
785
+ };
786
+ visit(root);
787
+ return best;
788
+ }
789
+
790
+ function isTopLevelMacroReplacement(
791
+ originalSourceFile: ts.SourceFile,
792
+ preparedSource: PreparedSourceFile,
793
+ replacementId: number,
794
+ ): boolean {
795
+ const macroInvocation = preparedSource.rewriteResult.macrosById.get(replacementId);
796
+ if (!macroInvocation) {
797
+ return false;
798
+ }
799
+
800
+ const anchorPosition = macroInvocation.declarationSpan?.start ?? macroInvocation.span.start;
801
+ const anchorNode = findInnermostNodeContainingPosition(originalSourceFile, anchorPosition);
802
+ if (!anchorNode || ts.isSourceFile(anchorNode)) {
803
+ return false;
804
+ }
805
+
806
+ let current: ts.Node | undefined = anchorNode;
807
+ while (current?.parent && !ts.isSourceFile(current.parent)) {
808
+ if (
809
+ ts.isBlock(current.parent) ||
810
+ ts.isFunctionLike(current.parent) ||
811
+ ts.isModuleBlock(current.parent)
812
+ ) {
813
+ return false;
814
+ }
815
+ current = current.parent;
816
+ }
817
+
818
+ return current !== undefined && ts.isSourceFile(current.parent);
819
+ }
820
+
821
+ function hasTopLevelMacroReplacements(
822
+ fileName: string,
823
+ preparedSource: PreparedSourceFile | undefined,
824
+ ): boolean {
825
+ if (!preparedSource || preparedSource.rewriteResult.replacements.length === 0) {
826
+ return false;
827
+ }
828
+
829
+ const originalSourceFile = createOriginalSourceFileForPreparedSource(fileName, preparedSource);
830
+ return preparedSource.rewriteResult.replacements.some((replacement) =>
831
+ isTopLevelMacroReplacement(originalSourceFile, preparedSource, replacement.id)
832
+ );
833
+ }
834
+
835
+ function prepareAnalysisView(
836
+ options: AnalyzeProjectOptions,
837
+ loadedConfig: ReturnType<typeof loadConfig>,
838
+ rootNames: readonly string[],
839
+ baseHost: ts.CompilerHost,
840
+ configFileParsingDiagnostics: readonly ts.Diagnostic[],
841
+ includeSourceFile:
842
+ | ((sourceFile: ts.SourceFile, preparedProgram: PreparedProgram) => boolean)
843
+ | undefined,
844
+ projectedDeclarationOverrides: ReadonlyMap<string, string> | undefined,
845
+ runSound: boolean,
846
+ universalPolicyScope: 'full' | 'sourceSupplemental' = 'full',
847
+ reusableCompilerHostState?: PreparedCompilerHostReuseState,
848
+ oldProgram?: ts.Program,
849
+ ): PreparedAnalysisView | null {
850
+ if (rootNames.length === 0) {
851
+ return null;
852
+ }
853
+
854
+ const expandedProgram = createBuiltinExpandedProgram({
855
+ allowSupplementalDiagnosticPrograms: true,
856
+ baseHost,
857
+ configFileParsingDiagnostics,
858
+ fileOverrides: options.fileOverrides ?? new Map(),
859
+ oldProgram,
860
+ options: loadedConfig.commandLine.options,
861
+ projectReferences: loadedConfig.commandLine.projectReferences,
862
+ projectedDeclarationOverrides,
863
+ runtime: loadedConfig.runtime,
864
+ reusableCompilerHostState,
865
+ rootNames,
866
+ });
867
+ const program = expandedProgram.program;
868
+ const isGeneratedNode = createPreparedProgramGeneratedNodeDetector(
869
+ expandedProgram.analysisPreparedProgram,
870
+ );
871
+ const sourceFileHasTopLevelMacroReplacements = (sourceFile: ts.SourceFile): boolean => {
872
+ const sourceFileName = expandedProgram.analysisPreparedProgram.toSourceFileName(
873
+ sourceFile.fileName,
874
+ );
875
+ const preparedSource = expandedProgram.preparedProgram.preparedHost.getPreparedSourceFile(
876
+ sourceFileName,
877
+ );
878
+ return hasTopLevelMacroReplacements(sourceFileName, preparedSource);
879
+ };
880
+ const analysisContext = createAnalysisContext({
881
+ includeSourceFile: includeSourceFile
882
+ ? (sourceFile) =>
883
+ !sourceFileHasTopLevelMacroReplacements(sourceFile) &&
884
+ !hasGeneratedTopLevelStatements(sourceFile, isGeneratedNode) &&
885
+ includeSourceFile(sourceFile, expandedProgram.analysisPreparedProgram)
886
+ : (sourceFile) =>
887
+ !sourceFileHasTopLevelMacroReplacements(sourceFile) &&
888
+ !hasGeneratedTopLevelStatements(sourceFile, isGeneratedNode),
889
+ isGeneratedNode,
890
+ program,
891
+ runtime: loadedConfig.runtime,
892
+ workingDirectory: options.workingDirectory,
893
+ });
894
+
895
+ return {
896
+ analysisContext,
897
+ analysisPreparedProgram: expandedProgram.analysisPreparedProgram,
898
+ diagnosticPreparedFiles: expandedProgram.diagnosticPreparedFiles,
899
+ frontendDiagnostics: remapDiagnostics(expandedProgram.frontendDiagnostics()),
900
+ macroEnvironment: expandedProgram.macroEnvironment,
901
+ macroCacheStats: expandedProgram.macroEnvironment.cacheStats(),
902
+ preparedProgram: expandedProgram.preparedProgram,
903
+ program,
904
+ runSound,
905
+ tsDiagnosticPrograms: expandedProgram.tsDiagnosticPrograms,
906
+ universalPolicyScope,
907
+ };
908
+ }
909
+
910
+ function analyzePreparedView(
911
+ preparedView: PreparedAnalysisView | null,
912
+ ): AnalyzedProgramResult {
913
+ if (!preparedView) {
914
+ return {
915
+ frontendDiagnostics: [],
916
+ tsDiagnostics: [],
917
+ soundDiagnostics: [],
918
+ };
919
+ }
920
+
921
+ const frontendDiagnostics = [...preparedView.frontendDiagnostics];
922
+ const tsDiagnostics = collectPreparedViewTsDiagnostics(
923
+ preparedView,
924
+ frontendDiagnostics,
925
+ );
926
+ const hasFrontendErrors = hasErrorDiagnostics(frontendDiagnostics);
927
+ const hasTsErrors = hasErrorDiagnostics(tsDiagnostics);
928
+ const universalDiagnostics = hasFrontendErrors ? [] : collectPreparedViewUniversalDiagnostics(
929
+ preparedView,
930
+ preparedView.analysisContext,
931
+ );
932
+ const soundDiagnostics = hasFrontendErrors ? [] : collectPreparedViewSoundDiagnostics(
933
+ preparedView,
934
+ preparedView.analysisContext,
935
+ );
936
+
937
+ return {
938
+ frontendDiagnostics,
939
+ tsDiagnostics,
940
+ soundDiagnostics: hasTsErrors
941
+ ? retainSoundDiagnosticsAlongsideTsErrors([...universalDiagnostics, ...soundDiagnostics])
942
+ : [...universalDiagnostics, ...soundDiagnostics],
943
+ };
944
+ }
945
+
946
+ function emitProjectedDeclarationsFailClosed(
947
+ preparedView: PreparedAnalysisView | null,
948
+ rootNames?: readonly string[],
949
+ ): ReadonlyMap<string, string> | undefined {
950
+ if (!preparedView) {
951
+ return undefined;
952
+ }
953
+
954
+ try {
955
+ return emitProjectedDeclarations(preparedView.analysisPreparedProgram, rootNames);
956
+ } catch (error) {
957
+ const analyzedView = analyzePreparedView(preparedView);
958
+ if (
959
+ hasErrorDiagnostics([
960
+ ...analyzedView.frontendDiagnostics,
961
+ ...analyzedView.tsDiagnostics,
962
+ ...analyzedView.soundDiagnostics,
963
+ ])
964
+ ) {
965
+ return undefined;
966
+ }
967
+
968
+ throw error;
969
+ }
970
+ }
971
+
972
+ function analyzePreparedViewForFile(
973
+ preparedView: PreparedAnalysisView | null,
974
+ filePath: string,
975
+ ): AnalyzedProgramResult {
976
+ if (!preparedView) {
977
+ return {
978
+ frontendDiagnostics: [],
979
+ tsDiagnostics: [],
980
+ soundDiagnostics: [],
981
+ };
982
+ }
983
+
984
+ const frontendDiagnosticPaths = collectPreparedViewFrontendDiagnosticPaths(
985
+ preparedView,
986
+ filePath,
987
+ );
988
+ const frontendDiagnostics = preparedView.frontendDiagnostics.filter((diagnostic) =>
989
+ matchesPreparedAnalysisAnyFilePath(diagnostic.filePath, frontendDiagnosticPaths)
990
+ );
991
+ const tsDiagnostics = collectPreparedViewTsDiagnostics(
992
+ preparedView,
993
+ frontendDiagnostics,
994
+ filePath,
995
+ true,
996
+ );
997
+ const hasFrontendErrors = hasErrorDiagnostics(frontendDiagnostics);
998
+ const hasTsErrors = hasErrorDiagnostics(tsDiagnostics);
999
+ const fileScopedAnalysisContext = getFileScopedAnalysisContext(preparedView, filePath);
1000
+ const universalDiagnostics = !fileScopedAnalysisContext || hasFrontendErrors
1001
+ ? []
1002
+ : filterAnalyzedDiagnosticsForFile(
1003
+ collectPreparedViewUniversalDiagnostics(
1004
+ preparedView,
1005
+ fileScopedAnalysisContext,
1006
+ filePath,
1007
+ ),
1008
+ filePath,
1009
+ );
1010
+ const soundDiagnostics = !fileScopedAnalysisContext ||
1011
+ hasFrontendErrors
1012
+ ? []
1013
+ : filterAnalyzedDiagnosticsForFile(
1014
+ collectPreparedViewSoundDiagnostics(
1015
+ preparedView,
1016
+ fileScopedAnalysisContext,
1017
+ filePath,
1018
+ ),
1019
+ filePath,
1020
+ );
1021
+
1022
+ return {
1023
+ frontendDiagnostics,
1024
+ tsDiagnostics,
1025
+ soundDiagnostics: hasTsErrors
1026
+ ? retainSoundDiagnosticsAlongsideTsErrors([...universalDiagnostics, ...soundDiagnostics])
1027
+ : [...universalDiagnostics, ...soundDiagnostics],
1028
+ };
1029
+ }
1030
+
1031
+ function analyzePreparedViewForDiagnosticPaths(
1032
+ preparedView: PreparedAnalysisView | null,
1033
+ diagnosticPaths: readonly string[],
1034
+ ): AnalyzedProgramResult {
1035
+ if (!preparedView || diagnosticPaths.length === 0) {
1036
+ return {
1037
+ frontendDiagnostics: [],
1038
+ tsDiagnostics: [],
1039
+ soundDiagnostics: [],
1040
+ };
1041
+ }
1042
+
1043
+ const frontendDiagnostics = preparedView.frontendDiagnostics.filter((diagnostic) =>
1044
+ matchesPreparedAnalysisAnyFilePath(diagnostic.filePath, diagnosticPaths)
1045
+ );
1046
+ const tsDiagnostics = hasErrorDiagnostics(frontendDiagnostics)
1047
+ ? []
1048
+ : collectPreparedViewTsDiagnostics(preparedView, frontendDiagnostics).filter((diagnostic) =>
1049
+ matchesPreparedAnalysisAnyFilePath(diagnostic.filePath, diagnosticPaths)
1050
+ );
1051
+ const hasFrontendErrors = hasErrorDiagnostics(frontendDiagnostics);
1052
+ const hasTsErrors = hasErrorDiagnostics(tsDiagnostics);
1053
+ const universalDiagnostics = hasFrontendErrors
1054
+ ? []
1055
+ : collectPreparedViewUniversalDiagnostics(preparedView, preparedView.analysisContext).filter(
1056
+ (diagnostic) => matchesPreparedAnalysisAnyFilePath(diagnostic.filePath, diagnosticPaths),
1057
+ );
1058
+ const soundDiagnostics = hasFrontendErrors
1059
+ ? []
1060
+ : collectPreparedViewSoundDiagnostics(preparedView, preparedView.analysisContext).filter(
1061
+ (diagnostic) => matchesPreparedAnalysisAnyFilePath(diagnostic.filePath, diagnosticPaths),
1062
+ );
1063
+
1064
+ return {
1065
+ frontendDiagnostics,
1066
+ tsDiagnostics,
1067
+ soundDiagnostics: hasTsErrors
1068
+ ? retainSoundDiagnosticsAlongsideTsErrors([...universalDiagnostics, ...soundDiagnostics])
1069
+ : [...universalDiagnostics, ...soundDiagnostics],
1070
+ };
1071
+ }
1072
+
1073
+ function retainSoundDiagnosticsAlongsideTsErrors(
1074
+ diagnostics: readonly MergedDiagnostic[],
1075
+ ): readonly SoundDiagnostic[] {
1076
+ return diagnostics.filter((diagnostic): diagnostic is SoundDiagnostic =>
1077
+ diagnostic.source === 'sound' &&
1078
+ (diagnostic.code === SOUND_DIAGNOSTIC_CODES.constructionLifecycleViolation ||
1079
+ diagnostic.code === SOUND_DIAGNOSTIC_CODES.fieldReadBeforeInitialization)
1080
+ );
1081
+ }
1082
+
1083
+ function matchesPreparedAnalysisAnyFilePath(
1084
+ candidateFilePath: string | undefined,
1085
+ expectedFilePaths: readonly string[],
1086
+ ): boolean {
1087
+ return expectedFilePaths.some((expectedFilePath) =>
1088
+ matchesPreparedAnalysisFilePath(candidateFilePath, expectedFilePath)
1089
+ );
1090
+ }
1091
+
1092
+ function collectPreparedViewFrontendDiagnosticPaths(
1093
+ preparedView: PreparedAnalysisView,
1094
+ filePath: string,
1095
+ ): readonly string[] {
1096
+ const diagnosticPaths = new Set<string>();
1097
+ const addDiagnosticPath = (candidateFilePath: string): void => {
1098
+ for (const variant of collectPreparedAnalysisFilePathCandidates(candidateFilePath)) {
1099
+ diagnosticPaths.add(variant);
1100
+ }
1101
+ if (isSoundscriptSourceFile(candidateFilePath)) {
1102
+ for (
1103
+ const variant of collectPreparedAnalysisFilePathCandidates(
1104
+ toProjectedDeclarationFileName(candidateFilePath),
1105
+ )
1106
+ ) {
1107
+ diagnosticPaths.add(variant);
1108
+ }
1109
+ }
1110
+ };
1111
+
1112
+ addDiagnosticPath(filePath);
1113
+
1114
+ const traversalRoots: Array<{
1115
+ readonly key: string;
1116
+ readonly program: ts.Program;
1117
+ readonly sourceFile: ts.SourceFile;
1118
+ }> = [];
1119
+ const addTraversalRoot = (
1120
+ key: string,
1121
+ program: ts.Program,
1122
+ sourceFile: ts.SourceFile | null,
1123
+ ): void => {
1124
+ if (!sourceFile || traversalRoots.some((root) => root.key === key)) {
1125
+ return;
1126
+ }
1127
+ traversalRoots.push({ key, program, sourceFile });
1128
+ };
1129
+
1130
+ const sourceFileMatch = getPreparedViewSourceFileMatch(preparedView, filePath);
1131
+ addTraversalRoot('prepared', preparedView.program, sourceFileMatch?.sourceFile ?? null);
1132
+
1133
+ const tsDiagnosticProgramMatch = getPreparedViewTsDiagnosticProgramMatch(preparedView, filePath);
1134
+ addTraversalRoot(
1135
+ `ts:${tsDiagnosticProgramMatch?.diagnosticProgram.filePaths?.join(',') ?? 'all'}`,
1136
+ tsDiagnosticProgramMatch?.diagnosticProgram.program ?? preparedView.program,
1137
+ tsDiagnosticProgramMatch?.sourceFile ?? null,
1138
+ );
1139
+
1140
+ if (traversalRoots.length === 0) {
1141
+ return [...diagnosticPaths];
1142
+ }
1143
+
1144
+ const visitedSourceFiles = new Set<string>();
1145
+ const getTraversalSourceFile = (
1146
+ program: ts.Program,
1147
+ candidateFilePath: string,
1148
+ ): ts.SourceFile | null => {
1149
+ for (const candidate of collectPreparedAnalysisFilePathCandidates(candidateFilePath)) {
1150
+ const sourceFile = program.getSourceFile(
1151
+ preparedView.preparedProgram.toProgramFileName(candidate),
1152
+ );
1153
+ if (sourceFile) {
1154
+ return sourceFile;
1155
+ }
1156
+
1157
+ if (isSoundscriptSourceFile(candidate)) {
1158
+ const projectedCandidate = toProjectedDeclarationFileName(candidate);
1159
+ const projectedSourceFile = program.getSourceFile(
1160
+ preparedView.preparedProgram.toProgramFileName(projectedCandidate),
1161
+ );
1162
+ if (projectedSourceFile) {
1163
+ return projectedSourceFile;
1164
+ }
1165
+ }
1166
+ }
1167
+
1168
+ return null;
1169
+ };
1170
+
1171
+ const visit = (programKey: string, program: ts.Program, sourceFile: ts.SourceFile): void => {
1172
+ const sourceFilePath = preparedView.preparedProgram.toSourceFileName(sourceFile.fileName);
1173
+ const visitKey = `${programKey}:${sourceFilePath}`;
1174
+ if (visitedSourceFiles.has(visitKey)) {
1175
+ return;
1176
+ }
1177
+ visitedSourceFiles.add(visitKey);
1178
+ addDiagnosticPath(sourceFilePath);
1179
+
1180
+ for (const moduleSpecifier of getStaticSourceFileModuleSpecifiers(sourceFile)) {
1181
+ const resolvedModule = resolveSoundScriptAwareModule(
1182
+ moduleSpecifier,
1183
+ sourceFilePath,
1184
+ preparedView.preparedProgram.options,
1185
+ preparedView.preparedProgram.preparedHost.host,
1186
+ );
1187
+ if (!resolvedModule) {
1188
+ continue;
1189
+ }
1190
+
1191
+ const resolvedSourcePath = preparedView.preparedProgram.toSourceFileName(
1192
+ resolvedModule.resolvedFileName,
1193
+ );
1194
+ if (
1195
+ !isSoundscriptSourceFile(resolvedSourcePath) &&
1196
+ !isProjectedSoundscriptDeclarationFile(resolvedSourcePath)
1197
+ ) {
1198
+ continue;
1199
+ }
1200
+
1201
+ addDiagnosticPath(resolvedSourcePath);
1202
+ const dependencySourceFile = getTraversalSourceFile(program, resolvedSourcePath);
1203
+ if (dependencySourceFile) {
1204
+ visit(programKey, program, dependencySourceFile);
1205
+ }
1206
+ }
1207
+ };
1208
+
1209
+ for (const traversalRoot of traversalRoots) {
1210
+ visit(traversalRoot.key, traversalRoot.program, traversalRoot.sourceFile);
1211
+ }
1212
+ return [...diagnosticPaths];
1213
+ }
1214
+
1215
+ function getStaticSourceFileModuleSpecifiers(sourceFile: ts.SourceFile): readonly string[] {
1216
+ const moduleSpecifiers: string[] = [];
1217
+
1218
+ for (const statement of sourceFile.statements) {
1219
+ if (
1220
+ (ts.isImportDeclaration(statement) || ts.isExportDeclaration(statement)) &&
1221
+ statement.moduleSpecifier &&
1222
+ ts.isStringLiteral(statement.moduleSpecifier)
1223
+ ) {
1224
+ moduleSpecifiers.push(statement.moduleSpecifier.text);
1225
+ continue;
1226
+ }
1227
+
1228
+ if (
1229
+ ts.isImportEqualsDeclaration(statement) &&
1230
+ ts.isExternalModuleReference(statement.moduleReference) &&
1231
+ ts.isStringLiteral(statement.moduleReference.expression)
1232
+ ) {
1233
+ moduleSpecifiers.push(statement.moduleReference.expression.text);
1234
+ }
1235
+ }
1236
+
1237
+ return moduleSpecifiers;
1238
+ }
1239
+
1240
+ function getPreparedViewTsDiagnosticProgramMatch(
1241
+ preparedView: PreparedAnalysisView,
1242
+ filePath: string,
1243
+ ): {
1244
+ readonly diagnosticProgram: BuiltinExpandedTsDiagnosticProgram;
1245
+ readonly matchedFilePath: string;
1246
+ readonly sourceFile: ts.SourceFile;
1247
+ } | null {
1248
+ const preferredPrograms = [
1249
+ ...preparedView.tsDiagnosticPrograms.filter((program) => program.filePaths !== undefined),
1250
+ ...preparedView.tsDiagnosticPrograms.filter((program) => program.filePaths === undefined),
1251
+ ];
1252
+
1253
+ for (const candidateFilePath of collectPreparedAnalysisFilePathCandidates(filePath)) {
1254
+ const programFileName = preparedView.preparedProgram.toProgramFileName(candidateFilePath);
1255
+ for (const diagnosticProgram of preferredPrograms) {
1256
+ if (
1257
+ diagnosticProgram.filePaths !== undefined &&
1258
+ !diagnosticProgram.filePaths.includes(candidateFilePath)
1259
+ ) {
1260
+ continue;
1261
+ }
1262
+
1263
+ const sourceFile = diagnosticProgram.program.getSourceFile(programFileName);
1264
+ if (!sourceFile) {
1265
+ continue;
1266
+ }
1267
+
1268
+ return {
1269
+ diagnosticProgram,
1270
+ matchedFilePath: candidateFilePath,
1271
+ sourceFile,
1272
+ };
1273
+ }
1274
+ }
1275
+
1276
+ return null;
1277
+ }
1278
+
1279
+ function collectPreparedViewTsDiagnostics(
1280
+ preparedView: PreparedAnalysisView,
1281
+ frontendDiagnostics: readonly MergedDiagnostic[],
1282
+ filePath?: string,
1283
+ requireSourceFile = false,
1284
+ ): readonly MergedDiagnostic[] {
1285
+ if (
1286
+ preparedView.universalPolicyScope === 'sourceSupplemental' ||
1287
+ hasErrorDiagnostics(frontendDiagnostics) ||
1288
+ (requireSourceFile && !filePath)
1289
+ ) {
1290
+ return [];
1291
+ }
1292
+
1293
+ const sourceFileMatch = filePath
1294
+ ? getPreparedViewTsDiagnosticProgramMatch(preparedView, filePath)
1295
+ : null;
1296
+ if (requireSourceFile && !sourceFileMatch) {
1297
+ return [];
1298
+ }
1299
+
1300
+ const metadata: Record<string, boolean | number | string | undefined> = {
1301
+ fileScoped: filePath !== undefined,
1302
+ requireSourceFile,
1303
+ rootCount: sourceFileMatch
1304
+ ? sourceFileMatch.diagnosticProgram.program.getRootFileNames().length
1305
+ : preparedView.program.getRootFileNames().length,
1306
+ universalPolicyScope: preparedView.universalPolicyScope,
1307
+ };
1308
+ if (sourceFileMatch) {
1309
+ metadata.filePath = sourceFileMatch.matchedFilePath;
1310
+ }
1311
+ const diagnostics = measureCheckerTiming(
1312
+ 'project.analyze.tsDiagnostics',
1313
+ metadata,
1314
+ () => {
1315
+ const handledFilePaths = new Set(
1316
+ preparedView.tsDiagnosticPrograms.flatMap((diagnosticProgram) =>
1317
+ diagnosticProgram.filePaths ? [...diagnosticProgram.filePaths] : []
1318
+ ),
1319
+ );
1320
+ const collectedDiagnostics = sourceFileMatch
1321
+ ? ts.getPreEmitDiagnostics(
1322
+ sourceFileMatch.diagnosticProgram.program,
1323
+ sourceFileMatch.sourceFile,
1324
+ )
1325
+ : preparedView.tsDiagnosticPrograms.flatMap((diagnosticProgram) => {
1326
+ if (!diagnosticProgram.filePaths || diagnosticProgram.filePaths.length === 0) {
1327
+ return ts.getPreEmitDiagnostics(diagnosticProgram.program).filter((diagnostic) =>
1328
+ !diagnostic.file ||
1329
+ !handledFilePaths.has(toSourceFileName(diagnostic.file.fileName))
1330
+ );
1331
+ }
1332
+
1333
+ return diagnosticProgram.filePaths.flatMap((diagnosticFilePath) => {
1334
+ const programFileName = preparedView.preparedProgram.toProgramFileName(
1335
+ diagnosticFilePath,
1336
+ );
1337
+ const diagnosticSourceFile = diagnosticProgram.program.getSourceFile(programFileName);
1338
+ return diagnosticSourceFile
1339
+ ? ts.getPreEmitDiagnostics(diagnosticProgram.program, diagnosticSourceFile)
1340
+ : [];
1341
+ });
1342
+ });
1343
+ metadata.diagnostics = collectedDiagnostics.length;
1344
+ return collectedDiagnostics;
1345
+ },
1346
+ { always: true },
1347
+ );
1348
+
1349
+ return remapDiagnostics(
1350
+ diagnostics.map((diagnostic) =>
1351
+ toMappedMergedDiagnostic(diagnostic, preparedView.diagnosticPreparedFiles)
1352
+ ),
1353
+ );
1354
+ }
1355
+
1356
+ function collectPreparedViewUniversalDiagnostics(
1357
+ preparedView: PreparedAnalysisView,
1358
+ analysisContext: AnalysisContext,
1359
+ filePath?: string,
1360
+ ): readonly SoundDiagnostic[] {
1361
+ const metadata: Record<string, boolean | number | string | undefined> = {
1362
+ fileScoped: filePath !== undefined,
1363
+ rootCount: preparedView.program.getRootFileNames().length,
1364
+ universalPolicyScope: preparedView.universalPolicyScope,
1365
+ };
1366
+ if (filePath) {
1367
+ metadata.filePath = filePath;
1368
+ }
1369
+ return measureCheckerTiming(
1370
+ 'project.analyze.universalPolicy',
1371
+ metadata,
1372
+ () => {
1373
+ const diagnostics = remapDiagnostics(
1374
+ remapSoundDiagnostics(
1375
+ preparedView.universalPolicyScope === 'sourceSupplemental'
1376
+ ? runSourceSupplementalPolicyAnalysis(analysisContext)
1377
+ : runUniversalPolicyAnalysis(analysisContext),
1378
+ preparedView.diagnosticPreparedFiles,
1379
+ ),
1380
+ );
1381
+ metadata.diagnostics = diagnostics.length;
1382
+ return diagnostics;
1383
+ },
1384
+ { always: true },
1385
+ );
1386
+ }
1387
+
1388
+ function collectPreparedViewSoundDiagnostics(
1389
+ preparedView: PreparedAnalysisView,
1390
+ analysisContext: AnalysisContext,
1391
+ filePath?: string,
1392
+ ): readonly SoundDiagnostic[] {
1393
+ const metadata: Record<string, boolean | number | string | undefined> = {
1394
+ fileScoped: filePath !== undefined,
1395
+ rootCount: preparedView.program.getRootFileNames().length,
1396
+ runSound: preparedView.runSound,
1397
+ };
1398
+ if (filePath) {
1399
+ metadata.filePath = filePath;
1400
+ }
1401
+ return measureCheckerTiming(
1402
+ 'project.analyze.soundRules',
1403
+ metadata,
1404
+ () => {
1405
+ const diagnostics = remapDiagnostics(
1406
+ remapSoundDiagnostics(
1407
+ preparedView.runSound ? runSoundAnalysis(analysisContext) : [],
1408
+ preparedView.diagnosticPreparedFiles,
1409
+ ),
1410
+ );
1411
+ metadata.diagnostics = diagnostics.length;
1412
+ return diagnostics;
1413
+ },
1414
+ { always: true },
1415
+ );
1416
+ }
1417
+
1418
+ function getFileScopedAnalysisContext(
1419
+ preparedView: PreparedAnalysisView,
1420
+ filePath: string,
1421
+ ): AnalysisContext | null {
1422
+ let byFile = fileScopedAnalysisContextCache.get(preparedView);
1423
+ if (!byFile) {
1424
+ byFile = new Map<string, AnalysisContext | null>();
1425
+ fileScopedAnalysisContextCache.set(preparedView, byFile);
1426
+ }
1427
+
1428
+ const cached = byFile.get(filePath);
1429
+ if (cached !== undefined) {
1430
+ return cached;
1431
+ }
1432
+
1433
+ const sourceFileMatch = getPreparedViewSourceFileMatch(preparedView, filePath);
1434
+ if (!sourceFileMatch) {
1435
+ byFile.set(filePath, null);
1436
+ return null;
1437
+ }
1438
+ const sourceFile = sourceFileMatch.sourceFile;
1439
+ const preparedSource = preparedView.preparedProgram.preparedHost.getPreparedSourceFile(
1440
+ sourceFileMatch.matchedFilePath,
1441
+ );
1442
+ if (
1443
+ hasTopLevelMacroReplacements(sourceFileMatch.matchedFilePath, preparedSource) ||
1444
+ hasGeneratedTopLevelStatements(sourceFile, preparedView.analysisContext.isGeneratedNode)
1445
+ ) {
1446
+ byFile.set(filePath, null);
1447
+ return null;
1448
+ }
1449
+
1450
+ const analysisContext = createAnalysisContext({
1451
+ includeSourceFile: (candidate) =>
1452
+ matchesPreparedAnalysisFilePath(toSourceFileName(candidate.fileName), filePath) &&
1453
+ !isMacroAuthoringSourceFile(candidate, preparedView.analysisPreparedProgram),
1454
+ isGeneratedNode: createPreparedProgramGeneratedNodeDetector(
1455
+ preparedView.analysisPreparedProgram,
1456
+ ),
1457
+ program: preparedView.program,
1458
+ runtime: preparedView.analysisContext.runtime,
1459
+ workingDirectory: preparedView.analysisContext.workingDirectory,
1460
+ });
1461
+ byFile.set(filePath, analysisContext);
1462
+ return analysisContext;
1463
+ }
1464
+
1465
+ function createSummary(diagnostics: readonly { category: 'error' | 'warning' | 'message' }[]) {
1466
+ return {
1467
+ total: diagnostics.length,
1468
+ errors: diagnostics.filter((diagnostic) => diagnostic.category === 'error').length,
1469
+ warnings: diagnostics.filter((diagnostic) => diagnostic.category === 'warning').length,
1470
+ messages: diagnostics.filter((diagnostic) => diagnostic.category === 'message').length,
1471
+ };
1472
+ }
1473
+
1474
+ function isMacroAuthoringSourceFile(
1475
+ sourceFile: ts.SourceFile,
1476
+ preparedProgram?: PreparedProgram,
1477
+ ): boolean {
1478
+ const sourceText = preparedProgram?.preparedHost.getPreparedSourceFile(
1479
+ toSourceFileName(sourceFile.fileName),
1480
+ )?.originalText ?? sourceFile.text;
1481
+ return sourceTextLooksLikeMacroModule(sourceText) ||
1482
+ usesLegacyDefineMacroAuthoring(sourceText);
1483
+ }
1484
+
1485
+ function applyMacroCacheStatsToMetadata(
1486
+ metadata: Record<string, string | number>,
1487
+ macroCacheStats: MacroModuleCacheStats,
1488
+ ): void {
1489
+ metadata.macroCacheHits = macroCacheStats.moduleCacheHits;
1490
+ metadata.macroCacheMisses = macroCacheStats.moduleCacheMisses;
1491
+ metadata.macroCacheInvalidations = macroCacheStats.moduleCacheInvalidations;
1492
+ metadata.macroModulesEvaluated = macroCacheStats.evaluatedModules;
1493
+ }
1494
+
1495
+ function createPreparedProgramGeneratedNodeDetector(
1496
+ preparedProgram: PreparedProgram,
1497
+ ): (node: ts.Node) => boolean {
1498
+ const preparedFileCache = new Map<string, PreparedSourceFile | null>();
1499
+
1500
+ function getPreparedFile(sourceFile: ts.SourceFile | undefined): PreparedSourceFile | undefined {
1501
+ if (!sourceFile) {
1502
+ return undefined;
1503
+ }
1504
+
1505
+ const sourceFileName = toSourceFileName(sourceFile.fileName);
1506
+ if (preparedFileCache.has(sourceFileName)) {
1507
+ return preparedFileCache.get(sourceFileName) ?? undefined;
1508
+ }
1509
+
1510
+ const preparedFile = preparedProgram.preparedHost.getPreparedSourceFile(sourceFileName);
1511
+ preparedFileCache.set(sourceFileName, preparedFile ?? null);
1512
+ return preparedFile;
1513
+ }
1514
+
1515
+ return (node: ts.Node): boolean => {
1516
+ if (ts.isSourceFile(node)) {
1517
+ return false;
1518
+ }
1519
+
1520
+ const sourceFile = node.getSourceFile();
1521
+ const preparedFile = getPreparedFile(sourceFile);
1522
+ if (!preparedFile) {
1523
+ return false;
1524
+ }
1525
+
1526
+ const programStart = node.getStart(sourceFile, false);
1527
+ const programEnd = node.getEnd();
1528
+ if (programEnd <= programStart) {
1529
+ return false;
1530
+ }
1531
+
1532
+ const startMapping = mapProgramPositionToSource(preparedFile, programStart);
1533
+ const endMapping = mapProgramPositionToSource(
1534
+ preparedFile,
1535
+ Math.max(programStart, programEnd - 1),
1536
+ );
1537
+ return startMapping.insideReplacement && endMapping.insideReplacement;
1538
+ };
1539
+ }
1540
+
1541
+ function aggregateMacroCacheStats(
1542
+ preparedProject: PreparedAnalysisProject,
1543
+ ): MacroModuleCacheStats {
1544
+ const aggregated: MacroModuleCacheStats = {
1545
+ evaluatedModules: 0,
1546
+ moduleCacheHits: 0,
1547
+ moduleCacheInvalidations: 0,
1548
+ moduleCacheMisses: 0,
1549
+ };
1550
+
1551
+ for (
1552
+ const view of [
1553
+ preparedProject.tsView,
1554
+ preparedProject.stsView,
1555
+ preparedProject.packageSourcePolicyView,
1556
+ ]
1557
+ ) {
1558
+ if (!view) {
1559
+ continue;
1560
+ }
1561
+
1562
+ aggregated.evaluatedModules += view.macroCacheStats.evaluatedModules;
1563
+ aggregated.moduleCacheHits += view.macroCacheStats.moduleCacheHits;
1564
+ aggregated.moduleCacheInvalidations += view.macroCacheStats.moduleCacheInvalidations;
1565
+ aggregated.moduleCacheMisses += view.macroCacheStats.moduleCacheMisses;
1566
+ }
1567
+
1568
+ return aggregated;
1569
+ }
1570
+
1571
+ function collectPreparedProjectViews(
1572
+ preparedProject: PreparedAnalysisProject | null | undefined,
1573
+ ): readonly PreparedAnalysisView[] {
1574
+ if (!preparedProject) {
1575
+ return [];
1576
+ }
1577
+
1578
+ return [
1579
+ preparedProject.tsView,
1580
+ preparedProject.stsView,
1581
+ preparedProject.packageSourcePolicyView,
1582
+ ].filter((view): view is PreparedAnalysisView => view !== null);
1583
+ }
1584
+
1585
+ export function disposePreparedAnalysisProject(
1586
+ preparedProject: PreparedAnalysisProject | null | undefined,
1587
+ retainedProject?: PreparedAnalysisProject | null,
1588
+ ): void {
1589
+ const retainedViews = new Set(collectPreparedProjectViews(retainedProject));
1590
+ const retainedPreparedPrograms = new Set<PreparedProgram>(
1591
+ collectPreparedProjectViews(retainedProject).flatMap((view) => [
1592
+ view.analysisPreparedProgram,
1593
+ view.preparedProgram,
1594
+ ]),
1595
+ );
1596
+ const retainedReuseStates = new Set<PreparedCompilerHostReuseState>(
1597
+ [...retainedPreparedPrograms].map((preparedProgram) => preparedProgram.preparedHost.reuseState),
1598
+ );
1599
+ const disposedMacroEnvironments = new Set<object>();
1600
+ const disposedPreparedPrograms = new Set<PreparedProgram>();
1601
+
1602
+ for (const view of collectPreparedProjectViews(preparedProject)) {
1603
+ if (retainedViews.has(view)) {
1604
+ continue;
1605
+ }
1606
+ const macroEnvironment = view.macroEnvironment as object;
1607
+ if (disposedMacroEnvironments.has(macroEnvironment)) {
1608
+ continue;
1609
+ }
1610
+ disposedMacroEnvironments.add(macroEnvironment);
1611
+ view.macroEnvironment.dispose();
1612
+
1613
+ for (const preparedProgram of [view.analysisPreparedProgram, view.preparedProgram]) {
1614
+ if (
1615
+ disposedPreparedPrograms.has(preparedProgram) ||
1616
+ retainedPreparedPrograms.has(preparedProgram)
1617
+ ) {
1618
+ continue;
1619
+ }
1620
+ disposedPreparedPrograms.add(preparedProgram);
1621
+ const reuseState = preparedProgram.preparedHost.reuseState;
1622
+ preparedProgram.dispose(false);
1623
+ if (!retainedReuseStates.has(reuseState)) {
1624
+ clearPreparedCompilerHostReuseState(reuseState);
1625
+ }
1626
+ }
1627
+ }
1628
+ }
1629
+
1630
+ export function analyzeProject(options: AnalyzeProjectOptions): AnalyzeProjectResult {
1631
+ const preparedProject = prepareProjectAnalysis(options);
1632
+ try {
1633
+ return analyzePreparedProject(preparedProject);
1634
+ } finally {
1635
+ disposePreparedAnalysisProject(preparedProject);
1636
+ }
1637
+ }
1638
+
1639
+ export function prepareProjectAnalysis(
1640
+ options: AnalyzeProjectOptions,
1641
+ reusableProject?: PreparedAnalysisProject,
1642
+ prepareOptions: PrepareProjectAnalysisOptions = {},
1643
+ ): PreparedAnalysisProject {
1644
+ const prepareMetadata: Record<string, string | number> = {
1645
+ projectPath: options.projectPath,
1646
+ };
1647
+ return measureCheckerTiming(
1648
+ 'project.prepareProjectAnalysis',
1649
+ prepareMetadata,
1650
+ () => {
1651
+ const loadedConfig = loadConfig(
1652
+ options.projectPath,
1653
+ { target: options.target },
1654
+ options.additionalRootNames,
1655
+ );
1656
+ const projectPackageJsonPath = findNearestPackageJsonPath(options.projectPath, ts.sys);
1657
+ const configReuseSignature = createProjectConfigReuseSignature(
1658
+ options.projectPath,
1659
+ loadedConfig,
1660
+ );
1661
+ const soundscriptRootDiscoverySignature = createSoundscriptRootDiscoverySignature(
1662
+ options.projectPath,
1663
+ loadedConfig,
1664
+ );
1665
+ // Same-stem .sts roots are discovered from the current filesystem, so
1666
+ // reusing a previous discovered-root list can keep removed files alive
1667
+ // across prepared-project rebuilds.
1668
+ const configuredSoundscriptRootNames = collectSoundscriptRootNames(
1669
+ options.projectPath,
1670
+ loadedConfig,
1671
+ );
1672
+ const allRootNames = combineRootNames(
1673
+ combineRootNames(
1674
+ loadedConfig.commandLine.fileNames,
1675
+ configuredSoundscriptRootNames,
1676
+ ),
1677
+ options.additionalRootNames,
1678
+ );
1679
+ const soundscriptRootNames = allRootNames.filter(isSoundscriptSourceFile);
1680
+ const typescriptRootNames = allRootNames.filter((fileName) =>
1681
+ !isSoundscriptSourceFile(fileName)
1682
+ );
1683
+ const configFileParsingDiagnostics = getConfigFileParsingDiagnostics(
1684
+ loadedConfig.diagnostics,
1685
+ options.additionalRootNames,
1686
+ );
1687
+ const soundscriptFileOverridesSignature = createFileOverrideSignature(
1688
+ options.fileOverrides,
1689
+ isSoundscriptSourceFile,
1690
+ );
1691
+ const soundscriptRootContentSignature = createSoundscriptRootContentSignature(
1692
+ soundscriptRootNames,
1693
+ loadedConfig.commandLine.options,
1694
+ options.fileOverrides,
1695
+ );
1696
+ const canReuseConfigArtifacts = reusableProject !== undefined &&
1697
+ reusableProject.analyzeOptions.projectPath === options.projectPath &&
1698
+ reusableProject.configReuseSignature === configReuseSignature;
1699
+ const canReuseStsArtifacts = canReuseConfigArtifacts &&
1700
+ rootNamesEqual(reusableProject.soundscriptRootNames, soundscriptRootNames) &&
1701
+ reusableProject.soundscriptRootContentSignature === soundscriptRootContentSignature &&
1702
+ reusableProject.soundscriptFileOverridesSignature === soundscriptFileOverridesSignature;
1703
+ const soundscriptRootNameSet = new Set(
1704
+ soundscriptRootNames.map((rootName) => ts.sys.resolvePath(rootName)),
1705
+ );
1706
+ const stsView = canReuseStsArtifacts ? reusableProject.stsView : (() => {
1707
+ const metadata: Record<string, string | number> = {
1708
+ rootCount: soundscriptRootNames.length,
1709
+ };
1710
+ return measureCheckerTiming(
1711
+ 'project.prepare.stsView',
1712
+ metadata,
1713
+ () => {
1714
+ const preparedView = prepareAnalysisView(
1715
+ options,
1716
+ loadedConfig,
1717
+ soundscriptRootNames,
1718
+ createSoundStdlibCompilerHost(
1719
+ loadedConfig.commandLine.options,
1720
+ dirname(options.projectPath),
1721
+ ),
1722
+ [],
1723
+ (sourceFile, preparedProgram) =>
1724
+ shouldAnalyzeProjectSoundscriptSourceFile(
1725
+ sourceFile,
1726
+ preparedProgram,
1727
+ projectPackageJsonPath,
1728
+ ),
1729
+ undefined,
1730
+ true,
1731
+ 'full',
1732
+ canReuseConfigArtifacts ? reusableProject?.stsCompilerHostReuseState : undefined,
1733
+ canReuseConfigArtifacts ? reusableProject?.stsView?.program : undefined,
1734
+ );
1735
+ if (preparedView) {
1736
+ applyMacroCacheStatsToMetadata(metadata, preparedView.macroCacheStats);
1737
+ }
1738
+ return preparedView;
1739
+ },
1740
+ { always: true },
1741
+ );
1742
+ })();
1743
+ const shouldDeferTypescriptView = prepareOptions.deferTypescriptView === true;
1744
+ if (shouldDeferTypescriptView) {
1745
+ const canReuseLocalProjectedDeclarationOverrides = canReuseStsArtifacts &&
1746
+ reusableProject?.localProjectedDeclarationOverrides !== undefined;
1747
+ const localProjectedDeclarationOverrides = !canReuseLocalProjectedDeclarationOverrides
1748
+ ? undefined
1749
+ : reusableProject.localProjectedDeclarationOverrides;
1750
+ const preparedProject = {
1751
+ analyzeOptions: { ...options },
1752
+ configReuseSignature,
1753
+ configuredSoundscriptRootNames,
1754
+ localProjectedDeclarationOverrides,
1755
+ packageSourcePolicyContentSignature: '',
1756
+ packageSourcePolicyCompilerHostReuseState: canReuseConfigArtifacts
1757
+ ? reusableProject?.packageSourcePolicyCompilerHostReuseState
1758
+ : undefined,
1759
+ packageSourcePolicyView: null,
1760
+ soundscriptRootContentSignature,
1761
+ soundscriptRootDiscoverySignature,
1762
+ stsCompilerHostReuseState: stsView?.preparedProgram.preparedHost.reuseState,
1763
+ soundscriptFileOverridesSignature,
1764
+ soundscriptRootNames,
1765
+ stsView,
1766
+ tsCompilerHostReuseState: canReuseConfigArtifacts
1767
+ ? reusableProject?.tsCompilerHostReuseState
1768
+ : undefined,
1769
+ tsView: null,
1770
+ };
1771
+ applyMacroCacheStatsToMetadata(prepareMetadata, aggregateMacroCacheStats(preparedProject));
1772
+ return preparedProject;
1773
+ }
1774
+ const needsSupplementalProjectionViews = typescriptRootNames.length > 0 ||
1775
+ (stsView !== null &&
1776
+ hasNonRootProjectedDeclarationCandidates(
1777
+ stsView.program,
1778
+ soundscriptRootNameSet,
1779
+ projectPackageJsonPath,
1780
+ ));
1781
+ if (!needsSupplementalProjectionViews) {
1782
+ const preparedProject = {
1783
+ analyzeOptions: { ...options },
1784
+ configReuseSignature,
1785
+ configuredSoundscriptRootNames,
1786
+ localProjectedDeclarationOverrides: undefined,
1787
+ packageSourcePolicyContentSignature: '',
1788
+ packageSourcePolicyCompilerHostReuseState: canReuseConfigArtifacts
1789
+ ? reusableProject?.packageSourcePolicyCompilerHostReuseState
1790
+ : undefined,
1791
+ packageSourcePolicyView: null,
1792
+ soundscriptRootContentSignature,
1793
+ soundscriptRootDiscoverySignature,
1794
+ stsCompilerHostReuseState: stsView?.preparedProgram.preparedHost.reuseState,
1795
+ soundscriptFileOverridesSignature,
1796
+ soundscriptRootNames,
1797
+ stsView,
1798
+ tsCompilerHostReuseState: canReuseConfigArtifacts
1799
+ ? reusableProject?.tsCompilerHostReuseState
1800
+ : undefined,
1801
+ tsView: null,
1802
+ };
1803
+ applyMacroCacheStatsToMetadata(prepareMetadata, aggregateMacroCacheStats(preparedProject));
1804
+ return preparedProject;
1805
+ }
1806
+
1807
+ const canReuseLocalProjectedDeclarationOverrides = canReuseStsArtifacts &&
1808
+ reusableProject?.localProjectedDeclarationOverrides !== undefined;
1809
+ const localProjectedDeclarationOverrides = canReuseLocalProjectedDeclarationOverrides
1810
+ ? reusableProject.localProjectedDeclarationOverrides
1811
+ : measureCheckerTiming(
1812
+ 'project.prepare.localProjection',
1813
+ {
1814
+ hasStsView: stsView !== null,
1815
+ rootCount: soundscriptRootNames.length,
1816
+ },
1817
+ () =>
1818
+ filterProjectedDeclarationOverridesToRootNames(
1819
+ emitProjectedDeclarationsFailClosed(stsView, soundscriptRootNames),
1820
+ soundscriptRootNames,
1821
+ ),
1822
+ { always: true },
1823
+ );
1824
+
1825
+ const preliminaryTsView = (() => {
1826
+ const metadata: Record<string, string | number> = {
1827
+ rootCount: typescriptRootNames.length,
1828
+ localProjectionCount: localProjectedDeclarationOverrides?.size ?? 0,
1829
+ };
1830
+ return measureCheckerTiming(
1831
+ 'project.prepare.preliminaryTsView',
1832
+ metadata,
1833
+ () => {
1834
+ const preparedView = prepareAnalysisView(
1835
+ options,
1836
+ loadedConfig,
1837
+ typescriptRootNames,
1838
+ ts.createCompilerHost(loadedConfig.commandLine.options),
1839
+ configFileParsingDiagnostics,
1840
+ (sourceFile) => shouldAnalyzeTypescriptViewSourceFile(sourceFile),
1841
+ localProjectedDeclarationOverrides,
1842
+ false,
1843
+ 'full',
1844
+ canReuseConfigArtifacts ? reusableProject?.tsCompilerHostReuseState : undefined,
1845
+ canReuseConfigArtifacts ? reusableProject?.tsView?.program : undefined,
1846
+ );
1847
+ if (preparedView) {
1848
+ applyMacroCacheStatsToMetadata(metadata, preparedView.macroCacheStats);
1849
+ }
1850
+ return preparedView;
1851
+ },
1852
+ { always: true },
1853
+ );
1854
+ })();
1855
+ const packageProjectedDeclarationRootNames = collectProjectedDeclarationCandidateRootNamesFromPrograms(
1856
+ [preliminaryTsView?.program, stsView?.program],
1857
+ localProjectedDeclarationOverrides,
1858
+ projectPackageJsonPath,
1859
+ );
1860
+ const packageSourcePolicyContentSignature = packageProjectedDeclarationRootNames.length === 0
1861
+ ? ''
1862
+ : createSoundscriptRootContentSignature(
1863
+ packageProjectedDeclarationRootNames,
1864
+ loadedConfig.commandLine.options,
1865
+ options.fileOverrides,
1866
+ );
1867
+ const packageProjectedDeclarationOverrides = measureCheckerTiming(
1868
+ 'project.prepare.packageProjection',
1869
+ {
1870
+ candidateCount: packageProjectedDeclarationRootNames.length,
1871
+ },
1872
+ () => {
1873
+ if (packageProjectedDeclarationRootNames.length === 0 || !preliminaryTsView) {
1874
+ return undefined;
1875
+ }
1876
+
1877
+ const expandedProgram = createBuiltinExpandedProgram({
1878
+ baseHost: createSoundStdlibCompilerHost(
1879
+ loadedConfig.commandLine.options,
1880
+ dirname(options.projectPath),
1881
+ ),
1882
+ configFileParsingDiagnostics: [],
1883
+ fileOverrides: options.fileOverrides ?? new Map(),
1884
+ options: loadedConfig.commandLine.options,
1885
+ projectReferences: loadedConfig.commandLine.projectReferences,
1886
+ projectedDeclarationOverrides: localProjectedDeclarationOverrides,
1887
+ runtime: loadedConfig.runtime,
1888
+ rootNames: packageProjectedDeclarationRootNames,
1889
+ });
1890
+ try {
1891
+ return emitProjectedDeclarations(
1892
+ expandedProgram.analysisPreparedProgram,
1893
+ packageProjectedDeclarationRootNames,
1894
+ );
1895
+ } finally {
1896
+ expandedProgram.dispose();
1897
+ }
1898
+ },
1899
+ { always: true },
1900
+ );
1901
+ const projectedDeclarationOverrides = mergeProjectedDeclarationOverrides(
1902
+ localProjectedDeclarationOverrides,
1903
+ packageProjectedDeclarationOverrides,
1904
+ );
1905
+ const canReusePackageSourcePolicyView = canReuseConfigArtifacts &&
1906
+ rootNamesEqual(
1907
+ reusableProject.packageSourcePolicyView?.program.getRootFileNames().map(
1908
+ toSourceFileName,
1909
+ ) ?? [],
1910
+ packageProjectedDeclarationRootNames,
1911
+ ) &&
1912
+ reusableProject.packageSourcePolicyContentSignature ===
1913
+ packageSourcePolicyContentSignature &&
1914
+ !projectedDeclarationOverridesDiffer(
1915
+ reusableProject.localProjectedDeclarationOverrides,
1916
+ localProjectedDeclarationOverrides,
1917
+ );
1918
+ const shouldRebuildTsView = projectedDeclarationOverridesDiffer(
1919
+ localProjectedDeclarationOverrides,
1920
+ projectedDeclarationOverrides,
1921
+ );
1922
+
1923
+ const preparedProject = {
1924
+ analyzeOptions: { ...options },
1925
+ configReuseSignature,
1926
+ configuredSoundscriptRootNames,
1927
+ localProjectedDeclarationOverrides,
1928
+ packageSourcePolicyContentSignature,
1929
+ packageSourcePolicyCompilerHostReuseState: canReusePackageSourcePolicyView
1930
+ ? reusableProject?.packageSourcePolicyCompilerHostReuseState
1931
+ : undefined,
1932
+ packageSourcePolicyView: canReusePackageSourcePolicyView
1933
+ ? reusableProject?.packageSourcePolicyView ?? null
1934
+ : measureCheckerTiming(
1935
+ 'project.prepare.packageSourcePolicyView',
1936
+ {
1937
+ rootCount: packageProjectedDeclarationRootNames.length,
1938
+ },
1939
+ () =>
1940
+ prepareAnalysisView(
1941
+ options,
1942
+ loadedConfig,
1943
+ packageProjectedDeclarationRootNames,
1944
+ createSoundStdlibCompilerHost(
1945
+ loadedConfig.commandLine.options,
1946
+ dirname(options.projectPath),
1947
+ ),
1948
+ [],
1949
+ shouldAnalyzeSoundscriptSourceFile,
1950
+ localProjectedDeclarationOverrides,
1951
+ true,
1952
+ 'sourceSupplemental',
1953
+ canReusePackageSourcePolicyView
1954
+ ? reusableProject?.packageSourcePolicyCompilerHostReuseState
1955
+ : undefined,
1956
+ canReusePackageSourcePolicyView
1957
+ ? reusableProject?.packageSourcePolicyView?.program
1958
+ : undefined,
1959
+ ),
1960
+ { always: true },
1961
+ ),
1962
+ soundscriptRootContentSignature,
1963
+ soundscriptRootDiscoverySignature,
1964
+ stsCompilerHostReuseState: stsView?.preparedProgram.preparedHost.reuseState,
1965
+ soundscriptFileOverridesSignature,
1966
+ soundscriptRootNames,
1967
+ stsView,
1968
+ tsCompilerHostReuseState: preliminaryTsView?.preparedProgram.preparedHost.reuseState,
1969
+ tsView: shouldRebuildTsView
1970
+ ? (() => {
1971
+ const metadata: Record<string, string | number> = {
1972
+ rootCount: typescriptRootNames.length,
1973
+ projectionCount: projectedDeclarationOverrides?.size ?? 0,
1974
+ };
1975
+ return measureCheckerTiming(
1976
+ 'project.prepare.finalTsView',
1977
+ metadata,
1978
+ () => {
1979
+ const preparedView = prepareAnalysisView(
1980
+ options,
1981
+ loadedConfig,
1982
+ typescriptRootNames,
1983
+ ts.createCompilerHost(loadedConfig.commandLine.options),
1984
+ configFileParsingDiagnostics,
1985
+ (sourceFile) => shouldAnalyzeTypescriptViewSourceFile(sourceFile),
1986
+ projectedDeclarationOverrides,
1987
+ false,
1988
+ 'full',
1989
+ preliminaryTsView?.preparedProgram.preparedHost.reuseState ??
1990
+ reusableProject?.tsCompilerHostReuseState,
1991
+ preliminaryTsView?.program,
1992
+ );
1993
+ if (preparedView) {
1994
+ applyMacroCacheStatsToMetadata(metadata, preparedView.macroCacheStats);
1995
+ }
1996
+ return preparedView;
1997
+ },
1998
+ { always: true },
1999
+ );
2000
+ })()
2001
+ : preliminaryTsView,
2002
+ };
2003
+ applyMacroCacheStatsToMetadata(prepareMetadata, aggregateMacroCacheStats(preparedProject));
2004
+ return preparedProject;
2005
+ },
2006
+ { always: true },
2007
+ );
2008
+ }
2009
+
2010
+ export function getPreparedAnalysisViewForFile(
2011
+ preparedProject: PreparedAnalysisProject,
2012
+ filePath: string,
2013
+ ): PreparedAnalysisView | null {
2014
+ if (isSoundscriptSourceFile(filePath)) {
2015
+ const packageSourceView = preparedProject.packageSourcePolicyView;
2016
+ if (
2017
+ packageSourceView &&
2018
+ isNodeModulesPath(filePath) &&
2019
+ getPreparedViewSourceFileMatch(packageSourceView, filePath)
2020
+ ) {
2021
+ return packageSourceView;
2022
+ }
2023
+ const stsView = preparedProject.stsView;
2024
+ if (stsView && getPreparedViewSourceFileMatch(stsView, filePath)) {
2025
+ return stsView;
2026
+ }
2027
+ if (packageSourceView && getPreparedViewSourceFileMatch(packageSourceView, filePath)) {
2028
+ return packageSourceView;
2029
+ }
2030
+ return stsView;
2031
+ }
2032
+
2033
+ return preparedProject.tsView;
2034
+ }
2035
+
2036
+ function getPreparedAnalysisSupplementalViewsForFile(
2037
+ preparedProject: PreparedAnalysisProject,
2038
+ filePath: string,
2039
+ primaryView: PreparedAnalysisView | null,
2040
+ ): readonly PreparedAnalysisView[] {
2041
+ const supplementalViews: PreparedAnalysisView[] = [];
2042
+
2043
+ const addView = (view: PreparedAnalysisView | null): void => {
2044
+ if (!view || view === primaryView || supplementalViews.includes(view)) {
2045
+ return;
2046
+ }
2047
+ supplementalViews.push(view);
2048
+ };
2049
+
2050
+ if (isSoundscriptSourceFile(filePath)) {
2051
+ addView(preparedProject.packageSourcePolicyView);
2052
+ return supplementalViews;
2053
+ }
2054
+
2055
+ addView(preparedProject.stsView);
2056
+ addView(preparedProject.packageSourcePolicyView);
2057
+ return supplementalViews;
2058
+ }
2059
+
2060
+ export function analyzePreparedProjectForFile(
2061
+ preparedProject: PreparedAnalysisProject,
2062
+ filePath: string,
2063
+ ): AnalyzeProjectResult {
2064
+ return measureCheckerTiming(
2065
+ 'project.analyzePreparedProjectForFile',
2066
+ {
2067
+ filePath,
2068
+ hasTsView: preparedProject.tsView !== null,
2069
+ hasStsView: preparedProject.stsView !== null,
2070
+ },
2071
+ () => {
2072
+ const primaryView = getPreparedAnalysisViewForFile(preparedProject, filePath);
2073
+ const primaryAnalysis = analyzePreparedViewForFile(primaryView, filePath);
2074
+ const diagnosticPaths = primaryView
2075
+ ? collectPreparedViewFrontendDiagnosticPaths(primaryView, filePath)
2076
+ : [filePath];
2077
+ const primaryDependencyAnalysis = analyzePreparedViewForDiagnosticPaths(
2078
+ primaryView,
2079
+ diagnosticPaths,
2080
+ );
2081
+ const supplementalAnalyses = getPreparedAnalysisSupplementalViewsForFile(
2082
+ preparedProject,
2083
+ filePath,
2084
+ primaryView,
2085
+ ).map((view) => analyzePreparedViewForDiagnosticPaths(view, diagnosticPaths));
2086
+ const diagnostics = dedupeMergedDiagnostics([
2087
+ ...primaryAnalysis.frontendDiagnostics,
2088
+ ...primaryAnalysis.tsDiagnostics,
2089
+ ...primaryAnalysis.soundDiagnostics,
2090
+ ...primaryDependencyAnalysis.frontendDiagnostics,
2091
+ ...primaryDependencyAnalysis.tsDiagnostics,
2092
+ ...primaryDependencyAnalysis.soundDiagnostics,
2093
+ ...supplementalAnalyses.flatMap((analyzedProgram) => [
2094
+ ...analyzedProgram.frontendDiagnostics,
2095
+ ...analyzedProgram.tsDiagnostics,
2096
+ ...analyzedProgram.soundDiagnostics,
2097
+ ]),
2098
+ ]);
2099
+
2100
+ return {
2101
+ diagnostics,
2102
+ summary: createSummary(diagnostics),
2103
+ };
2104
+ },
2105
+ { always: true },
2106
+ );
2107
+ }
2108
+
2109
+ function collectPreparedAnalysisFilePathCandidates(filePath: string): readonly string[] {
2110
+ const candidates = new Set<string>();
2111
+ const addCandidate = (candidate: string | undefined): void => {
2112
+ if (candidate) {
2113
+ candidates.add(candidate);
2114
+ }
2115
+ };
2116
+
2117
+ addCandidate(filePath);
2118
+ if (isProjectedSoundscriptDeclarationFile(filePath)) {
2119
+ addCandidate(toProjectedDeclarationSourceFileName(filePath));
2120
+ }
2121
+ addCandidate(ts.sys.resolvePath(filePath));
2122
+ if (isProjectedSoundscriptDeclarationFile(filePath)) {
2123
+ addCandidate(ts.sys.resolvePath(toProjectedDeclarationSourceFileName(filePath)));
2124
+ }
2125
+
2126
+ try {
2127
+ const realPath = ts.sys.realpath?.(filePath);
2128
+ addCandidate(realPath);
2129
+ if (realPath) {
2130
+ addCandidate(ts.sys.resolvePath(realPath));
2131
+ if (isProjectedSoundscriptDeclarationFile(realPath)) {
2132
+ const sourcePath = toProjectedDeclarationSourceFileName(realPath);
2133
+ addCandidate(sourcePath);
2134
+ addCandidate(ts.sys.resolvePath(sourcePath));
2135
+ }
2136
+ }
2137
+ } catch {
2138
+ // Ignore realpath failures for virtual or missing paths and fall back to the raw path.
2139
+ }
2140
+
2141
+ return [...candidates];
2142
+ }
2143
+
2144
+ function matchesPreparedAnalysisFilePath(
2145
+ candidateFilePath: string | undefined,
2146
+ expectedFilePath: string,
2147
+ ): boolean {
2148
+ if (!candidateFilePath) {
2149
+ return false;
2150
+ }
2151
+
2152
+ if (candidateFilePath === expectedFilePath) {
2153
+ return true;
2154
+ }
2155
+
2156
+ const expectedCandidates = new Set(collectPreparedAnalysisFilePathCandidates(expectedFilePath));
2157
+ if (expectedCandidates.has(candidateFilePath)) {
2158
+ return true;
2159
+ }
2160
+
2161
+ return collectPreparedAnalysisFilePathCandidates(candidateFilePath).some((candidate) =>
2162
+ expectedCandidates.has(candidate)
2163
+ );
2164
+ }
2165
+
2166
+ export function filterAnalyzedDiagnosticsForFile<T extends MergedDiagnostic>(
2167
+ diagnostics: readonly T[],
2168
+ filePath: string,
2169
+ ): T[] {
2170
+ return diagnostics.filter((diagnostic) =>
2171
+ matchesPreparedAnalysisFilePath(diagnostic.filePath, filePath)
2172
+ );
2173
+ }
2174
+
2175
+ function getPreparedViewSourceFileMatch(
2176
+ preparedView: PreparedAnalysisView,
2177
+ filePath: string,
2178
+ ): { readonly matchedFilePath: string; readonly sourceFile: ts.SourceFile } | null {
2179
+ for (const candidateFilePath of collectPreparedAnalysisFilePathCandidates(filePath)) {
2180
+ const programFileName = preparedView.preparedProgram.toProgramFileName(candidateFilePath);
2181
+ const sourceFile = preparedView.program.getSourceFile(programFileName);
2182
+ if (sourceFile) {
2183
+ return {
2184
+ matchedFilePath: candidateFilePath,
2185
+ sourceFile,
2186
+ };
2187
+ }
2188
+ }
2189
+
2190
+ return null;
2191
+ }
2192
+
2193
+ export function analyzePreparedProject(
2194
+ preparedProject: PreparedAnalysisProject,
2195
+ ): AnalyzeProjectResult {
2196
+ return measureCheckerTiming(
2197
+ 'project.analyzePreparedProject',
2198
+ {
2199
+ hasTsView: preparedProject.tsView !== null,
2200
+ hasStsView: preparedProject.stsView !== null,
2201
+ },
2202
+ () => {
2203
+ const analyzedPrograms = [
2204
+ analyzePreparedView(preparedProject.tsView),
2205
+ analyzePreparedView(preparedProject.stsView),
2206
+ analyzePreparedView(preparedProject.packageSourcePolicyView),
2207
+ ];
2208
+ const diagnostics = dedupeMergedDiagnostics(analyzedPrograms.flatMap((programResult) => [
2209
+ ...programResult.frontendDiagnostics,
2210
+ ...programResult.tsDiagnostics,
2211
+ ...programResult.soundDiagnostics,
2212
+ ]));
2213
+
2214
+ return {
2215
+ diagnostics,
2216
+ summary: createSummary(diagnostics),
2217
+ };
2218
+ },
2219
+ { always: true },
2220
+ );
2221
+ }
2222
+
2223
+ function dedupeMergedDiagnostics<T extends MergedDiagnostic>(diagnostics: readonly T[]): T[] {
2224
+ const deduped: T[] = [];
2225
+ const seen = new Set<string>();
2226
+
2227
+ for (const diagnostic of diagnostics) {
2228
+ const key = [
2229
+ diagnostic.source,
2230
+ diagnostic.code,
2231
+ diagnostic.filePath ?? '',
2232
+ diagnostic.line ?? 0,
2233
+ diagnostic.column ?? 0,
2234
+ diagnostic.endLine ?? 0,
2235
+ diagnostic.endColumn ?? 0,
2236
+ diagnostic.message,
2237
+ ].join('\u0000');
2238
+ if (seen.has(key)) {
2239
+ continue;
2240
+ }
2241
+ seen.add(key);
2242
+ deduped.push(diagnostic);
2243
+ }
2244
+
2245
+ return deduped;
2246
+ }