@soundscript/soundscript 0.1.12 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,2072 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { createAnnotationLookup } from '../annotation_syntax.js';
|
|
3
|
+
import { fromFileUrl } from '../platform/path.js';
|
|
4
|
+
import { macroSignature } from './macro_api.js';
|
|
5
|
+
import { attachMacroFactoryMetadata } from './macro_api_internal.js';
|
|
6
|
+
import { getHostDeclaration } from './macro_syntax_internal.js';
|
|
7
|
+
const DERIVE_MACRO_FILE_NAME = fromFileUrl(import.meta.url);
|
|
8
|
+
const DERIVE_SIGNATURE = macroSignature.oneOf(macroSignature.case('class', macroSignature.classDecl('target')), macroSignature.case('interface', macroSignature.interfaceDecl('target')), macroSignature.case('typeAlias', macroSignature.typeAliasDecl('target')));
|
|
9
|
+
const DECODE_SIGNATURE = macroSignature.oneOf(macroSignature.case('class', macroSignature.classDecl('target')), macroSignature.case('interface', macroSignature.interfaceDecl('target')), macroSignature.case('typeAlias', macroSignature.typeAliasDecl('target')));
|
|
10
|
+
const TAGGED_SIGNATURE = macroSignature.of(macroSignature.typeAliasDecl('target'));
|
|
11
|
+
const CLASS_DECODE_VALUE_PLACEHOLDER = '__sts_decoded_value__';
|
|
12
|
+
function attachDeriveFactory(factory) {
|
|
13
|
+
return attachMacroFactoryMetadata(factory, {
|
|
14
|
+
form: 'decl',
|
|
15
|
+
moduleFileName: DERIVE_MACRO_FILE_NAME,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function findAnnotation(annotations, name) {
|
|
19
|
+
return annotations.find((annotation) => annotation.name === name) ?? null;
|
|
20
|
+
}
|
|
21
|
+
function annotationIdentifierArgument(annotation) {
|
|
22
|
+
const [firstArgument] = annotation.arguments ?? [];
|
|
23
|
+
return firstArgument?.value.kind === 'identifier' ? firstArgument.value.name : null;
|
|
24
|
+
}
|
|
25
|
+
function annotationStringArgument(annotation) {
|
|
26
|
+
const [firstArgument] = annotation.arguments ?? [];
|
|
27
|
+
return firstArgument?.value.kind === 'string' ? firstArgument.value.value : null;
|
|
28
|
+
}
|
|
29
|
+
function annotationDiagnosticNode(declaration, annotation) {
|
|
30
|
+
const hostDeclaration = getHostDeclaration(declaration);
|
|
31
|
+
const sourceFile = hostDeclaration.getSourceFile();
|
|
32
|
+
const parsedAnnotation = createAnnotationLookup(sourceFile)
|
|
33
|
+
.getAttachedAnnotations(hostDeclaration)
|
|
34
|
+
.find((attached) => attached.name === annotation.name && attached.text === annotation.text);
|
|
35
|
+
if (!parsedAnnotation?.range) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
kind: 'annotation',
|
|
40
|
+
span: {
|
|
41
|
+
fileName: sourceFile.fileName,
|
|
42
|
+
start: parsedAnnotation.range.start,
|
|
43
|
+
end: parsedAnnotation.range.end,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function propertyAccessText(receiverName, propertyName) {
|
|
48
|
+
const receiver = /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(receiverName.trim())
|
|
49
|
+
? receiverName.trim()
|
|
50
|
+
: `(${receiverName.trim()})`;
|
|
51
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(propertyName)
|
|
52
|
+
? `${receiver}.${propertyName}`
|
|
53
|
+
: `${receiver}[${JSON.stringify(propertyName)}]`;
|
|
54
|
+
}
|
|
55
|
+
function propertyKeyText(propertyName) {
|
|
56
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(propertyName)
|
|
57
|
+
? propertyName
|
|
58
|
+
: JSON.stringify(propertyName);
|
|
59
|
+
}
|
|
60
|
+
function primitiveKindForTypeText(typeText) {
|
|
61
|
+
const normalized = typeText.trim();
|
|
62
|
+
switch (normalized) {
|
|
63
|
+
case 'string':
|
|
64
|
+
case 'number':
|
|
65
|
+
case 'boolean':
|
|
66
|
+
case 'bigint':
|
|
67
|
+
return normalized;
|
|
68
|
+
default:
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function parseGenericTypeArgument(text, typeName) {
|
|
73
|
+
const parsed = parseGenericTypeArguments(text, typeName);
|
|
74
|
+
return parsed?.length === 1 ? parsed[0] : null;
|
|
75
|
+
}
|
|
76
|
+
function parseGenericTypeArguments(text, typeName) {
|
|
77
|
+
const trimmed = text.trim();
|
|
78
|
+
const prefix = `${typeName}<`;
|
|
79
|
+
if (!trimmed.startsWith(prefix) || !trimmed.endsWith('>')) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
let angleDepth = 0;
|
|
83
|
+
let braceDepth = 0;
|
|
84
|
+
let bracketDepth = 0;
|
|
85
|
+
let parenDepth = 0;
|
|
86
|
+
let inString = null;
|
|
87
|
+
let escaped = false;
|
|
88
|
+
let segmentStart = prefix.length;
|
|
89
|
+
const args = [];
|
|
90
|
+
for (let index = typeName.length; index < trimmed.length; index += 1) {
|
|
91
|
+
const char = trimmed[index];
|
|
92
|
+
if (inString !== null) {
|
|
93
|
+
if (escaped) {
|
|
94
|
+
escaped = false;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (char === '\\') {
|
|
98
|
+
escaped = true;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (char === inString) {
|
|
102
|
+
inString = null;
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (char === '"' || char === "'" || char === '`') {
|
|
107
|
+
inString = char;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
switch (char) {
|
|
111
|
+
case '<':
|
|
112
|
+
angleDepth += 1;
|
|
113
|
+
continue;
|
|
114
|
+
case '>':
|
|
115
|
+
angleDepth -= 1;
|
|
116
|
+
if (angleDepth < 0) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
if (angleDepth === 0) {
|
|
120
|
+
if (index !== trimmed.length - 1) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const segment = trimmed.slice(segmentStart, index).trim();
|
|
124
|
+
if (segment.length === 0) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
args.push(segment);
|
|
128
|
+
}
|
|
129
|
+
continue;
|
|
130
|
+
case '{':
|
|
131
|
+
braceDepth += 1;
|
|
132
|
+
continue;
|
|
133
|
+
case '}':
|
|
134
|
+
braceDepth -= 1;
|
|
135
|
+
if (braceDepth < 0) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
continue;
|
|
139
|
+
case '[':
|
|
140
|
+
bracketDepth += 1;
|
|
141
|
+
continue;
|
|
142
|
+
case ']':
|
|
143
|
+
bracketDepth -= 1;
|
|
144
|
+
if (bracketDepth < 0) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
continue;
|
|
148
|
+
case '(':
|
|
149
|
+
parenDepth += 1;
|
|
150
|
+
continue;
|
|
151
|
+
case ')':
|
|
152
|
+
parenDepth -= 1;
|
|
153
|
+
if (parenDepth < 0) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
continue;
|
|
157
|
+
case ',':
|
|
158
|
+
if (angleDepth === 1 && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
|
|
159
|
+
const segment = trimmed.slice(segmentStart, index).trim();
|
|
160
|
+
if (segment.length === 0) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
args.push(segment);
|
|
164
|
+
segmentStart = index + 1;
|
|
165
|
+
}
|
|
166
|
+
continue;
|
|
167
|
+
default:
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (angleDepth !== 0 || braceDepth !== 0 || bracketDepth !== 0 || parenDepth !== 0 ||
|
|
172
|
+
inString !== null) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
return args;
|
|
176
|
+
}
|
|
177
|
+
function parseArrayInnerTypeText(typeText) {
|
|
178
|
+
const trimmed = typeText.trim();
|
|
179
|
+
if (trimmed.startsWith('readonly ') && trimmed.endsWith('[]')) {
|
|
180
|
+
return trimmed.slice('readonly '.length, -2).trim();
|
|
181
|
+
}
|
|
182
|
+
if (trimmed.endsWith('[]')) {
|
|
183
|
+
return trimmed.slice(0, -2).trim();
|
|
184
|
+
}
|
|
185
|
+
return parseGenericTypeArgument(trimmed, 'Array') ??
|
|
186
|
+
parseGenericTypeArgument(trimmed, 'ReadonlyArray');
|
|
187
|
+
}
|
|
188
|
+
function parseTupleElementTypeTexts(typeText) {
|
|
189
|
+
const trimmed = typeText.trim();
|
|
190
|
+
const tupleText = trimmed.startsWith('readonly [') ? trimmed.slice('readonly '.length) : trimmed;
|
|
191
|
+
if (!tupleText.startsWith('[') || !tupleText.endsWith(']')) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const body = tupleText.slice(1, -1).trim();
|
|
195
|
+
if (body.length === 0) {
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
let angleDepth = 0;
|
|
199
|
+
let braceDepth = 0;
|
|
200
|
+
let bracketDepth = 0;
|
|
201
|
+
let parenDepth = 0;
|
|
202
|
+
let inString = null;
|
|
203
|
+
let escaped = false;
|
|
204
|
+
let segmentStart = 0;
|
|
205
|
+
const elements = [];
|
|
206
|
+
for (let index = 0; index < body.length; index += 1) {
|
|
207
|
+
const char = body[index];
|
|
208
|
+
if (inString !== null) {
|
|
209
|
+
if (escaped) {
|
|
210
|
+
escaped = false;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (char === '\\') {
|
|
214
|
+
escaped = true;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (char === inString) {
|
|
218
|
+
inString = null;
|
|
219
|
+
}
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (char === '"' || char === "'" || char === '`') {
|
|
223
|
+
inString = char;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
switch (char) {
|
|
227
|
+
case '<':
|
|
228
|
+
angleDepth += 1;
|
|
229
|
+
continue;
|
|
230
|
+
case '>':
|
|
231
|
+
angleDepth -= 1;
|
|
232
|
+
if (angleDepth < 0) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
continue;
|
|
236
|
+
case '{':
|
|
237
|
+
braceDepth += 1;
|
|
238
|
+
continue;
|
|
239
|
+
case '}':
|
|
240
|
+
braceDepth -= 1;
|
|
241
|
+
if (braceDepth < 0) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
continue;
|
|
245
|
+
case '[':
|
|
246
|
+
bracketDepth += 1;
|
|
247
|
+
continue;
|
|
248
|
+
case ']':
|
|
249
|
+
bracketDepth -= 1;
|
|
250
|
+
if (bracketDepth < 0) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
continue;
|
|
254
|
+
case '(':
|
|
255
|
+
parenDepth += 1;
|
|
256
|
+
continue;
|
|
257
|
+
case ')':
|
|
258
|
+
parenDepth -= 1;
|
|
259
|
+
if (parenDepth < 0) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
continue;
|
|
263
|
+
case ',':
|
|
264
|
+
if (angleDepth === 0 && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
|
|
265
|
+
const segment = body.slice(segmentStart, index).trim();
|
|
266
|
+
if (segment.length === 0) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
elements.push(segment);
|
|
270
|
+
segmentStart = index + 1;
|
|
271
|
+
}
|
|
272
|
+
continue;
|
|
273
|
+
default:
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (angleDepth !== 0 || braceDepth !== 0 || bracketDepth !== 0 || parenDepth !== 0 ||
|
|
278
|
+
inString !== null) {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
const finalSegment = body.slice(segmentStart).trim();
|
|
282
|
+
if (finalSegment.length === 0) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
elements.push(finalSegment);
|
|
286
|
+
return elements;
|
|
287
|
+
}
|
|
288
|
+
function parseSupportedDerivedType(typeText) {
|
|
289
|
+
const primitiveKind = primitiveKindForTypeText(typeText);
|
|
290
|
+
if (primitiveKind) {
|
|
291
|
+
return { kind: 'primitive', primitiveKind };
|
|
292
|
+
}
|
|
293
|
+
const arrayInner = parseArrayInnerTypeText(typeText);
|
|
294
|
+
if (arrayInner !== null) {
|
|
295
|
+
const element = parseSupportedDerivedType(arrayInner);
|
|
296
|
+
return element ? { kind: 'array', element } : null;
|
|
297
|
+
}
|
|
298
|
+
const tupleElements = parseTupleElementTypeTexts(typeText);
|
|
299
|
+
if (tupleElements !== null) {
|
|
300
|
+
const elements = tupleElements.map((element) => parseSupportedDerivedType(element));
|
|
301
|
+
return elements.every((element) => element !== null)
|
|
302
|
+
? { kind: 'tuple', elements: elements }
|
|
303
|
+
: null;
|
|
304
|
+
}
|
|
305
|
+
const optionInner = parseGenericTypeArgument(typeText, 'Option');
|
|
306
|
+
if (optionInner !== null) {
|
|
307
|
+
const value = parseSupportedDerivedType(optionInner);
|
|
308
|
+
return value ? { kind: 'option', value } : null;
|
|
309
|
+
}
|
|
310
|
+
const resultArgs = parseGenericTypeArguments(typeText, 'Result');
|
|
311
|
+
if (resultArgs?.length === 2) {
|
|
312
|
+
const okType = parseSupportedDerivedType(resultArgs[0]);
|
|
313
|
+
const errType = parseSupportedDerivedType(resultArgs[1]);
|
|
314
|
+
return okType && errType ? { kind: 'result', ok: okType, err: errType } : null;
|
|
315
|
+
}
|
|
316
|
+
const trimmed = typeText.trim();
|
|
317
|
+
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(trimmed)) {
|
|
318
|
+
return { kind: 'named', typeName: trimmed };
|
|
319
|
+
}
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
function supportedDerivedTypeFromShape(shape) {
|
|
323
|
+
switch (shape.kind) {
|
|
324
|
+
case 'primitive':
|
|
325
|
+
return {
|
|
326
|
+
kind: 'primitive',
|
|
327
|
+
primitiveKind: shape.primitiveKind,
|
|
328
|
+
};
|
|
329
|
+
case 'named':
|
|
330
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(shape.name)
|
|
331
|
+
? { kind: 'named', typeName: shape.name }
|
|
332
|
+
: null;
|
|
333
|
+
case 'array': {
|
|
334
|
+
const element = supportedDerivedTypeFromShape(shape.element);
|
|
335
|
+
return element ? { kind: 'array', element } : null;
|
|
336
|
+
}
|
|
337
|
+
case 'tuple': {
|
|
338
|
+
const elements = shape.elements.map((element) => supportedDerivedTypeFromShape(element));
|
|
339
|
+
return elements.every((element) => element !== null)
|
|
340
|
+
? { kind: 'tuple', elements: elements }
|
|
341
|
+
: null;
|
|
342
|
+
}
|
|
343
|
+
case 'option': {
|
|
344
|
+
const value = supportedDerivedTypeFromShape(shape.value);
|
|
345
|
+
return value ? { kind: 'option', value } : null;
|
|
346
|
+
}
|
|
347
|
+
case 'result': {
|
|
348
|
+
const ok = supportedDerivedTypeFromShape(shape.ok);
|
|
349
|
+
const err = supportedDerivedTypeFromShape(shape.err);
|
|
350
|
+
return ok && err ? { kind: 'result', ok, err } : null;
|
|
351
|
+
}
|
|
352
|
+
case 'object':
|
|
353
|
+
case 'literal':
|
|
354
|
+
case 'union':
|
|
355
|
+
case 'unsupported':
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function objectLikeDeclarationShape(ctx, declaration, macroName) {
|
|
360
|
+
const shape = ctx.reflect.declarationShape(declaration);
|
|
361
|
+
if (shape.kind === 'objectLike') {
|
|
362
|
+
return shape;
|
|
363
|
+
}
|
|
364
|
+
ctx.error(`${macroName} currently only supports object-like declarations in v1.`, declaration);
|
|
365
|
+
}
|
|
366
|
+
function discriminatedUnionDeclarationShape(ctx, declaration, macroName) {
|
|
367
|
+
const shape = ctx.reflect.declarationShape(declaration);
|
|
368
|
+
if (shape.kind === 'discriminatedUnion') {
|
|
369
|
+
return shape;
|
|
370
|
+
}
|
|
371
|
+
ctx.error(`${macroName} only supports // #[tagged] unions of object-like variants in v1.`, declaration);
|
|
372
|
+
}
|
|
373
|
+
function eqHashUnsupportedFieldMessage(macroName) {
|
|
374
|
+
return `${macroName} only supports fields with explicit primitive, nested object literal, tuple, array, Option/Result, or named derived types in v1. Add // #[${macroName}.via(...)] or // #[${macroName}.skip].`;
|
|
375
|
+
}
|
|
376
|
+
function decodeLikeUnsupportedFieldMessage(macroName) {
|
|
377
|
+
switch (macroName) {
|
|
378
|
+
case 'decode':
|
|
379
|
+
return 'decode only supports fields with explicit primitive, nested object literal, tuple, array, Option/Result, or named derived types in v1. Add // #[decode.via(...)] to supply a custom decoder.';
|
|
380
|
+
case 'encode':
|
|
381
|
+
return 'encode only supports fields with explicit primitive, nested object literal, tuple, array, Option/Result, or named derived types in v1. Add // #[encode.via(...)] to supply a custom encoder.';
|
|
382
|
+
case 'codec':
|
|
383
|
+
return 'codec only supports fields with explicit primitive, nested object literal, tuple, array, Option/Result, or named derived types in v1. Add // #[codec.via(...)] to supply a custom codec.';
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function compareHelperName(kind) {
|
|
387
|
+
switch (kind) {
|
|
388
|
+
case 'string':
|
|
389
|
+
return 'stringEq';
|
|
390
|
+
case 'number':
|
|
391
|
+
return 'numberEq';
|
|
392
|
+
case 'boolean':
|
|
393
|
+
return 'booleanEq';
|
|
394
|
+
case 'bigint':
|
|
395
|
+
return 'bigintEq';
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function hashHelperName(kind) {
|
|
399
|
+
switch (kind) {
|
|
400
|
+
case 'string':
|
|
401
|
+
return 'stringHash';
|
|
402
|
+
case 'number':
|
|
403
|
+
return 'numberHash';
|
|
404
|
+
case 'boolean':
|
|
405
|
+
return 'booleanHash';
|
|
406
|
+
case 'bigint':
|
|
407
|
+
return 'bigintHash';
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function decodeHelperName(kind) {
|
|
411
|
+
switch (kind) {
|
|
412
|
+
case 'string':
|
|
413
|
+
return 'string';
|
|
414
|
+
case 'number':
|
|
415
|
+
return 'number';
|
|
416
|
+
case 'boolean':
|
|
417
|
+
return 'boolean';
|
|
418
|
+
case 'bigint':
|
|
419
|
+
return 'bigint';
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function encodeHelperName(kind) {
|
|
423
|
+
switch (kind) {
|
|
424
|
+
case 'string':
|
|
425
|
+
return 'stringEncoder';
|
|
426
|
+
case 'number':
|
|
427
|
+
return 'numberEncoder';
|
|
428
|
+
case 'boolean':
|
|
429
|
+
return 'booleanEncoder';
|
|
430
|
+
case 'bigint':
|
|
431
|
+
return 'bigintEncoder';
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function eqHelperTextForType(ctx, type) {
|
|
435
|
+
switch (type.kind) {
|
|
436
|
+
case 'primitive':
|
|
437
|
+
return ctx.runtime.named('sts:compare', compareHelperName(type.primitiveKind)).text();
|
|
438
|
+
case 'named':
|
|
439
|
+
return `${ctx.runtime.named('sts:compare', 'lazyEq').text()}(() => ${type.typeName}Eq)`;
|
|
440
|
+
case 'array':
|
|
441
|
+
return `${ctx.runtime.named('sts:compare', 'arrayEq').text()}(${eqHelperTextForType(ctx, type.element)})`;
|
|
442
|
+
case 'tuple':
|
|
443
|
+
return `${ctx.runtime.named('sts:compare', 'tupleEq').text()}(${type.elements.map((element) => eqHelperTextForType(ctx, element)).join(', ')})`;
|
|
444
|
+
case 'option':
|
|
445
|
+
return `${ctx.runtime.named('sts:compare', 'optionEq').text()}(${eqHelperTextForType(ctx, type.value)})`;
|
|
446
|
+
case 'result':
|
|
447
|
+
return `${ctx.runtime.named('sts:compare', 'resultEq').text()}(${eqHelperTextForType(ctx, type.ok)}, ${eqHelperTextForType(ctx, type.err)})`;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function hashHelperTextForType(ctx, type) {
|
|
451
|
+
switch (type.kind) {
|
|
452
|
+
case 'primitive':
|
|
453
|
+
return ctx.runtime.named('sts:hash', hashHelperName(type.primitiveKind)).text();
|
|
454
|
+
case 'named':
|
|
455
|
+
return `${ctx.runtime.named('sts:hash', 'lazyHashEq').text()}(() => ${type.typeName}Hash)`;
|
|
456
|
+
case 'array':
|
|
457
|
+
return `${ctx.runtime.named('sts:hash', 'arrayHash').text()}(${hashHelperTextForType(ctx, type.element)})`;
|
|
458
|
+
case 'tuple':
|
|
459
|
+
return `${ctx.runtime.named('sts:hash', 'tupleHash').text()}(${type.elements.map((element) => hashHelperTextForType(ctx, element)).join(', ')})`;
|
|
460
|
+
case 'option':
|
|
461
|
+
return `${ctx.runtime.named('sts:hash', 'optionHash').text()}(${hashHelperTextForType(ctx, type.value)})`;
|
|
462
|
+
case 'result':
|
|
463
|
+
return `${ctx.runtime.named('sts:hash', 'resultHash').text()}(${hashHelperTextForType(ctx, type.ok)}, ${hashHelperTextForType(ctx, type.err)})`;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function decodeHelperTextForType(ctx, type) {
|
|
467
|
+
switch (type.kind) {
|
|
468
|
+
case 'primitive':
|
|
469
|
+
return ctx.runtime.named('sts:decode', decodeHelperName(type.primitiveKind)).text();
|
|
470
|
+
case 'named':
|
|
471
|
+
return `${ctx.runtime.named('sts:decode', 'lazy').text()}(() => ${type.typeName}Decoder)`;
|
|
472
|
+
case 'array':
|
|
473
|
+
return `${ctx.runtime.named('sts:decode', 'array').text()}(${decodeHelperTextForType(ctx, type.element)})`;
|
|
474
|
+
case 'tuple':
|
|
475
|
+
return `${ctx.runtime.named('sts:decode', 'tuple').text()}(${type.elements.map((element) => decodeHelperTextForType(ctx, element)).join(', ')})`;
|
|
476
|
+
case 'option':
|
|
477
|
+
return `${ctx.runtime.named('sts:decode', 'option').text()}(${decodeHelperTextForType(ctx, type.value)})`;
|
|
478
|
+
case 'result':
|
|
479
|
+
return `${ctx.runtime.named('sts:decode', 'result').text()}(${decodeHelperTextForType(ctx, type.ok)}, ${decodeHelperTextForType(ctx, type.err)})`;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function encodeHelperTextForType(ctx, type) {
|
|
483
|
+
switch (type.kind) {
|
|
484
|
+
case 'primitive':
|
|
485
|
+
return ctx.runtime.named('sts:encode', encodeHelperName(type.primitiveKind)).text();
|
|
486
|
+
case 'named':
|
|
487
|
+
return `${ctx.runtime.named('sts:encode', 'lazy').text()}(() => ${type.typeName}Encoder)`;
|
|
488
|
+
case 'array':
|
|
489
|
+
return `${ctx.runtime.named('sts:encode', 'array').text()}(${encodeHelperTextForType(ctx, type.element)})`;
|
|
490
|
+
case 'tuple':
|
|
491
|
+
return `${ctx.runtime.named('sts:encode', 'tuple').text()}(${type.elements.map((element) => encodeHelperTextForType(ctx, element)).join(', ')})`;
|
|
492
|
+
case 'option':
|
|
493
|
+
return `${ctx.runtime.named('sts:encode', 'option').text()}(${encodeHelperTextForType(ctx, type.value)})`;
|
|
494
|
+
case 'result':
|
|
495
|
+
return `${ctx.runtime.named('sts:encode', 'result').text()}(${encodeHelperTextForType(ctx, type.ok)}, ${encodeHelperTextForType(ctx, type.err)})`;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function codecHelperTextsForType(ctx, type) {
|
|
499
|
+
switch (type.kind) {
|
|
500
|
+
case 'primitive':
|
|
501
|
+
return {
|
|
502
|
+
decodeText: ctx.runtime.named('sts:decode', decodeHelperName(type.primitiveKind)).text(),
|
|
503
|
+
encodeText: ctx.runtime.named('sts:encode', encodeHelperName(type.primitiveKind)).text(),
|
|
504
|
+
};
|
|
505
|
+
case 'named':
|
|
506
|
+
return {
|
|
507
|
+
decodeText: `${ctx.runtime.named('sts:decode', 'lazy').text()}(() => ${type.typeName}Codec)`,
|
|
508
|
+
encodeText: `${ctx.runtime.named('sts:encode', 'lazy').text()}(() => ${type.typeName}Codec)`,
|
|
509
|
+
};
|
|
510
|
+
case 'array': {
|
|
511
|
+
const element = codecHelperTextsForType(ctx, type.element);
|
|
512
|
+
return {
|
|
513
|
+
decodeText: `${ctx.runtime.named('sts:decode', 'array').text()}(${element.decodeText})`,
|
|
514
|
+
encodeText: `${ctx.runtime.named('sts:encode', 'array').text()}(${element.encodeText})`,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
case 'tuple':
|
|
518
|
+
return {
|
|
519
|
+
decodeText: `${ctx.runtime.named('sts:decode', 'tuple').text()}(${type.elements.map((element) => codecHelperTextsForType(ctx, element).decodeText).join(', ')})`,
|
|
520
|
+
encodeText: `${ctx.runtime.named('sts:encode', 'tuple').text()}(${type.elements.map((element) => codecHelperTextsForType(ctx, element).encodeText).join(', ')})`,
|
|
521
|
+
};
|
|
522
|
+
case 'option': {
|
|
523
|
+
const value = codecHelperTextsForType(ctx, type.value);
|
|
524
|
+
return {
|
|
525
|
+
decodeText: `${ctx.runtime.named('sts:decode', 'option').text()}(${value.decodeText})`,
|
|
526
|
+
encodeText: `${ctx.runtime.named('sts:encode', 'option').text()}(${value.encodeText})`,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
case 'result': {
|
|
530
|
+
const okType = codecHelperTextsForType(ctx, type.ok);
|
|
531
|
+
const errType = codecHelperTextsForType(ctx, type.err);
|
|
532
|
+
return {
|
|
533
|
+
decodeText: `${ctx.runtime.named('sts:decode', 'result').text()}(${okType.decodeText}, ${errType.decodeText})`,
|
|
534
|
+
encodeText: `${ctx.runtime.named('sts:encode', 'result').text()}(${okType.encodeText}, ${errType.encodeText})`,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function expectedCompanionName(typeName, macroName) {
|
|
540
|
+
switch (macroName) {
|
|
541
|
+
case 'eq':
|
|
542
|
+
return `${typeName}Eq`;
|
|
543
|
+
case 'hash':
|
|
544
|
+
return `${typeName}Hash`;
|
|
545
|
+
case 'decode':
|
|
546
|
+
return `${typeName}Decoder`;
|
|
547
|
+
case 'encode':
|
|
548
|
+
return `${typeName}Encoder`;
|
|
549
|
+
case 'codec':
|
|
550
|
+
return `${typeName}Codec`;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
function missingNamedDerivedCompanionMessage(macroName, companionName) {
|
|
554
|
+
switch (macroName) {
|
|
555
|
+
case 'eq':
|
|
556
|
+
case 'hash':
|
|
557
|
+
return `${macroName} requires the companion value "${companionName}" to be in scope for named derived types. Add an import or use // #[${macroName}.via(...)] or // #[${macroName}.skip].`;
|
|
558
|
+
case 'decode':
|
|
559
|
+
return `decode requires the companion value "${companionName}" to be in scope for named derived types. Add an import or use // #[decode.via(...)] to supply a custom decoder.`;
|
|
560
|
+
case 'encode':
|
|
561
|
+
return `encode requires the companion value "${companionName}" to be in scope for named derived types. Add an import or use // #[encode.via(...)] to supply a custom encoder.`;
|
|
562
|
+
case 'codec':
|
|
563
|
+
return `codec requires the companion value "${companionName}" to be in scope for named derived types. Add an import or use // #[codec.via(...)] to supply a custom codec.`;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, errorNode, type) {
|
|
567
|
+
switch (type.kind) {
|
|
568
|
+
case 'primitive':
|
|
569
|
+
return;
|
|
570
|
+
case 'array':
|
|
571
|
+
assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, errorNode, type.element);
|
|
572
|
+
return;
|
|
573
|
+
case 'tuple':
|
|
574
|
+
for (const element of type.elements) {
|
|
575
|
+
assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, errorNode, element);
|
|
576
|
+
}
|
|
577
|
+
return;
|
|
578
|
+
case 'option':
|
|
579
|
+
assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, errorNode, type.value);
|
|
580
|
+
return;
|
|
581
|
+
case 'result':
|
|
582
|
+
assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, errorNode, type.ok);
|
|
583
|
+
assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, errorNode, type.err);
|
|
584
|
+
return;
|
|
585
|
+
case 'named': {
|
|
586
|
+
if (type.typeName === ownerTypeName) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
const companionName = expectedCompanionName(type.typeName, macroName);
|
|
590
|
+
if (!ctx.semantics.valueBindingInScope(companionName) &&
|
|
591
|
+
!ctx.semantics.localDeclarationHasAnnotation(type.typeName, macroName)) {
|
|
592
|
+
ctx.error(missingNamedDerivedCompanionMessage(macroName, companionName), errorNode);
|
|
593
|
+
}
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
function eqHashFieldFromReflectedShape(ctx, field, macroName, ownerTypeName, scopeNode) {
|
|
599
|
+
const annotations = field.annotations;
|
|
600
|
+
if (findAnnotation(annotations, `${macroName}.skip`)) {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
const viaAnnotation = findAnnotation(annotations, `${macroName}.via`);
|
|
604
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
605
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
606
|
+
ctx.error(`${macroName}.via(...) requires a helper identifier.`, field.node);
|
|
607
|
+
}
|
|
608
|
+
if (viaIdentifier) {
|
|
609
|
+
return {
|
|
610
|
+
eqHelper: viaIdentifier,
|
|
611
|
+
hashHelper: viaIdentifier,
|
|
612
|
+
name: field.name,
|
|
613
|
+
optional: field.optional,
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
if (!field.type) {
|
|
617
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), field.node);
|
|
618
|
+
}
|
|
619
|
+
if (field.type.kind === 'object') {
|
|
620
|
+
return {
|
|
621
|
+
eqHelper: macroName === 'eq'
|
|
622
|
+
? nestedEqHelperTextFromFields(ctx, ownerTypeName, scopeNode, field.type.fields)
|
|
623
|
+
: nestedHashHelperTextFromFields(ctx, ownerTypeName, scopeNode, field.type.fields),
|
|
624
|
+
hashHelper: nestedHashHelperTextFromFields(ctx, ownerTypeName, scopeNode, field.type.fields),
|
|
625
|
+
name: field.name,
|
|
626
|
+
optional: field.optional,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
const fieldType = supportedDerivedTypeFromShape(field.type);
|
|
630
|
+
if (!fieldType) {
|
|
631
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), field.node);
|
|
632
|
+
}
|
|
633
|
+
assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, field.node, fieldType);
|
|
634
|
+
return {
|
|
635
|
+
eqHelper: macroName === 'eq'
|
|
636
|
+
? eqHelperTextForType(ctx, fieldType)
|
|
637
|
+
: hashHelperTextForType(ctx, fieldType),
|
|
638
|
+
hashHelper: hashHelperTextForType(ctx, fieldType),
|
|
639
|
+
name: field.name,
|
|
640
|
+
optional: field.optional,
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
function decodeFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode) {
|
|
644
|
+
const viaAnnotation = findAnnotation(field.annotations, 'decode.via');
|
|
645
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
646
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
647
|
+
ctx.error('decode.via(...) requires a helper identifier.', field.node);
|
|
648
|
+
}
|
|
649
|
+
const renameAnnotation = findAnnotation(field.annotations, 'decode.rename');
|
|
650
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
651
|
+
if (renameAnnotation && !renamedWireName) {
|
|
652
|
+
ctx.error('decode.rename(...) requires a string field name.', field.node);
|
|
653
|
+
}
|
|
654
|
+
if (!field.type) {
|
|
655
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), field.node);
|
|
656
|
+
}
|
|
657
|
+
const decoderText = (() => {
|
|
658
|
+
if (viaIdentifier) {
|
|
659
|
+
return viaIdentifier;
|
|
660
|
+
}
|
|
661
|
+
if (field.type.kind === 'object') {
|
|
662
|
+
return nestedDecodeHelperTextFromFields(ctx, ownerTypeName, scopeNode, field.type.fields);
|
|
663
|
+
}
|
|
664
|
+
const fieldType = supportedDerivedTypeFromShape(field.type);
|
|
665
|
+
if (!fieldType) {
|
|
666
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), field.node);
|
|
667
|
+
}
|
|
668
|
+
assertNamedDerivedCompanionsInScope(ctx, 'decode', ownerTypeName, scopeNode, field.node, fieldType);
|
|
669
|
+
return decodeHelperTextForType(ctx, fieldType);
|
|
670
|
+
})();
|
|
671
|
+
return {
|
|
672
|
+
decoderText,
|
|
673
|
+
localName: field.name,
|
|
674
|
+
optional: field.optional,
|
|
675
|
+
wireName: renamedWireName ?? field.name,
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
function encodeFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode) {
|
|
679
|
+
const viaAnnotation = findAnnotation(field.annotations, 'encode.via');
|
|
680
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
681
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
682
|
+
ctx.error('encode.via(...) requires a helper identifier.', field.node);
|
|
683
|
+
}
|
|
684
|
+
const renameAnnotation = findAnnotation(field.annotations, 'encode.rename');
|
|
685
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
686
|
+
if (renameAnnotation && !renamedWireName) {
|
|
687
|
+
ctx.error('encode.rename(...) requires a string field name.', field.node);
|
|
688
|
+
}
|
|
689
|
+
if (!field.type) {
|
|
690
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), field.node);
|
|
691
|
+
}
|
|
692
|
+
const encoderText = (() => {
|
|
693
|
+
if (viaIdentifier) {
|
|
694
|
+
return viaIdentifier;
|
|
695
|
+
}
|
|
696
|
+
if (field.type.kind === 'object') {
|
|
697
|
+
return nestedEncodeHelperTextFromFields(ctx, ownerTypeName, scopeNode, field.type.fields);
|
|
698
|
+
}
|
|
699
|
+
const fieldType = supportedDerivedTypeFromShape(field.type);
|
|
700
|
+
if (!fieldType) {
|
|
701
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), field.node);
|
|
702
|
+
}
|
|
703
|
+
assertNamedDerivedCompanionsInScope(ctx, 'encode', ownerTypeName, scopeNode, field.node, fieldType);
|
|
704
|
+
return encodeHelperTextForType(ctx, fieldType);
|
|
705
|
+
})();
|
|
706
|
+
return {
|
|
707
|
+
encoderText,
|
|
708
|
+
localName: field.name,
|
|
709
|
+
optional: field.optional,
|
|
710
|
+
wireName: renamedWireName ?? field.name,
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
function codecFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode) {
|
|
714
|
+
const viaAnnotation = findAnnotation(field.annotations, 'codec.via');
|
|
715
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
716
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
717
|
+
ctx.error('codec.via(...) requires a helper identifier.', field.node);
|
|
718
|
+
}
|
|
719
|
+
const renameAnnotation = findAnnotation(field.annotations, 'codec.rename');
|
|
720
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
721
|
+
if (renameAnnotation && !renamedWireName) {
|
|
722
|
+
ctx.error('codec.rename(...) requires a string field name.', field.node);
|
|
723
|
+
}
|
|
724
|
+
if (!field.type) {
|
|
725
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), field.node);
|
|
726
|
+
}
|
|
727
|
+
const helperTexts = (() => {
|
|
728
|
+
if (viaIdentifier) {
|
|
729
|
+
return { decodeText: viaIdentifier, encodeText: viaIdentifier };
|
|
730
|
+
}
|
|
731
|
+
if (field.type.kind === 'object') {
|
|
732
|
+
return nestedCodecHelperTextsFromFields(ctx, ownerTypeName, scopeNode, field.type.fields);
|
|
733
|
+
}
|
|
734
|
+
const fieldType = supportedDerivedTypeFromShape(field.type);
|
|
735
|
+
if (!fieldType) {
|
|
736
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), field.node);
|
|
737
|
+
}
|
|
738
|
+
assertNamedDerivedCompanionsInScope(ctx, 'codec', ownerTypeName, scopeNode, field.node, fieldType);
|
|
739
|
+
return codecHelperTextsForType(ctx, fieldType);
|
|
740
|
+
})();
|
|
741
|
+
return {
|
|
742
|
+
decodeText: helperTexts.decodeText,
|
|
743
|
+
encodeText: helperTexts.encodeText,
|
|
744
|
+
localName: field.name,
|
|
745
|
+
optional: field.optional,
|
|
746
|
+
wireName: renamedWireName ?? field.name,
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
function fieldFromObjectMember(ctx, member, macroName, ownerTypeName, scopeNode) {
|
|
750
|
+
if (member.memberKind !== 'property_signature' || member.name === null) {
|
|
751
|
+
ctx.error(`${macroName} only supports property-style object members in v1.`, member);
|
|
752
|
+
}
|
|
753
|
+
const annotations = ctx.syntax.annotations(member);
|
|
754
|
+
if (findAnnotation(annotations, `${macroName}.skip`)) {
|
|
755
|
+
return null;
|
|
756
|
+
}
|
|
757
|
+
const viaAnnotation = findAnnotation(annotations, `${macroName}.via`);
|
|
758
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
759
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
760
|
+
ctx.error(`${macroName}.via(...) requires a helper identifier.`, member);
|
|
761
|
+
}
|
|
762
|
+
const explicitType = member.explicitType();
|
|
763
|
+
if (!explicitType) {
|
|
764
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), member);
|
|
765
|
+
}
|
|
766
|
+
if (viaIdentifier) {
|
|
767
|
+
return {
|
|
768
|
+
eqHelper: viaIdentifier,
|
|
769
|
+
hashHelper: viaIdentifier,
|
|
770
|
+
name: member.name,
|
|
771
|
+
optional: member.isOptional(),
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
const objectType = explicitType.asObjectLiteral();
|
|
775
|
+
if (objectType) {
|
|
776
|
+
return {
|
|
777
|
+
eqHelper: macroName === 'eq'
|
|
778
|
+
? nestedEqHelperText(ctx, ownerTypeName, scopeNode, objectType)
|
|
779
|
+
: nestedHashHelperText(ctx, ownerTypeName, scopeNode, objectType),
|
|
780
|
+
hashHelper: nestedHashHelperText(ctx, ownerTypeName, scopeNode, objectType),
|
|
781
|
+
name: member.name,
|
|
782
|
+
optional: member.isOptional(),
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
786
|
+
if (!fieldType) {
|
|
787
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), member);
|
|
788
|
+
}
|
|
789
|
+
assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, member, fieldType);
|
|
790
|
+
return {
|
|
791
|
+
eqHelper: macroName === 'eq'
|
|
792
|
+
? eqHelperTextForType(ctx, fieldType)
|
|
793
|
+
: hashHelperTextForType(ctx, fieldType),
|
|
794
|
+
hashHelper: hashHelperTextForType(ctx, fieldType),
|
|
795
|
+
name: member.name,
|
|
796
|
+
optional: member.isOptional(),
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
function fieldFromClassMember(ctx, field, macroName, ownerTypeName, scopeNode) {
|
|
800
|
+
if (field.hasModifier('private') || field.hasModifier('protected') || field.hasModifier('static') ||
|
|
801
|
+
field.name === null) {
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
804
|
+
const annotations = ctx.syntax.annotations(field);
|
|
805
|
+
if (findAnnotation(annotations, `${macroName}.skip`)) {
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
const viaAnnotation = findAnnotation(annotations, `${macroName}.via`);
|
|
809
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
810
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
811
|
+
ctx.error(`${macroName}.via(...) requires a helper identifier.`, field);
|
|
812
|
+
}
|
|
813
|
+
const explicitType = field.explicitType();
|
|
814
|
+
if (!explicitType) {
|
|
815
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), field);
|
|
816
|
+
}
|
|
817
|
+
if (viaIdentifier) {
|
|
818
|
+
return {
|
|
819
|
+
eqHelper: viaIdentifier,
|
|
820
|
+
hashHelper: viaIdentifier,
|
|
821
|
+
name: field.name,
|
|
822
|
+
optional: field.isOptional(),
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
const objectType = explicitType.asObjectLiteral();
|
|
826
|
+
if (objectType) {
|
|
827
|
+
return {
|
|
828
|
+
eqHelper: macroName === 'eq'
|
|
829
|
+
? nestedEqHelperText(ctx, ownerTypeName, scopeNode, objectType)
|
|
830
|
+
: nestedHashHelperText(ctx, ownerTypeName, scopeNode, objectType),
|
|
831
|
+
hashHelper: nestedHashHelperText(ctx, ownerTypeName, scopeNode, objectType),
|
|
832
|
+
name: field.name,
|
|
833
|
+
optional: field.isOptional(),
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
837
|
+
if (!fieldType) {
|
|
838
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), field);
|
|
839
|
+
}
|
|
840
|
+
assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, field, fieldType);
|
|
841
|
+
return {
|
|
842
|
+
eqHelper: macroName === 'eq'
|
|
843
|
+
? eqHelperTextForType(ctx, fieldType)
|
|
844
|
+
: hashHelperTextForType(ctx, fieldType),
|
|
845
|
+
hashHelper: hashHelperTextForType(ctx, fieldType),
|
|
846
|
+
name: field.name,
|
|
847
|
+
optional: field.isOptional(),
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
function collectFields(ctx, macroName, decoded) {
|
|
851
|
+
const declaration = decoded.args.target;
|
|
852
|
+
const shape = objectLikeDeclarationShape(ctx, declaration, macroName);
|
|
853
|
+
const typeName = shape.name ??
|
|
854
|
+
ctx.error(`${macroName} currently requires named ${shape.declarationKind} declarations.`, declaration);
|
|
855
|
+
const fields = shape.fields
|
|
856
|
+
.map((field) => eqHashFieldFromReflectedShape(ctx, field, macroName, typeName, declaration))
|
|
857
|
+
.filter((field) => field !== null);
|
|
858
|
+
return { fields, typeName };
|
|
859
|
+
}
|
|
860
|
+
function eqCheckText(field, leftName, rightName) {
|
|
861
|
+
const leftAccess = propertyAccessText(leftName, field.name);
|
|
862
|
+
const rightAccess = propertyAccessText(rightName, field.name);
|
|
863
|
+
if (!field.optional) {
|
|
864
|
+
return `${field.eqHelper}.equals(${leftAccess}, ${rightAccess})`;
|
|
865
|
+
}
|
|
866
|
+
return `${leftAccess} === ${rightAccess} || (${leftAccess} !== undefined && ${rightAccess} !== undefined && ${field.eqHelper}.equals(${leftAccess}, ${rightAccess}))`;
|
|
867
|
+
}
|
|
868
|
+
function hashExprText(field, receiverName) {
|
|
869
|
+
const access = propertyAccessText(receiverName, field.name);
|
|
870
|
+
if (!field.optional) {
|
|
871
|
+
return `${field.hashHelper}.hash(${access})`;
|
|
872
|
+
}
|
|
873
|
+
return `${access} === undefined ? 0 : ${field.hashHelper}.hash(${access})`;
|
|
874
|
+
}
|
|
875
|
+
function nestedEqHelperTextFromFields(ctx, ownerTypeName, scopeNode, fields) {
|
|
876
|
+
const derivedFields = fields.map((field) => eqHashFieldFromReflectedShape(ctx, field, 'eq', ownerTypeName, scopeNode)).filter((field) => field !== null);
|
|
877
|
+
const equalsBody = derivedFields.length === 0
|
|
878
|
+
? 'true'
|
|
879
|
+
: derivedFields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
880
|
+
return `({
|
|
881
|
+
equals(left, right) {
|
|
882
|
+
return ${equalsBody};
|
|
883
|
+
},
|
|
884
|
+
})`;
|
|
885
|
+
}
|
|
886
|
+
function nestedHashHelperTextFromFields(ctx, ownerTypeName, scopeNode, fields) {
|
|
887
|
+
const derivedFields = fields.map((field) => eqHashFieldFromReflectedShape(ctx, field, 'hash', ownerTypeName, scopeNode)).filter((field) => field !== null);
|
|
888
|
+
const fromHashEq = ctx.runtime.named('sts:hash', 'fromHashEq').text();
|
|
889
|
+
const combineHashes = ctx.runtime.named('sts:hash', 'combineHashes').text();
|
|
890
|
+
const hashArgs = derivedFields.map((field) => hashExprText(field, 'value')).join(', ');
|
|
891
|
+
const equalsBody = derivedFields.length === 0
|
|
892
|
+
? 'true'
|
|
893
|
+
: derivedFields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
894
|
+
return `${fromHashEq}(
|
|
895
|
+
(value) => ${combineHashes}(${hashArgs}),
|
|
896
|
+
(left, right) => ${equalsBody},
|
|
897
|
+
)`;
|
|
898
|
+
}
|
|
899
|
+
function nestedDecodeHelperTextFromFields(ctx, ownerTypeName, scopeNode, fields) {
|
|
900
|
+
const decodedFields = fields.map((field) => decodeFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode));
|
|
901
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
902
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
903
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
904
|
+
const shapeText = decodedFields.length === 0 ? '{}' : `{
|
|
905
|
+
${decodedFields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${decodeOptional}(${field.decoderText})` : field.decoderText}`).join(',\n')}
|
|
906
|
+
}`;
|
|
907
|
+
const isIdentityProjection = decodedFields.every((field) => field.localName === field.wireName);
|
|
908
|
+
if (isIdentityProjection) {
|
|
909
|
+
return `${decodeObject}(${shapeText})`;
|
|
910
|
+
}
|
|
911
|
+
const projectionText = decodedFields.length === 0 ? '{}' : `({
|
|
912
|
+
${decodedFields.map((field) => `${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`).join(',\n')}
|
|
913
|
+
})`;
|
|
914
|
+
return `${decodeMap}(
|
|
915
|
+
${decodeObject}(${shapeText}),
|
|
916
|
+
(value) => ${projectionText},
|
|
917
|
+
)`;
|
|
918
|
+
}
|
|
919
|
+
function nestedEncodeHelperTextFromFields(ctx, ownerTypeName, scopeNode, fields) {
|
|
920
|
+
const encodedFields = fields.map((field) => encodeFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode));
|
|
921
|
+
const encodeContramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
922
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
923
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
924
|
+
const shapeText = encodedFields.length === 0 ? '{}' : `{
|
|
925
|
+
${encodedFields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${encodeOptional}(${field.encoderText})` : field.encoderText}`).join(',\n')}
|
|
926
|
+
}`;
|
|
927
|
+
const isIdentityProjection = encodedFields.every((field) => field.localName === field.wireName);
|
|
928
|
+
if (isIdentityProjection) {
|
|
929
|
+
return `${encodeObject}(${shapeText})`;
|
|
930
|
+
}
|
|
931
|
+
const projectionText = encodedFields.length === 0 ? '({})' : `({
|
|
932
|
+
${encodedFields.map((field) => `${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`).join(',\n')}
|
|
933
|
+
})`;
|
|
934
|
+
return `${encodeContramap}(
|
|
935
|
+
${encodeObject}(${shapeText}),
|
|
936
|
+
(value) => ${projectionText},
|
|
937
|
+
)`;
|
|
938
|
+
}
|
|
939
|
+
function nestedCodecHelperTextsFromFields(ctx, ownerTypeName, scopeNode, fields) {
|
|
940
|
+
const codecFields = fields.map((field) => codecFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode));
|
|
941
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
942
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
943
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
944
|
+
const encodeContramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
945
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
946
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
947
|
+
const decodeShapeText = codecFields.length === 0 ? '{}' : `{
|
|
948
|
+
${codecFields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${decodeOptional}(${field.decodeText})` : field.decodeText}`).join(',\n')}
|
|
949
|
+
}`;
|
|
950
|
+
const hasIdentityDecodeProjection = codecFields.every((field) => field.localName === field.wireName);
|
|
951
|
+
const encodeShapeText = codecFields.length === 0 ? '{}' : `{
|
|
952
|
+
${codecFields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${encodeOptional}(${field.encodeText})` : field.encodeText}`).join(',\n')}
|
|
953
|
+
}`;
|
|
954
|
+
const hasIdentityEncodeProjection = codecFields.every((field) => field.localName === field.wireName);
|
|
955
|
+
return {
|
|
956
|
+
decodeText: hasIdentityDecodeProjection ? `${decodeObject}(${decodeShapeText})` : `${decodeMap}(
|
|
957
|
+
${decodeObject}(${decodeShapeText}),
|
|
958
|
+
(value) => ({
|
|
959
|
+
${codecFields.map((field) => `${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`).join(',\n')}
|
|
960
|
+
}),
|
|
961
|
+
)`,
|
|
962
|
+
encodeText: hasIdentityEncodeProjection
|
|
963
|
+
? `${encodeObject}(${encodeShapeText})`
|
|
964
|
+
: `${encodeContramap}(
|
|
965
|
+
${encodeObject}(${encodeShapeText}),
|
|
966
|
+
(value) => ({
|
|
967
|
+
${codecFields.map((field) => `${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`).join(',\n')}
|
|
968
|
+
}),
|
|
969
|
+
)`,
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
function collectNestedDerivedFields(ctx, macroName, ownerTypeName, scopeNode, objectType) {
|
|
973
|
+
return objectType.members
|
|
974
|
+
.map((member) => fieldFromObjectMember(ctx, member, macroName, ownerTypeName, scopeNode))
|
|
975
|
+
.filter((member) => member !== null);
|
|
976
|
+
}
|
|
977
|
+
function nestedEqHelperText(ctx, ownerTypeName, scopeNode, objectType) {
|
|
978
|
+
const fields = collectNestedDerivedFields(ctx, 'eq', ownerTypeName, scopeNode, objectType);
|
|
979
|
+
const equalsBody = fields.length === 0
|
|
980
|
+
? 'true'
|
|
981
|
+
: fields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
982
|
+
return `({
|
|
983
|
+
equals(left, right) {
|
|
984
|
+
return ${equalsBody};
|
|
985
|
+
},
|
|
986
|
+
})`;
|
|
987
|
+
}
|
|
988
|
+
function nestedHashHelperText(ctx, ownerTypeName, scopeNode, objectType) {
|
|
989
|
+
const fields = collectNestedDerivedFields(ctx, 'hash', ownerTypeName, scopeNode, objectType);
|
|
990
|
+
const fromHashEq = ctx.runtime.named('sts:hash', 'fromHashEq').text();
|
|
991
|
+
const combineHashes = ctx.runtime.named('sts:hash', 'combineHashes').text();
|
|
992
|
+
const hashArgs = fields.map((field) => hashExprText(field, 'value')).join(', ');
|
|
993
|
+
const equalsBody = fields.length === 0
|
|
994
|
+
? 'true'
|
|
995
|
+
: fields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
996
|
+
return `${fromHashEq}(
|
|
997
|
+
(value) => ${combineHashes}(${hashArgs}),
|
|
998
|
+
(left, right) => ${equalsBody},
|
|
999
|
+
)`;
|
|
1000
|
+
}
|
|
1001
|
+
function nestedDecodeHelperText(ctx, ownerTypeName, scopeNode, objectType) {
|
|
1002
|
+
const fields = objectType.members.map((member) => fieldFromDecodeMember(ctx, member, ownerTypeName, scopeNode));
|
|
1003
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
1004
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
1005
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
1006
|
+
const shapeText = fields.length === 0 ? '{}' : `{
|
|
1007
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${decodeOptional}(${field.decoderText})` : field.decoderText}`).join(',\n')}
|
|
1008
|
+
}`;
|
|
1009
|
+
const isIdentityProjection = fields.every((field) => field.localName === field.wireName);
|
|
1010
|
+
if (isIdentityProjection) {
|
|
1011
|
+
return `${decodeObject}(${shapeText})`;
|
|
1012
|
+
}
|
|
1013
|
+
const projectionText = fields.length === 0 ? '{}' : `({
|
|
1014
|
+
${fields.map((field) => `${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`).join(',\n')}
|
|
1015
|
+
})`;
|
|
1016
|
+
return `${decodeMap}(
|
|
1017
|
+
${decodeObject}(${shapeText}),
|
|
1018
|
+
(value) => ${projectionText},
|
|
1019
|
+
)`;
|
|
1020
|
+
}
|
|
1021
|
+
function nestedEncodeHelperText(ctx, ownerTypeName, scopeNode, objectType) {
|
|
1022
|
+
const fields = objectType.members.map((member) => fieldFromEncodeMember(ctx, member, ownerTypeName, scopeNode));
|
|
1023
|
+
const encodeContramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
1024
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
1025
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
1026
|
+
const shapeText = fields.length === 0 ? '{}' : `{
|
|
1027
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${encodeOptional}(${field.encoderText})` : field.encoderText}`).join(',\n')}
|
|
1028
|
+
}`;
|
|
1029
|
+
const isIdentityProjection = fields.every((field) => field.localName === field.wireName);
|
|
1030
|
+
if (isIdentityProjection) {
|
|
1031
|
+
return `${encodeObject}(${shapeText})`;
|
|
1032
|
+
}
|
|
1033
|
+
const projectionText = fields.length === 0 ? '({})' : `({
|
|
1034
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`).join(',\n')}
|
|
1035
|
+
})`;
|
|
1036
|
+
return `${encodeContramap}(
|
|
1037
|
+
${encodeObject}(${shapeText}),
|
|
1038
|
+
(value) => ${projectionText},
|
|
1039
|
+
)`;
|
|
1040
|
+
}
|
|
1041
|
+
function nestedCodecHelperTexts(ctx, ownerTypeName, scopeNode, objectType) {
|
|
1042
|
+
const fields = objectType.members.map((member) => fieldFromCodecMember(ctx, member, ownerTypeName, scopeNode));
|
|
1043
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
1044
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
1045
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
1046
|
+
const encodeContramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
1047
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
1048
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
1049
|
+
const decodeShapeText = fields.length === 0 ? '{}' : `{
|
|
1050
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${decodeOptional}(${field.decodeText})` : field.decodeText}`).join(',\n')}
|
|
1051
|
+
}`;
|
|
1052
|
+
const hasIdentityDecodeProjection = fields.every((field) => field.localName === field.wireName);
|
|
1053
|
+
const encodeShapeText = fields.length === 0 ? '{}' : `{
|
|
1054
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${encodeOptional}(${field.encodeText})` : field.encodeText}`).join(',\n')}
|
|
1055
|
+
}`;
|
|
1056
|
+
const hasIdentityEncodeProjection = fields.every((field) => field.localName === field.wireName);
|
|
1057
|
+
return {
|
|
1058
|
+
decodeText: hasIdentityDecodeProjection ? `${decodeObject}(${decodeShapeText})` : `${decodeMap}(
|
|
1059
|
+
${decodeObject}(${decodeShapeText}),
|
|
1060
|
+
(value) => ({
|
|
1061
|
+
${fields.map((field) => `${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`).join(',\n')}
|
|
1062
|
+
}),
|
|
1063
|
+
)`,
|
|
1064
|
+
encodeText: hasIdentityEncodeProjection
|
|
1065
|
+
? `${encodeObject}(${encodeShapeText})`
|
|
1066
|
+
: `${encodeContramap}(
|
|
1067
|
+
${encodeObject}(${encodeShapeText}),
|
|
1068
|
+
(value) => ({
|
|
1069
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`).join(',\n')}
|
|
1070
|
+
}),
|
|
1071
|
+
)`,
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
function capitalizeName(name) {
|
|
1075
|
+
return name.length === 0 ? name : `${name[0].toUpperCase()}${name.slice(1)}`;
|
|
1076
|
+
}
|
|
1077
|
+
function decapitalizeName(name) {
|
|
1078
|
+
return name.length === 0 ? name : `${name[0].toLowerCase()}${name.slice(1)}`;
|
|
1079
|
+
}
|
|
1080
|
+
function annotationNamedStringArgument(annotation, name) {
|
|
1081
|
+
const argument = annotation.arguments?.find((entry) => entry.kind === 'named' && entry.name === name);
|
|
1082
|
+
return argument?.value.kind === 'string' ? argument.value.value : null;
|
|
1083
|
+
}
|
|
1084
|
+
function taggedDiscriminantName(ctx, declaration) {
|
|
1085
|
+
const annotation = findAnnotation(ctx.syntax.annotations(declaration), 'tagged');
|
|
1086
|
+
if (!annotation) {
|
|
1087
|
+
return 'tag';
|
|
1088
|
+
}
|
|
1089
|
+
const discriminant = annotationNamedStringArgument(annotation, 'discriminant');
|
|
1090
|
+
if (discriminant !== null) {
|
|
1091
|
+
return discriminant;
|
|
1092
|
+
}
|
|
1093
|
+
if ((annotation.arguments?.length ?? 0) > 0) {
|
|
1094
|
+
ctx.error("tagged only supports #[tagged(discriminant: '...')] options in v1.", declaration);
|
|
1095
|
+
}
|
|
1096
|
+
return 'tag';
|
|
1097
|
+
}
|
|
1098
|
+
function taggedPayloadField(ctx, member) {
|
|
1099
|
+
if (member.memberKind !== 'property_signature' || member.name === null) {
|
|
1100
|
+
ctx.error('tagged only supports property-style object members in v1.', member);
|
|
1101
|
+
}
|
|
1102
|
+
const explicitType = member.explicitType();
|
|
1103
|
+
if (!explicitType) {
|
|
1104
|
+
ctx.error('tagged currently requires explicit payload property types in v1.', member);
|
|
1105
|
+
}
|
|
1106
|
+
return {
|
|
1107
|
+
name: member.name,
|
|
1108
|
+
optional: member.isOptional(),
|
|
1109
|
+
typeText: explicitType.text(),
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
function fieldFromDecodeMember(ctx, member, ownerTypeName, scopeNode) {
|
|
1113
|
+
if (member.memberKind !== 'property_signature' || member.name === null) {
|
|
1114
|
+
ctx.error('decode only supports property-style object members in v1.', member);
|
|
1115
|
+
}
|
|
1116
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(member), 'decode.via');
|
|
1117
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1118
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1119
|
+
ctx.error('decode.via(...) requires a helper identifier.', member);
|
|
1120
|
+
}
|
|
1121
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(member), 'decode.rename');
|
|
1122
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1123
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1124
|
+
ctx.error('decode.rename(...) requires a string field name.', member);
|
|
1125
|
+
}
|
|
1126
|
+
const explicitType = member.explicitType();
|
|
1127
|
+
if (!explicitType) {
|
|
1128
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), member);
|
|
1129
|
+
}
|
|
1130
|
+
const decoderText = (() => {
|
|
1131
|
+
if (viaIdentifier) {
|
|
1132
|
+
return viaIdentifier;
|
|
1133
|
+
}
|
|
1134
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1135
|
+
if (objectType) {
|
|
1136
|
+
return nestedDecodeHelperText(ctx, ownerTypeName, scopeNode, objectType);
|
|
1137
|
+
}
|
|
1138
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1139
|
+
if (!fieldType) {
|
|
1140
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), member);
|
|
1141
|
+
}
|
|
1142
|
+
assertNamedDerivedCompanionsInScope(ctx, 'decode', ownerTypeName, scopeNode, member, fieldType);
|
|
1143
|
+
return decodeHelperTextForType(ctx, fieldType);
|
|
1144
|
+
})();
|
|
1145
|
+
return {
|
|
1146
|
+
decoderText,
|
|
1147
|
+
localName: member.name,
|
|
1148
|
+
optional: member.isOptional(),
|
|
1149
|
+
wireName: renamedWireName ?? member.name,
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
function fieldFromDecodeClassField(ctx, field, ownerTypeName, scopeNode) {
|
|
1153
|
+
if (field.hasModifier('private') || field.hasModifier('protected') || field.hasModifier('static') ||
|
|
1154
|
+
field.name === null) {
|
|
1155
|
+
return null;
|
|
1156
|
+
}
|
|
1157
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(field), 'decode.via');
|
|
1158
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1159
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1160
|
+
ctx.error('decode.via(...) requires a helper identifier.', field);
|
|
1161
|
+
}
|
|
1162
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(field), 'decode.rename');
|
|
1163
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1164
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1165
|
+
ctx.error('decode.rename(...) requires a string field name.', field);
|
|
1166
|
+
}
|
|
1167
|
+
const explicitType = field.explicitType();
|
|
1168
|
+
if (!explicitType) {
|
|
1169
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), field);
|
|
1170
|
+
}
|
|
1171
|
+
const decoderText = (() => {
|
|
1172
|
+
if (viaIdentifier) {
|
|
1173
|
+
return viaIdentifier;
|
|
1174
|
+
}
|
|
1175
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1176
|
+
if (objectType) {
|
|
1177
|
+
return nestedDecodeHelperText(ctx, ownerTypeName, scopeNode, objectType);
|
|
1178
|
+
}
|
|
1179
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1180
|
+
if (!fieldType) {
|
|
1181
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), field);
|
|
1182
|
+
}
|
|
1183
|
+
assertNamedDerivedCompanionsInScope(ctx, 'decode', ownerTypeName, scopeNode, field, fieldType);
|
|
1184
|
+
return decodeHelperTextForType(ctx, fieldType);
|
|
1185
|
+
})();
|
|
1186
|
+
return {
|
|
1187
|
+
decoderText,
|
|
1188
|
+
localName: field.name,
|
|
1189
|
+
optional: field.isOptional(),
|
|
1190
|
+
wireName: renamedWireName ?? field.name,
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
function classHasConstructorParameters(declaration) {
|
|
1194
|
+
return declaration.members().some((member) => member.memberKind === 'constructor' && member.parameters.length > 0);
|
|
1195
|
+
}
|
|
1196
|
+
function classifySelfFactoryHelper(declaration, typeName, factoryIdentifier) {
|
|
1197
|
+
const segments = factoryIdentifier.split('.').map((segment) => segment.trim()).filter((segment) => segment.length > 0);
|
|
1198
|
+
if (segments.length !== 2 || segments[0] !== typeName) {
|
|
1199
|
+
return null;
|
|
1200
|
+
}
|
|
1201
|
+
const member = declaration.member(segments[1]);
|
|
1202
|
+
if (!member || !member.hasModifier('static')) {
|
|
1203
|
+
return 'missing';
|
|
1204
|
+
}
|
|
1205
|
+
return member.memberKind === 'method' ? 'callable' : 'non-callable';
|
|
1206
|
+
}
|
|
1207
|
+
function classDecodeInstantiateText(ctx, declaration, macroName, typeName) {
|
|
1208
|
+
const factoryAnnotation = findAnnotation(ctx.syntax.annotations(declaration), `${macroName}.factory`);
|
|
1209
|
+
if (factoryAnnotation) {
|
|
1210
|
+
const factoryIdentifier = annotationIdentifierArgument(factoryAnnotation);
|
|
1211
|
+
if (!factoryIdentifier) {
|
|
1212
|
+
ctx.error(`${macroName}.factory(...) requires a helper identifier.`, annotationDiagnosticNode(declaration, factoryAnnotation) ?? declaration);
|
|
1213
|
+
}
|
|
1214
|
+
const selfFactoryClassification = classifySelfFactoryHelper(declaration, typeName, factoryIdentifier);
|
|
1215
|
+
if (selfFactoryClassification === 'missing') {
|
|
1216
|
+
ctx.error(`${macroName}.factory(...) requires the helper value "${factoryIdentifier}" to be in scope.`, annotationDiagnosticNode(declaration, factoryAnnotation) ?? declaration);
|
|
1217
|
+
}
|
|
1218
|
+
if (selfFactoryClassification === 'non-callable') {
|
|
1219
|
+
ctx.error(`${macroName}.factory(...) requires "${factoryIdentifier}" to be callable.`, annotationDiagnosticNode(declaration, factoryAnnotation) ?? declaration);
|
|
1220
|
+
}
|
|
1221
|
+
if (selfFactoryClassification === 'callable') {
|
|
1222
|
+
return `${factoryIdentifier}(${CLASS_DECODE_VALUE_PLACEHOLDER})`;
|
|
1223
|
+
}
|
|
1224
|
+
if (!ctx.semantics.valueBindingInScope(factoryIdentifier)) {
|
|
1225
|
+
ctx.error(`${macroName}.factory(...) requires the helper value "${factoryIdentifier}" to be in scope.`, annotationDiagnosticNode(declaration, factoryAnnotation) ?? declaration);
|
|
1226
|
+
}
|
|
1227
|
+
if (!ctx.semantics.valueBindingCallableInScope(factoryIdentifier)) {
|
|
1228
|
+
ctx.error(`${macroName}.factory(...) requires "${factoryIdentifier}" to be callable.`, annotationDiagnosticNode(declaration, factoryAnnotation) ?? declaration);
|
|
1229
|
+
}
|
|
1230
|
+
return `${factoryIdentifier}(${CLASS_DECODE_VALUE_PLACEHOLDER})`;
|
|
1231
|
+
}
|
|
1232
|
+
if (classHasConstructorParameters(declaration)) {
|
|
1233
|
+
ctx.error(`${macroName} class support in v1 requires a constructor with no parameters.`, declaration);
|
|
1234
|
+
}
|
|
1235
|
+
return `Object.assign(new ${typeName}(), ${CLASS_DECODE_VALUE_PLACEHOLDER})`;
|
|
1236
|
+
}
|
|
1237
|
+
function collectDecodeFields(ctx, decoded) {
|
|
1238
|
+
const declaration = decoded.args.target;
|
|
1239
|
+
const shape = objectLikeDeclarationShape(ctx, declaration, 'decode');
|
|
1240
|
+
const typeName = shape.name ??
|
|
1241
|
+
ctx.error(`decode currently requires named ${shape.declarationKind} declarations.`, declaration);
|
|
1242
|
+
return {
|
|
1243
|
+
fields: shape.fields.map((field) => decodeFieldFromReflectedShape(ctx, field, typeName, declaration)),
|
|
1244
|
+
instantiateText: decoded.caseName === 'class'
|
|
1245
|
+
? classDecodeInstantiateText(ctx, declaration, 'decode', typeName)
|
|
1246
|
+
: null,
|
|
1247
|
+
typeName,
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
function fieldFromEncodeMember(ctx, member, ownerTypeName, scopeNode) {
|
|
1251
|
+
if (member.memberKind !== 'property_signature' || member.name === null) {
|
|
1252
|
+
ctx.error('encode only supports property-style object members in v1.', member);
|
|
1253
|
+
}
|
|
1254
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(member), 'encode.via');
|
|
1255
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1256
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1257
|
+
ctx.error('encode.via(...) requires a helper identifier.', member);
|
|
1258
|
+
}
|
|
1259
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(member), 'encode.rename');
|
|
1260
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1261
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1262
|
+
ctx.error('encode.rename(...) requires a string field name.', member);
|
|
1263
|
+
}
|
|
1264
|
+
const explicitType = member.explicitType();
|
|
1265
|
+
if (!explicitType) {
|
|
1266
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), member);
|
|
1267
|
+
}
|
|
1268
|
+
const encoderText = (() => {
|
|
1269
|
+
if (viaIdentifier) {
|
|
1270
|
+
return viaIdentifier;
|
|
1271
|
+
}
|
|
1272
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1273
|
+
if (objectType) {
|
|
1274
|
+
return nestedEncodeHelperText(ctx, ownerTypeName, scopeNode, objectType);
|
|
1275
|
+
}
|
|
1276
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1277
|
+
if (!fieldType) {
|
|
1278
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), member);
|
|
1279
|
+
}
|
|
1280
|
+
assertNamedDerivedCompanionsInScope(ctx, 'encode', ownerTypeName, scopeNode, member, fieldType);
|
|
1281
|
+
return encodeHelperTextForType(ctx, fieldType);
|
|
1282
|
+
})();
|
|
1283
|
+
return {
|
|
1284
|
+
encoderText,
|
|
1285
|
+
localName: member.name,
|
|
1286
|
+
optional: member.isOptional(),
|
|
1287
|
+
wireName: renamedWireName ?? member.name,
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
function fieldFromEncodeClassField(ctx, field, ownerTypeName, scopeNode) {
|
|
1291
|
+
if (field.hasModifier('private') || field.hasModifier('protected') || field.hasModifier('static') ||
|
|
1292
|
+
field.name === null) {
|
|
1293
|
+
return null;
|
|
1294
|
+
}
|
|
1295
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(field), 'encode.via');
|
|
1296
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1297
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1298
|
+
ctx.error('encode.via(...) requires a helper identifier.', field);
|
|
1299
|
+
}
|
|
1300
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(field), 'encode.rename');
|
|
1301
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1302
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1303
|
+
ctx.error('encode.rename(...) requires a string field name.', field);
|
|
1304
|
+
}
|
|
1305
|
+
const explicitType = field.explicitType();
|
|
1306
|
+
if (!explicitType) {
|
|
1307
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), field);
|
|
1308
|
+
}
|
|
1309
|
+
const encoderText = (() => {
|
|
1310
|
+
if (viaIdentifier) {
|
|
1311
|
+
return viaIdentifier;
|
|
1312
|
+
}
|
|
1313
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1314
|
+
if (objectType) {
|
|
1315
|
+
return nestedEncodeHelperText(ctx, ownerTypeName, scopeNode, objectType);
|
|
1316
|
+
}
|
|
1317
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1318
|
+
if (!fieldType) {
|
|
1319
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), field);
|
|
1320
|
+
}
|
|
1321
|
+
assertNamedDerivedCompanionsInScope(ctx, 'encode', ownerTypeName, scopeNode, field, fieldType);
|
|
1322
|
+
return encodeHelperTextForType(ctx, fieldType);
|
|
1323
|
+
})();
|
|
1324
|
+
return {
|
|
1325
|
+
encoderText,
|
|
1326
|
+
localName: field.name,
|
|
1327
|
+
optional: field.isOptional(),
|
|
1328
|
+
wireName: renamedWireName ?? field.name,
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
function collectEncodeFields(ctx, decoded) {
|
|
1332
|
+
const declaration = decoded.args.target;
|
|
1333
|
+
const shape = objectLikeDeclarationShape(ctx, declaration, 'encode');
|
|
1334
|
+
const typeName = shape.name ??
|
|
1335
|
+
ctx.error(`encode currently requires named ${shape.declarationKind} declarations.`, declaration);
|
|
1336
|
+
return {
|
|
1337
|
+
fields: shape.fields.map((field) => encodeFieldFromReflectedShape(ctx, field, typeName, declaration)),
|
|
1338
|
+
typeName,
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
function fieldFromCodecMember(ctx, member, ownerTypeName, scopeNode) {
|
|
1342
|
+
if (member.memberKind !== 'property_signature' || member.name === null) {
|
|
1343
|
+
ctx.error('codec only supports property-style object members in v1.', member);
|
|
1344
|
+
}
|
|
1345
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(member), 'codec.via');
|
|
1346
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1347
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1348
|
+
ctx.error('codec.via(...) requires a helper identifier.', member);
|
|
1349
|
+
}
|
|
1350
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(member), 'codec.rename');
|
|
1351
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1352
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1353
|
+
ctx.error('codec.rename(...) requires a string field name.', member);
|
|
1354
|
+
}
|
|
1355
|
+
const explicitType = member.explicitType();
|
|
1356
|
+
if (!explicitType) {
|
|
1357
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), member);
|
|
1358
|
+
}
|
|
1359
|
+
const helperTexts = (() => {
|
|
1360
|
+
if (viaIdentifier) {
|
|
1361
|
+
return { decodeText: viaIdentifier, encodeText: viaIdentifier };
|
|
1362
|
+
}
|
|
1363
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1364
|
+
if (objectType) {
|
|
1365
|
+
return nestedCodecHelperTexts(ctx, ownerTypeName, scopeNode, objectType);
|
|
1366
|
+
}
|
|
1367
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1368
|
+
if (!fieldType) {
|
|
1369
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), member);
|
|
1370
|
+
}
|
|
1371
|
+
assertNamedDerivedCompanionsInScope(ctx, 'codec', ownerTypeName, scopeNode, member, fieldType);
|
|
1372
|
+
return codecHelperTextsForType(ctx, fieldType);
|
|
1373
|
+
})();
|
|
1374
|
+
return {
|
|
1375
|
+
decodeText: helperTexts.decodeText,
|
|
1376
|
+
encodeText: helperTexts.encodeText,
|
|
1377
|
+
localName: member.name,
|
|
1378
|
+
optional: member.isOptional(),
|
|
1379
|
+
wireName: renamedWireName ?? member.name,
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
function fieldFromCodecClassField(ctx, field, ownerTypeName, scopeNode) {
|
|
1383
|
+
if (field.hasModifier('private') || field.hasModifier('protected') || field.hasModifier('static') ||
|
|
1384
|
+
field.name === null) {
|
|
1385
|
+
return null;
|
|
1386
|
+
}
|
|
1387
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(field), 'codec.via');
|
|
1388
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1389
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1390
|
+
ctx.error('codec.via(...) requires a helper identifier.', field);
|
|
1391
|
+
}
|
|
1392
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(field), 'codec.rename');
|
|
1393
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1394
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1395
|
+
ctx.error('codec.rename(...) requires a string field name.', field);
|
|
1396
|
+
}
|
|
1397
|
+
const explicitType = field.explicitType();
|
|
1398
|
+
if (!explicitType) {
|
|
1399
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), field);
|
|
1400
|
+
}
|
|
1401
|
+
const helperTexts = (() => {
|
|
1402
|
+
if (viaIdentifier) {
|
|
1403
|
+
return { decodeText: viaIdentifier, encodeText: viaIdentifier };
|
|
1404
|
+
}
|
|
1405
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1406
|
+
if (objectType) {
|
|
1407
|
+
return nestedCodecHelperTexts(ctx, ownerTypeName, scopeNode, objectType);
|
|
1408
|
+
}
|
|
1409
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1410
|
+
if (!fieldType) {
|
|
1411
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), field);
|
|
1412
|
+
}
|
|
1413
|
+
assertNamedDerivedCompanionsInScope(ctx, 'codec', ownerTypeName, scopeNode, field, fieldType);
|
|
1414
|
+
return codecHelperTextsForType(ctx, fieldType);
|
|
1415
|
+
})();
|
|
1416
|
+
return {
|
|
1417
|
+
decodeText: helperTexts.decodeText,
|
|
1418
|
+
encodeText: helperTexts.encodeText,
|
|
1419
|
+
localName: field.name,
|
|
1420
|
+
optional: field.isOptional(),
|
|
1421
|
+
wireName: renamedWireName ?? field.name,
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
function collectCodecFields(ctx, decoded) {
|
|
1425
|
+
const declaration = decoded.args.target;
|
|
1426
|
+
const shape = objectLikeDeclarationShape(ctx, declaration, 'codec');
|
|
1427
|
+
const typeName = shape.name ??
|
|
1428
|
+
ctx.error(`codec currently requires named ${shape.declarationKind} declarations.`, declaration);
|
|
1429
|
+
return {
|
|
1430
|
+
fields: shape.fields.map((field) => codecFieldFromReflectedShape(ctx, field, typeName, declaration)),
|
|
1431
|
+
instantiateText: decoded.caseName === 'class'
|
|
1432
|
+
? classDecodeInstantiateText(ctx, declaration, 'codec', typeName)
|
|
1433
|
+
: null,
|
|
1434
|
+
typeName,
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
function validateTaggedUnionSyntaxForDiagnostics(ctx, declaration, macroName, discriminantName) {
|
|
1438
|
+
const unionType = declaration.type.asUnion();
|
|
1439
|
+
if (!unionType) {
|
|
1440
|
+
if (macroName === 'tagged') {
|
|
1441
|
+
ctx.error('tagged currently only supports type aliases declared as unions in v1.', declaration);
|
|
1442
|
+
}
|
|
1443
|
+
ctx.error(`${macroName} currently only supports object-like type aliases.`, declaration);
|
|
1444
|
+
}
|
|
1445
|
+
for (const member of unionType.members) {
|
|
1446
|
+
const objectType = member.asObjectLiteral();
|
|
1447
|
+
if (!objectType) {
|
|
1448
|
+
ctx.error(macroName === 'tagged'
|
|
1449
|
+
? 'tagged only supports unions of object-like variants in v1.'
|
|
1450
|
+
: `${macroName} only supports // #[tagged] unions of object-like variants in v1.`, declaration);
|
|
1451
|
+
}
|
|
1452
|
+
let hasDiscriminant = false;
|
|
1453
|
+
for (const objectMember of objectType.members) {
|
|
1454
|
+
if (objectMember.memberKind !== 'property_signature' || objectMember.name === null) {
|
|
1455
|
+
ctx.error(macroName === 'tagged'
|
|
1456
|
+
? 'tagged only supports property-style object members in v1.'
|
|
1457
|
+
: `${macroName} only supports property-style tagged union members in v1.`, objectMember);
|
|
1458
|
+
}
|
|
1459
|
+
if (objectMember.name !== discriminantName) {
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
const literalType = objectMember.explicitType()?.asLiteral();
|
|
1463
|
+
if (!literalType || literalType.literalKind !== 'string') {
|
|
1464
|
+
ctx.error(macroName === 'tagged'
|
|
1465
|
+
? 'tagged requires each variant discriminant to be a string literal type in v1.'
|
|
1466
|
+
: `${macroName} requires each tagged union discriminant to be a string literal type in v1.`, objectMember);
|
|
1467
|
+
}
|
|
1468
|
+
hasDiscriminant = true;
|
|
1469
|
+
}
|
|
1470
|
+
if (!hasDiscriminant) {
|
|
1471
|
+
ctx.error(macroName === 'tagged'
|
|
1472
|
+
? `tagged requires each variant to declare the discriminant property "${discriminantName}".`
|
|
1473
|
+
: `${macroName} requires each tagged variant to declare the discriminant property "${discriminantName}".`, objectType);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
ctx.error(macroName === 'tagged'
|
|
1477
|
+
? 'tagged only supports unions of object-like variants in v1.'
|
|
1478
|
+
: `${macroName} only supports // #[tagged] unions of object-like variants in v1.`, declaration);
|
|
1479
|
+
}
|
|
1480
|
+
function collectTaggedDerivedVariants(ctx, declaration, macroName, collectField) {
|
|
1481
|
+
const discriminantName = taggedDiscriminantName(ctx, declaration);
|
|
1482
|
+
const shape = ctx.reflect.declarationShape(declaration);
|
|
1483
|
+
if (shape.kind !== 'discriminatedUnion') {
|
|
1484
|
+
return validateTaggedUnionSyntaxForDiagnostics(ctx, declaration, macroName, discriminantName);
|
|
1485
|
+
}
|
|
1486
|
+
const unionShape = shape;
|
|
1487
|
+
const typeName = unionShape.name ??
|
|
1488
|
+
ctx.error(`${macroName} currently requires named type aliases.`, declaration);
|
|
1489
|
+
const variants = unionShape.variants.map((variant) => {
|
|
1490
|
+
const discriminant = variant.discriminants.find((entry) => entry.name === discriminantName);
|
|
1491
|
+
if (!discriminant) {
|
|
1492
|
+
ctx.error(`${macroName} requires each tagged variant to declare the discriminant property "${discriminantName}".`, variant.node);
|
|
1493
|
+
}
|
|
1494
|
+
const fields = [];
|
|
1495
|
+
for (const reflectedField of variant.fields) {
|
|
1496
|
+
const field = collectField(reflectedField, typeName, declaration);
|
|
1497
|
+
if (field !== null) {
|
|
1498
|
+
fields.push(field);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
return {
|
|
1502
|
+
fields,
|
|
1503
|
+
tag: discriminant.tag,
|
|
1504
|
+
};
|
|
1505
|
+
});
|
|
1506
|
+
return { discriminantName, typeName, variants };
|
|
1507
|
+
}
|
|
1508
|
+
function taggedVariantTypeText(typeName, discriminantName, tag) {
|
|
1509
|
+
return `Extract<${typeName}, { ${propertyKeyText(discriminantName)}: "${tag}" }>`;
|
|
1510
|
+
}
|
|
1511
|
+
function foldUnionText(helperText, expressions) {
|
|
1512
|
+
const [first, ...rest] = expressions;
|
|
1513
|
+
if (!first) {
|
|
1514
|
+
throw new Error('foldUnionText requires at least one expression.');
|
|
1515
|
+
}
|
|
1516
|
+
return rest.reduce((current, expression) => `${helperText}(${current}, ${expression})`, first);
|
|
1517
|
+
}
|
|
1518
|
+
function collectTaggedVariants(ctx, declaration) {
|
|
1519
|
+
const shape = ctx.reflect.declarationShape(declaration);
|
|
1520
|
+
if (shape.kind === 'discriminatedUnion') {
|
|
1521
|
+
const discriminantName = taggedDiscriminantName(ctx, declaration);
|
|
1522
|
+
const unionShape = shape;
|
|
1523
|
+
const typeName = unionShape.name ??
|
|
1524
|
+
ctx.error('tagged currently requires named type aliases.', declaration);
|
|
1525
|
+
const seenTags = new Set();
|
|
1526
|
+
const variants = unionShape.variants.map((variant) => {
|
|
1527
|
+
const discriminant = variant.discriminants.find((entry) => entry.name === discriminantName);
|
|
1528
|
+
if (!discriminant) {
|
|
1529
|
+
ctx.error(`tagged requires each variant to declare the discriminant property "${discriminantName}".`, variant.node);
|
|
1530
|
+
}
|
|
1531
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(discriminant.tag)) {
|
|
1532
|
+
ctx.error('tagged requires identifier-safe string literal tags in v1.', variant.node);
|
|
1533
|
+
}
|
|
1534
|
+
if (seenTags.has(discriminant.tag)) {
|
|
1535
|
+
ctx.error(`tagged requires unique "${discriminantName}" variant tags.`, variant.node);
|
|
1536
|
+
}
|
|
1537
|
+
seenTags.add(discriminant.tag);
|
|
1538
|
+
return {
|
|
1539
|
+
constructorName: discriminant.tag,
|
|
1540
|
+
constructorTypeParametersText: '',
|
|
1541
|
+
kind: 'object',
|
|
1542
|
+
payloadFields: variant.fields.map((field) => {
|
|
1543
|
+
if (!field.type) {
|
|
1544
|
+
ctx.error('tagged currently requires explicit payload property types in v1.', field.node);
|
|
1545
|
+
}
|
|
1546
|
+
return {
|
|
1547
|
+
name: field.name,
|
|
1548
|
+
optional: field.optional,
|
|
1549
|
+
typeText: field.type.text,
|
|
1550
|
+
};
|
|
1551
|
+
}),
|
|
1552
|
+
predicateConditionText: `${propertyAccessText('value', discriminantName)} === '${discriminant.tag}'`,
|
|
1553
|
+
predicateName: `is${capitalizeName(discriminant.tag)}`,
|
|
1554
|
+
predicateNarrowTypeText: `Extract<${typeName}, { ${propertyKeyText(discriminantName)}: "${discriminant.tag}" }>`,
|
|
1555
|
+
predicateTypeParametersText: '',
|
|
1556
|
+
predicateValueTypeText: typeName,
|
|
1557
|
+
returnExpressionText: variant.fields.length === 0
|
|
1558
|
+
? `{ ${propertyKeyText(discriminantName)}: '${discriminant.tag}' }`
|
|
1559
|
+
: `{ ${propertyKeyText(discriminantName)}: '${discriminant.tag}', ...payload }`,
|
|
1560
|
+
};
|
|
1561
|
+
});
|
|
1562
|
+
return { discriminantName, typeName, variants };
|
|
1563
|
+
}
|
|
1564
|
+
const unionType = declaration.type.asUnion();
|
|
1565
|
+
if (!unionType) {
|
|
1566
|
+
return validateTaggedUnionSyntaxForDiagnostics(ctx, declaration, 'tagged', taggedDiscriminantName(ctx, declaration));
|
|
1567
|
+
}
|
|
1568
|
+
if (unionType.members.every((member) => member.asObjectLiteral() !== null)) {
|
|
1569
|
+
return validateTaggedUnionSyntaxForDiagnostics(ctx, declaration, 'tagged', taggedDiscriminantName(ctx, declaration));
|
|
1570
|
+
}
|
|
1571
|
+
const hostDeclaration = getHostDeclaration(declaration);
|
|
1572
|
+
if (!ts.isTypeAliasDeclaration(hostDeclaration)) {
|
|
1573
|
+
return validateTaggedUnionSyntaxForDiagnostics(ctx, declaration, 'tagged', taggedDiscriminantName(ctx, declaration));
|
|
1574
|
+
}
|
|
1575
|
+
const sourceFile = hostDeclaration.getSourceFile();
|
|
1576
|
+
const hostUnionType = ts.isUnionTypeNode(hostDeclaration.type)
|
|
1577
|
+
? hostDeclaration.type
|
|
1578
|
+
: undefined;
|
|
1579
|
+
if (!hostUnionType) {
|
|
1580
|
+
return validateTaggedUnionSyntaxForDiagnostics(ctx, declaration, 'tagged', taggedDiscriminantName(ctx, declaration));
|
|
1581
|
+
}
|
|
1582
|
+
const typeName = hostDeclaration.name?.text ??
|
|
1583
|
+
ctx.error('tagged currently requires named type aliases.', declaration);
|
|
1584
|
+
const aliasTypeParameters = hostDeclaration.typeParameters?.map((parameter) => parameter.getText(sourceFile)) ?? [];
|
|
1585
|
+
const aliasTypeParametersText = aliasTypeParameters.length === 0
|
|
1586
|
+
? ''
|
|
1587
|
+
: `<${aliasTypeParameters.join(', ')}>`;
|
|
1588
|
+
const aliasAppliedTypeText = `${typeName}${aliasTypeParametersText}`;
|
|
1589
|
+
const variants = hostUnionType.types.map((hostMember, index) => {
|
|
1590
|
+
if (!ts.isTypeReferenceNode(hostMember)) {
|
|
1591
|
+
ctx.error('tagged only supports unions of object-like variants or named classes in the same module in v1.', declaration);
|
|
1592
|
+
}
|
|
1593
|
+
const member = unionType.members[index];
|
|
1594
|
+
if (!member) {
|
|
1595
|
+
ctx.error('tagged only supports unions of object-like variants or named classes in the same module in v1.', declaration);
|
|
1596
|
+
}
|
|
1597
|
+
const classDeclaration = ctx.semantics.classDeclarationOfType(member);
|
|
1598
|
+
if (!classDeclaration || classDeclaration.span.fileName !== sourceFile.fileName) {
|
|
1599
|
+
ctx.error('tagged only supports unions of object-like variants or named classes in the same module in v1.', declaration);
|
|
1600
|
+
}
|
|
1601
|
+
const className = classDeclaration.name ??
|
|
1602
|
+
ctx.error('tagged only supports unions of object-like variants or named classes in the same module in v1.', declaration);
|
|
1603
|
+
const constructor = classDeclaration.members().find((classMember) => classMember.memberKind === 'constructor');
|
|
1604
|
+
const payloadFields = constructor
|
|
1605
|
+
? constructor.parameters.map((parameter) => {
|
|
1606
|
+
const explicitType = parameter.explicitType();
|
|
1607
|
+
if (!parameter.name || !explicitType) {
|
|
1608
|
+
ctx.error('tagged value-class variants require constructors with simple identifier parameters and explicit types in v1.', declaration);
|
|
1609
|
+
}
|
|
1610
|
+
return {
|
|
1611
|
+
name: parameter.name,
|
|
1612
|
+
optional: false,
|
|
1613
|
+
typeText: explicitType.text(),
|
|
1614
|
+
};
|
|
1615
|
+
})
|
|
1616
|
+
: [];
|
|
1617
|
+
const variantTypeParameters = hostMember.typeArguments?.flatMap((typeArgument) => ts.isTypeReferenceNode(typeArgument) && ts.isIdentifier(typeArgument.typeName) &&
|
|
1618
|
+
!typeArgument.typeArguments?.length
|
|
1619
|
+
? [typeArgument.typeName.text]
|
|
1620
|
+
: []) ?? [];
|
|
1621
|
+
const variantTypeParametersText = variantTypeParameters.length === 0
|
|
1622
|
+
? ''
|
|
1623
|
+
: `<${variantTypeParameters.join(', ')}>`;
|
|
1624
|
+
return {
|
|
1625
|
+
constructorName: decapitalizeName(className),
|
|
1626
|
+
constructorTypeParametersText: variantTypeParametersText,
|
|
1627
|
+
kind: 'class',
|
|
1628
|
+
payloadFields,
|
|
1629
|
+
predicateConditionText: `value instanceof ${className}`,
|
|
1630
|
+
predicateName: `is${className}`,
|
|
1631
|
+
predicateNarrowTypeText: hostMember.getText(sourceFile),
|
|
1632
|
+
predicateTypeParametersText: aliasTypeParametersText,
|
|
1633
|
+
predicateValueTypeText: aliasAppliedTypeText,
|
|
1634
|
+
returnExpressionText: payloadFields.length === 0
|
|
1635
|
+
? `new ${className}()`
|
|
1636
|
+
: `new ${className}(${payloadFields.map((field) => field.name).join(', ')})`,
|
|
1637
|
+
};
|
|
1638
|
+
});
|
|
1639
|
+
return {
|
|
1640
|
+
discriminantName: null,
|
|
1641
|
+
typeName,
|
|
1642
|
+
variants,
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
function taggedPayloadTypeText(fields) {
|
|
1646
|
+
if (fields.length === 0) {
|
|
1647
|
+
return '{}';
|
|
1648
|
+
}
|
|
1649
|
+
return `{ ${fields.map((field) => `${propertyKeyText(field.name)}${field.optional ? '?' : ''}: ${field.typeText}`).join('; ')} }`;
|
|
1650
|
+
}
|
|
1651
|
+
function taggedConstructorText(discriminantName, typeName, variant) {
|
|
1652
|
+
const returnType = variant.kind === 'class' ? variant.predicateNarrowTypeText : typeName;
|
|
1653
|
+
const parameterText = variant.payloadFields.length === 0
|
|
1654
|
+
? ''
|
|
1655
|
+
: variant.kind === 'class'
|
|
1656
|
+
? variant.payloadFields.map((field) => `${field.name}: ${field.typeText}`).join(', ')
|
|
1657
|
+
: `payload: ${taggedPayloadTypeText(variant.payloadFields)}`;
|
|
1658
|
+
const typeParametersText = variant.constructorTypeParametersText;
|
|
1659
|
+
if (parameterText.length === 0) {
|
|
1660
|
+
return `${variant.constructorName}${typeParametersText}(): ${returnType} {
|
|
1661
|
+
return ${variant.returnExpressionText};
|
|
1662
|
+
}`;
|
|
1663
|
+
}
|
|
1664
|
+
return `${variant.constructorName}${typeParametersText}(${parameterText}): ${returnType} {
|
|
1665
|
+
return ${variant.returnExpressionText};
|
|
1666
|
+
}`;
|
|
1667
|
+
}
|
|
1668
|
+
function taggedPredicateText(_discriminantName, _typeName, variant) {
|
|
1669
|
+
return `${variant.predicateName}${variant.predicateTypeParametersText}(value: ${variant.predicateValueTypeText}): value is ${variant.predicateNarrowTypeText} {
|
|
1670
|
+
return ${variant.predicateConditionText};
|
|
1671
|
+
}`;
|
|
1672
|
+
}
|
|
1673
|
+
function taggedEqCaseText(discriminantName, typeName, variant) {
|
|
1674
|
+
const rightName = `right${capitalizeName(variant.tag)}`;
|
|
1675
|
+
const equalsBody = variant.fields.length === 0
|
|
1676
|
+
? 'true'
|
|
1677
|
+
: variant.fields.map((field) => eqCheckText(field, 'left', rightName)).join(' && ');
|
|
1678
|
+
return `case '${variant.tag}': {
|
|
1679
|
+
const ${rightName} = right as ${taggedVariantTypeText(typeName, discriminantName, variant.tag)};
|
|
1680
|
+
return ${equalsBody};
|
|
1681
|
+
}`;
|
|
1682
|
+
}
|
|
1683
|
+
function taggedHashCaseText(discriminantName, typeName, variant, stringHashText, combineHashesText) {
|
|
1684
|
+
const rightName = `right${capitalizeName(variant.tag)}`;
|
|
1685
|
+
const payloadHashes = variant.fields.map((field) => hashExprText(field, 'value'));
|
|
1686
|
+
const hashExpr = `${combineHashesText}(${[
|
|
1687
|
+
`${stringHashText}.hash(${JSON.stringify(variant.tag)})`,
|
|
1688
|
+
...payloadHashes,
|
|
1689
|
+
].join(', ')})`;
|
|
1690
|
+
const equalsBody = variant.fields.length === 0
|
|
1691
|
+
? 'true'
|
|
1692
|
+
: variant.fields.map((field) => eqCheckText(field, 'value', rightName)).join(' && ');
|
|
1693
|
+
return `case '${variant.tag}': {
|
|
1694
|
+
if (mode === 'hash') {
|
|
1695
|
+
return ${hashExpr};
|
|
1696
|
+
}
|
|
1697
|
+
const ${rightName} = right as ${taggedVariantTypeText(typeName, discriminantName, variant.tag)};
|
|
1698
|
+
return ${equalsBody};
|
|
1699
|
+
}`;
|
|
1700
|
+
}
|
|
1701
|
+
function taggedDecodeVariantText(discriminantName, variant, decodeLiteralText, decodeMapText, decodeObjectText, decodeOptionalText) {
|
|
1702
|
+
const shapeEntries = [
|
|
1703
|
+
`${propertyKeyText(discriminantName)}: ${decodeLiteralText}(${JSON.stringify(variant.tag)})`,
|
|
1704
|
+
...variant.fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${decodeOptionalText}(${field.decoderText})` : field.decoderText}`),
|
|
1705
|
+
];
|
|
1706
|
+
const projectionEntries = [
|
|
1707
|
+
`${propertyKeyText(discriminantName)}: ${JSON.stringify(variant.tag)}`,
|
|
1708
|
+
...variant.fields.map((field) => `${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`),
|
|
1709
|
+
];
|
|
1710
|
+
return `${decodeMapText}(
|
|
1711
|
+
${decodeObjectText}({
|
|
1712
|
+
${shapeEntries.join(',\n')}
|
|
1713
|
+
}),
|
|
1714
|
+
(value) => ({
|
|
1715
|
+
${projectionEntries.join(',\n')}
|
|
1716
|
+
}),
|
|
1717
|
+
)`;
|
|
1718
|
+
}
|
|
1719
|
+
function taggedEncodeVariantProjectionText(discriminantName, variant, receiverName) {
|
|
1720
|
+
return `({
|
|
1721
|
+
${[
|
|
1722
|
+
`${propertyKeyText(discriminantName)}: ${JSON.stringify(variant.tag)}`,
|
|
1723
|
+
...variant.fields.map((field) => `${propertyKeyText(field.wireName)}: ${propertyAccessText(receiverName, field.localName)}`),
|
|
1724
|
+
].join(',\n')}
|
|
1725
|
+
})`;
|
|
1726
|
+
}
|
|
1727
|
+
function taggedEncodeVariantShapeText(discriminantName, variant, encodeOptionalText, stringEncoderText) {
|
|
1728
|
+
return `{
|
|
1729
|
+
${[
|
|
1730
|
+
`${propertyKeyText(discriminantName)}: ${stringEncoderText}`,
|
|
1731
|
+
...variant.fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${encodeOptionalText}(${field.encoderText})` : field.encoderText}`),
|
|
1732
|
+
].join(',\n')}
|
|
1733
|
+
}`;
|
|
1734
|
+
}
|
|
1735
|
+
function taggedCodecDecodeVariantText(discriminantName, variant, decodeLiteralText, decodeMapText, decodeObjectText, decodeOptionalText) {
|
|
1736
|
+
const shapeEntries = [
|
|
1737
|
+
`${propertyKeyText(discriminantName)}: ${decodeLiteralText}(${JSON.stringify(variant.tag)})`,
|
|
1738
|
+
...variant.fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${decodeOptionalText}(${field.decodeText})` : field.decodeText}`),
|
|
1739
|
+
];
|
|
1740
|
+
const projectionEntries = [
|
|
1741
|
+
`${propertyKeyText(discriminantName)}: ${JSON.stringify(variant.tag)}`,
|
|
1742
|
+
...variant.fields.map((field) => `${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`),
|
|
1743
|
+
];
|
|
1744
|
+
return `${decodeMapText}(
|
|
1745
|
+
${decodeObjectText}({
|
|
1746
|
+
${shapeEntries.join(',\n')}
|
|
1747
|
+
}),
|
|
1748
|
+
(value) => ({
|
|
1749
|
+
${projectionEntries.join(',\n')}
|
|
1750
|
+
}),
|
|
1751
|
+
)`;
|
|
1752
|
+
}
|
|
1753
|
+
function taggedCodecEncodeVariantShapeText(discriminantName, variant, encodeOptionalText, stringEncoderText) {
|
|
1754
|
+
return `{
|
|
1755
|
+
${[
|
|
1756
|
+
`${propertyKeyText(discriminantName)}: ${stringEncoderText}`,
|
|
1757
|
+
...variant.fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${encodeOptionalText}(${field.encodeText})` : field.encodeText}`),
|
|
1758
|
+
].join(',\n')}
|
|
1759
|
+
}`;
|
|
1760
|
+
}
|
|
1761
|
+
function taggedCodecEncodeVariantProjectionText(discriminantName, variant, receiverName) {
|
|
1762
|
+
return `({
|
|
1763
|
+
${[
|
|
1764
|
+
`${propertyKeyText(discriminantName)}: ${JSON.stringify(variant.tag)}`,
|
|
1765
|
+
...variant.fields.map((field) => `${propertyKeyText(field.wireName)}: ${propertyAccessText(receiverName, field.localName)}`),
|
|
1766
|
+
].join(',\n')}
|
|
1767
|
+
})`;
|
|
1768
|
+
}
|
|
1769
|
+
// #[macro(decl)]
|
|
1770
|
+
export function eq() {
|
|
1771
|
+
return {
|
|
1772
|
+
declarationKinds: ['class', 'interface', 'typeAlias'],
|
|
1773
|
+
expansionMode: 'augment',
|
|
1774
|
+
expand(ctx, decoded) {
|
|
1775
|
+
if (decoded.caseName === 'typeAlias') {
|
|
1776
|
+
const declaration = decoded.args.target;
|
|
1777
|
+
if (!declaration.type.asObjectLiteral() && declaration.type.asUnion()) {
|
|
1778
|
+
const { discriminantName, typeName, variants } = collectTaggedDerivedVariants(ctx, declaration, 'eq', (field, ownerTypeName, scopeNode) => eqHashFieldFromReflectedShape(ctx, field, 'eq', ownerTypeName, scopeNode));
|
|
1779
|
+
const switchCases = variants.map((variant) => taggedEqCaseText(discriminantName, typeName, variant)).join('\n');
|
|
1780
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
1781
|
+
export const ${`${typeName}Eq`} = {
|
|
1782
|
+
equals(left: ${typeName}, right: ${typeName}) {
|
|
1783
|
+
if (${propertyAccessText('left', discriminantName)} !== ${propertyAccessText('right', discriminantName)}) {
|
|
1784
|
+
return false;
|
|
1785
|
+
}
|
|
1786
|
+
switch (${propertyAccessText('left', discriminantName)}) {
|
|
1787
|
+
${switchCases}
|
|
1788
|
+
default:
|
|
1789
|
+
return false;
|
|
1790
|
+
}
|
|
1791
|
+
},
|
|
1792
|
+
};
|
|
1793
|
+
`);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
const { fields, typeName } = collectFields(ctx, 'eq', decoded);
|
|
1797
|
+
const equalsBody = fields.length === 0
|
|
1798
|
+
? 'true'
|
|
1799
|
+
: fields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
1800
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
1801
|
+
export const ${`${typeName}Eq`} = {
|
|
1802
|
+
equals(left: ${typeName}, right: ${typeName}) {
|
|
1803
|
+
return ${equalsBody};
|
|
1804
|
+
},
|
|
1805
|
+
};
|
|
1806
|
+
`);
|
|
1807
|
+
},
|
|
1808
|
+
signature: DERIVE_SIGNATURE,
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
attachDeriveFactory(eq);
|
|
1812
|
+
// #[macro(decl)]
|
|
1813
|
+
export function hash() {
|
|
1814
|
+
return {
|
|
1815
|
+
declarationKinds: ['class', 'interface', 'typeAlias'],
|
|
1816
|
+
expansionMode: 'augment',
|
|
1817
|
+
expand(ctx, decoded) {
|
|
1818
|
+
if (decoded.caseName === 'typeAlias') {
|
|
1819
|
+
const declaration = decoded.args.target;
|
|
1820
|
+
if (!declaration.type.asObjectLiteral() && declaration.type.asUnion()) {
|
|
1821
|
+
const { discriminantName, typeName, variants } = collectTaggedDerivedVariants(ctx, declaration, 'hash', (field, ownerTypeName, scopeNode) => eqHashFieldFromReflectedShape(ctx, field, 'hash', ownerTypeName, scopeNode));
|
|
1822
|
+
const fromHashEq = ctx.runtime.named('sts:hash', 'fromHashEq').text();
|
|
1823
|
+
const combineHashes = ctx.runtime.named('sts:hash', 'combineHashes').text();
|
|
1824
|
+
const stringHash = ctx.runtime.named('sts:hash', 'stringHash').text();
|
|
1825
|
+
const switchCases = variants.map((variant) => taggedHashCaseText(discriminantName, typeName, variant, stringHash, combineHashes)).join('\n');
|
|
1826
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
1827
|
+
export const ${`${typeName}Hash`} = ${fromHashEq}<${typeName}>(
|
|
1828
|
+
(value) => {
|
|
1829
|
+
const mode = 'hash' as const;
|
|
1830
|
+
switch (${propertyAccessText('value', discriminantName)}) {
|
|
1831
|
+
${switchCases}
|
|
1832
|
+
default:
|
|
1833
|
+
return 0;
|
|
1834
|
+
}
|
|
1835
|
+
},
|
|
1836
|
+
(value, right) => {
|
|
1837
|
+
const mode = 'equals' as const;
|
|
1838
|
+
if (${propertyAccessText('value', discriminantName)} !== ${propertyAccessText('right', discriminantName)}) {
|
|
1839
|
+
return false;
|
|
1840
|
+
}
|
|
1841
|
+
switch (${propertyAccessText('value', discriminantName)}) {
|
|
1842
|
+
${switchCases}
|
|
1843
|
+
default:
|
|
1844
|
+
return false;
|
|
1845
|
+
}
|
|
1846
|
+
},
|
|
1847
|
+
);
|
|
1848
|
+
`);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
const { fields, typeName } = collectFields(ctx, 'hash', decoded);
|
|
1852
|
+
const fromHashEq = ctx.runtime.named('sts:hash', 'fromHashEq').text();
|
|
1853
|
+
const combineHashes = ctx.runtime.named('sts:hash', 'combineHashes').text();
|
|
1854
|
+
const hashArgs = fields.map((field) => hashExprText(field, 'value')).join(', ');
|
|
1855
|
+
const equalsBody = fields.length === 0
|
|
1856
|
+
? 'true'
|
|
1857
|
+
: fields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
1858
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
1859
|
+
export const ${`${typeName}Hash`} = ${fromHashEq}<${typeName}>(
|
|
1860
|
+
(value) => ${combineHashes}(${hashArgs}),
|
|
1861
|
+
(left, right) => ${equalsBody},
|
|
1862
|
+
);
|
|
1863
|
+
`);
|
|
1864
|
+
},
|
|
1865
|
+
signature: DERIVE_SIGNATURE,
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
attachDeriveFactory(hash);
|
|
1869
|
+
// #[macro(decl)]
|
|
1870
|
+
export function tagged() {
|
|
1871
|
+
return {
|
|
1872
|
+
declarationKinds: ['typeAlias'],
|
|
1873
|
+
expansionMode: 'augment',
|
|
1874
|
+
expand(ctx, decoded) {
|
|
1875
|
+
const declaration = decoded.args.target;
|
|
1876
|
+
const { discriminantName, typeName, variants } = collectTaggedVariants(ctx, declaration);
|
|
1877
|
+
const companionMembers = variants.flatMap((variant) => [
|
|
1878
|
+
taggedConstructorText(discriminantName, typeName, variant),
|
|
1879
|
+
taggedPredicateText(discriminantName, typeName, variant),
|
|
1880
|
+
]).join(',\n');
|
|
1881
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
1882
|
+
export const ${`${typeName}Tagged`} = {
|
|
1883
|
+
${companionMembers}
|
|
1884
|
+
};
|
|
1885
|
+
`);
|
|
1886
|
+
},
|
|
1887
|
+
signature: TAGGED_SIGNATURE,
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
attachDeriveFactory(tagged);
|
|
1891
|
+
// #[macro(decl)]
|
|
1892
|
+
export function decode() {
|
|
1893
|
+
return {
|
|
1894
|
+
declarationKinds: ['class', 'interface', 'typeAlias'],
|
|
1895
|
+
expansionMode: 'augment',
|
|
1896
|
+
expand(ctx, decoded) {
|
|
1897
|
+
if (decoded.caseName === 'typeAlias') {
|
|
1898
|
+
const declaration = decoded.args.target;
|
|
1899
|
+
if (!declaration.type.asObjectLiteral() && declaration.type.asUnion()) {
|
|
1900
|
+
const { discriminantName, typeName, variants } = collectTaggedDerivedVariants(ctx, declaration, 'decode', (field, ownerTypeName, scopeNode) => decodeFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode));
|
|
1901
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
1902
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
1903
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
1904
|
+
const decodeLiteral = ctx.runtime.named('sts:decode', 'literal').text();
|
|
1905
|
+
const decodeUnion = ctx.runtime.named('sts:decode', 'union').text();
|
|
1906
|
+
const variantDecoders = variants.map((variant) => taggedDecodeVariantText(discriminantName, variant, decodeLiteral, decodeMap, decodeObject, decodeOptional));
|
|
1907
|
+
const unionText = foldUnionText(decodeUnion, variantDecoders);
|
|
1908
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
1909
|
+
export const ${`${typeName}Decoder`} = ${unionText};
|
|
1910
|
+
`);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
const { fields, instantiateText, typeName } = collectDecodeFields(ctx, decoded);
|
|
1914
|
+
const object = ctx.runtime.named('sts:decode', 'object').text();
|
|
1915
|
+
const map = ctx.runtime.named('sts:decode', 'map').text();
|
|
1916
|
+
const optional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
1917
|
+
const shapeText = fields.length === 0 ? '{}' : `{
|
|
1918
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${optional}(${field.decoderText})` : field.decoderText}`).join(',\n')}
|
|
1919
|
+
}`;
|
|
1920
|
+
const projectionText = fields.length === 0 ? '{}' : `({
|
|
1921
|
+
${fields.map((field) => `${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`).join(',\n')}
|
|
1922
|
+
})`;
|
|
1923
|
+
const finalProjectionText = instantiateText === null
|
|
1924
|
+
? projectionText
|
|
1925
|
+
: `(${instantiateText.replace(CLASS_DECODE_VALUE_PLACEHOLDER, projectionText)})`;
|
|
1926
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
1927
|
+
export const ${`${typeName}Decoder`} = ${map}(
|
|
1928
|
+
${object}(${shapeText}),
|
|
1929
|
+
(value) => ${finalProjectionText},
|
|
1930
|
+
);
|
|
1931
|
+
`);
|
|
1932
|
+
},
|
|
1933
|
+
signature: DECODE_SIGNATURE,
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
attachDeriveFactory(decode);
|
|
1937
|
+
// #[macro(decl)]
|
|
1938
|
+
export function encode() {
|
|
1939
|
+
return {
|
|
1940
|
+
declarationKinds: ['class', 'interface', 'typeAlias'],
|
|
1941
|
+
expansionMode: 'augment',
|
|
1942
|
+
expand(ctx, decoded) {
|
|
1943
|
+
if (decoded.caseName === 'typeAlias') {
|
|
1944
|
+
const declaration = decoded.args.target;
|
|
1945
|
+
if (!declaration.type.asObjectLiteral() && declaration.type.asUnion()) {
|
|
1946
|
+
const { discriminantName, typeName, variants } = collectTaggedDerivedVariants(ctx, declaration, 'encode', (field, ownerTypeName, scopeNode) => encodeFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode));
|
|
1947
|
+
const encodeFromEncode = ctx.runtime.named('sts:encode', 'fromEncode').text();
|
|
1948
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
1949
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
1950
|
+
const stringEncoder = ctx.runtime.named('sts:encode', 'stringEncoder').text();
|
|
1951
|
+
const switchCases = variants.map((variant) => {
|
|
1952
|
+
const variantType = taggedVariantTypeText(typeName, discriminantName, variant.tag);
|
|
1953
|
+
const shapeText = taggedEncodeVariantShapeText(discriminantName, variant, encodeOptional, stringEncoder);
|
|
1954
|
+
const projectionText = taggedEncodeVariantProjectionText(discriminantName, variant, `value as ${variantType}`);
|
|
1955
|
+
return `case '${variant.tag}':
|
|
1956
|
+
return ${encodeObject}(${shapeText}).encode(${projectionText});`;
|
|
1957
|
+
}).join('\n');
|
|
1958
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
1959
|
+
export const ${`${typeName}Encoder`} = ${encodeFromEncode}((value: ${typeName}) => {
|
|
1960
|
+
switch (${propertyAccessText('value', discriminantName)}) {
|
|
1961
|
+
${switchCases}
|
|
1962
|
+
default:
|
|
1963
|
+
throw new Error('unreachable tagged union encoder case');
|
|
1964
|
+
}
|
|
1965
|
+
});
|
|
1966
|
+
`);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
const { fields, typeName } = collectEncodeFields(ctx, decoded);
|
|
1970
|
+
const contramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
1971
|
+
const object = ctx.runtime.named('sts:encode', 'object').text();
|
|
1972
|
+
const optional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
1973
|
+
const shapeText = fields.length === 0 ? '{}' : `{
|
|
1974
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${optional}(${field.encoderText})` : field.encoderText}`).join(',\n')}
|
|
1975
|
+
}`;
|
|
1976
|
+
const projectionText = fields.length === 0 ? '({})' : `({
|
|
1977
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`).join(',\n')}
|
|
1978
|
+
})`;
|
|
1979
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
1980
|
+
export const ${`${typeName}Encoder`} = ${contramap}(
|
|
1981
|
+
${object}(${shapeText}),
|
|
1982
|
+
(value: ${typeName}) => ${projectionText},
|
|
1983
|
+
);
|
|
1984
|
+
`);
|
|
1985
|
+
},
|
|
1986
|
+
signature: DECODE_SIGNATURE,
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
attachDeriveFactory(encode);
|
|
1990
|
+
// #[macro(decl)]
|
|
1991
|
+
export function codec() {
|
|
1992
|
+
return {
|
|
1993
|
+
declarationKinds: ['class', 'interface', 'typeAlias'],
|
|
1994
|
+
expansionMode: 'augment',
|
|
1995
|
+
expand(ctx, decoded) {
|
|
1996
|
+
if (decoded.caseName === 'typeAlias') {
|
|
1997
|
+
const declaration = decoded.args.target;
|
|
1998
|
+
if (!declaration.type.asObjectLiteral() && declaration.type.asUnion()) {
|
|
1999
|
+
const { discriminantName, typeName, variants } = collectTaggedDerivedVariants(ctx, declaration, 'codec', (field, ownerTypeName, scopeNode) => codecFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode));
|
|
2000
|
+
const createCodec = ctx.runtime.named('sts:codec', 'codec').text();
|
|
2001
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
2002
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
2003
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
2004
|
+
const decodeLiteral = ctx.runtime.named('sts:decode', 'literal').text();
|
|
2005
|
+
const decodeUnion = ctx.runtime.named('sts:decode', 'union').text();
|
|
2006
|
+
const encodeFromEncode = ctx.runtime.named('sts:encode', 'fromEncode').text();
|
|
2007
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
2008
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
2009
|
+
const stringEncoder = ctx.runtime.named('sts:encode', 'stringEncoder').text();
|
|
2010
|
+
const variantDecoders = variants.map((variant) => taggedCodecDecodeVariantText(discriminantName, variant, decodeLiteral, decodeMap, decodeObject, decodeOptional));
|
|
2011
|
+
const unionText = foldUnionText(decodeUnion, variantDecoders);
|
|
2012
|
+
const switchCases = variants.map((variant) => {
|
|
2013
|
+
const variantType = taggedVariantTypeText(typeName, discriminantName, variant.tag);
|
|
2014
|
+
const shapeText = taggedCodecEncodeVariantShapeText(discriminantName, variant, encodeOptional, stringEncoder);
|
|
2015
|
+
const projectionText = taggedCodecEncodeVariantProjectionText(discriminantName, variant, `value as ${variantType}`);
|
|
2016
|
+
return `case '${variant.tag}':
|
|
2017
|
+
return ${encodeObject}(${shapeText}).encode(${projectionText});`;
|
|
2018
|
+
}).join('\n');
|
|
2019
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
2020
|
+
export const ${`${typeName}Codec`} = ${createCodec}(
|
|
2021
|
+
${unionText},
|
|
2022
|
+
${encodeFromEncode}((value: ${typeName}) => {
|
|
2023
|
+
switch (${propertyAccessText('value', discriminantName)}) {
|
|
2024
|
+
${switchCases}
|
|
2025
|
+
default:
|
|
2026
|
+
throw new Error('unreachable tagged union codec case');
|
|
2027
|
+
}
|
|
2028
|
+
}),
|
|
2029
|
+
);
|
|
2030
|
+
`);
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
const { fields, instantiateText, typeName } = collectCodecFields(ctx, decoded);
|
|
2034
|
+
const codec = ctx.runtime.named('sts:codec', 'codec').text();
|
|
2035
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
2036
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
2037
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
2038
|
+
const encodeContramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
2039
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
2040
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
2041
|
+
const decodeShapeText = fields.length === 0 ? '{}' : `{
|
|
2042
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${decodeOptional}(${field.decodeText})` : field.decodeText}`).join(',\n')}
|
|
2043
|
+
}`;
|
|
2044
|
+
const decodeProjectionText = fields.length === 0 ? '{}' : `({
|
|
2045
|
+
${fields.map((field) => `${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`).join(',\n')}
|
|
2046
|
+
})`;
|
|
2047
|
+
const finalDecodeProjectionText = instantiateText === null
|
|
2048
|
+
? decodeProjectionText
|
|
2049
|
+
: `(${instantiateText.replace(CLASS_DECODE_VALUE_PLACEHOLDER, decodeProjectionText)})`;
|
|
2050
|
+
const encodeShapeText = fields.length === 0 ? '{}' : `{
|
|
2051
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${field.optional ? `${encodeOptional}(${field.encodeText})` : field.encodeText}`).join(',\n')}
|
|
2052
|
+
}`;
|
|
2053
|
+
const encodeProjectionText = fields.length === 0 ? '({})' : `({
|
|
2054
|
+
${fields.map((field) => `${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`).join(',\n')}
|
|
2055
|
+
})`;
|
|
2056
|
+
return ctx.output.stmt(ctx.quote.stmt `
|
|
2057
|
+
export const ${`${typeName}Codec`} = ${codec}(
|
|
2058
|
+
${decodeMap}(
|
|
2059
|
+
${decodeObject}(${decodeShapeText}),
|
|
2060
|
+
(value) => ${finalDecodeProjectionText},
|
|
2061
|
+
),
|
|
2062
|
+
${encodeContramap}(
|
|
2063
|
+
${encodeObject}(${encodeShapeText}),
|
|
2064
|
+
(value: ${typeName}) => ${encodeProjectionText},
|
|
2065
|
+
),
|
|
2066
|
+
);
|
|
2067
|
+
`);
|
|
2068
|
+
},
|
|
2069
|
+
signature: DECODE_SIGNATURE,
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
attachDeriveFactory(codec);
|