@soundscript/soundscript 0.1.12 → 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,3338 @@
1
+ import ts from 'typescript';
2
+
3
+ // Internal bridge from frontend-owned macro wrappers to the current TypeScript host substrate.
4
+ // Macro authors should depend on macro_api.ts instead of this module.
5
+
6
+ import type {
7
+ BlockSyntax,
8
+ DeclSyntax,
9
+ ExprSyntax,
10
+ InvocationSyntax,
11
+ JsxSyntax,
12
+ MacroAnyClassMemberSyntax,
13
+ MacroAnyJsxAttributeSyntax,
14
+ MacroAnyJsxChildSyntax,
15
+ MacroArgumentView,
16
+ MacroArrayLiteralElementSyntax,
17
+ MacroArrayLiteralExprSyntax,
18
+ MacroBinaryExprPattern,
19
+ MacroBinaryOperator,
20
+ MacroBindingIdentifierSyntax,
21
+ MacroCallExprPattern,
22
+ MacroClassConstructorSyntax,
23
+ MacroClassDeclSyntax,
24
+ MacroClassFieldSyntax,
25
+ MacroClassMethodSyntax,
26
+ MacroConditionalExprPattern,
27
+ MacroDeclarationKind,
28
+ MacroFieldBuildOptions,
29
+ MacroForBuildOptions,
30
+ MacroFunctionBuildOptions,
31
+ MacroFunctionDeclSyntax,
32
+ MacroFunctionExprSyntax,
33
+ MacroIfBuildOptions,
34
+ MacroInterfaceDeclSyntax,
35
+ MacroInvocationForm,
36
+ MacroJsxAttributeSyntax,
37
+ MacroJsxElementSyntax,
38
+ MacroJsxExpressionSyntax,
39
+ MacroJsxFragmentSyntax,
40
+ MacroJsxSpreadAttributeSyntax,
41
+ MacroJsxTextSyntax,
42
+ MacroLiteralTypeSyntax,
43
+ MacroMethodBuildOptions,
44
+ MacroModifierName,
45
+ MacroObjectMemberBuildOptions,
46
+ MacroObjectTypeMemberSyntax,
47
+ MacroObjectTypeSyntax,
48
+ MacroParameterBuildOptions,
49
+ MacroParameterSyntax,
50
+ MacroPropertyAccessPattern,
51
+ MacroSetterBuildOptions,
52
+ MacroSyntaxNode,
53
+ MacroSyntaxRewriteOptions,
54
+ MacroTemplateOperand,
55
+ MacroTemplateQuasi,
56
+ MacroTypeAliasDeclSyntax,
57
+ MacroTypeParameterSyntax,
58
+ MacroUnaryOperator,
59
+ MacroUnionTypeSyntax,
60
+ StmtSyntax,
61
+ TypeSyntax,
62
+ } from './macro_api.ts';
63
+ import {
64
+ parseHostExpression,
65
+ parseHostStatements,
66
+ parseSingleHostStatement,
67
+ synthesizeHostNode,
68
+ } from './macro_host_ast_internal.ts';
69
+ import { parseMacroInvocationAt } from './macro_parser.ts';
70
+ import { scanMacroCandidates } from './macro_scanner.ts';
71
+ import type { SourceSpan } from './macro_types.ts';
72
+
73
+ const HOST_NODE = Symbol.for('soundscript.macro-syntax.host-node');
74
+ const HOST_SOURCE_FILE = Symbol.for('soundscript.macro-syntax.host-source-file');
75
+ const HOST_HINT = Symbol.for('soundscript.macro-syntax.host-hint');
76
+ const HOST_SOURCE_OFFSET = Symbol.for('soundscript.macro-syntax.host-source-offset');
77
+
78
+ type HostHint = ts.EmitHint;
79
+
80
+ interface HostBackedSyntaxNode {
81
+ readonly [HOST_HINT]: HostHint;
82
+ readonly [HOST_NODE]: ts.Node;
83
+ readonly [HOST_SOURCE_FILE]: ts.SourceFile;
84
+ }
85
+
86
+ type SourceFileWithOffset = ts.SourceFile & {
87
+ [HOST_SOURCE_OFFSET]?: number;
88
+ };
89
+
90
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
91
+
92
+ function withHostNode<T extends MacroSyntaxNode>(
93
+ node: T,
94
+ hostNode: ts.Node,
95
+ sourceFile: ts.SourceFile,
96
+ hint: HostHint,
97
+ ): T {
98
+ return Object.assign(node, {
99
+ [HOST_HINT]: hint,
100
+ [HOST_NODE]: hostNode,
101
+ [HOST_SOURCE_FILE]: sourceFile,
102
+ });
103
+ }
104
+
105
+ function printHostNode(node: ts.Node, sourceFile: ts.SourceFile, hint: HostHint): string {
106
+ return printer.printNode(hint, node, sourceFile);
107
+ }
108
+
109
+ function offsetSpan(baseSpan: SourceSpan, innerSpan: SourceSpan): SourceSpan {
110
+ return {
111
+ fileName: baseSpan.fileName,
112
+ start: baseSpan.start + innerSpan.start,
113
+ end: baseSpan.start + innerSpan.end,
114
+ };
115
+ }
116
+
117
+ function setSourceFileOffset(sourceFile: ts.SourceFile, offset: number): ts.SourceFile {
118
+ (sourceFile as SourceFileWithOffset)[HOST_SOURCE_OFFSET] = offset;
119
+ return sourceFile;
120
+ }
121
+
122
+ function sourceFileOffset(sourceFile: ts.SourceFile): number {
123
+ return (sourceFile as SourceFileWithOffset)[HOST_SOURCE_OFFSET] ?? 0;
124
+ }
125
+
126
+ function scriptKindForHostFile(fileName: string): ts.ScriptKind {
127
+ const lowered = fileName.toLowerCase();
128
+ if (lowered.endsWith('.sts') || lowered.endsWith('.tsx') || lowered.endsWith('.jsx')) {
129
+ return ts.ScriptKind.TSX;
130
+ }
131
+ if (lowered.endsWith('.js') || lowered.endsWith('.mjs') || lowered.endsWith('.cjs')) {
132
+ return ts.ScriptKind.JS;
133
+ }
134
+ return ts.ScriptKind.TS;
135
+ }
136
+
137
+ function ensureNoParseDiagnostics(sourceFile: ts.SourceFile, message: string): void {
138
+ const parseDiagnostics = (sourceFile as ts.SourceFile & {
139
+ parseDiagnostics?: readonly ts.Diagnostic[];
140
+ }).parseDiagnostics ?? [];
141
+ if (parseDiagnostics.length > 0) {
142
+ throw new Error(message);
143
+ }
144
+ }
145
+
146
+ function fallbackText(node: MacroSyntaxNode): string {
147
+ const hostBacked = node as MacroSyntaxNode & Partial<HostBackedSyntaxNode>;
148
+ if (hostBacked[HOST_NODE] && hostBacked[HOST_SOURCE_FILE]) {
149
+ return printHostNode(
150
+ hostBacked[HOST_NODE],
151
+ hostBacked[HOST_SOURCE_FILE],
152
+ hostBacked[HOST_HINT] ?? ts.EmitHint.Unspecified,
153
+ );
154
+ }
155
+ return '';
156
+ }
157
+
158
+ export function getHostNode(node: MacroSyntaxNode): ts.Node | null {
159
+ return (node as MacroSyntaxNode & Partial<HostBackedSyntaxNode>)[HOST_NODE] ?? null;
160
+ }
161
+
162
+ export function getHostExpression(node: ExprSyntax): ts.Expression {
163
+ const hostNode = getHostNode(node);
164
+ if (!hostNode || !ts.isExpression(hostNode)) {
165
+ throw new Error('Expected an expression-backed syntax node.');
166
+ }
167
+ return hostNode;
168
+ }
169
+
170
+ export function getHostStatement(node: StmtSyntax | DeclSyntax): ts.Statement {
171
+ const hostNode = getHostNode(node);
172
+ if (!hostNode || !ts.isStatement(hostNode)) {
173
+ throw new Error('Expected a statement-backed syntax node.');
174
+ }
175
+ return hostNode;
176
+ }
177
+
178
+ export function getHostBlock(node: BlockSyntax): ts.Block {
179
+ const hostNode = getHostNode(node);
180
+ if (!hostNode || !ts.isBlock(hostNode)) {
181
+ throw new Error('Expected a block-backed syntax node.');
182
+ }
183
+ return hostNode;
184
+ }
185
+
186
+ export function getHostDeclaration(
187
+ node: DeclSyntax,
188
+ ):
189
+ | ts.ClassDeclaration
190
+ | ts.FunctionDeclaration
191
+ | ts.InterfaceDeclaration
192
+ | ts.TypeAliasDeclaration {
193
+ const hostNode = getHostNode(node);
194
+ if (
195
+ !hostNode ||
196
+ (!ts.isClassDeclaration(hostNode) && !ts.isFunctionDeclaration(hostNode) &&
197
+ !ts.isInterfaceDeclaration(hostNode) && !ts.isTypeAliasDeclaration(hostNode))
198
+ ) {
199
+ throw new Error('Expected a declaration-backed syntax node.');
200
+ }
201
+ return hostNode;
202
+ }
203
+
204
+ export function getHostJsx(
205
+ node: JsxSyntax,
206
+ ): ts.JsxChild | ts.JsxOpeningLikeElement | ts.JsxAttributeLike {
207
+ const hostNode = getHostNode(node);
208
+ if (!hostNode) {
209
+ throw new Error('Expected a JSX-backed syntax node.');
210
+ }
211
+ return hostNode as ts.JsxChild | ts.JsxOpeningLikeElement | ts.JsxAttributeLike;
212
+ }
213
+
214
+ function nodeSpan(
215
+ node: ts.Node,
216
+ sourceFile: ts.SourceFile,
217
+ fallback: SourceSpan,
218
+ ): SourceSpan {
219
+ if (node.pos >= 0 && node.end >= 0) {
220
+ const offset = sourceFileOffset(sourceFile);
221
+ return {
222
+ fileName: fallback.fileName,
223
+ start: offset + node.getStart(sourceFile, false),
224
+ end: offset + node.end,
225
+ };
226
+ }
227
+ return {
228
+ fileName: fallback.fileName,
229
+ start: fallback.start,
230
+ end: fallback.end,
231
+ };
232
+ }
233
+
234
+ function hasModifier(
235
+ node: { readonly modifiers?: readonly ts.ModifierLike[] },
236
+ name: MacroModifierName,
237
+ ): boolean {
238
+ const expectedKind = (() => {
239
+ switch (name) {
240
+ case 'async':
241
+ return ts.SyntaxKind.AsyncKeyword;
242
+ case 'default':
243
+ return ts.SyntaxKind.DefaultKeyword;
244
+ case 'export':
245
+ return ts.SyntaxKind.ExportKeyword;
246
+ case 'private':
247
+ return ts.SyntaxKind.PrivateKeyword;
248
+ case 'protected':
249
+ return ts.SyntaxKind.ProtectedKeyword;
250
+ case 'public':
251
+ return ts.SyntaxKind.PublicKeyword;
252
+ case 'readonly':
253
+ return ts.SyntaxKind.ReadonlyKeyword;
254
+ case 'static':
255
+ return ts.SyntaxKind.StaticKeyword;
256
+ }
257
+ })();
258
+ return node.modifiers?.some((modifier) => modifier.kind === expectedKind) ?? false;
259
+ }
260
+
261
+ function createModifierNodes(
262
+ names: readonly MacroModifierName[] | undefined,
263
+ ): readonly ts.Modifier[] | undefined {
264
+ if (!names || names.length === 0) {
265
+ return undefined;
266
+ }
267
+ return names.map((name) => {
268
+ switch (name) {
269
+ case 'async':
270
+ return ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword);
271
+ case 'default':
272
+ return ts.factory.createModifier(ts.SyntaxKind.DefaultKeyword);
273
+ case 'export':
274
+ return ts.factory.createModifier(ts.SyntaxKind.ExportKeyword);
275
+ case 'private':
276
+ return ts.factory.createModifier(ts.SyntaxKind.PrivateKeyword);
277
+ case 'protected':
278
+ return ts.factory.createModifier(ts.SyntaxKind.ProtectedKeyword);
279
+ case 'public':
280
+ return ts.factory.createModifier(ts.SyntaxKind.PublicKeyword);
281
+ case 'readonly':
282
+ return ts.factory.createModifier(ts.SyntaxKind.ReadonlyKeyword);
283
+ case 'static':
284
+ return ts.factory.createModifier(ts.SyntaxKind.StaticKeyword);
285
+ }
286
+ });
287
+ }
288
+
289
+ function createBuildSourceFile(fileName: string): ts.SourceFile {
290
+ return ts.createSourceFile(
291
+ fileName,
292
+ '',
293
+ ts.ScriptTarget.Latest,
294
+ true,
295
+ scriptKindForHostFile(fileName),
296
+ );
297
+ }
298
+
299
+ function cloneBlockNode(block: BlockSyntax): ts.Block {
300
+ return synthesizeHostNode(ts.factory.createBlock([...getHostBlock(block).statements], true));
301
+ }
302
+
303
+ function createBinaryOperatorToken(operator: MacroBinaryOperator): ts.BinaryOperatorToken {
304
+ switch (operator) {
305
+ case '&':
306
+ return ts.factory.createToken(ts.SyntaxKind.AmpersandToken);
307
+ case '<':
308
+ return ts.factory.createToken(ts.SyntaxKind.LessThanToken);
309
+ case '+':
310
+ return ts.factory.createToken(ts.SyntaxKind.PlusToken);
311
+ case '=':
312
+ return ts.factory.createToken(ts.SyntaxKind.EqualsToken);
313
+ case '!==':
314
+ return ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken);
315
+ case '===':
316
+ return ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken);
317
+ case '|':
318
+ return ts.factory.createToken(ts.SyntaxKind.BarToken);
319
+ }
320
+ }
321
+
322
+ function createUnaryOperatorToken(operator: MacroUnaryOperator): ts.PrefixUnaryOperator {
323
+ switch (operator) {
324
+ case '!':
325
+ return ts.SyntaxKind.ExclamationToken;
326
+ }
327
+ }
328
+
329
+ function createBuiltExprSyntax(
330
+ fileName: string,
331
+ node: ts.Expression,
332
+ ): ExprSyntax {
333
+ const sourceFile = createBuildSourceFile(fileName);
334
+ return createExprSyntaxFromNode(synthesizeHostNode(node), sourceFile, {
335
+ fileName,
336
+ start: 0,
337
+ end: 0,
338
+ });
339
+ }
340
+
341
+ function createBuiltStmtSyntax(
342
+ fileName: string,
343
+ node: ts.Statement,
344
+ ): StmtSyntax {
345
+ const sourceFile = createBuildSourceFile(fileName);
346
+ return createStmtSyntaxFromNode(synthesizeHostNode(node), sourceFile, {
347
+ fileName,
348
+ start: 0,
349
+ end: 0,
350
+ });
351
+ }
352
+
353
+ function createTypeNodeFromText(fileName: string, typeText: string): ts.TypeNode {
354
+ const sourceFile = ts.createSourceFile(
355
+ fileName,
356
+ `type __SoundscriptMacroParam = ${typeText};`,
357
+ ts.ScriptTarget.Latest,
358
+ true,
359
+ scriptKindForHostFile(fileName),
360
+ );
361
+ ensureNoParseDiagnostics(
362
+ sourceFile,
363
+ 'Macro builder parameter types must parse as valid host-language types.',
364
+ );
365
+ const statement = sourceFile.statements[0];
366
+ if (!statement || !ts.isTypeAliasDeclaration(statement) || !statement.type) {
367
+ throw new Error('Macro builder parameter types must parse as valid host-language types.');
368
+ }
369
+ return synthesizeHostNode(statement.type);
370
+ }
371
+
372
+ function createParameterDeclarations(
373
+ fileName: string,
374
+ parameters: readonly (string | MacroParameterBuildOptions)[] | undefined,
375
+ ): readonly ts.ParameterDeclaration[] {
376
+ return (parameters ?? []).map((parameter) => {
377
+ const normalized = typeof parameter === 'string' ? { name: parameter } : parameter;
378
+ return synthesizeHostNode(
379
+ ts.factory.createParameterDeclaration(
380
+ undefined,
381
+ undefined,
382
+ normalized.name,
383
+ undefined,
384
+ normalized.type ? createTypeNodeFromText(fileName, normalized.type) : undefined,
385
+ undefined,
386
+ ),
387
+ );
388
+ });
389
+ }
390
+
391
+ function collectThisMemberReferences(node: ts.Node | null): readonly string[] {
392
+ if (!node) {
393
+ return [];
394
+ }
395
+
396
+ const references = new Set<string>();
397
+ function visit(current: ts.Node): void {
398
+ if (
399
+ ts.isPropertyAccessExpression(current) &&
400
+ current.expression.kind === ts.SyntaxKind.ThisKeyword &&
401
+ ts.isIdentifier(current.name)
402
+ ) {
403
+ references.add(current.name.text);
404
+ }
405
+ ts.forEachChild(current, visit);
406
+ }
407
+
408
+ visit(node);
409
+ return [...references];
410
+ }
411
+
412
+ function containsCallNamed(node: ts.Node | null, name: string): boolean {
413
+ if (!node) {
414
+ return false;
415
+ }
416
+ let found = false;
417
+ function visit(current: ts.Node): void {
418
+ if (found) {
419
+ return;
420
+ }
421
+ if (ts.isCallExpression(current)) {
422
+ const callee = unwrapParenthesizedExpressionNode(current.expression);
423
+ if (ts.isIdentifier(callee) && callee.text === name) {
424
+ found = true;
425
+ return;
426
+ }
427
+ }
428
+ ts.forEachChild(current, visit);
429
+ }
430
+ visit(node);
431
+ return found;
432
+ }
433
+
434
+ function unwrapParenthesizedExpressionNode(node: ts.Expression): ts.Expression {
435
+ let current = node;
436
+ while (ts.isParenthesizedExpression(current)) {
437
+ current = current.expression;
438
+ }
439
+ return current;
440
+ }
441
+
442
+ function expressionAsIdentifier(node: ts.Expression): string | null {
443
+ const unwrapped = unwrapParenthesizedExpressionNode(node);
444
+ return ts.isIdentifier(unwrapped) ? unwrapped.text : null;
445
+ }
446
+
447
+ function createArrayLiteralElementSyntaxFromNode(
448
+ node: ts.Expression | ts.SpreadElement | ts.OmittedExpression,
449
+ sourceFile: ts.SourceFile,
450
+ fallbackSpan: SourceSpan,
451
+ ): MacroArrayLiteralElementSyntax {
452
+ if (ts.isOmittedExpression(node)) {
453
+ return withHostNode(
454
+ {
455
+ expression() {
456
+ return null;
457
+ },
458
+ isSpread: false,
459
+ kind: 'array_elision',
460
+ span: nodeSpan(node, sourceFile, fallbackSpan),
461
+ text() {
462
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
463
+ },
464
+ },
465
+ node,
466
+ sourceFile,
467
+ ts.EmitHint.Unspecified,
468
+ );
469
+ }
470
+
471
+ const expression = ts.isSpreadElement(node) ? node.expression : node;
472
+ return withHostNode(
473
+ {
474
+ expression() {
475
+ return createExprSyntaxFromNode(
476
+ expression,
477
+ sourceFile,
478
+ nodeSpan(expression, sourceFile, fallbackSpan),
479
+ );
480
+ },
481
+ isSpread: ts.isSpreadElement(node),
482
+ kind: 'array_element',
483
+ span: nodeSpan(node, sourceFile, fallbackSpan),
484
+ text() {
485
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
486
+ },
487
+ },
488
+ node,
489
+ sourceFile,
490
+ ts.EmitHint.Unspecified,
491
+ );
492
+ }
493
+
494
+ function expressionAsArrayLiteral(
495
+ node: ts.Expression,
496
+ sourceFile: ts.SourceFile,
497
+ span: SourceSpan,
498
+ ): MacroArrayLiteralExprSyntax | null {
499
+ const unwrapped = unwrapParenthesizedExpressionNode(node);
500
+ if (!ts.isArrayLiteralExpression(unwrapped)) {
501
+ return null;
502
+ }
503
+
504
+ const base = createExprSyntaxFromNode(unwrapped, sourceFile, span);
505
+ return {
506
+ ...base,
507
+ asArrayLiteral() {
508
+ return this;
509
+ },
510
+ elements: unwrapped.elements.map((element) =>
511
+ createArrayLiteralElementSyntaxFromNode(
512
+ element,
513
+ sourceFile,
514
+ nodeSpan(element, sourceFile, span),
515
+ )
516
+ ),
517
+ };
518
+ }
519
+
520
+ function expressionAsPropertyAccess(
521
+ node: ts.Expression,
522
+ sourceFile: ts.SourceFile,
523
+ span: SourceSpan,
524
+ ): MacroPropertyAccessPattern | null {
525
+ const unwrapped = unwrapParenthesizedExpressionNode(node);
526
+ if (!ts.isPropertyAccessExpression(unwrapped)) {
527
+ return null;
528
+ }
529
+ return {
530
+ name: unwrapped.name.text,
531
+ object: createExprSyntaxFromNode(
532
+ unwrapped.expression,
533
+ sourceFile,
534
+ nodeSpan(unwrapped.expression, sourceFile, span),
535
+ ),
536
+ };
537
+ }
538
+
539
+ function expressionAsCall(
540
+ node: ts.Expression,
541
+ sourceFile: ts.SourceFile,
542
+ span: SourceSpan,
543
+ ): MacroCallExprPattern | null {
544
+ const unwrapped = unwrapParenthesizedExpressionNode(node);
545
+ if (!ts.isCallExpression(unwrapped)) {
546
+ return null;
547
+ }
548
+ return {
549
+ args: unwrapped.arguments.map((argument) =>
550
+ createExprSyntaxFromNode(argument, sourceFile, nodeSpan(argument, sourceFile, span))
551
+ ),
552
+ callee: createExprSyntaxFromNode(
553
+ unwrapped.expression,
554
+ sourceFile,
555
+ nodeSpan(unwrapped.expression, sourceFile, span),
556
+ ),
557
+ };
558
+ }
559
+
560
+ function expressionAsBinary(
561
+ node: ts.Expression,
562
+ sourceFile: ts.SourceFile,
563
+ span: SourceSpan,
564
+ ): MacroBinaryExprPattern | null {
565
+ const unwrapped = unwrapParenthesizedExpressionNode(node);
566
+ if (!ts.isBinaryExpression(unwrapped)) {
567
+ return null;
568
+ }
569
+ return {
570
+ left: createExprSyntaxFromNode(
571
+ unwrapped.left,
572
+ sourceFile,
573
+ nodeSpan(unwrapped.left, sourceFile, span),
574
+ ),
575
+ operator: ts.tokenToString(unwrapped.operatorToken.kind) ??
576
+ unwrapped.operatorToken.getText(sourceFile),
577
+ right: createExprSyntaxFromNode(
578
+ unwrapped.right,
579
+ sourceFile,
580
+ nodeSpan(unwrapped.right, sourceFile, span),
581
+ ),
582
+ };
583
+ }
584
+
585
+ function expressionAsConditional(
586
+ node: ts.Expression,
587
+ sourceFile: ts.SourceFile,
588
+ span: SourceSpan,
589
+ ): MacroConditionalExprPattern | null {
590
+ const unwrapped = unwrapParenthesizedExpressionNode(node);
591
+ if (!ts.isConditionalExpression(unwrapped)) {
592
+ return null;
593
+ }
594
+ return {
595
+ condition: createExprSyntaxFromNode(
596
+ unwrapped.condition,
597
+ sourceFile,
598
+ nodeSpan(unwrapped.condition, sourceFile, span),
599
+ ),
600
+ whenFalse: createExprSyntaxFromNode(
601
+ unwrapped.whenFalse,
602
+ sourceFile,
603
+ nodeSpan(unwrapped.whenFalse, sourceFile, span),
604
+ ),
605
+ whenTrue: createExprSyntaxFromNode(
606
+ unwrapped.whenTrue,
607
+ sourceFile,
608
+ nodeSpan(unwrapped.whenTrue, sourceFile, span),
609
+ ),
610
+ };
611
+ }
612
+
613
+ function expressionAsJsxElement(
614
+ node: ts.Expression,
615
+ sourceFile: ts.SourceFile,
616
+ span: SourceSpan,
617
+ ): MacroJsxElementSyntax | null {
618
+ const current = unwrapParenthesizedExpressionNode(node);
619
+ if (ts.isJsxElement(current) || ts.isJsxSelfClosingElement(current)) {
620
+ return createJsxElementSyntaxFromNode(
621
+ current,
622
+ sourceFile,
623
+ nodeSpan(current, sourceFile, span),
624
+ );
625
+ }
626
+ return null;
627
+ }
628
+
629
+ function expressionAsJsxFragment(
630
+ node: ts.Expression,
631
+ sourceFile: ts.SourceFile,
632
+ span: SourceSpan,
633
+ ): MacroJsxFragmentSyntax | null {
634
+ const current = unwrapParenthesizedExpressionNode(node);
635
+ if (ts.isJsxFragment(current)) {
636
+ return createJsxFragmentSyntaxFromNode(
637
+ current,
638
+ sourceFile,
639
+ nodeSpan(current, sourceFile, span),
640
+ );
641
+ }
642
+ return null;
643
+ }
644
+
645
+ function replaceThisNode<T extends ts.Node>(
646
+ node: T,
647
+ replacement: ExprSyntax,
648
+ ): T {
649
+ return rewriteNode(node, { replaceThisWith: replacement });
650
+ }
651
+
652
+ function cloneExpressionNode(node: ts.Expression): ts.Expression {
653
+ return synthesizeHostNode(
654
+ (ts.factory as typeof ts.factory & { cloneNode(node: ts.Expression): ts.Expression }).cloneNode(
655
+ node,
656
+ ),
657
+ );
658
+ }
659
+
660
+ function compoundAssignmentOperatorTokenForRewrite(
661
+ kind: ts.SyntaxKind,
662
+ ): ts.BinaryOperatorToken | null {
663
+ switch (kind) {
664
+ case ts.SyntaxKind.PlusEqualsToken:
665
+ return ts.factory.createToken(ts.SyntaxKind.PlusToken);
666
+ case ts.SyntaxKind.MinusEqualsToken:
667
+ return ts.factory.createToken(ts.SyntaxKind.MinusToken);
668
+ case ts.SyntaxKind.AsteriskEqualsToken:
669
+ return ts.factory.createToken(ts.SyntaxKind.AsteriskToken);
670
+ case ts.SyntaxKind.SlashEqualsToken:
671
+ return ts.factory.createToken(ts.SyntaxKind.SlashToken);
672
+ case ts.SyntaxKind.PercentEqualsToken:
673
+ return ts.factory.createToken(ts.SyntaxKind.PercentToken);
674
+ default:
675
+ return null;
676
+ }
677
+ }
678
+
679
+ function updateOperatorTokenForRewrite(kind: ts.SyntaxKind): ts.BinaryOperatorToken | null {
680
+ switch (kind) {
681
+ case ts.SyntaxKind.PlusPlusToken:
682
+ return ts.factory.createToken(ts.SyntaxKind.PlusToken);
683
+ case ts.SyntaxKind.MinusMinusToken:
684
+ return ts.factory.createToken(ts.SyntaxKind.MinusToken);
685
+ default:
686
+ return null;
687
+ }
688
+ }
689
+
690
+ function thisPropertyNameForRewrite(expression: ts.Expression): string | null {
691
+ return ts.isPropertyAccessExpression(expression) &&
692
+ expression.expression.kind === ts.SyntaxKind.ThisKeyword
693
+ ? expression.name.text
694
+ : null;
695
+ }
696
+
697
+ function rewriteNode<T extends ts.Node>(
698
+ node: T,
699
+ options: MacroSyntaxRewriteOptions,
700
+ ): T {
701
+ const replaceThisWith = options.replaceThisWith ? getHostExpression(options.replaceThisWith) : null;
702
+ const replaceCallNamed = new Map(
703
+ Object.entries(options.replaceCallNamed ?? {}).map(([name, replacement]) => [
704
+ name,
705
+ getHostExpression(replacement),
706
+ ]),
707
+ );
708
+ const replaceThisMemberWriteNamed = new Map(
709
+ Object.entries(options.replaceThisMemberWriteNamed ?? {}).map(([name, replacement]) => [
710
+ name,
711
+ getHostExpression(replacement),
712
+ ]),
713
+ );
714
+ let temporaryCount = 0;
715
+ const transformed = ts.transform(node, [(
716
+ context: ts.TransformationContext,
717
+ ) =>
718
+ (root: ts.Node) =>
719
+ ts.visitNode(root, function visit(current): ts.Node {
720
+ if (ts.isBinaryExpression(current)) {
721
+ const targetName = thisPropertyNameForRewrite(current.left);
722
+ const setterExpression = targetName
723
+ ? replaceThisMemberWriteNamed.get(targetName)
724
+ : undefined;
725
+ if (targetName && setterExpression) {
726
+ if (current.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
727
+ return synthesizeHostNode(
728
+ ts.factory.createCallExpression(
729
+ cloneExpressionNode(setterExpression),
730
+ undefined,
731
+ [ts.visitNode(current.right, visit) as ts.Expression],
732
+ ),
733
+ );
734
+ }
735
+ const operator = compoundAssignmentOperatorTokenForRewrite(current.operatorToken.kind);
736
+ if (operator) {
737
+ const receiver = replaceThisWith
738
+ ? cloneExpressionNode(replaceThisWith)
739
+ : ts.factory.createThis();
740
+ return synthesizeHostNode(
741
+ ts.factory.createCallExpression(
742
+ cloneExpressionNode(setterExpression),
743
+ undefined,
744
+ [
745
+ ts.factory.createBinaryExpression(
746
+ ts.factory.createPropertyAccessExpression(receiver, targetName),
747
+ operator,
748
+ ts.visitNode(current.right, visit) as ts.Expression,
749
+ ),
750
+ ],
751
+ ),
752
+ );
753
+ }
754
+ }
755
+ }
756
+ if (ts.isPrefixUnaryExpression(current) || ts.isPostfixUnaryExpression(current)) {
757
+ const targetName = thisPropertyNameForRewrite(current.operand);
758
+ const setterExpression = targetName
759
+ ? replaceThisMemberWriteNamed.get(targetName)
760
+ : undefined;
761
+ if (targetName && setterExpression) {
762
+ const operator = updateOperatorTokenForRewrite(current.operator);
763
+ if (operator) {
764
+ const receiver = replaceThisWith
765
+ ? cloneExpressionNode(replaceThisWith)
766
+ : ts.factory.createThis();
767
+ const currentValue = ts.factory.createPropertyAccessExpression(receiver, targetName);
768
+ const nextValue = ts.factory.createBinaryExpression(
769
+ currentValue,
770
+ operator,
771
+ ts.factory.createNumericLiteral(1),
772
+ );
773
+ if (ts.isPrefixUnaryExpression(current)) {
774
+ return synthesizeHostNode(
775
+ ts.factory.createCallExpression(
776
+ cloneExpressionNode(setterExpression),
777
+ undefined,
778
+ [nextValue],
779
+ ),
780
+ );
781
+ }
782
+ if (current.parent && ts.isExpressionStatement(current.parent)) {
783
+ return synthesizeHostNode(
784
+ ts.factory.createCallExpression(
785
+ cloneExpressionNode(setterExpression),
786
+ undefined,
787
+ [nextValue],
788
+ ),
789
+ );
790
+ }
791
+
792
+ const previousName = `__sts_prev_${temporaryCount++}`;
793
+ return synthesizeHostNode(
794
+ ts.factory.createCallExpression(
795
+ ts.factory.createParenthesizedExpression(
796
+ ts.factory.createArrowFunction(
797
+ undefined,
798
+ undefined,
799
+ [],
800
+ undefined,
801
+ ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
802
+ ts.factory.createBlock([
803
+ ts.factory.createVariableStatement(
804
+ undefined,
805
+ ts.factory.createVariableDeclarationList([
806
+ ts.factory.createVariableDeclaration(
807
+ previousName,
808
+ undefined,
809
+ undefined,
810
+ currentValue,
811
+ ),
812
+ ], ts.NodeFlags.Const),
813
+ ),
814
+ ts.factory.createExpressionStatement(
815
+ ts.factory.createCallExpression(
816
+ cloneExpressionNode(setterExpression),
817
+ undefined,
818
+ [
819
+ ts.factory.createBinaryExpression(
820
+ ts.factory.createIdentifier(previousName),
821
+ operator,
822
+ ts.factory.createNumericLiteral(1),
823
+ ),
824
+ ],
825
+ ),
826
+ ),
827
+ ts.factory.createReturnStatement(ts.factory.createIdentifier(previousName)),
828
+ ], true),
829
+ ),
830
+ ),
831
+ undefined,
832
+ [],
833
+ ),
834
+ );
835
+ }
836
+ }
837
+ }
838
+ if (replaceThisWith && current.kind === ts.SyntaxKind.ThisKeyword) {
839
+ return cloneExpressionNode(replaceThisWith);
840
+ }
841
+ if (ts.isCallExpression(current) && ts.isIdentifier(current.expression)) {
842
+ const replacementCallee = replaceCallNamed.get(current.expression.text);
843
+ if (replacementCallee) {
844
+ return synthesizeHostNode(
845
+ ts.factory.updateCallExpression(
846
+ current,
847
+ cloneExpressionNode(replacementCallee),
848
+ current.typeArguments,
849
+ current.arguments.map((argument) =>
850
+ ts.visitNode(argument, visit) as ts.Expression
851
+ ),
852
+ ),
853
+ );
854
+ }
855
+ }
856
+ return ts.visitEachChild(current, visit, context);
857
+ })]);
858
+ try {
859
+ const [rewritten] = transformed.transformed;
860
+ if (!rewritten) {
861
+ throw new Error('Macro syntax transform produced an empty result.');
862
+ }
863
+ return rewritten as T;
864
+ } finally {
865
+ transformed.dispose();
866
+ }
867
+ }
868
+
869
+ function rewriteExprSyntax(
870
+ node: ts.Expression,
871
+ sourceFile: ts.SourceFile,
872
+ span: SourceSpan,
873
+ options: MacroSyntaxRewriteOptions,
874
+ ): ExprSyntax {
875
+ return createExprSyntaxFromNode(rewriteNode(node, options), sourceFile, span);
876
+ }
877
+
878
+ function rewriteBlockSyntax(
879
+ node: ts.Block,
880
+ sourceFile: ts.SourceFile,
881
+ span: SourceSpan,
882
+ options: MacroSyntaxRewriteOptions,
883
+ ): BlockSyntax {
884
+ return createBlockSyntaxFromNode(rewriteNode(node, options), sourceFile, span);
885
+ }
886
+
887
+ function resolveThisDependenciesForClass(
888
+ node: ts.ClassDeclaration,
889
+ rootMemberNames: readonly string[],
890
+ target: ExprSyntax | BlockSyntax,
891
+ ): readonly string[] {
892
+ const rootNames = new Set(rootMemberNames);
893
+ const getters = new Map<string, ts.GetAccessorDeclaration>();
894
+ for (const member of node.members) {
895
+ if (
896
+ ts.isGetAccessorDeclaration(member) && member.name && ts.isIdentifier(member.name) &&
897
+ member.body
898
+ ) {
899
+ getters.set(member.name.text, member);
900
+ }
901
+ }
902
+
903
+ const cache = new Map<string, ReadonlySet<string>>();
904
+ function resolveMember(
905
+ memberName: string,
906
+ seen: ReadonlySet<string> = new Set(),
907
+ ): ReadonlySet<string> {
908
+ if (rootNames.has(memberName)) {
909
+ return new Set([memberName]);
910
+ }
911
+
912
+ const cached = cache.get(memberName);
913
+ if (cached) {
914
+ return cached;
915
+ }
916
+ if (seen.has(memberName)) {
917
+ return new Set();
918
+ }
919
+
920
+ const getter = getters.get(memberName);
921
+ if (!getter?.body) {
922
+ return new Set();
923
+ }
924
+
925
+ const nextSeen = new Set(seen);
926
+ nextSeen.add(memberName);
927
+ const dependencies = new Set<string>();
928
+ for (const reference of collectThisMemberReferences(getter.body)) {
929
+ for (const dependency of resolveMember(reference, nextSeen)) {
930
+ dependencies.add(dependency);
931
+ }
932
+ }
933
+ cache.set(memberName, dependencies);
934
+ return dependencies;
935
+ }
936
+
937
+ const dependencies = new Set<string>();
938
+ for (const reference of target.thisMemberReferences()) {
939
+ for (const dependency of resolveMember(reference)) {
940
+ dependencies.add(dependency);
941
+ }
942
+ }
943
+ return [...dependencies];
944
+ }
945
+
946
+ function returnedJsxNode(
947
+ node:
948
+ | ts.MethodDeclaration
949
+ | ts.GetAccessorDeclaration
950
+ | ts.SetAccessorDeclaration
951
+ | ts.FunctionDeclaration,
952
+ ): ts.JsxElement | ts.JsxSelfClosingElement | null {
953
+ if (!node.body) {
954
+ return null;
955
+ }
956
+ if (node.body.statements.length !== 1) {
957
+ return null;
958
+ }
959
+ const [statement] = node.body.statements;
960
+ if (!statement || !ts.isReturnStatement(statement) || !statement.expression) {
961
+ return null;
962
+ }
963
+ if (ts.isJsxElement(statement.expression) || ts.isJsxSelfClosingElement(statement.expression)) {
964
+ return statement.expression;
965
+ }
966
+ return null;
967
+ }
968
+
969
+ function returnedExprNode(
970
+ node:
971
+ | ts.MethodDeclaration
972
+ | ts.GetAccessorDeclaration
973
+ | ts.SetAccessorDeclaration
974
+ | ts.FunctionDeclaration,
975
+ ): ts.Expression | null {
976
+ if (!node.body) {
977
+ return null;
978
+ }
979
+ if (node.body.statements.length !== 1) {
980
+ return null;
981
+ }
982
+ const [statement] = node.body.statements;
983
+ if (!statement || !ts.isReturnStatement(statement) || !statement.expression) {
984
+ return null;
985
+ }
986
+ return statement.expression;
987
+ }
988
+
989
+ function typeElementNameText(name: ts.PropertyName | undefined): string | null {
990
+ if (!name) {
991
+ return null;
992
+ }
993
+ if (
994
+ ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNoSubstitutionTemplateLiteral(name) ||
995
+ ts.isNumericLiteral(name) || ts.isPrivateIdentifier(name)
996
+ ) {
997
+ return name.text;
998
+ }
999
+ return null;
1000
+ }
1001
+
1002
+ function createObjectTypeMemberSyntaxFromNode(
1003
+ node: ts.TypeElement,
1004
+ sourceFile: ts.SourceFile,
1005
+ fallbackSpan: SourceSpan,
1006
+ ): MacroObjectTypeMemberSyntax {
1007
+ const memberKind: MacroObjectTypeMemberSyntax['memberKind'] = ts.isPropertySignature(node)
1008
+ ? 'property_signature'
1009
+ : ts.isMethodSignature(node)
1010
+ ? 'method_signature'
1011
+ : ts.isIndexSignatureDeclaration(node)
1012
+ ? 'index_signature'
1013
+ : ts.isConstructSignatureDeclaration(node)
1014
+ ? 'construct_signature'
1015
+ : 'call_signature';
1016
+
1017
+ return withHostNode(
1018
+ {
1019
+ explicitType() {
1020
+ if (
1021
+ ts.isPropertySignature(node) ||
1022
+ ts.isMethodSignature(node) ||
1023
+ ts.isIndexSignatureDeclaration(node) ||
1024
+ ts.isConstructSignatureDeclaration(node) ||
1025
+ ts.isCallSignatureDeclaration(node)
1026
+ ) {
1027
+ return node.type
1028
+ ? createTypeSyntaxFromNode(
1029
+ node.type,
1030
+ sourceFile,
1031
+ nodeSpan(node.type, sourceFile, fallbackSpan),
1032
+ )
1033
+ : null;
1034
+ }
1035
+ return null;
1036
+ },
1037
+ hasExplicitType() {
1038
+ if (ts.isPropertySignature(node) || ts.isMethodSignature(node)) {
1039
+ return node.type !== undefined;
1040
+ }
1041
+ if (ts.isIndexSignatureDeclaration(node) || ts.isConstructSignatureDeclaration(node)) {
1042
+ return node.type !== undefined;
1043
+ }
1044
+ if (ts.isCallSignatureDeclaration(node)) {
1045
+ return node.type !== undefined;
1046
+ }
1047
+ return false;
1048
+ },
1049
+ isOptional() {
1050
+ return (
1051
+ (ts.isPropertySignature(node) || ts.isMethodSignature(node)) &&
1052
+ node.questionToken !== undefined
1053
+ );
1054
+ },
1055
+ kind: 'type_member',
1056
+ memberKind,
1057
+ name: 'name' in node ? typeElementNameText(node.name) : null,
1058
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1059
+ text() {
1060
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1061
+ },
1062
+ },
1063
+ node,
1064
+ sourceFile,
1065
+ ts.EmitHint.Unspecified,
1066
+ );
1067
+ }
1068
+
1069
+ function createTypeSyntaxFromNode(
1070
+ node: ts.TypeNode,
1071
+ sourceFile: ts.SourceFile,
1072
+ fallbackSpan: SourceSpan,
1073
+ ): TypeSyntax {
1074
+ if (ts.isUnionTypeNode(node)) {
1075
+ const members = node.types.map((member) =>
1076
+ createTypeSyntaxFromNode(member, sourceFile, fallbackSpan)
1077
+ );
1078
+ let unionTypeSyntax!: MacroUnionTypeSyntax;
1079
+ unionTypeSyntax = withHostNode(
1080
+ {
1081
+ asLiteral() {
1082
+ return null;
1083
+ },
1084
+ asObjectLiteral() {
1085
+ return null;
1086
+ },
1087
+ asUnion() {
1088
+ return unionTypeSyntax;
1089
+ },
1090
+ kind: 'type',
1091
+ members,
1092
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1093
+ text() {
1094
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1095
+ },
1096
+ },
1097
+ node,
1098
+ sourceFile,
1099
+ ts.EmitHint.Unspecified,
1100
+ );
1101
+ return unionTypeSyntax;
1102
+ }
1103
+
1104
+ if (ts.isLiteralTypeNode(node)) {
1105
+ const literal = (() => {
1106
+ if (ts.isStringLiteral(node.literal) || ts.isNoSubstitutionTemplateLiteral(node.literal)) {
1107
+ return {
1108
+ literalKind: 'string' as const,
1109
+ value: node.literal.text,
1110
+ };
1111
+ }
1112
+ if (ts.isNumericLiteral(node.literal)) {
1113
+ return {
1114
+ literalKind: 'number' as const,
1115
+ value: Number(node.literal.text),
1116
+ };
1117
+ }
1118
+ if (node.literal.kind === ts.SyntaxKind.TrueKeyword) {
1119
+ return {
1120
+ literalKind: 'boolean' as const,
1121
+ value: true,
1122
+ };
1123
+ }
1124
+ if (node.literal.kind === ts.SyntaxKind.FalseKeyword) {
1125
+ return {
1126
+ literalKind: 'boolean' as const,
1127
+ value: false,
1128
+ };
1129
+ }
1130
+ if (
1131
+ ts.isPrefixUnaryExpression(node.literal) &&
1132
+ node.literal.operator === ts.SyntaxKind.MinusToken &&
1133
+ ts.isNumericLiteral(node.literal.operand)
1134
+ ) {
1135
+ return {
1136
+ literalKind: 'number' as const,
1137
+ value: -Number(node.literal.operand.text),
1138
+ };
1139
+ }
1140
+ return null;
1141
+ })();
1142
+
1143
+ if (literal) {
1144
+ let literalTypeSyntax!: MacroLiteralTypeSyntax;
1145
+ literalTypeSyntax = withHostNode(
1146
+ {
1147
+ asLiteral() {
1148
+ return literalTypeSyntax;
1149
+ },
1150
+ asObjectLiteral() {
1151
+ return null;
1152
+ },
1153
+ asUnion() {
1154
+ return null;
1155
+ },
1156
+ kind: 'type',
1157
+ literalKind: literal.literalKind,
1158
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1159
+ text() {
1160
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1161
+ },
1162
+ value: literal.value,
1163
+ },
1164
+ node,
1165
+ sourceFile,
1166
+ ts.EmitHint.Unspecified,
1167
+ );
1168
+ return literalTypeSyntax;
1169
+ }
1170
+ }
1171
+
1172
+ if (ts.isTypeLiteralNode(node)) {
1173
+ const members = node.members.map((member) =>
1174
+ createObjectTypeMemberSyntaxFromNode(member, sourceFile, fallbackSpan)
1175
+ );
1176
+ let objectTypeSyntax!: MacroObjectTypeSyntax;
1177
+ objectTypeSyntax = withHostNode(
1178
+ {
1179
+ asLiteral() {
1180
+ return null;
1181
+ },
1182
+ asObjectLiteral() {
1183
+ return objectTypeSyntax;
1184
+ },
1185
+ asUnion() {
1186
+ return null;
1187
+ },
1188
+ kind: 'type',
1189
+ members,
1190
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1191
+ text() {
1192
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1193
+ },
1194
+ },
1195
+ node,
1196
+ sourceFile,
1197
+ ts.EmitHint.Unspecified,
1198
+ );
1199
+ return objectTypeSyntax;
1200
+ }
1201
+
1202
+ return withHostNode(
1203
+ {
1204
+ asLiteral() {
1205
+ return null;
1206
+ },
1207
+ asObjectLiteral() {
1208
+ return null;
1209
+ },
1210
+ asUnion() {
1211
+ return null;
1212
+ },
1213
+ kind: 'type',
1214
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1215
+ text() {
1216
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1217
+ },
1218
+ },
1219
+ node,
1220
+ sourceFile,
1221
+ ts.EmitHint.Unspecified,
1222
+ );
1223
+ }
1224
+
1225
+ function createTypeSyntaxFromText(
1226
+ fileName: string,
1227
+ span: SourceSpan,
1228
+ text: string,
1229
+ ): TypeSyntax {
1230
+ const sourceFile = ts.createSourceFile(
1231
+ fileName,
1232
+ `type __MacroType = ${text};`,
1233
+ ts.ScriptTarget.Latest,
1234
+ true,
1235
+ scriptKindForHostFile(fileName),
1236
+ );
1237
+ ensureNoParseDiagnostics(
1238
+ sourceFile,
1239
+ 'Generated macro type syntax must parse as a valid type expression.',
1240
+ );
1241
+ const statement = sourceFile.statements[0];
1242
+ if (!statement || !ts.isTypeAliasDeclaration(statement)) {
1243
+ throw new Error('Generated macro type syntax must parse as a valid type expression.');
1244
+ }
1245
+ return createTypeSyntaxFromNode(
1246
+ statement.type,
1247
+ setSourceFileOffset(sourceFile, span.start - statement.type.getStart(sourceFile, false)),
1248
+ span,
1249
+ );
1250
+ }
1251
+
1252
+ function createTypeParameterSyntaxFromNode(
1253
+ node: ts.TypeParameterDeclaration,
1254
+ sourceFile: ts.SourceFile,
1255
+ fallbackSpan: SourceSpan,
1256
+ ): MacroTypeParameterSyntax {
1257
+ return withHostNode(
1258
+ {
1259
+ constraint() {
1260
+ return node.constraint
1261
+ ? createTypeSyntaxFromNode(
1262
+ node.constraint,
1263
+ sourceFile,
1264
+ nodeSpan(node.constraint, sourceFile, fallbackSpan),
1265
+ )
1266
+ : null;
1267
+ },
1268
+ defaultType() {
1269
+ return node.default
1270
+ ? createTypeSyntaxFromNode(
1271
+ node.default,
1272
+ sourceFile,
1273
+ nodeSpan(node.default, sourceFile, fallbackSpan),
1274
+ )
1275
+ : null;
1276
+ },
1277
+ kind: 'type_parameter',
1278
+ name: node.name.text,
1279
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1280
+ text() {
1281
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1282
+ },
1283
+ },
1284
+ node,
1285
+ sourceFile,
1286
+ ts.EmitHint.Unspecified,
1287
+ );
1288
+ }
1289
+
1290
+ function createParameterSyntaxFromNode(
1291
+ node: ts.ParameterDeclaration,
1292
+ sourceFile: ts.SourceFile,
1293
+ fallbackSpan: SourceSpan,
1294
+ ): MacroParameterSyntax {
1295
+ function collectBindingIdentifiers(
1296
+ name: ts.BindingName,
1297
+ ): readonly MacroBindingIdentifierSyntax[] {
1298
+ if (ts.isIdentifier(name)) {
1299
+ return [withHostNode(
1300
+ {
1301
+ kind: 'binding_identifier',
1302
+ name: name.text,
1303
+ span: nodeSpan(name, sourceFile, fallbackSpan),
1304
+ },
1305
+ name,
1306
+ sourceFile,
1307
+ ts.EmitHint.Unspecified,
1308
+ )];
1309
+ }
1310
+
1311
+ const bindings: MacroBindingIdentifierSyntax[] = [];
1312
+ for (const element of name.elements) {
1313
+ if (!ts.isBindingElement(element)) {
1314
+ continue;
1315
+ }
1316
+ bindings.push(...collectBindingIdentifiers(element.name));
1317
+ }
1318
+ return bindings;
1319
+ }
1320
+
1321
+ return withHostNode(
1322
+ {
1323
+ bindingIdentifiers() {
1324
+ return collectBindingIdentifiers(node.name);
1325
+ },
1326
+ explicitType() {
1327
+ return node.type ? createTypeSyntaxFromNode(node.type, sourceFile, fallbackSpan) : null;
1328
+ },
1329
+ hasDefault() {
1330
+ return node.initializer !== undefined;
1331
+ },
1332
+ hasExplicitType() {
1333
+ return node.type !== undefined;
1334
+ },
1335
+ isRest() {
1336
+ return node.dotDotDotToken !== undefined;
1337
+ },
1338
+ kind: 'parameter',
1339
+ name: ts.isIdentifier(node.name) ? node.name.text : null,
1340
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1341
+ text() {
1342
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1343
+ },
1344
+ },
1345
+ node,
1346
+ sourceFile,
1347
+ ts.EmitHint.Unspecified,
1348
+ );
1349
+ }
1350
+
1351
+ function createJsxTextSyntaxFromNode(
1352
+ node: ts.JsxText,
1353
+ sourceFile: ts.SourceFile,
1354
+ fallbackSpan: SourceSpan,
1355
+ ): MacroJsxTextSyntax {
1356
+ return withHostNode(
1357
+ {
1358
+ kind: 'jsx_text',
1359
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1360
+ text() {
1361
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1362
+ },
1363
+ value: node.getText(sourceFile),
1364
+ },
1365
+ node,
1366
+ sourceFile,
1367
+ ts.EmitHint.Unspecified,
1368
+ );
1369
+ }
1370
+
1371
+ function createJsxExpressionSyntaxFromNode(
1372
+ node: ts.JsxExpression,
1373
+ sourceFile: ts.SourceFile,
1374
+ fallbackSpan: SourceSpan,
1375
+ ): MacroJsxExpressionSyntax {
1376
+ return withHostNode(
1377
+ {
1378
+ kind: 'jsx_expr',
1379
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1380
+ expression() {
1381
+ return node.expression
1382
+ ? createExprSyntaxFromNode(
1383
+ node.expression,
1384
+ sourceFile,
1385
+ nodeSpan(node.expression, sourceFile, fallbackSpan),
1386
+ )
1387
+ : null;
1388
+ },
1389
+ text() {
1390
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1391
+ },
1392
+ },
1393
+ node,
1394
+ sourceFile,
1395
+ ts.EmitHint.Unspecified,
1396
+ );
1397
+ }
1398
+
1399
+ function createJsxChildSyntaxFromNode(
1400
+ node: ts.JsxChild,
1401
+ sourceFile: ts.SourceFile,
1402
+ fallbackSpan: SourceSpan,
1403
+ ): readonly MacroAnyJsxChildSyntax[] {
1404
+ if (ts.isJsxText(node)) {
1405
+ return [createJsxTextSyntaxFromNode(node, sourceFile, fallbackSpan)];
1406
+ }
1407
+ if (ts.isJsxExpression(node)) {
1408
+ return [createJsxExpressionSyntaxFromNode(node, sourceFile, fallbackSpan)];
1409
+ }
1410
+ if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node)) {
1411
+ return [createJsxElementSyntaxFromNode(node, sourceFile, fallbackSpan)];
1412
+ }
1413
+ if (ts.isJsxFragment(node)) {
1414
+ return [createJsxFragmentSyntaxFromNode(node, sourceFile, fallbackSpan)];
1415
+ }
1416
+ return [];
1417
+ }
1418
+
1419
+ function createJsxChildren(
1420
+ children: readonly ts.JsxChild[],
1421
+ sourceFile: ts.SourceFile,
1422
+ fallbackSpan: SourceSpan,
1423
+ ): readonly MacroAnyJsxChildSyntax[] {
1424
+ return children.flatMap((child) => createJsxChildSyntaxFromNode(child, sourceFile, fallbackSpan));
1425
+ }
1426
+
1427
+ function createJsxAttributeSyntaxFromNode(
1428
+ node: ts.JsxAttribute,
1429
+ sourceFile: ts.SourceFile,
1430
+ fallbackSpan: SourceSpan,
1431
+ ): MacroJsxAttributeSyntax {
1432
+ return withHostNode(
1433
+ {
1434
+ kind: 'jsx_attribute',
1435
+ name: ts.isIdentifier(node.name) ? node.name.text : node.name.getText(sourceFile),
1436
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1437
+ expression() {
1438
+ if (
1439
+ !node.initializer || !ts.isJsxExpression(node.initializer) || !node.initializer.expression
1440
+ ) {
1441
+ return null;
1442
+ }
1443
+ return createExprSyntaxFromNode(
1444
+ node.initializer.expression,
1445
+ sourceFile,
1446
+ nodeSpan(node.initializer.expression, sourceFile, fallbackSpan),
1447
+ );
1448
+ },
1449
+ stringValue() {
1450
+ return node.initializer && ts.isStringLiteral(node.initializer)
1451
+ ? node.initializer.text
1452
+ : null;
1453
+ },
1454
+ text() {
1455
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1456
+ },
1457
+ },
1458
+ node,
1459
+ sourceFile,
1460
+ ts.EmitHint.Unspecified,
1461
+ );
1462
+ }
1463
+
1464
+ function createJsxSpreadAttributeSyntaxFromNode(
1465
+ node: ts.JsxSpreadAttribute,
1466
+ sourceFile: ts.SourceFile,
1467
+ fallbackSpan: SourceSpan,
1468
+ ): MacroJsxSpreadAttributeSyntax {
1469
+ return withHostNode(
1470
+ {
1471
+ kind: 'jsx_spread_attribute',
1472
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1473
+ expression() {
1474
+ return createExprSyntaxFromNode(
1475
+ node.expression,
1476
+ sourceFile,
1477
+ nodeSpan(node.expression, sourceFile, fallbackSpan),
1478
+ );
1479
+ },
1480
+ text() {
1481
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1482
+ },
1483
+ },
1484
+ node,
1485
+ sourceFile,
1486
+ ts.EmitHint.Unspecified,
1487
+ );
1488
+ }
1489
+
1490
+ export function createJsxElementSyntaxFromNode(
1491
+ node: ts.JsxElement | ts.JsxSelfClosingElement,
1492
+ sourceFile: ts.SourceFile,
1493
+ fallbackSpan: SourceSpan,
1494
+ ): MacroJsxElementSyntax {
1495
+ const opening = ts.isJsxElement(node) ? node.openingElement : node;
1496
+ const tagName = ts.isIdentifier(opening.tagName) ? opening.tagName.text : null;
1497
+ const attributes = () =>
1498
+ opening.attributes.properties.map((property): MacroAnyJsxAttributeSyntax =>
1499
+ ts.isJsxAttribute(property)
1500
+ ? createJsxAttributeSyntaxFromNode(property, sourceFile, fallbackSpan)
1501
+ : createJsxSpreadAttributeSyntaxFromNode(property, sourceFile, fallbackSpan)
1502
+ );
1503
+ return withHostNode(
1504
+ {
1505
+ attribute(name: string) {
1506
+ return attributes().find((attribute) =>
1507
+ attribute.kind === 'jsx_attribute' && attribute.name === name
1508
+ ) ?? null;
1509
+ },
1510
+ attributes,
1511
+ children() {
1512
+ if (ts.isJsxSelfClosingElement(node)) {
1513
+ return [];
1514
+ }
1515
+ return createJsxChildren(node.children, sourceFile, fallbackSpan);
1516
+ },
1517
+ kind: 'jsx_element',
1518
+ selfClosing: ts.isJsxSelfClosingElement(node),
1519
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1520
+ tagName,
1521
+ text() {
1522
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1523
+ },
1524
+ },
1525
+ node,
1526
+ sourceFile,
1527
+ ts.EmitHint.Unspecified,
1528
+ );
1529
+ }
1530
+
1531
+ function createJsxFragmentSyntaxFromNode(
1532
+ node: ts.JsxFragment,
1533
+ sourceFile: ts.SourceFile,
1534
+ fallbackSpan: SourceSpan,
1535
+ ): MacroJsxFragmentSyntax {
1536
+ return withHostNode(
1537
+ {
1538
+ children() {
1539
+ return createJsxChildren(node.children, sourceFile, fallbackSpan);
1540
+ },
1541
+ kind: 'jsx_fragment',
1542
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1543
+ text() {
1544
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1545
+ },
1546
+ },
1547
+ node,
1548
+ sourceFile,
1549
+ ts.EmitHint.Unspecified,
1550
+ );
1551
+ }
1552
+
1553
+ function createFunctionExprSyntaxFromNode(
1554
+ node: ts.ArrowFunction | ts.FunctionExpression,
1555
+ sourceFile: ts.SourceFile,
1556
+ fallbackSpan: SourceSpan,
1557
+ ): MacroFunctionExprSyntax {
1558
+ const span = nodeSpan(node, sourceFile, fallbackSpan);
1559
+ const syntax: MacroFunctionExprSyntax = withHostNode(
1560
+ {
1561
+ asArrayLiteral() {
1562
+ return null;
1563
+ },
1564
+ asBinary() {
1565
+ return null;
1566
+ },
1567
+ asCall() {
1568
+ return null;
1569
+ },
1570
+ asConditional() {
1571
+ return null;
1572
+ },
1573
+ asFunction() {
1574
+ return syntax;
1575
+ },
1576
+ asIdentifier() {
1577
+ return null;
1578
+ },
1579
+ asInvocation() {
1580
+ return null;
1581
+ },
1582
+ asJsxElement() {
1583
+ return null;
1584
+ },
1585
+ asJsxFragment() {
1586
+ return null;
1587
+ },
1588
+ asPropertyAccess() {
1589
+ return null;
1590
+ },
1591
+ body() {
1592
+ return ts.isBlock(node.body)
1593
+ ? createBlockSyntaxFromNode(node.body, sourceFile, nodeSpan(node.body, sourceFile, span))
1594
+ : null;
1595
+ },
1596
+ containsCallNamed(name: string) {
1597
+ return containsCallNamed(node, name);
1598
+ },
1599
+ functionKind: ts.isArrowFunction(node) ? 'arrow' : 'function',
1600
+ hasAsyncModifier() {
1601
+ return node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) ??
1602
+ false;
1603
+ },
1604
+ isBooleanLiteral() {
1605
+ return false;
1606
+ },
1607
+ isNullLiteral() {
1608
+ return false;
1609
+ },
1610
+ kind: 'expr',
1611
+ parameters: node.parameters.map((parameter) =>
1612
+ createParameterSyntaxFromNode(parameter, sourceFile, nodeSpan(parameter, sourceFile, span))
1613
+ ),
1614
+ replaceThis(replacement: ExprSyntax) {
1615
+ return createExprSyntaxFromNode(replaceThisNode(node, replacement), sourceFile, span);
1616
+ },
1617
+ rewrite(options: MacroSyntaxRewriteOptions) {
1618
+ return rewriteExprSyntax(node, sourceFile, span, options);
1619
+ },
1620
+ returnedExpr() {
1621
+ if (ts.isBlock(node.body)) {
1622
+ if (node.body.statements.length !== 1) {
1623
+ return null;
1624
+ }
1625
+ const [statement] = node.body.statements;
1626
+ if (!statement || !ts.isReturnStatement(statement) || !statement.expression) {
1627
+ return null;
1628
+ }
1629
+ return createExprSyntaxFromNode(
1630
+ statement.expression,
1631
+ sourceFile,
1632
+ nodeSpan(statement.expression, sourceFile, span),
1633
+ );
1634
+ }
1635
+ return createExprSyntaxFromNode(
1636
+ node.body,
1637
+ sourceFile,
1638
+ nodeSpan(node.body, sourceFile, span),
1639
+ );
1640
+ },
1641
+ returnedJsx() {
1642
+ return syntax.returnedExpr()?.asJsxElement() ?? null;
1643
+ },
1644
+ span,
1645
+ text() {
1646
+ return printHostNode(node, sourceFile, ts.EmitHint.Expression);
1647
+ },
1648
+ thisMemberReferences() {
1649
+ return collectThisMemberReferences(node);
1650
+ },
1651
+ typeParameterCount() {
1652
+ return node.typeParameters?.length ?? 0;
1653
+ },
1654
+ unparenthesized() {
1655
+ return syntax;
1656
+ },
1657
+ },
1658
+ node,
1659
+ sourceFile,
1660
+ ts.EmitHint.Expression,
1661
+ );
1662
+ return syntax;
1663
+ }
1664
+
1665
+ function createClassFieldSyntaxFromNode(
1666
+ node: ts.PropertyDeclaration,
1667
+ sourceFile: ts.SourceFile,
1668
+ fallbackSpan: SourceSpan,
1669
+ ): MacroClassFieldSyntax {
1670
+ return withHostNode(
1671
+ {
1672
+ explicitType() {
1673
+ return node.type
1674
+ ? createTypeSyntaxFromNode(
1675
+ node.type,
1676
+ sourceFile,
1677
+ nodeSpan(node.type, sourceFile, fallbackSpan),
1678
+ )
1679
+ : null;
1680
+ },
1681
+ hasExplicitType() {
1682
+ return node.type !== undefined;
1683
+ },
1684
+ hasModifier(name: MacroModifierName) {
1685
+ return hasModifier(node, name);
1686
+ },
1687
+ initializer() {
1688
+ return node.initializer
1689
+ ? createExprSyntaxFromNode(
1690
+ node.initializer,
1691
+ sourceFile,
1692
+ nodeSpan(node.initializer, sourceFile, fallbackSpan),
1693
+ )
1694
+ : null;
1695
+ },
1696
+ isOptional() {
1697
+ return node.questionToken !== undefined;
1698
+ },
1699
+ kind: 'class_member',
1700
+ memberKind: 'field',
1701
+ name: ts.isIdentifier(node.name) ? node.name.text : null,
1702
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1703
+ text() {
1704
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1705
+ },
1706
+ withInitializer(initializer: ExprSyntax | null) {
1707
+ return createClassFieldSyntaxFromNode(
1708
+ ts.factory.updatePropertyDeclaration(
1709
+ node,
1710
+ node.modifiers,
1711
+ node.name,
1712
+ node.questionToken ?? node.exclamationToken,
1713
+ node.type,
1714
+ initializer ? getHostExpression(initializer) : undefined,
1715
+ ),
1716
+ sourceFile,
1717
+ fallbackSpan,
1718
+ );
1719
+ },
1720
+ },
1721
+ node,
1722
+ sourceFile,
1723
+ ts.EmitHint.Unspecified,
1724
+ );
1725
+ }
1726
+
1727
+ function createClassMethodSyntaxFromNode(
1728
+ node: ts.MethodDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration,
1729
+ sourceFile: ts.SourceFile,
1730
+ fallbackSpan: SourceSpan,
1731
+ ): MacroClassMethodSyntax {
1732
+ return withHostNode(
1733
+ {
1734
+ body() {
1735
+ return node.body
1736
+ ? createBlockSyntaxFromNode(
1737
+ node.body,
1738
+ sourceFile,
1739
+ nodeSpan(node.body, sourceFile, fallbackSpan),
1740
+ )
1741
+ : null;
1742
+ },
1743
+ hasModifier(name: MacroModifierName) {
1744
+ return hasModifier(node, name);
1745
+ },
1746
+ kind: 'class_member',
1747
+ memberKind: ts.isGetAccessorDeclaration(node)
1748
+ ? 'getter'
1749
+ : ts.isSetAccessorDeclaration(node)
1750
+ ? 'setter'
1751
+ : 'method',
1752
+ name: ts.isIdentifier(node.name) ? node.name.text : null,
1753
+ parameters: node.parameters.map((parameter) =>
1754
+ createParameterSyntaxFromNode(parameter, sourceFile, fallbackSpan)
1755
+ ),
1756
+ returnedExpr() {
1757
+ const returned = returnedExprNode(node);
1758
+ return returned
1759
+ ? createExprSyntaxFromNode(
1760
+ returned,
1761
+ sourceFile,
1762
+ nodeSpan(returned, sourceFile, fallbackSpan),
1763
+ )
1764
+ : null;
1765
+ },
1766
+ returnedJsx() {
1767
+ const jsx = returnedJsxNode(node);
1768
+ return jsx
1769
+ ? createJsxElementSyntaxFromNode(jsx, sourceFile, nodeSpan(jsx, sourceFile, fallbackSpan))
1770
+ : null;
1771
+ },
1772
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1773
+ text() {
1774
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1775
+ },
1776
+ withBody(body: BlockSyntax) {
1777
+ const hostBody = getHostBlock(body);
1778
+ const updated = ts.isGetAccessorDeclaration(node)
1779
+ ? ts.factory.updateGetAccessorDeclaration(
1780
+ node,
1781
+ node.modifiers,
1782
+ node.name,
1783
+ node.parameters,
1784
+ node.type,
1785
+ hostBody,
1786
+ )
1787
+ : ts.isSetAccessorDeclaration(node)
1788
+ ? ts.factory.updateSetAccessorDeclaration(
1789
+ node,
1790
+ node.modifiers,
1791
+ node.name,
1792
+ node.parameters,
1793
+ hostBody,
1794
+ )
1795
+ : ts.factory.updateMethodDeclaration(
1796
+ node,
1797
+ node.modifiers,
1798
+ node.asteriskToken,
1799
+ node.name,
1800
+ node.questionToken,
1801
+ node.typeParameters,
1802
+ node.parameters,
1803
+ node.type,
1804
+ hostBody,
1805
+ );
1806
+ return createClassMethodSyntaxFromNode(updated, sourceFile, fallbackSpan);
1807
+ },
1808
+ },
1809
+ node,
1810
+ sourceFile,
1811
+ ts.EmitHint.Unspecified,
1812
+ );
1813
+ }
1814
+
1815
+ function createClassConstructorSyntaxFromNode(
1816
+ node: ts.ConstructorDeclaration,
1817
+ sourceFile: ts.SourceFile,
1818
+ fallbackSpan: SourceSpan,
1819
+ ): MacroClassConstructorSyntax {
1820
+ return withHostNode(
1821
+ {
1822
+ body() {
1823
+ return node.body
1824
+ ? createBlockSyntaxFromNode(
1825
+ node.body,
1826
+ sourceFile,
1827
+ nodeSpan(node.body, sourceFile, fallbackSpan),
1828
+ )
1829
+ : null;
1830
+ },
1831
+ hasModifier(name: MacroModifierName) {
1832
+ return hasModifier(node, name);
1833
+ },
1834
+ kind: 'class_member',
1835
+ memberKind: 'constructor',
1836
+ name: null,
1837
+ parameters: node.parameters.map((parameter) =>
1838
+ createParameterSyntaxFromNode(parameter, sourceFile, fallbackSpan)
1839
+ ),
1840
+ span: nodeSpan(node, sourceFile, fallbackSpan),
1841
+ text() {
1842
+ return printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
1843
+ },
1844
+ withBody(body: BlockSyntax) {
1845
+ return createClassConstructorSyntaxFromNode(
1846
+ ts.factory.updateConstructorDeclaration(
1847
+ node,
1848
+ node.modifiers,
1849
+ node.parameters,
1850
+ getHostBlock(body),
1851
+ ),
1852
+ sourceFile,
1853
+ fallbackSpan,
1854
+ );
1855
+ },
1856
+ },
1857
+ node,
1858
+ sourceFile,
1859
+ ts.EmitHint.Unspecified,
1860
+ );
1861
+ }
1862
+
1863
+ function createClassMemberSyntaxFromNode(
1864
+ node: ts.ClassElement,
1865
+ sourceFile: ts.SourceFile,
1866
+ fallbackSpan: SourceSpan,
1867
+ ): MacroAnyClassMemberSyntax | null {
1868
+ if (ts.isPropertyDeclaration(node)) {
1869
+ return createClassFieldSyntaxFromNode(node, sourceFile, fallbackSpan);
1870
+ }
1871
+ if (
1872
+ ts.isMethodDeclaration(node) || ts.isGetAccessorDeclaration(node) ||
1873
+ ts.isSetAccessorDeclaration(node)
1874
+ ) {
1875
+ return createClassMethodSyntaxFromNode(node, sourceFile, fallbackSpan);
1876
+ }
1877
+ if (ts.isConstructorDeclaration(node)) {
1878
+ return createClassConstructorSyntaxFromNode(node, sourceFile, fallbackSpan);
1879
+ }
1880
+ return null;
1881
+ }
1882
+
1883
+ export function createExprSyntaxFromNode(
1884
+ node: ts.Expression,
1885
+ sourceFile: ts.SourceFile,
1886
+ span: SourceSpan,
1887
+ text?: string,
1888
+ ): ExprSyntax {
1889
+ const unwrapped = unwrapParenthesizedExpressionNode(node);
1890
+ if (ts.isArrowFunction(unwrapped) || ts.isFunctionExpression(unwrapped)) {
1891
+ return createFunctionExprSyntaxFromNode(unwrapped, sourceFile, span);
1892
+ }
1893
+ return withHostNode(
1894
+ {
1895
+ asArrayLiteral() {
1896
+ return expressionAsArrayLiteral(node, sourceFile, span);
1897
+ },
1898
+ asBinary() {
1899
+ return expressionAsBinary(node, sourceFile, span);
1900
+ },
1901
+ asCall() {
1902
+ return expressionAsCall(node, sourceFile, span);
1903
+ },
1904
+ asConditional() {
1905
+ return expressionAsConditional(node, sourceFile, span);
1906
+ },
1907
+ asFunction() {
1908
+ return null;
1909
+ },
1910
+ asIdentifier() {
1911
+ return expressionAsIdentifier(node);
1912
+ },
1913
+ asInvocation() {
1914
+ return expressionAsInvocation(
1915
+ span.fileName,
1916
+ span,
1917
+ text ?? printHostNode(node, sourceFile, ts.EmitHint.Expression),
1918
+ );
1919
+ },
1920
+ asJsxElement() {
1921
+ return expressionAsJsxElement(node, sourceFile, span);
1922
+ },
1923
+ asJsxFragment() {
1924
+ return expressionAsJsxFragment(node, sourceFile, span);
1925
+ },
1926
+ asPropertyAccess() {
1927
+ return expressionAsPropertyAccess(node, sourceFile, span);
1928
+ },
1929
+ containsCallNamed(name: string) {
1930
+ return containsCallNamed(node, name);
1931
+ },
1932
+ isBooleanLiteral(value: boolean) {
1933
+ const current = unwrapParenthesizedExpressionNode(node);
1934
+ return value
1935
+ ? current.kind === ts.SyntaxKind.TrueKeyword
1936
+ : current.kind === ts.SyntaxKind.FalseKeyword;
1937
+ },
1938
+ isNullLiteral() {
1939
+ return unwrapParenthesizedExpressionNode(node).kind === ts.SyntaxKind.NullKeyword;
1940
+ },
1941
+ kind: 'expr',
1942
+ replaceThis(replacement: ExprSyntax) {
1943
+ return createExprSyntaxFromNode(
1944
+ replaceThisNode(node, replacement),
1945
+ sourceFile,
1946
+ span,
1947
+ );
1948
+ },
1949
+ rewrite(options: MacroSyntaxRewriteOptions) {
1950
+ return rewriteExprSyntax(node, sourceFile, span, options);
1951
+ },
1952
+ span,
1953
+ text() {
1954
+ return text ?? printHostNode(node, sourceFile, ts.EmitHint.Expression);
1955
+ },
1956
+ thisMemberReferences() {
1957
+ return collectThisMemberReferences(node);
1958
+ },
1959
+ unparenthesized() {
1960
+ return createExprSyntaxFromNode(unwrapParenthesizedExpressionNode(node), sourceFile, span);
1961
+ },
1962
+ },
1963
+ node,
1964
+ sourceFile,
1965
+ ts.EmitHint.Expression,
1966
+ );
1967
+ }
1968
+
1969
+ export function createBareExprSyntax(
1970
+ span: SourceSpan,
1971
+ text: string,
1972
+ ): ExprSyntax {
1973
+ return {
1974
+ asArrayLiteral() {
1975
+ return null;
1976
+ },
1977
+ asBinary() {
1978
+ return null;
1979
+ },
1980
+ asCall() {
1981
+ return null;
1982
+ },
1983
+ asConditional() {
1984
+ return null;
1985
+ },
1986
+ asFunction() {
1987
+ return null;
1988
+ },
1989
+ asIdentifier() {
1990
+ return null;
1991
+ },
1992
+ asInvocation() {
1993
+ return expressionAsInvocation(span.fileName, span, text);
1994
+ },
1995
+ asJsxElement() {
1996
+ return null;
1997
+ },
1998
+ asJsxFragment() {
1999
+ return null;
2000
+ },
2001
+ asPropertyAccess() {
2002
+ return null;
2003
+ },
2004
+ containsCallNamed() {
2005
+ return false;
2006
+ },
2007
+ isBooleanLiteral() {
2008
+ return false;
2009
+ },
2010
+ isNullLiteral() {
2011
+ return false;
2012
+ },
2013
+ kind: 'expr',
2014
+ replaceThis() {
2015
+ return this;
2016
+ },
2017
+ rewrite() {
2018
+ return this;
2019
+ },
2020
+ span,
2021
+ text() {
2022
+ return text;
2023
+ },
2024
+ thisMemberReferences() {
2025
+ return [];
2026
+ },
2027
+ unparenthesized() {
2028
+ return this;
2029
+ },
2030
+ };
2031
+ }
2032
+
2033
+ export function createArgumentSyntax(
2034
+ index: number,
2035
+ node: ts.Expression,
2036
+ sourceFile: ts.SourceFile,
2037
+ span: SourceSpan,
2038
+ text: string,
2039
+ ): MacroArgumentView {
2040
+ return withHostNode(
2041
+ {
2042
+ asArrayLiteral() {
2043
+ return expressionAsArrayLiteral(node, sourceFile, span);
2044
+ },
2045
+ asBinary() {
2046
+ return expressionAsBinary(node, sourceFile, span);
2047
+ },
2048
+ asCall() {
2049
+ return expressionAsCall(node, sourceFile, span);
2050
+ },
2051
+ asConditional() {
2052
+ return expressionAsConditional(node, sourceFile, span);
2053
+ },
2054
+ asFunction() {
2055
+ const current = unwrapParenthesizedExpressionNode(node);
2056
+ return ts.isArrowFunction(current) || ts.isFunctionExpression(current)
2057
+ ? createFunctionExprSyntaxFromNode(current, sourceFile, span)
2058
+ : null;
2059
+ },
2060
+ asIdentifier() {
2061
+ return expressionAsIdentifier(node);
2062
+ },
2063
+ asInvocation() {
2064
+ return expressionAsInvocation(span.fileName, span, text);
2065
+ },
2066
+ asJsxElement() {
2067
+ return expressionAsJsxElement(node, sourceFile, span);
2068
+ },
2069
+ asJsxFragment() {
2070
+ return expressionAsJsxFragment(node, sourceFile, span);
2071
+ },
2072
+ asPropertyAccess() {
2073
+ return expressionAsPropertyAccess(node, sourceFile, span);
2074
+ },
2075
+ containsCallNamed(name: string) {
2076
+ return containsCallNamed(node, name);
2077
+ },
2078
+ index,
2079
+ isBooleanLiteral(value: boolean) {
2080
+ const current = unwrapParenthesizedExpressionNode(node);
2081
+ return value
2082
+ ? current.kind === ts.SyntaxKind.TrueKeyword
2083
+ : current.kind === ts.SyntaxKind.FalseKeyword;
2084
+ },
2085
+ isNullLiteral() {
2086
+ return unwrapParenthesizedExpressionNode(node).kind === ts.SyntaxKind.NullKeyword;
2087
+ },
2088
+ kind: 'expr',
2089
+ replaceThis(replacement: ExprSyntax) {
2090
+ return createExprSyntaxFromNode(
2091
+ replaceThisNode(node, replacement),
2092
+ sourceFile,
2093
+ span,
2094
+ );
2095
+ },
2096
+ rewrite(options: MacroSyntaxRewriteOptions) {
2097
+ return rewriteExprSyntax(node, sourceFile, span, options);
2098
+ },
2099
+ span,
2100
+ text() {
2101
+ return text;
2102
+ },
2103
+ thisMemberReferences() {
2104
+ return collectThisMemberReferences(node);
2105
+ },
2106
+ unparenthesized() {
2107
+ return createExprSyntaxFromNode(unwrapParenthesizedExpressionNode(node), sourceFile, span);
2108
+ },
2109
+ },
2110
+ node,
2111
+ sourceFile,
2112
+ ts.EmitHint.Expression,
2113
+ );
2114
+ }
2115
+
2116
+ export function createBareArgumentSyntax(
2117
+ index: number,
2118
+ span: SourceSpan,
2119
+ text: string,
2120
+ ): MacroArgumentView {
2121
+ const expr = createBareExprSyntax(span, text);
2122
+ return {
2123
+ asArrayLiteral: expr.asArrayLiteral,
2124
+ asBinary: expr.asBinary,
2125
+ asCall: expr.asCall,
2126
+ asConditional: expr.asConditional,
2127
+ asFunction: expr.asFunction,
2128
+ asIdentifier: expr.asIdentifier,
2129
+ asInvocation: expr.asInvocation,
2130
+ asJsxElement: expr.asJsxElement,
2131
+ asJsxFragment: expr.asJsxFragment,
2132
+ asPropertyAccess: expr.asPropertyAccess,
2133
+ containsCallNamed: expr.containsCallNamed,
2134
+ index,
2135
+ isBooleanLiteral: expr.isBooleanLiteral,
2136
+ isNullLiteral: expr.isNullLiteral,
2137
+ kind: 'expr',
2138
+ replaceThis: expr.replaceThis,
2139
+ rewrite: expr.rewrite,
2140
+ span,
2141
+ text() {
2142
+ return text;
2143
+ },
2144
+ thisMemberReferences: expr.thisMemberReferences,
2145
+ unparenthesized: expr.unparenthesized,
2146
+ };
2147
+ }
2148
+
2149
+ function parseHostExpressionWithNestedMacroFallback(
2150
+ fileName: string,
2151
+ text: string,
2152
+ message: string,
2153
+ ): ts.Expression {
2154
+ try {
2155
+ return parseHostExpression(fileName, text, message);
2156
+ } catch {
2157
+ return parseHostExpression(
2158
+ fileName,
2159
+ neutralizeNestedMacrosForHostParse(fileName, text),
2160
+ message,
2161
+ );
2162
+ }
2163
+ }
2164
+
2165
+ export function createArgumentSyntaxFromText(
2166
+ index: number,
2167
+ fileName: string,
2168
+ span: SourceSpan,
2169
+ text: string,
2170
+ ): MacroArgumentView {
2171
+ try {
2172
+ const node = parseHostExpressionWithNestedMacroFallback(
2173
+ fileName,
2174
+ text,
2175
+ 'Macro expression operands must parse as exactly one host-language expression.',
2176
+ );
2177
+ const sourceFile = setSourceFileOffset(
2178
+ node.getSourceFile(),
2179
+ span.start - node.getStart(node.getSourceFile(), false),
2180
+ );
2181
+ return createArgumentSyntax(index, node, sourceFile, span, text);
2182
+ } catch {
2183
+ return createBareArgumentSyntax(index, span, text);
2184
+ }
2185
+ }
2186
+
2187
+ export function createStmtSyntaxFromNode(
2188
+ node: ts.Statement,
2189
+ sourceFile: ts.SourceFile,
2190
+ span: SourceSpan,
2191
+ text?: string,
2192
+ ): StmtSyntax {
2193
+ return withHostNode(
2194
+ {
2195
+ kind: 'stmt',
2196
+ span,
2197
+ text() {
2198
+ return text ?? printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
2199
+ },
2200
+ },
2201
+ node,
2202
+ sourceFile,
2203
+ ts.EmitHint.Unspecified,
2204
+ );
2205
+ }
2206
+
2207
+ export function createBlockSyntaxFromNode(
2208
+ node: ts.Block,
2209
+ sourceFile: ts.SourceFile,
2210
+ span: SourceSpan,
2211
+ text?: string,
2212
+ ): BlockSyntax {
2213
+ return withHostNode(
2214
+ {
2215
+ containsCallNamed(name: string) {
2216
+ return containsCallNamed(node, name);
2217
+ },
2218
+ kind: 'block',
2219
+ replaceThis(replacement: ExprSyntax) {
2220
+ return createBlockSyntaxFromNode(
2221
+ replaceThisNode(node, replacement),
2222
+ sourceFile,
2223
+ span,
2224
+ );
2225
+ },
2226
+ rewrite(options: MacroSyntaxRewriteOptions) {
2227
+ return rewriteBlockSyntax(node, sourceFile, span, options);
2228
+ },
2229
+ span,
2230
+ statements: node.statements.map((statement) =>
2231
+ createStmtSyntaxFromNode(statement, sourceFile, span)
2232
+ ),
2233
+ text() {
2234
+ return text ?? printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
2235
+ },
2236
+ thisMemberReferences() {
2237
+ return collectThisMemberReferences(node);
2238
+ },
2239
+ },
2240
+ node,
2241
+ sourceFile,
2242
+ ts.EmitHint.Unspecified,
2243
+ );
2244
+ }
2245
+
2246
+ export function buildBlockSyntax(
2247
+ fileName: string,
2248
+ statements: readonly StmtSyntax[],
2249
+ ): BlockSyntax {
2250
+ const sourceFile = createBuildSourceFile(fileName);
2251
+ const block = synthesizeHostNode(ts.factory.createBlock(
2252
+ statements.map((statement) => synthesizeHostNode(getHostStatement(statement))),
2253
+ true,
2254
+ ));
2255
+ return createBlockSyntaxFromNode(block, sourceFile, {
2256
+ fileName,
2257
+ start: 0,
2258
+ end: 0,
2259
+ });
2260
+ }
2261
+
2262
+ export function buildIdentifierExprSyntax(fileName: string, name: string): ExprSyntax {
2263
+ return createBuiltExprSyntax(fileName, ts.factory.createIdentifier(name));
2264
+ }
2265
+
2266
+ export function buildThisExprSyntax(fileName: string): ExprSyntax {
2267
+ return createBuiltExprSyntax(fileName, ts.factory.createThis());
2268
+ }
2269
+
2270
+ export function buildStringLiteralExprSyntax(fileName: string, value: string): ExprSyntax {
2271
+ return createBuiltExprSyntax(fileName, ts.factory.createStringLiteral(value));
2272
+ }
2273
+
2274
+ export function buildNumberLiteralExprSyntax(fileName: string, value: number): ExprSyntax {
2275
+ if (value < 0) {
2276
+ return createBuiltExprSyntax(
2277
+ fileName,
2278
+ ts.factory.createPrefixUnaryExpression(
2279
+ ts.SyntaxKind.MinusToken,
2280
+ ts.factory.createNumericLiteral(Math.abs(value)),
2281
+ ),
2282
+ );
2283
+ }
2284
+ return createBuiltExprSyntax(fileName, ts.factory.createNumericLiteral(value));
2285
+ }
2286
+
2287
+ export function buildBooleanLiteralExprSyntax(fileName: string, value: boolean): ExprSyntax {
2288
+ return createBuiltExprSyntax(
2289
+ fileName,
2290
+ value ? ts.factory.createTrue() : ts.factory.createFalse(),
2291
+ );
2292
+ }
2293
+
2294
+ export function buildNullLiteralExprSyntax(fileName: string): ExprSyntax {
2295
+ return createBuiltExprSyntax(fileName, ts.factory.createNull());
2296
+ }
2297
+
2298
+ export function buildPropertyAccessExprSyntax(
2299
+ fileName: string,
2300
+ object: ExprSyntax,
2301
+ name: string,
2302
+ ): ExprSyntax {
2303
+ return createBuiltExprSyntax(
2304
+ fileName,
2305
+ ts.factory.createPropertyAccessExpression(getHostExpression(object), name),
2306
+ );
2307
+ }
2308
+
2309
+ export function buildCallExprSyntax(
2310
+ fileName: string,
2311
+ callee: ExprSyntax,
2312
+ args: readonly ExprSyntax[],
2313
+ ): ExprSyntax {
2314
+ return createBuiltExprSyntax(
2315
+ fileName,
2316
+ ts.factory.createCallExpression(
2317
+ getHostExpression(callee),
2318
+ undefined,
2319
+ args.map((arg) => getHostExpression(arg)),
2320
+ ),
2321
+ );
2322
+ }
2323
+
2324
+ export function buildElementAccessExprSyntax(
2325
+ fileName: string,
2326
+ object: ExprSyntax,
2327
+ index: ExprSyntax,
2328
+ ): ExprSyntax {
2329
+ return createBuiltExprSyntax(
2330
+ fileName,
2331
+ ts.factory.createElementAccessExpression(
2332
+ getHostExpression(object),
2333
+ getHostExpression(index),
2334
+ ),
2335
+ );
2336
+ }
2337
+
2338
+ export function buildNewExprSyntax(
2339
+ fileName: string,
2340
+ callee: ExprSyntax,
2341
+ args: readonly ExprSyntax[],
2342
+ ): ExprSyntax {
2343
+ return createBuiltExprSyntax(
2344
+ fileName,
2345
+ ts.factory.createNewExpression(
2346
+ getHostExpression(callee),
2347
+ undefined,
2348
+ args.map((arg) => getHostExpression(arg)),
2349
+ ),
2350
+ );
2351
+ }
2352
+
2353
+ export function buildBinaryExprSyntax(
2354
+ fileName: string,
2355
+ left: ExprSyntax,
2356
+ operator: MacroBinaryOperator,
2357
+ right: ExprSyntax,
2358
+ ): ExprSyntax {
2359
+ return createBuiltExprSyntax(
2360
+ fileName,
2361
+ ts.factory.createBinaryExpression(
2362
+ getHostExpression(left),
2363
+ createBinaryOperatorToken(operator),
2364
+ getHostExpression(right),
2365
+ ),
2366
+ );
2367
+ }
2368
+
2369
+ export function buildAssignmentExprSyntax(
2370
+ fileName: string,
2371
+ target: ExprSyntax,
2372
+ value: ExprSyntax,
2373
+ ): ExprSyntax {
2374
+ return buildBinaryExprSyntax(fileName, target, '=', value);
2375
+ }
2376
+
2377
+ export function buildUnaryExprSyntax(
2378
+ fileName: string,
2379
+ operator: MacroUnaryOperator,
2380
+ value: ExprSyntax,
2381
+ ): ExprSyntax {
2382
+ return createBuiltExprSyntax(
2383
+ fileName,
2384
+ ts.factory.createPrefixUnaryExpression(
2385
+ createUnaryOperatorToken(operator),
2386
+ getHostExpression(value),
2387
+ ),
2388
+ );
2389
+ }
2390
+
2391
+ export function buildArrowFunctionExprSyntax(
2392
+ fileName: string,
2393
+ parameters: readonly (string | MacroParameterBuildOptions)[],
2394
+ body: BlockSyntax | ExprSyntax,
2395
+ ): ExprSyntax {
2396
+ return createBuiltExprSyntax(
2397
+ fileName,
2398
+ ts.factory.createArrowFunction(
2399
+ undefined,
2400
+ undefined,
2401
+ createParameterDeclarations(fileName, parameters),
2402
+ undefined,
2403
+ ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
2404
+ body.kind === 'block' ? cloneBlockNode(body) : getHostExpression(body),
2405
+ ),
2406
+ );
2407
+ }
2408
+
2409
+ export function buildObjectLiteralExprSyntax(
2410
+ fileName: string,
2411
+ members: readonly MacroObjectMemberBuildOptions[],
2412
+ ): ExprSyntax {
2413
+ return createBuiltExprSyntax(
2414
+ fileName,
2415
+ ts.factory.createObjectLiteralExpression(
2416
+ members.map((member): ts.ObjectLiteralElementLike => {
2417
+ if (member.kind === 'property') {
2418
+ return ts.factory.createPropertyAssignment(member.name, getHostExpression(member.value));
2419
+ }
2420
+ return ts.factory.createMethodDeclaration(
2421
+ undefined,
2422
+ undefined,
2423
+ member.name,
2424
+ undefined,
2425
+ undefined,
2426
+ createParameterDeclarations(fileName, member.parameters),
2427
+ member.returnType ? createTypeNodeFromText(fileName, member.returnType) : undefined,
2428
+ cloneBlockNode(member.body),
2429
+ );
2430
+ }),
2431
+ true,
2432
+ ),
2433
+ );
2434
+ }
2435
+
2436
+ export function buildOptionalMethodCallExprSyntax(
2437
+ fileName: string,
2438
+ receiver: ExprSyntax,
2439
+ name: string,
2440
+ args: readonly ExprSyntax[],
2441
+ ): ExprSyntax {
2442
+ return createBuiltExprSyntax(
2443
+ fileName,
2444
+ ts.factory.createCallChain(
2445
+ ts.factory.createPropertyAccessChain(
2446
+ getHostExpression(receiver),
2447
+ ts.factory.createToken(ts.SyntaxKind.QuestionDotToken),
2448
+ name,
2449
+ ),
2450
+ undefined,
2451
+ undefined,
2452
+ args.map((arg) => getHostExpression(arg)),
2453
+ ),
2454
+ );
2455
+ }
2456
+
2457
+ export function buildExprStmtSyntax(
2458
+ fileName: string,
2459
+ expression: ExprSyntax,
2460
+ ): StmtSyntax {
2461
+ return createBuiltStmtSyntax(
2462
+ fileName,
2463
+ ts.factory.createExpressionStatement(getHostExpression(expression)),
2464
+ );
2465
+ }
2466
+
2467
+ export function buildConstDeclStmtSyntax(
2468
+ fileName: string,
2469
+ name: string,
2470
+ initializer: ExprSyntax,
2471
+ ): StmtSyntax {
2472
+ return createBuiltStmtSyntax(
2473
+ fileName,
2474
+ ts.factory.createVariableStatement(
2475
+ undefined,
2476
+ ts.factory.createVariableDeclarationList(
2477
+ [ts.factory.createVariableDeclaration(
2478
+ name,
2479
+ undefined,
2480
+ undefined,
2481
+ getHostExpression(initializer),
2482
+ )],
2483
+ ts.NodeFlags.Const,
2484
+ ),
2485
+ ),
2486
+ );
2487
+ }
2488
+
2489
+ export function buildLetDeclStmtSyntax(
2490
+ fileName: string,
2491
+ name: string,
2492
+ initializer: ExprSyntax,
2493
+ ): StmtSyntax {
2494
+ return createBuiltStmtSyntax(
2495
+ fileName,
2496
+ ts.factory.createVariableStatement(
2497
+ undefined,
2498
+ ts.factory.createVariableDeclarationList(
2499
+ [ts.factory.createVariableDeclaration(
2500
+ name,
2501
+ undefined,
2502
+ undefined,
2503
+ getHostExpression(initializer),
2504
+ )],
2505
+ ts.NodeFlags.Let,
2506
+ ),
2507
+ ),
2508
+ );
2509
+ }
2510
+
2511
+ export function buildReturnStmtSyntax(
2512
+ fileName: string,
2513
+ expression?: ExprSyntax,
2514
+ ): StmtSyntax {
2515
+ return createBuiltStmtSyntax(
2516
+ fileName,
2517
+ ts.factory.createReturnStatement(expression ? getHostExpression(expression) : undefined),
2518
+ );
2519
+ }
2520
+
2521
+ export function buildIfStmtSyntax(
2522
+ fileName: string,
2523
+ options: MacroIfBuildOptions,
2524
+ ): StmtSyntax {
2525
+ return createBuiltStmtSyntax(
2526
+ fileName,
2527
+ ts.factory.createIfStatement(
2528
+ getHostExpression(options.condition),
2529
+ getHostBlock(buildBlockSyntax(fileName, options.thenStatements)),
2530
+ options.elseStatements
2531
+ ? getHostBlock(buildBlockSyntax(fileName, options.elseStatements))
2532
+ : undefined,
2533
+ ),
2534
+ );
2535
+ }
2536
+
2537
+ function createForInitializer(
2538
+ initializer: ExprSyntax | {
2539
+ readonly kind: 'const' | 'let';
2540
+ readonly name: string;
2541
+ readonly value: ExprSyntax;
2542
+ } | undefined,
2543
+ ): ts.ForInitializer | undefined {
2544
+ if (!initializer) {
2545
+ return undefined;
2546
+ }
2547
+ if (
2548
+ typeof initializer === 'object' &&
2549
+ 'kind' in initializer &&
2550
+ (initializer.kind === 'const' || initializer.kind === 'let')
2551
+ ) {
2552
+ const flags = initializer.kind === 'const' ? ts.NodeFlags.Const : ts.NodeFlags.Let;
2553
+ return ts.factory.createVariableDeclarationList(
2554
+ [ts.factory.createVariableDeclaration(
2555
+ initializer.name,
2556
+ undefined,
2557
+ undefined,
2558
+ getHostExpression(initializer.value),
2559
+ )],
2560
+ flags,
2561
+ );
2562
+ }
2563
+ return getHostExpression(initializer as ExprSyntax);
2564
+ }
2565
+
2566
+ export function buildForStmtSyntax(
2567
+ fileName: string,
2568
+ options: MacroForBuildOptions,
2569
+ ): StmtSyntax {
2570
+ return createBuiltStmtSyntax(
2571
+ fileName,
2572
+ ts.factory.createForStatement(
2573
+ createForInitializer(options.initializer),
2574
+ options.condition ? getHostExpression(options.condition) : undefined,
2575
+ options.increment ? getHostExpression(options.increment) : undefined,
2576
+ getHostBlock(buildBlockSyntax(fileName, options.statements)),
2577
+ ),
2578
+ );
2579
+ }
2580
+
2581
+ export function buildThrowStmtSyntax(
2582
+ fileName: string,
2583
+ expression: ExprSyntax,
2584
+ ): StmtSyntax {
2585
+ return createBuiltStmtSyntax(
2586
+ fileName,
2587
+ ts.factory.createThrowStatement(getHostExpression(expression)),
2588
+ );
2589
+ }
2590
+
2591
+ export function createDeclSyntaxFromNode(
2592
+ node:
2593
+ | ts.ClassDeclaration
2594
+ | ts.FunctionDeclaration
2595
+ | ts.InterfaceDeclaration
2596
+ | ts.TypeAliasDeclaration,
2597
+ sourceFile: ts.SourceFile,
2598
+ span: SourceSpan,
2599
+ text?: string,
2600
+ ): DeclSyntax {
2601
+ if (ts.isClassDeclaration(node)) {
2602
+ const members = () =>
2603
+ node.members.flatMap((member) => {
2604
+ const wrapped = createClassMemberSyntaxFromNode(member, sourceFile, span);
2605
+ return wrapped ? [wrapped] : [];
2606
+ });
2607
+ const syntax: MacroClassDeclSyntax = withHostNode(
2608
+ {
2609
+ asClass() {
2610
+ return syntax;
2611
+ },
2612
+ asFunction() {
2613
+ return null;
2614
+ },
2615
+ asInterface() {
2616
+ return null;
2617
+ },
2618
+ asTypeAlias() {
2619
+ return null;
2620
+ },
2621
+ declarationKind: 'class',
2622
+ hasModifier(name: MacroModifierName) {
2623
+ return hasModifier(node, name);
2624
+ },
2625
+ kind: 'decl',
2626
+ member(name: string) {
2627
+ return members().find((member) => member.name === name) ?? null;
2628
+ },
2629
+ members,
2630
+ name: node.name?.text ?? null,
2631
+ resolveThisDependencies(
2632
+ target: ExprSyntax | BlockSyntax,
2633
+ rootMemberNames: readonly string[],
2634
+ ) {
2635
+ return resolveThisDependenciesForClass(node, rootMemberNames, target);
2636
+ },
2637
+ span,
2638
+ text() {
2639
+ return text ?? printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
2640
+ },
2641
+ },
2642
+ node,
2643
+ sourceFile,
2644
+ ts.EmitHint.Unspecified,
2645
+ );
2646
+ return syntax;
2647
+ }
2648
+
2649
+ if (ts.isFunctionDeclaration(node)) {
2650
+ const syntax: MacroFunctionDeclSyntax = withHostNode(
2651
+ {
2652
+ asClass() {
2653
+ return null;
2654
+ },
2655
+ asFunction() {
2656
+ return syntax;
2657
+ },
2658
+ asInterface() {
2659
+ return null;
2660
+ },
2661
+ asTypeAlias() {
2662
+ return null;
2663
+ },
2664
+ body() {
2665
+ return node.body
2666
+ ? createBlockSyntaxFromNode(
2667
+ node.body,
2668
+ sourceFile,
2669
+ nodeSpan(node.body, sourceFile, span),
2670
+ )
2671
+ : null;
2672
+ },
2673
+ declarationKind: 'function',
2674
+ hasModifier(name: MacroModifierName) {
2675
+ return hasModifier(node, name);
2676
+ },
2677
+ kind: 'decl',
2678
+ name: node.name?.text ?? null,
2679
+ parameters: node.parameters.map((parameter) =>
2680
+ createParameterSyntaxFromNode(parameter, sourceFile, span)
2681
+ ),
2682
+ returnedExpr() {
2683
+ const returned = returnedExprNode(node);
2684
+ return returned
2685
+ ? createExprSyntaxFromNode(
2686
+ returned,
2687
+ sourceFile,
2688
+ nodeSpan(returned, sourceFile, span),
2689
+ )
2690
+ : null;
2691
+ },
2692
+ returnedJsx() {
2693
+ const jsx = returnedJsxNode(node);
2694
+ return jsx
2695
+ ? createJsxElementSyntaxFromNode(jsx, sourceFile, nodeSpan(jsx, sourceFile, span))
2696
+ : null;
2697
+ },
2698
+ span,
2699
+ text() {
2700
+ return text ?? printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
2701
+ },
2702
+ },
2703
+ node,
2704
+ sourceFile,
2705
+ ts.EmitHint.Unspecified,
2706
+ );
2707
+ return syntax;
2708
+ }
2709
+
2710
+ if (ts.isInterfaceDeclaration(node)) {
2711
+ const members = node.members.map((member) =>
2712
+ createObjectTypeMemberSyntaxFromNode(member, sourceFile, nodeSpan(member, sourceFile, span))
2713
+ );
2714
+ const typeParameters = node.typeParameters?.map((parameter) =>
2715
+ createTypeParameterSyntaxFromNode(
2716
+ parameter,
2717
+ sourceFile,
2718
+ nodeSpan(parameter, sourceFile, span),
2719
+ )
2720
+ ) ?? [];
2721
+ const extendsTypes = node.heritageClauses
2722
+ ?.filter((clause) =>
2723
+ clause.token === ts.SyntaxKind.ExtendsKeyword
2724
+ )
2725
+ .flatMap((clause) =>
2726
+ clause.types.map((type) =>
2727
+ createTypeSyntaxFromText(
2728
+ sourceFile.fileName,
2729
+ nodeSpan(type, sourceFile, span),
2730
+ printHostNode(type, sourceFile, ts.EmitHint.Unspecified),
2731
+ )
2732
+ )
2733
+ ) ?? [];
2734
+ const syntax: MacroInterfaceDeclSyntax = withHostNode(
2735
+ {
2736
+ asClass() {
2737
+ return null;
2738
+ },
2739
+ asFunction() {
2740
+ return null;
2741
+ },
2742
+ asInterface() {
2743
+ return syntax;
2744
+ },
2745
+ asTypeAlias() {
2746
+ return null;
2747
+ },
2748
+ declarationKind: 'interface',
2749
+ extendsTypes,
2750
+ hasModifier(name: MacroModifierName) {
2751
+ return hasModifier(node, name);
2752
+ },
2753
+ kind: 'decl',
2754
+ members,
2755
+ name: node.name.text,
2756
+ span,
2757
+ text() {
2758
+ return text ?? printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
2759
+ },
2760
+ typeParameters,
2761
+ },
2762
+ node,
2763
+ sourceFile,
2764
+ ts.EmitHint.Unspecified,
2765
+ );
2766
+ return syntax;
2767
+ }
2768
+
2769
+ if (ts.isTypeAliasDeclaration(node)) {
2770
+ const typeParameters = node.typeParameters?.map((parameter) =>
2771
+ createTypeParameterSyntaxFromNode(
2772
+ parameter,
2773
+ sourceFile,
2774
+ nodeSpan(parameter, sourceFile, span),
2775
+ )
2776
+ ) ?? [];
2777
+ const syntax: MacroTypeAliasDeclSyntax = withHostNode(
2778
+ {
2779
+ asClass() {
2780
+ return null;
2781
+ },
2782
+ asFunction() {
2783
+ return null;
2784
+ },
2785
+ asInterface() {
2786
+ return null;
2787
+ },
2788
+ asTypeAlias() {
2789
+ return syntax;
2790
+ },
2791
+ declarationKind: 'typeAlias',
2792
+ hasModifier(name: MacroModifierName) {
2793
+ return hasModifier(node, name);
2794
+ },
2795
+ kind: 'decl',
2796
+ name: node.name.text,
2797
+ span,
2798
+ text() {
2799
+ return text ?? printHostNode(node, sourceFile, ts.EmitHint.Unspecified);
2800
+ },
2801
+ type: createTypeSyntaxFromNode(
2802
+ node.type,
2803
+ sourceFile,
2804
+ nodeSpan(node.type, sourceFile, span),
2805
+ ),
2806
+ typeParameters,
2807
+ },
2808
+ node,
2809
+ sourceFile,
2810
+ ts.EmitHint.Unspecified,
2811
+ );
2812
+ return syntax;
2813
+ }
2814
+
2815
+ throw new Error('Expected a declaration-backed syntax node.');
2816
+ }
2817
+
2818
+ export function createInvocationSyntax(options: {
2819
+ readonly args: readonly MacroArgumentView[];
2820
+ readonly block: BlockSyntax | null;
2821
+ readonly declaration: DeclSyntax | null;
2822
+ readonly form: MacroInvocationForm;
2823
+ readonly hasBlock: boolean;
2824
+ readonly name: string;
2825
+ readonly span: SourceSpan;
2826
+ readonly text: string;
2827
+ }): InvocationSyntax {
2828
+ return {
2829
+ args: options.args,
2830
+ block: options.block,
2831
+ declaration: options.declaration,
2832
+ form: options.form,
2833
+ hasBlock: options.hasBlock,
2834
+ kind: 'invocation',
2835
+ name: options.name,
2836
+ span: options.span,
2837
+ text() {
2838
+ return options.text;
2839
+ },
2840
+ };
2841
+ }
2842
+
2843
+ function expressionAsInvocation(
2844
+ fileName: string,
2845
+ span: SourceSpan,
2846
+ text: string,
2847
+ ): InvocationSyntax | null {
2848
+ const trimmed = text.trim();
2849
+ if (!trimmed.startsWith('#')) {
2850
+ return null;
2851
+ }
2852
+
2853
+ const leadingWhitespace = text.length - text.trimStart().length;
2854
+ const parsed = parseMacroInvocationAt(fileName, text, leadingWhitespace);
2855
+ if ('reason' in parsed || parsed.span.end !== text.length) {
2856
+ return null;
2857
+ }
2858
+
2859
+ const args = parsed.argumentSpans
2860
+ .filter((argument) => argument.kind === 'ExprArg')
2861
+ .map((argument, index) =>
2862
+ createArgumentSyntaxFromText(
2863
+ index,
2864
+ fileName,
2865
+ offsetSpan(span, argument.span),
2866
+ text.slice(argument.span.start, argument.span.end),
2867
+ )
2868
+ );
2869
+ const block = parsed.trailingBlockSpan
2870
+ ? createBlockSyntaxFromText(
2871
+ fileName,
2872
+ offsetSpan(span, parsed.trailingBlockSpan),
2873
+ text.slice(parsed.trailingBlockSpan.start, parsed.trailingBlockSpan.end),
2874
+ )
2875
+ : parsed.invocationKind === 'block' && parsed.argumentSpans[0]?.kind === 'BlockArg'
2876
+ ? createBlockSyntaxFromText(
2877
+ fileName,
2878
+ offsetSpan(span, parsed.argumentSpans[0].span),
2879
+ text.slice(parsed.argumentSpans[0].span.start, parsed.argumentSpans[0].span.end),
2880
+ )
2881
+ : null;
2882
+ const declaration = parsed.declarationSpan
2883
+ ? createDeclSyntaxFromText(
2884
+ fileName,
2885
+ offsetSpan(span, parsed.declarationSpan),
2886
+ text.slice(parsed.declarationSpan.start, parsed.declarationSpan.end),
2887
+ )
2888
+ : null;
2889
+
2890
+ return createInvocationSyntax({
2891
+ args,
2892
+ block,
2893
+ declaration,
2894
+ form: parsed.invocationKind === 'decl'
2895
+ ? 'decl'
2896
+ : parsed.invocationKind === 'arglist+decl'
2897
+ ? 'arglist_decl'
2898
+ : parsed.invocationKind === 'block'
2899
+ ? 'block'
2900
+ : 'arglist',
2901
+ hasBlock: block !== null,
2902
+ name: parsed.nameText,
2903
+ span,
2904
+ text,
2905
+ });
2906
+ }
2907
+
2908
+ export function createClassMemberSyntaxListFromCode(
2909
+ fileName: string,
2910
+ code: string,
2911
+ ): readonly MacroAnyClassMemberSyntax[] {
2912
+ const sourceFile = ts.createSourceFile(
2913
+ fileName.endsWith('.tsx') || fileName.endsWith('.jsx') || fileName.endsWith('.sts')
2914
+ ? fileName
2915
+ : `${fileName}.tsx`,
2916
+ `class __SoundscriptQuotedMembers {${code}}`,
2917
+ ts.ScriptTarget.Latest,
2918
+ true,
2919
+ scriptKindForHostFile(fileName),
2920
+ );
2921
+ ensureNoParseDiagnostics(sourceFile, '// #[component] generated invalid class members.');
2922
+ const statement = sourceFile.statements[0];
2923
+ if (!statement || !ts.isClassDeclaration(statement)) {
2924
+ throw new Error('// #[component] generated invalid class members.');
2925
+ }
2926
+ return statement.members.flatMap((member) => {
2927
+ const wrapped = createClassMemberSyntaxFromNode(
2928
+ synthesizeHostNode(member),
2929
+ sourceFile,
2930
+ {
2931
+ fileName,
2932
+ start: 0,
2933
+ end: code.length,
2934
+ },
2935
+ );
2936
+ return wrapped ? [wrapped] : [];
2937
+ });
2938
+ }
2939
+
2940
+ export function createTemplateSyntaxFromPieces(
2941
+ span: SourceSpan,
2942
+ text: string,
2943
+ quasis: readonly MacroTemplateQuasi[],
2944
+ expressions: readonly ExprSyntax[],
2945
+ ): MacroTemplateOperand {
2946
+ return {
2947
+ expressions,
2948
+ kind: 'template',
2949
+ quasis,
2950
+ span,
2951
+ text() {
2952
+ return text;
2953
+ },
2954
+ };
2955
+ }
2956
+
2957
+ export function createExprSyntaxFromText(
2958
+ fileName: string,
2959
+ span: SourceSpan,
2960
+ text: string,
2961
+ ): ExprSyntax {
2962
+ const node = parseHostExpressionWithNestedMacroFallback(
2963
+ fileName,
2964
+ text,
2965
+ 'Macro expression operands must parse as exactly one host-language expression.',
2966
+ );
2967
+ const parsedSourceFile = node.getSourceFile?.() ?? ts.createSourceFile(
2968
+ fileName,
2969
+ text,
2970
+ ts.ScriptTarget.Latest,
2971
+ true,
2972
+ ts.ScriptKind.TSX,
2973
+ );
2974
+ const sourceFile = setSourceFileOffset(
2975
+ parsedSourceFile,
2976
+ span.start - node.getStart(parsedSourceFile, false),
2977
+ );
2978
+ return createExprSyntaxFromNode(node, sourceFile, span, text);
2979
+ }
2980
+
2981
+ export function createBlockSyntaxFromText(
2982
+ fileName: string,
2983
+ span: SourceSpan,
2984
+ text: string,
2985
+ ): BlockSyntax {
2986
+ try {
2987
+ const statement = parseSingleHostStatement(
2988
+ fileName,
2989
+ 'macro_block_operand',
2990
+ text,
2991
+ 'Macro block operands must parse as exactly one host-language block statement.',
2992
+ );
2993
+ if (!ts.isBlock(statement)) {
2994
+ throw new Error(
2995
+ 'Macro block operands must parse as exactly one host-language block statement.',
2996
+ );
2997
+ }
2998
+ const sourceFile = setSourceFileOffset(
2999
+ statement.getSourceFile(),
3000
+ span.start - statement.getStart(statement.getSourceFile(), false),
3001
+ );
3002
+ return createBlockSyntaxFromNode(statement, sourceFile, span, text);
3003
+ } catch {
3004
+ try {
3005
+ const statement = parseSingleHostStatement(
3006
+ fileName,
3007
+ 'macro_block_operand',
3008
+ neutralizeNestedMacrosForHostParse(fileName, text),
3009
+ 'Macro block operands must parse as exactly one host-language block statement.',
3010
+ );
3011
+ if (!ts.isBlock(statement)) {
3012
+ throw new Error(
3013
+ 'Macro block operands must parse as exactly one host-language block statement.',
3014
+ );
3015
+ }
3016
+ const sourceFile = setSourceFileOffset(
3017
+ statement.getSourceFile(),
3018
+ span.start - statement.getStart(statement.getSourceFile(), false),
3019
+ );
3020
+ return createBlockSyntaxFromNode(statement, sourceFile, span, text);
3021
+ } catch {
3022
+ return {
3023
+ containsCallNamed() {
3024
+ return false;
3025
+ },
3026
+ kind: 'block',
3027
+ replaceThis() {
3028
+ return this;
3029
+ },
3030
+ rewrite() {
3031
+ return this;
3032
+ },
3033
+ span,
3034
+ statements: [],
3035
+ text() {
3036
+ return text;
3037
+ },
3038
+ thisMemberReferences() {
3039
+ return [];
3040
+ },
3041
+ };
3042
+ }
3043
+ }
3044
+ }
3045
+
3046
+ export function createDeclSyntaxFromText(
3047
+ fileName: string,
3048
+ span: SourceSpan,
3049
+ text: string,
3050
+ ): DeclSyntax {
3051
+ const parseText = (() => {
3052
+ try {
3053
+ const sourceFile = ts.createSourceFile(
3054
+ fileName,
3055
+ text,
3056
+ ts.ScriptTarget.Latest,
3057
+ true,
3058
+ scriptKindForHostFile(fileName),
3059
+ );
3060
+ ensureNoParseDiagnostics(
3061
+ sourceFile,
3062
+ 'Macro declaration operands must parse as exactly one class, function, interface, or type alias declaration.',
3063
+ );
3064
+ return { sourceFile, statement: sourceFile.statements[0] };
3065
+ } catch {
3066
+ const neutralized = neutralizeNestedMacrosForHostParse(fileName, text);
3067
+ const sourceFile = ts.createSourceFile(
3068
+ fileName,
3069
+ neutralized,
3070
+ ts.ScriptTarget.Latest,
3071
+ true,
3072
+ scriptKindForHostFile(fileName),
3073
+ );
3074
+ ensureNoParseDiagnostics(
3075
+ sourceFile,
3076
+ 'Macro declaration operands must parse as exactly one class, function, interface, or type alias declaration.',
3077
+ );
3078
+ return { sourceFile, statement: sourceFile.statements[0] };
3079
+ }
3080
+ })();
3081
+
3082
+ const statement = parseText.statement;
3083
+ if (
3084
+ !ts.isClassDeclaration(statement) && !ts.isFunctionDeclaration(statement) &&
3085
+ !ts.isInterfaceDeclaration(statement) && !ts.isTypeAliasDeclaration(statement)
3086
+ ) {
3087
+ throw new Error(
3088
+ 'Macro declaration operands must parse as exactly one class, function, interface, or type alias declaration.',
3089
+ );
3090
+ }
3091
+ return createDeclSyntaxFromNode(
3092
+ statement,
3093
+ setSourceFileOffset(
3094
+ parseText.sourceFile,
3095
+ span.start - statement.getStart(parseText.sourceFile, false),
3096
+ ),
3097
+ span,
3098
+ text,
3099
+ );
3100
+ }
3101
+
3102
+ export function createStmtListSyntaxFromCode(
3103
+ fileName: string,
3104
+ suffix: string,
3105
+ code: string,
3106
+ ): readonly StmtSyntax[] {
3107
+ const statements = parseHostStatements(
3108
+ fileName,
3109
+ suffix,
3110
+ code,
3111
+ 'Quoted macro statements must parse as host-language statements.',
3112
+ );
3113
+ const sourceFile = statements[0]?.getSourceFile() ??
3114
+ ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
3115
+ return statements.map((statement) =>
3116
+ createStmtSyntaxFromNode(statement, sourceFile, {
3117
+ fileName,
3118
+ start: 0,
3119
+ end: code.length,
3120
+ })
3121
+ );
3122
+ }
3123
+
3124
+ export function updateClassSyntax(
3125
+ base: MacroClassDeclSyntax,
3126
+ members: readonly MacroAnyClassMemberSyntax[],
3127
+ ): MacroClassDeclSyntax {
3128
+ const hostClassDecl = getHostNode(base);
3129
+ if (!hostClassDecl || !ts.isClassDeclaration(hostClassDecl)) {
3130
+ throw new Error('Expected a class declaration-backed syntax node.');
3131
+ }
3132
+
3133
+ const updatedClass = synthesizeHostNode(ts.factory.updateClassDeclaration(
3134
+ hostClassDecl,
3135
+ hostClassDecl.modifiers,
3136
+ hostClassDecl.name,
3137
+ hostClassDecl.typeParameters,
3138
+ hostClassDecl.heritageClauses,
3139
+ members.map((member) => synthesizeHostNode(getHostNode(member) as ts.ClassElement)),
3140
+ ));
3141
+ return createDeclSyntaxFromNode(
3142
+ updatedClass,
3143
+ hostClassDecl.getSourceFile(),
3144
+ base.span,
3145
+ ).asClass()!;
3146
+ }
3147
+
3148
+ export function buildClassFieldSyntax(
3149
+ fileName: string,
3150
+ options: MacroFieldBuildOptions,
3151
+ ): MacroClassFieldSyntax {
3152
+ const sourceFile = createBuildSourceFile(fileName);
3153
+ const initializer = options.initializer ? getHostExpression(options.initializer) : undefined;
3154
+ const field = synthesizeHostNode(ts.factory.createPropertyDeclaration(
3155
+ createModifierNodes(options.modifiers),
3156
+ options.name,
3157
+ undefined,
3158
+ options.type ? createTypeNodeFromText(fileName, options.type) : undefined,
3159
+ initializer,
3160
+ ));
3161
+ return createClassFieldSyntaxFromNode(field, sourceFile, {
3162
+ fileName,
3163
+ start: 0,
3164
+ end: 0,
3165
+ });
3166
+ }
3167
+
3168
+ export function buildClassGetterSyntax(
3169
+ fileName: string,
3170
+ options: Omit<MacroMethodBuildOptions, 'parameters'>,
3171
+ ): MacroClassMethodSyntax {
3172
+ const sourceFile = createBuildSourceFile(fileName);
3173
+ const getter = synthesizeHostNode(ts.factory.createGetAccessorDeclaration(
3174
+ createModifierNodes(options.modifiers),
3175
+ options.name,
3176
+ [],
3177
+ undefined,
3178
+ cloneBlockNode(options.body),
3179
+ ));
3180
+ return createClassMethodSyntaxFromNode(getter, sourceFile, {
3181
+ fileName,
3182
+ start: 0,
3183
+ end: 0,
3184
+ });
3185
+ }
3186
+
3187
+ export function buildClassSetterSyntax(
3188
+ fileName: string,
3189
+ options: MacroSetterBuildOptions,
3190
+ ): MacroClassMethodSyntax {
3191
+ const sourceFile = createBuildSourceFile(fileName);
3192
+ const parameter = typeof options.parameter === 'string'
3193
+ ? { name: options.parameter }
3194
+ : options.parameter;
3195
+ const setter = synthesizeHostNode(ts.factory.createSetAccessorDeclaration(
3196
+ createModifierNodes(options.modifiers),
3197
+ options.name,
3198
+ [
3199
+ synthesizeHostNode(ts.factory.createParameterDeclaration(
3200
+ undefined,
3201
+ undefined,
3202
+ parameter.name,
3203
+ undefined,
3204
+ parameter.type ? createTypeNodeFromText(fileName, parameter.type) : undefined,
3205
+ undefined,
3206
+ )),
3207
+ ],
3208
+ cloneBlockNode(options.body),
3209
+ ));
3210
+ return createClassMethodSyntaxFromNode(setter, sourceFile, {
3211
+ fileName,
3212
+ start: 0,
3213
+ end: 0,
3214
+ });
3215
+ }
3216
+
3217
+ export function buildClassMethodSyntax(
3218
+ fileName: string,
3219
+ options: MacroMethodBuildOptions,
3220
+ ): MacroClassMethodSyntax {
3221
+ const sourceFile = createBuildSourceFile(fileName);
3222
+ const method = synthesizeHostNode(ts.factory.createMethodDeclaration(
3223
+ createModifierNodes(options.modifiers),
3224
+ undefined,
3225
+ options.name,
3226
+ undefined,
3227
+ undefined,
3228
+ createParameterDeclarations(fileName, options.parameters),
3229
+ options.returnType ? createTypeNodeFromText(fileName, options.returnType) : undefined,
3230
+ cloneBlockNode(options.body),
3231
+ ));
3232
+ return createClassMethodSyntaxFromNode(method, sourceFile, {
3233
+ fileName,
3234
+ start: 0,
3235
+ end: 0,
3236
+ });
3237
+ }
3238
+
3239
+ export function buildFunctionDeclSyntax(
3240
+ fileName: string,
3241
+ options: MacroFunctionBuildOptions,
3242
+ ): MacroFunctionDeclSyntax {
3243
+ const sourceFile = createBuildSourceFile(fileName);
3244
+ const declaration = synthesizeHostNode(ts.factory.createFunctionDeclaration(
3245
+ createModifierNodes(options.modifiers),
3246
+ undefined,
3247
+ options.name,
3248
+ undefined,
3249
+ createParameterDeclarations(fileName, options.parameters),
3250
+ options.returnType ? createTypeNodeFromText(fileName, options.returnType) : undefined,
3251
+ cloneBlockNode(options.body),
3252
+ ));
3253
+ return createDeclSyntaxFromNode(declaration, sourceFile, {
3254
+ fileName,
3255
+ start: 0,
3256
+ end: 0,
3257
+ }).asFunction()!;
3258
+ }
3259
+
3260
+ export function syntaxText(node: MacroSyntaxNode): string {
3261
+ if ('text' in node && typeof node.text === 'function') {
3262
+ return node.text();
3263
+ }
3264
+ return fallbackText(node);
3265
+ }
3266
+
3267
+ export function quoteText(value: unknown): string {
3268
+ if (Array.isArray(value)) {
3269
+ return value.map((entry) => quoteText(entry)).join('\n');
3270
+ }
3271
+ if (value && typeof value === 'object' && 'kind' in value) {
3272
+ return syntaxText(value as MacroSyntaxNode);
3273
+ }
3274
+ if (typeof value === 'string') {
3275
+ return value;
3276
+ }
3277
+ if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
3278
+ return String(value);
3279
+ }
3280
+ if (value === null) {
3281
+ return 'null';
3282
+ }
3283
+ if (value === undefined) {
3284
+ return 'undefined';
3285
+ }
3286
+ throw new Error(`Unsupported macro quote interpolation value: ${String(value)}`);
3287
+ }
3288
+
3289
+ function neutralizeNestedMacrosForHostParse(fileName: string, text: string): string {
3290
+ const hashes = scanMacroCandidates(fileName, text).hashes
3291
+ .filter((hash) => hash.kind === 'macro-start')
3292
+ .sort((left, right) => right.span.start - left.span.start);
3293
+ if (hashes.length === 0) {
3294
+ return text;
3295
+ }
3296
+
3297
+ let rewritten = text;
3298
+ for (const hash of hashes) {
3299
+ const parsed = parseMacroInvocationAt(fileName, rewritten, hash.span.start);
3300
+ if ('reason' in parsed) {
3301
+ continue;
3302
+ }
3303
+
3304
+ const args = parsed.argumentSpans
3305
+ .filter((argument) => argument.kind === 'ExprArg')
3306
+ .map((argument) => rewritten.slice(argument.span.start, argument.span.end));
3307
+ const block = parsed.trailingBlockSpan
3308
+ ? rewritten.slice(parsed.trailingBlockSpan.start, parsed.trailingBlockSpan.end)
3309
+ : (
3310
+ parsed.invocationKind === 'block' &&
3311
+ parsed.argumentSpans[0]?.kind === 'BlockArg'
3312
+ )
3313
+ ? rewritten.slice(parsed.argumentSpans[0].span.start, parsed.argumentSpans[0].span.end)
3314
+ : null;
3315
+
3316
+ let replacement = `__sts_macro_${parsed.nameText}`;
3317
+ switch (parsed.invocationKind) {
3318
+ case 'arglist':
3319
+ replacement += `(${args.join(', ')})`;
3320
+ break;
3321
+ case 'block':
3322
+ replacement += `(() => ${block ?? '{}'})`;
3323
+ break;
3324
+ case 'arglist+block':
3325
+ replacement += `(${[...args, `() => ${block ?? '{}'}`].join(', ')})`;
3326
+ break;
3327
+ case 'decl':
3328
+ case 'arglist+decl':
3329
+ replacement = 'undefined';
3330
+ break;
3331
+ }
3332
+
3333
+ rewritten = rewritten.slice(0, parsed.span.start) + replacement +
3334
+ rewritten.slice(parsed.span.end);
3335
+ }
3336
+
3337
+ return rewritten;
3338
+ }