@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,3188 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
import { createAnnotationLookup } from '../annotation_syntax.ts';
|
|
4
|
+
import { fromFileUrl } from '../platform/path.ts';
|
|
5
|
+
import type {
|
|
6
|
+
MacroAnnotation,
|
|
7
|
+
MacroClassDeclSyntax,
|
|
8
|
+
MacroClassFieldSyntax,
|
|
9
|
+
MacroDefinition,
|
|
10
|
+
MacroInterfaceDeclSyntax,
|
|
11
|
+
MacroObjectTypeMemberSyntax,
|
|
12
|
+
MacroObjectTypeSyntax,
|
|
13
|
+
MacroReflectedDeclarationShape,
|
|
14
|
+
MacroReflectedFieldShape,
|
|
15
|
+
MacroReflectedTypeShape,
|
|
16
|
+
MacroSyntaxNode,
|
|
17
|
+
MacroTypeAliasDeclSyntax,
|
|
18
|
+
} from './macro_api.ts';
|
|
19
|
+
import { macroSignature } from './macro_api.ts';
|
|
20
|
+
import { attachMacroFactoryMetadata } from './macro_api_internal.ts';
|
|
21
|
+
import { getHostDeclaration, getHostNode } from './macro_syntax_internal.ts';
|
|
22
|
+
|
|
23
|
+
const DERIVE_MACRO_FILE_NAME = fromFileUrl(import.meta.url);
|
|
24
|
+
const DERIVE_SIGNATURE = macroSignature.oneOf(
|
|
25
|
+
macroSignature.case('class', macroSignature.classDecl('target')),
|
|
26
|
+
macroSignature.case('interface', macroSignature.interfaceDecl('target')),
|
|
27
|
+
macroSignature.case('typeAlias', macroSignature.typeAliasDecl('target')),
|
|
28
|
+
);
|
|
29
|
+
const DECODE_SIGNATURE = macroSignature.oneOf(
|
|
30
|
+
macroSignature.case('class', macroSignature.classDecl('target')),
|
|
31
|
+
macroSignature.case('interface', macroSignature.interfaceDecl('target')),
|
|
32
|
+
macroSignature.case('typeAlias', macroSignature.typeAliasDecl('target')),
|
|
33
|
+
);
|
|
34
|
+
const TAGGED_SIGNATURE = macroSignature.of(macroSignature.typeAliasDecl('target'));
|
|
35
|
+
type DeriveContext = Parameters<MacroDefinition<typeof DERIVE_SIGNATURE>['expand']>[0];
|
|
36
|
+
|
|
37
|
+
type PrimitiveFieldKind = 'bigint' | 'boolean' | 'number' | 'string';
|
|
38
|
+
type DerivedMacroName = 'codec' | 'decode' | 'encode' | 'eq' | 'hash';
|
|
39
|
+
type SupportedDerivedType =
|
|
40
|
+
| { readonly kind: 'array'; readonly element: SupportedDerivedType }
|
|
41
|
+
| { readonly kind: 'named'; readonly typeName: string }
|
|
42
|
+
| { readonly kind: 'option'; readonly value: SupportedDerivedType }
|
|
43
|
+
| { readonly kind: 'primitive'; readonly primitiveKind: PrimitiveFieldKind }
|
|
44
|
+
| {
|
|
45
|
+
readonly err: SupportedDerivedType;
|
|
46
|
+
readonly kind: 'result';
|
|
47
|
+
readonly ok: SupportedDerivedType;
|
|
48
|
+
}
|
|
49
|
+
| { readonly elements: readonly SupportedDerivedType[]; readonly kind: 'tuple' };
|
|
50
|
+
|
|
51
|
+
interface DerivedField {
|
|
52
|
+
readonly eqHelper: string;
|
|
53
|
+
readonly hashHelper: string;
|
|
54
|
+
readonly name: string;
|
|
55
|
+
readonly optional: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface TaggedVariantField {
|
|
59
|
+
readonly name: string;
|
|
60
|
+
readonly optional: boolean;
|
|
61
|
+
readonly typeText: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface TaggedVariant {
|
|
65
|
+
readonly constructorName: string;
|
|
66
|
+
readonly constructorTypeParametersText: string;
|
|
67
|
+
readonly kind: 'class' | 'object';
|
|
68
|
+
readonly payloadFields: readonly TaggedVariantField[];
|
|
69
|
+
readonly predicateName: string;
|
|
70
|
+
readonly predicateConditionText: string;
|
|
71
|
+
readonly predicateNarrowTypeText: string;
|
|
72
|
+
readonly predicateTypeParametersText: string;
|
|
73
|
+
readonly predicateValueTypeText: string;
|
|
74
|
+
readonly returnExpressionText: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface DecodedField {
|
|
78
|
+
readonly decoderText: string;
|
|
79
|
+
readonly localName: string;
|
|
80
|
+
readonly optional: boolean;
|
|
81
|
+
readonly wireName: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface EncodedField {
|
|
85
|
+
readonly encoderText: string;
|
|
86
|
+
readonly localName: string;
|
|
87
|
+
readonly optional: boolean;
|
|
88
|
+
readonly wireName: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface CodecField {
|
|
92
|
+
readonly decodeText: string;
|
|
93
|
+
readonly encodeText: string;
|
|
94
|
+
readonly localName: string;
|
|
95
|
+
readonly optional: boolean;
|
|
96
|
+
readonly wireName: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface TaggedDerivedVariant<TField> {
|
|
100
|
+
readonly fields: readonly TField[];
|
|
101
|
+
readonly tag: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const CLASS_DECODE_VALUE_PLACEHOLDER = '__sts_decoded_value__';
|
|
105
|
+
|
|
106
|
+
function attachDeriveFactory<T extends () => MacroDefinition>(factory: T): T {
|
|
107
|
+
return attachMacroFactoryMetadata(factory, {
|
|
108
|
+
form: 'decl',
|
|
109
|
+
moduleFileName: DERIVE_MACRO_FILE_NAME,
|
|
110
|
+
}) as T;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function findAnnotation(
|
|
114
|
+
annotations: readonly MacroAnnotation[],
|
|
115
|
+
name: string,
|
|
116
|
+
): MacroAnnotation | null {
|
|
117
|
+
return annotations.find((annotation) => annotation.name === name) ?? null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function annotationIdentifierArgument(annotation: MacroAnnotation): string | null {
|
|
121
|
+
const [firstArgument] = annotation.arguments ?? [];
|
|
122
|
+
return firstArgument?.value.kind === 'identifier' ? firstArgument.value.name : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function annotationStringArgument(annotation: MacroAnnotation): string | null {
|
|
126
|
+
const [firstArgument] = annotation.arguments ?? [];
|
|
127
|
+
return firstArgument?.value.kind === 'string' ? firstArgument.value.value : null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function annotationDiagnosticNode(
|
|
131
|
+
declaration: MacroClassDeclSyntax,
|
|
132
|
+
annotation: MacroAnnotation,
|
|
133
|
+
): MacroSyntaxNode | null {
|
|
134
|
+
const hostDeclaration = getHostDeclaration(declaration);
|
|
135
|
+
const sourceFile = hostDeclaration.getSourceFile();
|
|
136
|
+
const parsedAnnotation = createAnnotationLookup(sourceFile)
|
|
137
|
+
.getAttachedAnnotations(hostDeclaration)
|
|
138
|
+
.find((attached) => attached.name === annotation.name && attached.text === annotation.text);
|
|
139
|
+
if (!parsedAnnotation?.range) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
kind: 'annotation',
|
|
145
|
+
span: {
|
|
146
|
+
fileName: sourceFile.fileName,
|
|
147
|
+
start: parsedAnnotation.range.start,
|
|
148
|
+
end: parsedAnnotation.range.end,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function propertyAccessText(receiverName: string, propertyName: string): string {
|
|
154
|
+
const receiver = /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(receiverName.trim())
|
|
155
|
+
? receiverName.trim()
|
|
156
|
+
: `(${receiverName.trim()})`;
|
|
157
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(propertyName)
|
|
158
|
+
? `${receiver}.${propertyName}`
|
|
159
|
+
: `${receiver}[${JSON.stringify(propertyName)}]`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function propertyKeyText(propertyName: string): string {
|
|
163
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(propertyName)
|
|
164
|
+
? propertyName
|
|
165
|
+
: JSON.stringify(propertyName);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function primitiveKindForTypeText(typeText: string): PrimitiveFieldKind | null {
|
|
169
|
+
const normalized = typeText.trim();
|
|
170
|
+
switch (normalized) {
|
|
171
|
+
case 'string':
|
|
172
|
+
case 'number':
|
|
173
|
+
case 'boolean':
|
|
174
|
+
case 'bigint':
|
|
175
|
+
return normalized as PrimitiveFieldKind;
|
|
176
|
+
default:
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function parseGenericTypeArgument(text: string, typeName: string): string | null {
|
|
182
|
+
const parsed = parseGenericTypeArguments(text, typeName);
|
|
183
|
+
return parsed?.length === 1 ? parsed[0]! : null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function parseGenericTypeArguments(text: string, typeName: string): readonly string[] | null {
|
|
187
|
+
const trimmed = text.trim();
|
|
188
|
+
const prefix = `${typeName}<`;
|
|
189
|
+
if (!trimmed.startsWith(prefix) || !trimmed.endsWith('>')) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let angleDepth = 0;
|
|
194
|
+
let braceDepth = 0;
|
|
195
|
+
let bracketDepth = 0;
|
|
196
|
+
let parenDepth = 0;
|
|
197
|
+
let inString: '"' | "'" | '`' | null = null;
|
|
198
|
+
let escaped = false;
|
|
199
|
+
let segmentStart = prefix.length;
|
|
200
|
+
const args: string[] = [];
|
|
201
|
+
|
|
202
|
+
for (let index = typeName.length; index < trimmed.length; index += 1) {
|
|
203
|
+
const char = trimmed[index]!;
|
|
204
|
+
|
|
205
|
+
if (inString !== null) {
|
|
206
|
+
if (escaped) {
|
|
207
|
+
escaped = false;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (char === '\\') {
|
|
211
|
+
escaped = true;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (char === inString) {
|
|
215
|
+
inString = null;
|
|
216
|
+
}
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (char === '"' || char === "'" || char === '`') {
|
|
221
|
+
inString = char;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
switch (char) {
|
|
226
|
+
case '<':
|
|
227
|
+
angleDepth += 1;
|
|
228
|
+
continue;
|
|
229
|
+
case '>':
|
|
230
|
+
angleDepth -= 1;
|
|
231
|
+
if (angleDepth < 0) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
if (angleDepth === 0) {
|
|
235
|
+
if (index !== trimmed.length - 1) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
const segment = trimmed.slice(segmentStart, index).trim();
|
|
239
|
+
if (segment.length === 0) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
args.push(segment);
|
|
243
|
+
}
|
|
244
|
+
continue;
|
|
245
|
+
case '{':
|
|
246
|
+
braceDepth += 1;
|
|
247
|
+
continue;
|
|
248
|
+
case '}':
|
|
249
|
+
braceDepth -= 1;
|
|
250
|
+
if (braceDepth < 0) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
continue;
|
|
254
|
+
case '[':
|
|
255
|
+
bracketDepth += 1;
|
|
256
|
+
continue;
|
|
257
|
+
case ']':
|
|
258
|
+
bracketDepth -= 1;
|
|
259
|
+
if (bracketDepth < 0) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
continue;
|
|
263
|
+
case '(':
|
|
264
|
+
parenDepth += 1;
|
|
265
|
+
continue;
|
|
266
|
+
case ')':
|
|
267
|
+
parenDepth -= 1;
|
|
268
|
+
if (parenDepth < 0) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
continue;
|
|
272
|
+
case ',':
|
|
273
|
+
if (angleDepth === 1 && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
|
|
274
|
+
const segment = trimmed.slice(segmentStart, index).trim();
|
|
275
|
+
if (segment.length === 0) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
args.push(segment);
|
|
279
|
+
segmentStart = index + 1;
|
|
280
|
+
}
|
|
281
|
+
continue;
|
|
282
|
+
default:
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (
|
|
288
|
+
angleDepth !== 0 || braceDepth !== 0 || bracketDepth !== 0 || parenDepth !== 0 ||
|
|
289
|
+
inString !== null
|
|
290
|
+
) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return args;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function parseArrayInnerTypeText(typeText: string): string | null {
|
|
298
|
+
const trimmed = typeText.trim();
|
|
299
|
+
if (trimmed.startsWith('readonly ') && trimmed.endsWith('[]')) {
|
|
300
|
+
return trimmed.slice('readonly '.length, -2).trim();
|
|
301
|
+
}
|
|
302
|
+
if (trimmed.endsWith('[]')) {
|
|
303
|
+
return trimmed.slice(0, -2).trim();
|
|
304
|
+
}
|
|
305
|
+
return parseGenericTypeArgument(trimmed, 'Array') ??
|
|
306
|
+
parseGenericTypeArgument(trimmed, 'ReadonlyArray');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function parseTupleElementTypeTexts(typeText: string): readonly string[] | null {
|
|
310
|
+
const trimmed = typeText.trim();
|
|
311
|
+
const tupleText = trimmed.startsWith('readonly [') ? trimmed.slice('readonly '.length) : trimmed;
|
|
312
|
+
if (!tupleText.startsWith('[') || !tupleText.endsWith(']')) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const body = tupleText.slice(1, -1).trim();
|
|
317
|
+
if (body.length === 0) {
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
let angleDepth = 0;
|
|
322
|
+
let braceDepth = 0;
|
|
323
|
+
let bracketDepth = 0;
|
|
324
|
+
let parenDepth = 0;
|
|
325
|
+
let inString: '"' | "'" | '`' | null = null;
|
|
326
|
+
let escaped = false;
|
|
327
|
+
let segmentStart = 0;
|
|
328
|
+
const elements: string[] = [];
|
|
329
|
+
|
|
330
|
+
for (let index = 0; index < body.length; index += 1) {
|
|
331
|
+
const char = body[index]!;
|
|
332
|
+
|
|
333
|
+
if (inString !== null) {
|
|
334
|
+
if (escaped) {
|
|
335
|
+
escaped = false;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (char === '\\') {
|
|
339
|
+
escaped = true;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (char === inString) {
|
|
343
|
+
inString = null;
|
|
344
|
+
}
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (char === '"' || char === "'" || char === '`') {
|
|
349
|
+
inString = char;
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
switch (char) {
|
|
354
|
+
case '<':
|
|
355
|
+
angleDepth += 1;
|
|
356
|
+
continue;
|
|
357
|
+
case '>':
|
|
358
|
+
angleDepth -= 1;
|
|
359
|
+
if (angleDepth < 0) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
continue;
|
|
363
|
+
case '{':
|
|
364
|
+
braceDepth += 1;
|
|
365
|
+
continue;
|
|
366
|
+
case '}':
|
|
367
|
+
braceDepth -= 1;
|
|
368
|
+
if (braceDepth < 0) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
continue;
|
|
372
|
+
case '[':
|
|
373
|
+
bracketDepth += 1;
|
|
374
|
+
continue;
|
|
375
|
+
case ']':
|
|
376
|
+
bracketDepth -= 1;
|
|
377
|
+
if (bracketDepth < 0) {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
continue;
|
|
381
|
+
case '(':
|
|
382
|
+
parenDepth += 1;
|
|
383
|
+
continue;
|
|
384
|
+
case ')':
|
|
385
|
+
parenDepth -= 1;
|
|
386
|
+
if (parenDepth < 0) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
continue;
|
|
390
|
+
case ',':
|
|
391
|
+
if (angleDepth === 0 && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
|
|
392
|
+
const segment = body.slice(segmentStart, index).trim();
|
|
393
|
+
if (segment.length === 0) {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
elements.push(segment);
|
|
397
|
+
segmentStart = index + 1;
|
|
398
|
+
}
|
|
399
|
+
continue;
|
|
400
|
+
default:
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (
|
|
406
|
+
angleDepth !== 0 || braceDepth !== 0 || bracketDepth !== 0 || parenDepth !== 0 ||
|
|
407
|
+
inString !== null
|
|
408
|
+
) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const finalSegment = body.slice(segmentStart).trim();
|
|
413
|
+
if (finalSegment.length === 0) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
elements.push(finalSegment);
|
|
417
|
+
return elements;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function parseSupportedDerivedType(typeText: string): SupportedDerivedType | null {
|
|
421
|
+
const primitiveKind = primitiveKindForTypeText(typeText);
|
|
422
|
+
if (primitiveKind) {
|
|
423
|
+
return { kind: 'primitive', primitiveKind };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const arrayInner = parseArrayInnerTypeText(typeText);
|
|
427
|
+
if (arrayInner !== null) {
|
|
428
|
+
const element = parseSupportedDerivedType(arrayInner);
|
|
429
|
+
return element ? { kind: 'array', element } : null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const tupleElements = parseTupleElementTypeTexts(typeText);
|
|
433
|
+
if (tupleElements !== null) {
|
|
434
|
+
const elements = tupleElements.map((element) => parseSupportedDerivedType(element));
|
|
435
|
+
return elements.every((element) => element !== null)
|
|
436
|
+
? { kind: 'tuple', elements: elements as readonly SupportedDerivedType[] }
|
|
437
|
+
: null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const optionInner = parseGenericTypeArgument(typeText, 'Option');
|
|
441
|
+
if (optionInner !== null) {
|
|
442
|
+
const value = parseSupportedDerivedType(optionInner);
|
|
443
|
+
return value ? { kind: 'option', value } : null;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const resultArgs = parseGenericTypeArguments(typeText, 'Result');
|
|
447
|
+
if (resultArgs?.length === 2) {
|
|
448
|
+
const okType = parseSupportedDerivedType(resultArgs[0]!);
|
|
449
|
+
const errType = parseSupportedDerivedType(resultArgs[1]!);
|
|
450
|
+
return okType && errType ? { kind: 'result', ok: okType, err: errType } : null;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const trimmed = typeText.trim();
|
|
454
|
+
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(trimmed)) {
|
|
455
|
+
return { kind: 'named', typeName: trimmed };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function supportedDerivedTypeFromShape(
|
|
462
|
+
shape: MacroReflectedTypeShape,
|
|
463
|
+
): SupportedDerivedType | null {
|
|
464
|
+
switch (shape.kind) {
|
|
465
|
+
case 'primitive':
|
|
466
|
+
return {
|
|
467
|
+
kind: 'primitive',
|
|
468
|
+
primitiveKind: shape.primitiveKind,
|
|
469
|
+
};
|
|
470
|
+
case 'named':
|
|
471
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(shape.name)
|
|
472
|
+
? { kind: 'named', typeName: shape.name }
|
|
473
|
+
: null;
|
|
474
|
+
case 'array': {
|
|
475
|
+
const element = supportedDerivedTypeFromShape(shape.element);
|
|
476
|
+
return element ? { kind: 'array', element } : null;
|
|
477
|
+
}
|
|
478
|
+
case 'tuple': {
|
|
479
|
+
const elements = shape.elements.map((element) => supportedDerivedTypeFromShape(element));
|
|
480
|
+
return elements.every((element) => element !== null)
|
|
481
|
+
? { kind: 'tuple', elements: elements as readonly SupportedDerivedType[] }
|
|
482
|
+
: null;
|
|
483
|
+
}
|
|
484
|
+
case 'option': {
|
|
485
|
+
const value = supportedDerivedTypeFromShape(shape.value);
|
|
486
|
+
return value ? { kind: 'option', value } : null;
|
|
487
|
+
}
|
|
488
|
+
case 'result': {
|
|
489
|
+
const ok = supportedDerivedTypeFromShape(shape.ok);
|
|
490
|
+
const err = supportedDerivedTypeFromShape(shape.err);
|
|
491
|
+
return ok && err ? { kind: 'result', ok, err } : null;
|
|
492
|
+
}
|
|
493
|
+
case 'object':
|
|
494
|
+
case 'literal':
|
|
495
|
+
case 'union':
|
|
496
|
+
case 'unsupported':
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function objectLikeDeclarationShape(
|
|
502
|
+
ctx: DeriveContext,
|
|
503
|
+
declaration: MacroClassDeclSyntax | MacroInterfaceDeclSyntax | MacroTypeAliasDeclSyntax,
|
|
504
|
+
macroName: DerivedMacroName,
|
|
505
|
+
): Extract<MacroReflectedDeclarationShape, { kind: 'objectLike' }> {
|
|
506
|
+
const shape = ctx.reflect.declarationShape(declaration);
|
|
507
|
+
if (shape.kind === 'objectLike') {
|
|
508
|
+
return shape;
|
|
509
|
+
}
|
|
510
|
+
ctx.error(`${macroName} currently only supports object-like declarations in v1.`, declaration);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function discriminatedUnionDeclarationShape(
|
|
514
|
+
ctx: DeriveContext,
|
|
515
|
+
declaration: MacroTypeAliasDeclSyntax,
|
|
516
|
+
macroName: DerivedMacroName | 'tagged',
|
|
517
|
+
): Extract<MacroReflectedDeclarationShape, { kind: 'discriminatedUnion' }> {
|
|
518
|
+
const shape = ctx.reflect.declarationShape(declaration);
|
|
519
|
+
if (shape.kind === 'discriminatedUnion') {
|
|
520
|
+
return shape;
|
|
521
|
+
}
|
|
522
|
+
ctx.error(
|
|
523
|
+
`${macroName} only supports // #[tagged] unions of object-like variants in v1.`,
|
|
524
|
+
declaration,
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function eqHashUnsupportedFieldMessage(macroName: 'eq' | 'hash'): string {
|
|
529
|
+
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].`;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function decodeLikeUnsupportedFieldMessage(
|
|
533
|
+
macroName: 'codec' | 'decode' | 'encode',
|
|
534
|
+
): string {
|
|
535
|
+
switch (macroName) {
|
|
536
|
+
case 'decode':
|
|
537
|
+
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.';
|
|
538
|
+
case 'encode':
|
|
539
|
+
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.';
|
|
540
|
+
case 'codec':
|
|
541
|
+
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.';
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function compareHelperName(kind: PrimitiveFieldKind): string {
|
|
546
|
+
switch (kind) {
|
|
547
|
+
case 'string':
|
|
548
|
+
return 'stringEq';
|
|
549
|
+
case 'number':
|
|
550
|
+
return 'numberEq';
|
|
551
|
+
case 'boolean':
|
|
552
|
+
return 'booleanEq';
|
|
553
|
+
case 'bigint':
|
|
554
|
+
return 'bigintEq';
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function hashHelperName(kind: PrimitiveFieldKind): string {
|
|
559
|
+
switch (kind) {
|
|
560
|
+
case 'string':
|
|
561
|
+
return 'stringHash';
|
|
562
|
+
case 'number':
|
|
563
|
+
return 'numberHash';
|
|
564
|
+
case 'boolean':
|
|
565
|
+
return 'booleanHash';
|
|
566
|
+
case 'bigint':
|
|
567
|
+
return 'bigintHash';
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function decodeHelperName(kind: PrimitiveFieldKind): string {
|
|
572
|
+
switch (kind) {
|
|
573
|
+
case 'string':
|
|
574
|
+
return 'string';
|
|
575
|
+
case 'number':
|
|
576
|
+
return 'number';
|
|
577
|
+
case 'boolean':
|
|
578
|
+
return 'boolean';
|
|
579
|
+
case 'bigint':
|
|
580
|
+
return 'bigint';
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function encodeHelperName(kind: PrimitiveFieldKind): string {
|
|
585
|
+
switch (kind) {
|
|
586
|
+
case 'string':
|
|
587
|
+
return 'stringEncoder';
|
|
588
|
+
case 'number':
|
|
589
|
+
return 'numberEncoder';
|
|
590
|
+
case 'boolean':
|
|
591
|
+
return 'booleanEncoder';
|
|
592
|
+
case 'bigint':
|
|
593
|
+
return 'bigintEncoder';
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function eqHelperTextForType(ctx: DeriveContext, type: SupportedDerivedType): string {
|
|
598
|
+
switch (type.kind) {
|
|
599
|
+
case 'primitive':
|
|
600
|
+
return ctx.runtime.named('sts:compare', compareHelperName(type.primitiveKind)).text();
|
|
601
|
+
case 'named':
|
|
602
|
+
return `${ctx.runtime.named('sts:compare', 'lazyEq').text()}(() => ${type.typeName}Eq)`;
|
|
603
|
+
case 'array':
|
|
604
|
+
return `${ctx.runtime.named('sts:compare', 'arrayEq').text()}(${
|
|
605
|
+
eqHelperTextForType(ctx, type.element)
|
|
606
|
+
})`;
|
|
607
|
+
case 'tuple':
|
|
608
|
+
return `${ctx.runtime.named('sts:compare', 'tupleEq').text()}(${
|
|
609
|
+
type.elements.map((element) => eqHelperTextForType(ctx, element)).join(', ')
|
|
610
|
+
})`;
|
|
611
|
+
case 'option':
|
|
612
|
+
return `${ctx.runtime.named('sts:compare', 'optionEq').text()}(${
|
|
613
|
+
eqHelperTextForType(ctx, type.value)
|
|
614
|
+
})`;
|
|
615
|
+
case 'result':
|
|
616
|
+
return `${ctx.runtime.named('sts:compare', 'resultEq').text()}(${
|
|
617
|
+
eqHelperTextForType(ctx, type.ok)
|
|
618
|
+
}, ${eqHelperTextForType(ctx, type.err)})`;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function hashHelperTextForType(ctx: DeriveContext, type: SupportedDerivedType): string {
|
|
623
|
+
switch (type.kind) {
|
|
624
|
+
case 'primitive':
|
|
625
|
+
return ctx.runtime.named('sts:hash', hashHelperName(type.primitiveKind)).text();
|
|
626
|
+
case 'named':
|
|
627
|
+
return `${ctx.runtime.named('sts:hash', 'lazyHashEq').text()}(() => ${type.typeName}Hash)`;
|
|
628
|
+
case 'array':
|
|
629
|
+
return `${ctx.runtime.named('sts:hash', 'arrayHash').text()}(${
|
|
630
|
+
hashHelperTextForType(ctx, type.element)
|
|
631
|
+
})`;
|
|
632
|
+
case 'tuple':
|
|
633
|
+
return `${ctx.runtime.named('sts:hash', 'tupleHash').text()}(${
|
|
634
|
+
type.elements.map((element) => hashHelperTextForType(ctx, element)).join(', ')
|
|
635
|
+
})`;
|
|
636
|
+
case 'option':
|
|
637
|
+
return `${ctx.runtime.named('sts:hash', 'optionHash').text()}(${
|
|
638
|
+
hashHelperTextForType(ctx, type.value)
|
|
639
|
+
})`;
|
|
640
|
+
case 'result':
|
|
641
|
+
return `${ctx.runtime.named('sts:hash', 'resultHash').text()}(${
|
|
642
|
+
hashHelperTextForType(ctx, type.ok)
|
|
643
|
+
}, ${hashHelperTextForType(ctx, type.err)})`;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function decodeHelperTextForType(ctx: DeriveContext, type: SupportedDerivedType): string {
|
|
648
|
+
switch (type.kind) {
|
|
649
|
+
case 'primitive':
|
|
650
|
+
return ctx.runtime.named('sts:decode', decodeHelperName(type.primitiveKind)).text();
|
|
651
|
+
case 'named':
|
|
652
|
+
return `${ctx.runtime.named('sts:decode', 'lazy').text()}(() => ${type.typeName}Decoder)`;
|
|
653
|
+
case 'array':
|
|
654
|
+
return `${ctx.runtime.named('sts:decode', 'array').text()}(${
|
|
655
|
+
decodeHelperTextForType(ctx, type.element)
|
|
656
|
+
})`;
|
|
657
|
+
case 'tuple':
|
|
658
|
+
return `${ctx.runtime.named('sts:decode', 'tuple').text()}(${
|
|
659
|
+
type.elements.map((element) => decodeHelperTextForType(ctx, element)).join(', ')
|
|
660
|
+
})`;
|
|
661
|
+
case 'option':
|
|
662
|
+
return `${ctx.runtime.named('sts:decode', 'option').text()}(${
|
|
663
|
+
decodeHelperTextForType(ctx, type.value)
|
|
664
|
+
})`;
|
|
665
|
+
case 'result':
|
|
666
|
+
return `${ctx.runtime.named('sts:decode', 'result').text()}(${
|
|
667
|
+
decodeHelperTextForType(ctx, type.ok)
|
|
668
|
+
}, ${decodeHelperTextForType(ctx, type.err)})`;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function encodeHelperTextForType(ctx: DeriveContext, type: SupportedDerivedType): string {
|
|
673
|
+
switch (type.kind) {
|
|
674
|
+
case 'primitive':
|
|
675
|
+
return ctx.runtime.named('sts:encode', encodeHelperName(type.primitiveKind)).text();
|
|
676
|
+
case 'named':
|
|
677
|
+
return `${ctx.runtime.named('sts:encode', 'lazy').text()}(() => ${type.typeName}Encoder)`;
|
|
678
|
+
case 'array':
|
|
679
|
+
return `${ctx.runtime.named('sts:encode', 'array').text()}(${
|
|
680
|
+
encodeHelperTextForType(ctx, type.element)
|
|
681
|
+
})`;
|
|
682
|
+
case 'tuple':
|
|
683
|
+
return `${ctx.runtime.named('sts:encode', 'tuple').text()}(${
|
|
684
|
+
type.elements.map((element) => encodeHelperTextForType(ctx, element)).join(', ')
|
|
685
|
+
})`;
|
|
686
|
+
case 'option':
|
|
687
|
+
return `${ctx.runtime.named('sts:encode', 'option').text()}(${
|
|
688
|
+
encodeHelperTextForType(ctx, type.value)
|
|
689
|
+
})`;
|
|
690
|
+
case 'result':
|
|
691
|
+
return `${ctx.runtime.named('sts:encode', 'result').text()}(${
|
|
692
|
+
encodeHelperTextForType(ctx, type.ok)
|
|
693
|
+
}, ${encodeHelperTextForType(ctx, type.err)})`;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
function codecHelperTextsForType(
|
|
698
|
+
ctx: DeriveContext,
|
|
699
|
+
type: SupportedDerivedType,
|
|
700
|
+
): { readonly decodeText: string; readonly encodeText: string } {
|
|
701
|
+
switch (type.kind) {
|
|
702
|
+
case 'primitive':
|
|
703
|
+
return {
|
|
704
|
+
decodeText: ctx.runtime.named('sts:decode', decodeHelperName(type.primitiveKind)).text(),
|
|
705
|
+
encodeText: ctx.runtime.named('sts:encode', encodeHelperName(type.primitiveKind)).text(),
|
|
706
|
+
};
|
|
707
|
+
case 'named':
|
|
708
|
+
return {
|
|
709
|
+
decodeText: `${
|
|
710
|
+
ctx.runtime.named('sts:decode', 'lazy').text()
|
|
711
|
+
}(() => ${type.typeName}Codec)`,
|
|
712
|
+
encodeText: `${
|
|
713
|
+
ctx.runtime.named('sts:encode', 'lazy').text()
|
|
714
|
+
}(() => ${type.typeName}Codec)`,
|
|
715
|
+
};
|
|
716
|
+
case 'array': {
|
|
717
|
+
const element = codecHelperTextsForType(ctx, type.element);
|
|
718
|
+
return {
|
|
719
|
+
decodeText: `${ctx.runtime.named('sts:decode', 'array').text()}(${element.decodeText})`,
|
|
720
|
+
encodeText: `${ctx.runtime.named('sts:encode', 'array').text()}(${element.encodeText})`,
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
case 'tuple':
|
|
724
|
+
return {
|
|
725
|
+
decodeText: `${ctx.runtime.named('sts:decode', 'tuple').text()}(${
|
|
726
|
+
type.elements.map((element) => codecHelperTextsForType(ctx, element).decodeText).join(
|
|
727
|
+
', ',
|
|
728
|
+
)
|
|
729
|
+
})`,
|
|
730
|
+
encodeText: `${ctx.runtime.named('sts:encode', 'tuple').text()}(${
|
|
731
|
+
type.elements.map((element) => codecHelperTextsForType(ctx, element).encodeText).join(
|
|
732
|
+
', ',
|
|
733
|
+
)
|
|
734
|
+
})`,
|
|
735
|
+
};
|
|
736
|
+
case 'option': {
|
|
737
|
+
const value = codecHelperTextsForType(ctx, type.value);
|
|
738
|
+
return {
|
|
739
|
+
decodeText: `${ctx.runtime.named('sts:decode', 'option').text()}(${value.decodeText})`,
|
|
740
|
+
encodeText: `${ctx.runtime.named('sts:encode', 'option').text()}(${value.encodeText})`,
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
case 'result': {
|
|
744
|
+
const okType = codecHelperTextsForType(ctx, type.ok);
|
|
745
|
+
const errType = codecHelperTextsForType(ctx, type.err);
|
|
746
|
+
return {
|
|
747
|
+
decodeText: `${
|
|
748
|
+
ctx.runtime.named('sts:decode', 'result').text()
|
|
749
|
+
}(${okType.decodeText}, ${errType.decodeText})`,
|
|
750
|
+
encodeText: `${
|
|
751
|
+
ctx.runtime.named('sts:encode', 'result').text()
|
|
752
|
+
}(${okType.encodeText}, ${errType.encodeText})`,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function expectedCompanionName(typeName: string, macroName: DerivedMacroName): string {
|
|
759
|
+
switch (macroName) {
|
|
760
|
+
case 'eq':
|
|
761
|
+
return `${typeName}Eq`;
|
|
762
|
+
case 'hash':
|
|
763
|
+
return `${typeName}Hash`;
|
|
764
|
+
case 'decode':
|
|
765
|
+
return `${typeName}Decoder`;
|
|
766
|
+
case 'encode':
|
|
767
|
+
return `${typeName}Encoder`;
|
|
768
|
+
case 'codec':
|
|
769
|
+
return `${typeName}Codec`;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function missingNamedDerivedCompanionMessage(
|
|
774
|
+
macroName: DerivedMacroName,
|
|
775
|
+
companionName: string,
|
|
776
|
+
): string {
|
|
777
|
+
switch (macroName) {
|
|
778
|
+
case 'eq':
|
|
779
|
+
case 'hash':
|
|
780
|
+
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].`;
|
|
781
|
+
case 'decode':
|
|
782
|
+
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.`;
|
|
783
|
+
case 'encode':
|
|
784
|
+
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.`;
|
|
785
|
+
case 'codec':
|
|
786
|
+
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.`;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function assertNamedDerivedCompanionsInScope(
|
|
791
|
+
ctx: DeriveContext,
|
|
792
|
+
macroName: DerivedMacroName,
|
|
793
|
+
ownerTypeName: string,
|
|
794
|
+
scopeNode: MacroSyntaxNode,
|
|
795
|
+
errorNode: MacroSyntaxNode,
|
|
796
|
+
type: SupportedDerivedType,
|
|
797
|
+
): void {
|
|
798
|
+
switch (type.kind) {
|
|
799
|
+
case 'primitive':
|
|
800
|
+
return;
|
|
801
|
+
case 'array':
|
|
802
|
+
assertNamedDerivedCompanionsInScope(
|
|
803
|
+
ctx,
|
|
804
|
+
macroName,
|
|
805
|
+
ownerTypeName,
|
|
806
|
+
scopeNode,
|
|
807
|
+
errorNode,
|
|
808
|
+
type.element,
|
|
809
|
+
);
|
|
810
|
+
return;
|
|
811
|
+
case 'tuple':
|
|
812
|
+
for (const element of type.elements) {
|
|
813
|
+
assertNamedDerivedCompanionsInScope(
|
|
814
|
+
ctx,
|
|
815
|
+
macroName,
|
|
816
|
+
ownerTypeName,
|
|
817
|
+
scopeNode,
|
|
818
|
+
errorNode,
|
|
819
|
+
element,
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
return;
|
|
823
|
+
case 'option':
|
|
824
|
+
assertNamedDerivedCompanionsInScope(
|
|
825
|
+
ctx,
|
|
826
|
+
macroName,
|
|
827
|
+
ownerTypeName,
|
|
828
|
+
scopeNode,
|
|
829
|
+
errorNode,
|
|
830
|
+
type.value,
|
|
831
|
+
);
|
|
832
|
+
return;
|
|
833
|
+
case 'result':
|
|
834
|
+
assertNamedDerivedCompanionsInScope(
|
|
835
|
+
ctx,
|
|
836
|
+
macroName,
|
|
837
|
+
ownerTypeName,
|
|
838
|
+
scopeNode,
|
|
839
|
+
errorNode,
|
|
840
|
+
type.ok,
|
|
841
|
+
);
|
|
842
|
+
assertNamedDerivedCompanionsInScope(
|
|
843
|
+
ctx,
|
|
844
|
+
macroName,
|
|
845
|
+
ownerTypeName,
|
|
846
|
+
scopeNode,
|
|
847
|
+
errorNode,
|
|
848
|
+
type.err,
|
|
849
|
+
);
|
|
850
|
+
return;
|
|
851
|
+
case 'named': {
|
|
852
|
+
if (type.typeName === ownerTypeName) {
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
const companionName = expectedCompanionName(type.typeName, macroName);
|
|
856
|
+
if (
|
|
857
|
+
!ctx.semantics.valueBindingInScope(companionName) &&
|
|
858
|
+
!ctx.semantics.localDeclarationHasAnnotation(type.typeName, macroName)
|
|
859
|
+
) {
|
|
860
|
+
ctx.error(missingNamedDerivedCompanionMessage(macroName, companionName), errorNode);
|
|
861
|
+
}
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function eqHashFieldFromReflectedShape(
|
|
868
|
+
ctx: DeriveContext,
|
|
869
|
+
field: MacroReflectedFieldShape,
|
|
870
|
+
macroName: 'eq' | 'hash',
|
|
871
|
+
ownerTypeName: string,
|
|
872
|
+
scopeNode: MacroSyntaxNode,
|
|
873
|
+
): DerivedField | null {
|
|
874
|
+
const annotations = field.annotations;
|
|
875
|
+
if (findAnnotation(annotations, `${macroName}.skip`)) {
|
|
876
|
+
return null;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
const viaAnnotation = findAnnotation(annotations, `${macroName}.via`);
|
|
880
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
881
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
882
|
+
ctx.error(`${macroName}.via(...) requires a helper identifier.`, field.node);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
if (viaIdentifier) {
|
|
886
|
+
return {
|
|
887
|
+
eqHelper: viaIdentifier,
|
|
888
|
+
hashHelper: viaIdentifier,
|
|
889
|
+
name: field.name,
|
|
890
|
+
optional: field.optional,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (!field.type) {
|
|
895
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), field.node);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
if (field.type.kind === 'object') {
|
|
899
|
+
return {
|
|
900
|
+
eqHelper: macroName === 'eq'
|
|
901
|
+
? nestedEqHelperTextFromFields(ctx, ownerTypeName, scopeNode, field.type.fields)
|
|
902
|
+
: nestedHashHelperTextFromFields(ctx, ownerTypeName, scopeNode, field.type.fields),
|
|
903
|
+
hashHelper: nestedHashHelperTextFromFields(ctx, ownerTypeName, scopeNode, field.type.fields),
|
|
904
|
+
name: field.name,
|
|
905
|
+
optional: field.optional,
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
const fieldType = supportedDerivedTypeFromShape(field.type);
|
|
910
|
+
if (!fieldType) {
|
|
911
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), field.node);
|
|
912
|
+
}
|
|
913
|
+
assertNamedDerivedCompanionsInScope(
|
|
914
|
+
ctx,
|
|
915
|
+
macroName,
|
|
916
|
+
ownerTypeName,
|
|
917
|
+
scopeNode,
|
|
918
|
+
field.node,
|
|
919
|
+
fieldType,
|
|
920
|
+
);
|
|
921
|
+
|
|
922
|
+
return {
|
|
923
|
+
eqHelper: macroName === 'eq'
|
|
924
|
+
? eqHelperTextForType(ctx, fieldType)
|
|
925
|
+
: hashHelperTextForType(ctx, fieldType),
|
|
926
|
+
hashHelper: hashHelperTextForType(ctx, fieldType),
|
|
927
|
+
name: field.name,
|
|
928
|
+
optional: field.optional,
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function decodeFieldFromReflectedShape(
|
|
933
|
+
ctx: DeriveContext,
|
|
934
|
+
field: MacroReflectedFieldShape,
|
|
935
|
+
ownerTypeName: string,
|
|
936
|
+
scopeNode: MacroSyntaxNode,
|
|
937
|
+
): DecodedField {
|
|
938
|
+
const viaAnnotation = findAnnotation(field.annotations, 'decode.via');
|
|
939
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
940
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
941
|
+
ctx.error('decode.via(...) requires a helper identifier.', field.node);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
const renameAnnotation = findAnnotation(field.annotations, 'decode.rename');
|
|
945
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
946
|
+
if (renameAnnotation && !renamedWireName) {
|
|
947
|
+
ctx.error('decode.rename(...) requires a string field name.', field.node);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
if (!field.type) {
|
|
951
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), field.node);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
const decoderText = (() => {
|
|
955
|
+
if (viaIdentifier) {
|
|
956
|
+
return viaIdentifier;
|
|
957
|
+
}
|
|
958
|
+
if (field.type.kind === 'object') {
|
|
959
|
+
return nestedDecodeHelperTextFromFields(ctx, ownerTypeName, scopeNode, field.type.fields);
|
|
960
|
+
}
|
|
961
|
+
const fieldType = supportedDerivedTypeFromShape(field.type);
|
|
962
|
+
if (!fieldType) {
|
|
963
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), field.node);
|
|
964
|
+
}
|
|
965
|
+
assertNamedDerivedCompanionsInScope(
|
|
966
|
+
ctx,
|
|
967
|
+
'decode',
|
|
968
|
+
ownerTypeName,
|
|
969
|
+
scopeNode,
|
|
970
|
+
field.node,
|
|
971
|
+
fieldType,
|
|
972
|
+
);
|
|
973
|
+
return decodeHelperTextForType(ctx, fieldType);
|
|
974
|
+
})();
|
|
975
|
+
|
|
976
|
+
return {
|
|
977
|
+
decoderText,
|
|
978
|
+
localName: field.name,
|
|
979
|
+
optional: field.optional,
|
|
980
|
+
wireName: renamedWireName ?? field.name,
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
function encodeFieldFromReflectedShape(
|
|
985
|
+
ctx: DeriveContext,
|
|
986
|
+
field: MacroReflectedFieldShape,
|
|
987
|
+
ownerTypeName: string,
|
|
988
|
+
scopeNode: MacroSyntaxNode,
|
|
989
|
+
): EncodedField {
|
|
990
|
+
const viaAnnotation = findAnnotation(field.annotations, 'encode.via');
|
|
991
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
992
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
993
|
+
ctx.error('encode.via(...) requires a helper identifier.', field.node);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
const renameAnnotation = findAnnotation(field.annotations, 'encode.rename');
|
|
997
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
998
|
+
if (renameAnnotation && !renamedWireName) {
|
|
999
|
+
ctx.error('encode.rename(...) requires a string field name.', field.node);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
if (!field.type) {
|
|
1003
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), field.node);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
const encoderText = (() => {
|
|
1007
|
+
if (viaIdentifier) {
|
|
1008
|
+
return viaIdentifier;
|
|
1009
|
+
}
|
|
1010
|
+
if (field.type.kind === 'object') {
|
|
1011
|
+
return nestedEncodeHelperTextFromFields(ctx, ownerTypeName, scopeNode, field.type.fields);
|
|
1012
|
+
}
|
|
1013
|
+
const fieldType = supportedDerivedTypeFromShape(field.type);
|
|
1014
|
+
if (!fieldType) {
|
|
1015
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), field.node);
|
|
1016
|
+
}
|
|
1017
|
+
assertNamedDerivedCompanionsInScope(
|
|
1018
|
+
ctx,
|
|
1019
|
+
'encode',
|
|
1020
|
+
ownerTypeName,
|
|
1021
|
+
scopeNode,
|
|
1022
|
+
field.node,
|
|
1023
|
+
fieldType,
|
|
1024
|
+
);
|
|
1025
|
+
return encodeHelperTextForType(ctx, fieldType);
|
|
1026
|
+
})();
|
|
1027
|
+
|
|
1028
|
+
return {
|
|
1029
|
+
encoderText,
|
|
1030
|
+
localName: field.name,
|
|
1031
|
+
optional: field.optional,
|
|
1032
|
+
wireName: renamedWireName ?? field.name,
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
function codecFieldFromReflectedShape(
|
|
1037
|
+
ctx: DeriveContext,
|
|
1038
|
+
field: MacroReflectedFieldShape,
|
|
1039
|
+
ownerTypeName: string,
|
|
1040
|
+
scopeNode: MacroSyntaxNode,
|
|
1041
|
+
): CodecField {
|
|
1042
|
+
const viaAnnotation = findAnnotation(field.annotations, 'codec.via');
|
|
1043
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1044
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1045
|
+
ctx.error('codec.via(...) requires a helper identifier.', field.node);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const renameAnnotation = findAnnotation(field.annotations, 'codec.rename');
|
|
1049
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1050
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1051
|
+
ctx.error('codec.rename(...) requires a string field name.', field.node);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
if (!field.type) {
|
|
1055
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), field.node);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const helperTexts = (() => {
|
|
1059
|
+
if (viaIdentifier) {
|
|
1060
|
+
return { decodeText: viaIdentifier, encodeText: viaIdentifier };
|
|
1061
|
+
}
|
|
1062
|
+
if (field.type.kind === 'object') {
|
|
1063
|
+
return nestedCodecHelperTextsFromFields(ctx, ownerTypeName, scopeNode, field.type.fields);
|
|
1064
|
+
}
|
|
1065
|
+
const fieldType = supportedDerivedTypeFromShape(field.type);
|
|
1066
|
+
if (!fieldType) {
|
|
1067
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), field.node);
|
|
1068
|
+
}
|
|
1069
|
+
assertNamedDerivedCompanionsInScope(
|
|
1070
|
+
ctx,
|
|
1071
|
+
'codec',
|
|
1072
|
+
ownerTypeName,
|
|
1073
|
+
scopeNode,
|
|
1074
|
+
field.node,
|
|
1075
|
+
fieldType,
|
|
1076
|
+
);
|
|
1077
|
+
return codecHelperTextsForType(ctx, fieldType);
|
|
1078
|
+
})();
|
|
1079
|
+
|
|
1080
|
+
return {
|
|
1081
|
+
decodeText: helperTexts.decodeText,
|
|
1082
|
+
encodeText: helperTexts.encodeText,
|
|
1083
|
+
localName: field.name,
|
|
1084
|
+
optional: field.optional,
|
|
1085
|
+
wireName: renamedWireName ?? field.name,
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
function fieldFromObjectMember(
|
|
1090
|
+
ctx: Parameters<MacroDefinition<typeof DERIVE_SIGNATURE>['expand']>[0],
|
|
1091
|
+
member: MacroObjectTypeMemberSyntax,
|
|
1092
|
+
macroName: 'eq' | 'hash',
|
|
1093
|
+
ownerTypeName: string,
|
|
1094
|
+
scopeNode: MacroSyntaxNode,
|
|
1095
|
+
): DerivedField | null {
|
|
1096
|
+
if (member.memberKind !== 'property_signature' || member.name === null) {
|
|
1097
|
+
ctx.error(`${macroName} only supports property-style object members in v1.`, member);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
const annotations = ctx.syntax.annotations(member);
|
|
1101
|
+
if (findAnnotation(annotations, `${macroName}.skip`)) {
|
|
1102
|
+
return null;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
const viaAnnotation = findAnnotation(annotations, `${macroName}.via`);
|
|
1106
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1107
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1108
|
+
ctx.error(`${macroName}.via(...) requires a helper identifier.`, member);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
const explicitType = member.explicitType();
|
|
1112
|
+
if (!explicitType) {
|
|
1113
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), member);
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
if (viaIdentifier) {
|
|
1117
|
+
return {
|
|
1118
|
+
eqHelper: viaIdentifier,
|
|
1119
|
+
hashHelper: viaIdentifier,
|
|
1120
|
+
name: member.name,
|
|
1121
|
+
optional: member.isOptional(),
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1126
|
+
if (objectType) {
|
|
1127
|
+
return {
|
|
1128
|
+
eqHelper: macroName === 'eq'
|
|
1129
|
+
? nestedEqHelperText(ctx, ownerTypeName, scopeNode, objectType)
|
|
1130
|
+
: nestedHashHelperText(ctx, ownerTypeName, scopeNode, objectType),
|
|
1131
|
+
hashHelper: nestedHashHelperText(ctx, ownerTypeName, scopeNode, objectType),
|
|
1132
|
+
name: member.name,
|
|
1133
|
+
optional: member.isOptional(),
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1138
|
+
if (!fieldType) {
|
|
1139
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), member);
|
|
1140
|
+
}
|
|
1141
|
+
assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, member, fieldType);
|
|
1142
|
+
|
|
1143
|
+
return {
|
|
1144
|
+
eqHelper: macroName === 'eq'
|
|
1145
|
+
? eqHelperTextForType(ctx, fieldType)
|
|
1146
|
+
: hashHelperTextForType(ctx, fieldType),
|
|
1147
|
+
hashHelper: hashHelperTextForType(ctx, fieldType),
|
|
1148
|
+
name: member.name,
|
|
1149
|
+
optional: member.isOptional(),
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
function fieldFromClassMember(
|
|
1154
|
+
ctx: Parameters<MacroDefinition<typeof DERIVE_SIGNATURE>['expand']>[0],
|
|
1155
|
+
field: MacroClassFieldSyntax,
|
|
1156
|
+
macroName: 'eq' | 'hash',
|
|
1157
|
+
ownerTypeName: string,
|
|
1158
|
+
scopeNode: MacroSyntaxNode,
|
|
1159
|
+
): DerivedField | null {
|
|
1160
|
+
if (
|
|
1161
|
+
field.hasModifier('private') || field.hasModifier('protected') || field.hasModifier('static') ||
|
|
1162
|
+
field.name === null
|
|
1163
|
+
) {
|
|
1164
|
+
return null;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
const annotations = ctx.syntax.annotations(field);
|
|
1168
|
+
if (findAnnotation(annotations, `${macroName}.skip`)) {
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
const viaAnnotation = findAnnotation(annotations, `${macroName}.via`);
|
|
1173
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1174
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1175
|
+
ctx.error(`${macroName}.via(...) requires a helper identifier.`, field);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
const explicitType = field.explicitType();
|
|
1179
|
+
if (!explicitType) {
|
|
1180
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), field);
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
if (viaIdentifier) {
|
|
1184
|
+
return {
|
|
1185
|
+
eqHelper: viaIdentifier,
|
|
1186
|
+
hashHelper: viaIdentifier,
|
|
1187
|
+
name: field.name,
|
|
1188
|
+
optional: field.isOptional(),
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1193
|
+
if (objectType) {
|
|
1194
|
+
return {
|
|
1195
|
+
eqHelper: macroName === 'eq'
|
|
1196
|
+
? nestedEqHelperText(ctx, ownerTypeName, scopeNode, objectType)
|
|
1197
|
+
: nestedHashHelperText(ctx, ownerTypeName, scopeNode, objectType),
|
|
1198
|
+
hashHelper: nestedHashHelperText(ctx, ownerTypeName, scopeNode, objectType),
|
|
1199
|
+
name: field.name,
|
|
1200
|
+
optional: field.isOptional(),
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1205
|
+
if (!fieldType) {
|
|
1206
|
+
ctx.error(eqHashUnsupportedFieldMessage(macroName), field);
|
|
1207
|
+
}
|
|
1208
|
+
assertNamedDerivedCompanionsInScope(ctx, macroName, ownerTypeName, scopeNode, field, fieldType);
|
|
1209
|
+
|
|
1210
|
+
return {
|
|
1211
|
+
eqHelper: macroName === 'eq'
|
|
1212
|
+
? eqHelperTextForType(ctx, fieldType)
|
|
1213
|
+
: hashHelperTextForType(ctx, fieldType),
|
|
1214
|
+
hashHelper: hashHelperTextForType(ctx, fieldType),
|
|
1215
|
+
name: field.name,
|
|
1216
|
+
optional: field.isOptional(),
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
function collectFields(
|
|
1221
|
+
ctx: Parameters<MacroDefinition<typeof DERIVE_SIGNATURE>['expand']>[0],
|
|
1222
|
+
macroName: 'eq' | 'hash',
|
|
1223
|
+
decoded: Parameters<MacroDefinition<typeof DERIVE_SIGNATURE>['expand']>[1],
|
|
1224
|
+
): { readonly fields: readonly DerivedField[]; readonly typeName: string } {
|
|
1225
|
+
const declaration = decoded.args.target;
|
|
1226
|
+
const shape = objectLikeDeclarationShape(ctx, declaration, macroName);
|
|
1227
|
+
const typeName = shape.name ??
|
|
1228
|
+
ctx.error(
|
|
1229
|
+
`${macroName} currently requires named ${shape.declarationKind} declarations.`,
|
|
1230
|
+
declaration,
|
|
1231
|
+
);
|
|
1232
|
+
const fields = shape.fields
|
|
1233
|
+
.map((field) => eqHashFieldFromReflectedShape(ctx, field, macroName, typeName, declaration))
|
|
1234
|
+
.filter((field): field is DerivedField => field !== null);
|
|
1235
|
+
return { fields, typeName };
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
function eqCheckText(field: DerivedField, leftName: string, rightName: string): string {
|
|
1239
|
+
const leftAccess = propertyAccessText(leftName, field.name);
|
|
1240
|
+
const rightAccess = propertyAccessText(rightName, field.name);
|
|
1241
|
+
if (!field.optional) {
|
|
1242
|
+
return `${field.eqHelper}.equals(${leftAccess}, ${rightAccess})`;
|
|
1243
|
+
}
|
|
1244
|
+
return `${leftAccess} === ${rightAccess} || (${leftAccess} !== undefined && ${rightAccess} !== undefined && ${field.eqHelper}.equals(${leftAccess}, ${rightAccess}))`;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
function hashExprText(field: DerivedField, receiverName: string): string {
|
|
1248
|
+
const access = propertyAccessText(receiverName, field.name);
|
|
1249
|
+
if (!field.optional) {
|
|
1250
|
+
return `${field.hashHelper}.hash(${access})`;
|
|
1251
|
+
}
|
|
1252
|
+
return `${access} === undefined ? 0 : ${field.hashHelper}.hash(${access})`;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
function nestedEqHelperTextFromFields(
|
|
1256
|
+
ctx: DeriveContext,
|
|
1257
|
+
ownerTypeName: string,
|
|
1258
|
+
scopeNode: MacroSyntaxNode,
|
|
1259
|
+
fields: readonly MacroReflectedFieldShape[],
|
|
1260
|
+
): string {
|
|
1261
|
+
const derivedFields = fields.map((field) =>
|
|
1262
|
+
eqHashFieldFromReflectedShape(ctx, field, 'eq', ownerTypeName, scopeNode)
|
|
1263
|
+
).filter((field): field is DerivedField => field !== null);
|
|
1264
|
+
const equalsBody = derivedFields.length === 0
|
|
1265
|
+
? 'true'
|
|
1266
|
+
: derivedFields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
1267
|
+
return `({
|
|
1268
|
+
equals(left, right) {
|
|
1269
|
+
return ${equalsBody};
|
|
1270
|
+
},
|
|
1271
|
+
})`;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function nestedHashHelperTextFromFields(
|
|
1275
|
+
ctx: DeriveContext,
|
|
1276
|
+
ownerTypeName: string,
|
|
1277
|
+
scopeNode: MacroSyntaxNode,
|
|
1278
|
+
fields: readonly MacroReflectedFieldShape[],
|
|
1279
|
+
): string {
|
|
1280
|
+
const derivedFields = fields.map((field) =>
|
|
1281
|
+
eqHashFieldFromReflectedShape(ctx, field, 'hash', ownerTypeName, scopeNode)
|
|
1282
|
+
).filter((field): field is DerivedField => field !== null);
|
|
1283
|
+
const fromHashEq = ctx.runtime.named('sts:hash', 'fromHashEq').text();
|
|
1284
|
+
const combineHashes = ctx.runtime.named('sts:hash', 'combineHashes').text();
|
|
1285
|
+
const hashArgs = derivedFields.map((field) => hashExprText(field, 'value')).join(', ');
|
|
1286
|
+
const equalsBody = derivedFields.length === 0
|
|
1287
|
+
? 'true'
|
|
1288
|
+
: derivedFields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
1289
|
+
return `${fromHashEq}(
|
|
1290
|
+
(value) => ${combineHashes}(${hashArgs}),
|
|
1291
|
+
(left, right) => ${equalsBody},
|
|
1292
|
+
)`;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
function nestedDecodeHelperTextFromFields(
|
|
1296
|
+
ctx: DeriveContext,
|
|
1297
|
+
ownerTypeName: string,
|
|
1298
|
+
scopeNode: MacroSyntaxNode,
|
|
1299
|
+
fields: readonly MacroReflectedFieldShape[],
|
|
1300
|
+
): string {
|
|
1301
|
+
const decodedFields = fields.map((field) =>
|
|
1302
|
+
decodeFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode)
|
|
1303
|
+
);
|
|
1304
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
1305
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
1306
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
1307
|
+
const shapeText = decodedFields.length === 0 ? '{}' : `{
|
|
1308
|
+
${
|
|
1309
|
+
decodedFields.map((field) =>
|
|
1310
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
1311
|
+
field.optional ? `${decodeOptional}(${field.decoderText})` : field.decoderText
|
|
1312
|
+
}`
|
|
1313
|
+
).join(',\n')
|
|
1314
|
+
}
|
|
1315
|
+
}`;
|
|
1316
|
+
const isIdentityProjection = decodedFields.every((field) => field.localName === field.wireName);
|
|
1317
|
+
if (isIdentityProjection) {
|
|
1318
|
+
return `${decodeObject}(${shapeText})`;
|
|
1319
|
+
}
|
|
1320
|
+
const projectionText = decodedFields.length === 0 ? '{}' : `({
|
|
1321
|
+
${
|
|
1322
|
+
decodedFields.map((field) =>
|
|
1323
|
+
`${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`
|
|
1324
|
+
).join(',\n')
|
|
1325
|
+
}
|
|
1326
|
+
})`;
|
|
1327
|
+
return `${decodeMap}(
|
|
1328
|
+
${decodeObject}(${shapeText}),
|
|
1329
|
+
(value) => ${projectionText},
|
|
1330
|
+
)`;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
function nestedEncodeHelperTextFromFields(
|
|
1334
|
+
ctx: DeriveContext,
|
|
1335
|
+
ownerTypeName: string,
|
|
1336
|
+
scopeNode: MacroSyntaxNode,
|
|
1337
|
+
fields: readonly MacroReflectedFieldShape[],
|
|
1338
|
+
): string {
|
|
1339
|
+
const encodedFields = fields.map((field) =>
|
|
1340
|
+
encodeFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode)
|
|
1341
|
+
);
|
|
1342
|
+
const encodeContramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
1343
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
1344
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
1345
|
+
const shapeText = encodedFields.length === 0 ? '{}' : `{
|
|
1346
|
+
${
|
|
1347
|
+
encodedFields.map((field) =>
|
|
1348
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
1349
|
+
field.optional ? `${encodeOptional}(${field.encoderText})` : field.encoderText
|
|
1350
|
+
}`
|
|
1351
|
+
).join(',\n')
|
|
1352
|
+
}
|
|
1353
|
+
}`;
|
|
1354
|
+
const isIdentityProjection = encodedFields.every((field) => field.localName === field.wireName);
|
|
1355
|
+
if (isIdentityProjection) {
|
|
1356
|
+
return `${encodeObject}(${shapeText})`;
|
|
1357
|
+
}
|
|
1358
|
+
const projectionText = encodedFields.length === 0 ? '({})' : `({
|
|
1359
|
+
${
|
|
1360
|
+
encodedFields.map((field) =>
|
|
1361
|
+
`${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`
|
|
1362
|
+
).join(',\n')
|
|
1363
|
+
}
|
|
1364
|
+
})`;
|
|
1365
|
+
return `${encodeContramap}(
|
|
1366
|
+
${encodeObject}(${shapeText}),
|
|
1367
|
+
(value) => ${projectionText},
|
|
1368
|
+
)`;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
function nestedCodecHelperTextsFromFields(
|
|
1372
|
+
ctx: DeriveContext,
|
|
1373
|
+
ownerTypeName: string,
|
|
1374
|
+
scopeNode: MacroSyntaxNode,
|
|
1375
|
+
fields: readonly MacroReflectedFieldShape[],
|
|
1376
|
+
): { readonly decodeText: string; readonly encodeText: string } {
|
|
1377
|
+
const codecFields = fields.map((field) =>
|
|
1378
|
+
codecFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode)
|
|
1379
|
+
);
|
|
1380
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
1381
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
1382
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
1383
|
+
const encodeContramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
1384
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
1385
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
1386
|
+
const decodeShapeText = codecFields.length === 0 ? '{}' : `{
|
|
1387
|
+
${
|
|
1388
|
+
codecFields.map((field) =>
|
|
1389
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
1390
|
+
field.optional ? `${decodeOptional}(${field.decodeText})` : field.decodeText
|
|
1391
|
+
}`
|
|
1392
|
+
).join(',\n')
|
|
1393
|
+
}
|
|
1394
|
+
}`;
|
|
1395
|
+
const hasIdentityDecodeProjection = codecFields.every((field) =>
|
|
1396
|
+
field.localName === field.wireName
|
|
1397
|
+
);
|
|
1398
|
+
const encodeShapeText = codecFields.length === 0 ? '{}' : `{
|
|
1399
|
+
${
|
|
1400
|
+
codecFields.map((field) =>
|
|
1401
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
1402
|
+
field.optional ? `${encodeOptional}(${field.encodeText})` : field.encodeText
|
|
1403
|
+
}`
|
|
1404
|
+
).join(',\n')
|
|
1405
|
+
}
|
|
1406
|
+
}`;
|
|
1407
|
+
const hasIdentityEncodeProjection = codecFields.every((field) =>
|
|
1408
|
+
field.localName === field.wireName
|
|
1409
|
+
);
|
|
1410
|
+
return {
|
|
1411
|
+
decodeText: hasIdentityDecodeProjection ? `${decodeObject}(${decodeShapeText})` : `${decodeMap}(
|
|
1412
|
+
${decodeObject}(${decodeShapeText}),
|
|
1413
|
+
(value) => ({
|
|
1414
|
+
${
|
|
1415
|
+
codecFields.map((field) =>
|
|
1416
|
+
`${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`
|
|
1417
|
+
).join(',\n')
|
|
1418
|
+
}
|
|
1419
|
+
}),
|
|
1420
|
+
)`,
|
|
1421
|
+
encodeText: hasIdentityEncodeProjection
|
|
1422
|
+
? `${encodeObject}(${encodeShapeText})`
|
|
1423
|
+
: `${encodeContramap}(
|
|
1424
|
+
${encodeObject}(${encodeShapeText}),
|
|
1425
|
+
(value) => ({
|
|
1426
|
+
${
|
|
1427
|
+
codecFields.map((field) =>
|
|
1428
|
+
`${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`
|
|
1429
|
+
).join(',\n')
|
|
1430
|
+
}
|
|
1431
|
+
}),
|
|
1432
|
+
)`,
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
function collectNestedDerivedFields(
|
|
1437
|
+
ctx: Parameters<MacroDefinition<typeof DERIVE_SIGNATURE>['expand']>[0],
|
|
1438
|
+
macroName: 'eq' | 'hash',
|
|
1439
|
+
ownerTypeName: string,
|
|
1440
|
+
scopeNode: MacroSyntaxNode,
|
|
1441
|
+
objectType: MacroObjectTypeSyntax,
|
|
1442
|
+
): readonly DerivedField[] {
|
|
1443
|
+
return objectType.members
|
|
1444
|
+
.map((member) => fieldFromObjectMember(ctx, member, macroName, ownerTypeName, scopeNode))
|
|
1445
|
+
.filter((member): member is DerivedField => member !== null);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
function nestedEqHelperText(
|
|
1449
|
+
ctx: Parameters<MacroDefinition<typeof DERIVE_SIGNATURE>['expand']>[0],
|
|
1450
|
+
ownerTypeName: string,
|
|
1451
|
+
scopeNode: MacroSyntaxNode,
|
|
1452
|
+
objectType: MacroObjectTypeSyntax,
|
|
1453
|
+
): string {
|
|
1454
|
+
const fields = collectNestedDerivedFields(ctx, 'eq', ownerTypeName, scopeNode, objectType);
|
|
1455
|
+
const equalsBody = fields.length === 0
|
|
1456
|
+
? 'true'
|
|
1457
|
+
: fields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
1458
|
+
return `({
|
|
1459
|
+
equals(left, right) {
|
|
1460
|
+
return ${equalsBody};
|
|
1461
|
+
},
|
|
1462
|
+
})`;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
function nestedHashHelperText(
|
|
1466
|
+
ctx: Parameters<MacroDefinition<typeof DERIVE_SIGNATURE>['expand']>[0],
|
|
1467
|
+
ownerTypeName: string,
|
|
1468
|
+
scopeNode: MacroSyntaxNode,
|
|
1469
|
+
objectType: MacroObjectTypeSyntax,
|
|
1470
|
+
): string {
|
|
1471
|
+
const fields = collectNestedDerivedFields(ctx, 'hash', ownerTypeName, scopeNode, objectType);
|
|
1472
|
+
const fromHashEq = ctx.runtime.named('sts:hash', 'fromHashEq').text();
|
|
1473
|
+
const combineHashes = ctx.runtime.named('sts:hash', 'combineHashes').text();
|
|
1474
|
+
const hashArgs = fields.map((field) => hashExprText(field, 'value')).join(', ');
|
|
1475
|
+
const equalsBody = fields.length === 0
|
|
1476
|
+
? 'true'
|
|
1477
|
+
: fields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
1478
|
+
return `${fromHashEq}(
|
|
1479
|
+
(value) => ${combineHashes}(${hashArgs}),
|
|
1480
|
+
(left, right) => ${equalsBody},
|
|
1481
|
+
)`;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
function nestedDecodeHelperText(
|
|
1485
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
1486
|
+
ownerTypeName: string,
|
|
1487
|
+
scopeNode: MacroSyntaxNode,
|
|
1488
|
+
objectType: MacroObjectTypeSyntax,
|
|
1489
|
+
): string {
|
|
1490
|
+
const fields = objectType.members.map((member) =>
|
|
1491
|
+
fieldFromDecodeMember(ctx, member, ownerTypeName, scopeNode)
|
|
1492
|
+
);
|
|
1493
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
1494
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
1495
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
1496
|
+
const shapeText = fields.length === 0 ? '{}' : `{
|
|
1497
|
+
${
|
|
1498
|
+
fields.map((field) =>
|
|
1499
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
1500
|
+
field.optional ? `${decodeOptional}(${field.decoderText})` : field.decoderText
|
|
1501
|
+
}`
|
|
1502
|
+
).join(',\n')
|
|
1503
|
+
}
|
|
1504
|
+
}`;
|
|
1505
|
+
const isIdentityProjection = fields.every((field) => field.localName === field.wireName);
|
|
1506
|
+
if (isIdentityProjection) {
|
|
1507
|
+
return `${decodeObject}(${shapeText})`;
|
|
1508
|
+
}
|
|
1509
|
+
const projectionText = fields.length === 0 ? '{}' : `({
|
|
1510
|
+
${
|
|
1511
|
+
fields.map((field) =>
|
|
1512
|
+
`${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`
|
|
1513
|
+
).join(',\n')
|
|
1514
|
+
}
|
|
1515
|
+
})`;
|
|
1516
|
+
return `${decodeMap}(
|
|
1517
|
+
${decodeObject}(${shapeText}),
|
|
1518
|
+
(value) => ${projectionText},
|
|
1519
|
+
)`;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
function nestedEncodeHelperText(
|
|
1523
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
1524
|
+
ownerTypeName: string,
|
|
1525
|
+
scopeNode: MacroSyntaxNode,
|
|
1526
|
+
objectType: MacroObjectTypeSyntax,
|
|
1527
|
+
): string {
|
|
1528
|
+
const fields = objectType.members.map((member) =>
|
|
1529
|
+
fieldFromEncodeMember(ctx, member, ownerTypeName, scopeNode)
|
|
1530
|
+
);
|
|
1531
|
+
const encodeContramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
1532
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
1533
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
1534
|
+
const shapeText = fields.length === 0 ? '{}' : `{
|
|
1535
|
+
${
|
|
1536
|
+
fields.map((field) =>
|
|
1537
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
1538
|
+
field.optional ? `${encodeOptional}(${field.encoderText})` : field.encoderText
|
|
1539
|
+
}`
|
|
1540
|
+
).join(',\n')
|
|
1541
|
+
}
|
|
1542
|
+
}`;
|
|
1543
|
+
const isIdentityProjection = fields.every((field) => field.localName === field.wireName);
|
|
1544
|
+
if (isIdentityProjection) {
|
|
1545
|
+
return `${encodeObject}(${shapeText})`;
|
|
1546
|
+
}
|
|
1547
|
+
const projectionText = fields.length === 0 ? '({})' : `({
|
|
1548
|
+
${
|
|
1549
|
+
fields.map((field) =>
|
|
1550
|
+
`${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`
|
|
1551
|
+
).join(',\n')
|
|
1552
|
+
}
|
|
1553
|
+
})`;
|
|
1554
|
+
return `${encodeContramap}(
|
|
1555
|
+
${encodeObject}(${shapeText}),
|
|
1556
|
+
(value) => ${projectionText},
|
|
1557
|
+
)`;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
function nestedCodecHelperTexts(
|
|
1561
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
1562
|
+
ownerTypeName: string,
|
|
1563
|
+
scopeNode: MacroSyntaxNode,
|
|
1564
|
+
objectType: MacroObjectTypeSyntax,
|
|
1565
|
+
): { readonly decodeText: string; readonly encodeText: string } {
|
|
1566
|
+
const fields = objectType.members.map((member) =>
|
|
1567
|
+
fieldFromCodecMember(ctx, member, ownerTypeName, scopeNode)
|
|
1568
|
+
);
|
|
1569
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
1570
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
1571
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
1572
|
+
const encodeContramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
1573
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
1574
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
1575
|
+
const decodeShapeText = fields.length === 0 ? '{}' : `{
|
|
1576
|
+
${
|
|
1577
|
+
fields.map((field) =>
|
|
1578
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
1579
|
+
field.optional ? `${decodeOptional}(${field.decodeText})` : field.decodeText
|
|
1580
|
+
}`
|
|
1581
|
+
).join(',\n')
|
|
1582
|
+
}
|
|
1583
|
+
}`;
|
|
1584
|
+
const hasIdentityDecodeProjection = fields.every((field) => field.localName === field.wireName);
|
|
1585
|
+
const encodeShapeText = fields.length === 0 ? '{}' : `{
|
|
1586
|
+
${
|
|
1587
|
+
fields.map((field) =>
|
|
1588
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
1589
|
+
field.optional ? `${encodeOptional}(${field.encodeText})` : field.encodeText
|
|
1590
|
+
}`
|
|
1591
|
+
).join(',\n')
|
|
1592
|
+
}
|
|
1593
|
+
}`;
|
|
1594
|
+
const hasIdentityEncodeProjection = fields.every((field) => field.localName === field.wireName);
|
|
1595
|
+
return {
|
|
1596
|
+
decodeText: hasIdentityDecodeProjection ? `${decodeObject}(${decodeShapeText})` : `${decodeMap}(
|
|
1597
|
+
${decodeObject}(${decodeShapeText}),
|
|
1598
|
+
(value) => ({
|
|
1599
|
+
${
|
|
1600
|
+
fields.map((field) =>
|
|
1601
|
+
`${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`
|
|
1602
|
+
).join(',\n')
|
|
1603
|
+
}
|
|
1604
|
+
}),
|
|
1605
|
+
)`,
|
|
1606
|
+
encodeText: hasIdentityEncodeProjection
|
|
1607
|
+
? `${encodeObject}(${encodeShapeText})`
|
|
1608
|
+
: `${encodeContramap}(
|
|
1609
|
+
${encodeObject}(${encodeShapeText}),
|
|
1610
|
+
(value) => ({
|
|
1611
|
+
${
|
|
1612
|
+
fields.map((field) =>
|
|
1613
|
+
`${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`
|
|
1614
|
+
).join(',\n')
|
|
1615
|
+
}
|
|
1616
|
+
}),
|
|
1617
|
+
)`,
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
function capitalizeName(name: string): string {
|
|
1622
|
+
return name.length === 0 ? name : `${name[0]!.toUpperCase()}${name.slice(1)}`;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
function decapitalizeName(name: string): string {
|
|
1626
|
+
return name.length === 0 ? name : `${name[0]!.toLowerCase()}${name.slice(1)}`;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
function annotationNamedStringArgument(
|
|
1630
|
+
annotation: MacroAnnotation,
|
|
1631
|
+
name: string,
|
|
1632
|
+
): string | null {
|
|
1633
|
+
const argument = annotation.arguments?.find((entry) =>
|
|
1634
|
+
entry.kind === 'named' && entry.name === name
|
|
1635
|
+
);
|
|
1636
|
+
return argument?.value.kind === 'string' ? argument.value.value : null;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
function taggedDiscriminantName(
|
|
1640
|
+
ctx: Parameters<MacroDefinition<typeof TAGGED_SIGNATURE>['expand']>[0],
|
|
1641
|
+
declaration: MacroTypeAliasDeclSyntax,
|
|
1642
|
+
): string {
|
|
1643
|
+
const annotation = findAnnotation(ctx.syntax.annotations(declaration), 'tagged');
|
|
1644
|
+
if (!annotation) {
|
|
1645
|
+
return 'tag';
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
const discriminant = annotationNamedStringArgument(annotation, 'discriminant');
|
|
1649
|
+
if (discriminant !== null) {
|
|
1650
|
+
return discriminant;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
if ((annotation.arguments?.length ?? 0) > 0) {
|
|
1654
|
+
ctx.error("tagged only supports #[tagged(discriminant: '...')] options in v1.", declaration);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
return 'tag';
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
function taggedPayloadField(
|
|
1661
|
+
ctx: Parameters<MacroDefinition<typeof TAGGED_SIGNATURE>['expand']>[0],
|
|
1662
|
+
member: MacroObjectTypeMemberSyntax,
|
|
1663
|
+
): TaggedVariantField {
|
|
1664
|
+
if (member.memberKind !== 'property_signature' || member.name === null) {
|
|
1665
|
+
ctx.error('tagged only supports property-style object members in v1.', member);
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
const explicitType = member.explicitType();
|
|
1669
|
+
if (!explicitType) {
|
|
1670
|
+
ctx.error('tagged currently requires explicit payload property types in v1.', member);
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
return {
|
|
1674
|
+
name: member.name,
|
|
1675
|
+
optional: member.isOptional(),
|
|
1676
|
+
typeText: explicitType.text(),
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
function fieldFromDecodeMember(
|
|
1681
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
1682
|
+
member: MacroObjectTypeMemberSyntax,
|
|
1683
|
+
ownerTypeName: string,
|
|
1684
|
+
scopeNode: MacroSyntaxNode,
|
|
1685
|
+
): DecodedField {
|
|
1686
|
+
if (member.memberKind !== 'property_signature' || member.name === null) {
|
|
1687
|
+
ctx.error('decode only supports property-style object members in v1.', member);
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(member), 'decode.via');
|
|
1691
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1692
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1693
|
+
ctx.error('decode.via(...) requires a helper identifier.', member);
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(member), 'decode.rename');
|
|
1697
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1698
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1699
|
+
ctx.error('decode.rename(...) requires a string field name.', member);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
const explicitType = member.explicitType();
|
|
1703
|
+
if (!explicitType) {
|
|
1704
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), member);
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
const decoderText = (() => {
|
|
1708
|
+
if (viaIdentifier) {
|
|
1709
|
+
return viaIdentifier;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1713
|
+
if (objectType) {
|
|
1714
|
+
return nestedDecodeHelperText(ctx, ownerTypeName, scopeNode, objectType);
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1718
|
+
if (!fieldType) {
|
|
1719
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), member);
|
|
1720
|
+
}
|
|
1721
|
+
assertNamedDerivedCompanionsInScope(ctx, 'decode', ownerTypeName, scopeNode, member, fieldType);
|
|
1722
|
+
|
|
1723
|
+
return decodeHelperTextForType(ctx, fieldType);
|
|
1724
|
+
})();
|
|
1725
|
+
|
|
1726
|
+
return {
|
|
1727
|
+
decoderText,
|
|
1728
|
+
localName: member.name,
|
|
1729
|
+
optional: member.isOptional(),
|
|
1730
|
+
wireName: renamedWireName ?? member.name,
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
function fieldFromDecodeClassField(
|
|
1735
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
1736
|
+
field: MacroClassFieldSyntax,
|
|
1737
|
+
ownerTypeName: string,
|
|
1738
|
+
scopeNode: MacroSyntaxNode,
|
|
1739
|
+
): DecodedField | null {
|
|
1740
|
+
if (
|
|
1741
|
+
field.hasModifier('private') || field.hasModifier('protected') || field.hasModifier('static') ||
|
|
1742
|
+
field.name === null
|
|
1743
|
+
) {
|
|
1744
|
+
return null;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(field), 'decode.via');
|
|
1748
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1749
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1750
|
+
ctx.error('decode.via(...) requires a helper identifier.', field);
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(field), 'decode.rename');
|
|
1754
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1755
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1756
|
+
ctx.error('decode.rename(...) requires a string field name.', field);
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
const explicitType = field.explicitType();
|
|
1760
|
+
if (!explicitType) {
|
|
1761
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), field);
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
const decoderText = (() => {
|
|
1765
|
+
if (viaIdentifier) {
|
|
1766
|
+
return viaIdentifier;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1770
|
+
if (objectType) {
|
|
1771
|
+
return nestedDecodeHelperText(ctx, ownerTypeName, scopeNode, objectType);
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1775
|
+
if (!fieldType) {
|
|
1776
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('decode'), field);
|
|
1777
|
+
}
|
|
1778
|
+
assertNamedDerivedCompanionsInScope(ctx, 'decode', ownerTypeName, scopeNode, field, fieldType);
|
|
1779
|
+
|
|
1780
|
+
return decodeHelperTextForType(ctx, fieldType);
|
|
1781
|
+
})();
|
|
1782
|
+
|
|
1783
|
+
return {
|
|
1784
|
+
decoderText,
|
|
1785
|
+
localName: field.name,
|
|
1786
|
+
optional: field.isOptional(),
|
|
1787
|
+
wireName: renamedWireName ?? field.name,
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
function classHasConstructorParameters(declaration: MacroClassDeclSyntax): boolean {
|
|
1792
|
+
return declaration.members().some((member) =>
|
|
1793
|
+
member.memberKind === 'constructor' && member.parameters.length > 0
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
function classifySelfFactoryHelper(
|
|
1798
|
+
declaration: MacroClassDeclSyntax,
|
|
1799
|
+
typeName: string,
|
|
1800
|
+
factoryIdentifier: string,
|
|
1801
|
+
): 'callable' | 'missing' | 'non-callable' | null {
|
|
1802
|
+
const segments = factoryIdentifier.split('.').map((segment) => segment.trim()).filter((segment) =>
|
|
1803
|
+
segment.length > 0
|
|
1804
|
+
);
|
|
1805
|
+
if (segments.length !== 2 || segments[0] !== typeName) {
|
|
1806
|
+
return null;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
const member = declaration.member(segments[1]!);
|
|
1810
|
+
if (!member || !member.hasModifier('static')) {
|
|
1811
|
+
return 'missing';
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
return member.memberKind === 'method' ? 'callable' : 'non-callable';
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
function classDecodeInstantiateText(
|
|
1818
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
1819
|
+
declaration: MacroClassDeclSyntax,
|
|
1820
|
+
macroName: 'codec' | 'decode',
|
|
1821
|
+
typeName: string,
|
|
1822
|
+
): string {
|
|
1823
|
+
const factoryAnnotation = findAnnotation(
|
|
1824
|
+
ctx.syntax.annotations(declaration),
|
|
1825
|
+
`${macroName}.factory`,
|
|
1826
|
+
);
|
|
1827
|
+
if (factoryAnnotation) {
|
|
1828
|
+
const factoryIdentifier = annotationIdentifierArgument(factoryAnnotation);
|
|
1829
|
+
if (!factoryIdentifier) {
|
|
1830
|
+
ctx.error(
|
|
1831
|
+
`${macroName}.factory(...) requires a helper identifier.`,
|
|
1832
|
+
annotationDiagnosticNode(declaration, factoryAnnotation) ?? declaration,
|
|
1833
|
+
);
|
|
1834
|
+
}
|
|
1835
|
+
const selfFactoryClassification = classifySelfFactoryHelper(
|
|
1836
|
+
declaration,
|
|
1837
|
+
typeName,
|
|
1838
|
+
factoryIdentifier,
|
|
1839
|
+
);
|
|
1840
|
+
if (selfFactoryClassification === 'missing') {
|
|
1841
|
+
ctx.error(
|
|
1842
|
+
`${macroName}.factory(...) requires the helper value "${factoryIdentifier}" to be in scope.`,
|
|
1843
|
+
annotationDiagnosticNode(declaration, factoryAnnotation) ?? declaration,
|
|
1844
|
+
);
|
|
1845
|
+
}
|
|
1846
|
+
if (selfFactoryClassification === 'non-callable') {
|
|
1847
|
+
ctx.error(
|
|
1848
|
+
`${macroName}.factory(...) requires "${factoryIdentifier}" to be callable.`,
|
|
1849
|
+
annotationDiagnosticNode(declaration, factoryAnnotation) ?? declaration,
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1852
|
+
if (selfFactoryClassification === 'callable') {
|
|
1853
|
+
return `${factoryIdentifier}(${CLASS_DECODE_VALUE_PLACEHOLDER})`;
|
|
1854
|
+
}
|
|
1855
|
+
if (!ctx.semantics.valueBindingInScope(factoryIdentifier)) {
|
|
1856
|
+
ctx.error(
|
|
1857
|
+
`${macroName}.factory(...) requires the helper value "${factoryIdentifier}" to be in scope.`,
|
|
1858
|
+
annotationDiagnosticNode(declaration, factoryAnnotation) ?? declaration,
|
|
1859
|
+
);
|
|
1860
|
+
}
|
|
1861
|
+
if (!ctx.semantics.valueBindingCallableInScope(factoryIdentifier)) {
|
|
1862
|
+
ctx.error(
|
|
1863
|
+
`${macroName}.factory(...) requires "${factoryIdentifier}" to be callable.`,
|
|
1864
|
+
annotationDiagnosticNode(declaration, factoryAnnotation) ?? declaration,
|
|
1865
|
+
);
|
|
1866
|
+
}
|
|
1867
|
+
return `${factoryIdentifier}(${CLASS_DECODE_VALUE_PLACEHOLDER})`;
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
if (classHasConstructorParameters(declaration)) {
|
|
1871
|
+
ctx.error(
|
|
1872
|
+
`${macroName} class support in v1 requires a constructor with no parameters.`,
|
|
1873
|
+
declaration,
|
|
1874
|
+
);
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
return `Object.assign(new ${typeName}(), ${CLASS_DECODE_VALUE_PLACEHOLDER})`;
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
function collectDecodeFields(
|
|
1881
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
1882
|
+
decoded: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[1],
|
|
1883
|
+
): {
|
|
1884
|
+
readonly fields: readonly DecodedField[];
|
|
1885
|
+
readonly instantiateText: string | null;
|
|
1886
|
+
readonly typeName: string;
|
|
1887
|
+
} {
|
|
1888
|
+
const declaration = decoded.args.target;
|
|
1889
|
+
const shape = objectLikeDeclarationShape(ctx, declaration, 'decode');
|
|
1890
|
+
const typeName = shape.name ??
|
|
1891
|
+
ctx.error(
|
|
1892
|
+
`decode currently requires named ${shape.declarationKind} declarations.`,
|
|
1893
|
+
declaration,
|
|
1894
|
+
);
|
|
1895
|
+
return {
|
|
1896
|
+
fields: shape.fields.map((field) =>
|
|
1897
|
+
decodeFieldFromReflectedShape(ctx, field, typeName, declaration)
|
|
1898
|
+
),
|
|
1899
|
+
instantiateText: decoded.caseName === 'class'
|
|
1900
|
+
? classDecodeInstantiateText(ctx, declaration as MacroClassDeclSyntax, 'decode', typeName)
|
|
1901
|
+
: null,
|
|
1902
|
+
typeName,
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
function fieldFromEncodeMember(
|
|
1907
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
1908
|
+
member: MacroObjectTypeMemberSyntax,
|
|
1909
|
+
ownerTypeName: string,
|
|
1910
|
+
scopeNode: MacroSyntaxNode,
|
|
1911
|
+
): EncodedField {
|
|
1912
|
+
if (member.memberKind !== 'property_signature' || member.name === null) {
|
|
1913
|
+
ctx.error('encode only supports property-style object members in v1.', member);
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(member), 'encode.via');
|
|
1917
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1918
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1919
|
+
ctx.error('encode.via(...) requires a helper identifier.', member);
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(member), 'encode.rename');
|
|
1923
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1924
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1925
|
+
ctx.error('encode.rename(...) requires a string field name.', member);
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
const explicitType = member.explicitType();
|
|
1929
|
+
if (!explicitType) {
|
|
1930
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), member);
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
const encoderText = (() => {
|
|
1934
|
+
if (viaIdentifier) {
|
|
1935
|
+
return viaIdentifier;
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1939
|
+
if (objectType) {
|
|
1940
|
+
return nestedEncodeHelperText(ctx, ownerTypeName, scopeNode, objectType);
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
1944
|
+
if (!fieldType) {
|
|
1945
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), member);
|
|
1946
|
+
}
|
|
1947
|
+
assertNamedDerivedCompanionsInScope(ctx, 'encode', ownerTypeName, scopeNode, member, fieldType);
|
|
1948
|
+
|
|
1949
|
+
return encodeHelperTextForType(ctx, fieldType);
|
|
1950
|
+
})();
|
|
1951
|
+
|
|
1952
|
+
return {
|
|
1953
|
+
encoderText,
|
|
1954
|
+
localName: member.name,
|
|
1955
|
+
optional: member.isOptional(),
|
|
1956
|
+
wireName: renamedWireName ?? member.name,
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
function fieldFromEncodeClassField(
|
|
1961
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
1962
|
+
field: MacroClassFieldSyntax,
|
|
1963
|
+
ownerTypeName: string,
|
|
1964
|
+
scopeNode: MacroSyntaxNode,
|
|
1965
|
+
): EncodedField | null {
|
|
1966
|
+
if (
|
|
1967
|
+
field.hasModifier('private') || field.hasModifier('protected') || field.hasModifier('static') ||
|
|
1968
|
+
field.name === null
|
|
1969
|
+
) {
|
|
1970
|
+
return null;
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(field), 'encode.via');
|
|
1974
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
1975
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
1976
|
+
ctx.error('encode.via(...) requires a helper identifier.', field);
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(field), 'encode.rename');
|
|
1980
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
1981
|
+
if (renameAnnotation && !renamedWireName) {
|
|
1982
|
+
ctx.error('encode.rename(...) requires a string field name.', field);
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
const explicitType = field.explicitType();
|
|
1986
|
+
if (!explicitType) {
|
|
1987
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), field);
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
const encoderText = (() => {
|
|
1991
|
+
if (viaIdentifier) {
|
|
1992
|
+
return viaIdentifier;
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
const objectType = explicitType.asObjectLiteral();
|
|
1996
|
+
if (objectType) {
|
|
1997
|
+
return nestedEncodeHelperText(ctx, ownerTypeName, scopeNode, objectType);
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
2001
|
+
if (!fieldType) {
|
|
2002
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('encode'), field);
|
|
2003
|
+
}
|
|
2004
|
+
assertNamedDerivedCompanionsInScope(ctx, 'encode', ownerTypeName, scopeNode, field, fieldType);
|
|
2005
|
+
|
|
2006
|
+
return encodeHelperTextForType(ctx, fieldType);
|
|
2007
|
+
})();
|
|
2008
|
+
|
|
2009
|
+
return {
|
|
2010
|
+
encoderText,
|
|
2011
|
+
localName: field.name,
|
|
2012
|
+
optional: field.isOptional(),
|
|
2013
|
+
wireName: renamedWireName ?? field.name,
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
function collectEncodeFields(
|
|
2018
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
2019
|
+
decoded: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[1],
|
|
2020
|
+
): { readonly fields: readonly EncodedField[]; readonly typeName: string } {
|
|
2021
|
+
const declaration = decoded.args.target;
|
|
2022
|
+
const shape = objectLikeDeclarationShape(ctx, declaration, 'encode');
|
|
2023
|
+
const typeName = shape.name ??
|
|
2024
|
+
ctx.error(
|
|
2025
|
+
`encode currently requires named ${shape.declarationKind} declarations.`,
|
|
2026
|
+
declaration,
|
|
2027
|
+
);
|
|
2028
|
+
return {
|
|
2029
|
+
fields: shape.fields.map((field) =>
|
|
2030
|
+
encodeFieldFromReflectedShape(ctx, field, typeName, declaration)
|
|
2031
|
+
),
|
|
2032
|
+
typeName,
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
function fieldFromCodecMember(
|
|
2037
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
2038
|
+
member: MacroObjectTypeMemberSyntax,
|
|
2039
|
+
ownerTypeName: string,
|
|
2040
|
+
scopeNode: MacroSyntaxNode,
|
|
2041
|
+
): CodecField {
|
|
2042
|
+
if (member.memberKind !== 'property_signature' || member.name === null) {
|
|
2043
|
+
ctx.error('codec only supports property-style object members in v1.', member);
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(member), 'codec.via');
|
|
2047
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
2048
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
2049
|
+
ctx.error('codec.via(...) requires a helper identifier.', member);
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(member), 'codec.rename');
|
|
2053
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
2054
|
+
if (renameAnnotation && !renamedWireName) {
|
|
2055
|
+
ctx.error('codec.rename(...) requires a string field name.', member);
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
const explicitType = member.explicitType();
|
|
2059
|
+
if (!explicitType) {
|
|
2060
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), member);
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
const helperTexts = (() => {
|
|
2064
|
+
if (viaIdentifier) {
|
|
2065
|
+
return { decodeText: viaIdentifier, encodeText: viaIdentifier };
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
const objectType = explicitType.asObjectLiteral();
|
|
2069
|
+
if (objectType) {
|
|
2070
|
+
return nestedCodecHelperTexts(ctx, ownerTypeName, scopeNode, objectType);
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
2074
|
+
if (!fieldType) {
|
|
2075
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), member);
|
|
2076
|
+
}
|
|
2077
|
+
assertNamedDerivedCompanionsInScope(ctx, 'codec', ownerTypeName, scopeNode, member, fieldType);
|
|
2078
|
+
|
|
2079
|
+
return codecHelperTextsForType(ctx, fieldType);
|
|
2080
|
+
})();
|
|
2081
|
+
|
|
2082
|
+
return {
|
|
2083
|
+
decodeText: helperTexts.decodeText,
|
|
2084
|
+
encodeText: helperTexts.encodeText,
|
|
2085
|
+
localName: member.name,
|
|
2086
|
+
optional: member.isOptional(),
|
|
2087
|
+
wireName: renamedWireName ?? member.name,
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
function fieldFromCodecClassField(
|
|
2092
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
2093
|
+
field: MacroClassFieldSyntax,
|
|
2094
|
+
ownerTypeName: string,
|
|
2095
|
+
scopeNode: MacroSyntaxNode,
|
|
2096
|
+
): CodecField | null {
|
|
2097
|
+
if (
|
|
2098
|
+
field.hasModifier('private') || field.hasModifier('protected') || field.hasModifier('static') ||
|
|
2099
|
+
field.name === null
|
|
2100
|
+
) {
|
|
2101
|
+
return null;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
const viaAnnotation = findAnnotation(ctx.syntax.annotations(field), 'codec.via');
|
|
2105
|
+
const viaIdentifier = viaAnnotation ? annotationIdentifierArgument(viaAnnotation) : null;
|
|
2106
|
+
if (viaAnnotation && !viaIdentifier) {
|
|
2107
|
+
ctx.error('codec.via(...) requires a helper identifier.', field);
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
const renameAnnotation = findAnnotation(ctx.syntax.annotations(field), 'codec.rename');
|
|
2111
|
+
const renamedWireName = renameAnnotation ? annotationStringArgument(renameAnnotation) : null;
|
|
2112
|
+
if (renameAnnotation && !renamedWireName) {
|
|
2113
|
+
ctx.error('codec.rename(...) requires a string field name.', field);
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
const explicitType = field.explicitType();
|
|
2117
|
+
if (!explicitType) {
|
|
2118
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), field);
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
const helperTexts = (() => {
|
|
2122
|
+
if (viaIdentifier) {
|
|
2123
|
+
return { decodeText: viaIdentifier, encodeText: viaIdentifier };
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
const objectType = explicitType.asObjectLiteral();
|
|
2127
|
+
if (objectType) {
|
|
2128
|
+
return nestedCodecHelperTexts(ctx, ownerTypeName, scopeNode, objectType);
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
const fieldType = parseSupportedDerivedType(explicitType.text());
|
|
2132
|
+
if (!fieldType) {
|
|
2133
|
+
ctx.error(decodeLikeUnsupportedFieldMessage('codec'), field);
|
|
2134
|
+
}
|
|
2135
|
+
assertNamedDerivedCompanionsInScope(ctx, 'codec', ownerTypeName, scopeNode, field, fieldType);
|
|
2136
|
+
|
|
2137
|
+
return codecHelperTextsForType(ctx, fieldType);
|
|
2138
|
+
})();
|
|
2139
|
+
|
|
2140
|
+
return {
|
|
2141
|
+
decodeText: helperTexts.decodeText,
|
|
2142
|
+
encodeText: helperTexts.encodeText,
|
|
2143
|
+
localName: field.name,
|
|
2144
|
+
optional: field.isOptional(),
|
|
2145
|
+
wireName: renamedWireName ?? field.name,
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
function collectCodecFields(
|
|
2150
|
+
ctx: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[0],
|
|
2151
|
+
decoded: Parameters<MacroDefinition<typeof DECODE_SIGNATURE>['expand']>[1],
|
|
2152
|
+
): {
|
|
2153
|
+
readonly fields: readonly CodecField[];
|
|
2154
|
+
readonly instantiateText: string | null;
|
|
2155
|
+
readonly typeName: string;
|
|
2156
|
+
} {
|
|
2157
|
+
const declaration = decoded.args.target;
|
|
2158
|
+
const shape = objectLikeDeclarationShape(ctx, declaration, 'codec');
|
|
2159
|
+
const typeName = shape.name ??
|
|
2160
|
+
ctx.error(`codec currently requires named ${shape.declarationKind} declarations.`, declaration);
|
|
2161
|
+
return {
|
|
2162
|
+
fields: shape.fields.map((field) =>
|
|
2163
|
+
codecFieldFromReflectedShape(ctx, field, typeName, declaration)
|
|
2164
|
+
),
|
|
2165
|
+
instantiateText: decoded.caseName === 'class'
|
|
2166
|
+
? classDecodeInstantiateText(ctx, declaration as MacroClassDeclSyntax, 'codec', typeName)
|
|
2167
|
+
: null,
|
|
2168
|
+
typeName,
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
function validateTaggedUnionSyntaxForDiagnostics(
|
|
2173
|
+
ctx: DeriveContext,
|
|
2174
|
+
declaration: MacroTypeAliasDeclSyntax,
|
|
2175
|
+
macroName: 'codec' | 'decode' | 'encode' | 'eq' | 'hash' | 'tagged',
|
|
2176
|
+
discriminantName: string,
|
|
2177
|
+
): never {
|
|
2178
|
+
const unionType = declaration.type.asUnion();
|
|
2179
|
+
if (!unionType) {
|
|
2180
|
+
if (macroName === 'tagged') {
|
|
2181
|
+
ctx.error(
|
|
2182
|
+
'tagged currently only supports type aliases declared as unions in v1.',
|
|
2183
|
+
declaration,
|
|
2184
|
+
);
|
|
2185
|
+
}
|
|
2186
|
+
ctx.error(`${macroName} currently only supports object-like type aliases.`, declaration);
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
for (const member of unionType.members) {
|
|
2190
|
+
const objectType = member.asObjectLiteral();
|
|
2191
|
+
if (!objectType) {
|
|
2192
|
+
ctx.error(
|
|
2193
|
+
macroName === 'tagged'
|
|
2194
|
+
? 'tagged only supports unions of object-like variants in v1.'
|
|
2195
|
+
: `${macroName} only supports // #[tagged] unions of object-like variants in v1.`,
|
|
2196
|
+
declaration,
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
let hasDiscriminant = false;
|
|
2201
|
+
for (const objectMember of objectType.members) {
|
|
2202
|
+
if (objectMember.memberKind !== 'property_signature' || objectMember.name === null) {
|
|
2203
|
+
ctx.error(
|
|
2204
|
+
macroName === 'tagged'
|
|
2205
|
+
? 'tagged only supports property-style object members in v1.'
|
|
2206
|
+
: `${macroName} only supports property-style tagged union members in v1.`,
|
|
2207
|
+
objectMember,
|
|
2208
|
+
);
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
if (objectMember.name !== discriminantName) {
|
|
2212
|
+
continue;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
const literalType = objectMember.explicitType()?.asLiteral();
|
|
2216
|
+
if (!literalType || literalType.literalKind !== 'string') {
|
|
2217
|
+
ctx.error(
|
|
2218
|
+
macroName === 'tagged'
|
|
2219
|
+
? 'tagged requires each variant discriminant to be a string literal type in v1.'
|
|
2220
|
+
: `${macroName} requires each tagged union discriminant to be a string literal type in v1.`,
|
|
2221
|
+
objectMember,
|
|
2222
|
+
);
|
|
2223
|
+
}
|
|
2224
|
+
hasDiscriminant = true;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
if (!hasDiscriminant) {
|
|
2228
|
+
ctx.error(
|
|
2229
|
+
macroName === 'tagged'
|
|
2230
|
+
? `tagged requires each variant to declare the discriminant property "${discriminantName}".`
|
|
2231
|
+
: `${macroName} requires each tagged variant to declare the discriminant property "${discriminantName}".`,
|
|
2232
|
+
objectType,
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
ctx.error(
|
|
2238
|
+
macroName === 'tagged'
|
|
2239
|
+
? 'tagged only supports unions of object-like variants in v1.'
|
|
2240
|
+
: `${macroName} only supports // #[tagged] unions of object-like variants in v1.`,
|
|
2241
|
+
declaration,
|
|
2242
|
+
);
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
function collectTaggedDerivedVariants<TField>(
|
|
2246
|
+
ctx: DeriveContext,
|
|
2247
|
+
declaration: MacroTypeAliasDeclSyntax,
|
|
2248
|
+
macroName: 'codec' | 'decode' | 'encode' | 'eq' | 'hash',
|
|
2249
|
+
collectField: (
|
|
2250
|
+
field: MacroReflectedFieldShape,
|
|
2251
|
+
ownerTypeName: string,
|
|
2252
|
+
scopeNode: MacroTypeAliasDeclSyntax,
|
|
2253
|
+
) => TField | null,
|
|
2254
|
+
): {
|
|
2255
|
+
readonly discriminantName: string;
|
|
2256
|
+
readonly typeName: string;
|
|
2257
|
+
readonly variants: readonly TaggedDerivedVariant<TField>[];
|
|
2258
|
+
} {
|
|
2259
|
+
const discriminantName = taggedDiscriminantName(ctx, declaration);
|
|
2260
|
+
const shape = ctx.reflect.declarationShape(declaration);
|
|
2261
|
+
if (shape.kind !== 'discriminatedUnion') {
|
|
2262
|
+
return validateTaggedUnionSyntaxForDiagnostics(ctx, declaration, macroName, discriminantName);
|
|
2263
|
+
}
|
|
2264
|
+
const unionShape = shape;
|
|
2265
|
+
const typeName = unionShape.name ??
|
|
2266
|
+
ctx.error(`${macroName} currently requires named type aliases.`, declaration);
|
|
2267
|
+
|
|
2268
|
+
const variants = unionShape.variants.map((variant) => {
|
|
2269
|
+
const discriminant = variant.discriminants.find((entry) => entry.name === discriminantName);
|
|
2270
|
+
if (!discriminant) {
|
|
2271
|
+
ctx.error(
|
|
2272
|
+
`${macroName} requires each tagged variant to declare the discriminant property "${discriminantName}".`,
|
|
2273
|
+
variant.node,
|
|
2274
|
+
);
|
|
2275
|
+
}
|
|
2276
|
+
const fields: TField[] = [];
|
|
2277
|
+
|
|
2278
|
+
for (const reflectedField of variant.fields) {
|
|
2279
|
+
const field = collectField(reflectedField, typeName, declaration);
|
|
2280
|
+
if (field !== null) {
|
|
2281
|
+
fields.push(field);
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
return {
|
|
2286
|
+
fields,
|
|
2287
|
+
tag: discriminant.tag,
|
|
2288
|
+
};
|
|
2289
|
+
});
|
|
2290
|
+
|
|
2291
|
+
return { discriminantName, typeName, variants };
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
function taggedVariantTypeText(typeName: string, discriminantName: string, tag: string): string {
|
|
2295
|
+
return `Extract<${typeName}, { ${propertyKeyText(discriminantName)}: "${tag}" }>`;
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
function foldUnionText(helperText: string, expressions: readonly string[]): string {
|
|
2299
|
+
const [first, ...rest] = expressions;
|
|
2300
|
+
if (!first) {
|
|
2301
|
+
throw new Error('foldUnionText requires at least one expression.');
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
return rest.reduce((current, expression) => `${helperText}(${current}, ${expression})`, first);
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
function collectTaggedVariants(
|
|
2308
|
+
ctx: Parameters<MacroDefinition<typeof TAGGED_SIGNATURE>['expand']>[0],
|
|
2309
|
+
declaration: MacroTypeAliasDeclSyntax,
|
|
2310
|
+
): {
|
|
2311
|
+
readonly discriminantName: string | null;
|
|
2312
|
+
readonly typeName: string;
|
|
2313
|
+
readonly variants: readonly TaggedVariant[];
|
|
2314
|
+
} {
|
|
2315
|
+
const shape = ctx.reflect.declarationShape(declaration);
|
|
2316
|
+
if (shape.kind === 'discriminatedUnion') {
|
|
2317
|
+
const discriminantName = taggedDiscriminantName(ctx, declaration);
|
|
2318
|
+
const unionShape = shape;
|
|
2319
|
+
const typeName = unionShape.name ??
|
|
2320
|
+
ctx.error('tagged currently requires named type aliases.', declaration);
|
|
2321
|
+
|
|
2322
|
+
const seenTags = new Set<string>();
|
|
2323
|
+
const variants = unionShape.variants.map((variant) => {
|
|
2324
|
+
const discriminant = variant.discriminants.find((entry) => entry.name === discriminantName);
|
|
2325
|
+
if (!discriminant) {
|
|
2326
|
+
ctx.error(
|
|
2327
|
+
`tagged requires each variant to declare the discriminant property "${discriminantName}".`,
|
|
2328
|
+
variant.node,
|
|
2329
|
+
);
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/u.test(discriminant.tag)) {
|
|
2333
|
+
ctx.error('tagged requires identifier-safe string literal tags in v1.', variant.node);
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
if (seenTags.has(discriminant.tag)) {
|
|
2337
|
+
ctx.error(`tagged requires unique "${discriminantName}" variant tags.`, variant.node);
|
|
2338
|
+
}
|
|
2339
|
+
seenTags.add(discriminant.tag);
|
|
2340
|
+
|
|
2341
|
+
return {
|
|
2342
|
+
constructorName: discriminant.tag,
|
|
2343
|
+
constructorTypeParametersText: '',
|
|
2344
|
+
kind: 'object' as const,
|
|
2345
|
+
payloadFields: variant.fields.map((field) => {
|
|
2346
|
+
if (!field.type) {
|
|
2347
|
+
ctx.error(
|
|
2348
|
+
'tagged currently requires explicit payload property types in v1.',
|
|
2349
|
+
field.node,
|
|
2350
|
+
);
|
|
2351
|
+
}
|
|
2352
|
+
return {
|
|
2353
|
+
name: field.name,
|
|
2354
|
+
optional: field.optional,
|
|
2355
|
+
typeText: field.type.text,
|
|
2356
|
+
};
|
|
2357
|
+
}),
|
|
2358
|
+
predicateConditionText: `${
|
|
2359
|
+
propertyAccessText('value', discriminantName)
|
|
2360
|
+
} === '${discriminant.tag}'`,
|
|
2361
|
+
predicateName: `is${capitalizeName(discriminant.tag)}`,
|
|
2362
|
+
predicateNarrowTypeText: `Extract<${typeName}, { ${
|
|
2363
|
+
propertyKeyText(discriminantName)
|
|
2364
|
+
}: "${discriminant.tag}" }>`,
|
|
2365
|
+
predicateTypeParametersText: '',
|
|
2366
|
+
predicateValueTypeText: typeName,
|
|
2367
|
+
returnExpressionText: variant.fields.length === 0
|
|
2368
|
+
? `{ ${propertyKeyText(discriminantName)}: '${discriminant.tag}' }`
|
|
2369
|
+
: `{ ${propertyKeyText(discriminantName)}: '${discriminant.tag}', ...payload }`,
|
|
2370
|
+
};
|
|
2371
|
+
});
|
|
2372
|
+
|
|
2373
|
+
return { discriminantName, typeName, variants };
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
const unionType = declaration.type.asUnion();
|
|
2377
|
+
if (!unionType) {
|
|
2378
|
+
return validateTaggedUnionSyntaxForDiagnostics(
|
|
2379
|
+
ctx,
|
|
2380
|
+
declaration,
|
|
2381
|
+
'tagged',
|
|
2382
|
+
taggedDiscriminantName(ctx, declaration),
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
if (unionType.members.every((member) => member.asObjectLiteral() !== null)) {
|
|
2386
|
+
return validateTaggedUnionSyntaxForDiagnostics(
|
|
2387
|
+
ctx,
|
|
2388
|
+
declaration,
|
|
2389
|
+
'tagged',
|
|
2390
|
+
taggedDiscriminantName(ctx, declaration),
|
|
2391
|
+
);
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
const hostDeclaration = getHostDeclaration(declaration);
|
|
2395
|
+
if (!ts.isTypeAliasDeclaration(hostDeclaration)) {
|
|
2396
|
+
return validateTaggedUnionSyntaxForDiagnostics(
|
|
2397
|
+
ctx,
|
|
2398
|
+
declaration,
|
|
2399
|
+
'tagged',
|
|
2400
|
+
taggedDiscriminantName(ctx, declaration),
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
const sourceFile = hostDeclaration.getSourceFile();
|
|
2404
|
+
const hostUnionType = ts.isUnionTypeNode(hostDeclaration.type)
|
|
2405
|
+
? hostDeclaration.type
|
|
2406
|
+
: undefined;
|
|
2407
|
+
if (!hostUnionType) {
|
|
2408
|
+
return validateTaggedUnionSyntaxForDiagnostics(
|
|
2409
|
+
ctx,
|
|
2410
|
+
declaration,
|
|
2411
|
+
'tagged',
|
|
2412
|
+
taggedDiscriminantName(ctx, declaration),
|
|
2413
|
+
);
|
|
2414
|
+
}
|
|
2415
|
+
const typeName = hostDeclaration.name?.text ??
|
|
2416
|
+
ctx.error('tagged currently requires named type aliases.', declaration);
|
|
2417
|
+
const aliasTypeParameters =
|
|
2418
|
+
hostDeclaration.typeParameters?.map((parameter) => parameter.getText(sourceFile)) ?? [];
|
|
2419
|
+
const aliasTypeParametersText = aliasTypeParameters.length === 0
|
|
2420
|
+
? ''
|
|
2421
|
+
: `<${aliasTypeParameters.join(', ')}>`;
|
|
2422
|
+
const aliasAppliedTypeText = `${typeName}${aliasTypeParametersText}`;
|
|
2423
|
+
const variants = hostUnionType.types.map((hostMember: ts.TypeNode, index: number) => {
|
|
2424
|
+
if (!ts.isTypeReferenceNode(hostMember)) {
|
|
2425
|
+
ctx.error(
|
|
2426
|
+
'tagged only supports unions of object-like variants or named classes in the same module in v1.',
|
|
2427
|
+
declaration,
|
|
2428
|
+
);
|
|
2429
|
+
}
|
|
2430
|
+
const member = unionType.members[index];
|
|
2431
|
+
if (!member) {
|
|
2432
|
+
ctx.error(
|
|
2433
|
+
'tagged only supports unions of object-like variants or named classes in the same module in v1.',
|
|
2434
|
+
declaration,
|
|
2435
|
+
);
|
|
2436
|
+
}
|
|
2437
|
+
const classDeclaration = ctx.semantics.classDeclarationOfType(member);
|
|
2438
|
+
if (!classDeclaration || classDeclaration.span.fileName !== sourceFile.fileName) {
|
|
2439
|
+
ctx.error(
|
|
2440
|
+
'tagged only supports unions of object-like variants or named classes in the same module in v1.',
|
|
2441
|
+
declaration,
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
const className = classDeclaration.name ??
|
|
2445
|
+
ctx.error(
|
|
2446
|
+
'tagged only supports unions of object-like variants or named classes in the same module in v1.',
|
|
2447
|
+
declaration,
|
|
2448
|
+
);
|
|
2449
|
+
const constructor = classDeclaration.members().find((classMember) =>
|
|
2450
|
+
classMember.memberKind === 'constructor'
|
|
2451
|
+
);
|
|
2452
|
+
const payloadFields = constructor
|
|
2453
|
+
? constructor.parameters.map((parameter) => {
|
|
2454
|
+
const explicitType = parameter.explicitType();
|
|
2455
|
+
if (!parameter.name || !explicitType) {
|
|
2456
|
+
ctx.error(
|
|
2457
|
+
'tagged value-class variants require constructors with simple identifier parameters and explicit types in v1.',
|
|
2458
|
+
declaration,
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2461
|
+
return {
|
|
2462
|
+
name: parameter.name,
|
|
2463
|
+
optional: false,
|
|
2464
|
+
typeText: explicitType.text(),
|
|
2465
|
+
};
|
|
2466
|
+
})
|
|
2467
|
+
: [];
|
|
2468
|
+
|
|
2469
|
+
const variantTypeParameters = hostMember.typeArguments?.flatMap((typeArgument) =>
|
|
2470
|
+
ts.isTypeReferenceNode(typeArgument) && ts.isIdentifier(typeArgument.typeName) &&
|
|
2471
|
+
!typeArgument.typeArguments?.length
|
|
2472
|
+
? [typeArgument.typeName.text]
|
|
2473
|
+
: []
|
|
2474
|
+
) ?? [];
|
|
2475
|
+
const variantTypeParametersText = variantTypeParameters.length === 0
|
|
2476
|
+
? ''
|
|
2477
|
+
: `<${variantTypeParameters.join(', ')}>`;
|
|
2478
|
+
|
|
2479
|
+
return {
|
|
2480
|
+
constructorName: decapitalizeName(className),
|
|
2481
|
+
constructorTypeParametersText: variantTypeParametersText,
|
|
2482
|
+
kind: 'class' as const,
|
|
2483
|
+
payloadFields,
|
|
2484
|
+
predicateConditionText: `value instanceof ${className}`,
|
|
2485
|
+
predicateName: `is${className}`,
|
|
2486
|
+
predicateNarrowTypeText: hostMember.getText(sourceFile),
|
|
2487
|
+
predicateTypeParametersText: aliasTypeParametersText,
|
|
2488
|
+
predicateValueTypeText: aliasAppliedTypeText,
|
|
2489
|
+
returnExpressionText: payloadFields.length === 0
|
|
2490
|
+
? `new ${className}()`
|
|
2491
|
+
: `new ${className}(${
|
|
2492
|
+
payloadFields.map((field) => field.name).join(', ')
|
|
2493
|
+
})`,
|
|
2494
|
+
};
|
|
2495
|
+
});
|
|
2496
|
+
|
|
2497
|
+
return {
|
|
2498
|
+
discriminantName: null,
|
|
2499
|
+
typeName,
|
|
2500
|
+
variants,
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
function taggedPayloadTypeText(fields: readonly TaggedVariantField[]): string {
|
|
2505
|
+
if (fields.length === 0) {
|
|
2506
|
+
return '{}';
|
|
2507
|
+
}
|
|
2508
|
+
return `{ ${
|
|
2509
|
+
fields.map((field) =>
|
|
2510
|
+
`${propertyKeyText(field.name)}${field.optional ? '?' : ''}: ${field.typeText}`
|
|
2511
|
+
).join('; ')
|
|
2512
|
+
} }`;
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
function taggedConstructorText(
|
|
2516
|
+
discriminantName: string | null,
|
|
2517
|
+
typeName: string,
|
|
2518
|
+
variant: TaggedVariant,
|
|
2519
|
+
): string {
|
|
2520
|
+
const returnType = variant.kind === 'class' ? variant.predicateNarrowTypeText : typeName;
|
|
2521
|
+
const parameterText = variant.payloadFields.length === 0
|
|
2522
|
+
? ''
|
|
2523
|
+
: variant.kind === 'class'
|
|
2524
|
+
? variant.payloadFields.map((field) => `${field.name}: ${field.typeText}`).join(', ')
|
|
2525
|
+
: `payload: ${taggedPayloadTypeText(variant.payloadFields)}`;
|
|
2526
|
+
const typeParametersText = variant.constructorTypeParametersText;
|
|
2527
|
+
|
|
2528
|
+
if (parameterText.length === 0) {
|
|
2529
|
+
return `${variant.constructorName}${typeParametersText}(): ${returnType} {
|
|
2530
|
+
return ${variant.returnExpressionText};
|
|
2531
|
+
}`;
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
return `${variant.constructorName}${typeParametersText}(${parameterText}): ${returnType} {
|
|
2535
|
+
return ${variant.returnExpressionText};
|
|
2536
|
+
}`;
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
function taggedPredicateText(
|
|
2540
|
+
_discriminantName: string | null,
|
|
2541
|
+
_typeName: string,
|
|
2542
|
+
variant: TaggedVariant,
|
|
2543
|
+
): string {
|
|
2544
|
+
return `${variant.predicateName}${variant.predicateTypeParametersText}(value: ${variant.predicateValueTypeText}): value is ${variant.predicateNarrowTypeText} {
|
|
2545
|
+
return ${variant.predicateConditionText};
|
|
2546
|
+
}`;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
function taggedEqCaseText(
|
|
2550
|
+
discriminantName: string,
|
|
2551
|
+
typeName: string,
|
|
2552
|
+
variant: TaggedDerivedVariant<DerivedField>,
|
|
2553
|
+
): string {
|
|
2554
|
+
const rightName = `right${capitalizeName(variant.tag)}`;
|
|
2555
|
+
const equalsBody = variant.fields.length === 0
|
|
2556
|
+
? 'true'
|
|
2557
|
+
: variant.fields.map((field) => eqCheckText(field, 'left', rightName)).join(' && ');
|
|
2558
|
+
return `case '${variant.tag}': {
|
|
2559
|
+
const ${rightName} = right as ${taggedVariantTypeText(typeName, discriminantName, variant.tag)};
|
|
2560
|
+
return ${equalsBody};
|
|
2561
|
+
}`;
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
function taggedHashCaseText(
|
|
2565
|
+
discriminantName: string,
|
|
2566
|
+
typeName: string,
|
|
2567
|
+
variant: TaggedDerivedVariant<DerivedField>,
|
|
2568
|
+
stringHashText: string,
|
|
2569
|
+
combineHashesText: string,
|
|
2570
|
+
): string {
|
|
2571
|
+
const rightName = `right${capitalizeName(variant.tag)}`;
|
|
2572
|
+
const payloadHashes = variant.fields.map((field) => hashExprText(field, 'value'));
|
|
2573
|
+
const hashExpr = `${combineHashesText}(${
|
|
2574
|
+
[
|
|
2575
|
+
`${stringHashText}.hash(${JSON.stringify(variant.tag)})`,
|
|
2576
|
+
...payloadHashes,
|
|
2577
|
+
].join(', ')
|
|
2578
|
+
})`;
|
|
2579
|
+
const equalsBody = variant.fields.length === 0
|
|
2580
|
+
? 'true'
|
|
2581
|
+
: variant.fields.map((field) => eqCheckText(field, 'value', rightName)).join(' && ');
|
|
2582
|
+
return `case '${variant.tag}': {
|
|
2583
|
+
if (mode === 'hash') {
|
|
2584
|
+
return ${hashExpr};
|
|
2585
|
+
}
|
|
2586
|
+
const ${rightName} = right as ${taggedVariantTypeText(typeName, discriminantName, variant.tag)};
|
|
2587
|
+
return ${equalsBody};
|
|
2588
|
+
}`;
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
function taggedDecodeVariantText(
|
|
2592
|
+
discriminantName: string,
|
|
2593
|
+
variant: TaggedDerivedVariant<DecodedField>,
|
|
2594
|
+
decodeLiteralText: string,
|
|
2595
|
+
decodeMapText: string,
|
|
2596
|
+
decodeObjectText: string,
|
|
2597
|
+
decodeOptionalText: string,
|
|
2598
|
+
): string {
|
|
2599
|
+
const shapeEntries = [
|
|
2600
|
+
`${propertyKeyText(discriminantName)}: ${decodeLiteralText}(${JSON.stringify(variant.tag)})`,
|
|
2601
|
+
...variant.fields.map((field) =>
|
|
2602
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
2603
|
+
field.optional ? `${decodeOptionalText}(${field.decoderText})` : field.decoderText
|
|
2604
|
+
}`
|
|
2605
|
+
),
|
|
2606
|
+
];
|
|
2607
|
+
const projectionEntries = [
|
|
2608
|
+
`${propertyKeyText(discriminantName)}: ${JSON.stringify(variant.tag)}`,
|
|
2609
|
+
...variant.fields.map((field) =>
|
|
2610
|
+
`${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`
|
|
2611
|
+
),
|
|
2612
|
+
];
|
|
2613
|
+
return `${decodeMapText}(
|
|
2614
|
+
${decodeObjectText}({
|
|
2615
|
+
${shapeEntries.join(',\n')}
|
|
2616
|
+
}),
|
|
2617
|
+
(value) => ({
|
|
2618
|
+
${projectionEntries.join(',\n')}
|
|
2619
|
+
}),
|
|
2620
|
+
)`;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
function taggedEncodeVariantProjectionText(
|
|
2624
|
+
discriminantName: string,
|
|
2625
|
+
variant: TaggedDerivedVariant<EncodedField>,
|
|
2626
|
+
receiverName: string,
|
|
2627
|
+
): string {
|
|
2628
|
+
return `({
|
|
2629
|
+
${
|
|
2630
|
+
[
|
|
2631
|
+
`${propertyKeyText(discriminantName)}: ${JSON.stringify(variant.tag)}`,
|
|
2632
|
+
...variant.fields.map((field) =>
|
|
2633
|
+
`${propertyKeyText(field.wireName)}: ${propertyAccessText(receiverName, field.localName)}`
|
|
2634
|
+
),
|
|
2635
|
+
].join(',\n')
|
|
2636
|
+
}
|
|
2637
|
+
})`;
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
function taggedEncodeVariantShapeText(
|
|
2641
|
+
discriminantName: string,
|
|
2642
|
+
variant: TaggedDerivedVariant<EncodedField>,
|
|
2643
|
+
encodeOptionalText: string,
|
|
2644
|
+
stringEncoderText: string,
|
|
2645
|
+
): string {
|
|
2646
|
+
return `{
|
|
2647
|
+
${
|
|
2648
|
+
[
|
|
2649
|
+
`${propertyKeyText(discriminantName)}: ${stringEncoderText}`,
|
|
2650
|
+
...variant.fields.map((field) =>
|
|
2651
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
2652
|
+
field.optional ? `${encodeOptionalText}(${field.encoderText})` : field.encoderText
|
|
2653
|
+
}`
|
|
2654
|
+
),
|
|
2655
|
+
].join(',\n')
|
|
2656
|
+
}
|
|
2657
|
+
}`;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
function taggedCodecDecodeVariantText(
|
|
2661
|
+
discriminantName: string,
|
|
2662
|
+
variant: TaggedDerivedVariant<CodecField>,
|
|
2663
|
+
decodeLiteralText: string,
|
|
2664
|
+
decodeMapText: string,
|
|
2665
|
+
decodeObjectText: string,
|
|
2666
|
+
decodeOptionalText: string,
|
|
2667
|
+
): string {
|
|
2668
|
+
const shapeEntries = [
|
|
2669
|
+
`${propertyKeyText(discriminantName)}: ${decodeLiteralText}(${JSON.stringify(variant.tag)})`,
|
|
2670
|
+
...variant.fields.map((field) =>
|
|
2671
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
2672
|
+
field.optional ? `${decodeOptionalText}(${field.decodeText})` : field.decodeText
|
|
2673
|
+
}`
|
|
2674
|
+
),
|
|
2675
|
+
];
|
|
2676
|
+
const projectionEntries = [
|
|
2677
|
+
`${propertyKeyText(discriminantName)}: ${JSON.stringify(variant.tag)}`,
|
|
2678
|
+
...variant.fields.map((field) =>
|
|
2679
|
+
`${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`
|
|
2680
|
+
),
|
|
2681
|
+
];
|
|
2682
|
+
return `${decodeMapText}(
|
|
2683
|
+
${decodeObjectText}({
|
|
2684
|
+
${shapeEntries.join(',\n')}
|
|
2685
|
+
}),
|
|
2686
|
+
(value) => ({
|
|
2687
|
+
${projectionEntries.join(',\n')}
|
|
2688
|
+
}),
|
|
2689
|
+
)`;
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
function taggedCodecEncodeVariantShapeText(
|
|
2693
|
+
discriminantName: string,
|
|
2694
|
+
variant: TaggedDerivedVariant<CodecField>,
|
|
2695
|
+
encodeOptionalText: string,
|
|
2696
|
+
stringEncoderText: string,
|
|
2697
|
+
): string {
|
|
2698
|
+
return `{
|
|
2699
|
+
${
|
|
2700
|
+
[
|
|
2701
|
+
`${propertyKeyText(discriminantName)}: ${stringEncoderText}`,
|
|
2702
|
+
...variant.fields.map((field) =>
|
|
2703
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
2704
|
+
field.optional ? `${encodeOptionalText}(${field.encodeText})` : field.encodeText
|
|
2705
|
+
}`
|
|
2706
|
+
),
|
|
2707
|
+
].join(',\n')
|
|
2708
|
+
}
|
|
2709
|
+
}`;
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
function taggedCodecEncodeVariantProjectionText(
|
|
2713
|
+
discriminantName: string,
|
|
2714
|
+
variant: TaggedDerivedVariant<CodecField>,
|
|
2715
|
+
receiverName: string,
|
|
2716
|
+
): string {
|
|
2717
|
+
return `({
|
|
2718
|
+
${
|
|
2719
|
+
[
|
|
2720
|
+
`${propertyKeyText(discriminantName)}: ${JSON.stringify(variant.tag)}`,
|
|
2721
|
+
...variant.fields.map((field) =>
|
|
2722
|
+
`${propertyKeyText(field.wireName)}: ${propertyAccessText(receiverName, field.localName)}`
|
|
2723
|
+
),
|
|
2724
|
+
].join(',\n')
|
|
2725
|
+
}
|
|
2726
|
+
})`;
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
// #[macro(decl)]
|
|
2730
|
+
export function eq(): MacroDefinition<typeof DERIVE_SIGNATURE> {
|
|
2731
|
+
return {
|
|
2732
|
+
declarationKinds: ['class', 'interface', 'typeAlias'],
|
|
2733
|
+
expansionMode: 'augment',
|
|
2734
|
+
expand(ctx, decoded) {
|
|
2735
|
+
if (decoded.caseName === 'typeAlias') {
|
|
2736
|
+
const declaration = decoded.args.target as MacroTypeAliasDeclSyntax;
|
|
2737
|
+
if (!declaration.type.asObjectLiteral() && declaration.type.asUnion()) {
|
|
2738
|
+
const { discriminantName, typeName, variants } = collectTaggedDerivedVariants(
|
|
2739
|
+
ctx,
|
|
2740
|
+
declaration,
|
|
2741
|
+
'eq',
|
|
2742
|
+
(field, ownerTypeName, scopeNode) =>
|
|
2743
|
+
eqHashFieldFromReflectedShape(ctx, field, 'eq', ownerTypeName, scopeNode),
|
|
2744
|
+
);
|
|
2745
|
+
const switchCases = variants.map((variant) =>
|
|
2746
|
+
taggedEqCaseText(discriminantName, typeName, variant)
|
|
2747
|
+
).join('\n');
|
|
2748
|
+
return ctx.output.stmt(
|
|
2749
|
+
ctx.quote.stmt`
|
|
2750
|
+
export const ${`${typeName}Eq`} = {
|
|
2751
|
+
equals(left: ${typeName}, right: ${typeName}) {
|
|
2752
|
+
if (${propertyAccessText('left', discriminantName)} !== ${
|
|
2753
|
+
propertyAccessText('right', discriminantName)
|
|
2754
|
+
}) {
|
|
2755
|
+
return false;
|
|
2756
|
+
}
|
|
2757
|
+
switch (${propertyAccessText('left', discriminantName)}) {
|
|
2758
|
+
${switchCases}
|
|
2759
|
+
default:
|
|
2760
|
+
return false;
|
|
2761
|
+
}
|
|
2762
|
+
},
|
|
2763
|
+
};
|
|
2764
|
+
`,
|
|
2765
|
+
);
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
const { fields, typeName } = collectFields(ctx, 'eq', decoded);
|
|
2770
|
+
const equalsBody = fields.length === 0
|
|
2771
|
+
? 'true'
|
|
2772
|
+
: fields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
2773
|
+
return ctx.output.stmt(
|
|
2774
|
+
ctx.quote.stmt`
|
|
2775
|
+
export const ${`${typeName}Eq`} = {
|
|
2776
|
+
equals(left: ${typeName}, right: ${typeName}) {
|
|
2777
|
+
return ${equalsBody};
|
|
2778
|
+
},
|
|
2779
|
+
};
|
|
2780
|
+
`,
|
|
2781
|
+
);
|
|
2782
|
+
},
|
|
2783
|
+
signature: DERIVE_SIGNATURE,
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2786
|
+
attachDeriveFactory(eq);
|
|
2787
|
+
|
|
2788
|
+
// #[macro(decl)]
|
|
2789
|
+
export function hash(): MacroDefinition<typeof DERIVE_SIGNATURE> {
|
|
2790
|
+
return {
|
|
2791
|
+
declarationKinds: ['class', 'interface', 'typeAlias'],
|
|
2792
|
+
expansionMode: 'augment',
|
|
2793
|
+
expand(ctx, decoded) {
|
|
2794
|
+
if (decoded.caseName === 'typeAlias') {
|
|
2795
|
+
const declaration = decoded.args.target as MacroTypeAliasDeclSyntax;
|
|
2796
|
+
if (!declaration.type.asObjectLiteral() && declaration.type.asUnion()) {
|
|
2797
|
+
const { discriminantName, typeName, variants } = collectTaggedDerivedVariants(
|
|
2798
|
+
ctx,
|
|
2799
|
+
declaration,
|
|
2800
|
+
'hash',
|
|
2801
|
+
(field, ownerTypeName, scopeNode) =>
|
|
2802
|
+
eqHashFieldFromReflectedShape(ctx, field, 'hash', ownerTypeName, scopeNode),
|
|
2803
|
+
);
|
|
2804
|
+
const fromHashEq = ctx.runtime.named('sts:hash', 'fromHashEq').text();
|
|
2805
|
+
const combineHashes = ctx.runtime.named('sts:hash', 'combineHashes').text();
|
|
2806
|
+
const stringHash = ctx.runtime.named('sts:hash', 'stringHash').text();
|
|
2807
|
+
const switchCases = variants.map((variant) =>
|
|
2808
|
+
taggedHashCaseText(discriminantName, typeName, variant, stringHash, combineHashes)
|
|
2809
|
+
).join('\n');
|
|
2810
|
+
return ctx.output.stmt(
|
|
2811
|
+
ctx.quote.stmt`
|
|
2812
|
+
export const ${`${typeName}Hash`} = ${fromHashEq}<${typeName}>(
|
|
2813
|
+
(value) => {
|
|
2814
|
+
const mode = 'hash' as const;
|
|
2815
|
+
switch (${propertyAccessText('value', discriminantName)}) {
|
|
2816
|
+
${switchCases}
|
|
2817
|
+
default:
|
|
2818
|
+
return 0;
|
|
2819
|
+
}
|
|
2820
|
+
},
|
|
2821
|
+
(value, right) => {
|
|
2822
|
+
const mode = 'equals' as const;
|
|
2823
|
+
if (${propertyAccessText('value', discriminantName)} !== ${
|
|
2824
|
+
propertyAccessText('right', discriminantName)
|
|
2825
|
+
}) {
|
|
2826
|
+
return false;
|
|
2827
|
+
}
|
|
2828
|
+
switch (${propertyAccessText('value', discriminantName)}) {
|
|
2829
|
+
${switchCases}
|
|
2830
|
+
default:
|
|
2831
|
+
return false;
|
|
2832
|
+
}
|
|
2833
|
+
},
|
|
2834
|
+
);
|
|
2835
|
+
`,
|
|
2836
|
+
);
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
const { fields, typeName } = collectFields(ctx, 'hash', decoded);
|
|
2841
|
+
const fromHashEq = ctx.runtime.named('sts:hash', 'fromHashEq').text();
|
|
2842
|
+
const combineHashes = ctx.runtime.named('sts:hash', 'combineHashes').text();
|
|
2843
|
+
const hashArgs = fields.map((field) => hashExprText(field, 'value')).join(', ');
|
|
2844
|
+
const equalsBody = fields.length === 0
|
|
2845
|
+
? 'true'
|
|
2846
|
+
: fields.map((field) => eqCheckText(field, 'left', 'right')).join(' && ');
|
|
2847
|
+
|
|
2848
|
+
return ctx.output.stmt(
|
|
2849
|
+
ctx.quote.stmt`
|
|
2850
|
+
export const ${`${typeName}Hash`} = ${fromHashEq}<${typeName}>(
|
|
2851
|
+
(value) => ${combineHashes}(${hashArgs}),
|
|
2852
|
+
(left, right) => ${equalsBody},
|
|
2853
|
+
);
|
|
2854
|
+
`,
|
|
2855
|
+
);
|
|
2856
|
+
},
|
|
2857
|
+
signature: DERIVE_SIGNATURE,
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
attachDeriveFactory(hash);
|
|
2861
|
+
|
|
2862
|
+
// #[macro(decl)]
|
|
2863
|
+
export function tagged(): MacroDefinition<typeof TAGGED_SIGNATURE> {
|
|
2864
|
+
return {
|
|
2865
|
+
declarationKinds: ['typeAlias'],
|
|
2866
|
+
expansionMode: 'augment',
|
|
2867
|
+
expand(ctx, decoded) {
|
|
2868
|
+
const declaration = decoded.args.target;
|
|
2869
|
+
const { discriminantName, typeName, variants } = collectTaggedVariants(ctx, declaration);
|
|
2870
|
+
const companionMembers = variants.flatMap((variant) => [
|
|
2871
|
+
taggedConstructorText(discriminantName, typeName, variant),
|
|
2872
|
+
taggedPredicateText(discriminantName, typeName, variant),
|
|
2873
|
+
]).join(',\n');
|
|
2874
|
+
|
|
2875
|
+
return ctx.output.stmt(
|
|
2876
|
+
ctx.quote.stmt`
|
|
2877
|
+
export const ${`${typeName}Tagged`} = {
|
|
2878
|
+
${companionMembers}
|
|
2879
|
+
};
|
|
2880
|
+
`,
|
|
2881
|
+
);
|
|
2882
|
+
},
|
|
2883
|
+
signature: TAGGED_SIGNATURE,
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
attachDeriveFactory(tagged);
|
|
2887
|
+
|
|
2888
|
+
// #[macro(decl)]
|
|
2889
|
+
export function decode(): MacroDefinition<typeof DECODE_SIGNATURE> {
|
|
2890
|
+
return {
|
|
2891
|
+
declarationKinds: ['class', 'interface', 'typeAlias'],
|
|
2892
|
+
expansionMode: 'augment',
|
|
2893
|
+
expand(ctx, decoded) {
|
|
2894
|
+
if (decoded.caseName === 'typeAlias') {
|
|
2895
|
+
const declaration = decoded.args.target as MacroTypeAliasDeclSyntax;
|
|
2896
|
+
if (!declaration.type.asObjectLiteral() && declaration.type.asUnion()) {
|
|
2897
|
+
const { discriminantName, typeName, variants } = collectTaggedDerivedVariants(
|
|
2898
|
+
ctx,
|
|
2899
|
+
declaration,
|
|
2900
|
+
'decode',
|
|
2901
|
+
(field, ownerTypeName, scopeNode) =>
|
|
2902
|
+
decodeFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode),
|
|
2903
|
+
);
|
|
2904
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
2905
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
2906
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
2907
|
+
const decodeLiteral = ctx.runtime.named('sts:decode', 'literal').text();
|
|
2908
|
+
const decodeUnion = ctx.runtime.named('sts:decode', 'union').text();
|
|
2909
|
+
const variantDecoders = variants.map((variant) =>
|
|
2910
|
+
taggedDecodeVariantText(
|
|
2911
|
+
discriminantName,
|
|
2912
|
+
variant,
|
|
2913
|
+
decodeLiteral,
|
|
2914
|
+
decodeMap,
|
|
2915
|
+
decodeObject,
|
|
2916
|
+
decodeOptional,
|
|
2917
|
+
)
|
|
2918
|
+
);
|
|
2919
|
+
const unionText = foldUnionText(decodeUnion, variantDecoders);
|
|
2920
|
+
return ctx.output.stmt(
|
|
2921
|
+
ctx.quote.stmt`
|
|
2922
|
+
export const ${`${typeName}Decoder`} = ${unionText};
|
|
2923
|
+
`,
|
|
2924
|
+
);
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
const { fields, instantiateText, typeName } = collectDecodeFields(ctx, decoded);
|
|
2929
|
+
const object = ctx.runtime.named('sts:decode', 'object').text();
|
|
2930
|
+
const map = ctx.runtime.named('sts:decode', 'map').text();
|
|
2931
|
+
const optional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
2932
|
+
const shapeText = fields.length === 0 ? '{}' : `{
|
|
2933
|
+
${
|
|
2934
|
+
fields.map((field) =>
|
|
2935
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
2936
|
+
field.optional ? `${optional}(${field.decoderText})` : field.decoderText
|
|
2937
|
+
}`
|
|
2938
|
+
).join(',\n')
|
|
2939
|
+
}
|
|
2940
|
+
}`;
|
|
2941
|
+
|
|
2942
|
+
const projectionText = fields.length === 0 ? '{}' : `({
|
|
2943
|
+
${
|
|
2944
|
+
fields.map((field) =>
|
|
2945
|
+
`${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`
|
|
2946
|
+
).join(',\n')
|
|
2947
|
+
}
|
|
2948
|
+
})`;
|
|
2949
|
+
const finalProjectionText = instantiateText === null
|
|
2950
|
+
? projectionText
|
|
2951
|
+
: `(${instantiateText.replace(CLASS_DECODE_VALUE_PLACEHOLDER, projectionText)})`;
|
|
2952
|
+
|
|
2953
|
+
return ctx.output.stmt(
|
|
2954
|
+
ctx.quote.stmt`
|
|
2955
|
+
export const ${`${typeName}Decoder`} = ${map}(
|
|
2956
|
+
${object}(${shapeText}),
|
|
2957
|
+
(value) => ${finalProjectionText},
|
|
2958
|
+
);
|
|
2959
|
+
`,
|
|
2960
|
+
);
|
|
2961
|
+
},
|
|
2962
|
+
signature: DECODE_SIGNATURE,
|
|
2963
|
+
};
|
|
2964
|
+
}
|
|
2965
|
+
attachDeriveFactory(decode);
|
|
2966
|
+
|
|
2967
|
+
// #[macro(decl)]
|
|
2968
|
+
export function encode(): MacroDefinition<typeof DECODE_SIGNATURE> {
|
|
2969
|
+
return {
|
|
2970
|
+
declarationKinds: ['class', 'interface', 'typeAlias'],
|
|
2971
|
+
expansionMode: 'augment',
|
|
2972
|
+
expand(ctx, decoded) {
|
|
2973
|
+
if (decoded.caseName === 'typeAlias') {
|
|
2974
|
+
const declaration = decoded.args.target as MacroTypeAliasDeclSyntax;
|
|
2975
|
+
if (!declaration.type.asObjectLiteral() && declaration.type.asUnion()) {
|
|
2976
|
+
const { discriminantName, typeName, variants } = collectTaggedDerivedVariants(
|
|
2977
|
+
ctx,
|
|
2978
|
+
declaration,
|
|
2979
|
+
'encode',
|
|
2980
|
+
(field, ownerTypeName, scopeNode) =>
|
|
2981
|
+
encodeFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode),
|
|
2982
|
+
);
|
|
2983
|
+
const encodeFromEncode = ctx.runtime.named('sts:encode', 'fromEncode').text();
|
|
2984
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
2985
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
2986
|
+
const stringEncoder = ctx.runtime.named('sts:encode', 'stringEncoder').text();
|
|
2987
|
+
const switchCases = variants.map((variant) => {
|
|
2988
|
+
const variantType = taggedVariantTypeText(typeName, discriminantName, variant.tag);
|
|
2989
|
+
const shapeText = taggedEncodeVariantShapeText(
|
|
2990
|
+
discriminantName,
|
|
2991
|
+
variant,
|
|
2992
|
+
encodeOptional,
|
|
2993
|
+
stringEncoder,
|
|
2994
|
+
);
|
|
2995
|
+
const projectionText = taggedEncodeVariantProjectionText(
|
|
2996
|
+
discriminantName,
|
|
2997
|
+
variant,
|
|
2998
|
+
`value as ${variantType}`,
|
|
2999
|
+
);
|
|
3000
|
+
return `case '${variant.tag}':
|
|
3001
|
+
return ${encodeObject}(${shapeText}).encode(${projectionText});`;
|
|
3002
|
+
}).join('\n');
|
|
3003
|
+
return ctx.output.stmt(
|
|
3004
|
+
ctx.quote.stmt`
|
|
3005
|
+
export const ${`${typeName}Encoder`} = ${encodeFromEncode}((value: ${typeName}) => {
|
|
3006
|
+
switch (${propertyAccessText('value', discriminantName)}) {
|
|
3007
|
+
${switchCases}
|
|
3008
|
+
default:
|
|
3009
|
+
throw new Error('unreachable tagged union encoder case');
|
|
3010
|
+
}
|
|
3011
|
+
});
|
|
3012
|
+
`,
|
|
3013
|
+
);
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
const { fields, typeName } = collectEncodeFields(ctx, decoded);
|
|
3018
|
+
const contramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
3019
|
+
const object = ctx.runtime.named('sts:encode', 'object').text();
|
|
3020
|
+
const optional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
3021
|
+
const shapeText = fields.length === 0 ? '{}' : `{
|
|
3022
|
+
${
|
|
3023
|
+
fields.map((field) =>
|
|
3024
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
3025
|
+
field.optional ? `${optional}(${field.encoderText})` : field.encoderText
|
|
3026
|
+
}`
|
|
3027
|
+
).join(',\n')
|
|
3028
|
+
}
|
|
3029
|
+
}`;
|
|
3030
|
+
|
|
3031
|
+
const projectionText = fields.length === 0 ? '({})' : `({
|
|
3032
|
+
${
|
|
3033
|
+
fields.map((field) =>
|
|
3034
|
+
`${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`
|
|
3035
|
+
).join(',\n')
|
|
3036
|
+
}
|
|
3037
|
+
})`;
|
|
3038
|
+
|
|
3039
|
+
return ctx.output.stmt(
|
|
3040
|
+
ctx.quote.stmt`
|
|
3041
|
+
export const ${`${typeName}Encoder`} = ${contramap}(
|
|
3042
|
+
${object}(${shapeText}),
|
|
3043
|
+
(value: ${typeName}) => ${projectionText},
|
|
3044
|
+
);
|
|
3045
|
+
`,
|
|
3046
|
+
);
|
|
3047
|
+
},
|
|
3048
|
+
signature: DECODE_SIGNATURE,
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
attachDeriveFactory(encode);
|
|
3052
|
+
|
|
3053
|
+
// #[macro(decl)]
|
|
3054
|
+
export function codec(): MacroDefinition<typeof DECODE_SIGNATURE> {
|
|
3055
|
+
return {
|
|
3056
|
+
declarationKinds: ['class', 'interface', 'typeAlias'],
|
|
3057
|
+
expansionMode: 'augment',
|
|
3058
|
+
expand(ctx, decoded) {
|
|
3059
|
+
if (decoded.caseName === 'typeAlias') {
|
|
3060
|
+
const declaration = decoded.args.target as MacroTypeAliasDeclSyntax;
|
|
3061
|
+
if (!declaration.type.asObjectLiteral() && declaration.type.asUnion()) {
|
|
3062
|
+
const { discriminantName, typeName, variants } = collectTaggedDerivedVariants(
|
|
3063
|
+
ctx,
|
|
3064
|
+
declaration,
|
|
3065
|
+
'codec',
|
|
3066
|
+
(field, ownerTypeName, scopeNode) =>
|
|
3067
|
+
codecFieldFromReflectedShape(ctx, field, ownerTypeName, scopeNode),
|
|
3068
|
+
);
|
|
3069
|
+
const createCodec = ctx.runtime.named('sts:codec', 'codec').text();
|
|
3070
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
3071
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
3072
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
3073
|
+
const decodeLiteral = ctx.runtime.named('sts:decode', 'literal').text();
|
|
3074
|
+
const decodeUnion = ctx.runtime.named('sts:decode', 'union').text();
|
|
3075
|
+
const encodeFromEncode = ctx.runtime.named('sts:encode', 'fromEncode').text();
|
|
3076
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
3077
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
3078
|
+
const stringEncoder = ctx.runtime.named('sts:encode', 'stringEncoder').text();
|
|
3079
|
+
const variantDecoders = variants.map((variant) =>
|
|
3080
|
+
taggedCodecDecodeVariantText(
|
|
3081
|
+
discriminantName,
|
|
3082
|
+
variant,
|
|
3083
|
+
decodeLiteral,
|
|
3084
|
+
decodeMap,
|
|
3085
|
+
decodeObject,
|
|
3086
|
+
decodeOptional,
|
|
3087
|
+
)
|
|
3088
|
+
);
|
|
3089
|
+
const unionText = foldUnionText(decodeUnion, variantDecoders);
|
|
3090
|
+
const switchCases = variants.map((variant) => {
|
|
3091
|
+
const variantType = taggedVariantTypeText(typeName, discriminantName, variant.tag);
|
|
3092
|
+
const shapeText = taggedCodecEncodeVariantShapeText(
|
|
3093
|
+
discriminantName,
|
|
3094
|
+
variant,
|
|
3095
|
+
encodeOptional,
|
|
3096
|
+
stringEncoder,
|
|
3097
|
+
);
|
|
3098
|
+
const projectionText = taggedCodecEncodeVariantProjectionText(
|
|
3099
|
+
discriminantName,
|
|
3100
|
+
variant,
|
|
3101
|
+
`value as ${variantType}`,
|
|
3102
|
+
);
|
|
3103
|
+
return `case '${variant.tag}':
|
|
3104
|
+
return ${encodeObject}(${shapeText}).encode(${projectionText});`;
|
|
3105
|
+
}).join('\n');
|
|
3106
|
+
return ctx.output.stmt(
|
|
3107
|
+
ctx.quote.stmt`
|
|
3108
|
+
export const ${`${typeName}Codec`} = ${createCodec}(
|
|
3109
|
+
${unionText},
|
|
3110
|
+
${encodeFromEncode}((value: ${typeName}) => {
|
|
3111
|
+
switch (${propertyAccessText('value', discriminantName)}) {
|
|
3112
|
+
${switchCases}
|
|
3113
|
+
default:
|
|
3114
|
+
throw new Error('unreachable tagged union codec case');
|
|
3115
|
+
}
|
|
3116
|
+
}),
|
|
3117
|
+
);
|
|
3118
|
+
`,
|
|
3119
|
+
);
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
const { fields, instantiateText, typeName } = collectCodecFields(ctx, decoded);
|
|
3124
|
+
const codec = ctx.runtime.named('sts:codec', 'codec').text();
|
|
3125
|
+
const decodeObject = ctx.runtime.named('sts:decode', 'object').text();
|
|
3126
|
+
const decodeMap = ctx.runtime.named('sts:decode', 'map').text();
|
|
3127
|
+
const decodeOptional = ctx.runtime.named('sts:decode', 'optional').text();
|
|
3128
|
+
const encodeContramap = ctx.runtime.named('sts:encode', 'contramap').text();
|
|
3129
|
+
const encodeObject = ctx.runtime.named('sts:encode', 'object').text();
|
|
3130
|
+
const encodeOptional = ctx.runtime.named('sts:encode', 'optional').text();
|
|
3131
|
+
const decodeShapeText = fields.length === 0 ? '{}' : `{
|
|
3132
|
+
${
|
|
3133
|
+
fields.map((field) =>
|
|
3134
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
3135
|
+
field.optional ? `${decodeOptional}(${field.decodeText})` : field.decodeText
|
|
3136
|
+
}`
|
|
3137
|
+
).join(',\n')
|
|
3138
|
+
}
|
|
3139
|
+
}`;
|
|
3140
|
+
|
|
3141
|
+
const decodeProjectionText = fields.length === 0 ? '{}' : `({
|
|
3142
|
+
${
|
|
3143
|
+
fields.map((field) =>
|
|
3144
|
+
`${propertyKeyText(field.localName)}: ${propertyAccessText('value', field.wireName)}`
|
|
3145
|
+
).join(',\n')
|
|
3146
|
+
}
|
|
3147
|
+
})`;
|
|
3148
|
+
const finalDecodeProjectionText = instantiateText === null
|
|
3149
|
+
? decodeProjectionText
|
|
3150
|
+
: `(${instantiateText.replace(CLASS_DECODE_VALUE_PLACEHOLDER, decodeProjectionText)})`;
|
|
3151
|
+
|
|
3152
|
+
const encodeShapeText = fields.length === 0 ? '{}' : `{
|
|
3153
|
+
${
|
|
3154
|
+
fields.map((field) =>
|
|
3155
|
+
`${propertyKeyText(field.wireName)}: ${
|
|
3156
|
+
field.optional ? `${encodeOptional}(${field.encodeText})` : field.encodeText
|
|
3157
|
+
}`
|
|
3158
|
+
).join(',\n')
|
|
3159
|
+
}
|
|
3160
|
+
}`;
|
|
3161
|
+
|
|
3162
|
+
const encodeProjectionText = fields.length === 0 ? '({})' : `({
|
|
3163
|
+
${
|
|
3164
|
+
fields.map((field) =>
|
|
3165
|
+
`${propertyKeyText(field.wireName)}: ${propertyAccessText('value', field.localName)}`
|
|
3166
|
+
).join(',\n')
|
|
3167
|
+
}
|
|
3168
|
+
})`;
|
|
3169
|
+
|
|
3170
|
+
return ctx.output.stmt(
|
|
3171
|
+
ctx.quote.stmt`
|
|
3172
|
+
export const ${`${typeName}Codec`} = ${codec}(
|
|
3173
|
+
${decodeMap}(
|
|
3174
|
+
${decodeObject}(${decodeShapeText}),
|
|
3175
|
+
(value) => ${finalDecodeProjectionText},
|
|
3176
|
+
),
|
|
3177
|
+
${encodeContramap}(
|
|
3178
|
+
${encodeObject}(${encodeShapeText}),
|
|
3179
|
+
(value: ${typeName}) => ${encodeProjectionText},
|
|
3180
|
+
),
|
|
3181
|
+
);
|
|
3182
|
+
`,
|
|
3183
|
+
);
|
|
3184
|
+
},
|
|
3185
|
+
signature: DECODE_SIGNATURE,
|
|
3186
|
+
};
|
|
3187
|
+
}
|
|
3188
|
+
attachDeriveFactory(codec);
|