@soundscript/soundscript 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +15 -6
- package/project-transform/index.js +2 -0
- package/project-transform/index.ts +8 -0
- package/project-transform/src/annotation_syntax.js +948 -0
- package/project-transform/src/annotation_syntax.ts +1217 -0
- package/project-transform/src/build_package.js +475 -0
- package/project-transform/src/build_package.ts +683 -0
- package/project-transform/src/bundled/portable-web-globals.d.ts +153 -0
- package/project-transform/src/bundled/runtime_externs.js +220 -0
- package/project-transform/src/bundled/runtime_externs.ts +237 -0
- package/project-transform/src/bundled/sound-libs/lib.decorators.d.ts +385 -0
- package/project-transform/src/bundled/sound-libs/lib.decorators.legacy.d.ts +22 -0
- package/project-transform/src/bundled/sound-libs/lib.dom.asynciterable.d.ts +42 -0
- package/project-transform/src/bundled/sound-libs/lib.dom.d.ts +39440 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.collection.d.ts +149 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.core.d.ts +657 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.d.ts +28 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.generator.d.ts +77 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.iterable.d.ts +616 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.promise.d.ts +80 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.proxy.d.ts +128 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.reflect.d.ts +144 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.symbol.d.ts +46 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.symbol.wellknown.d.ts +170 -0
- package/project-transform/src/bundled/sound-libs/lib.es2016.array.include.d.ts +116 -0
- package/project-transform/src/bundled/sound-libs/lib.es2016.d.ts +21 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.arraybuffer.d.ts +21 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.d.ts +26 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.date.d.ts +31 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.object.d.ts +49 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.string.d.ts +45 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.typedarrays.d.ts +53 -0
- package/project-transform/src/bundled/sound-libs/lib.es2018.asyncgenerator.d.ts +77 -0
- package/project-transform/src/bundled/sound-libs/lib.es2018.asynciterable.d.ts +57 -0
- package/project-transform/src/bundled/sound-libs/lib.es2018.d.ts +24 -0
- package/project-transform/src/bundled/sound-libs/lib.es2018.promise.d.ts +30 -0
- package/project-transform/src/bundled/sound-libs/lib.es2018.regexp.d.ts +37 -0
- package/project-transform/src/bundled/sound-libs/lib.es2019.array.d.ts +79 -0
- package/project-transform/src/bundled/sound-libs/lib.es2019.d.ts +24 -0
- package/project-transform/src/bundled/sound-libs/lib.es2019.object.d.ts +47 -0
- package/project-transform/src/bundled/sound-libs/lib.es2019.string.d.ts +37 -0
- package/project-transform/src/bundled/sound-libs/lib.es2019.symbol.d.ts +24 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.bigint.d.ts +765 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.d.ts +27 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.date.d.ts +42 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.number.d.ts +28 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.promise.d.ts +49 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.string.d.ts +44 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.symbol.wellknown.d.ts +41 -0
- package/project-transform/src/bundled/sound-libs/lib.es2021.d.ts +23 -0
- package/project-transform/src/bundled/sound-libs/lib.es2021.promise.d.ts +48 -0
- package/project-transform/src/bundled/sound-libs/lib.es2021.string.d.ts +33 -0
- package/project-transform/src/bundled/sound-libs/lib.es2021.weakref.d.ts +78 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.array.d.ts +121 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.d.ts +25 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.error.d.ts +75 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.object.d.ts +26 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.regexp.d.ts +39 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.string.d.ts +25 -0
- package/project-transform/src/bundled/sound-libs/lib.es2023.array.d.ts +924 -0
- package/project-transform/src/bundled/sound-libs/lib.es2023.collection.d.ts +21 -0
- package/project-transform/src/bundled/sound-libs/lib.es2023.d.ts +22 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.arraybuffer.d.ts +65 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.collection.d.ts +29 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.d.ts +26 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.object.d.ts +33 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.promise.d.ts +35 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.regexp.d.ts +25 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.string.d.ts +29 -0
- package/project-transform/src/bundled/sound-libs/lib.es5.d.ts +4924 -0
- package/project-transform/src/bundled/sound_stdlib.js +142 -0
- package/project-transform/src/bundled/sound_stdlib.ts +180 -0
- package/project-transform/src/checker/analyze_project.js +1361 -0
- package/project-transform/src/checker/analyze_project.ts +2246 -0
- package/project-transform/src/checker/diagnostics.js +112 -0
- package/project-transform/src/checker/diagnostics.ts +222 -0
- package/project-transform/src/checker/engine/context.js +235 -0
- package/project-transform/src/checker/engine/context.ts +340 -0
- package/project-transform/src/checker/engine/diagnostic_codes.js +72 -0
- package/project-transform/src/checker/engine/diagnostic_codes.ts +95 -0
- package/project-transform/src/checker/engine/facts.js +35 -0
- package/project-transform/src/checker/engine/facts.ts +48 -0
- package/project-transform/src/checker/engine/types.js +1 -0
- package/project-transform/src/checker/engine/types.ts +485 -0
- package/project-transform/src/checker/proof_escape_hatch_diagnostics.js +104 -0
- package/project-transform/src/checker/proof_escape_hatch_diagnostics.ts +173 -0
- package/project-transform/src/checker/rules/async_surface.js +231 -0
- package/project-transform/src/checker/rules/async_surface.ts +335 -0
- package/project-transform/src/checker/rules/class_lifecycle.js +798 -0
- package/project-transform/src/checker/rules/class_lifecycle.ts +1276 -0
- package/project-transform/src/checker/rules/directive_validation.js +571 -0
- package/project-transform/src/checker/rules/directive_validation.ts +938 -0
- package/project-transform/src/checker/rules/directives.js +23 -0
- package/project-transform/src/checker/rules/directives.ts +25 -0
- package/project-transform/src/checker/rules/flow.js +202 -0
- package/project-transform/src/checker/rules/flow.ts +333 -0
- package/project-transform/src/checker/rules/flow_facts.js +601 -0
- package/project-transform/src/checker/rules/flow_facts.ts +978 -0
- package/project-transform/src/checker/rules/flow_invalidation.js +1119 -0
- package/project-transform/src/checker/rules/flow_invalidation.ts +2150 -0
- package/project-transform/src/checker/rules/flow_shared.js +2822 -0
- package/project-transform/src/checker/rules/flow_shared.ts +4383 -0
- package/project-transform/src/checker/rules/foreign_boundary.js +120 -0
- package/project-transform/src/checker/rules/foreign_boundary.ts +196 -0
- package/project-transform/src/checker/rules/foreign_projection.js +279 -0
- package/project-transform/src/checker/rules/foreign_projection.ts +425 -0
- package/project-transform/src/checker/rules/generated_helpers.js +13 -0
- package/project-transform/src/checker/rules/generated_helpers.ts +18 -0
- package/project-transform/src/checker/rules/index.js +35 -0
- package/project-transform/src/checker/rules/index.ts +49 -0
- package/project-transform/src/checker/rules/namespace_object.js +845 -0
- package/project-transform/src/checker/rules/namespace_object.ts +1224 -0
- package/project-transform/src/checker/rules/non_ordinary_recovery.js +1328 -0
- package/project-transform/src/checker/rules/non_ordinary_recovery.ts +2391 -0
- package/project-transform/src/checker/rules/null_prototype.js +3 -0
- package/project-transform/src/checker/rules/null_prototype.ts +6 -0
- package/project-transform/src/checker/rules/overloads.js +181 -0
- package/project-transform/src/checker/rules/overloads.ts +317 -0
- package/project-transform/src/checker/rules/predicate_verification.js +691 -0
- package/project-transform/src/checker/rules/predicate_verification.ts +1088 -0
- package/project-transform/src/checker/rules/prototype_hardening.js +237 -0
- package/project-transform/src/checker/rules/prototype_hardening.ts +343 -0
- package/project-transform/src/checker/rules/receiver_discipline.js +263 -0
- package/project-transform/src/checker/rules/receiver_discipline.ts +356 -0
- package/project-transform/src/checker/rules/relations.js +6861 -0
- package/project-transform/src/checker/rules/relations.ts +12158 -0
- package/project-transform/src/checker/rules/resolved_builtins.js +274 -0
- package/project-transform/src/checker/rules/resolved_builtins.ts +438 -0
- package/project-transform/src/checker/rules/trust.js +217 -0
- package/project-transform/src/checker/rules/trust.ts +301 -0
- package/project-transform/src/checker/rules/type_guards.js +173 -0
- package/project-transform/src/checker/rules/type_guards.ts +257 -0
- package/project-transform/src/checker/rules/universal.js +17 -0
- package/project-transform/src/checker/rules/universal.ts +22 -0
- package/project-transform/src/checker/rules/unsafe_value_origin.js +80 -0
- package/project-transform/src/checker/rules/unsafe_value_origin.ts +125 -0
- package/project-transform/src/checker/rules/unsound_imports.js +218 -0
- package/project-transform/src/checker/rules/unsound_imports.ts +301 -0
- package/project-transform/src/checker/rules/unsound_syntax.js +1695 -0
- package/project-transform/src/checker/rules/unsound_syntax.ts +2540 -0
- package/project-transform/src/checker/rules/value_types.js +206 -0
- package/project-transform/src/checker/rules/value_types.ts +407 -0
- package/project-transform/src/checker/timing.js +43 -0
- package/project-transform/src/checker/timing.ts +78 -0
- package/project-transform/src/checker/unsupported_feature_messages.js +337 -0
- package/project-transform/src/checker/unsupported_feature_messages.ts +531 -0
- package/project-transform/src/cli.js +892 -0
- package/project-transform/src/cli.ts +1476 -0
- package/project-transform/src/compiler/compile_project.js +319 -0
- package/project-transform/src/compiler/compile_project.ts +508 -0
- package/project-transform/src/compiler/errors.js +10 -0
- package/project-transform/src/compiler/errors.ts +29 -0
- package/project-transform/src/compiler/ir.js +1 -0
- package/project-transform/src/compiler/ir.ts +1526 -0
- package/project-transform/src/compiler/lower.js +30550 -0
- package/project-transform/src/compiler/lower.ts +43645 -0
- package/project-transform/src/compiler/lower_arrays.js +140 -0
- package/project-transform/src/compiler/lower_arrays.ts +190 -0
- package/project-transform/src/compiler/lower_strings.js +121 -0
- package/project-transform/src/compiler/lower_strings.ts +198 -0
- package/project-transform/src/compiler/lower_tagged.js +329 -0
- package/project-transform/src/compiler/lower_tagged.ts +427 -0
- package/project-transform/src/compiler/lower_views.js +171 -0
- package/project-transform/src/compiler/lower_views.ts +251 -0
- package/project-transform/src/compiler/object_keys.js +25 -0
- package/project-transform/src/compiler/object_keys.ts +35 -0
- package/project-transform/src/compiler/runtime_ir.js +30 -0
- package/project-transform/src/compiler/runtime_ir.ts +727 -0
- package/project-transform/src/compiler/tagged_boundary.js +18 -0
- package/project-transform/src/compiler/tagged_boundary.ts +37 -0
- package/project-transform/src/compiler/toolchain.js +170 -0
- package/project-transform/src/compiler/toolchain.ts +229 -0
- package/project-transform/src/compiler/unicode_case_data.js +2102 -0
- package/project-transform/src/compiler/unicode_case_data.ts +2112 -0
- package/project-transform/src/compiler/wasm_js_host_runtime.js +656 -0
- package/project-transform/src/compiler/wasm_js_host_runtime.ts +762 -0
- package/project-transform/src/compiler/wat_arrays.js +3132 -0
- package/project-transform/src/compiler/wat_arrays.ts +3768 -0
- package/project-transform/src/compiler/wat_emitter.js +17952 -0
- package/project-transform/src/compiler/wat_emitter.ts +22812 -0
- package/project-transform/src/compiler/wat_strings.js +129 -0
- package/project-transform/src/compiler/wat_strings.ts +187 -0
- package/project-transform/src/compiler/wat_tagged.js +548 -0
- package/project-transform/src/compiler/wat_tagged.ts +674 -0
- package/project-transform/src/compiler_generator_runner.js +153 -0
- package/project-transform/src/compiler_generator_runner.ts +171 -0
- package/project-transform/src/compiler_object_test_helpers.js +69 -0
- package/project-transform/src/compiler_object_test_helpers.ts +96 -0
- package/project-transform/src/compiler_promise_runner.js +2116 -0
- package/project-transform/src/compiler_promise_runner.ts +2184 -0
- package/project-transform/src/compiler_test_helpers.js +854 -0
- package/project-transform/src/compiler_test_helpers.ts +1087 -0
- package/project-transform/src/config.js +568 -0
- package/project-transform/src/config.ts +892 -0
- package/project-transform/src/diagnostic_metadata.js +67 -0
- package/project-transform/src/diagnostic_metadata.ts +99 -0
- package/project-transform/src/diagnostic_reference.js +1368 -0
- package/project-transform/src/diagnostic_reference.ts +1523 -0
- package/project-transform/src/editor_diagnostics_worker.js +176 -0
- package/project-transform/src/editor_diagnostics_worker.ts +250 -0
- package/project-transform/src/editor_projection.js +224 -0
- package/project-transform/src/editor_projection.ts +421 -0
- package/project-transform/src/frontend/builtin_expanded_program_test_cleanup.js +47 -0
- package/project-transform/src/frontend/builtin_expanded_program_test_cleanup.ts +72 -0
- package/project-transform/src/frontend/builtin_macro_support.js +842 -0
- package/project-transform/src/frontend/builtin_macro_support.ts +1386 -0
- package/project-transform/src/frontend/builtin_macros.js +409 -0
- package/project-transform/src/frontend/builtin_macros.ts +542 -0
- package/project-transform/src/frontend/component_poc_runtime.js +279 -0
- package/project-transform/src/frontend/component_poc_runtime.ts +372 -0
- package/project-transform/src/frontend/css_macro.js +148 -0
- package/project-transform/src/frontend/css_macro.ts +222 -0
- package/project-transform/src/frontend/derive_macros.js +2072 -0
- package/project-transform/src/frontend/derive_macros.ts +3188 -0
- package/project-transform/src/frontend/embedded_fragment_support.js +106 -0
- package/project-transform/src/frontend/embedded_fragment_support.ts +172 -0
- package/project-transform/src/frontend/error_normalization.js +403 -0
- package/project-transform/src/frontend/error_normalization.ts +832 -0
- package/project-transform/src/frontend/error_stdlib_support.js +1 -0
- package/project-transform/src/frontend/error_stdlib_support.ts +6 -0
- package/project-transform/src/frontend/expand_project.js +169 -0
- package/project-transform/src/frontend/expand_project.ts +248 -0
- package/project-transform/src/frontend/format_soundscript.js +297 -0
- package/project-transform/src/frontend/format_soundscript.ts +582 -0
- package/project-transform/src/frontend/graphql_macro.js +174 -0
- package/project-transform/src/frontend/graphql_macro.ts +253 -0
- package/project-transform/src/frontend/hash_context.js +83 -0
- package/project-transform/src/frontend/hash_context.ts +113 -0
- package/project-transform/src/frontend/hkt_macro.js +448 -0
- package/project-transform/src/frontend/hkt_macro.ts +897 -0
- package/project-transform/src/frontend/import_binding_usage.js +190 -0
- package/project-transform/src/frontend/import_binding_usage.ts +277 -0
- package/project-transform/src/frontend/macro_advanced_backend_adapter.js +58 -0
- package/project-transform/src/frontend/macro_advanced_backend_adapter.ts +123 -0
- package/project-transform/src/frontend/macro_advanced_context.js +826 -0
- package/project-transform/src/frontend/macro_advanced_context.ts +1102 -0
- package/project-transform/src/frontend/macro_advanced_output.js +21 -0
- package/project-transform/src/frontend/macro_advanced_output.ts +41 -0
- package/project-transform/src/frontend/macro_api.js +353 -0
- package/project-transform/src/frontend/macro_api.ts +1722 -0
- package/project-transform/src/frontend/macro_api_internal.js +35 -0
- package/project-transform/src/frontend/macro_api_internal.ts +80 -0
- package/project-transform/src/frontend/macro_api_module_support.js +39 -0
- package/project-transform/src/frontend/macro_api_module_support.ts +65 -0
- package/project-transform/src/frontend/macro_backend_adapter.js +272 -0
- package/project-transform/src/frontend/macro_backend_adapter.ts +420 -0
- package/project-transform/src/frontend/macro_context.js +816 -0
- package/project-transform/src/frontend/macro_context.ts +1105 -0
- package/project-transform/src/frontend/macro_debug.js +99 -0
- package/project-transform/src/frontend/macro_debug.ts +157 -0
- package/project-transform/src/frontend/macro_definition_support.js +28 -0
- package/project-transform/src/frontend/macro_definition_support.ts +73 -0
- package/project-transform/src/frontend/macro_errors.js +40 -0
- package/project-transform/src/frontend/macro_errors.ts +70 -0
- package/project-transform/src/frontend/macro_expander.js +919 -0
- package/project-transform/src/frontend/macro_expander.ts +1611 -0
- package/project-transform/src/frontend/macro_factory_support.js +176 -0
- package/project-transform/src/frontend/macro_factory_support.ts +263 -0
- package/project-transform/src/frontend/macro_host_ast_internal.js +64 -0
- package/project-transform/src/frontend/macro_host_ast_internal.ts +109 -0
- package/project-transform/src/frontend/macro_index.js +27 -0
- package/project-transform/src/frontend/macro_index.ts +50 -0
- package/project-transform/src/frontend/macro_loader.js +281 -0
- package/project-transform/src/frontend/macro_loader.ts +506 -0
- package/project-transform/src/frontend/macro_operand_semantics.js +838 -0
- package/project-transform/src/frontend/macro_operand_semantics.ts +1489 -0
- package/project-transform/src/frontend/macro_output.js +54 -0
- package/project-transform/src/frontend/macro_output.ts +123 -0
- package/project-transform/src/frontend/macro_parser.js +611 -0
- package/project-transform/src/frontend/macro_parser.ts +832 -0
- package/project-transform/src/frontend/macro_resolver.js +69 -0
- package/project-transform/src/frontend/macro_resolver.ts +125 -0
- package/project-transform/src/frontend/macro_rewrite.js +285 -0
- package/project-transform/src/frontend/macro_rewrite.ts +442 -0
- package/project-transform/src/frontend/macro_runtime_support.js +232 -0
- package/project-transform/src/frontend/macro_runtime_support.ts +324 -0
- package/project-transform/src/frontend/macro_scanner.js +393 -0
- package/project-transform/src/frontend/macro_scanner.ts +455 -0
- package/project-transform/src/frontend/macro_semantic_backend_adapter.js +87 -0
- package/project-transform/src/frontend/macro_semantic_backend_adapter.ts +166 -0
- package/project-transform/src/frontend/macro_semantic_context.js +5 -0
- package/project-transform/src/frontend/macro_semantic_context.ts +12 -0
- package/project-transform/src/frontend/macro_semantic_output.js +24 -0
- package/project-transform/src/frontend/macro_semantic_output.ts +47 -0
- package/project-transform/src/frontend/macro_semantic_types.js +1 -0
- package/project-transform/src/frontend/macro_semantic_types.ts +98 -0
- package/project-transform/src/frontend/macro_semantics.js +1172 -0
- package/project-transform/src/frontend/macro_semantics.ts +1502 -0
- package/project-transform/src/frontend/macro_site_kind_support.js +164 -0
- package/project-transform/src/frontend/macro_site_kind_support.ts +255 -0
- package/project-transform/src/frontend/macro_syntax_internal.js +1950 -0
- package/project-transform/src/frontend/macro_syntax_internal.ts +3338 -0
- package/project-transform/src/frontend/macro_templates.js +57 -0
- package/project-transform/src/frontend/macro_templates.ts +143 -0
- package/project-transform/src/frontend/macro_test_helpers.js +82 -0
- package/project-transform/src/frontend/macro_test_helpers.ts +136 -0
- package/project-transform/src/frontend/macro_types.js +1 -0
- package/project-transform/src/frontend/macro_types.ts +103 -0
- package/project-transform/src/frontend/macro_vm.js +39 -0
- package/project-transform/src/frontend/macro_vm.ts +113 -0
- package/project-transform/src/frontend/match_macro.js +885 -0
- package/project-transform/src/frontend/match_macro.ts +1220 -0
- package/project-transform/src/frontend/numeric_normalization.js +824 -0
- package/project-transform/src/frontend/numeric_normalization.ts +1380 -0
- package/project-transform/src/frontend/numeric_prelude.js +278 -0
- package/project-transform/src/frontend/numeric_prelude.ts +370 -0
- package/project-transform/src/frontend/project_frontend.js +2396 -0
- package/project-transform/src/frontend/project_frontend.ts +3776 -0
- package/project-transform/src/frontend/project_macro_support.js +1401 -0
- package/project-transform/src/frontend/project_macro_support.ts +2137 -0
- package/project-transform/src/frontend/sql_macro.js +175 -0
- package/project-transform/src/frontend/sql_macro.ts +254 -0
- package/project-transform/src/frontend/sql_stdlib_support.js +1 -0
- package/project-transform/src/frontend/sql_stdlib_support.ts +6 -0
- package/project-transform/src/frontend/std_package_support.js +228 -0
- package/project-transform/src/frontend/std_package_support.ts +400 -0
- package/project-transform/src/frontend/value_normalization.js +306 -0
- package/project-transform/src/frontend/value_normalization.ts +599 -0
- package/project-transform/src/lsp/project_service.js +4771 -0
- package/project-transform/src/lsp/project_service.ts +7580 -0
- package/project-transform/src/lsp/protocol.js +9 -0
- package/project-transform/src/lsp/protocol.ts +38 -0
- package/project-transform/src/lsp/server.js +355 -0
- package/project-transform/src/lsp/server.ts +671 -0
- package/project-transform/src/lsp/session.js +49 -0
- package/project-transform/src/lsp/session.ts +48 -0
- package/project-transform/src/lsp/timing.js +43 -0
- package/project-transform/src/lsp/timing.ts +76 -0
- package/project-transform/src/lsp/transport.js +205 -0
- package/project-transform/src/lsp/transport.ts +253 -0
- package/project-transform/src/lsp_main.js +5 -0
- package/project-transform/src/lsp_main.ts +7 -0
- package/project-transform/src/macros.d.ts +1 -0
- package/project-transform/src/macros.js +1 -0
- package/project-transform/src/macros.ts +1 -0
- package/project-transform/src/main.js +24 -0
- package/project-transform/src/main.ts +28 -0
- package/project-transform/src/platform/host.js +264 -0
- package/project-transform/src/platform/host.ts +343 -0
- package/project-transform/src/platform/path.js +8 -0
- package/project-transform/src/platform/path.ts +20 -0
- package/project-transform/src/public_macro_api/macro_api.d.ts +1054 -0
- package/project-transform/src/public_macro_api/macro_semantic_types.d.ts +66 -0
- package/project-transform/src/public_macro_api/macro_types.d.ts +70 -0
- package/project-transform/src/run_program.js +14 -0
- package/project-transform/src/run_program.ts +33 -0
- package/project-transform/src/runtime/materialize.js +371 -0
- package/project-transform/src/runtime/materialize.ts +502 -0
- package/project-transform/src/runtime/on_demand.js +203 -0
- package/project-transform/src/runtime/on_demand.ts +305 -0
- package/project-transform/src/runtime/source_maps.js +205 -0
- package/project-transform/src/runtime/source_maps.ts +297 -0
- package/project-transform/src/runtime/transform.js +148 -0
- package/project-transform/src/runtime/transform.ts +295 -0
- package/project-transform/src/service/types.js +1 -0
- package/project-transform/src/service/types.ts +22 -0
- package/project-transform/src/soundscript_packages.js +477 -0
- package/project-transform/src/soundscript_packages.ts +754 -0
- package/project-transform/src/soundscript_runtime_specifiers.js +88 -0
- package/project-transform/src/soundscript_runtime_specifiers.ts +96 -0
- package/project-transform/src/stdlib/async.d.ts +81 -0
- package/project-transform/src/stdlib/async.js +213 -0
- package/project-transform/src/stdlib/async.ts +315 -0
- package/project-transform/src/stdlib/codec.d.ts +32 -0
- package/project-transform/src/stdlib/codec.js +30 -0
- package/project-transform/src/stdlib/codec.ts +76 -0
- package/project-transform/src/stdlib/compare.d.ts +28 -0
- package/project-transform/src/stdlib/compare.js +115 -0
- package/project-transform/src/stdlib/compare.ts +151 -0
- package/project-transform/src/stdlib/css.d.ts +16 -0
- package/project-transform/src/stdlib/css.js +9 -0
- package/project-transform/src/stdlib/css.ts +28 -0
- package/project-transform/src/stdlib/debug.d.ts +2 -0
- package/project-transform/src/stdlib/debug.js +9 -0
- package/project-transform/src/stdlib/debug.ts +10 -0
- package/project-transform/src/stdlib/decode.d.ts +86 -0
- package/project-transform/src/stdlib/decode.js +254 -0
- package/project-transform/src/stdlib/decode.ts +390 -0
- package/project-transform/src/stdlib/derive.d.ts +6 -0
- package/project-transform/src/stdlib/derive.js +7 -0
- package/project-transform/src/stdlib/derive.ts +7 -0
- package/project-transform/src/stdlib/encode.d.ts +100 -0
- package/project-transform/src/stdlib/encode.js +130 -0
- package/project-transform/src/stdlib/encode.ts +259 -0
- package/project-transform/src/stdlib/failures.d.ts +23 -0
- package/project-transform/src/stdlib/failures.js +41 -0
- package/project-transform/src/stdlib/failures.ts +64 -0
- package/project-transform/src/stdlib/fetch.d.ts +67 -0
- package/project-transform/src/stdlib/fetch.js +5 -0
- package/project-transform/src/stdlib/fetch.ts +11 -0
- package/project-transform/src/stdlib/graphql.d.ts +16 -0
- package/project-transform/src/stdlib/graphql.js +9 -0
- package/project-transform/src/stdlib/graphql.ts +28 -0
- package/project-transform/src/stdlib/hash.d.ts +34 -0
- package/project-transform/src/stdlib/hash.js +110 -0
- package/project-transform/src/stdlib/hash.ts +188 -0
- package/project-transform/src/stdlib/hkt.d.ts +40 -0
- package/project-transform/src/stdlib/hkt.js +3 -0
- package/project-transform/src/stdlib/hkt.ts +41 -0
- package/project-transform/src/stdlib/index.d.ts +9 -0
- package/project-transform/src/stdlib/index.js +15 -0
- package/project-transform/src/stdlib/index.ts +23 -0
- package/project-transform/src/stdlib/json.d.ts +125 -0
- package/project-transform/src/stdlib/json.js +764 -0
- package/project-transform/src/stdlib/json.ts +1034 -0
- package/project-transform/src/stdlib/match.d.ts +11 -0
- package/project-transform/src/stdlib/match.js +13 -0
- package/project-transform/src/stdlib/match.ts +26 -0
- package/project-transform/src/stdlib/numerics.d.ts +523 -0
- package/project-transform/src/stdlib/numerics.js +1356 -0
- package/project-transform/src/stdlib/numerics.ts +1937 -0
- package/project-transform/src/stdlib/random.d.ts +19 -0
- package/project-transform/src/stdlib/random.js +3 -0
- package/project-transform/src/stdlib/random.ts +5 -0
- package/project-transform/src/stdlib/result.d.ts +68 -0
- package/project-transform/src/stdlib/result.js +139 -0
- package/project-transform/src/stdlib/result.ts +248 -0
- package/project-transform/src/stdlib/sql.d.ts +22 -0
- package/project-transform/src/stdlib/sql.js +23 -0
- package/project-transform/src/stdlib/sql.ts +53 -0
- package/project-transform/src/stdlib/text.d.ts +24 -0
- package/project-transform/src/stdlib/text.js +3 -0
- package/project-transform/src/stdlib/text.ts +4 -0
- package/project-transform/src/stdlib/thunk.d.ts +2 -0
- package/project-transform/src/stdlib/thunk.js +9 -0
- package/project-transform/src/stdlib/thunk.ts +15 -0
- package/project-transform/src/stdlib/typeclasses.d.ts +57 -0
- package/project-transform/src/stdlib/typeclasses.js +78 -0
- package/project-transform/src/stdlib/typeclasses.ts +173 -0
- package/project-transform/src/stdlib/url.d.ts +37 -0
- package/project-transform/src/stdlib/url.js +3 -0
- package/project-transform/src/stdlib/url.ts +4 -0
- package/project-transform/src/stdlib/value.d.ts +9 -0
- package/project-transform/src/stdlib/value.js +104 -0
- package/project-transform/src/stdlib/value.ts +133 -0
- package/project-transform/src/test_installed_stdlib.js +147 -0
- package/project-transform/src/test_installed_stdlib.ts +245 -0
- package/project-transform/src/test_macro_package_fixture.js +50 -0
- package/project-transform/src/test_macro_package_fixture.ts +68 -0
- package/project-transform/src/value_deep_safe.js +191 -0
- package/project-transform/src/value_deep_safe.ts +273 -0
|
@@ -0,0 +1,3776 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
import { normalizeRuntimeContext, type RuntimeContext } from '../config.ts';
|
|
4
|
+
import type { MergedDiagnostic } from '../checker/diagnostics.ts';
|
|
5
|
+
import { dirname, join } from '../platform/path.ts';
|
|
6
|
+
import {
|
|
7
|
+
SOUND_DIAGNOSTIC_CODES,
|
|
8
|
+
SOUND_DIAGNOSTIC_MESSAGES,
|
|
9
|
+
} from '../checker/engine/diagnostic_codes.ts';
|
|
10
|
+
import { describeUnsupportedFeature } from '../checker/unsupported_feature_messages.ts';
|
|
11
|
+
import { measureCheckerTiming } from '../checker/timing.ts';
|
|
12
|
+
import { BUILTIN_DIRECTIVE_NAMES, createAnnotationLookup } from '../annotation_syntax.ts';
|
|
13
|
+
import {
|
|
14
|
+
getSoundScriptPackageExportInfoForResolvedModule,
|
|
15
|
+
isForeignPackageSourceFile,
|
|
16
|
+
isForeignResolvedModule,
|
|
17
|
+
remapResolvedModuleToSoundScriptSource,
|
|
18
|
+
resolveSoundScriptAwareModule,
|
|
19
|
+
} from '../soundscript_packages.ts';
|
|
20
|
+
import { buildRewriteStageFromTexts } from './error_normalization.ts';
|
|
21
|
+
|
|
22
|
+
import { buildMacroPlaceholderIndex } from './macro_index.ts';
|
|
23
|
+
import {
|
|
24
|
+
macroSiteKindForFactoryForm,
|
|
25
|
+
scanMacroFactoryExports,
|
|
26
|
+
sourceTextLooksLikeMacroModule,
|
|
27
|
+
stripMacroFactoryAuthoringFromText,
|
|
28
|
+
usesLegacyDefineMacroAuthoring,
|
|
29
|
+
} from './macro_factory_support.ts';
|
|
30
|
+
import {
|
|
31
|
+
collectImportedMacroSiteKindsBySpecifier as collectImportedMacroSiteKindsForSource,
|
|
32
|
+
collectImportedNamedBindings,
|
|
33
|
+
} from './macro_site_kind_support.ts';
|
|
34
|
+
import {
|
|
35
|
+
classifyImportedBindingUsage,
|
|
36
|
+
type ImportedBindingUsage,
|
|
37
|
+
macroInvocationReferenceSpans,
|
|
38
|
+
stripCompileTimeOnlyImportedBindings,
|
|
39
|
+
} from './import_binding_usage.ts';
|
|
40
|
+
import {
|
|
41
|
+
declarationTextUsesMachineNumerics,
|
|
42
|
+
ELABORATED_BIGINT_TYPE_EXPORT_NAME,
|
|
43
|
+
isElaboratedBigIntTypeImportName,
|
|
44
|
+
isElaboratedF64TypeImportName,
|
|
45
|
+
prependMachineNumericPrelude,
|
|
46
|
+
prependMachineNumericSourcePrelude,
|
|
47
|
+
} from './numeric_prelude.ts';
|
|
48
|
+
import { type ImportedMacroSiteKind, rewriteMacroSource } from './macro_rewrite.ts';
|
|
49
|
+
import { scanMacroCandidates } from './macro_scanner.ts';
|
|
50
|
+
import type {
|
|
51
|
+
HashDiagnostic,
|
|
52
|
+
MacroParseDiagnostic,
|
|
53
|
+
MacroReplacement,
|
|
54
|
+
RewriteResult,
|
|
55
|
+
} from './macro_types.ts';
|
|
56
|
+
import type { MacroPlaceholderIndex } from './macro_index.ts';
|
|
57
|
+
import type { SourceSpan } from './macro_types.ts';
|
|
58
|
+
|
|
59
|
+
const MACRO_HELPER_PREAMBLE = [
|
|
60
|
+
'declare function __sts_macro_expr(id: number): never;',
|
|
61
|
+
'declare function __sts_macro_stmt(id: number): void;',
|
|
62
|
+
'',
|
|
63
|
+
].join('\n');
|
|
64
|
+
|
|
65
|
+
const SOUNDSCRIPT_PROGRAM_SUFFIX = '.sts.ts';
|
|
66
|
+
const SOUNDSCRIPT_DECLARATION_SUFFIX = '.sts.d.ts';
|
|
67
|
+
const PROJECTED_DECLARATION_EMIT_CACHE_LIMIT = 32;
|
|
68
|
+
|
|
69
|
+
interface ProjectedDeclarationEmitCacheSource {
|
|
70
|
+
fileName: string;
|
|
71
|
+
text: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface ProjectedDeclarationEmitCacheEntry {
|
|
75
|
+
declarations: ReadonlyMap<string, string>;
|
|
76
|
+
optionSignature: string;
|
|
77
|
+
rootNames: readonly string[];
|
|
78
|
+
sources: readonly ProjectedDeclarationEmitCacheSource[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const projectedDeclarationEmitCache = new Map<
|
|
82
|
+
string,
|
|
83
|
+
readonly ProjectedDeclarationEmitCacheEntry[]
|
|
84
|
+
>();
|
|
85
|
+
const projectedDeclarationPrinter = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
86
|
+
|
|
87
|
+
export function isSoundscriptSourceFile(fileName: string): boolean {
|
|
88
|
+
return fileName.endsWith('.sts');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function isSoundscriptMacroSourceFile(fileName: string): boolean {
|
|
92
|
+
return fileName.endsWith('.macro.sts');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function toProgramFileName(fileName: string): string {
|
|
96
|
+
return isSoundscriptSourceFile(fileName) ? `${fileName}.ts` : fileName;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function toProjectedDeclarationFileName(fileName: string): string {
|
|
100
|
+
return isSoundscriptSourceFile(fileName) ? `${fileName}.d.ts` : fileName;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function isProjectedSoundscriptDeclarationFile(fileName: string): boolean {
|
|
104
|
+
return fileName.endsWith(SOUNDSCRIPT_DECLARATION_SUFFIX);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function toSourceFileName(fileName: string): string {
|
|
108
|
+
return fileName.endsWith(SOUNDSCRIPT_PROGRAM_SUFFIX) ? fileName.slice(0, -3) : fileName;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function toProjectedDeclarationSourceFileName(fileName: string): string {
|
|
112
|
+
return isProjectedSoundscriptDeclarationFile(fileName) ? fileName.slice(0, -5) : fileName;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isDeclarationFileName(fileName: string): boolean {
|
|
116
|
+
return fileName.endsWith('.d.ts') || fileName.endsWith('.d.mts') || fileName.endsWith('.d.cts');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function isLoadableMacroModuleFile(fileName: string): boolean {
|
|
120
|
+
return isSoundscriptMacroSourceFile(fileName);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function blankPreservingLines(text: string): string {
|
|
124
|
+
return text.replace(/[^\r\n]/gu, ' ');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const PROJECTED_BINDING_NAME_PATTERN = /^__sts_projected_(?:type|value)_\d+$/u;
|
|
128
|
+
const PRELUDE_TYPE_IMPORT_NAMES = ['Result', 'Option', 'Ok', 'Err', 'Some', 'None'] as const;
|
|
129
|
+
const PRELUDE_VALUE_IMPORT_NAMES = [
|
|
130
|
+
'ok',
|
|
131
|
+
'err',
|
|
132
|
+
'some',
|
|
133
|
+
'none',
|
|
134
|
+
'isOk',
|
|
135
|
+
'isErr',
|
|
136
|
+
'isSome',
|
|
137
|
+
'isNone',
|
|
138
|
+
'Failure',
|
|
139
|
+
'where',
|
|
140
|
+
] as const;
|
|
141
|
+
const PRELUDE_TYPE_IMPORT_PATTERNS = PRELUDE_TYPE_IMPORT_NAMES.map((name) => ({
|
|
142
|
+
name,
|
|
143
|
+
pattern: new RegExp(`\\b${name}\\b`, 'u'),
|
|
144
|
+
}));
|
|
145
|
+
const PRELUDE_VALUE_IMPORT_PATTERNS = PRELUDE_VALUE_IMPORT_NAMES.map((name) => ({
|
|
146
|
+
name,
|
|
147
|
+
pattern: new RegExp(`\\b${name}\\b`, 'u'),
|
|
148
|
+
}));
|
|
149
|
+
const PRELUDE_MODULE_SPECIFIER = 'sts:prelude';
|
|
150
|
+
const EXPLICIT_FOREIGN_SOURCE_EXTENSION_PATTERN = /\.(?:[cm]?[jt]sx?|[cm]?js)$/u;
|
|
151
|
+
const EXPLICIT_FOREIGN_SOURCE_SPECIFIER_PATTERN =
|
|
152
|
+
/['"][^'"]+\.(?:[cm]?[jt]sx?|[cm]?js)['"]/u;
|
|
153
|
+
const SCRIPT_SCOPE_BUILTIN_INTERFACE_NAMES = new Set([
|
|
154
|
+
'Array',
|
|
155
|
+
'ArrayConstructor',
|
|
156
|
+
'Boolean',
|
|
157
|
+
'BooleanConstructor',
|
|
158
|
+
'CallableFunction',
|
|
159
|
+
'Date',
|
|
160
|
+
'DateConstructor',
|
|
161
|
+
'Function',
|
|
162
|
+
'Map',
|
|
163
|
+
'MapConstructor',
|
|
164
|
+
'NewableFunction',
|
|
165
|
+
'Number',
|
|
166
|
+
'NumberConstructor',
|
|
167
|
+
'Object',
|
|
168
|
+
'ObjectConstructor',
|
|
169
|
+
'Promise',
|
|
170
|
+
'PromiseConstructor',
|
|
171
|
+
'ReadonlyArray',
|
|
172
|
+
'RegExp',
|
|
173
|
+
'RegExpConstructor',
|
|
174
|
+
'Set',
|
|
175
|
+
'SetConstructor',
|
|
176
|
+
'String',
|
|
177
|
+
'StringConstructor',
|
|
178
|
+
'Symbol',
|
|
179
|
+
'SymbolConstructor',
|
|
180
|
+
'WeakMap',
|
|
181
|
+
'WeakMapConstructor',
|
|
182
|
+
'WeakSet',
|
|
183
|
+
'WeakSetConstructor',
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
function collectTopLevelBindingNames(sourceFile: ts.SourceFile): ReadonlySet<string> {
|
|
187
|
+
const names = new Set<string>();
|
|
188
|
+
|
|
189
|
+
const collectBindingName = (name: ts.BindingName): void => {
|
|
190
|
+
if (ts.isIdentifier(name)) {
|
|
191
|
+
names.add(name.text);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
for (const element of name.elements) {
|
|
195
|
+
if (!ts.isOmittedExpression(element)) {
|
|
196
|
+
collectBindingName(element.name);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
for (const statement of sourceFile.statements) {
|
|
202
|
+
if (ts.isImportDeclaration(statement)) {
|
|
203
|
+
if (statement.importClause?.name) {
|
|
204
|
+
names.add(statement.importClause.name.text);
|
|
205
|
+
}
|
|
206
|
+
const namedBindings = statement.importClause?.namedBindings;
|
|
207
|
+
if (namedBindings) {
|
|
208
|
+
if (ts.isNamespaceImport(namedBindings)) {
|
|
209
|
+
names.add(namedBindings.name.text);
|
|
210
|
+
} else {
|
|
211
|
+
for (const element of namedBindings.elements) {
|
|
212
|
+
names.add(element.name.text);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (
|
|
220
|
+
ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement) ||
|
|
221
|
+
ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement) ||
|
|
222
|
+
ts.isEnumDeclaration(statement)
|
|
223
|
+
) {
|
|
224
|
+
if (statement.name) {
|
|
225
|
+
names.add(statement.name.text);
|
|
226
|
+
}
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (ts.isVariableStatement(statement)) {
|
|
231
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
232
|
+
collectBindingName(declaration.name);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return names;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function injectPreludeImports(
|
|
241
|
+
fileName: string,
|
|
242
|
+
sourceText: string,
|
|
243
|
+
rewrittenText: string,
|
|
244
|
+
): string {
|
|
245
|
+
if (!isSoundscriptSourceFile(fileName) || isInstalledRuntimeStdlibSourceFile(fileName)) {
|
|
246
|
+
return rewrittenText;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const typeNames = PRELUDE_TYPE_IMPORT_PATTERNS
|
|
250
|
+
.filter(({ pattern }) => pattern.test(sourceText));
|
|
251
|
+
const valueNames = PRELUDE_VALUE_IMPORT_PATTERNS
|
|
252
|
+
.filter(({ pattern }) => pattern.test(sourceText));
|
|
253
|
+
|
|
254
|
+
if (typeNames.length === 0 && valueNames.length === 0) {
|
|
255
|
+
return rewrittenText;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const sourceFile = ts.createSourceFile(
|
|
259
|
+
fileName,
|
|
260
|
+
sourceText,
|
|
261
|
+
ts.ScriptTarget.Latest,
|
|
262
|
+
true,
|
|
263
|
+
ts.ScriptKind.TSX,
|
|
264
|
+
);
|
|
265
|
+
const topLevelBindingNames = collectTopLevelBindingNames(sourceFile);
|
|
266
|
+
const filteredTypeNames = typeNames
|
|
267
|
+
.filter(({ name }) => !topLevelBindingNames.has(name))
|
|
268
|
+
.map(({ name }) => name);
|
|
269
|
+
const filteredValueNames = valueNames
|
|
270
|
+
.filter(({ name }) => !topLevelBindingNames.has(name))
|
|
271
|
+
.map(({ name }) => name);
|
|
272
|
+
|
|
273
|
+
if (filteredTypeNames.length === 0 && filteredValueNames.length === 0) {
|
|
274
|
+
return rewrittenText;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const lines: string[] = [];
|
|
278
|
+
if (filteredTypeNames.length > 0) {
|
|
279
|
+
lines.push(
|
|
280
|
+
`import type { ${filteredTypeNames.join(', ')} } from '${PRELUDE_MODULE_SPECIFIER}';`,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (filteredValueNames.length > 0) {
|
|
284
|
+
lines.push(`import { ${filteredValueNames.join(', ')} } from '${PRELUDE_MODULE_SPECIFIER}';`);
|
|
285
|
+
}
|
|
286
|
+
lines.push('');
|
|
287
|
+
const { prefix, suffix } = splitLeadingNonAnnotationTrivia(rewrittenText);
|
|
288
|
+
return `${prefix}${lines.join('\n')}${suffix}`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function splitLeadingNonAnnotationTrivia(text: string): { prefix: string; suffix: string } {
|
|
292
|
+
let index = 0;
|
|
293
|
+
|
|
294
|
+
while (index < text.length) {
|
|
295
|
+
const whitespaceStart = index;
|
|
296
|
+
while (index < text.length && /\s/u.test(text[index] ?? '')) {
|
|
297
|
+
index += 1;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (text.startsWith('//', index)) {
|
|
301
|
+
const lineEnd = text.indexOf('\n', index);
|
|
302
|
+
const commentEnd = lineEnd === -1 ? text.length : lineEnd + 1;
|
|
303
|
+
const commentText = text.slice(index, commentEnd);
|
|
304
|
+
if (shouldStopPreludeInsertionAtComment(commentText)) {
|
|
305
|
+
index = whitespaceStart;
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
index = commentEnd;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (text.startsWith('/*', index)) {
|
|
313
|
+
const closeIndex = text.indexOf('*/', index + 2);
|
|
314
|
+
if (closeIndex === -1) {
|
|
315
|
+
index = whitespaceStart;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
const commentText = text.slice(index, closeIndex + 2);
|
|
319
|
+
if (shouldStopPreludeInsertionAtComment(commentText)) {
|
|
320
|
+
index = whitespaceStart;
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
index = closeIndex + 2;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
index = whitespaceStart;
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
prefix: text.slice(0, index),
|
|
333
|
+
suffix: text.slice(index),
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function shouldStopPreludeInsertionAtComment(commentText: string): boolean {
|
|
338
|
+
return /^\/\/\s*#\[/u.test(commentText) ||
|
|
339
|
+
(
|
|
340
|
+
/@ts-/u.test(commentText) &&
|
|
341
|
+
!/@ts-(?:no)?check\b/u.test(commentText)
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function isNodeModulesPath(fileName: string): boolean {
|
|
346
|
+
return fileName.includes('/node_modules/') || fileName.includes('\\node_modules\\');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function isInstalledRuntimeStdlibSourceFile(fileName: string): boolean {
|
|
350
|
+
const normalizedFileName = toSourceFileName(fileName).replaceAll('\\', '/');
|
|
351
|
+
return normalizedFileName.includes('/node_modules/@soundscript/soundscript/soundscript/') &&
|
|
352
|
+
normalizedFileName.endsWith('.sts');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function installedRuntimeStdlibDeclarationPath(fileName: string): string | null {
|
|
356
|
+
const sourceFileName = toSourceFileName(fileName).replaceAll('\\', '/');
|
|
357
|
+
const marker = '/node_modules/@soundscript/soundscript/soundscript/';
|
|
358
|
+
const markerIndex = sourceFileName.indexOf(marker);
|
|
359
|
+
if (markerIndex === -1 || !sourceFileName.endsWith('.sts')) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const packageRoot = sourceFileName.slice(0, markerIndex + '/node_modules/@soundscript/soundscript'.length);
|
|
364
|
+
const relativeSourcePath = sourceFileName.slice(markerIndex + marker.length);
|
|
365
|
+
const declarationRelativePath = relativeSourcePath.startsWith('experimental/')
|
|
366
|
+
? relativeSourcePath.replace(/^experimental\//u, 'experimental/').replace(/\.sts$/u, '.d.ts')
|
|
367
|
+
: relativeSourcePath.replace(/\.sts$/u, '.d.ts');
|
|
368
|
+
return `${packageRoot}/${declarationRelativePath}`;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function isIdentifierStart(character: string | undefined): boolean {
|
|
372
|
+
return character !== undefined && /[\p{ID_Start}_$]/u.test(character);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function isIdentifierPart(character: string | undefined): boolean {
|
|
376
|
+
return character !== undefined && /[\p{ID_Continue}_$\u200C\u200D]/u.test(character);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export interface PreparedSourceFile {
|
|
380
|
+
diagnostics: readonly MergedDiagnostic[];
|
|
381
|
+
originalText: string;
|
|
382
|
+
postRewriteStage?: PreparedRewriteStage;
|
|
383
|
+
rewriteResult: RewriteResult;
|
|
384
|
+
rewrittenText: string;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export interface PreparedRewriteStage {
|
|
388
|
+
lineMappings?: readonly PreparedRewriteStageLineMapping[];
|
|
389
|
+
replacements: readonly MacroReplacement[];
|
|
390
|
+
rewrittenText: string;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export interface PreparedRewriteStageLineMapping {
|
|
394
|
+
originalEnd: number;
|
|
395
|
+
originalStart: number;
|
|
396
|
+
rewrittenEnd: number;
|
|
397
|
+
rewrittenStart: number;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export interface MappedProgramPosition {
|
|
401
|
+
insideReplacement: boolean;
|
|
402
|
+
position: number;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export interface MappedSourceRange {
|
|
406
|
+
end: number;
|
|
407
|
+
intersectsReplacement: boolean;
|
|
408
|
+
start: number;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export interface PreparedCompilerHost {
|
|
412
|
+
dispose(): void;
|
|
413
|
+
frontendDiagnostics(): readonly MergedDiagnostic[];
|
|
414
|
+
getPreparedSourceFile(fileName: string): PreparedSourceFile | undefined;
|
|
415
|
+
getCachedPreparedSourceFiles(): readonly PreparedSourceFile[];
|
|
416
|
+
getMacroPlaceholderIndex(): MacroPlaceholderIndex;
|
|
417
|
+
host: ts.CompilerHost;
|
|
418
|
+
reuseState: PreparedCompilerHostReuseState;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
interface CachedPreparedSourceFileEntry {
|
|
422
|
+
environmentSignature: string;
|
|
423
|
+
expansionEnabled: boolean;
|
|
424
|
+
importedMacroSiteKindsSignature: string;
|
|
425
|
+
prepared: PreparedSourceFile;
|
|
426
|
+
preserveMacroAuthoring: boolean;
|
|
427
|
+
sourceText: string;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
interface CachedSourceFileEntry {
|
|
431
|
+
environmentSignature?: string;
|
|
432
|
+
sourceFile: ts.SourceFile;
|
|
433
|
+
text: string;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export interface CachedMacroModuleArtifactEntry {
|
|
437
|
+
dependencySourceTexts: ReadonlyMap<string, string>;
|
|
438
|
+
javaScriptText: string;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export interface PreparedCompilerHostReuseState {
|
|
442
|
+
macroModuleArtifactCache: Map<string, CachedMacroModuleArtifactEntry>;
|
|
443
|
+
moduleResolutionCache: ts.ModuleResolutionCache;
|
|
444
|
+
moduleResolutionCacheSignature: string;
|
|
445
|
+
preparedSourceFiles: Map<string, CachedPreparedSourceFileEntry>;
|
|
446
|
+
projectedDeclarationBuilderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram | undefined;
|
|
447
|
+
projectedDeclarationOutputs: ReadonlyMap<string, string> | undefined;
|
|
448
|
+
projectedDeclarationOptionSignature: string;
|
|
449
|
+
projectedDeclarationProgram: ts.Program | undefined;
|
|
450
|
+
projectedDeclarationRootNamesSignature: string;
|
|
451
|
+
projectedDeclarationSourceFiles: Map<string, CachedSourceFileEntry>;
|
|
452
|
+
rewrittenSourceFiles: Map<string, CachedSourceFileEntry>;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export interface CreatePreparedProgramOptions {
|
|
456
|
+
alwaysAvailableMacroSiteKinds?: ReadonlyMap<string, ImportedMacroSiteKind>;
|
|
457
|
+
baseHost: ts.CompilerHost;
|
|
458
|
+
configFileParsingDiagnostics?: readonly ts.Diagnostic[];
|
|
459
|
+
expansionEnabled?: boolean;
|
|
460
|
+
fileOverrides?: ReadonlyMap<string, string>;
|
|
461
|
+
invalidateModuleResolutions?: boolean;
|
|
462
|
+
importedMacroSiteKindsBySpecifier?: ReadonlyMap<
|
|
463
|
+
string,
|
|
464
|
+
ReadonlyMap<string, ImportedMacroSiteKind>
|
|
465
|
+
>;
|
|
466
|
+
oldProgram?: ts.Program;
|
|
467
|
+
options: ts.CompilerOptions;
|
|
468
|
+
projectReferences?: readonly ts.ProjectReference[];
|
|
469
|
+
projectedDeclarationOverrides?: ReadonlyMap<string, string>;
|
|
470
|
+
preserveMacroAuthoring?: boolean;
|
|
471
|
+
runtime?: RuntimeContext;
|
|
472
|
+
reusableCompilerHostState?: PreparedCompilerHostReuseState;
|
|
473
|
+
rootNames: readonly string[];
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export type { ImportedMacroSiteKind };
|
|
477
|
+
|
|
478
|
+
export interface PreparedProgram {
|
|
479
|
+
dispose(clearReuseState?: boolean): void;
|
|
480
|
+
frontendDiagnostics(): readonly MergedDiagnostic[];
|
|
481
|
+
options: ts.CompilerOptions;
|
|
482
|
+
placeholderIndex(): MacroPlaceholderIndex;
|
|
483
|
+
preparedHost: PreparedCompilerHost;
|
|
484
|
+
program: ts.Program;
|
|
485
|
+
runtime: RuntimeContext;
|
|
486
|
+
rootNames: readonly string[];
|
|
487
|
+
toProgramFileName(fileName: string): string;
|
|
488
|
+
toProjectedDeclarationFileName(fileName: string): string;
|
|
489
|
+
toSourceFileName(fileName: string): string;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const DEFAULT_PREPARED_PROGRAM_RUNTIME = normalizeRuntimeContext({
|
|
493
|
+
externs: [],
|
|
494
|
+
target: 'js-node',
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
export function createPreparedCompilerHostReuseState(
|
|
498
|
+
currentDirectory = ts.sys.getCurrentDirectory(),
|
|
499
|
+
): PreparedCompilerHostReuseState {
|
|
500
|
+
const getCanonicalFileName = ts.sys.useCaseSensitiveFileNames
|
|
501
|
+
? (fileName: string) => fileName
|
|
502
|
+
: (fileName: string) => fileName.toLowerCase();
|
|
503
|
+
return {
|
|
504
|
+
macroModuleArtifactCache: new Map(),
|
|
505
|
+
moduleResolutionCache: ts.createModuleResolutionCache(
|
|
506
|
+
currentDirectory,
|
|
507
|
+
getCanonicalFileName,
|
|
508
|
+
),
|
|
509
|
+
moduleResolutionCacheSignature: '',
|
|
510
|
+
preparedSourceFiles: new Map(),
|
|
511
|
+
projectedDeclarationBuilderProgram: undefined,
|
|
512
|
+
projectedDeclarationOutputs: undefined,
|
|
513
|
+
projectedDeclarationOptionSignature: '',
|
|
514
|
+
projectedDeclarationProgram: undefined,
|
|
515
|
+
projectedDeclarationRootNamesSignature: '',
|
|
516
|
+
projectedDeclarationSourceFiles: new Map(),
|
|
517
|
+
rewrittenSourceFiles: new Map(),
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export function clearPreparedCompilerHostReuseState(
|
|
522
|
+
reusableState: PreparedCompilerHostReuseState,
|
|
523
|
+
): void {
|
|
524
|
+
reusableState.macroModuleArtifactCache.clear();
|
|
525
|
+
reusableState.preparedSourceFiles.clear();
|
|
526
|
+
reusableState.projectedDeclarationBuilderProgram = undefined;
|
|
527
|
+
reusableState.projectedDeclarationOutputs = undefined;
|
|
528
|
+
reusableState.projectedDeclarationProgram = undefined;
|
|
529
|
+
reusableState.projectedDeclarationSourceFiles.clear();
|
|
530
|
+
reusableState.rewrittenSourceFiles.clear();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function createProjectedDeclarationPresenceSignature(
|
|
534
|
+
projectedDeclarationOverrides: ReadonlyMap<string, string>,
|
|
535
|
+
): string {
|
|
536
|
+
if (projectedDeclarationOverrides.size === 0) {
|
|
537
|
+
return '';
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return [...projectedDeclarationOverrides.keys()]
|
|
541
|
+
.sort((left, right) => left.localeCompare(right))
|
|
542
|
+
.map((fileName) => fileName)
|
|
543
|
+
.join('|');
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export function isUnsoundImportedModuleForTypeProjection(
|
|
547
|
+
moduleSpecifier: string,
|
|
548
|
+
containingFile: string,
|
|
549
|
+
compilerOptions: ts.CompilerOptions,
|
|
550
|
+
host: ts.ModuleResolutionHost,
|
|
551
|
+
): boolean {
|
|
552
|
+
const resolvedModule = resolveSoundScriptAwareModule(
|
|
553
|
+
moduleSpecifier,
|
|
554
|
+
containingFile,
|
|
555
|
+
compilerOptions,
|
|
556
|
+
host,
|
|
557
|
+
);
|
|
558
|
+
if (!resolvedModule) {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const importedFileName = resolvedModule.resolvedFileName;
|
|
563
|
+
const importedIsTrustedPackageArtifact = isNodeModulesPath(importedFileName) &&
|
|
564
|
+
!isForeignPackageSourceFile(importedFileName, host);
|
|
565
|
+
|
|
566
|
+
if (
|
|
567
|
+
importedFileName.endsWith('.d.ts') || importedFileName.endsWith('.d.mts') ||
|
|
568
|
+
importedFileName.endsWith('.d.cts')
|
|
569
|
+
) {
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (isForeignResolvedModule(moduleSpecifier, resolvedModule, host)) {
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const importedSourceFileName = toProjectedDeclarationSourceFileName(
|
|
578
|
+
toSourceFileName(importedFileName),
|
|
579
|
+
);
|
|
580
|
+
return isSoundscriptSourceFile(containingFile) &&
|
|
581
|
+
EXPLICIT_FOREIGN_SOURCE_EXTENSION_PATTERN.test(moduleSpecifier) &&
|
|
582
|
+
!isSoundscriptSourceFile(importedSourceFileName) &&
|
|
583
|
+
!importedIsTrustedPackageArtifact;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function hasExportModifier(node: ts.Node): boolean {
|
|
587
|
+
return ts.canHaveModifiers(node) &&
|
|
588
|
+
(ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ??
|
|
589
|
+
false);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function isExplicitAnyTypeNode(typeNode: ts.TypeNode | undefined): boolean {
|
|
593
|
+
return typeNode?.kind === ts.SyntaxKind.AnyKeyword;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function collectLocallyDeclaredExplicitAnyValueNames(
|
|
597
|
+
sourceFile: ts.SourceFile,
|
|
598
|
+
): ReadonlySet<string> {
|
|
599
|
+
const names = new Set<string>();
|
|
600
|
+
|
|
601
|
+
for (const statement of sourceFile.statements) {
|
|
602
|
+
if (ts.isVariableStatement(statement)) {
|
|
603
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
604
|
+
if (ts.isIdentifier(declaration.name) && isExplicitAnyTypeNode(declaration.type)) {
|
|
605
|
+
names.add(declaration.name.text);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (
|
|
612
|
+
ts.isFunctionDeclaration(statement) && statement.name && isExplicitAnyTypeNode(statement.type)
|
|
613
|
+
) {
|
|
614
|
+
names.add(statement.name.text);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return names;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export function collectProjectedUnknownValueExportNames(
|
|
622
|
+
moduleSpecifier: string,
|
|
623
|
+
containingFile: string,
|
|
624
|
+
compilerOptions: ts.CompilerOptions,
|
|
625
|
+
host: ts.ModuleResolutionHost,
|
|
626
|
+
): ReadonlySet<string> {
|
|
627
|
+
if (
|
|
628
|
+
!isUnsoundImportedModuleForTypeProjection(
|
|
629
|
+
moduleSpecifier,
|
|
630
|
+
containingFile,
|
|
631
|
+
compilerOptions,
|
|
632
|
+
host,
|
|
633
|
+
)
|
|
634
|
+
) {
|
|
635
|
+
return new Set();
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const resolvedModule = resolveSoundScriptAwareModule(
|
|
639
|
+
moduleSpecifier,
|
|
640
|
+
containingFile,
|
|
641
|
+
compilerOptions,
|
|
642
|
+
host,
|
|
643
|
+
);
|
|
644
|
+
const importedFileName = resolvedModule?.resolvedFileName;
|
|
645
|
+
if (!importedFileName) {
|
|
646
|
+
return new Set();
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const importedSourceFileName = toSourceFileName(importedFileName);
|
|
650
|
+
const sourceText = host.readFile?.(importedSourceFileName);
|
|
651
|
+
if (!sourceText) {
|
|
652
|
+
return new Set();
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const sourceFile = ts.createSourceFile(
|
|
656
|
+
importedSourceFileName,
|
|
657
|
+
sourceText,
|
|
658
|
+
ts.ScriptTarget.Latest,
|
|
659
|
+
true,
|
|
660
|
+
);
|
|
661
|
+
const locallyDeclaredAnyNames = collectLocallyDeclaredExplicitAnyValueNames(sourceFile);
|
|
662
|
+
if (locallyDeclaredAnyNames.size === 0) {
|
|
663
|
+
return new Set();
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const exportedNames = new Set<string>();
|
|
667
|
+
for (const statement of sourceFile.statements) {
|
|
668
|
+
if (ts.isVariableStatement(statement) && hasExportModifier(statement)) {
|
|
669
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
670
|
+
if (ts.isIdentifier(declaration.name) && isExplicitAnyTypeNode(declaration.type)) {
|
|
671
|
+
exportedNames.add(declaration.name.text);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if (
|
|
678
|
+
ts.isFunctionDeclaration(statement) && statement.name && hasExportModifier(statement) &&
|
|
679
|
+
isExplicitAnyTypeNode(statement.type)
|
|
680
|
+
) {
|
|
681
|
+
exportedNames.add(statement.name.text);
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if (
|
|
686
|
+
ts.isExportDeclaration(statement) && !statement.moduleSpecifier && statement.exportClause &&
|
|
687
|
+
ts.isNamedExports(statement.exportClause)
|
|
688
|
+
) {
|
|
689
|
+
for (const element of statement.exportClause.elements) {
|
|
690
|
+
const localName = (element.propertyName ?? element.name).text;
|
|
691
|
+
if (locallyDeclaredAnyNames.has(localName)) {
|
|
692
|
+
exportedNames.add(element.name.text);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
return exportedNames;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function createProjectedUnknownValueAlias(
|
|
702
|
+
name: string,
|
|
703
|
+
hiddenLocalName: string,
|
|
704
|
+
): ts.VariableStatement {
|
|
705
|
+
return ts.factory.createVariableStatement(
|
|
706
|
+
undefined,
|
|
707
|
+
ts.factory.createVariableDeclarationList(
|
|
708
|
+
[
|
|
709
|
+
ts.factory.createVariableDeclaration(
|
|
710
|
+
ts.factory.createIdentifier(name),
|
|
711
|
+
undefined,
|
|
712
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
|
|
713
|
+
ts.factory.createIdentifier(hiddenLocalName),
|
|
714
|
+
),
|
|
715
|
+
],
|
|
716
|
+
ts.NodeFlags.Const,
|
|
717
|
+
),
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function createHiddenImportSpecifier(
|
|
722
|
+
element: ts.ImportSpecifier,
|
|
723
|
+
hiddenLocalName: string,
|
|
724
|
+
): ts.ImportSpecifier {
|
|
725
|
+
return ts.factory.updateImportSpecifier(
|
|
726
|
+
element,
|
|
727
|
+
false,
|
|
728
|
+
element.propertyName ?? element.name,
|
|
729
|
+
ts.factory.createIdentifier(hiddenLocalName),
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function importClauseAlreadyUsesProjectedBindings(importClause: ts.ImportClause): boolean {
|
|
734
|
+
if (importClause.name && PROJECTED_BINDING_NAME_PATTERN.test(importClause.name.text)) {
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const namedBindings = importClause.namedBindings;
|
|
739
|
+
if (!namedBindings) {
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (ts.isNamespaceImport(namedBindings)) {
|
|
744
|
+
return PROJECTED_BINDING_NAME_PATTERN.test(namedBindings.name.text);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
return namedBindings.elements.some((element) =>
|
|
748
|
+
PROJECTED_BINDING_NAME_PATTERN.test(element.name.text)
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function rewriteForeignImportStatement(
|
|
753
|
+
statement: ts.ImportDeclaration,
|
|
754
|
+
nextHiddenName: () => string,
|
|
755
|
+
projectedUnknownValueExportNames: ReadonlySet<string>,
|
|
756
|
+
): readonly ts.Statement[] | null {
|
|
757
|
+
const importClause = statement.importClause;
|
|
758
|
+
if (!importClause) {
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (importClauseAlreadyUsesProjectedBindings(importClause)) {
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const projectedValueBindings: Array<{ hiddenLocalName: string; name: string }> = [];
|
|
767
|
+
let updatedImportClause: ts.ImportClause | undefined;
|
|
768
|
+
|
|
769
|
+
if (importClause.isTypeOnly) {
|
|
770
|
+
return null;
|
|
771
|
+
} else {
|
|
772
|
+
let hiddenNamedBindings = importClause.namedBindings;
|
|
773
|
+
if (importClause.namedBindings) {
|
|
774
|
+
if (ts.isNamespaceImport(importClause.namedBindings)) {
|
|
775
|
+
// Preserve namespace imports so sound diagnostics can report the missing
|
|
776
|
+
// interop boundary instead of TypeScript collapsing the namespace to
|
|
777
|
+
// `unknown` and short-circuiting the more specific error.
|
|
778
|
+
return null;
|
|
779
|
+
} else {
|
|
780
|
+
const hiddenImportElements = importClause.namedBindings.elements.map((element) => {
|
|
781
|
+
if (element.isTypeOnly) {
|
|
782
|
+
return element;
|
|
783
|
+
} else {
|
|
784
|
+
const importedName = (element.propertyName ?? element.name).text;
|
|
785
|
+
if (!projectedUnknownValueExportNames.has(importedName)) {
|
|
786
|
+
return element;
|
|
787
|
+
}
|
|
788
|
+
const hiddenLocalName = nextHiddenName().replace('_type_', '_value_');
|
|
789
|
+
projectedValueBindings.push({ name: element.name.text, hiddenLocalName });
|
|
790
|
+
return createHiddenImportSpecifier(element, hiddenLocalName);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
hiddenNamedBindings = ts.factory.updateNamedImports(
|
|
794
|
+
importClause.namedBindings,
|
|
795
|
+
hiddenImportElements,
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (projectedValueBindings.length === 0) {
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
updatedImportClause = ts.factory.updateImportClause(
|
|
805
|
+
importClause,
|
|
806
|
+
false,
|
|
807
|
+
importClause.name,
|
|
808
|
+
hiddenNamedBindings,
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if (
|
|
813
|
+
projectedValueBindings.length === 0 ||
|
|
814
|
+
!updatedImportClause
|
|
815
|
+
) {
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
return [
|
|
820
|
+
ts.factory.updateImportDeclaration(
|
|
821
|
+
statement,
|
|
822
|
+
statement.modifiers,
|
|
823
|
+
updatedImportClause,
|
|
824
|
+
statement.moduleSpecifier,
|
|
825
|
+
statement.attributes,
|
|
826
|
+
),
|
|
827
|
+
...projectedValueBindings.map(({ name, hiddenLocalName }) =>
|
|
828
|
+
createProjectedUnknownValueAlias(name, hiddenLocalName)
|
|
829
|
+
),
|
|
830
|
+
];
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function hasDirectAnnotationComment(
|
|
834
|
+
sourceFile: ts.SourceFile,
|
|
835
|
+
node: ts.Node,
|
|
836
|
+
annotationName: string,
|
|
837
|
+
): boolean {
|
|
838
|
+
return createAnnotationLookup(sourceFile).hasAttachedAnnotation(node, annotationName);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function rewriteForeignTypeImportsToUnknown(
|
|
842
|
+
fileName: string,
|
|
843
|
+
sourceText: string,
|
|
844
|
+
compilerOptions: ts.CompilerOptions,
|
|
845
|
+
host: ts.ModuleResolutionHost,
|
|
846
|
+
importedMacroSiteKindsBySpecifier: ReadonlyMap<
|
|
847
|
+
string,
|
|
848
|
+
ReadonlyMap<string, ImportedMacroSiteKind>
|
|
849
|
+
>,
|
|
850
|
+
): string {
|
|
851
|
+
if (!isSoundscriptSourceFile(fileName)) {
|
|
852
|
+
return sourceText;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (!EXPLICIT_FOREIGN_SOURCE_SPECIFIER_PATTERN.test(sourceText)) {
|
|
856
|
+
return sourceText;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
const sourceFile = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest, true);
|
|
860
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
861
|
+
const replacements: Array<{ end: number; start: number; text: string }> = [];
|
|
862
|
+
let hiddenCounter = 0;
|
|
863
|
+
const projectedUnknownValueExportNamesBySpecifier = new Map<string, ReadonlySet<string>>();
|
|
864
|
+
|
|
865
|
+
const nextHiddenName = () => `__sts_projected_type_${hiddenCounter++}`;
|
|
866
|
+
|
|
867
|
+
for (const statement of sourceFile.statements) {
|
|
868
|
+
if (
|
|
869
|
+
!ts.isImportDeclaration(statement) ||
|
|
870
|
+
!statement.importClause ||
|
|
871
|
+
!ts.isStringLiteral(statement.moduleSpecifier)
|
|
872
|
+
) {
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
if (importedMacroSiteKindsBySpecifier.get(statement.moduleSpecifier.text)?.size) {
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (
|
|
881
|
+
!isUnsoundImportedModuleForTypeProjection(
|
|
882
|
+
statement.moduleSpecifier.text,
|
|
883
|
+
fileName,
|
|
884
|
+
compilerOptions,
|
|
885
|
+
host,
|
|
886
|
+
)
|
|
887
|
+
) {
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
let projectedUnknownValueExportNames = projectedUnknownValueExportNamesBySpecifier.get(
|
|
892
|
+
statement.moduleSpecifier.text,
|
|
893
|
+
);
|
|
894
|
+
if (!projectedUnknownValueExportNames) {
|
|
895
|
+
projectedUnknownValueExportNames = collectProjectedUnknownValueExportNames(
|
|
896
|
+
statement.moduleSpecifier.text,
|
|
897
|
+
fileName,
|
|
898
|
+
compilerOptions,
|
|
899
|
+
host,
|
|
900
|
+
);
|
|
901
|
+
projectedUnknownValueExportNamesBySpecifier.set(
|
|
902
|
+
statement.moduleSpecifier.text,
|
|
903
|
+
projectedUnknownValueExportNames,
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
const rewrittenStatements = rewriteForeignImportStatement(
|
|
908
|
+
statement,
|
|
909
|
+
nextHiddenName,
|
|
910
|
+
projectedUnknownValueExportNames,
|
|
911
|
+
);
|
|
912
|
+
if (!rewrittenStatements) {
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
const leadingCommentRanges =
|
|
917
|
+
ts.getLeadingCommentRanges(sourceFile.text, statement.getFullStart()) ??
|
|
918
|
+
[];
|
|
919
|
+
const replacementStart = leadingCommentRanges[0]?.pos ?? statement.getStart(sourceFile);
|
|
920
|
+
|
|
921
|
+
replacements.push({
|
|
922
|
+
end: statement.getEnd(),
|
|
923
|
+
start: replacementStart,
|
|
924
|
+
text: rewrittenStatements.map((node) =>
|
|
925
|
+
printer.printNode(ts.EmitHint.Unspecified, node, sourceFile)
|
|
926
|
+
).join(' '),
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
if (replacements.length === 0) {
|
|
931
|
+
return sourceText;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
let result = '';
|
|
935
|
+
let cursor = 0;
|
|
936
|
+
for (const replacement of replacements) {
|
|
937
|
+
result += sourceText.slice(cursor, replacement.start);
|
|
938
|
+
result += replacement.text;
|
|
939
|
+
cursor = replacement.end;
|
|
940
|
+
}
|
|
941
|
+
result += sourceText.slice(cursor);
|
|
942
|
+
return result;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
function ensureSourceFileVersion(sourceFile: ts.SourceFile, text: string): ts.SourceFile {
|
|
946
|
+
(sourceFile as ts.SourceFile & { version?: string }).version ??= `${text.length}:${
|
|
947
|
+
fnv1aHash(text).toString(16)
|
|
948
|
+
}`;
|
|
949
|
+
return sourceFile;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function stableStringify(value: unknown): string {
|
|
953
|
+
if (value === null || typeof value !== 'object') {
|
|
954
|
+
return JSON.stringify(value);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
if (Array.isArray(value)) {
|
|
958
|
+
return `[${value.map((entry) => stableStringify(entry)).join(',')}]`;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
const objectValue = value as Record<string, unknown>;
|
|
962
|
+
const entries = Object.keys(objectValue)
|
|
963
|
+
.sort()
|
|
964
|
+
.map((key) => `${JSON.stringify(key)}:${stableStringify(objectValue[key])}`);
|
|
965
|
+
return `{${entries.join(',')}}`;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function fnv1aHash(text: string, seed = 0x811c9dc5): number {
|
|
969
|
+
let hash = seed >>> 0;
|
|
970
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
971
|
+
hash ^= text.charCodeAt(index);
|
|
972
|
+
hash = Math.imul(hash, 0x01000193) >>> 0;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
return hash >>> 0;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
function getResolvedExportSymbol(
|
|
979
|
+
checker: ts.TypeChecker,
|
|
980
|
+
symbol: ts.Symbol | undefined,
|
|
981
|
+
): ts.Symbol | undefined {
|
|
982
|
+
if (!symbol) {
|
|
983
|
+
return undefined;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
return (symbol.flags & ts.SymbolFlags.Alias) !== 0 ? checker.getAliasedSymbol(symbol) : symbol;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
function hasAttachedNewtypeAnnotation(
|
|
990
|
+
sourceFile: ts.SourceFile,
|
|
991
|
+
declaration: ts.TypeAliasDeclaration,
|
|
992
|
+
): boolean {
|
|
993
|
+
return createAnnotationLookup(sourceFile).hasAttachedAnnotation(declaration, 'newtype');
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
function collectExportedNewtypeAliasNames(
|
|
997
|
+
preparedProgram: PreparedProgram,
|
|
998
|
+
sourceFileName: string,
|
|
999
|
+
): ReadonlySet<string> {
|
|
1000
|
+
const preparedSourceFile = preparedProgram.preparedHost.getPreparedSourceFile(sourceFileName);
|
|
1001
|
+
if (!preparedSourceFile) {
|
|
1002
|
+
return new Set();
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
const sourceFile = preparedProgram.program.getSourceFile(
|
|
1006
|
+
preparedProgram.toProgramFileName(sourceFileName),
|
|
1007
|
+
);
|
|
1008
|
+
if (!sourceFile) {
|
|
1009
|
+
return new Set();
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
const originalSourceFile = ts.createSourceFile(
|
|
1013
|
+
sourceFileName,
|
|
1014
|
+
preparedSourceFile.originalText,
|
|
1015
|
+
ts.ScriptTarget.Latest,
|
|
1016
|
+
true,
|
|
1017
|
+
ts.ScriptKind.TS,
|
|
1018
|
+
);
|
|
1019
|
+
const annotatedAliasNames = new Set<string>();
|
|
1020
|
+
for (const statement of originalSourceFile.statements) {
|
|
1021
|
+
if (
|
|
1022
|
+
ts.isTypeAliasDeclaration(statement) &&
|
|
1023
|
+
hasAttachedNewtypeAnnotation(originalSourceFile, statement)
|
|
1024
|
+
) {
|
|
1025
|
+
annotatedAliasNames.add(statement.name.text);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
if (annotatedAliasNames.size === 0) {
|
|
1029
|
+
return new Set();
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
const checker = preparedProgram.program.getTypeChecker();
|
|
1033
|
+
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
|
|
1034
|
+
const exportedSymbols = new Set<ts.Symbol>();
|
|
1035
|
+
if (moduleSymbol) {
|
|
1036
|
+
for (const exportSymbol of checker.getExportsOfModule(moduleSymbol)) {
|
|
1037
|
+
const resolvedExportSymbol = getResolvedExportSymbol(checker, exportSymbol);
|
|
1038
|
+
if (resolvedExportSymbol) {
|
|
1039
|
+
exportedSymbols.add(resolvedExportSymbol);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
const aliasNames = new Set<string>();
|
|
1045
|
+
for (const statement of sourceFile.statements) {
|
|
1046
|
+
if (!ts.isTypeAliasDeclaration(statement) || !annotatedAliasNames.has(statement.name.text)) {
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
const declarationSymbol = getResolvedExportSymbol(
|
|
1051
|
+
checker,
|
|
1052
|
+
checker.getSymbolAtLocation(statement.name),
|
|
1053
|
+
);
|
|
1054
|
+
if (declarationSymbol && exportedSymbols.has(declarationSymbol)) {
|
|
1055
|
+
aliasNames.add(statement.name.text);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
return aliasNames;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
function createProjectedNewtypeBrandName(sourceFileName: string, aliasName: string): string {
|
|
1063
|
+
return `__soundscript_newtype_${fnv1aHash(`${sourceFileName}:${aliasName}`).toString(16)}_brand`;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
function createProjectedNewtypeBrandDeclaration(brandName: string): ts.VariableStatement {
|
|
1067
|
+
return ts.factory.createVariableStatement(
|
|
1068
|
+
[ts.factory.createModifier(ts.SyntaxKind.DeclareKeyword)],
|
|
1069
|
+
ts.factory.createVariableDeclarationList(
|
|
1070
|
+
[
|
|
1071
|
+
ts.factory.createVariableDeclaration(
|
|
1072
|
+
ts.factory.createIdentifier(brandName),
|
|
1073
|
+
undefined,
|
|
1074
|
+
ts.factory.createTypeOperatorNode(
|
|
1075
|
+
ts.SyntaxKind.UniqueKeyword,
|
|
1076
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.SymbolKeyword),
|
|
1077
|
+
),
|
|
1078
|
+
undefined,
|
|
1079
|
+
),
|
|
1080
|
+
],
|
|
1081
|
+
ts.NodeFlags.Const,
|
|
1082
|
+
),
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
function createProjectedBrandedNewtypeAliasDeclaration(
|
|
1087
|
+
statement: ts.TypeAliasDeclaration,
|
|
1088
|
+
brandName: string,
|
|
1089
|
+
): ts.TypeAliasDeclaration {
|
|
1090
|
+
return ts.factory.updateTypeAliasDeclaration(
|
|
1091
|
+
statement,
|
|
1092
|
+
statement.modifiers,
|
|
1093
|
+
statement.name,
|
|
1094
|
+
statement.typeParameters,
|
|
1095
|
+
ts.factory.createIntersectionTypeNode([
|
|
1096
|
+
statement.type,
|
|
1097
|
+
ts.factory.createTypeLiteralNode([
|
|
1098
|
+
ts.factory.createPropertySignature(
|
|
1099
|
+
[ts.factory.createModifier(ts.SyntaxKind.ReadonlyKeyword)],
|
|
1100
|
+
ts.factory.createComputedPropertyName(ts.factory.createIdentifier(brandName)),
|
|
1101
|
+
undefined,
|
|
1102
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword),
|
|
1103
|
+
),
|
|
1104
|
+
]),
|
|
1105
|
+
]),
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
function rewriteProjectedDeclarationNewtypes(
|
|
1110
|
+
preparedProgram: PreparedProgram,
|
|
1111
|
+
sourceFileName: string,
|
|
1112
|
+
declarationText: string,
|
|
1113
|
+
): string {
|
|
1114
|
+
const exportedNewtypeAliasNames = collectExportedNewtypeAliasNames(
|
|
1115
|
+
preparedProgram,
|
|
1116
|
+
sourceFileName,
|
|
1117
|
+
);
|
|
1118
|
+
if (exportedNewtypeAliasNames.size === 0) {
|
|
1119
|
+
return declarationText;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
const declarationSourceFile = ts.createSourceFile(
|
|
1123
|
+
preparedProgram.toProjectedDeclarationFileName(sourceFileName),
|
|
1124
|
+
declarationText,
|
|
1125
|
+
ts.ScriptTarget.Latest,
|
|
1126
|
+
true,
|
|
1127
|
+
ts.ScriptKind.TS,
|
|
1128
|
+
);
|
|
1129
|
+
let changed = false;
|
|
1130
|
+
const statements: ts.Statement[] = [];
|
|
1131
|
+
|
|
1132
|
+
for (const statement of declarationSourceFile.statements) {
|
|
1133
|
+
if (
|
|
1134
|
+
ts.isTypeAliasDeclaration(statement) &&
|
|
1135
|
+
exportedNewtypeAliasNames.has(statement.name.text)
|
|
1136
|
+
) {
|
|
1137
|
+
const brandName = createProjectedNewtypeBrandName(sourceFileName, statement.name.text);
|
|
1138
|
+
statements.push(createProjectedNewtypeBrandDeclaration(brandName));
|
|
1139
|
+
statements.push(createProjectedBrandedNewtypeAliasDeclaration(statement, brandName));
|
|
1140
|
+
changed = true;
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
statements.push(statement);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
if (!changed) {
|
|
1148
|
+
return declarationText;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
return ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }).printFile(
|
|
1152
|
+
ts.factory.updateSourceFile(declarationSourceFile, statements),
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function rewriteProjectedDeclarationInternalNumericReferences(declarationText: string): string {
|
|
1157
|
+
const declarationSourceFile = ts.createSourceFile(
|
|
1158
|
+
'projected-internal-numerics.d.ts',
|
|
1159
|
+
declarationText,
|
|
1160
|
+
ts.ScriptTarget.Latest,
|
|
1161
|
+
true,
|
|
1162
|
+
ts.ScriptKind.TS,
|
|
1163
|
+
);
|
|
1164
|
+
let changed = false;
|
|
1165
|
+
const transformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
|
|
1166
|
+
const visit = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
|
1167
|
+
if (
|
|
1168
|
+
ts.isImportDeclaration(node) &&
|
|
1169
|
+
ts.isStringLiteral(node.moduleSpecifier) &&
|
|
1170
|
+
node.moduleSpecifier.text === 'sts:numerics' &&
|
|
1171
|
+
node.importClause?.isTypeOnly &&
|
|
1172
|
+
node.importClause.namedBindings &&
|
|
1173
|
+
ts.isNamedImports(node.importClause.namedBindings)
|
|
1174
|
+
) {
|
|
1175
|
+
let importChanged = false;
|
|
1176
|
+
const seenSpecifiers = new Set<string>();
|
|
1177
|
+
const elements: ts.ImportSpecifier[] = [];
|
|
1178
|
+
for (const element of node.importClause.namedBindings.elements) {
|
|
1179
|
+
let nextPropertyName = element.propertyName;
|
|
1180
|
+
let nextName = element.name;
|
|
1181
|
+
if (
|
|
1182
|
+
element.propertyName &&
|
|
1183
|
+
element.propertyName.text === 'f64' &&
|
|
1184
|
+
isElaboratedF64TypeImportName(element.name.text)
|
|
1185
|
+
) {
|
|
1186
|
+
importChanged = true;
|
|
1187
|
+
changed = true;
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
if (
|
|
1192
|
+
element.propertyName &&
|
|
1193
|
+
element.propertyName.text === ELABORATED_BIGINT_TYPE_EXPORT_NAME &&
|
|
1194
|
+
isElaboratedBigIntTypeImportName(element.name.text)
|
|
1195
|
+
) {
|
|
1196
|
+
importChanged = true;
|
|
1197
|
+
changed = true;
|
|
1198
|
+
continue;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
const dedupeKey = `${nextPropertyName?.text ?? ''}:${nextName.text}`;
|
|
1202
|
+
if (seenSpecifiers.has(dedupeKey)) {
|
|
1203
|
+
importChanged = true;
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
seenSpecifiers.add(dedupeKey);
|
|
1207
|
+
elements.push(
|
|
1208
|
+
nextPropertyName === element.propertyName && nextName === element.name
|
|
1209
|
+
? element
|
|
1210
|
+
: ts.factory.createImportSpecifier(false, nextPropertyName, nextName),
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
if (importChanged) {
|
|
1215
|
+
changed = true;
|
|
1216
|
+
if (elements.length === 0 && !node.importClause.name) {
|
|
1217
|
+
return ts.factory.createNotEmittedStatement(node);
|
|
1218
|
+
}
|
|
1219
|
+
return ts.factory.updateImportDeclaration(
|
|
1220
|
+
node,
|
|
1221
|
+
node.modifiers,
|
|
1222
|
+
ts.factory.updateImportClause(
|
|
1223
|
+
node.importClause,
|
|
1224
|
+
node.importClause.isTypeOnly,
|
|
1225
|
+
node.importClause.name,
|
|
1226
|
+
ts.factory.updateNamedImports(node.importClause.namedBindings, elements),
|
|
1227
|
+
),
|
|
1228
|
+
node.moduleSpecifier,
|
|
1229
|
+
node.attributes,
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
if (
|
|
1235
|
+
ts.isTypeReferenceNode(node) &&
|
|
1236
|
+
ts.isIdentifier(node.typeName) &&
|
|
1237
|
+
isElaboratedF64TypeImportName(node.typeName.text)
|
|
1238
|
+
) {
|
|
1239
|
+
changed = true;
|
|
1240
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
if (
|
|
1244
|
+
ts.isTypeReferenceNode(node) &&
|
|
1245
|
+
ts.isIdentifier(node.typeName) &&
|
|
1246
|
+
isElaboratedBigIntTypeImportName(node.typeName.text)
|
|
1247
|
+
) {
|
|
1248
|
+
changed = true;
|
|
1249
|
+
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BigIntKeyword);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
if (
|
|
1253
|
+
ts.isTypeReferenceNode(node) &&
|
|
1254
|
+
ts.isQualifiedName(node.typeName) &&
|
|
1255
|
+
ts.isIdentifier(node.typeName.left) &&
|
|
1256
|
+
node.typeName.left.text === '__soundscript_numerics' &&
|
|
1257
|
+
ts.isIdentifier(node.typeName.right)
|
|
1258
|
+
) {
|
|
1259
|
+
changed = true;
|
|
1260
|
+
return ts.factory.updateTypeReferenceNode(
|
|
1261
|
+
node,
|
|
1262
|
+
ts.factory.createIdentifier(node.typeName.right.text),
|
|
1263
|
+
node.typeArguments,
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
return ts.visitEachChild(node, visit, context);
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
return (node) => ts.visitEachChild(node, visit, context);
|
|
1271
|
+
};
|
|
1272
|
+
const transformed = ts.transform(declarationSourceFile, [transformer]);
|
|
1273
|
+
|
|
1274
|
+
if (!changed) {
|
|
1275
|
+
transformed.dispose();
|
|
1276
|
+
return declarationText;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
const rewritten = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }).printFile(
|
|
1280
|
+
transformed.transformed[0] as ts.SourceFile,
|
|
1281
|
+
);
|
|
1282
|
+
transformed.dispose();
|
|
1283
|
+
return rewritten;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
function rewriteProjectedDeclarationBuiltinNumerics(
|
|
1287
|
+
_sourceFileName: string,
|
|
1288
|
+
declarationText: string,
|
|
1289
|
+
): string {
|
|
1290
|
+
const rewrittenInternalReferences = rewriteProjectedDeclarationInternalNumericReferences(
|
|
1291
|
+
declarationText,
|
|
1292
|
+
);
|
|
1293
|
+
return declarationTextUsesMachineNumerics(rewrittenInternalReferences)
|
|
1294
|
+
? prependMachineNumericPrelude(rewrittenInternalReferences)
|
|
1295
|
+
: rewrittenInternalReferences;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
function collectProjectedDeclarationEmitSources(
|
|
1299
|
+
preparedProgram: PreparedProgram,
|
|
1300
|
+
): readonly ProjectedDeclarationEmitCacheSource[] {
|
|
1301
|
+
return preparedProgram.program.getSourceFiles()
|
|
1302
|
+
.filter((sourceFile) => !sourceFile.isDeclarationFile)
|
|
1303
|
+
.map((sourceFile) => ({
|
|
1304
|
+
fileName: toSourceFileName(sourceFile.fileName),
|
|
1305
|
+
text: sourceFile.text,
|
|
1306
|
+
}))
|
|
1307
|
+
.filter((sourceFile) => isSoundscriptSourceFile(sourceFile.fileName))
|
|
1308
|
+
.sort((left, right) => left.fileName.localeCompare(right.fileName));
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
function createProjectedDeclarationEmitBucketKey(
|
|
1312
|
+
optionSignature: string,
|
|
1313
|
+
rootNames: readonly string[],
|
|
1314
|
+
sources: readonly ProjectedDeclarationEmitCacheSource[],
|
|
1315
|
+
): string {
|
|
1316
|
+
let hash = fnv1aHash(optionSignature);
|
|
1317
|
+
hash = fnv1aHash('\u0000', hash);
|
|
1318
|
+
|
|
1319
|
+
for (const rootName of rootNames) {
|
|
1320
|
+
hash = fnv1aHash(rootName, hash);
|
|
1321
|
+
hash = fnv1aHash('\u0001', hash);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
hash = fnv1aHash('\u0002', hash);
|
|
1325
|
+
|
|
1326
|
+
for (const source of sources) {
|
|
1327
|
+
hash = fnv1aHash(source.fileName, hash);
|
|
1328
|
+
hash = fnv1aHash('\u0003', hash);
|
|
1329
|
+
hash = fnv1aHash(String(source.text.length), hash);
|
|
1330
|
+
hash = fnv1aHash('\u0004', hash);
|
|
1331
|
+
hash = fnv1aHash(source.text, hash);
|
|
1332
|
+
hash = fnv1aHash('\u0005', hash);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
return hash.toString(16).padStart(8, '0');
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
function projectedDeclarationEmitSourcesEqual(
|
|
1339
|
+
left: readonly ProjectedDeclarationEmitCacheSource[],
|
|
1340
|
+
right: readonly ProjectedDeclarationEmitCacheSource[],
|
|
1341
|
+
): boolean {
|
|
1342
|
+
if (left.length !== right.length) {
|
|
1343
|
+
return false;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
1347
|
+
if (
|
|
1348
|
+
left[index]?.fileName !== right[index]?.fileName || left[index]?.text !== right[index]?.text
|
|
1349
|
+
) {
|
|
1350
|
+
return false;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
return true;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
function projectedDeclarationEmitRootNamesEqual(
|
|
1358
|
+
left: readonly string[],
|
|
1359
|
+
right: readonly string[],
|
|
1360
|
+
): boolean {
|
|
1361
|
+
if (left.length !== right.length) {
|
|
1362
|
+
return false;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
1366
|
+
if (left[index] !== right[index]) {
|
|
1367
|
+
return false;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
return true;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
function getCachedProjectedDeclarations(
|
|
1375
|
+
optionSignature: string,
|
|
1376
|
+
rootNames: readonly string[],
|
|
1377
|
+
sources: readonly ProjectedDeclarationEmitCacheSource[],
|
|
1378
|
+
): ReadonlyMap<string, string> | undefined {
|
|
1379
|
+
const bucketKey = createProjectedDeclarationEmitBucketKey(optionSignature, rootNames, sources);
|
|
1380
|
+
const entries = projectedDeclarationEmitCache.get(bucketKey);
|
|
1381
|
+
if (!entries) {
|
|
1382
|
+
return undefined;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
const matchedEntry = entries.find((entry) =>
|
|
1386
|
+
entry.optionSignature === optionSignature &&
|
|
1387
|
+
projectedDeclarationEmitRootNamesEqual(entry.rootNames, rootNames) &&
|
|
1388
|
+
projectedDeclarationEmitSourcesEqual(entry.sources, sources)
|
|
1389
|
+
);
|
|
1390
|
+
if (!matchedEntry) {
|
|
1391
|
+
return undefined;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
projectedDeclarationEmitCache.delete(bucketKey);
|
|
1395
|
+
projectedDeclarationEmitCache.set(bucketKey, entries);
|
|
1396
|
+
return matchedEntry.declarations;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
function setCachedProjectedDeclarations(
|
|
1400
|
+
optionSignature: string,
|
|
1401
|
+
rootNames: readonly string[],
|
|
1402
|
+
sources: readonly ProjectedDeclarationEmitCacheSource[],
|
|
1403
|
+
declarations: ReadonlyMap<string, string>,
|
|
1404
|
+
): void {
|
|
1405
|
+
const bucketKey = createProjectedDeclarationEmitBucketKey(optionSignature, rootNames, sources);
|
|
1406
|
+
const existingEntries = projectedDeclarationEmitCache.get(bucketKey) ?? [];
|
|
1407
|
+
const filteredEntries = existingEntries.filter((entry) =>
|
|
1408
|
+
!(
|
|
1409
|
+
entry.optionSignature === optionSignature &&
|
|
1410
|
+
projectedDeclarationEmitRootNamesEqual(entry.rootNames, rootNames) &&
|
|
1411
|
+
projectedDeclarationEmitSourcesEqual(entry.sources, sources)
|
|
1412
|
+
)
|
|
1413
|
+
);
|
|
1414
|
+
projectedDeclarationEmitCache.delete(bucketKey);
|
|
1415
|
+
projectedDeclarationEmitCache.set(bucketKey, [
|
|
1416
|
+
...filteredEntries,
|
|
1417
|
+
{ declarations, optionSignature, rootNames: [...rootNames], sources },
|
|
1418
|
+
]);
|
|
1419
|
+
|
|
1420
|
+
while (projectedDeclarationEmitCache.size > PROJECTED_DECLARATION_EMIT_CACHE_LIMIT) {
|
|
1421
|
+
const oldestKey = projectedDeclarationEmitCache.keys().next().value;
|
|
1422
|
+
if (!oldestKey) {
|
|
1423
|
+
break;
|
|
1424
|
+
}
|
|
1425
|
+
projectedDeclarationEmitCache.delete(oldestKey);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
export function clearProjectedDeclarationEmitCacheForTest(): void {
|
|
1430
|
+
projectedDeclarationEmitCache.clear();
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
function scriptKindForSourceFileName(fileName: string): ts.ScriptKind {
|
|
1434
|
+
const lowered = fileName.toLowerCase();
|
|
1435
|
+
if (lowered.endsWith('.sts') || lowered.endsWith('.tsx') || lowered.endsWith('.jsx')) {
|
|
1436
|
+
return ts.ScriptKind.TSX;
|
|
1437
|
+
}
|
|
1438
|
+
if (lowered.endsWith('.js') || lowered.endsWith('.mjs') || lowered.endsWith('.cjs')) {
|
|
1439
|
+
return ts.ScriptKind.JS;
|
|
1440
|
+
}
|
|
1441
|
+
return ts.ScriptKind.TS;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
function compileTimeBindingUsageForSourceFile(
|
|
1445
|
+
preparedProgram: PreparedProgram,
|
|
1446
|
+
sourceFileName: string,
|
|
1447
|
+
): ReadonlyMap<string, ImportedBindingUsage> {
|
|
1448
|
+
const preparedSource = preparedProgram.preparedHost.getPreparedSourceFile(sourceFileName);
|
|
1449
|
+
const originalText = preparedSource?.originalText ??
|
|
1450
|
+
preparedProgram.preparedHost.host.readFile(sourceFileName);
|
|
1451
|
+
if (originalText === undefined) {
|
|
1452
|
+
return new Map();
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
const sourceFile = ts.createSourceFile(
|
|
1456
|
+
sourceFileName,
|
|
1457
|
+
originalText,
|
|
1458
|
+
ts.ScriptTarget.Latest,
|
|
1459
|
+
true,
|
|
1460
|
+
scriptKindForSourceFileName(sourceFileName),
|
|
1461
|
+
);
|
|
1462
|
+
const macroNames = new Set(
|
|
1463
|
+
[...(preparedSource?.rewriteResult.macrosById.values() ?? [])].map((macro) => macro.nameText),
|
|
1464
|
+
);
|
|
1465
|
+
return classifyImportedBindingUsage(
|
|
1466
|
+
sourceFile,
|
|
1467
|
+
macroNames,
|
|
1468
|
+
macroInvocationReferenceSpans(preparedSource?.rewriteResult.macrosById.values() ?? []),
|
|
1469
|
+
);
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
function stripCompileTimeOnlyImportsFromProjectedDeclaration(
|
|
1473
|
+
preparedProgram: PreparedProgram,
|
|
1474
|
+
sourceFileName: string,
|
|
1475
|
+
declarationText: string,
|
|
1476
|
+
): string {
|
|
1477
|
+
const usage = compileTimeBindingUsageForSourceFile(preparedProgram, sourceFileName);
|
|
1478
|
+
if (usage.size === 0) {
|
|
1479
|
+
return declarationText;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const declarationFile = ts.createSourceFile(
|
|
1483
|
+
toProjectedDeclarationFileName(sourceFileName),
|
|
1484
|
+
declarationText,
|
|
1485
|
+
ts.ScriptTarget.Latest,
|
|
1486
|
+
true,
|
|
1487
|
+
ts.ScriptKind.TS,
|
|
1488
|
+
);
|
|
1489
|
+
const importedLocalNames = new Set<string>();
|
|
1490
|
+
for (const statement of declarationFile.statements) {
|
|
1491
|
+
if (!ts.isImportDeclaration(statement) || !statement.importClause) {
|
|
1492
|
+
continue;
|
|
1493
|
+
}
|
|
1494
|
+
if (statement.importClause.name) {
|
|
1495
|
+
importedLocalNames.add(statement.importClause.name.text);
|
|
1496
|
+
}
|
|
1497
|
+
const namedBindings = statement.importClause.namedBindings;
|
|
1498
|
+
if (!namedBindings) {
|
|
1499
|
+
continue;
|
|
1500
|
+
}
|
|
1501
|
+
if (ts.isNamespaceImport(namedBindings)) {
|
|
1502
|
+
importedLocalNames.add(namedBindings.name.text);
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
for (const element of namedBindings.elements) {
|
|
1506
|
+
importedLocalNames.add(element.name.text);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
if (![...usage.keys()].some((name) => importedLocalNames.has(name))) {
|
|
1511
|
+
return declarationText;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
const stripped = stripCompileTimeOnlyImportedBindings(declarationFile, usage);
|
|
1515
|
+
if (stripped === declarationFile) {
|
|
1516
|
+
return declarationText;
|
|
1517
|
+
}
|
|
1518
|
+
const printed = projectedDeclarationPrinter.printFile(stripped);
|
|
1519
|
+
return printed.endsWith('\n') ? printed : `${printed}\n`;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
function stripMacroAuthoringModuleReferencesFromProjectedDeclaration(
|
|
1523
|
+
preparedProgram: PreparedProgram,
|
|
1524
|
+
sourceFileName: string,
|
|
1525
|
+
declarationText: string,
|
|
1526
|
+
): string {
|
|
1527
|
+
const declarationFile = ts.createSourceFile(
|
|
1528
|
+
toProjectedDeclarationFileName(sourceFileName),
|
|
1529
|
+
declarationText,
|
|
1530
|
+
ts.ScriptTarget.Latest,
|
|
1531
|
+
true,
|
|
1532
|
+
ts.ScriptKind.TS,
|
|
1533
|
+
);
|
|
1534
|
+
const moduleResolutionHost = preparedProgram.preparedHost.host;
|
|
1535
|
+
let changed = false;
|
|
1536
|
+
const isInstalledPath = (fileName: string): boolean =>
|
|
1537
|
+
toSourceFileName(fileName).replaceAll('\\', '/').includes('/node_modules/');
|
|
1538
|
+
|
|
1539
|
+
const resolvesToMacroAuthoringModule = (specifier: string): boolean => {
|
|
1540
|
+
const resolvedModule = ts.resolveModuleName(
|
|
1541
|
+
specifier,
|
|
1542
|
+
sourceFileName,
|
|
1543
|
+
preparedProgram.options,
|
|
1544
|
+
moduleResolutionHost,
|
|
1545
|
+
preparedProgram.preparedHost.reuseState.moduleResolutionCache,
|
|
1546
|
+
).resolvedModule;
|
|
1547
|
+
if (!resolvedModule) {
|
|
1548
|
+
return false;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
const resolvedSourceFileName = preparedProgram.toSourceFileName(resolvedModule.resolvedFileName);
|
|
1552
|
+
return isSoundscriptMacroSourceFile(resolvedSourceFileName) && !isInstalledPath(resolvedSourceFileName);
|
|
1553
|
+
};
|
|
1554
|
+
|
|
1555
|
+
const transformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
|
|
1556
|
+
const visit = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
|
1557
|
+
if (
|
|
1558
|
+
ts.isImportDeclaration(node) &&
|
|
1559
|
+
ts.isStringLiteral(node.moduleSpecifier) &&
|
|
1560
|
+
resolvesToMacroAuthoringModule(node.moduleSpecifier.text)
|
|
1561
|
+
) {
|
|
1562
|
+
changed = true;
|
|
1563
|
+
return ts.factory.createNotEmittedStatement(node);
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
if (
|
|
1567
|
+
ts.isExportDeclaration(node) &&
|
|
1568
|
+
node.moduleSpecifier &&
|
|
1569
|
+
ts.isStringLiteral(node.moduleSpecifier) &&
|
|
1570
|
+
resolvesToMacroAuthoringModule(node.moduleSpecifier.text)
|
|
1571
|
+
) {
|
|
1572
|
+
changed = true;
|
|
1573
|
+
return ts.factory.createNotEmittedStatement(node);
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
if (
|
|
1577
|
+
ts.isImportEqualsDeclaration(node) &&
|
|
1578
|
+
ts.isExternalModuleReference(node.moduleReference) &&
|
|
1579
|
+
ts.isStringLiteral(node.moduleReference.expression) &&
|
|
1580
|
+
resolvesToMacroAuthoringModule(node.moduleReference.expression.text)
|
|
1581
|
+
) {
|
|
1582
|
+
changed = true;
|
|
1583
|
+
return ts.factory.createNotEmittedStatement(node);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
return ts.visitEachChild(node, visit, context);
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
return (node) => ts.visitEachChild(node, visit, context);
|
|
1590
|
+
};
|
|
1591
|
+
const transformed = ts.transform(declarationFile, [transformer]);
|
|
1592
|
+
try {
|
|
1593
|
+
if (!changed) {
|
|
1594
|
+
return declarationText;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
const printed = projectedDeclarationPrinter.printFile(transformed.transformed[0] as ts.SourceFile);
|
|
1598
|
+
return printed.endsWith('\n') ? printed : `${printed}\n`;
|
|
1599
|
+
} finally {
|
|
1600
|
+
transformed.dispose();
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
export function emitProjectedDeclarations(
|
|
1605
|
+
preparedProgram: PreparedProgram,
|
|
1606
|
+
rootNames: readonly string[] = preparedProgram.rootNames,
|
|
1607
|
+
): ReadonlyMap<string, string> {
|
|
1608
|
+
const metadata: Record<string, string | number> = {
|
|
1609
|
+
cache: 'miss',
|
|
1610
|
+
rootNames: rootNames.length,
|
|
1611
|
+
};
|
|
1612
|
+
return measureCheckerTiming(
|
|
1613
|
+
'project.emitProjectedDeclarations',
|
|
1614
|
+
metadata,
|
|
1615
|
+
() => {
|
|
1616
|
+
const optionSignature = stableStringify(preparedProgram.options);
|
|
1617
|
+
const rootNamesSignature = rootNames.join('\u0000');
|
|
1618
|
+
const sources = collectProjectedDeclarationEmitSources(preparedProgram);
|
|
1619
|
+
const cachedDeclarations = getCachedProjectedDeclarations(
|
|
1620
|
+
optionSignature,
|
|
1621
|
+
rootNames,
|
|
1622
|
+
sources,
|
|
1623
|
+
);
|
|
1624
|
+
if (cachedDeclarations) {
|
|
1625
|
+
metadata.cache = 'hit';
|
|
1626
|
+
return cachedDeclarations;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
const reusableState = preparedProgram.preparedHost.reuseState;
|
|
1630
|
+
const canIncrementallyReuseDeclarations =
|
|
1631
|
+
reusableState.projectedDeclarationOptionSignature === optionSignature &&
|
|
1632
|
+
reusableState.projectedDeclarationRootNamesSignature === rootNamesSignature;
|
|
1633
|
+
const declarationBuilderProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
|
|
1634
|
+
rootNames.map(toProgramFileName),
|
|
1635
|
+
{
|
|
1636
|
+
...preparedProgram.options,
|
|
1637
|
+
declaration: true,
|
|
1638
|
+
emitDeclarationOnly: true,
|
|
1639
|
+
noEmit: false,
|
|
1640
|
+
},
|
|
1641
|
+
preparedProgram.preparedHost.host,
|
|
1642
|
+
canIncrementallyReuseDeclarations
|
|
1643
|
+
? reusableState.projectedDeclarationBuilderProgram
|
|
1644
|
+
: undefined,
|
|
1645
|
+
);
|
|
1646
|
+
const declarationProgram = declarationBuilderProgram.getProgram();
|
|
1647
|
+
reusableState.projectedDeclarationBuilderProgram = declarationBuilderProgram;
|
|
1648
|
+
reusableState.projectedDeclarationOptionSignature = optionSignature;
|
|
1649
|
+
reusableState.projectedDeclarationProgram = declarationProgram;
|
|
1650
|
+
reusableState.projectedDeclarationRootNamesSignature = rootNamesSignature;
|
|
1651
|
+
const projectedDeclarations = canIncrementallyReuseDeclarations
|
|
1652
|
+
? new Map(reusableState.projectedDeclarationOutputs ?? [])
|
|
1653
|
+
: new Map<string, string>();
|
|
1654
|
+
const writeFile: ts.WriteFileCallback = (
|
|
1655
|
+
fileName,
|
|
1656
|
+
text,
|
|
1657
|
+
_writeByteOrderMark,
|
|
1658
|
+
_onError,
|
|
1659
|
+
sourceFiles,
|
|
1660
|
+
) => {
|
|
1661
|
+
const sourceFileName = sourceFiles
|
|
1662
|
+
?.map((sourceFile) => preparedProgram.toSourceFileName(sourceFile.fileName))
|
|
1663
|
+
.find((sourceFileName) => isSoundscriptSourceFile(sourceFileName)) ??
|
|
1664
|
+
toProjectedDeclarationSourceFileName(fileName);
|
|
1665
|
+
if (isSoundscriptSourceFile(sourceFileName)) {
|
|
1666
|
+
const rewrittenNewtypes = rewriteProjectedDeclarationNewtypes(
|
|
1667
|
+
preparedProgram,
|
|
1668
|
+
sourceFileName,
|
|
1669
|
+
text,
|
|
1670
|
+
);
|
|
1671
|
+
projectedDeclarations.set(
|
|
1672
|
+
sourceFileName,
|
|
1673
|
+
stripMacroAuthoringModuleReferencesFromProjectedDeclaration(
|
|
1674
|
+
preparedProgram,
|
|
1675
|
+
sourceFileName,
|
|
1676
|
+
stripCompileTimeOnlyImportsFromProjectedDeclaration(
|
|
1677
|
+
preparedProgram,
|
|
1678
|
+
sourceFileName,
|
|
1679
|
+
rewriteProjectedDeclarationBuiltinNumerics(sourceFileName, rewrittenNewtypes),
|
|
1680
|
+
),
|
|
1681
|
+
),
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1685
|
+
if (canIncrementallyReuseDeclarations) {
|
|
1686
|
+
while (declarationBuilderProgram.emitNextAffectedFile(writeFile, undefined, true)) {
|
|
1687
|
+
// Keep draining affected declaration outputs until the builder reports completion.
|
|
1688
|
+
}
|
|
1689
|
+
} else {
|
|
1690
|
+
declarationProgram.emit(
|
|
1691
|
+
undefined,
|
|
1692
|
+
writeFile,
|
|
1693
|
+
undefined,
|
|
1694
|
+
true,
|
|
1695
|
+
);
|
|
1696
|
+
}
|
|
1697
|
+
reusableState.projectedDeclarationOutputs = projectedDeclarations;
|
|
1698
|
+
setCachedProjectedDeclarations(optionSignature, rootNames, sources, projectedDeclarations);
|
|
1699
|
+
return projectedDeclarations;
|
|
1700
|
+
},
|
|
1701
|
+
{ always: true },
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
export function getPositionOfLineAndCharacter(
|
|
1706
|
+
text: string,
|
|
1707
|
+
line: number,
|
|
1708
|
+
character: number,
|
|
1709
|
+
): number {
|
|
1710
|
+
let currentLine = 0;
|
|
1711
|
+
let currentCharacter = 0;
|
|
1712
|
+
|
|
1713
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
1714
|
+
if (currentLine === line && currentCharacter === character) {
|
|
1715
|
+
return index;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
if (text[index] === '\n') {
|
|
1719
|
+
currentLine += 1;
|
|
1720
|
+
currentCharacter = 0;
|
|
1721
|
+
continue;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
if (currentLine === line) {
|
|
1725
|
+
currentCharacter += 1;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
return text.length;
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
export function getLineAndCharacterOfPosition(
|
|
1733
|
+
text: string,
|
|
1734
|
+
position: number,
|
|
1735
|
+
): { character: number; line: number } {
|
|
1736
|
+
let line = 0;
|
|
1737
|
+
let character = 0;
|
|
1738
|
+
|
|
1739
|
+
for (let index = 0; index < Math.min(position, text.length); index += 1) {
|
|
1740
|
+
if (text[index] === '\n') {
|
|
1741
|
+
line += 1;
|
|
1742
|
+
character = 0;
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
character += 1;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
return { line, character };
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
export function mapSourcePositionToProgram(
|
|
1753
|
+
preparedFile: PreparedSourceFile,
|
|
1754
|
+
sourcePosition: number,
|
|
1755
|
+
): MappedProgramPosition {
|
|
1756
|
+
const stageOne = mapSourcePositionToStage(
|
|
1757
|
+
preparedFile.rewriteResult,
|
|
1758
|
+
sourcePosition,
|
|
1759
|
+
);
|
|
1760
|
+
if (!preparedFile.postRewriteStage) {
|
|
1761
|
+
return stageOne;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
const stageTwo = mapSourcePositionToStage(
|
|
1765
|
+
preparedFile.postRewriteStage,
|
|
1766
|
+
stageOne.position,
|
|
1767
|
+
);
|
|
1768
|
+
return {
|
|
1769
|
+
insideReplacement: stageOne.insideReplacement || stageTwo.insideReplacement,
|
|
1770
|
+
position: stageTwo.position,
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
function mapSourcePositionToStage(
|
|
1775
|
+
stage: Pick<PreparedRewriteStage, 'lineMappings' | 'replacements' | 'rewrittenText'>,
|
|
1776
|
+
sourcePosition: number,
|
|
1777
|
+
): MappedProgramPosition {
|
|
1778
|
+
for (const replacement of stage.replacements) {
|
|
1779
|
+
if (sourcePosition < replacement.originalSpan.start) {
|
|
1780
|
+
break;
|
|
1781
|
+
}
|
|
1782
|
+
if (sourcePosition < replacement.originalSpan.end) {
|
|
1783
|
+
const mappedSegmentPosition = mapSourcePositionThroughReplacementSegments(
|
|
1784
|
+
replacement,
|
|
1785
|
+
sourcePosition,
|
|
1786
|
+
);
|
|
1787
|
+
if (mappedSegmentPosition !== undefined) {
|
|
1788
|
+
return {
|
|
1789
|
+
insideReplacement: false,
|
|
1790
|
+
position: mappedSegmentPosition,
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
return {
|
|
1794
|
+
insideReplacement: true,
|
|
1795
|
+
position: replacement.rewrittenSpan.start,
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
const mappedPosition = mapSourcePositionToAlignedStageLine(stage, sourcePosition);
|
|
1801
|
+
if (mappedPosition !== undefined) {
|
|
1802
|
+
return {
|
|
1803
|
+
insideReplacement: false,
|
|
1804
|
+
position: mappedPosition,
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
let delta = 0;
|
|
1809
|
+
for (const replacement of stage.replacements) {
|
|
1810
|
+
if (sourcePosition < replacement.originalSpan.start) {
|
|
1811
|
+
return {
|
|
1812
|
+
insideReplacement: false,
|
|
1813
|
+
position: sourcePosition + delta,
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
delta += (replacement.rewrittenSpan.end - replacement.rewrittenSpan.start) -
|
|
1818
|
+
(replacement.originalSpan.end - replacement.originalSpan.start);
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
return {
|
|
1822
|
+
insideReplacement: false,
|
|
1823
|
+
position: Math.min(
|
|
1824
|
+
sourcePosition + delta,
|
|
1825
|
+
stage.rewrittenText.length,
|
|
1826
|
+
),
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
export function mapProgramPositionToSource(
|
|
1831
|
+
preparedFile: PreparedSourceFile,
|
|
1832
|
+
programPosition: number,
|
|
1833
|
+
): MappedProgramPosition {
|
|
1834
|
+
const stageTwo = preparedFile.postRewriteStage
|
|
1835
|
+
? mapProgramPositionToStageSource(preparedFile.postRewriteStage, programPosition)
|
|
1836
|
+
: {
|
|
1837
|
+
insideReplacement: false,
|
|
1838
|
+
position: Math.min(programPosition, preparedFile.rewriteResult.rewrittenText.length),
|
|
1839
|
+
};
|
|
1840
|
+
const stageOne = mapProgramPositionToStageSource(
|
|
1841
|
+
preparedFile.rewriteResult,
|
|
1842
|
+
stageTwo.position,
|
|
1843
|
+
);
|
|
1844
|
+
return {
|
|
1845
|
+
insideReplacement: stageOne.insideReplacement || stageTwo.insideReplacement,
|
|
1846
|
+
position: stageOne.position,
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
function mapProgramPositionToStageSource(
|
|
1851
|
+
stage: Pick<PreparedRewriteStage, 'lineMappings' | 'replacements' | 'rewrittenText'>,
|
|
1852
|
+
programPosition: number,
|
|
1853
|
+
): MappedProgramPosition {
|
|
1854
|
+
const clampedPosition = Math.min(
|
|
1855
|
+
programPosition,
|
|
1856
|
+
stage.rewrittenText.length,
|
|
1857
|
+
);
|
|
1858
|
+
for (const replacement of stage.replacements) {
|
|
1859
|
+
if (clampedPosition < replacement.rewrittenSpan.start) {
|
|
1860
|
+
break;
|
|
1861
|
+
}
|
|
1862
|
+
if (clampedPosition < replacement.rewrittenSpan.end) {
|
|
1863
|
+
const mappedSegmentPosition = mapProgramPositionThroughReplacementSegments(
|
|
1864
|
+
replacement,
|
|
1865
|
+
clampedPosition,
|
|
1866
|
+
);
|
|
1867
|
+
if (mappedSegmentPosition !== undefined) {
|
|
1868
|
+
return {
|
|
1869
|
+
insideReplacement: false,
|
|
1870
|
+
position: mappedSegmentPosition,
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1873
|
+
return {
|
|
1874
|
+
insideReplacement: true,
|
|
1875
|
+
position: replacement.originalSpan.start,
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
const mappedPosition = mapProgramPositionToAlignedStageLine(stage, clampedPosition);
|
|
1881
|
+
if (mappedPosition !== undefined) {
|
|
1882
|
+
return {
|
|
1883
|
+
insideReplacement: false,
|
|
1884
|
+
position: mappedPosition,
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
let delta = 0;
|
|
1889
|
+
for (const replacement of stage.replacements) {
|
|
1890
|
+
if (clampedPosition < replacement.rewrittenSpan.start) {
|
|
1891
|
+
return {
|
|
1892
|
+
insideReplacement: false,
|
|
1893
|
+
position: clampedPosition - delta,
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
delta += (replacement.rewrittenSpan.end - replacement.rewrittenSpan.start) -
|
|
1898
|
+
(replacement.originalSpan.end - replacement.originalSpan.start);
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
return {
|
|
1902
|
+
insideReplacement: false,
|
|
1903
|
+
position: Math.max(0, clampedPosition - delta),
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
function mapProgramPositionToSourceBoundary(
|
|
1908
|
+
preparedFile: PreparedSourceFile,
|
|
1909
|
+
programPosition: number,
|
|
1910
|
+
affinity: 'start' | 'end',
|
|
1911
|
+
): number {
|
|
1912
|
+
const stageTwo = preparedFile.postRewriteStage
|
|
1913
|
+
? mapProgramPositionToStageSourceBoundary(
|
|
1914
|
+
preparedFile.postRewriteStage,
|
|
1915
|
+
programPosition,
|
|
1916
|
+
affinity,
|
|
1917
|
+
)
|
|
1918
|
+
: Math.min(programPosition, preparedFile.rewriteResult.rewrittenText.length);
|
|
1919
|
+
return Math.min(
|
|
1920
|
+
preparedFile.originalText.length,
|
|
1921
|
+
mapProgramPositionToStageSourceBoundary(
|
|
1922
|
+
preparedFile.rewriteResult,
|
|
1923
|
+
stageTwo,
|
|
1924
|
+
affinity,
|
|
1925
|
+
),
|
|
1926
|
+
);
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
function mapProgramPositionToStageSourceBoundary(
|
|
1930
|
+
stage: Pick<PreparedRewriteStage, 'lineMappings' | 'replacements' | 'rewrittenText'>,
|
|
1931
|
+
programPosition: number,
|
|
1932
|
+
affinity: 'start' | 'end',
|
|
1933
|
+
): number {
|
|
1934
|
+
const clampedPosition = Math.min(
|
|
1935
|
+
programPosition,
|
|
1936
|
+
stage.rewrittenText.length,
|
|
1937
|
+
);
|
|
1938
|
+
for (const replacement of stage.replacements) {
|
|
1939
|
+
if (clampedPosition < replacement.rewrittenSpan.start) {
|
|
1940
|
+
break;
|
|
1941
|
+
}
|
|
1942
|
+
if (clampedPosition < replacement.rewrittenSpan.end) {
|
|
1943
|
+
const mappedSegmentPosition = mapProgramPositionThroughReplacementBoundarySegments(
|
|
1944
|
+
replacement,
|
|
1945
|
+
clampedPosition,
|
|
1946
|
+
affinity,
|
|
1947
|
+
);
|
|
1948
|
+
if (mappedSegmentPosition !== undefined) {
|
|
1949
|
+
return mappedSegmentPosition;
|
|
1950
|
+
}
|
|
1951
|
+
return affinity === 'start' ? replacement.originalSpan.start : replacement.originalSpan.end;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
const mappedPosition = mapProgramPositionToAlignedStageLine(stage, clampedPosition);
|
|
1956
|
+
if (mappedPosition !== undefined) {
|
|
1957
|
+
return mappedPosition;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
let delta = 0;
|
|
1961
|
+
for (const replacement of stage.replacements) {
|
|
1962
|
+
if (clampedPosition < replacement.rewrittenSpan.start) {
|
|
1963
|
+
return Math.max(0, clampedPosition - delta);
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
delta += (replacement.rewrittenSpan.end - replacement.rewrittenSpan.start) -
|
|
1967
|
+
(replacement.originalSpan.end - replacement.originalSpan.start);
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
return Math.max(0, clampedPosition - delta);
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
export function mapProgramRangeToSource(
|
|
1974
|
+
preparedFile: PreparedSourceFile,
|
|
1975
|
+
programStart: number,
|
|
1976
|
+
programEnd: number,
|
|
1977
|
+
): MappedSourceRange {
|
|
1978
|
+
const stageTwoRange = preparedFile.postRewriteStage
|
|
1979
|
+
? mapProgramRangeToStageSource(
|
|
1980
|
+
preparedFile.postRewriteStage,
|
|
1981
|
+
programStart,
|
|
1982
|
+
programEnd,
|
|
1983
|
+
)
|
|
1984
|
+
: {
|
|
1985
|
+
intersectsReplacement: false,
|
|
1986
|
+
start: Math.min(programStart, preparedFile.rewriteResult.rewrittenText.length),
|
|
1987
|
+
end: Math.min(programEnd, preparedFile.rewriteResult.rewrittenText.length),
|
|
1988
|
+
};
|
|
1989
|
+
const stageOneRange = mapProgramRangeToStageSource(
|
|
1990
|
+
preparedFile.rewriteResult,
|
|
1991
|
+
stageTwoRange.start,
|
|
1992
|
+
stageTwoRange.end,
|
|
1993
|
+
);
|
|
1994
|
+
|
|
1995
|
+
return {
|
|
1996
|
+
intersectsReplacement: stageTwoRange.intersectsReplacement ||
|
|
1997
|
+
stageOneRange.intersectsReplacement,
|
|
1998
|
+
start: stageOneRange.start,
|
|
1999
|
+
end: Math.max(stageOneRange.start, stageOneRange.end),
|
|
2000
|
+
};
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
export function mapProgramEnclosingRangeToSource(
|
|
2004
|
+
preparedFile: PreparedSourceFile,
|
|
2005
|
+
programStart: number,
|
|
2006
|
+
programEnd: number,
|
|
2007
|
+
): MappedSourceRange {
|
|
2008
|
+
const finalTextLength = preparedFile.rewrittenText.length;
|
|
2009
|
+
const clampedStart = Math.min(programStart, finalTextLength);
|
|
2010
|
+
const clampedEnd = Math.min(programEnd, finalTextLength);
|
|
2011
|
+
const mappedStart = mapProgramPositionToSourceBoundary(preparedFile, clampedStart, 'start');
|
|
2012
|
+
const mappedEnd = mapProgramPositionToSourceBoundary(preparedFile, clampedEnd, 'end');
|
|
2013
|
+
const stageTwoIntersects = preparedFile.postRewriteStage
|
|
2014
|
+
? preparedFile.postRewriteStage.replacements.some((replacement) =>
|
|
2015
|
+
!(clampedEnd <= replacement.rewrittenSpan.start ||
|
|
2016
|
+
clampedStart >= replacement.rewrittenSpan.end)
|
|
2017
|
+
)
|
|
2018
|
+
: false;
|
|
2019
|
+
const stageTwoMapped = preparedFile.postRewriteStage
|
|
2020
|
+
? mapProgramRangeToStageSource(preparedFile.postRewriteStage, clampedStart, clampedEnd)
|
|
2021
|
+
: {
|
|
2022
|
+
intersectsReplacement: false,
|
|
2023
|
+
start: clampedStart,
|
|
2024
|
+
end: clampedEnd,
|
|
2025
|
+
};
|
|
2026
|
+
const stageOneIntersects = preparedFile.rewriteResult.replacements.some((replacement) =>
|
|
2027
|
+
!(stageTwoMapped.end <= replacement.rewrittenSpan.start ||
|
|
2028
|
+
stageTwoMapped.start >= replacement.rewrittenSpan.end)
|
|
2029
|
+
);
|
|
2030
|
+
|
|
2031
|
+
return {
|
|
2032
|
+
intersectsReplacement: stageTwoIntersects || stageOneIntersects,
|
|
2033
|
+
start: mappedStart,
|
|
2034
|
+
end: Math.max(mappedStart, mappedEnd),
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
function mapProgramRangeToStageSource(
|
|
2039
|
+
stage: Pick<PreparedRewriteStage, 'lineMappings' | 'replacements' | 'rewrittenText'>,
|
|
2040
|
+
programStart: number,
|
|
2041
|
+
programEnd: number,
|
|
2042
|
+
): MappedSourceRange {
|
|
2043
|
+
const clampedStart = Math.min(programStart, stage.rewrittenText.length);
|
|
2044
|
+
const clampedEnd = Math.min(programEnd, stage.rewrittenText.length);
|
|
2045
|
+
const intersectingReplacements = stage.replacements.filter((replacement) =>
|
|
2046
|
+
!(clampedEnd <= replacement.rewrittenSpan.start ||
|
|
2047
|
+
clampedStart >= replacement.rewrittenSpan.end)
|
|
2048
|
+
);
|
|
2049
|
+
|
|
2050
|
+
if (intersectingReplacements.length > 0) {
|
|
2051
|
+
const preciselyMappedRange = mapProgramRangeThroughReplacementSegments(
|
|
2052
|
+
intersectingReplacements,
|
|
2053
|
+
clampedStart,
|
|
2054
|
+
clampedEnd,
|
|
2055
|
+
);
|
|
2056
|
+
if (preciselyMappedRange) {
|
|
2057
|
+
return preciselyMappedRange;
|
|
2058
|
+
}
|
|
2059
|
+
return {
|
|
2060
|
+
intersectsReplacement: true,
|
|
2061
|
+
start: intersectingReplacements[0]!.originalSpan.start,
|
|
2062
|
+
end: intersectingReplacements[intersectingReplacements.length - 1]!.originalSpan.end,
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
const mappedStart = mapProgramPositionToStageSource(stage, clampedStart).position;
|
|
2067
|
+
const mappedEnd = mapProgramPositionToStageSource(stage, clampedEnd).position;
|
|
2068
|
+
|
|
2069
|
+
return {
|
|
2070
|
+
intersectsReplacement: false,
|
|
2071
|
+
start: mappedStart,
|
|
2072
|
+
end: Math.max(mappedStart, mappedEnd),
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
function mapSourcePositionThroughReplacementSegments(
|
|
2077
|
+
replacement: MacroReplacement,
|
|
2078
|
+
sourcePosition: number,
|
|
2079
|
+
): number | undefined {
|
|
2080
|
+
for (const segment of replacement.mappedSegments ?? []) {
|
|
2081
|
+
if (sourcePosition < segment.originalStart || sourcePosition >= segment.originalEnd) {
|
|
2082
|
+
continue;
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
return Math.min(
|
|
2086
|
+
segment.rewrittenEnd,
|
|
2087
|
+
segment.rewrittenStart + (sourcePosition - segment.originalStart),
|
|
2088
|
+
);
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
return undefined;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
function mapProgramPositionThroughReplacementSegments(
|
|
2095
|
+
replacement: MacroReplacement,
|
|
2096
|
+
programPosition: number,
|
|
2097
|
+
): number | undefined {
|
|
2098
|
+
for (const segment of replacement.mappedSegments ?? []) {
|
|
2099
|
+
if (programPosition < segment.rewrittenStart || programPosition >= segment.rewrittenEnd) {
|
|
2100
|
+
continue;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
return Math.min(
|
|
2104
|
+
segment.originalEnd,
|
|
2105
|
+
segment.originalStart + (programPosition - segment.rewrittenStart),
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
return undefined;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
function mapProgramPositionThroughReplacementBoundarySegments(
|
|
2113
|
+
replacement: MacroReplacement,
|
|
2114
|
+
programPosition: number,
|
|
2115
|
+
affinity: 'start' | 'end',
|
|
2116
|
+
): number | undefined {
|
|
2117
|
+
for (const segment of replacement.mappedSegments ?? []) {
|
|
2118
|
+
const isInside = programPosition >= segment.rewrittenStart &&
|
|
2119
|
+
programPosition < segment.rewrittenEnd;
|
|
2120
|
+
const isExactEnd = affinity === 'end' && programPosition === segment.rewrittenEnd;
|
|
2121
|
+
if (!isInside && !isExactEnd) {
|
|
2122
|
+
continue;
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
const clampedProgramPosition = Math.min(programPosition, segment.rewrittenEnd);
|
|
2126
|
+
return Math.min(
|
|
2127
|
+
segment.originalEnd,
|
|
2128
|
+
segment.originalStart + (clampedProgramPosition - segment.rewrittenStart),
|
|
2129
|
+
);
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
return undefined;
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
function mapProgramRangeThroughReplacementSegments(
|
|
2136
|
+
intersectingReplacements: readonly MacroReplacement[],
|
|
2137
|
+
programStart: number,
|
|
2138
|
+
programEnd: number,
|
|
2139
|
+
): MappedSourceRange | null {
|
|
2140
|
+
if (intersectingReplacements.length !== 1) {
|
|
2141
|
+
return null;
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
const [replacement] = intersectingReplacements;
|
|
2145
|
+
if (!replacement) {
|
|
2146
|
+
return null;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
const mappedSegment = (replacement.mappedSegments ?? []).find((segment) =>
|
|
2150
|
+
programStart >= segment.rewrittenStart &&
|
|
2151
|
+
programEnd <= segment.rewrittenEnd
|
|
2152
|
+
);
|
|
2153
|
+
if (!mappedSegment) {
|
|
2154
|
+
return null;
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
const start = mappedSegment.originalStart + (programStart - mappedSegment.rewrittenStart);
|
|
2158
|
+
const end = mappedSegment.originalStart + (programEnd - mappedSegment.rewrittenStart);
|
|
2159
|
+
if (start === undefined || end === undefined) {
|
|
2160
|
+
return null;
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
return {
|
|
2164
|
+
intersectsReplacement: false,
|
|
2165
|
+
start,
|
|
2166
|
+
end: Math.max(start, end),
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
function findAlignedLineMapping(
|
|
2171
|
+
lineMappings: readonly PreparedRewriteStageLineMapping[] | undefined,
|
|
2172
|
+
position: number,
|
|
2173
|
+
direction: 'original' | 'rewritten',
|
|
2174
|
+
): PreparedRewriteStageLineMapping | undefined {
|
|
2175
|
+
if (!lineMappings) {
|
|
2176
|
+
return undefined;
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
for (const mapping of lineMappings) {
|
|
2180
|
+
const start = direction === 'original' ? mapping.originalStart : mapping.rewrittenStart;
|
|
2181
|
+
const end = direction === 'original' ? mapping.originalEnd : mapping.rewrittenEnd;
|
|
2182
|
+
if (position >= start && position <= end) {
|
|
2183
|
+
return mapping;
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
return undefined;
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
function mapProgramPositionToAlignedStageLine(
|
|
2191
|
+
stage: Pick<PreparedRewriteStage, 'lineMappings' | 'replacements'>,
|
|
2192
|
+
position: number,
|
|
2193
|
+
): number | undefined {
|
|
2194
|
+
const mapping = findAlignedLineMapping(stage.lineMappings, position, 'rewritten');
|
|
2195
|
+
if (!mapping) {
|
|
2196
|
+
return undefined;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
const intersectsReplacement = stage.replacements.some((replacement) =>
|
|
2200
|
+
!(mapping.rewrittenEnd <= replacement.rewrittenSpan.start ||
|
|
2201
|
+
mapping.rewrittenStart >= replacement.rewrittenSpan.end)
|
|
2202
|
+
);
|
|
2203
|
+
if (intersectsReplacement) {
|
|
2204
|
+
return undefined;
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
if (!hasSufficientAlignedLineContext(stage, mapping, 'rewritten')) {
|
|
2208
|
+
return undefined;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
return Math.min(
|
|
2212
|
+
mapping.originalEnd,
|
|
2213
|
+
mapping.originalStart + (position - mapping.rewrittenStart),
|
|
2214
|
+
);
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
function mapSourcePositionToAlignedStageLine(
|
|
2218
|
+
stage: Pick<PreparedRewriteStage, 'lineMappings' | 'replacements'>,
|
|
2219
|
+
position: number,
|
|
2220
|
+
): number | undefined {
|
|
2221
|
+
const mapping = findAlignedLineMapping(stage.lineMappings, position, 'original');
|
|
2222
|
+
if (!mapping) {
|
|
2223
|
+
return undefined;
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
const intersectsReplacement = stage.replacements.some((replacement) =>
|
|
2227
|
+
!(mapping.originalEnd <= replacement.originalSpan.start ||
|
|
2228
|
+
mapping.originalStart >= replacement.originalSpan.end)
|
|
2229
|
+
);
|
|
2230
|
+
if (intersectsReplacement) {
|
|
2231
|
+
return undefined;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
if (!hasSufficientAlignedLineContext(stage, mapping, 'original')) {
|
|
2235
|
+
return undefined;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
return Math.min(
|
|
2239
|
+
mapping.rewrittenEnd,
|
|
2240
|
+
mapping.rewrittenStart + (position - mapping.originalStart),
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
function hasSufficientAlignedLineContext(
|
|
2245
|
+
stage: Pick<PreparedRewriteStage, 'lineMappings' | 'replacements'>,
|
|
2246
|
+
targetMapping: PreparedRewriteStageLineMapping,
|
|
2247
|
+
direction: 'original' | 'rewritten',
|
|
2248
|
+
): boolean {
|
|
2249
|
+
const lineMappings = stage.lineMappings;
|
|
2250
|
+
if (!lineMappings || lineMappings.length === 0) {
|
|
2251
|
+
return false;
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
const targetIndex = lineMappings.indexOf(targetMapping);
|
|
2255
|
+
if (targetIndex === -1) {
|
|
2256
|
+
return false;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
const previousReplacementBoundary = [...stage.replacements]
|
|
2260
|
+
.filter((replacement) => {
|
|
2261
|
+
const start = direction === 'original'
|
|
2262
|
+
? replacement.originalSpan.start
|
|
2263
|
+
: replacement.rewrittenSpan.start;
|
|
2264
|
+
const end = direction === 'original'
|
|
2265
|
+
? replacement.originalSpan.end
|
|
2266
|
+
: replacement.rewrittenSpan.end;
|
|
2267
|
+
return end > start;
|
|
2268
|
+
})
|
|
2269
|
+
.map((replacement) =>
|
|
2270
|
+
direction === 'original' ? replacement.originalSpan.end : replacement.rewrittenSpan.end
|
|
2271
|
+
)
|
|
2272
|
+
.filter((end) =>
|
|
2273
|
+
end <= (direction === 'original' ? targetMapping.originalStart : targetMapping.rewrittenStart)
|
|
2274
|
+
)
|
|
2275
|
+
.at(-1);
|
|
2276
|
+
if (previousReplacementBoundary === undefined) {
|
|
2277
|
+
return true;
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
let alignedLinesSincePreviousRewrite = 0;
|
|
2281
|
+
for (let index = targetIndex; index >= 0; index -= 1) {
|
|
2282
|
+
const mapping = lineMappings[index]!;
|
|
2283
|
+
const mappingStart = direction === 'original' ? mapping.originalStart : mapping.rewrittenStart;
|
|
2284
|
+
if (mappingStart < previousReplacementBoundary) {
|
|
2285
|
+
break;
|
|
2286
|
+
}
|
|
2287
|
+
alignedLinesSincePreviousRewrite += 1;
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
return alignedLinesSincePreviousRewrite >= 1;
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
function getLineAndColumn(text: string, position: number): { column: number; line: number } {
|
|
2294
|
+
let line = 1;
|
|
2295
|
+
let column = 1;
|
|
2296
|
+
|
|
2297
|
+
for (let index = 0; index < position; index += 1) {
|
|
2298
|
+
if (text[index] === '\n') {
|
|
2299
|
+
line += 1;
|
|
2300
|
+
column = 1;
|
|
2301
|
+
} else {
|
|
2302
|
+
column += 1;
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
return { column, line };
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
function skipTrivia(text: string, index: number): number {
|
|
2310
|
+
let currentIndex = index;
|
|
2311
|
+
|
|
2312
|
+
while (currentIndex < text.length) {
|
|
2313
|
+
const character = text[currentIndex];
|
|
2314
|
+
const nextCharacter = text[currentIndex + 1];
|
|
2315
|
+
if (character === ' ' || character === '\t' || character === '\r' || character === '\n') {
|
|
2316
|
+
currentIndex += 1;
|
|
2317
|
+
continue;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
if (character === '/' && nextCharacter === '/') {
|
|
2321
|
+
currentIndex += 2;
|
|
2322
|
+
while (currentIndex < text.length && text[currentIndex] !== '\n') {
|
|
2323
|
+
currentIndex += 1;
|
|
2324
|
+
}
|
|
2325
|
+
continue;
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
if (character === '/' && nextCharacter === '*') {
|
|
2329
|
+
currentIndex += 2;
|
|
2330
|
+
while (
|
|
2331
|
+
currentIndex + 1 < text.length &&
|
|
2332
|
+
!(text[currentIndex] === '*' && text[currentIndex + 1] === '/')
|
|
2333
|
+
) {
|
|
2334
|
+
currentIndex += 1;
|
|
2335
|
+
}
|
|
2336
|
+
currentIndex = Math.min(currentIndex + 2, text.length);
|
|
2337
|
+
continue;
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
break;
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
return currentIndex;
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
function recoverBalancedRegion(
|
|
2347
|
+
text: string,
|
|
2348
|
+
start: number,
|
|
2349
|
+
openChar: '(' | '{',
|
|
2350
|
+
closeChar: ')' | '}',
|
|
2351
|
+
): number {
|
|
2352
|
+
if (text[start] !== openChar) {
|
|
2353
|
+
return start;
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
let depth = 0;
|
|
2357
|
+
let index = start;
|
|
2358
|
+
while (index < text.length) {
|
|
2359
|
+
const character = text[index];
|
|
2360
|
+
if (character === '"' || character === "'" || character === '`') {
|
|
2361
|
+
const quote = character;
|
|
2362
|
+
index += 1;
|
|
2363
|
+
while (index < text.length) {
|
|
2364
|
+
if (text[index] === '\\') {
|
|
2365
|
+
index += 2;
|
|
2366
|
+
continue;
|
|
2367
|
+
}
|
|
2368
|
+
if (text[index] === quote) {
|
|
2369
|
+
index += 1;
|
|
2370
|
+
break;
|
|
2371
|
+
}
|
|
2372
|
+
index += 1;
|
|
2373
|
+
}
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
if (character === '/' && text[index + 1] === '/') {
|
|
2378
|
+
index += 2;
|
|
2379
|
+
while (index < text.length && text[index] !== '\n') {
|
|
2380
|
+
index += 1;
|
|
2381
|
+
}
|
|
2382
|
+
continue;
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
if (character === '/' && text[index + 1] === '*') {
|
|
2386
|
+
index += 2;
|
|
2387
|
+
while (index + 1 < text.length && !(text[index] === '*' && text[index + 1] === '/')) {
|
|
2388
|
+
index += 1;
|
|
2389
|
+
}
|
|
2390
|
+
index = Math.min(index + 2, text.length);
|
|
2391
|
+
continue;
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
if (character === openChar) {
|
|
2395
|
+
depth += 1;
|
|
2396
|
+
} else if (character === closeChar) {
|
|
2397
|
+
depth -= 1;
|
|
2398
|
+
if (depth === 0) {
|
|
2399
|
+
return index + 1;
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
index += 1;
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
return text.length;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
function recoverExpressionEnd(text: string, start: number): number {
|
|
2410
|
+
let index = start;
|
|
2411
|
+
let parenDepth = 0;
|
|
2412
|
+
let bracketDepth = 0;
|
|
2413
|
+
let braceDepth = 0;
|
|
2414
|
+
|
|
2415
|
+
while (index < text.length) {
|
|
2416
|
+
const character = text[index];
|
|
2417
|
+
if (character === '"' || character === "'" || character === '`') {
|
|
2418
|
+
const quote = character;
|
|
2419
|
+
index += 1;
|
|
2420
|
+
while (index < text.length) {
|
|
2421
|
+
if (text[index] === '\\') {
|
|
2422
|
+
index += 2;
|
|
2423
|
+
continue;
|
|
2424
|
+
}
|
|
2425
|
+
if (text[index] === quote) {
|
|
2426
|
+
index += 1;
|
|
2427
|
+
break;
|
|
2428
|
+
}
|
|
2429
|
+
index += 1;
|
|
2430
|
+
}
|
|
2431
|
+
continue;
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
if (character === '/' && text[index + 1] === '/') {
|
|
2435
|
+
return index;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
if (character === '/' && text[index + 1] === '*') {
|
|
2439
|
+
index += 2;
|
|
2440
|
+
while (index + 1 < text.length && !(text[index] === '*' && text[index + 1] === '/')) {
|
|
2441
|
+
index += 1;
|
|
2442
|
+
}
|
|
2443
|
+
index = Math.min(index + 2, text.length);
|
|
2444
|
+
continue;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
if (parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) {
|
|
2448
|
+
if (character === ';' || character === ',' || character === ')' || character === ']') {
|
|
2449
|
+
return index;
|
|
2450
|
+
}
|
|
2451
|
+
if (character === '\n') {
|
|
2452
|
+
return index;
|
|
2453
|
+
}
|
|
2454
|
+
if (character === '{') {
|
|
2455
|
+
const blockEnd = recoverBalancedRegion(text, index, '{', '}');
|
|
2456
|
+
if (blockEnd === text.length) {
|
|
2457
|
+
return blockEnd;
|
|
2458
|
+
}
|
|
2459
|
+
return blockEnd;
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
switch (character) {
|
|
2464
|
+
case '(':
|
|
2465
|
+
parenDepth += 1;
|
|
2466
|
+
break;
|
|
2467
|
+
case ')':
|
|
2468
|
+
if (parenDepth === 0) {
|
|
2469
|
+
return index;
|
|
2470
|
+
}
|
|
2471
|
+
parenDepth -= 1;
|
|
2472
|
+
break;
|
|
2473
|
+
case '[':
|
|
2474
|
+
bracketDepth += 1;
|
|
2475
|
+
break;
|
|
2476
|
+
case ']':
|
|
2477
|
+
if (bracketDepth === 0) {
|
|
2478
|
+
return index;
|
|
2479
|
+
}
|
|
2480
|
+
bracketDepth -= 1;
|
|
2481
|
+
break;
|
|
2482
|
+
case '{':
|
|
2483
|
+
braceDepth += 1;
|
|
2484
|
+
break;
|
|
2485
|
+
case '}':
|
|
2486
|
+
if (braceDepth === 0) {
|
|
2487
|
+
return index;
|
|
2488
|
+
}
|
|
2489
|
+
braceDepth -= 1;
|
|
2490
|
+
break;
|
|
2491
|
+
default:
|
|
2492
|
+
break;
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
index += 1;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
return index;
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
function determineRecoveryRewriteKind(text: string, start: number): 'expr' | 'stmt' {
|
|
2502
|
+
const before = text.slice(0, start).trimEnd();
|
|
2503
|
+
if (
|
|
2504
|
+
before.endsWith('return') ||
|
|
2505
|
+
before.endsWith('throw') ||
|
|
2506
|
+
before.endsWith('case')
|
|
2507
|
+
) {
|
|
2508
|
+
return 'expr';
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
let index = before.length - 1;
|
|
2512
|
+
while (index >= 0) {
|
|
2513
|
+
const character = before[index];
|
|
2514
|
+
if (character === ' ' || character === '\t' || character === '\r' || character === '\n') {
|
|
2515
|
+
index -= 1;
|
|
2516
|
+
continue;
|
|
2517
|
+
}
|
|
2518
|
+
return character === '=' || character === '(' || character === '[' || character === ',' ||
|
|
2519
|
+
character === ':' || character === '?'
|
|
2520
|
+
? 'expr'
|
|
2521
|
+
: 'stmt';
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
return 'stmt';
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
function recoverMacroSpan(
|
|
2528
|
+
text: string,
|
|
2529
|
+
start: number,
|
|
2530
|
+
): { end: number; rewriteKind: 'expr' | 'stmt' } {
|
|
2531
|
+
let index = start + 1;
|
|
2532
|
+
if (!isIdentifierStart(text[index])) {
|
|
2533
|
+
return {
|
|
2534
|
+
end: Math.min(start + 1, text.length),
|
|
2535
|
+
rewriteKind: determineRecoveryRewriteKind(text, start),
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
index += 1;
|
|
2540
|
+
while (isIdentifierPart(text[index])) {
|
|
2541
|
+
index += 1;
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
index = skipTrivia(text, index);
|
|
2545
|
+
if (index >= text.length) {
|
|
2546
|
+
return {
|
|
2547
|
+
end: text.length,
|
|
2548
|
+
rewriteKind: determineRecoveryRewriteKind(text, start),
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
if (text[index] === '{') {
|
|
2553
|
+
return {
|
|
2554
|
+
end: recoverBalancedRegion(text, index, '{', '}'),
|
|
2555
|
+
rewriteKind: 'stmt',
|
|
2556
|
+
};
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
if (text[index] === '(') {
|
|
2560
|
+
let end = recoverBalancedRegion(text, index, '(', ')');
|
|
2561
|
+
const trailingIndex = skipTrivia(text, end);
|
|
2562
|
+
if (text[trailingIndex] === '{') {
|
|
2563
|
+
end = recoverBalancedRegion(text, trailingIndex, '{', '}');
|
|
2564
|
+
}
|
|
2565
|
+
return {
|
|
2566
|
+
end,
|
|
2567
|
+
rewriteKind: determineRecoveryRewriteKind(text, start),
|
|
2568
|
+
};
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
return {
|
|
2572
|
+
end: recoverExpressionEnd(text, index),
|
|
2573
|
+
rewriteKind: determineRecoveryRewriteKind(text, start),
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
function sanitizeMalformedMacroSource(fileName: string, text: string): string | undefined {
|
|
2578
|
+
const scanResult = scanMacroCandidates(fileName, text);
|
|
2579
|
+
const macroStarts = scanResult.hashes.filter((hash) => hash.kind === 'macro-start');
|
|
2580
|
+
if (macroStarts.length === 0) {
|
|
2581
|
+
return undefined;
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
let sanitized = '';
|
|
2585
|
+
let cursor = 0;
|
|
2586
|
+
let replacedAny = false;
|
|
2587
|
+
|
|
2588
|
+
for (const hash of macroStarts) {
|
|
2589
|
+
if (hash.span.start < cursor) {
|
|
2590
|
+
continue;
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
const recovered = recoverMacroSpan(text, hash.span.start);
|
|
2594
|
+
if (recovered.end <= hash.span.start) {
|
|
2595
|
+
continue;
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
sanitized += text.slice(cursor, hash.span.start);
|
|
2599
|
+
sanitized += recovered.rewriteKind === 'expr' ? '__sts_macro_expr(0)' : '__sts_macro_stmt(0);';
|
|
2600
|
+
cursor = recovered.end;
|
|
2601
|
+
replacedAny = true;
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
if (!replacedAny) {
|
|
2605
|
+
return undefined;
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
sanitized += text.slice(cursor);
|
|
2609
|
+
return `${sanitized}\n${MACRO_HELPER_PREAMBLE}`;
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
function toFrontendDiagnostic(
|
|
2613
|
+
diagnostic: HashDiagnostic | MacroParseDiagnostic,
|
|
2614
|
+
originalText: string,
|
|
2615
|
+
): MergedDiagnostic {
|
|
2616
|
+
const start = getLineAndColumn(originalText, diagnostic.span.start);
|
|
2617
|
+
const end = getLineAndColumn(originalText, diagnostic.span.end);
|
|
2618
|
+
|
|
2619
|
+
const message = (() => {
|
|
2620
|
+
switch (diagnostic.reason) {
|
|
2621
|
+
case 'legacy-syntax':
|
|
2622
|
+
return 'Legacy # macro syntax is no longer supported. Rewrite this macro to the TS-native call, tag, or annotation form.';
|
|
2623
|
+
case 'illegal-context':
|
|
2624
|
+
return 'soundscript macro syntax is not allowed in this context.';
|
|
2625
|
+
case 'missing-expression':
|
|
2626
|
+
return 'soundscript macro invocation is missing its required expression.';
|
|
2627
|
+
case 'missing-macro-name':
|
|
2628
|
+
return 'soundscript macro invocation is missing a macro name after #.';
|
|
2629
|
+
case 'not-followed-by-identifier':
|
|
2630
|
+
return 'soundscript # must be followed by a macro or private identifier name.';
|
|
2631
|
+
case 'unexpected-token':
|
|
2632
|
+
return 'soundscript macro invocation has malformed syntax.';
|
|
2633
|
+
case 'unterminated-arglist':
|
|
2634
|
+
return 'soundscript macro invocation has an unterminated argument list.';
|
|
2635
|
+
case 'unterminated-block':
|
|
2636
|
+
return 'soundscript macro invocation has an unterminated block.';
|
|
2637
|
+
default:
|
|
2638
|
+
return 'soundscript macro invocation has malformed syntax.';
|
|
2639
|
+
}
|
|
2640
|
+
})();
|
|
2641
|
+
|
|
2642
|
+
return {
|
|
2643
|
+
source: 'cli',
|
|
2644
|
+
code: 'SOUNDSCRIPT_MACRO_PARSE',
|
|
2645
|
+
category: 'error',
|
|
2646
|
+
message,
|
|
2647
|
+
filePath: diagnostic.fileName,
|
|
2648
|
+
line: start.line,
|
|
2649
|
+
column: start.column,
|
|
2650
|
+
endLine: end.line,
|
|
2651
|
+
endColumn: end.column,
|
|
2652
|
+
};
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
function createExpansionDisabledDiagnostic(
|
|
2656
|
+
span: SourceSpan,
|
|
2657
|
+
originalText: string,
|
|
2658
|
+
): MergedDiagnostic {
|
|
2659
|
+
const start = getLineAndColumn(originalText, span.start);
|
|
2660
|
+
const end = getLineAndColumn(originalText, span.end);
|
|
2661
|
+
|
|
2662
|
+
return {
|
|
2663
|
+
source: 'cli',
|
|
2664
|
+
code: 'SOUNDSCRIPT_EXPANSION_DISABLED',
|
|
2665
|
+
category: 'error',
|
|
2666
|
+
message: 'Expansion-based features are disabled for this analysis run.',
|
|
2667
|
+
hint:
|
|
2668
|
+
'Enable expansion-based features for this analysis run, or remove the expansion-only syntax.',
|
|
2669
|
+
filePath: span.fileName,
|
|
2670
|
+
line: start.line,
|
|
2671
|
+
column: start.column,
|
|
2672
|
+
endLine: end.line,
|
|
2673
|
+
endColumn: end.column,
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
function scriptKindForSourceFile(fileName: string): ts.ScriptKind {
|
|
2678
|
+
const lowered = fileName.toLowerCase();
|
|
2679
|
+
if (lowered.endsWith('.sts') || lowered.endsWith('.tsx') || lowered.endsWith('.jsx')) {
|
|
2680
|
+
return ts.ScriptKind.TSX;
|
|
2681
|
+
}
|
|
2682
|
+
if (lowered.endsWith('.js') || lowered.endsWith('.mjs') || lowered.endsWith('.cjs')) {
|
|
2683
|
+
return ts.ScriptKind.JS;
|
|
2684
|
+
}
|
|
2685
|
+
return ts.ScriptKind.TS;
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
function createReservedAnnotationNameConflictDiagnostic(
|
|
2689
|
+
fileName: string,
|
|
2690
|
+
originalText: string,
|
|
2691
|
+
annotationName: string,
|
|
2692
|
+
span: { end: number; start: number },
|
|
2693
|
+
specifier: string,
|
|
2694
|
+
): MergedDiagnostic {
|
|
2695
|
+
const start = getLineAndColumn(originalText, span.start);
|
|
2696
|
+
const end = getLineAndColumn(originalText, span.end);
|
|
2697
|
+
const label = `#[${annotationName}]`;
|
|
2698
|
+
const example =
|
|
2699
|
+
`Import the macro as an alias such as \`import { ${annotationName} as macro${annotationName[0]?.toUpperCase() ?? ''}${annotationName.slice(1)} } from "${specifier}";\`, then write \`// #[macro${annotationName[0]?.toUpperCase() ?? ''}${annotationName.slice(1)}]\` at the annotation site.`;
|
|
2700
|
+
|
|
2701
|
+
return {
|
|
2702
|
+
source: 'sound',
|
|
2703
|
+
code: SOUND_DIAGNOSTIC_CODES.reservedAnnotationNameConflict,
|
|
2704
|
+
category: 'error',
|
|
2705
|
+
message:
|
|
2706
|
+
`${SOUND_DIAGNOSTIC_MESSAGES.reservedAnnotationNameConflict} \`${label}\` is reserved for the builtin directive. Alias the import from "${specifier}" and use the alias at the annotation site instead.`,
|
|
2707
|
+
metadata: {
|
|
2708
|
+
rule: 'reserved_annotation_name_conflict',
|
|
2709
|
+
primarySymbol: label,
|
|
2710
|
+
fixability: 'local_rewrite',
|
|
2711
|
+
invariant:
|
|
2712
|
+
'Builtin annotation names take precedence in annotation position; imported annotation macros must use distinct bindings.',
|
|
2713
|
+
replacementFamily: 'aliased_annotation_macro_binding',
|
|
2714
|
+
evidence: [
|
|
2715
|
+
{ label: 'annotationName', value: annotationName },
|
|
2716
|
+
{ label: 'importSpecifier', value: specifier },
|
|
2717
|
+
{ label: 'importedBinding', value: annotationName },
|
|
2718
|
+
],
|
|
2719
|
+
counterexample:
|
|
2720
|
+
'If an imported annotation macro reuses a builtin directive name, the annotation site looks configurable even though only the builtin meaning is recognized there.',
|
|
2721
|
+
example,
|
|
2722
|
+
},
|
|
2723
|
+
notes: [
|
|
2724
|
+
`\`${label}\` is reserved for the builtin directive, so the imported annotation macro from "${specifier}" must use an alias at this site.`,
|
|
2725
|
+
`Example: ${example}`,
|
|
2726
|
+
],
|
|
2727
|
+
hint:
|
|
2728
|
+
'Alias the imported annotation macro and use that alias in the `// #[...]` annotation.',
|
|
2729
|
+
filePath: fileName,
|
|
2730
|
+
line: start.line,
|
|
2731
|
+
column: start.column,
|
|
2732
|
+
endLine: end.line,
|
|
2733
|
+
endColumn: end.column,
|
|
2734
|
+
};
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
function hasLegacyOctalEscape(rawText: string): boolean {
|
|
2738
|
+
let consecutiveBackslashes = 0;
|
|
2739
|
+
|
|
2740
|
+
for (let index = 0; index < rawText.length; index += 1) {
|
|
2741
|
+
const char = rawText[index];
|
|
2742
|
+
if (char === '\\') {
|
|
2743
|
+
consecutiveBackslashes += 1;
|
|
2744
|
+
continue;
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2747
|
+
const escaped = consecutiveBackslashes % 2 === 1;
|
|
2748
|
+
consecutiveBackslashes = 0;
|
|
2749
|
+
if (!escaped) {
|
|
2750
|
+
continue;
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
if (/[1-7]/.test(char)) {
|
|
2754
|
+
return true;
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
if (char === '0' && /[0-7]/.test(rawText[index + 1] ?? '')) {
|
|
2758
|
+
return true;
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
return false;
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
function collectLegacyOctalDiagnostics(
|
|
2766
|
+
fileName: string,
|
|
2767
|
+
text: string,
|
|
2768
|
+
): readonly MergedDiagnostic[] {
|
|
2769
|
+
const sourceFile = ts.createSourceFile(
|
|
2770
|
+
fileName,
|
|
2771
|
+
text,
|
|
2772
|
+
ts.ScriptTarget.Latest,
|
|
2773
|
+
true,
|
|
2774
|
+
scriptKindForSourceFile(fileName),
|
|
2775
|
+
);
|
|
2776
|
+
const diagnostics: MergedDiagnostic[] = [];
|
|
2777
|
+
|
|
2778
|
+
const addDiagnostic = (node: ts.Node) => {
|
|
2779
|
+
const start = getLineAndColumn(text, node.getStart(sourceFile));
|
|
2780
|
+
const end = getLineAndColumn(text, node.getEnd());
|
|
2781
|
+
const guidance = describeUnsupportedFeature('legacyOctalLiteral');
|
|
2782
|
+
diagnostics.push({
|
|
2783
|
+
source: 'sound',
|
|
2784
|
+
code: SOUND_DIAGNOSTIC_CODES.unsupportedJavaScriptFeature,
|
|
2785
|
+
category: 'error',
|
|
2786
|
+
message: guidance.message,
|
|
2787
|
+
metadata: guidance.metadata,
|
|
2788
|
+
notes: guidance.example ? [`Example: ${guidance.example}`] : undefined,
|
|
2789
|
+
hint: guidance.hint,
|
|
2790
|
+
filePath: fileName,
|
|
2791
|
+
line: start.line,
|
|
2792
|
+
column: start.column,
|
|
2793
|
+
endLine: end.line,
|
|
2794
|
+
endColumn: end.column,
|
|
2795
|
+
});
|
|
2796
|
+
};
|
|
2797
|
+
|
|
2798
|
+
const visit = (node: ts.Node): void => {
|
|
2799
|
+
const numericLiteralFlags = ts.isNumericLiteral(node)
|
|
2800
|
+
? ((node as ts.NumericLiteral & { numericLiteralFlags?: number }).numericLiteralFlags ?? 0)
|
|
2801
|
+
: 0;
|
|
2802
|
+
if (
|
|
2803
|
+
(ts.isNumericLiteral(node) && (numericLiteralFlags & ts.TokenFlags.Octal) !== 0) ||
|
|
2804
|
+
(ts.isStringLiteralLike(node) && hasLegacyOctalEscape(node.getText(sourceFile).slice(1, -1)))
|
|
2805
|
+
) {
|
|
2806
|
+
addDiagnostic(node);
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
ts.forEachChild(node, visit);
|
|
2810
|
+
};
|
|
2811
|
+
|
|
2812
|
+
visit(sourceFile);
|
|
2813
|
+
return diagnostics;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
function collectScriptScopeBuiltinInterfaceMergeDiagnostics(
|
|
2817
|
+
fileName: string,
|
|
2818
|
+
text: string,
|
|
2819
|
+
): readonly MergedDiagnostic[] {
|
|
2820
|
+
const sourceFile = ts.createSourceFile(
|
|
2821
|
+
fileName,
|
|
2822
|
+
text,
|
|
2823
|
+
ts.ScriptTarget.Latest,
|
|
2824
|
+
true,
|
|
2825
|
+
scriptKindForSourceFile(fileName),
|
|
2826
|
+
);
|
|
2827
|
+
if (ts.isExternalModule(sourceFile)) {
|
|
2828
|
+
return [];
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
const diagnostics: MergedDiagnostic[] = [];
|
|
2832
|
+
for (const statement of sourceFile.statements) {
|
|
2833
|
+
if (
|
|
2834
|
+
!ts.isInterfaceDeclaration(statement) ||
|
|
2835
|
+
!SCRIPT_SCOPE_BUILTIN_INTERFACE_NAMES.has(statement.name.text)
|
|
2836
|
+
) {
|
|
2837
|
+
continue;
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
const start = getLineAndColumn(text, statement.name.getStart(sourceFile));
|
|
2841
|
+
const end = getLineAndColumn(text, statement.name.getEnd());
|
|
2842
|
+
const guidance = describeUnsupportedFeature('scriptScopeInterfaceMerge');
|
|
2843
|
+
diagnostics.push({
|
|
2844
|
+
source: 'sound',
|
|
2845
|
+
code: SOUND_DIAGNOSTIC_CODES.unsupportedJavaScriptFeature,
|
|
2846
|
+
category: 'error',
|
|
2847
|
+
message: guidance.message,
|
|
2848
|
+
metadata: guidance.metadata,
|
|
2849
|
+
notes: guidance.example ? [`Example: ${guidance.example}`] : undefined,
|
|
2850
|
+
hint: guidance.hint,
|
|
2851
|
+
filePath: fileName,
|
|
2852
|
+
line: start.line,
|
|
2853
|
+
column: start.column,
|
|
2854
|
+
endLine: end.line,
|
|
2855
|
+
endColumn: end.column,
|
|
2856
|
+
});
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
return diagnostics;
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
function collectReservedAnnotationNameConflictDiagnostics(
|
|
2863
|
+
fileName: string,
|
|
2864
|
+
text: string,
|
|
2865
|
+
importedMacroSiteKindsBySpecifier: ReadonlyMap<
|
|
2866
|
+
string,
|
|
2867
|
+
ReadonlyMap<string, ImportedMacroSiteKind>
|
|
2868
|
+
>,
|
|
2869
|
+
): readonly MergedDiagnostic[] {
|
|
2870
|
+
if (importedMacroSiteKindsBySpecifier.size === 0) {
|
|
2871
|
+
return [];
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
const importedBindings = collectImportedNamedBindings(fileName, text);
|
|
2875
|
+
const reservedBindings = new Map<string, string>();
|
|
2876
|
+
for (const binding of importedBindings) {
|
|
2877
|
+
if (!BUILTIN_DIRECTIVE_NAMES.has(binding.localName)) {
|
|
2878
|
+
continue;
|
|
2879
|
+
}
|
|
2880
|
+
const explicitKind = importedMacroSiteKindsBySpecifier.get(binding.specifier)?.get(
|
|
2881
|
+
binding.exportName,
|
|
2882
|
+
);
|
|
2883
|
+
if (explicitKind === 'annotation') {
|
|
2884
|
+
reservedBindings.set(binding.localName, binding.specifier);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
if (reservedBindings.size === 0) {
|
|
2889
|
+
return [];
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
const sourceFile = ts.createSourceFile(
|
|
2893
|
+
fileName,
|
|
2894
|
+
text,
|
|
2895
|
+
ts.ScriptTarget.Latest,
|
|
2896
|
+
true,
|
|
2897
|
+
scriptKindForSourceFile(fileName),
|
|
2898
|
+
);
|
|
2899
|
+
const annotationLookup = createAnnotationLookup(sourceFile);
|
|
2900
|
+
const diagnostics: MergedDiagnostic[] = [];
|
|
2901
|
+
|
|
2902
|
+
for (const block of annotationLookup.getBlocks()) {
|
|
2903
|
+
for (const annotation of block.annotations) {
|
|
2904
|
+
const specifier = reservedBindings.get(annotation.name);
|
|
2905
|
+
if (!specifier || !annotation.nameRange) {
|
|
2906
|
+
continue;
|
|
2907
|
+
}
|
|
2908
|
+
diagnostics.push(
|
|
2909
|
+
createReservedAnnotationNameConflictDiagnostic(
|
|
2910
|
+
fileName,
|
|
2911
|
+
text,
|
|
2912
|
+
annotation.name,
|
|
2913
|
+
annotation.nameRange,
|
|
2914
|
+
specifier,
|
|
2915
|
+
),
|
|
2916
|
+
);
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2920
|
+
return diagnostics;
|
|
2921
|
+
}
|
|
2922
|
+
function createEmptyRewriteResult(rewrittenText: string): RewriteResult {
|
|
2923
|
+
return {
|
|
2924
|
+
diagnostics: [],
|
|
2925
|
+
generatedSpans: [],
|
|
2926
|
+
macrosById: new Map(),
|
|
2927
|
+
replacements: [],
|
|
2928
|
+
rewrittenText,
|
|
2929
|
+
};
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
export function prepareSourceFile(
|
|
2933
|
+
fileName: string,
|
|
2934
|
+
text: string,
|
|
2935
|
+
expansionEnabled = true,
|
|
2936
|
+
importedMacroSiteKindsBySpecifier: ReadonlyMap<
|
|
2937
|
+
string,
|
|
2938
|
+
ReadonlyMap<string, ImportedMacroSiteKind>
|
|
2939
|
+
> = new Map(),
|
|
2940
|
+
alwaysAvailableMacroSiteKinds: ReadonlyMap<string, ImportedMacroSiteKind> = new Map(),
|
|
2941
|
+
preserveMacroAuthoring = false,
|
|
2942
|
+
): PreparedSourceFile {
|
|
2943
|
+
const rewriteInputText = !preserveMacroAuthoring && sourceTextLooksLikeMacroModule(text)
|
|
2944
|
+
? stripMacroFactoryAuthoringFromText(fileName, text)
|
|
2945
|
+
: text;
|
|
2946
|
+
const rewriteResult = rewriteMacroSource(
|
|
2947
|
+
fileName,
|
|
2948
|
+
rewriteInputText,
|
|
2949
|
+
importedMacroSiteKindsBySpecifier,
|
|
2950
|
+
alwaysAvailableMacroSiteKinds,
|
|
2951
|
+
);
|
|
2952
|
+
const reservedNameConflictDiagnostics = collectReservedAnnotationNameConflictDiagnostics(
|
|
2953
|
+
fileName,
|
|
2954
|
+
text,
|
|
2955
|
+
importedMacroSiteKindsBySpecifier,
|
|
2956
|
+
);
|
|
2957
|
+
const legacyOctalDiagnostics = collectLegacyOctalDiagnostics(fileName, text);
|
|
2958
|
+
const scriptScopeBuiltinInterfaceMergeDiagnostics =
|
|
2959
|
+
collectScriptScopeBuiltinInterfaceMergeDiagnostics(fileName, text);
|
|
2960
|
+
if (!expansionEnabled) {
|
|
2961
|
+
const disabledDiagnostics = rewriteResult.diagnostics.map((diagnostic) =>
|
|
2962
|
+
createExpansionDisabledDiagnostic(diagnostic.span, text)
|
|
2963
|
+
);
|
|
2964
|
+
const diagnostics = [
|
|
2965
|
+
...legacyOctalDiagnostics,
|
|
2966
|
+
...scriptScopeBuiltinInterfaceMergeDiagnostics,
|
|
2967
|
+
...reservedNameConflictDiagnostics,
|
|
2968
|
+
...disabledDiagnostics,
|
|
2969
|
+
];
|
|
2970
|
+
if (diagnostics.length > 0) {
|
|
2971
|
+
const rewrittenText = sanitizeMalformedMacroSource(fileName, text) ??
|
|
2972
|
+
blankPreservingLines(text);
|
|
2973
|
+
return {
|
|
2974
|
+
diagnostics,
|
|
2975
|
+
originalText: text,
|
|
2976
|
+
rewriteResult: createEmptyRewriteResult(rewrittenText),
|
|
2977
|
+
rewrittenText,
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2981
|
+
if (rewriteResult.replacements.length > 0) {
|
|
2982
|
+
const rewrittenText = `${rewriteResult.rewrittenText}\n${MACRO_HELPER_PREAMBLE}`;
|
|
2983
|
+
return {
|
|
2984
|
+
diagnostics: rewriteResult.replacements.map((replacement) =>
|
|
2985
|
+
createExpansionDisabledDiagnostic(replacement.originalSpan, text)
|
|
2986
|
+
),
|
|
2987
|
+
originalText: text,
|
|
2988
|
+
rewriteResult: createEmptyRewriteResult(rewriteResult.rewrittenText),
|
|
2989
|
+
rewrittenText,
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
return {
|
|
2994
|
+
diagnostics: [],
|
|
2995
|
+
originalText: text,
|
|
2996
|
+
rewriteResult,
|
|
2997
|
+
rewrittenText: rewriteResult.rewrittenText,
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
const diagnostics = [
|
|
3002
|
+
...legacyOctalDiagnostics,
|
|
3003
|
+
...scriptScopeBuiltinInterfaceMergeDiagnostics,
|
|
3004
|
+
...reservedNameConflictDiagnostics,
|
|
3005
|
+
...rewriteResult.diagnostics.map((diagnostic) => toFrontendDiagnostic(diagnostic, text)),
|
|
3006
|
+
];
|
|
3007
|
+
const rewrittenText = diagnostics.length > 0
|
|
3008
|
+
? sanitizeMalformedMacroSource(fileName, text) ?? blankPreservingLines(text)
|
|
3009
|
+
: rewriteResult.replacements.length > 0
|
|
3010
|
+
? `${rewriteResult.rewrittenText}\n${MACRO_HELPER_PREAMBLE}`
|
|
3011
|
+
: rewriteResult.rewrittenText;
|
|
3012
|
+
|
|
3013
|
+
const finalRewrittenText = diagnostics.length === 0
|
|
3014
|
+
? injectPreludeImports(fileName, text, rewrittenText)
|
|
3015
|
+
: rewrittenText;
|
|
3016
|
+
|
|
3017
|
+
return {
|
|
3018
|
+
diagnostics,
|
|
3019
|
+
originalText: text,
|
|
3020
|
+
postRewriteStage: finalRewrittenText === rewrittenText
|
|
3021
|
+
? undefined
|
|
3022
|
+
: buildRewriteStageFromTexts(fileName, rewrittenText, finalRewrittenText),
|
|
3023
|
+
rewriteResult,
|
|
3024
|
+
rewrittenText: finalRewrittenText,
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
export function createPreparedCompilerHost(
|
|
3029
|
+
baseHost: ts.CompilerHost,
|
|
3030
|
+
fileOverrides: ReadonlyMap<string, string> = new Map(),
|
|
3031
|
+
projectedDeclarationOverrides: ReadonlyMap<string, string> = new Map(),
|
|
3032
|
+
reusableState: PreparedCompilerHostReuseState = createPreparedCompilerHostReuseState(
|
|
3033
|
+
baseHost.getCurrentDirectory?.() ?? ts.sys.getCurrentDirectory(),
|
|
3034
|
+
),
|
|
3035
|
+
compilerOptions: ts.CompilerOptions = {},
|
|
3036
|
+
importedMacroSiteKindsBySpecifier: ReadonlyMap<
|
|
3037
|
+
string,
|
|
3038
|
+
ReadonlyMap<string, ImportedMacroSiteKind>
|
|
3039
|
+
> = new Map(),
|
|
3040
|
+
expansionEnabled = true,
|
|
3041
|
+
alwaysAvailableMacroSiteKinds: ReadonlyMap<string, ImportedMacroSiteKind> = new Map(),
|
|
3042
|
+
preserveMacroAuthoring = false,
|
|
3043
|
+
invalidateModuleResolutions = true,
|
|
3044
|
+
): PreparedCompilerHost {
|
|
3045
|
+
const preparedFiles = new Map<string, PreparedSourceFile>();
|
|
3046
|
+
const macroPreparationByFile = new Map<string, boolean>();
|
|
3047
|
+
const currentDirectory = baseHost.getCurrentDirectory?.() ?? ts.sys.getCurrentDirectory();
|
|
3048
|
+
const projectedDeclarationPresenceSignature = createProjectedDeclarationPresenceSignature(
|
|
3049
|
+
projectedDeclarationOverrides,
|
|
3050
|
+
);
|
|
3051
|
+
const preparedEnvironmentSignature = stableStringify({
|
|
3052
|
+
alwaysAvailableMacroSiteKinds: serializeMacroSiteKinds(alwaysAvailableMacroSiteKinds),
|
|
3053
|
+
compilerOptions,
|
|
3054
|
+
projectedDeclarationPresenceSignature,
|
|
3055
|
+
});
|
|
3056
|
+
const getCanonicalFileName = ts.sys.useCaseSensitiveFileNames
|
|
3057
|
+
? (fileName: string) => fileName
|
|
3058
|
+
: (fileName: string) => fileName.toLowerCase();
|
|
3059
|
+
reusableState.moduleResolutionCache = ts.createModuleResolutionCache(
|
|
3060
|
+
currentDirectory,
|
|
3061
|
+
getCanonicalFileName,
|
|
3062
|
+
);
|
|
3063
|
+
reusableState.moduleResolutionCacheSignature = projectedDeclarationPresenceSignature;
|
|
3064
|
+
|
|
3065
|
+
function getProjectedDeclarationText(fileName: string): string | undefined {
|
|
3066
|
+
if (!isProjectedSoundscriptDeclarationFile(fileName)) {
|
|
3067
|
+
return undefined;
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
return projectedDeclarationOverrides.get(toProjectedDeclarationSourceFileName(fileName));
|
|
3071
|
+
}
|
|
3072
|
+
|
|
3073
|
+
const resolvedImportedMacroSiteKindsByFile = new Map<
|
|
3074
|
+
string,
|
|
3075
|
+
ReadonlyMap<string, ImportedMacroSiteKind>
|
|
3076
|
+
>();
|
|
3077
|
+
const resolvingImportedMacroSiteKindsByFile = new Set<string>();
|
|
3078
|
+
|
|
3079
|
+
function sourceFileForImportedMacroSiteKinds(fileName: string, text: string): ts.SourceFile {
|
|
3080
|
+
return ts.createSourceFile(
|
|
3081
|
+
fileName,
|
|
3082
|
+
text,
|
|
3083
|
+
ts.ScriptTarget.Latest,
|
|
3084
|
+
true,
|
|
3085
|
+
scriptKindForSourceFileName(toSourceFileName(fileName)),
|
|
3086
|
+
);
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
function importedMacroSiteKindsForResolvedFile(
|
|
3090
|
+
resolvedFileName: string,
|
|
3091
|
+
): ReadonlyMap<string, ImportedMacroSiteKind> {
|
|
3092
|
+
const cached = resolvedImportedMacroSiteKindsByFile.get(resolvedFileName);
|
|
3093
|
+
if (cached) {
|
|
3094
|
+
return cached;
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
if (resolvingImportedMacroSiteKindsByFile.has(resolvedFileName)) {
|
|
3098
|
+
return new Map();
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
resolvingImportedMacroSiteKindsByFile.add(resolvedFileName);
|
|
3102
|
+
|
|
3103
|
+
const resolvedText = fileOverrides.get(toSourceFileName(resolvedFileName)) ??
|
|
3104
|
+
baseHost.readFile(toSourceFileName(resolvedFileName)) ?? '';
|
|
3105
|
+
const siteKinds = new Map<string, ImportedMacroSiteKind>();
|
|
3106
|
+
try {
|
|
3107
|
+
if (
|
|
3108
|
+
sourceTextLooksLikeMacroModule(resolvedText) || usesLegacyDefineMacroAuthoring(resolvedText)
|
|
3109
|
+
) {
|
|
3110
|
+
for (
|
|
3111
|
+
const scannedExport of scanMacroFactoryExports(resolvedFileName, resolvedText).values()
|
|
3112
|
+
) {
|
|
3113
|
+
siteKinds.set(scannedExport.exportName, macroSiteKindForFactoryForm(scannedExport.form));
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
const importedBindingsByLocalName = new Map(
|
|
3118
|
+
collectImportedNamedBindings(resolvedFileName, resolvedText)
|
|
3119
|
+
.map((binding) => [binding.localName, binding] as const),
|
|
3120
|
+
);
|
|
3121
|
+
const sourceFile = sourceFileForImportedMacroSiteKinds(resolvedFileName, resolvedText);
|
|
3122
|
+
|
|
3123
|
+
for (const statement of sourceFile.statements) {
|
|
3124
|
+
if (
|
|
3125
|
+
!ts.isExportDeclaration(statement) ||
|
|
3126
|
+
!statement.moduleSpecifier ||
|
|
3127
|
+
!ts.isStringLiteral(statement.moduleSpecifier)
|
|
3128
|
+
) {
|
|
3129
|
+
continue;
|
|
3130
|
+
}
|
|
3131
|
+
|
|
3132
|
+
const exportedKinds = resolveImportedMacroSiteKindsForSpecifier(
|
|
3133
|
+
resolvedFileName,
|
|
3134
|
+
statement.moduleSpecifier.text,
|
|
3135
|
+
);
|
|
3136
|
+
if (!exportedKinds) {
|
|
3137
|
+
continue;
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
if (!statement.exportClause) {
|
|
3141
|
+
for (const [exportName, kind] of exportedKinds.entries()) {
|
|
3142
|
+
siteKinds.set(exportName, kind);
|
|
3143
|
+
}
|
|
3144
|
+
continue;
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
if (!ts.isNamedExports(statement.exportClause)) {
|
|
3148
|
+
continue;
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3151
|
+
for (const element of statement.exportClause.elements) {
|
|
3152
|
+
const sourceName = element.propertyName?.text ?? element.name.text;
|
|
3153
|
+
const exportedName = element.name.text;
|
|
3154
|
+
const kind = exportedKinds.get(sourceName);
|
|
3155
|
+
if (kind) {
|
|
3156
|
+
siteKinds.set(exportedName, kind);
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
for (const statement of sourceFile.statements) {
|
|
3162
|
+
if (
|
|
3163
|
+
!ts.isExportDeclaration(statement) ||
|
|
3164
|
+
!!statement.moduleSpecifier ||
|
|
3165
|
+
!statement.exportClause ||
|
|
3166
|
+
!ts.isNamedExports(statement.exportClause)
|
|
3167
|
+
) {
|
|
3168
|
+
continue;
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
for (const element of statement.exportClause.elements) {
|
|
3172
|
+
const localName = element.propertyName?.text ?? element.name.text;
|
|
3173
|
+
const exportedName = element.name.text;
|
|
3174
|
+
const binding = importedBindingsByLocalName.get(localName);
|
|
3175
|
+
if (!binding) {
|
|
3176
|
+
continue;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
const importedKinds = resolveImportedMacroSiteKindsForSpecifier(
|
|
3180
|
+
resolvedFileName,
|
|
3181
|
+
binding.specifier,
|
|
3182
|
+
);
|
|
3183
|
+
const kind = importedKinds?.get(binding.exportName);
|
|
3184
|
+
if (kind) {
|
|
3185
|
+
siteKinds.set(exportedName, kind);
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
} finally {
|
|
3190
|
+
resolvingImportedMacroSiteKindsByFile.delete(resolvedFileName);
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
resolvedImportedMacroSiteKindsByFile.set(resolvedFileName, siteKinds);
|
|
3194
|
+
return siteKinds;
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
function resolveImportedMacroSiteKindsForSpecifier(
|
|
3198
|
+
fileName: string,
|
|
3199
|
+
specifier: string,
|
|
3200
|
+
): ReadonlyMap<string, ImportedMacroSiteKind> | undefined {
|
|
3201
|
+
const moduleResolutionHost = createModuleResolutionHost();
|
|
3202
|
+
const explicitKinds = importedMacroSiteKindsBySpecifier.get(specifier);
|
|
3203
|
+
if (explicitKinds) {
|
|
3204
|
+
return explicitKinds;
|
|
3205
|
+
}
|
|
3206
|
+
|
|
3207
|
+
const resolved = resolvePreferredSoundscriptModule(
|
|
3208
|
+
specifier,
|
|
3209
|
+
fileName,
|
|
3210
|
+
compilerOptions,
|
|
3211
|
+
moduleResolutionHost,
|
|
3212
|
+
) ?? ts.resolveModuleName(
|
|
3213
|
+
specifier,
|
|
3214
|
+
fileName,
|
|
3215
|
+
compilerOptions,
|
|
3216
|
+
moduleResolutionHost,
|
|
3217
|
+
reusableState.moduleResolutionCache,
|
|
3218
|
+
).resolvedModule;
|
|
3219
|
+
const packageMacroSourceEntry = resolved?.resolvedFileName
|
|
3220
|
+
? getSoundScriptPackageExportInfoForResolvedModule(
|
|
3221
|
+
specifier,
|
|
3222
|
+
resolved.resolvedFileName,
|
|
3223
|
+
moduleResolutionHost,
|
|
3224
|
+
)?.sourceEntryPath
|
|
3225
|
+
: undefined;
|
|
3226
|
+
const resolvedFileName = resolved?.resolvedFileName
|
|
3227
|
+
? packageMacroSourceEntry
|
|
3228
|
+
? toSourceFileName(packageMacroSourceEntry)
|
|
3229
|
+
: isProjectedSoundscriptDeclarationFile(resolved.resolvedFileName)
|
|
3230
|
+
? toProjectedDeclarationSourceFileName(resolved.resolvedFileName)
|
|
3231
|
+
: toSourceFileName(resolved.resolvedFileName)
|
|
3232
|
+
: undefined;
|
|
3233
|
+
if (!resolvedFileName) {
|
|
3234
|
+
return undefined;
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
const resolvedKinds = importedMacroSiteKindsForResolvedFile(resolvedFileName);
|
|
3238
|
+
return resolvedKinds.size > 0 ? resolvedKinds : undefined;
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3241
|
+
function collectImportedMacroSiteKindsBySpecifier(
|
|
3242
|
+
fileName: string,
|
|
3243
|
+
sourceText: string,
|
|
3244
|
+
): ReadonlyMap<string, ReadonlyMap<string, ImportedMacroSiteKind>> {
|
|
3245
|
+
return collectImportedMacroSiteKindsForSource(fileName, sourceText, {
|
|
3246
|
+
explicitSiteKindsBySpecifier: importedMacroSiteKindsBySpecifier,
|
|
3247
|
+
resolveOnlySyntaxCandidates: true,
|
|
3248
|
+
resolveSiteKindsForSpecifier: (specifier) =>
|
|
3249
|
+
resolveImportedMacroSiteKindsForSpecifier(fileName, specifier),
|
|
3250
|
+
});
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
function shouldPrepareSourceFile(
|
|
3254
|
+
fileName: string,
|
|
3255
|
+
sourceText: string,
|
|
3256
|
+
importedMacroSiteKindsBySpecifier?: ReadonlyMap<
|
|
3257
|
+
string,
|
|
3258
|
+
ReadonlyMap<string, ImportedMacroSiteKind>
|
|
3259
|
+
>,
|
|
3260
|
+
): boolean {
|
|
3261
|
+
if (isSoundscriptSourceFile(fileName)) {
|
|
3262
|
+
return true;
|
|
3263
|
+
}
|
|
3264
|
+
|
|
3265
|
+
const cached = macroPreparationByFile.get(fileName);
|
|
3266
|
+
if (cached !== undefined) {
|
|
3267
|
+
return cached;
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3270
|
+
const scanResult = scanMacroCandidates(fileName, sourceText);
|
|
3271
|
+
if (
|
|
3272
|
+
scanResult.diagnostics.length > 0 ||
|
|
3273
|
+
scanResult.hashes.some((hash) => hash.kind === 'macro-start')
|
|
3274
|
+
) {
|
|
3275
|
+
macroPreparationByFile.set(fileName, true);
|
|
3276
|
+
return true;
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
if (sourceTextLooksLikeMacroModule(sourceText) || usesLegacyDefineMacroAuthoring(sourceText)) {
|
|
3280
|
+
macroPreparationByFile.set(fileName, true);
|
|
3281
|
+
return true;
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
if (
|
|
3285
|
+
(importedMacroSiteKindsBySpecifier ??
|
|
3286
|
+
collectImportedMacroSiteKindsBySpecifier(fileName, sourceText)).size > 0
|
|
3287
|
+
) {
|
|
3288
|
+
macroPreparationByFile.set(fileName, true);
|
|
3289
|
+
return true;
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
macroPreparationByFile.set(fileName, false);
|
|
3293
|
+
return false;
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3296
|
+
function getPreparedSourceFile(fileName: string) {
|
|
3297
|
+
const sourceFileName = toSourceFileName(fileName);
|
|
3298
|
+
const cached = preparedFiles.get(sourceFileName);
|
|
3299
|
+
if (cached) {
|
|
3300
|
+
return cached;
|
|
3301
|
+
}
|
|
3302
|
+
|
|
3303
|
+
const sourceText = fileOverrides.get(sourceFileName) ?? baseHost.readFile(sourceFileName);
|
|
3304
|
+
if (sourceText === undefined) {
|
|
3305
|
+
return undefined;
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3308
|
+
const declarationFile = isDeclarationFileName(sourceFileName);
|
|
3309
|
+
const importedMacroSiteKinds = declarationFile
|
|
3310
|
+
? new Map<string, ReadonlyMap<string, ImportedMacroSiteKind>>()
|
|
3311
|
+
: collectImportedMacroSiteKindsBySpecifier(
|
|
3312
|
+
sourceFileName,
|
|
3313
|
+
sourceText,
|
|
3314
|
+
);
|
|
3315
|
+
const importedMacroSiteKindsSignature = declarationFile
|
|
3316
|
+
? ''
|
|
3317
|
+
: stableStringify(
|
|
3318
|
+
serializeImportedMacroSiteKindsBySpecifier(importedMacroSiteKinds),
|
|
3319
|
+
);
|
|
3320
|
+
const shouldPrepare = declarationFile
|
|
3321
|
+
? false
|
|
3322
|
+
: shouldPrepareSourceFile(
|
|
3323
|
+
sourceFileName,
|
|
3324
|
+
sourceText,
|
|
3325
|
+
importedMacroSiteKinds,
|
|
3326
|
+
);
|
|
3327
|
+
const cachedEntry = reusableState.preparedSourceFiles.get(sourceFileName);
|
|
3328
|
+
const prepared = cachedEntry?.sourceText === sourceText &&
|
|
3329
|
+
cachedEntry.expansionEnabled === expansionEnabled &&
|
|
3330
|
+
cachedEntry.importedMacroSiteKindsSignature === importedMacroSiteKindsSignature &&
|
|
3331
|
+
cachedEntry.preserveMacroAuthoring === preserveMacroAuthoring &&
|
|
3332
|
+
cachedEntry.environmentSignature === preparedEnvironmentSignature &&
|
|
3333
|
+
shouldPrepare
|
|
3334
|
+
? cachedEntry.prepared
|
|
3335
|
+
: shouldPrepare
|
|
3336
|
+
? prepareSourceFile(
|
|
3337
|
+
sourceFileName,
|
|
3338
|
+
sourceText,
|
|
3339
|
+
expansionEnabled,
|
|
3340
|
+
importedMacroSiteKinds,
|
|
3341
|
+
alwaysAvailableMacroSiteKinds,
|
|
3342
|
+
preserveMacroAuthoring,
|
|
3343
|
+
)
|
|
3344
|
+
: {
|
|
3345
|
+
diagnostics: [],
|
|
3346
|
+
originalText: sourceText,
|
|
3347
|
+
rewriteResult: createEmptyRewriteResult(sourceText),
|
|
3348
|
+
rewrittenText: sourceText,
|
|
3349
|
+
};
|
|
3350
|
+
|
|
3351
|
+
const rewrittenWithNumericPrelude = isSoundscriptSourceFile(sourceFileName) &&
|
|
3352
|
+
!isInstalledRuntimeStdlibSourceFile(sourceFileName)
|
|
3353
|
+
? prependMachineNumericSourcePrelude(sourceFileName, prepared.rewrittenText)
|
|
3354
|
+
: prepared.rewrittenText;
|
|
3355
|
+
const rewrittenWithProjectedTypeImports = rewriteForeignTypeImportsToUnknown(
|
|
3356
|
+
sourceFileName,
|
|
3357
|
+
rewrittenWithNumericPrelude,
|
|
3358
|
+
compilerOptions,
|
|
3359
|
+
createModuleResolutionHost(),
|
|
3360
|
+
importedMacroSiteKinds,
|
|
3361
|
+
);
|
|
3362
|
+
const finalizedPrepared = rewrittenWithProjectedTypeImports === prepared.rewrittenText
|
|
3363
|
+
? prepared
|
|
3364
|
+
: {
|
|
3365
|
+
...prepared,
|
|
3366
|
+
postRewriteStage: buildRewriteStageFromTexts(
|
|
3367
|
+
sourceFileName,
|
|
3368
|
+
prepared.rewriteResult.rewrittenText,
|
|
3369
|
+
rewrittenWithProjectedTypeImports,
|
|
3370
|
+
),
|
|
3371
|
+
rewrittenText: rewrittenWithProjectedTypeImports,
|
|
3372
|
+
};
|
|
3373
|
+
|
|
3374
|
+
reusableState.preparedSourceFiles.set(sourceFileName, {
|
|
3375
|
+
environmentSignature: preparedEnvironmentSignature,
|
|
3376
|
+
expansionEnabled,
|
|
3377
|
+
importedMacroSiteKindsSignature,
|
|
3378
|
+
prepared: finalizedPrepared,
|
|
3379
|
+
preserveMacroAuthoring,
|
|
3380
|
+
sourceText,
|
|
3381
|
+
});
|
|
3382
|
+
preparedFiles.set(sourceFileName, finalizedPrepared);
|
|
3383
|
+
return finalizedPrepared;
|
|
3384
|
+
}
|
|
3385
|
+
|
|
3386
|
+
function getCachedOrCreateSourceFile(
|
|
3387
|
+
cache: Map<string, CachedSourceFileEntry>,
|
|
3388
|
+
fileName: string,
|
|
3389
|
+
text: string,
|
|
3390
|
+
languageVersion: ts.ScriptTarget | ts.CreateSourceFileOptions,
|
|
3391
|
+
environmentSignature: string,
|
|
3392
|
+
shouldCreateNewSourceFile?: boolean,
|
|
3393
|
+
): ts.SourceFile {
|
|
3394
|
+
if (!shouldCreateNewSourceFile) {
|
|
3395
|
+
const cached = cache.get(fileName);
|
|
3396
|
+
if (cached?.text === text && cached.environmentSignature === environmentSignature) {
|
|
3397
|
+
return cached.sourceFile;
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
|
|
3401
|
+
const sourceFile = ts.createSourceFile(
|
|
3402
|
+
fileName,
|
|
3403
|
+
text,
|
|
3404
|
+
languageVersion,
|
|
3405
|
+
true,
|
|
3406
|
+
scriptKindForSourceFileName(toSourceFileName(fileName)),
|
|
3407
|
+
);
|
|
3408
|
+
ensureSourceFileVersion(sourceFile, text);
|
|
3409
|
+
if (!shouldCreateNewSourceFile) {
|
|
3410
|
+
cache.set(fileName, {
|
|
3411
|
+
environmentSignature,
|
|
3412
|
+
sourceFile,
|
|
3413
|
+
text,
|
|
3414
|
+
});
|
|
3415
|
+
}
|
|
3416
|
+
return sourceFile;
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
function createModuleResolutionHost(): ts.ModuleResolutionHost {
|
|
3420
|
+
return {
|
|
3421
|
+
directoryExists: baseHost.directoryExists?.bind(baseHost),
|
|
3422
|
+
fileExists(fileName: string): boolean {
|
|
3423
|
+
if (getProjectedDeclarationText(fileName) !== undefined) {
|
|
3424
|
+
return true;
|
|
3425
|
+
}
|
|
3426
|
+
const sourceFileName = toSourceFileName(fileName);
|
|
3427
|
+
return fileOverrides.has(sourceFileName) || baseHost.fileExists(sourceFileName);
|
|
3428
|
+
},
|
|
3429
|
+
getCurrentDirectory: baseHost.getCurrentDirectory?.bind(baseHost) ??
|
|
3430
|
+
(() => ts.sys.getCurrentDirectory()),
|
|
3431
|
+
getDirectories: baseHost.getDirectories?.bind(baseHost),
|
|
3432
|
+
readFile(fileName: string): string | undefined {
|
|
3433
|
+
const projectedDeclarationText = getProjectedDeclarationText(fileName);
|
|
3434
|
+
if (projectedDeclarationText !== undefined) {
|
|
3435
|
+
return projectedDeclarationText;
|
|
3436
|
+
}
|
|
3437
|
+
const sourceFileName = toSourceFileName(fileName);
|
|
3438
|
+
return fileOverrides.get(sourceFileName) ?? baseHost.readFile(sourceFileName);
|
|
3439
|
+
},
|
|
3440
|
+
realpath: baseHost.realpath?.bind(baseHost),
|
|
3441
|
+
useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames,
|
|
3442
|
+
};
|
|
3443
|
+
}
|
|
3444
|
+
|
|
3445
|
+
function resolvePreferredSoundscriptModule(
|
|
3446
|
+
moduleName: string,
|
|
3447
|
+
containingFile: string,
|
|
3448
|
+
options: ts.CompilerOptions,
|
|
3449
|
+
moduleResolutionHost: ts.ModuleResolutionHost,
|
|
3450
|
+
redirectedReference?: ts.ResolvedProjectReference,
|
|
3451
|
+
): ts.ResolvedModuleFull | undefined {
|
|
3452
|
+
if (!moduleName.startsWith('.')) {
|
|
3453
|
+
return undefined;
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3456
|
+
const explicitNonSoundscriptExtensionPattern = /\.(?:[cm]?[jt]sx?|[cm]?js)$/u;
|
|
3457
|
+
if (explicitNonSoundscriptExtensionPattern.test(moduleName)) {
|
|
3458
|
+
return undefined;
|
|
3459
|
+
}
|
|
3460
|
+
|
|
3461
|
+
const candidates = moduleName.endsWith('.macro.sts')
|
|
3462
|
+
? [moduleName]
|
|
3463
|
+
: moduleName.endsWith('.macro')
|
|
3464
|
+
? [`${moduleName}.sts`, `${moduleName}/index.macro.sts`]
|
|
3465
|
+
: moduleName.endsWith('.sts')
|
|
3466
|
+
? [moduleName]
|
|
3467
|
+
: [`${moduleName}.sts`, `${moduleName}/index.sts`];
|
|
3468
|
+
|
|
3469
|
+
for (const candidate of candidates) {
|
|
3470
|
+
const absoluteCandidate = join(dirname(containingFile), candidate);
|
|
3471
|
+
if (moduleResolutionHost.fileExists(absoluteCandidate)) {
|
|
3472
|
+
return {
|
|
3473
|
+
extension: ts.Extension.Ts,
|
|
3474
|
+
isExternalLibraryImport: false,
|
|
3475
|
+
resolvedFileName: absoluteCandidate,
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
return undefined;
|
|
3481
|
+
}
|
|
3482
|
+
|
|
3483
|
+
function resolveModuleNames(
|
|
3484
|
+
moduleNames: string[],
|
|
3485
|
+
containingFile: string,
|
|
3486
|
+
reusedNames?: string[],
|
|
3487
|
+
redirectedReference?: ts.ResolvedProjectReference,
|
|
3488
|
+
options?: ts.CompilerOptions,
|
|
3489
|
+
): (ts.ResolvedModule | undefined)[] {
|
|
3490
|
+
const sourceContainingFile = toSourceFileName(containingFile);
|
|
3491
|
+
const baseResolvedModules = baseHost.resolveModuleNames?.(
|
|
3492
|
+
moduleNames,
|
|
3493
|
+
sourceContainingFile,
|
|
3494
|
+
reusedNames,
|
|
3495
|
+
redirectedReference,
|
|
3496
|
+
options ?? {},
|
|
3497
|
+
);
|
|
3498
|
+
const moduleResolutionHost = createModuleResolutionHost();
|
|
3499
|
+
|
|
3500
|
+
return moduleNames.map((moduleName, index) => {
|
|
3501
|
+
const preferredSoundscript = resolvePreferredSoundscriptModule(
|
|
3502
|
+
moduleName,
|
|
3503
|
+
sourceContainingFile,
|
|
3504
|
+
options ?? {},
|
|
3505
|
+
moduleResolutionHost,
|
|
3506
|
+
redirectedReference,
|
|
3507
|
+
);
|
|
3508
|
+
const baseResolved = baseResolvedModules?.[index];
|
|
3509
|
+
if (preferredSoundscript) {
|
|
3510
|
+
const installedRuntimeDeclarationPath = installedRuntimeStdlibDeclarationPath(
|
|
3511
|
+
preferredSoundscript.resolvedFileName,
|
|
3512
|
+
);
|
|
3513
|
+
if (installedRuntimeDeclarationPath) {
|
|
3514
|
+
return {
|
|
3515
|
+
...preferredSoundscript,
|
|
3516
|
+
extension: ts.Extension.Dts,
|
|
3517
|
+
resolvedFileName: installedRuntimeDeclarationPath,
|
|
3518
|
+
};
|
|
3519
|
+
}
|
|
3520
|
+
if (
|
|
3521
|
+
projectedDeclarationOverrides.has(toSourceFileName(preferredSoundscript.resolvedFileName))
|
|
3522
|
+
) {
|
|
3523
|
+
return {
|
|
3524
|
+
...preferredSoundscript,
|
|
3525
|
+
extension: ts.Extension.Dts,
|
|
3526
|
+
resolvedFileName: toProjectedDeclarationFileName(
|
|
3527
|
+
toSourceFileName(preferredSoundscript.resolvedFileName),
|
|
3528
|
+
),
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
const remapped = remapResolvedModuleToSoundScriptSource(
|
|
3532
|
+
moduleName,
|
|
3533
|
+
preferredSoundscript,
|
|
3534
|
+
moduleResolutionHost,
|
|
3535
|
+
);
|
|
3536
|
+
return {
|
|
3537
|
+
...remapped,
|
|
3538
|
+
extension: ts.Extension.Ts,
|
|
3539
|
+
resolvedFileName: toProgramFileName(remapped.resolvedFileName),
|
|
3540
|
+
};
|
|
3541
|
+
}
|
|
3542
|
+
|
|
3543
|
+
if (baseResolved) {
|
|
3544
|
+
const remapped = remapResolvedModuleToSoundScriptSource(
|
|
3545
|
+
moduleName,
|
|
3546
|
+
baseResolved,
|
|
3547
|
+
moduleResolutionHost,
|
|
3548
|
+
);
|
|
3549
|
+
const installedRuntimeDeclarationPath = installedRuntimeStdlibDeclarationPath(
|
|
3550
|
+
remapped.resolvedFileName,
|
|
3551
|
+
);
|
|
3552
|
+
if (installedRuntimeDeclarationPath) {
|
|
3553
|
+
return {
|
|
3554
|
+
...remapped,
|
|
3555
|
+
extension: ts.Extension.Dts,
|
|
3556
|
+
resolvedFileName: installedRuntimeDeclarationPath,
|
|
3557
|
+
};
|
|
3558
|
+
}
|
|
3559
|
+
if (projectedDeclarationOverrides.has(toSourceFileName(remapped.resolvedFileName))) {
|
|
3560
|
+
return {
|
|
3561
|
+
...remapped,
|
|
3562
|
+
extension: ts.Extension.Dts,
|
|
3563
|
+
resolvedFileName: toProjectedDeclarationFileName(
|
|
3564
|
+
toSourceFileName(remapped.resolvedFileName),
|
|
3565
|
+
),
|
|
3566
|
+
};
|
|
3567
|
+
}
|
|
3568
|
+
return isSoundscriptSourceFile(remapped.resolvedFileName)
|
|
3569
|
+
? {
|
|
3570
|
+
...remapped,
|
|
3571
|
+
extension: ts.Extension.Ts,
|
|
3572
|
+
resolvedFileName: toProgramFileName(remapped.resolvedFileName),
|
|
3573
|
+
}
|
|
3574
|
+
: remapped;
|
|
3575
|
+
}
|
|
3576
|
+
|
|
3577
|
+
const resolvedModule = ts.resolveModuleName(
|
|
3578
|
+
moduleName,
|
|
3579
|
+
sourceContainingFile,
|
|
3580
|
+
options ?? {},
|
|
3581
|
+
moduleResolutionHost,
|
|
3582
|
+
reusableState.moduleResolutionCache,
|
|
3583
|
+
redirectedReference,
|
|
3584
|
+
).resolvedModule;
|
|
3585
|
+
|
|
3586
|
+
const resolvedOrFallback = resolvedModule ?? (() => {
|
|
3587
|
+
return resolvePreferredSoundscriptModule(
|
|
3588
|
+
moduleName,
|
|
3589
|
+
sourceContainingFile,
|
|
3590
|
+
options ?? {},
|
|
3591
|
+
moduleResolutionHost,
|
|
3592
|
+
redirectedReference,
|
|
3593
|
+
);
|
|
3594
|
+
})();
|
|
3595
|
+
|
|
3596
|
+
if (!resolvedOrFallback) {
|
|
3597
|
+
return undefined;
|
|
3598
|
+
}
|
|
3599
|
+
|
|
3600
|
+
const remapped = remapResolvedModuleToSoundScriptSource(
|
|
3601
|
+
moduleName,
|
|
3602
|
+
resolvedOrFallback,
|
|
3603
|
+
moduleResolutionHost,
|
|
3604
|
+
);
|
|
3605
|
+
const installedRuntimeDeclarationPath = installedRuntimeStdlibDeclarationPath(
|
|
3606
|
+
remapped.resolvedFileName,
|
|
3607
|
+
);
|
|
3608
|
+
if (installedRuntimeDeclarationPath) {
|
|
3609
|
+
return {
|
|
3610
|
+
...remapped,
|
|
3611
|
+
extension: ts.Extension.Dts,
|
|
3612
|
+
resolvedFileName: installedRuntimeDeclarationPath,
|
|
3613
|
+
};
|
|
3614
|
+
}
|
|
3615
|
+
if (projectedDeclarationOverrides.has(toSourceFileName(remapped.resolvedFileName))) {
|
|
3616
|
+
return {
|
|
3617
|
+
...remapped,
|
|
3618
|
+
extension: ts.Extension.Dts,
|
|
3619
|
+
resolvedFileName: toProjectedDeclarationFileName(
|
|
3620
|
+
toSourceFileName(remapped.resolvedFileName),
|
|
3621
|
+
),
|
|
3622
|
+
};
|
|
3623
|
+
}
|
|
3624
|
+
return isSoundscriptSourceFile(remapped.resolvedFileName)
|
|
3625
|
+
? {
|
|
3626
|
+
...remapped,
|
|
3627
|
+
extension: ts.Extension.Ts,
|
|
3628
|
+
resolvedFileName: toProgramFileName(remapped.resolvedFileName),
|
|
3629
|
+
}
|
|
3630
|
+
: remapped;
|
|
3631
|
+
});
|
|
3632
|
+
}
|
|
3633
|
+
|
|
3634
|
+
return {
|
|
3635
|
+
dispose(): void {
|
|
3636
|
+
preparedFiles.clear();
|
|
3637
|
+
macroPreparationByFile.clear();
|
|
3638
|
+
resolvedImportedMacroSiteKindsByFile.clear();
|
|
3639
|
+
},
|
|
3640
|
+
frontendDiagnostics(): readonly MergedDiagnostic[] {
|
|
3641
|
+
return [...preparedFiles.values()].flatMap((prepared) => prepared.diagnostics);
|
|
3642
|
+
},
|
|
3643
|
+
getPreparedSourceFile,
|
|
3644
|
+
getCachedPreparedSourceFiles(): readonly PreparedSourceFile[] {
|
|
3645
|
+
return [...preparedFiles.values()];
|
|
3646
|
+
},
|
|
3647
|
+
getMacroPlaceholderIndex(): MacroPlaceholderIndex {
|
|
3648
|
+
return buildMacroPlaceholderIndex([...preparedFiles.values()]);
|
|
3649
|
+
},
|
|
3650
|
+
host: {
|
|
3651
|
+
...baseHost,
|
|
3652
|
+
fileExists(fileName: string): boolean {
|
|
3653
|
+
const sourceFileName = toSourceFileName(fileName);
|
|
3654
|
+
return fileOverrides.has(sourceFileName) || baseHost.fileExists(sourceFileName);
|
|
3655
|
+
},
|
|
3656
|
+
getSourceFile(
|
|
3657
|
+
fileName: string,
|
|
3658
|
+
languageVersion: ts.ScriptTarget | ts.CreateSourceFileOptions,
|
|
3659
|
+
onError?: (message: string) => void,
|
|
3660
|
+
shouldCreateNewSourceFile?: boolean,
|
|
3661
|
+
): ts.SourceFile | undefined {
|
|
3662
|
+
const projectedDeclarationText = getProjectedDeclarationText(fileName);
|
|
3663
|
+
if (projectedDeclarationText !== undefined) {
|
|
3664
|
+
return getCachedOrCreateSourceFile(
|
|
3665
|
+
reusableState.projectedDeclarationSourceFiles,
|
|
3666
|
+
fileName,
|
|
3667
|
+
projectedDeclarationText,
|
|
3668
|
+
languageVersion,
|
|
3669
|
+
'',
|
|
3670
|
+
shouldCreateNewSourceFile,
|
|
3671
|
+
);
|
|
3672
|
+
}
|
|
3673
|
+
const prepared = getPreparedSourceFile(fileName);
|
|
3674
|
+
if (prepared !== undefined) {
|
|
3675
|
+
return getCachedOrCreateSourceFile(
|
|
3676
|
+
reusableState.rewrittenSourceFiles,
|
|
3677
|
+
fileName,
|
|
3678
|
+
prepared.rewrittenText,
|
|
3679
|
+
languageVersion,
|
|
3680
|
+
projectedDeclarationPresenceSignature,
|
|
3681
|
+
shouldCreateNewSourceFile,
|
|
3682
|
+
);
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3685
|
+
const sourceFileName = toSourceFileName(fileName);
|
|
3686
|
+
const sourceFile = baseHost.getSourceFile(
|
|
3687
|
+
sourceFileName,
|
|
3688
|
+
languageVersion,
|
|
3689
|
+
onError,
|
|
3690
|
+
shouldCreateNewSourceFile,
|
|
3691
|
+
);
|
|
3692
|
+
return sourceFile ? ensureSourceFileVersion(sourceFile, sourceFile.text) : undefined;
|
|
3693
|
+
},
|
|
3694
|
+
hasInvalidatedResolutions(): boolean {
|
|
3695
|
+
return invalidateModuleResolutions;
|
|
3696
|
+
},
|
|
3697
|
+
readFile(fileName: string): string | undefined {
|
|
3698
|
+
const projectedDeclarationText = getProjectedDeclarationText(fileName);
|
|
3699
|
+
if (projectedDeclarationText !== undefined) {
|
|
3700
|
+
return projectedDeclarationText;
|
|
3701
|
+
}
|
|
3702
|
+
const sourceFileName = toSourceFileName(fileName);
|
|
3703
|
+
return getPreparedSourceFile(fileName)?.rewrittenText ?? baseHost.readFile(sourceFileName);
|
|
3704
|
+
},
|
|
3705
|
+
resolveModuleNames,
|
|
3706
|
+
},
|
|
3707
|
+
reuseState: reusableState,
|
|
3708
|
+
};
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
function serializeImportedMacroSiteKindsBySpecifier(
|
|
3712
|
+
siteKindsBySpecifier: ReadonlyMap<string, ReadonlyMap<string, ImportedMacroSiteKind>>,
|
|
3713
|
+
): readonly (readonly [string, readonly (readonly [string, ImportedMacroSiteKind])[]])[] {
|
|
3714
|
+
return [...siteKindsBySpecifier.entries()]
|
|
3715
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
3716
|
+
.map(([specifier, siteKinds]) =>
|
|
3717
|
+
[
|
|
3718
|
+
specifier,
|
|
3719
|
+
serializeMacroSiteKinds(siteKinds),
|
|
3720
|
+
] as const
|
|
3721
|
+
);
|
|
3722
|
+
}
|
|
3723
|
+
|
|
3724
|
+
function serializeMacroSiteKinds(
|
|
3725
|
+
siteKinds: ReadonlyMap<string, ImportedMacroSiteKind>,
|
|
3726
|
+
): readonly (readonly [string, ImportedMacroSiteKind])[] {
|
|
3727
|
+
return [...siteKinds.entries()].sort(([left], [right]) => left.localeCompare(right));
|
|
3728
|
+
}
|
|
3729
|
+
|
|
3730
|
+
export function createPreparedProgram(
|
|
3731
|
+
options: CreatePreparedProgramOptions,
|
|
3732
|
+
): PreparedProgram {
|
|
3733
|
+
const preparedHost = createPreparedCompilerHost(
|
|
3734
|
+
options.baseHost,
|
|
3735
|
+
options.fileOverrides ?? new Map(),
|
|
3736
|
+
options.projectedDeclarationOverrides ?? new Map(),
|
|
3737
|
+
options.reusableCompilerHostState,
|
|
3738
|
+
options.options,
|
|
3739
|
+
options.importedMacroSiteKindsBySpecifier ?? new Map(),
|
|
3740
|
+
options.expansionEnabled ?? true,
|
|
3741
|
+
options.alwaysAvailableMacroSiteKinds ?? new Map(),
|
|
3742
|
+
options.preserveMacroAuthoring ?? false,
|
|
3743
|
+
options.invalidateModuleResolutions ?? true,
|
|
3744
|
+
);
|
|
3745
|
+
const program = ts.createProgram({
|
|
3746
|
+
oldProgram: options.oldProgram,
|
|
3747
|
+
host: preparedHost.host,
|
|
3748
|
+
rootNames: options.rootNames.map(toProgramFileName),
|
|
3749
|
+
options: options.options,
|
|
3750
|
+
projectReferences: options.projectReferences,
|
|
3751
|
+
configFileParsingDiagnostics: options.configFileParsingDiagnostics,
|
|
3752
|
+
});
|
|
3753
|
+
|
|
3754
|
+
return {
|
|
3755
|
+
dispose(clearReuseState = false): void {
|
|
3756
|
+
preparedHost.dispose();
|
|
3757
|
+
if (clearReuseState) {
|
|
3758
|
+
clearPreparedCompilerHostReuseState(preparedHost.reuseState);
|
|
3759
|
+
}
|
|
3760
|
+
},
|
|
3761
|
+
frontendDiagnostics(): readonly MergedDiagnostic[] {
|
|
3762
|
+
return preparedHost.frontendDiagnostics();
|
|
3763
|
+
},
|
|
3764
|
+
options: options.options,
|
|
3765
|
+
placeholderIndex(): MacroPlaceholderIndex {
|
|
3766
|
+
return preparedHost.getMacroPlaceholderIndex();
|
|
3767
|
+
},
|
|
3768
|
+
preparedHost,
|
|
3769
|
+
program,
|
|
3770
|
+
runtime: options.runtime ?? DEFAULT_PREPARED_PROGRAM_RUNTIME,
|
|
3771
|
+
rootNames: [...options.rootNames],
|
|
3772
|
+
toProgramFileName,
|
|
3773
|
+
toProjectedDeclarationFileName,
|
|
3774
|
+
toSourceFileName,
|
|
3775
|
+
};
|
|
3776
|
+
}
|