From 0c2398a5297b9da3a72a7e6834200cb81ae5ff53 Mon Sep 17 00:00:00 2001 From: Mathi <11136549+Mathicha@users.noreply.github.com> Date: Fri, 21 Aug 2026 19:16:00 +0200 Subject: [PATCH 1/6] fix stuff related to exports --- crates/jsshaker/src/analyzer/conditional.rs | 31 +++++ crates/jsshaker/src/entity.rs | 2 +- crates/jsshaker/src/module.rs | 29 +++-- crates/jsshaker/src/nodes/misc/callee.rs | 13 ++- .../jsshaker/src/nodes/stmt/if_statement.rs | 15 ++- .../src/nodes/stmt/module_declaration.rs | 43 +++++-- crates/jsshaker/src/scope/variable_scope.rs | 3 +- crates/jsshaker/src/value/function/mod.rs | 2 +- crates/jsshaker/src/value/literal/mod.rs | 2 +- crates/jsshaker/src/value/literal/string.rs | 2 +- crates/jsshaker/src/value/logical_result.rs | 2 +- crates/jsshaker/src/value/mod.rs | 2 +- crates/jsshaker/src/value/module_object.rs | 4 +- crates/jsshaker/src/value/never.rs | 2 +- crates/jsshaker/src/value/object/mod.rs | 2 +- crates/jsshaker/src/value/primitive.rs | 2 +- crates/jsshaker/src/value/union.rs | 2 +- crates/jsshaker/tests/mod.rs | 108 +++++++++++++++--- .../module_fixtures/async_sequential/entry.js | 3 + .../module_fixtures/async_sequential/m.js | 8 ++ .../module_fixtures/async_try_catch/entry.js | 3 + .../module_fixtures/async_try_catch/m.js | 10 ++ .../cf_comma_sequence/entry.js | 3 + .../module_fixtures/cf_comma_sequence/m.js | 5 + .../module_fixtures/cf_early_return/entry.js | 4 + .../module_fixtures/cf_early_return/m.js | 6 + .../tests/module_fixtures/cf_for_in/entry.js | 3 + .../tests/module_fixtures/cf_for_in/m.js | 4 + .../module_fixtures/cf_for_of_break/entry.js | 3 + .../module_fixtures/cf_for_of_break/m.js | 7 ++ .../cf_labeled_continue/entry.js | 3 + .../module_fixtures/cf_labeled_continue/m.js | 9 ++ .../cf_switch_fallthrough/entry.js | 3 + .../cf_switch_fallthrough/m.js | 12 ++ .../tests/module_fixtures/cf_ternary/entry.js | 3 + .../tests/module_fixtures/cf_ternary/m.js | 4 + .../module_fixtures/cf_while_nested/entry.js | 3 + .../module_fixtures/cf_while_nested/m.js | 12 ++ .../cls_inheritance_super/entry.js | 2 + .../cls_inheritance_super/m.js | 17 +++ .../cls_static_block_order/entry.js | 3 + .../cls_static_block_order/m.js | 9 ++ .../default_export_reassignment/entry.js | 4 + .../default_export_reassignment/mod.js | 6 + .../destructure_computed/entry.js | 3 + .../module_fixtures/destructure_computed/m.js | 5 + .../destructure_defaults/entry.js | 3 + .../module_fixtures/destructure_defaults/m.js | 5 + .../destructure_nested/entry.js | 3 + .../module_fixtures/destructure_nested/m.js | 12 ++ .../destructure_object_rest/entry.js | 3 + .../destructure_object_rest/m.js | 5 + .../module_fixtures/destructure_rest/entry.js | 3 + .../module_fixtures/destructure_rest/m.js | 5 + .../module_fixtures/destructure_swap/entry.js | 3 + .../module_fixtures/destructure_swap/m.js | 5 + .../module_fixtures/exc_rethrow/entry.js | 7 ++ .../tests/module_fixtures/exc_rethrow/m.js | 12 ++ .../exc_throw_in_loop/entry.js | 3 + .../module_fixtures/exc_throw_in_loop/m.js | 11 ++ .../fn_arguments_object/entry.js | 3 + .../module_fixtures/fn_arguments_object/m.js | 4 + .../fn_default_params/entry.js | 4 + .../module_fixtures/fn_default_params/m.js | 4 + .../module_fixtures/fn_getter_setter/entry.js | 3 + .../module_fixtures/fn_getter_setter/m.js | 9 ++ .../module_fixtures/fn_higher_order/entry.js | 3 + .../module_fixtures/fn_higher_order/m.js | 4 + .../fn_reassign_then_call/entry.js | 5 + .../fn_reassign_then_call/m.js | 7 ++ .../module_fixtures/fn_recursion/entry.js | 2 + .../tests/module_fixtures/fn_recursion/m.js | 5 + .../module_fixtures/fn_rest_args/entry.js | 3 + .../tests/module_fixtures/fn_rest_args/m.js | 4 + .../module_fixtures/gen_early_return/entry.js | 5 + .../module_fixtures/gen_early_return/m.js | 10 ++ .../module_fixtures/live_binding/entry.js | 6 + .../module_fixtures/live_binding/state.js | 12 ++ .../misc_json_roundtrip/entry.js | 3 + .../module_fixtures/misc_json_roundtrip/m.js | 4 + .../module_fixtures/misc_many_writes/entry.js | 3 + .../module_fixtures/misc_many_writes/m.js | 7 ++ .../misc_reassign_to_null/entry.js | 3 + .../misc_reassign_to_null/m.js | 4 + .../misc_template_literal/entry.js | 3 + .../misc_template_literal/m.js | 4 + .../misc_typeof_export/entry.js | 4 + .../module_fixtures/misc_typeof_export/m.js | 4 + .../mod_default_class_reassign/entry.js | 4 + .../mod_default_class_reassign/m.js | 6 + .../module_fixtures/mod_diamond/entry.js | 3 + .../tests/module_fixtures/mod_diamond/l.js | 2 + .../tests/module_fixtures/mod_diamond/left.js | 5 + .../module_fixtures/mod_diamond/right.js | 4 + .../module_fixtures/mod_diamond/shared.js | 4 + .../module_fixtures/mod_import_order/b.js | 2 + .../module_fixtures/mod_import_order/c.js | 4 + .../module_fixtures/mod_import_order/entry.js | 3 + .../mod_reexport_default_bare/entry.js | 2 + .../mod_reexport_default_bare/mid.js | 1 + .../mod_reexport_default_bare/src.js | 3 + .../module_fixtures/ns_object_keys/entry.js | 2 + .../tests/module_fixtures/ns_object_keys/m.js | 3 + .../tests/module_fixtures/ns_spread/entry.js | 3 + .../tests/module_fixtures/ns_spread/m.js | 2 + .../obj_getter_side_effect/entry.js | 4 + .../obj_getter_side_effect/m.js | 7 ++ .../tests/module_fixtures/op_add_sub/entry.js | 3 + .../tests/module_fixtures/op_add_sub/m.js | 5 + .../module_fixtures/op_exp_bitwise/entry.js | 3 + .../tests/module_fixtures/op_exp_bitwise/m.js | 7 ++ .../op_logical_assign/entry.js | 3 + .../module_fixtures/op_logical_assign/m.js | 8 ++ .../module_fixtures/op_mul_div_mod/entry.js | 3 + .../tests/module_fixtures/op_mul_div_mod/m.js | 6 + .../tests/module_fixtures/op_shifts/entry.js | 3 + .../tests/module_fixtures/op_shifts/m.js | 6 + .../reexported_binding/entry.js | 4 + .../reexported_binding/middle.js | 1 + .../reexported_binding/source.js | 4 + .../module_fixtures/tdz_circular_throw/a.js | 3 + .../module_fixtures/tdz_circular_throw/b.js | 4 + .../tdz_circular_throw/entry.js | 1 + ...odule_test@async_sequential__entry.js.snap | 17 +++ ...module_test@async_try_catch__entry.js.snap | 21 ++++ ...dule_test@cf_comma_sequence__entry.js.snap | 15 +++ ...module_test@cf_early_return__entry.js.snap | 18 +++ .../module_test@cf_for_in__entry.js.snap | 18 +++ ...module_test@cf_for_of_break__entry.js.snap | 23 ++++ ...le_test@cf_labeled_continue__entry.js.snap | 20 ++++ ..._test@cf_switch_fallthrough__entry.js.snap | 20 ++++ .../module_test@cf_ternary__entry.js.snap | 15 +++ ...module_test@cf_while_nested__entry.js.snap | 23 ++++ ..._test@cls_inheritance_super__entry.js.snap | 10 ++ ...test@cls_static_block_order__entry.js.snap | 20 ++++ ...default_export_reassignment__entry.js.snap | 17 +++ ...e_test@destructure_computed__entry.js.snap | 15 +++ ...e_test@destructure_defaults__entry.js.snap | 16 +++ ...ule_test@destructure_nested__entry.js.snap | 21 ++++ ...est@destructure_object_rest__entry.js.snap | 19 +++ ...odule_test@destructure_rest__entry.js.snap | 19 +++ ...odule_test@destructure_swap__entry.js.snap | 15 +++ .../module_test@exc_rethrow__entry.js.snap | 27 +++++ ...dule_test@exc_throw_in_loop__entry.js.snap | 22 ++++ ...le_test@fn_arguments_object__entry.js.snap | 15 +++ ...dule_test@fn_default_params__entry.js.snap | 16 +++ ...odule_test@fn_getter_setter__entry.js.snap | 20 ++++ ...module_test@fn_higher_order__entry.js.snap | 15 +++ ..._test@fn_reassign_then_call__entry.js.snap | 20 ++++ .../module_test@fn_recursion__entry.js.snap | 15 +++ .../module_test@fn_rest_args__entry.js.snap | 15 +++ ...odule_test@gen_early_return__entry.js.snap | 23 ++++ .../module_test@live_binding__entry.js.snap | 18 +++ ...le_test@misc_json_roundtrip__entry.js.snap | 15 +++ ...odule_test@misc_many_writes__entry.js.snap | 18 +++ ..._test@misc_reassign_to_null__entry.js.snap | 15 +++ ..._test@misc_template_literal__entry.js.snap | 15 +++ ...ule_test@misc_typeof_export__entry.js.snap | 16 +++ ...@mod_default_class_reassign__entry.js.snap | 18 +++ .../module_test@mod_diamond__entry.js.snap | 32 ++++++ ...odule_test@mod_import_order__entry.js.snap | 19 +++ ...t@mod_reexport_default_bare__entry.js.snap | 14 +++ .../module_test@ns_object_keys__entry.js.snap | 13 +++ .../module_test@ns_spread__entry.js.snap | 13 +++ ...test@obj_getter_side_effect__entry.js.snap | 17 +++ .../module_test@op_add_sub__entry.js.snap | 16 +++ .../module_test@op_exp_bitwise__entry.js.snap | 18 +++ ...dule_test@op_logical_assign__entry.js.snap | 17 +++ .../module_test@op_mul_div_mod__entry.js.snap | 17 +++ .../module_test@op_shifts__entry.js.snap | 17 +++ ...ule_test@reexported_binding__entry.js.snap | 18 +++ ...ule_test@tdz_circular_throw__entry.js.snap | 18 +++ .../snapshots/test@for_of_statement.js.snap | 1 - .../tests/snapshots/test@throw_catch.js.snap | 1 - 174 files changed, 1584 insertions(+), 55 deletions(-) create mode 100644 crates/jsshaker/tests/module_fixtures/async_sequential/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/async_sequential/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/async_try_catch/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/async_try_catch/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_comma_sequence/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_comma_sequence/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_early_return/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_early_return/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_for_in/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_for_in/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_for_of_break/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_for_of_break/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_labeled_continue/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_labeled_continue/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_ternary/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_ternary/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_while_nested/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_while_nested/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cls_inheritance_super/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cls_inheritance_super/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cls_static_block_order/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cls_static_block_order/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/default_export_reassignment/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/default_export_reassignment/mod.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_computed/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_computed/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_defaults/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_defaults/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_nested/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_nested/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_object_rest/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_object_rest/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_rest/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_rest/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_swap/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_swap/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/exc_rethrow/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/exc_rethrow/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_arguments_object/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_arguments_object/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_default_params/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_default_params/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_getter_setter/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_getter_setter/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_higher_order/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_higher_order/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_reassign_then_call/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_reassign_then_call/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_recursion/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_recursion/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_rest_args/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_rest_args/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/gen_early_return/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/gen_early_return/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/live_binding/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/live_binding/state.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_many_writes/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_many_writes/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_template_literal/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_template_literal/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_typeof_export/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_typeof_export/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_default_class_reassign/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_default_class_reassign/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_diamond/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_diamond/l.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_diamond/left.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_diamond/right.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_diamond/shared.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_import_order/b.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_import_order/c.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_import_order/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/mid.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/src.js create mode 100644 crates/jsshaker/tests/module_fixtures/ns_object_keys/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/ns_object_keys/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/ns_spread/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/ns_spread/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/obj_getter_side_effect/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/obj_getter_side_effect/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_add_sub/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_add_sub/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_exp_bitwise/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_exp_bitwise/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_logical_assign/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_logical_assign/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_mul_div_mod/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_mul_div_mod/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_shifts/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_shifts/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/reexported_binding/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/reexported_binding/middle.js create mode 100644 crates/jsshaker/tests/module_fixtures/reexported_binding/source.js create mode 100644 crates/jsshaker/tests/module_fixtures/tdz_circular_throw/a.js create mode 100644 crates/jsshaker/tests/module_fixtures/tdz_circular_throw/b.js create mode 100644 crates/jsshaker/tests/module_fixtures/tdz_circular_throw/entry.js create mode 100644 crates/jsshaker/tests/snapshots/module_test@async_sequential__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@async_try_catch__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_comma_sequence__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_early_return__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_for_in__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_for_of_break__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_labeled_continue__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_switch_fallthrough__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_ternary__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_while_nested__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cls_inheritance_super__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cls_static_block_order__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@default_export_reassignment__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_computed__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_defaults__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_nested__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_object_rest__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_rest__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_swap__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@exc_rethrow__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@exc_throw_in_loop__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_arguments_object__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_default_params__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_getter_setter__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_higher_order__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_reassign_then_call__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_recursion__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_rest_args__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@gen_early_return__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@live_binding__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_json_roundtrip__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_many_writes__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_reassign_to_null__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_template_literal__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_typeof_export__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@mod_default_class_reassign__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@mod_diamond__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@mod_import_order__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@mod_reexport_default_bare__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@ns_object_keys__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@ns_spread__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@obj_getter_side_effect__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@op_add_sub__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@op_exp_bitwise__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@op_logical_assign__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@op_mul_div_mod__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@op_shifts__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@reexported_binding__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@tdz_circular_throw__entry.js.snap diff --git a/crates/jsshaker/src/analyzer/conditional.rs b/crates/jsshaker/src/analyzer/conditional.rs index a2fbc036..32ac05e3 100644 --- a/crates/jsshaker/src/analyzer/conditional.rs +++ b/crates/jsshaker/src/analyzer/conditional.rs @@ -18,6 +18,7 @@ struct ConditionalData<'a> { maybe_false: bool, impure_true: bool, impure_false: bool, + exiting_branches: Vec>, tests_to_include: Vec>, } @@ -53,6 +54,22 @@ impl<'a> ConditionalBranch<'a> { } } +#[derive(Debug)] +struct ConditionalExitLink { + id: DepAtom, +} + +impl<'a> CustomDepTrait<'a> for ConditionalExitLink { + fn include(&self, analyzer: &mut Analyzer<'a>) { + let Some(data) = analyzer.conditional_data.node_to_data.get_mut(&self.id) else { + return; + }; + for branch in mem::take(&mut data.exiting_branches) { + analyzer.include(branch); + } + } +} + impl<'a> CustomDepTrait<'a> for ConditionalBranch<'a> { fn include(&self, analyzer: &mut Analyzer<'a>) { let data = analyzer.conditional_data.node_to_data.get_mut(&self.id).unwrap(); @@ -182,6 +199,20 @@ impl<'a> Analyzer<'a> { Some(Dep(branch)) } + pub fn note_conditional_branch_exit(&mut self, id: impl Into, dep: Option>) { + let Some(dep) = dep else { + return; + }; + let id = id.into(); + if let Some(data) = self.conditional_data.node_to_data.get_mut(&id) { + data.exiting_branches.push(dep); + } + } + + pub fn conditional_exit_link(&mut self, id: impl Into) -> Dep<'a> { + Dep(self.allocator.alloc(ConditionalExitLink { id: id.into() })) + } + pub fn post_analyze_handle_conditional(&mut self) -> bool { if !self.config.branch_folding { return false; diff --git a/crates/jsshaker/src/entity.rs b/crates/jsshaker/src/entity.rs index f5f801f6..0eedaa84 100644 --- a/crates/jsshaker/src/entity.rs +++ b/crates/jsshaker/src/entity.rs @@ -184,7 +184,7 @@ impl<'a> Entity<'a> { /// Returns vec![(definite, key)] pub fn get_keys( &self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, check_proto: bool, ) -> Option)>> { self.value.get_keys(analyzer, check_proto) diff --git a/crates/jsshaker/src/module.rs b/crates/jsshaker/src/module.rs index 3cbd5a21..9964f0c2 100644 --- a/crates/jsshaker/src/module.rs +++ b/crates/jsshaker/src/module.rs @@ -311,8 +311,13 @@ impl<'a> Analyzer<'a> { match named_export { ExportedValue::Variable(scope, symbol, dep) => { let old_module = self.set_current_module(module_id); + if !self.is_readonly_symbol(symbol) { + self.include_on_scope(scope, symbol); + } let value = self.read_on_scope(scope, symbol).unwrap(); - // TODO: handle TDZ + if value.is_none() { + self.include_on_scope(scope, symbol); + } let value = value.unwrap_or(self.factory.unknown); self.set_current_module(old_module); self.factory.computed(value, dep) @@ -339,18 +344,20 @@ impl<'a> Analyzer<'a> { return None; } let module = &self.modules.modules[module_id]; - if name == "default" { - module.default_export.map(|e| e.unwrap_or(self.factory.unknown)) - } else if let Some(exported_value) = module.named_exports.get(&name) { - Some(self.get_named_export_value(module_id, *exported_value)) - } else { - for reexport_module_id in module.reexport_all.clone() { - if let Some(entity) = self.get_export_value_by_name(reexport_module_id, name, searched) { - return Some(entity); - } + if name == "default" + && let Some(default_export) = module.default_export + { + return Some(default_export.unwrap_or(self.factory.unknown)); + } + if let Some(&exported_value) = module.named_exports.get(&name) { + return Some(self.get_named_export_value(module_id, exported_value)); + } + for reexport_module_id in module.reexport_all.clone() { + if let Some(entity) = self.get_export_value_by_name(reexport_module_id, name, searched) { + return Some(entity); } - None } + None } pub fn does_module_reexport_unknown( diff --git a/crates/jsshaker/src/nodes/misc/callee.rs b/crates/jsshaker/src/nodes/misc/callee.rs index 61ee3d0e..0b2f1ca2 100644 --- a/crates/jsshaker/src/nodes/misc/callee.rs +++ b/crates/jsshaker/src/nodes/misc/callee.rs @@ -39,20 +39,29 @@ impl<'a> Analyzer<'a> { let dep = AstKind2::Callee(node); if let Some((member_expr, same_chain)) = unwrap_to_member_expression(node) { + let this_of = |analyzer: &mut Analyzer<'a>, object: Entity<'a>| { + if matches!(member_expr.object(), Expression::Super(_)) { + analyzer.get_this() + } else { + object + } + }; if same_chain { let (scope_count, callee, undefined, (object, _)) = self.exec_member_expression_read_in_chain(member_expr, false)?; - Ok((scope_count, callee, undefined, self.factory.computed(object, dep))) + let this = this_of(self, object); + Ok((scope_count, callee, undefined, self.factory.computed(this, dep))) } else { let result = self.exec_member_expression_read_in_chain(member_expr, false); Ok(match result { Ok((scope_count, value, undefined, (object, _))) => { self.pop_multiple_cf_scopes(scope_count); + let this = this_of(self, object); ( 0, self.factory.optional_union(value, undefined), None, - self.factory.computed(object, dep), + self.factory.computed(this, dep), ) } Err(value) => (0, value, None, self.factory.unknown), diff --git a/crates/jsshaker/src/nodes/stmt/if_statement.rs b/crates/jsshaker/src/nodes/stmt/if_statement.rs index 8d3cec33..b4e312d7 100644 --- a/crates/jsshaker/src/nodes/stmt/if_statement.rs +++ b/crates/jsshaker/src/nodes/stmt/if_statement.rs @@ -24,7 +24,7 @@ impl<'a> Analyzer<'a> { let mut acc_dep_2 = None; if maybe_consequent { - self.push_if_like_branch_cf_scope( + let branch_dep = self.push_if_like_branch_cf_scope( AstKind2::IfStatement(node), CfScopeKind::ExitBlocker(None), test, @@ -34,6 +34,9 @@ impl<'a> Analyzer<'a> { node.alternate.is_some(), ); self.exec_statement(&node.consequent); + if self.cf_scope().must_exited() { + self.note_conditional_branch_exit(AstKind2::IfStatement(node), branch_dep); + } let mut conditional_scope = self.pop_cf_scope(); if let CfScopeKind::ExitBlocker(Some(stopped_exit)) = &conditional_scope.kind { exit_target_inner = exit_target_inner.max(*stopped_exit); @@ -44,7 +47,7 @@ impl<'a> Analyzer<'a> { acc_dep_1 = conditional_scope.deps.collect(factory); } if maybe_alternate { - self.push_if_like_branch_cf_scope( + let branch_dep = self.push_if_like_branch_cf_scope( AstKind2::IfStatement(node), CfScopeKind::ExitBlocker(None), test, @@ -55,6 +58,9 @@ impl<'a> Analyzer<'a> { ); if let Some(alternate) = &node.alternate { self.exec_statement(alternate); + if self.cf_scope().must_exited() { + self.note_conditional_branch_exit(AstKind2::IfStatement(node), branch_dep); + } let mut conditional_scope = self.pop_cf_scope(); if let CfScopeKind::ExitBlocker(Some(stopped_exit)) = &conditional_scope.kind { exit_target_inner = exit_target_inner.max(*stopped_exit); @@ -79,6 +85,11 @@ impl<'a> Analyzer<'a> { } else { self.exit_to_impl(exit_target_outer, self.scoping.cf.stack_len(), false, acc_dep); } + + if self.config.branch_folding && !self.cf_scope().must_exited() { + let link = self.conditional_exit_link(AstKind2::IfStatement(node)); + self.cf_scope_mut().push_dep(link); + } } } diff --git a/crates/jsshaker/src/nodes/stmt/module_declaration.rs b/crates/jsshaker/src/nodes/stmt/module_declaration.rs index a90c630e..5b49468c 100644 --- a/crates/jsshaker/src/nodes/stmt/module_declaration.rs +++ b/crates/jsshaker/src/nodes/stmt/module_declaration.rs @@ -1,15 +1,34 @@ -use oxc::ast::ast::{ - ExportDefaultDeclaration, ExportDefaultDeclarationKind, ExportNamedDeclaration, - ImportDeclaration, ImportDeclarationSpecifier, ImportDefaultSpecifier, ImportNamespaceSpecifier, - ImportOrExportKind, ImportSpecifier, ModuleDeclaration, ModuleExportName, Statement, +use oxc::{ + ast::ast::{ + BindingIdentifier, ExportDefaultDeclaration, ExportDefaultDeclarationKind, + ExportNamedDeclaration, ImportDeclaration, ImportDeclarationSpecifier, ImportDefaultSpecifier, + ImportNamespaceSpecifier, ImportOrExportKind, ImportSpecifier, ModuleDeclaration, + ModuleExportName, Statement, + }, + span::Atom, }; use crate::{ - Analyzer, ast::DeclarationKind, module::ExportedValue, transformer::Transformer, + Analyzer, ast::DeclarationKind, entity::Entity, module::ExportedValue, transformer::Transformer, utils::ast::AstKind2, }; impl<'a> Analyzer<'a> { + fn live_default_export( + &mut self, + id: &Option>, + value: Entity<'a>, + ) -> Entity<'a> { + match id { + Some(id) if !self.is_readonly_symbol(id.symbol_id()) => { + let scope = self.scoping.variable.top().unwrap(); + self.include_on_scope(scope, id.symbol_id()); + self.factory.computed_unknown(value) + } + _ => value, + } + } + pub fn declare_module_declaration(&mut self, node: &'a ModuleDeclaration<'a>) { let module = self.module_info_mut(); match node { @@ -152,9 +171,9 @@ impl<'a> Analyzer<'a> { } else if let Some(resolved) = resolved { let module = &self.modules.modules[resolved]; match specifier { - ImportDeclarationSpecifier::ImportDefaultSpecifier(_node) => { - module.default_export.flatten().unwrap_or(self.factory.unknown) - } + ImportDeclarationSpecifier::ImportDefaultSpecifier(_node) => self + .get_export_value_by_name(resolved, Atom::from("default"), &mut Default::default()) + .unwrap_or(self.factory.unknown), ImportDeclarationSpecifier::ImportNamespaceSpecifier(_node) => module.module_object, ImportDeclarationSpecifier::ImportSpecifier(node) => self .get_export_value_by_name(resolved, node.imported.name(), &mut Default::default()) @@ -183,13 +202,17 @@ impl<'a> Analyzer<'a> { } ModuleDeclaration::ExportDefaultDeclaration(node) => { let value = match &node.declaration { - ExportDefaultDeclarationKind::FunctionDeclaration(node) => self.exec_function(node), + ExportDefaultDeclarationKind::FunctionDeclaration(node) => { + let value = self.exec_function(node); + self.live_default_export(&node.id, value) + } ExportDefaultDeclarationKind::ClassDeclaration(node) => { if node.id.is_none() { // Patch `export default class{}` self.exec_class(node) } else { - self.init_class(node) + let value = self.init_class(node); + self.live_default_export(&node.id, value) } } node => self.exec_expression(node.to_expression()), diff --git a/crates/jsshaker/src/scope/variable_scope.rs b/crates/jsshaker/src/scope/variable_scope.rs index 876021f0..f1b00e00 100644 --- a/crates/jsshaker/src/scope/variable_scope.rs +++ b/crates/jsshaker/src/scope/variable_scope.rs @@ -388,9 +388,10 @@ impl<'a> Analyzer<'a> { if let Some(exporting) = exporting { let name = Atom::from_in(self.semantic().scoping().symbol_name(symbol), self.allocator); + let readonly = self.is_readonly_symbol(symbol); self.module_info_mut().named_exports.insert( name, - if let Some(fn_value) = fn_value { + if let Some(fn_value) = fn_value.filter(|_| readonly) { ExportedValue::Function(fn_value, exporting) } else { ExportedValue::Variable(variable_scope, symbol, exporting) diff --git a/crates/jsshaker/src/value/function/mod.rs b/crates/jsshaker/src/value/function/mod.rs index 969d3983..4003ff8a 100644 --- a/crates/jsshaker/src/value/function/mod.rs +++ b/crates/jsshaker/src/value/function/mod.rs @@ -171,7 +171,7 @@ impl<'a> ValueTrait<'a> for FunctionValue<'a> { fn get_keys( &'a self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, check_proto: bool, ) -> Option)>> { self.statics.get_keys(analyzer, check_proto) diff --git a/crates/jsshaker/src/value/literal/mod.rs b/crates/jsshaker/src/value/literal/mod.rs index aeb22969..aee82ba3 100644 --- a/crates/jsshaker/src/value/literal/mod.rs +++ b/crates/jsshaker/src/value/literal/mod.rs @@ -251,7 +251,7 @@ impl<'a> ValueTrait<'a> for LiteralValue<'a> { fn get_keys( &self, - _analyzer: &Analyzer<'a>, + _analyzer: &mut Analyzer<'a>, _check_proto: bool, ) -> Option)>> { match self { diff --git a/crates/jsshaker/src/value/literal/string.rs b/crates/jsshaker/src/value/literal/string.rs index ff2507f6..be33470c 100644 --- a/crates/jsshaker/src/value/literal/string.rs +++ b/crates/jsshaker/src/value/literal/string.rs @@ -194,7 +194,7 @@ impl<'a> ValueTrait<'a> for Atom<'a> { fn get_keys( &self, - _analyzer: &Analyzer<'a>, + _analyzer: &mut Analyzer<'a>, _check_proto: bool, ) -> Option)>> { None diff --git a/crates/jsshaker/src/value/logical_result.rs b/crates/jsshaker/src/value/logical_result.rs index 0a49b67a..4a423ff2 100644 --- a/crates/jsshaker/src/value/logical_result.rs +++ b/crates/jsshaker/src/value/logical_result.rs @@ -126,7 +126,7 @@ impl<'a> ValueTrait<'a> for LogicalResultValue<'a> { fn get_keys( &'a self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, check_proto: bool, ) -> Option)>> { self.value.get_keys(analyzer, check_proto) diff --git a/crates/jsshaker/src/value/mod.rs b/crates/jsshaker/src/value/mod.rs index 1b457696..b2a067f5 100644 --- a/crates/jsshaker/src/value/mod.rs +++ b/crates/jsshaker/src/value/mod.rs @@ -137,7 +137,7 @@ pub trait ValueTrait<'a>: Debug { /// Returns vec![(definite, key)] fn get_keys( &'a self, - _analyzer: &Analyzer<'a>, + _analyzer: &mut Analyzer<'a>, _check_proto: bool, ) -> Option)>> { None diff --git a/crates/jsshaker/src/value/module_object.rs b/crates/jsshaker/src/value/module_object.rs index fc8e2938..0639dc9c 100644 --- a/crates/jsshaker/src/value/module_object.rs +++ b/crates/jsshaker/src/value/module_object.rs @@ -82,6 +82,7 @@ impl<'a> ValueTrait<'a> for ModuleObjectValue { analyzer: &mut Analyzer<'a>, dep: Dep<'a>, ) -> EnumeratedProperties<'a> { + self.include(analyzer); // TODO: Optimize this escaped::enumerate_properties(self, analyzer, dep) } @@ -149,9 +150,10 @@ impl<'a> ValueTrait<'a> for ModuleObjectValue { fn get_keys( &'a self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, _check_proto: bool, ) -> Option)>> { + self.include(analyzer); if analyzer.does_module_reexport_unknown(self.module, &mut Default::default()) { return None; } diff --git a/crates/jsshaker/src/value/never.rs b/crates/jsshaker/src/value/never.rs index 8b12a21b..8ec6254a 100644 --- a/crates/jsshaker/src/value/never.rs +++ b/crates/jsshaker/src/value/never.rs @@ -93,7 +93,7 @@ impl<'a> ValueTrait<'a> for NeverValue { } fn get_keys( &'a self, - _analyzer: &Analyzer<'a>, + _analyzer: &mut Analyzer<'a>, _check_proto: bool, ) -> Option)>> { Some(vec![]) diff --git a/crates/jsshaker/src/value/object/mod.rs b/crates/jsshaker/src/value/object/mod.rs index 0ff1c941..18aeb191 100644 --- a/crates/jsshaker/src/value/object/mod.rs +++ b/crates/jsshaker/src/value/object/mod.rs @@ -222,7 +222,7 @@ impl<'a> ValueTrait<'a> for ObjectValue<'a> { fn get_keys( &'a self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, check_proto: bool, ) -> Option)>> { if self.included.get() { diff --git a/crates/jsshaker/src/value/primitive.rs b/crates/jsshaker/src/value/primitive.rs index b4308439..04239d69 100644 --- a/crates/jsshaker/src/value/primitive.rs +++ b/crates/jsshaker/src/value/primitive.rs @@ -153,7 +153,7 @@ impl<'a> ValueTrait<'a> for PrimitiveValue { } fn get_keys( &'a self, - _analyzer: &Analyzer<'a>, + _analyzer: &mut Analyzer<'a>, _check_proto: bool, ) -> Option)>> { if self.maybe_string() { None } else { Some(vec![]) } diff --git a/crates/jsshaker/src/value/union.rs b/crates/jsshaker/src/value/union.rs index 8eb41979..6bf73215 100644 --- a/crates/jsshaker/src/value/union.rs +++ b/crates/jsshaker/src/value/union.rs @@ -292,7 +292,7 @@ impl<'a, V: UnionValues<'a> + Debug + 'a> ValueTrait<'a> for UnionValue<'a, V> { fn get_keys( &'a self, - analyzer: &Analyzer<'a>, + analyzer: &mut Analyzer<'a>, check_proto: bool, ) -> Option)>> { let mut result = Vec::new(); diff --git a/crates/jsshaker/tests/mod.rs b/crates/jsshaker/tests/mod.rs index eb267f3d..165d9831 100644 --- a/crates/jsshaker/tests/mod.rs +++ b/crates/jsshaker/tests/mod.rs @@ -1,29 +1,40 @@ -use std::fs; +use std::{collections::HashMap, fs, path::Path}; use insta::{assert_snapshot, glob}; -use jsshaker::{JsShakerOptions, TreeShakeConfig, tree_shake, vfs::SingleFileFs}; +use jsshaker::{ + JsShakerOptions, TreeShakeConfig, tree_shake, + vfs::{MultiModuleFs, SingleFileFs, normalize_path::normalize_str}, +}; use oxc::{ codegen::{CodegenOptions, CommentOptions}, minifier::MinifierOptions, }; +fn config_for(input: &str) -> TreeShakeConfig { + let mut config = TreeShakeConfig::recommended(); + if input.contains("@react-jsx") { + config.jsx = jsshaker::TreeShakeJsxPreset::React; + } + config.unknown_global_side_effects = true; + config.advanced = true; + config +} + +fn minify_options(input: &str) -> Option { + input.contains("@minify").then(|| MinifierOptions { mangle: None, ..Default::default() }) +} + +fn codegen_options() -> CodegenOptions { + CodegenOptions { comments: CommentOptions::default(), ..Default::default() } +} + fn do_tree_shake(input: String) -> String { - let do_minify = input.contains("@minify"); - let react_jsx = input.contains("@react-jsx"); let result = tree_shake( JsShakerOptions { - vfs: SingleFileFs(input), - config: { - let mut config = TreeShakeConfig::recommended(); - if react_jsx { - config.jsx = jsshaker::TreeShakeJsxPreset::React; - } - config.unknown_global_side_effects = true; - config.advanced = true; - config - }, - minify_options: do_minify.then(|| MinifierOptions { mangle: None, ..Default::default() }), - codegen_options: CodegenOptions { comments: CommentOptions::default(), ..Default::default() }, + vfs: SingleFileFs(input.clone()), + config: config_for(&input), + minify_options: minify_options(&input), + codegen_options: codegen_options(), source_map: false, }, SingleFileFs::ENTRY_PATH.to_string(), @@ -42,3 +53,68 @@ fn test() { }) }); } + +/// Loads every `.js` file under `dir` into the virtual file system, keyed by its +/// path relative to the fixture root (so `./dep.js` resolves from `/entry.js`). +fn collect_modules(dir: &Path, prefix: &str, files: &mut HashMap) { + let mut entries: Vec<_> = fs::read_dir(dir).unwrap().map(|entry| entry.unwrap()).collect(); + entries.sort_by_key(|entry| entry.file_name()); + for entry in entries { + let name = entry.file_name().to_string_lossy().into_owned(); + let path = entry.path(); + if path.is_dir() { + collect_modules(&path, &format!("{prefix}{name}/"), files); + } else if path.extension().is_some_and(|ext| ext == "js") { + let key = normalize_str(&format!("/{prefix}{name}")); + files.insert(key, fs::read_to_string(&path).unwrap()); + } + } +} + +fn do_tree_shake_modules(dir: &Path) -> String { + let mut files = HashMap::new(); + collect_modules(dir, "", &mut files); + + let entry = normalize_str("/entry.js"); + let input = files + .get(&entry) + .unwrap_or_else(|| panic!("module fixture {} must contain an entry.js", dir.display())) + .clone(); + + let result = tree_shake( + JsShakerOptions { + vfs: MultiModuleFs(files), + config: config_for(&input), + minify_options: minify_options(&input), + codegen_options: codegen_options(), + source_map: false, + }, + entry.clone(), + ); + + // Entry first, then the remaining modules alphabetically, so the snapshot is + // stable regardless of hash ordering. + let mut modules: Vec<_> = result.codegen_return.iter().collect(); + modules.sort_by(|(a, _), (b, _)| (**a != entry, *a).cmp(&(**b != entry, *b))); + + let mut output = String::new(); + for (path, codegen) in modules { + // Normalize separators so snapshots match on Windows and Unix alike. + output.push_str(&format!("//#region {}\n", path.replace('\\', "/"))); + output.push_str(codegen.code.trim_end()); + output.push_str("\n\n"); + } + output.trim_end().to_string() +} + +#[test] +fn module_test() { + glob!("module_fixtures/*/entry.js", |path| { + let dir = path.parent().unwrap(); + let mut settings = insta::Settings::clone_current(); + settings.set_prepend_module_to_snapshot(false); + settings.bind(|| { + assert_snapshot!(do_tree_shake_modules(dir)); + }) + }); +} diff --git a/crates/jsshaker/tests/module_fixtures/async_sequential/entry.js b/crates/jsshaker/tests/module_fixtures/async_sequential/entry.js new file mode 100644 index 00000000..36b3d97c --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/async_sequential/entry.js @@ -0,0 +1,3 @@ +import { order, run } from "./m.js"; +await run(); +console.log(order); diff --git a/crates/jsshaker/tests/module_fixtures/async_sequential/m.js b/crates/jsshaker/tests/module_fixtures/async_sequential/m.js new file mode 100644 index 00000000..ed202476 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/async_sequential/m.js @@ -0,0 +1,8 @@ +export let order = ""; +export async function run() { + order += "a"; + await null; + order += "b"; + await null; + order += "c"; +} diff --git a/crates/jsshaker/tests/module_fixtures/async_try_catch/entry.js b/crates/jsshaker/tests/module_fixtures/async_try_catch/entry.js new file mode 100644 index 00000000..0583e2f2 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/async_try_catch/entry.js @@ -0,0 +1,3 @@ +import { v, run } from "./m.js"; +await run(); +console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/async_try_catch/m.js b/crates/jsshaker/tests/module_fixtures/async_try_catch/m.js new file mode 100644 index 00000000..3c9388a5 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/async_try_catch/m.js @@ -0,0 +1,10 @@ +export let v = ""; +export async function run() { + try { + await Promise.reject(new Error("x")); + } catch { + v = "caught"; + } finally { + v += "|done"; + } +} diff --git a/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/entry.js b/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/entry.js new file mode 100644 index 00000000..fb07041f --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/entry.js @@ -0,0 +1,3 @@ +import { a, b, f } from "./m.js"; +f(); +console.log(a, b); diff --git a/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/m.js b/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/m.js new file mode 100644 index 00000000..4dfc80ce --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/m.js @@ -0,0 +1,5 @@ +export let a = 0, + b = 0; +export function f() { + a = ((b = 2), 3); +} diff --git a/crates/jsshaker/tests/module_fixtures/cf_early_return/entry.js b/crates/jsshaker/tests/module_fixtures/cf_early_return/entry.js new file mode 100644 index 00000000..36a4fcda --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_early_return/entry.js @@ -0,0 +1,4 @@ +import { v, f } from "./m.js"; +f(true); +f(false); +console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/cf_early_return/m.js b/crates/jsshaker/tests/module_fixtures/cf_early_return/m.js new file mode 100644 index 00000000..9cbd75d4 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_early_return/m.js @@ -0,0 +1,6 @@ +export let v = ""; +export function f(bail) { + v += "1"; + if (bail) return; + v += "2"; +} diff --git a/crates/jsshaker/tests/module_fixtures/cf_for_in/entry.js b/crates/jsshaker/tests/module_fixtures/cf_for_in/entry.js new file mode 100644 index 00000000..4248d55a --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_for_in/entry.js @@ -0,0 +1,3 @@ +import { keys, f } from "./m.js"; +f(); +console.log(keys); diff --git a/crates/jsshaker/tests/module_fixtures/cf_for_in/m.js b/crates/jsshaker/tests/module_fixtures/cf_for_in/m.js new file mode 100644 index 00000000..a072d137 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_for_in/m.js @@ -0,0 +1,4 @@ +export let keys = ""; +export function f() { + for (const k in { a: 1, b: 2 }) keys += k; +} diff --git a/crates/jsshaker/tests/module_fixtures/cf_for_of_break/entry.js b/crates/jsshaker/tests/module_fixtures/cf_for_of_break/entry.js new file mode 100644 index 00000000..0e1aa3ef --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_for_of_break/entry.js @@ -0,0 +1,3 @@ +import { seen, f } from "./m.js"; +f(); +console.log(seen); diff --git a/crates/jsshaker/tests/module_fixtures/cf_for_of_break/m.js b/crates/jsshaker/tests/module_fixtures/cf_for_of_break/m.js new file mode 100644 index 00000000..31aeac6b --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_for_of_break/m.js @@ -0,0 +1,7 @@ +export let seen = ""; +export function f() { + for (const x of [1, 2, 3, 4]) { + if (x === 3) break; + seen += x; + } +} diff --git a/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/entry.js b/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/entry.js new file mode 100644 index 00000000..134cc3c4 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/entry.js @@ -0,0 +1,3 @@ +import { acc, f } from "./m.js"; +f(); +console.log(acc); diff --git a/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/m.js b/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/m.js new file mode 100644 index 00000000..7d336e47 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/m.js @@ -0,0 +1,9 @@ +export let acc = ""; +export function f() { + outer: for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + if (j === 1) continue outer; + acc += "" + i + j; + } + } +} diff --git a/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/entry.js b/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/entry.js new file mode 100644 index 00000000..6825d6ed --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/entry.js @@ -0,0 +1,3 @@ +import { log, f } from "./m.js"; +f(1); +console.log(log); diff --git a/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/m.js b/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/m.js new file mode 100644 index 00000000..4aa0de87 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/m.js @@ -0,0 +1,12 @@ +export let log = ""; +export function f(n) { + switch (n) { + case 1: + log += "a"; + case 2: + log += "b"; + break; + case 3: + log += "c"; + } +} diff --git a/crates/jsshaker/tests/module_fixtures/cf_ternary/entry.js b/crates/jsshaker/tests/module_fixtures/cf_ternary/entry.js new file mode 100644 index 00000000..7cfb508f --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_ternary/entry.js @@ -0,0 +1,3 @@ +import { v, f } from "./m.js"; +f(0); +console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/cf_ternary/m.js b/crates/jsshaker/tests/module_fixtures/cf_ternary/m.js new file mode 100644 index 00000000..90f8927c --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_ternary/m.js @@ -0,0 +1,4 @@ +export let v = "x"; +export function f(n) { + v = n ? "yes" : "no"; +} diff --git a/crates/jsshaker/tests/module_fixtures/cf_while_nested/entry.js b/crates/jsshaker/tests/module_fixtures/cf_while_nested/entry.js new file mode 100644 index 00000000..4a023717 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_while_nested/entry.js @@ -0,0 +1,3 @@ +import { t, f } from "./m.js"; +f(); +console.log(t); diff --git a/crates/jsshaker/tests/module_fixtures/cf_while_nested/m.js b/crates/jsshaker/tests/module_fixtures/cf_while_nested/m.js new file mode 100644 index 00000000..8fc2234a --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_while_nested/m.js @@ -0,0 +1,12 @@ +export let t = 0; +export function f() { + let i = 0; + while (i < 3) { + let j = 0; + while (j < 2) { + t += 1; + j++; + } + i++; + } +} diff --git a/crates/jsshaker/tests/module_fixtures/cls_inheritance_super/entry.js b/crates/jsshaker/tests/module_fixtures/cls_inheritance_super/entry.js new file mode 100644 index 00000000..600ddf4c --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cls_inheritance_super/entry.js @@ -0,0 +1,2 @@ +import { Child } from "./m.js"; +console.log(new Child().describe()); diff --git a/crates/jsshaker/tests/module_fixtures/cls_inheritance_super/m.js b/crates/jsshaker/tests/module_fixtures/cls_inheritance_super/m.js new file mode 100644 index 00000000..b89def12 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cls_inheritance_super/m.js @@ -0,0 +1,17 @@ +class Base { + constructor() { + this.tag = "base"; + } + describe() { + return "I am " + this.tag; + } +} +export class Child extends Base { + constructor() { + super(); + this.tag = "child"; + } + describe() { + return super.describe() + "!"; + } +} diff --git a/crates/jsshaker/tests/module_fixtures/cls_static_block_order/entry.js b/crates/jsshaker/tests/module_fixtures/cls_static_block_order/entry.js new file mode 100644 index 00000000..42e796bf --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cls_static_block_order/entry.js @@ -0,0 +1,3 @@ +import { log, K } from "./m.js"; +new K(); +console.log(log); diff --git a/crates/jsshaker/tests/module_fixtures/cls_static_block_order/m.js b/crates/jsshaker/tests/module_fixtures/cls_static_block_order/m.js new file mode 100644 index 00000000..03ab9180 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cls_static_block_order/m.js @@ -0,0 +1,9 @@ +export let log = ""; +export class K { + static { + log += "static;"; + } + constructor() { + log += "ctor;"; + } +} diff --git a/crates/jsshaker/tests/module_fixtures/default_export_reassignment/entry.js b/crates/jsshaker/tests/module_fixtures/default_export_reassignment/entry.js new file mode 100644 index 00000000..40d23b0a --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/default_export_reassignment/entry.js @@ -0,0 +1,4 @@ +import impl, { reassign } from "./mod.js"; + +reassign(); +console.log(impl()); diff --git a/crates/jsshaker/tests/module_fixtures/default_export_reassignment/mod.js b/crates/jsshaker/tests/module_fixtures/default_export_reassignment/mod.js new file mode 100644 index 00000000..5e9b5aa4 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/default_export_reassignment/mod.js @@ -0,0 +1,6 @@ +export default function impl() { + return 1; +} +export function reassign() { + impl = () => 2; +} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_computed/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_computed/entry.js new file mode 100644 index 00000000..fcf6cb3a --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_computed/entry.js @@ -0,0 +1,3 @@ +import { v, f } from "./m.js"; +f(); +console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_computed/m.js b/crates/jsshaker/tests/module_fixtures/destructure_computed/m.js new file mode 100644 index 00000000..2e4478d0 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_computed/m.js @@ -0,0 +1,5 @@ +export let v = 0; +const k = "dyn"; +export function f() { + ({ [k]: v } = { dyn: 42 }); +} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_defaults/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_defaults/entry.js new file mode 100644 index 00000000..fb07041f --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_defaults/entry.js @@ -0,0 +1,3 @@ +import { a, b, f } from "./m.js"; +f(); +console.log(a, b); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_defaults/m.js b/crates/jsshaker/tests/module_fixtures/destructure_defaults/m.js new file mode 100644 index 00000000..a1c0a5ac --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_defaults/m.js @@ -0,0 +1,5 @@ +export let a = 0, + b = 0; +export function f() { + [a = 5, b = 6] = [undefined]; +} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_nested/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_nested/entry.js new file mode 100644 index 00000000..38bef853 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_nested/entry.js @@ -0,0 +1,3 @@ +import { a, b, c, f } from "./m.js"; +f(); +console.log(a, b, c); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_nested/m.js b/crates/jsshaker/tests/module_fixtures/destructure_nested/m.js new file mode 100644 index 00000000..fea97154 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_nested/m.js @@ -0,0 +1,12 @@ +export let a = 0, + b = 0, + c = 0; +export function f() { + ({ + x: a, + y: { + z: b, + w: [c], + }, + } = { x: 1, y: { z: 2, w: [3] } }); +} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_object_rest/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_object_rest/entry.js new file mode 100644 index 00000000..96566492 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_object_rest/entry.js @@ -0,0 +1,3 @@ +import { one, others, f } from "./m.js"; +f(); +console.log(one, JSON.stringify(others)); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_object_rest/m.js b/crates/jsshaker/tests/module_fixtures/destructure_object_rest/m.js new file mode 100644 index 00000000..007a360a --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_object_rest/m.js @@ -0,0 +1,5 @@ +export let one = 0, + others = {}; +export function f() { + ({ one, ...others } = { one: 1, two: 2, three: 3 }); +} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_rest/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_rest/entry.js new file mode 100644 index 00000000..2302cb00 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_rest/entry.js @@ -0,0 +1,3 @@ +import { head, rest, f } from "./m.js"; +f(); +console.log(head, JSON.stringify(rest)); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_rest/m.js b/crates/jsshaker/tests/module_fixtures/destructure_rest/m.js new file mode 100644 index 00000000..e40ce9e9 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_rest/m.js @@ -0,0 +1,5 @@ +export let head = 0, + rest = []; +export function f() { + [head, ...rest] = [1, 2, 3]; +} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_swap/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_swap/entry.js new file mode 100644 index 00000000..fb07041f --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_swap/entry.js @@ -0,0 +1,3 @@ +import { a, b, f } from "./m.js"; +f(); +console.log(a, b); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_swap/m.js b/crates/jsshaker/tests/module_fixtures/destructure_swap/m.js new file mode 100644 index 00000000..33cb1f4c --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_swap/m.js @@ -0,0 +1,5 @@ +export let a = 1, + b = 2; +export function f() { + [a, b] = [b, a]; +} diff --git a/crates/jsshaker/tests/module_fixtures/exc_rethrow/entry.js b/crates/jsshaker/tests/module_fixtures/exc_rethrow/entry.js new file mode 100644 index 00000000..295013d6 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/exc_rethrow/entry.js @@ -0,0 +1,7 @@ +import { v, f } from "./m.js"; +try { + f(); +} catch (e) { + console.log("caught", e.message); +} +console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/exc_rethrow/m.js b/crates/jsshaker/tests/module_fixtures/exc_rethrow/m.js new file mode 100644 index 00000000..531f8516 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/exc_rethrow/m.js @@ -0,0 +1,12 @@ +export let v = ""; +export function f() { + try { + v = "try"; + throw new Error("boom"); + } catch (e) { + v += "|catch"; + throw e; + } finally { + v += "|finally"; + } +} diff --git a/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/entry.js b/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/entry.js new file mode 100644 index 00000000..c58672f1 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/entry.js @@ -0,0 +1,3 @@ +import { count, f } from "./m.js"; +f(); +console.log(count); diff --git a/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/m.js b/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/m.js new file mode 100644 index 00000000..2f8998b5 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/m.js @@ -0,0 +1,11 @@ +export let count = 0; +export function f() { + for (let i = 0; i < 5; i++) { + try { + if (i % 2) throw new Error(); + count += 10; + } catch { + count += 1; + } + } +} diff --git a/crates/jsshaker/tests/module_fixtures/fn_arguments_object/entry.js b/crates/jsshaker/tests/module_fixtures/fn_arguments_object/entry.js new file mode 100644 index 00000000..1c205480 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_arguments_object/entry.js @@ -0,0 +1,3 @@ +import { n, f } from "./m.js"; +f(1, 2, 3); +console.log(n); diff --git a/crates/jsshaker/tests/module_fixtures/fn_arguments_object/m.js b/crates/jsshaker/tests/module_fixtures/fn_arguments_object/m.js new file mode 100644 index 00000000..25d2af8f --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_arguments_object/m.js @@ -0,0 +1,4 @@ +export let n = 0; +export function f() { + n = arguments.length + arguments[2]; +} diff --git a/crates/jsshaker/tests/module_fixtures/fn_default_params/entry.js b/crates/jsshaker/tests/module_fixtures/fn_default_params/entry.js new file mode 100644 index 00000000..0132e0bb --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_default_params/entry.js @@ -0,0 +1,4 @@ +import { v, f } from "./m.js"; +f(); +f(9); +console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/fn_default_params/m.js b/crates/jsshaker/tests/module_fixtures/fn_default_params/m.js new file mode 100644 index 00000000..2210ecc9 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_default_params/m.js @@ -0,0 +1,4 @@ +export let v = ""; +export function f(x = 5) { + v += x; +} diff --git a/crates/jsshaker/tests/module_fixtures/fn_getter_setter/entry.js b/crates/jsshaker/tests/module_fixtures/fn_getter_setter/entry.js new file mode 100644 index 00000000..2e659bc1 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_getter_setter/entry.js @@ -0,0 +1,3 @@ +import { obj, backing } from "./m.js"; +obj.val = 5; +console.log(obj.val, backing); diff --git a/crates/jsshaker/tests/module_fixtures/fn_getter_setter/m.js b/crates/jsshaker/tests/module_fixtures/fn_getter_setter/m.js new file mode 100644 index 00000000..caf44963 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_getter_setter/m.js @@ -0,0 +1,9 @@ +export let backing = 0; +export const obj = { + get val() { + return backing * 2; + }, + set val(v) { + backing = v; + }, +}; diff --git a/crates/jsshaker/tests/module_fixtures/fn_higher_order/entry.js b/crates/jsshaker/tests/module_fixtures/fn_higher_order/entry.js new file mode 100644 index 00000000..d61c3f47 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_higher_order/entry.js @@ -0,0 +1,3 @@ +import { v, apply } from "./m.js"; +apply((x) => x * 3); +console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/fn_higher_order/m.js b/crates/jsshaker/tests/module_fixtures/fn_higher_order/m.js new file mode 100644 index 00000000..994c0761 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_higher_order/m.js @@ -0,0 +1,4 @@ +export let v = 2; +export function apply(fn) { + v = fn(v); +} diff --git a/crates/jsshaker/tests/module_fixtures/fn_reassign_then_call/entry.js b/crates/jsshaker/tests/module_fixtures/fn_reassign_then_call/entry.js new file mode 100644 index 00000000..cecc6b1e --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_reassign_then_call/entry.js @@ -0,0 +1,5 @@ +import { impl, swap, call } from "./m.js"; +console.log(call()); +swap(); +console.log(call()); +console.log(impl()); diff --git a/crates/jsshaker/tests/module_fixtures/fn_reassign_then_call/m.js b/crates/jsshaker/tests/module_fixtures/fn_reassign_then_call/m.js new file mode 100644 index 00000000..c21d03cc --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_reassign_then_call/m.js @@ -0,0 +1,7 @@ +export let impl = () => "first"; +export function swap() { + impl = () => "second"; +} +export function call() { + return impl(); +} diff --git a/crates/jsshaker/tests/module_fixtures/fn_recursion/entry.js b/crates/jsshaker/tests/module_fixtures/fn_recursion/entry.js new file mode 100644 index 00000000..f6755f60 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_recursion/entry.js @@ -0,0 +1,2 @@ +import { calls, fact } from "./m.js"; +console.log(fact(5), calls); diff --git a/crates/jsshaker/tests/module_fixtures/fn_recursion/m.js b/crates/jsshaker/tests/module_fixtures/fn_recursion/m.js new file mode 100644 index 00000000..b528687f --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_recursion/m.js @@ -0,0 +1,5 @@ +export let calls = 0; +export function fact(n) { + calls++; + return n <= 1 ? 1 : n * fact(n - 1); +} diff --git a/crates/jsshaker/tests/module_fixtures/fn_rest_args/entry.js b/crates/jsshaker/tests/module_fixtures/fn_rest_args/entry.js new file mode 100644 index 00000000..f13b2174 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_rest_args/entry.js @@ -0,0 +1,3 @@ +import { total, f } from "./m.js"; +f(1, 2, 3); +console.log(total); diff --git a/crates/jsshaker/tests/module_fixtures/fn_rest_args/m.js b/crates/jsshaker/tests/module_fixtures/fn_rest_args/m.js new file mode 100644 index 00000000..f81e7752 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_rest_args/m.js @@ -0,0 +1,4 @@ +export let total = 0; +export function f(...xs) { + for (const x of xs) total += x; +} diff --git a/crates/jsshaker/tests/module_fixtures/gen_early_return/entry.js b/crates/jsshaker/tests/module_fixtures/gen_early_return/entry.js new file mode 100644 index 00000000..5695b682 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/gen_early_return/entry.js @@ -0,0 +1,5 @@ +import { state, gen } from "./m.js"; +const it = gen(); +it.next(); +it.return(); +console.log(state); diff --git a/crates/jsshaker/tests/module_fixtures/gen_early_return/m.js b/crates/jsshaker/tests/module_fixtures/gen_early_return/m.js new file mode 100644 index 00000000..7a19b3d8 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/gen_early_return/m.js @@ -0,0 +1,10 @@ +export let state = ""; +export function* gen() { + try { + state += "start;"; + yield 1; + state += "never;"; + } finally { + state += "cleanup;"; + } +} diff --git a/crates/jsshaker/tests/module_fixtures/live_binding/entry.js b/crates/jsshaker/tests/module_fixtures/live_binding/entry.js new file mode 100644 index 00000000..8c43a11f --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/live_binding/entry.js @@ -0,0 +1,6 @@ +import { counter, increment, $window } from "./state.js"; + +increment(); +increment(); +console.log(counter); +console.log($window); diff --git a/crates/jsshaker/tests/module_fixtures/live_binding/state.js b/crates/jsshaker/tests/module_fixtures/live_binding/state.js new file mode 100644 index 00000000..d21a1de5 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/live_binding/state.js @@ -0,0 +1,12 @@ +export let counter = 0; +export function increment() { + counter++; +} +export function neverCalled() { + counter = 999; +} + +export let $window; +export function init() { + $window = window; +} diff --git a/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/entry.js b/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/entry.js new file mode 100644 index 00000000..a36d3806 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/entry.js @@ -0,0 +1,3 @@ +import { data, load } from "./m.js"; +load(); +console.log(JSON.stringify(data)); diff --git a/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/m.js b/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/m.js new file mode 100644 index 00000000..eab18a31 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/m.js @@ -0,0 +1,4 @@ +export let data = null; +export function load() { + data = JSON.parse('{"k":[1,2]}'); +} diff --git a/crates/jsshaker/tests/module_fixtures/misc_many_writes/entry.js b/crates/jsshaker/tests/module_fixtures/misc_many_writes/entry.js new file mode 100644 index 00000000..52257677 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_many_writes/entry.js @@ -0,0 +1,3 @@ +import { v, run } from "./m.js"; +run(); +console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/misc_many_writes/m.js b/crates/jsshaker/tests/module_fixtures/misc_many_writes/m.js new file mode 100644 index 00000000..a308f85e --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_many_writes/m.js @@ -0,0 +1,7 @@ +export let v = 0; +export function run() { + v = 1; + v = 2; + v = 3; + v = v + 1; +} diff --git a/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/entry.js b/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/entry.js new file mode 100644 index 00000000..697fd36d --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/entry.js @@ -0,0 +1,3 @@ +import { v, clear } from "./m.js"; +clear(); +console.log(v, v === null); diff --git a/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/m.js b/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/m.js new file mode 100644 index 00000000..aec06d53 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/m.js @@ -0,0 +1,4 @@ +export let v = { a: 1 }; +export function clear() { + v = null; +} diff --git a/crates/jsshaker/tests/module_fixtures/misc_template_literal/entry.js b/crates/jsshaker/tests/module_fixtures/misc_template_literal/entry.js new file mode 100644 index 00000000..6da74732 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_template_literal/entry.js @@ -0,0 +1,3 @@ +import { name, rename } from "./m.js"; +rename(); +console.log(`hello ${name}`); diff --git a/crates/jsshaker/tests/module_fixtures/misc_template_literal/m.js b/crates/jsshaker/tests/module_fixtures/misc_template_literal/m.js new file mode 100644 index 00000000..1053aaa8 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_template_literal/m.js @@ -0,0 +1,4 @@ +export let name = "old"; +export function rename() { + name = "new"; +} diff --git a/crates/jsshaker/tests/module_fixtures/misc_typeof_export/entry.js b/crates/jsshaker/tests/module_fixtures/misc_typeof_export/entry.js new file mode 100644 index 00000000..096b96f1 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_typeof_export/entry.js @@ -0,0 +1,4 @@ +import { v, setUndef } from "./m.js"; +console.log(typeof v); +setUndef(); +console.log(typeof v); diff --git a/crates/jsshaker/tests/module_fixtures/misc_typeof_export/m.js b/crates/jsshaker/tests/module_fixtures/misc_typeof_export/m.js new file mode 100644 index 00000000..5aa25581 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_typeof_export/m.js @@ -0,0 +1,4 @@ +export let v = "str"; +export function setUndef() { + v = undefined; +} diff --git a/crates/jsshaker/tests/module_fixtures/mod_default_class_reassign/entry.js b/crates/jsshaker/tests/module_fixtures/mod_default_class_reassign/entry.js new file mode 100644 index 00000000..12045930 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_default_class_reassign/entry.js @@ -0,0 +1,4 @@ +import D, { bump } from "./m.js"; +console.log(typeof D); +bump(); +console.log(typeof D, D); diff --git a/crates/jsshaker/tests/module_fixtures/mod_default_class_reassign/m.js b/crates/jsshaker/tests/module_fixtures/mod_default_class_reassign/m.js new file mode 100644 index 00000000..64c14511 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_default_class_reassign/m.js @@ -0,0 +1,6 @@ +export default class D { + static tag = "cls"; +} +export function bump() { + D = 42; +} diff --git a/crates/jsshaker/tests/module_fixtures/mod_diamond/entry.js b/crates/jsshaker/tests/module_fixtures/mod_diamond/entry.js new file mode 100644 index 00000000..7cf8001d --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_diamond/entry.js @@ -0,0 +1,3 @@ +import { bump, readL, readR } from "./l.js"; +bump(); +console.log(readL(), readR()); diff --git a/crates/jsshaker/tests/module_fixtures/mod_diamond/l.js b/crates/jsshaker/tests/module_fixtures/mod_diamond/l.js new file mode 100644 index 00000000..4fff0123 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_diamond/l.js @@ -0,0 +1,2 @@ +export { bump, readL } from "./left.js"; +export { readR } from "./right.js"; diff --git a/crates/jsshaker/tests/module_fixtures/mod_diamond/left.js b/crates/jsshaker/tests/module_fixtures/mod_diamond/left.js new file mode 100644 index 00000000..89ac76d7 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_diamond/left.js @@ -0,0 +1,5 @@ +import { n } from "./shared.js"; +export { bump } from "./shared.js"; +export function readL() { + return n; +} diff --git a/crates/jsshaker/tests/module_fixtures/mod_diamond/right.js b/crates/jsshaker/tests/module_fixtures/mod_diamond/right.js new file mode 100644 index 00000000..e7b56be0 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_diamond/right.js @@ -0,0 +1,4 @@ +import { n } from "./shared.js"; +export function readR() { + return n; +} diff --git a/crates/jsshaker/tests/module_fixtures/mod_diamond/shared.js b/crates/jsshaker/tests/module_fixtures/mod_diamond/shared.js new file mode 100644 index 00000000..0359058c --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_diamond/shared.js @@ -0,0 +1,4 @@ +export let n = 1; +export function bump() { + n = 99; +} diff --git a/crates/jsshaker/tests/module_fixtures/mod_import_order/b.js b/crates/jsshaker/tests/module_fixtures/mod_import_order/b.js new file mode 100644 index 00000000..ba21b064 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_import_order/b.js @@ -0,0 +1,2 @@ +import { add } from "./c.js"; +add("b;"); diff --git a/crates/jsshaker/tests/module_fixtures/mod_import_order/c.js b/crates/jsshaker/tests/module_fixtures/mod_import_order/c.js new file mode 100644 index 00000000..ba446de9 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_import_order/c.js @@ -0,0 +1,4 @@ +export let log = ""; +export function add(s) { + log += s; +} diff --git a/crates/jsshaker/tests/module_fixtures/mod_import_order/entry.js b/crates/jsshaker/tests/module_fixtures/mod_import_order/entry.js new file mode 100644 index 00000000..6dff65f8 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_import_order/entry.js @@ -0,0 +1,3 @@ +import { log } from "./c.js"; +import "./b.js"; +console.log(log); diff --git a/crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/entry.js b/crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/entry.js new file mode 100644 index 00000000..7acbc099 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/entry.js @@ -0,0 +1,2 @@ +import d from "./mid.js"; +console.log(d()); diff --git a/crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/mid.js b/crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/mid.js new file mode 100644 index 00000000..1c1047e3 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/mid.js @@ -0,0 +1 @@ +export { default } from "./src.js"; diff --git a/crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/src.js b/crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/src.js new file mode 100644 index 00000000..300a1a6d --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/src.js @@ -0,0 +1,3 @@ +export default function () { + return "passthrough"; +} diff --git a/crates/jsshaker/tests/module_fixtures/ns_object_keys/entry.js b/crates/jsshaker/tests/module_fixtures/ns_object_keys/entry.js new file mode 100644 index 00000000..7cea6fbb --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/ns_object_keys/entry.js @@ -0,0 +1,2 @@ +import * as ns from "./m.js"; +console.log(Object.keys(ns).sort().join(",")); diff --git a/crates/jsshaker/tests/module_fixtures/ns_object_keys/m.js b/crates/jsshaker/tests/module_fixtures/ns_object_keys/m.js new file mode 100644 index 00000000..54c6af45 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/ns_object_keys/m.js @@ -0,0 +1,3 @@ +export const a = 1; +export const b = 2; +export function c() {} diff --git a/crates/jsshaker/tests/module_fixtures/ns_spread/entry.js b/crates/jsshaker/tests/module_fixtures/ns_spread/entry.js new file mode 100644 index 00000000..77291531 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/ns_spread/entry.js @@ -0,0 +1,3 @@ +import * as ns from "./m.js"; +const copy = { ...ns }; +console.log(copy.a, copy.b); diff --git a/crates/jsshaker/tests/module_fixtures/ns_spread/m.js b/crates/jsshaker/tests/module_fixtures/ns_spread/m.js new file mode 100644 index 00000000..72ab60e1 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/ns_spread/m.js @@ -0,0 +1,2 @@ +export const a = 1; +export const b = 2; diff --git a/crates/jsshaker/tests/module_fixtures/obj_getter_side_effect/entry.js b/crates/jsshaker/tests/module_fixtures/obj_getter_side_effect/entry.js new file mode 100644 index 00000000..586d6b8b --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/obj_getter_side_effect/entry.js @@ -0,0 +1,4 @@ +import { hits, o } from "./m.js"; +o.val; +o.val; +console.log(hits); diff --git a/crates/jsshaker/tests/module_fixtures/obj_getter_side_effect/m.js b/crates/jsshaker/tests/module_fixtures/obj_getter_side_effect/m.js new file mode 100644 index 00000000..6fc2dae8 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/obj_getter_side_effect/m.js @@ -0,0 +1,7 @@ +export let hits = 0; +export const o = { + get val() { + hits++; + return 1; + }, +}; diff --git a/crates/jsshaker/tests/module_fixtures/op_add_sub/entry.js b/crates/jsshaker/tests/module_fixtures/op_add_sub/entry.js new file mode 100644 index 00000000..b20111a9 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_add_sub/entry.js @@ -0,0 +1,3 @@ +import { n, f } from "./m.js"; +f(); +console.log(n); diff --git a/crates/jsshaker/tests/module_fixtures/op_add_sub/m.js b/crates/jsshaker/tests/module_fixtures/op_add_sub/m.js new file mode 100644 index 00000000..50a36b18 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_add_sub/m.js @@ -0,0 +1,5 @@ +export let n = 10; +export function f() { + n += 3; + n -= 1; +} diff --git a/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/entry.js b/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/entry.js new file mode 100644 index 00000000..b20111a9 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/entry.js @@ -0,0 +1,3 @@ +import { n, f } from "./m.js"; +f(); +console.log(n); diff --git a/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/m.js b/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/m.js new file mode 100644 index 00000000..72e43618 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/m.js @@ -0,0 +1,7 @@ +export let n = 2; +export function f() { + n **= 5; + n &= 0xff; + n |= 1; + n ^= 3; +} diff --git a/crates/jsshaker/tests/module_fixtures/op_logical_assign/entry.js b/crates/jsshaker/tests/module_fixtures/op_logical_assign/entry.js new file mode 100644 index 00000000..38bef853 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_logical_assign/entry.js @@ -0,0 +1,3 @@ +import { a, b, c, f } from "./m.js"; +f(); +console.log(a, b, c); diff --git a/crates/jsshaker/tests/module_fixtures/op_logical_assign/m.js b/crates/jsshaker/tests/module_fixtures/op_logical_assign/m.js new file mode 100644 index 00000000..e3c2a683 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_logical_assign/m.js @@ -0,0 +1,8 @@ +export let a = null, + b = 1, + c = 0; +export function f() { + a ??= "set"; + b &&= 9; + c ||= 7; +} diff --git a/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/entry.js b/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/entry.js new file mode 100644 index 00000000..b20111a9 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/entry.js @@ -0,0 +1,3 @@ +import { n, f } from "./m.js"; +f(); +console.log(n); diff --git a/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/m.js b/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/m.js new file mode 100644 index 00000000..01532306 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/m.js @@ -0,0 +1,6 @@ +export let n = 20; +export function f() { + n *= 3; + n /= 2; + n %= 7; +} diff --git a/crates/jsshaker/tests/module_fixtures/op_shifts/entry.js b/crates/jsshaker/tests/module_fixtures/op_shifts/entry.js new file mode 100644 index 00000000..b20111a9 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_shifts/entry.js @@ -0,0 +1,3 @@ +import { n, f } from "./m.js"; +f(); +console.log(n); diff --git a/crates/jsshaker/tests/module_fixtures/op_shifts/m.js b/crates/jsshaker/tests/module_fixtures/op_shifts/m.js new file mode 100644 index 00000000..d1cba0c8 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_shifts/m.js @@ -0,0 +1,6 @@ +export let n = -64; +export function f() { + n <<= 1; + n >>= 2; + n >>>= 0; +} diff --git a/crates/jsshaker/tests/module_fixtures/reexported_binding/entry.js b/crates/jsshaker/tests/module_fixtures/reexported_binding/entry.js new file mode 100644 index 00000000..df65efb4 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/reexported_binding/entry.js @@ -0,0 +1,4 @@ +import { value, bump } from "./middle.js"; + +bump(); +console.log(value); diff --git a/crates/jsshaker/tests/module_fixtures/reexported_binding/middle.js b/crates/jsshaker/tests/module_fixtures/reexported_binding/middle.js new file mode 100644 index 00000000..affdf399 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/reexported_binding/middle.js @@ -0,0 +1 @@ +export { value, bump } from "./source.js"; diff --git a/crates/jsshaker/tests/module_fixtures/reexported_binding/source.js b/crates/jsshaker/tests/module_fixtures/reexported_binding/source.js new file mode 100644 index 00000000..720b88b6 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/reexported_binding/source.js @@ -0,0 +1,4 @@ +export let value = 1; +export function bump() { + value += 1; +} diff --git a/crates/jsshaker/tests/module_fixtures/tdz_circular_throw/a.js b/crates/jsshaker/tests/module_fixtures/tdz_circular_throw/a.js new file mode 100644 index 00000000..bb1ce261 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/tdz_circular_throw/a.js @@ -0,0 +1,3 @@ +import { readEarly } from "./b.js"; +export let x = 1; +console.log(readEarly()); diff --git a/crates/jsshaker/tests/module_fixtures/tdz_circular_throw/b.js b/crates/jsshaker/tests/module_fixtures/tdz_circular_throw/b.js new file mode 100644 index 00000000..0c361fc9 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/tdz_circular_throw/b.js @@ -0,0 +1,4 @@ +import { x } from "./a.js"; +export function readEarly() { + return x; +} diff --git a/crates/jsshaker/tests/module_fixtures/tdz_circular_throw/entry.js b/crates/jsshaker/tests/module_fixtures/tdz_circular_throw/entry.js new file mode 100644 index 00000000..1c125510 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/tdz_circular_throw/entry.js @@ -0,0 +1 @@ +import "./a.js"; diff --git a/crates/jsshaker/tests/snapshots/module_test@async_sequential__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@async_sequential__entry.js.snap new file mode 100644 index 00000000..2cda2afe --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@async_sequential__entry.js.snap @@ -0,0 +1,17 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/async_sequential/entry.js +--- +//#region /entry.js +import { order, run } from "./m.js"; +await run(); +console.log(order); + +//#region /m.js +export let order = ""; +export async function run() { + order += "a"; + order += "b"; + order += "c"; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@async_try_catch__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@async_try_catch__entry.js.snap new file mode 100644 index 00000000..e26a05f4 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@async_try_catch__entry.js.snap @@ -0,0 +1,21 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/async_try_catch/entry.js +--- +//#region /entry.js +import { v, run } from "./m.js"; +await run(); +console.log(v); + +//#region /m.js +export let v = ""; +export async function run() { + try { + await Promise.reject(new Error("x")); + } catch { + v = "caught"; + } finally { + v += "|done"; + } +} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_comma_sequence__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_comma_sequence__entry.js.snap new file mode 100644 index 00000000..cb9e362d --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cf_comma_sequence__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cf_comma_sequence/entry.js +--- +//#region /entry.js +import { a, b, f } from "./m.js"; +f(); +console.log(a, b); + +//#region /m.js +export let a = 0, b = 0; +export function f() { + a = (b = 2, 3); +} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_early_return__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_early_return__entry.js.snap new file mode 100644 index 00000000..c7a800e0 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cf_early_return__entry.js.snap @@ -0,0 +1,18 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cf_early_return/entry.js +--- +//#region /entry.js +import { v, f } from "./m.js"; +f(true); +f(false); +console.log(v); + +//#region /m.js +export let v = ""; +export function f(bail) { + v += "1"; + if (bail) return; + v += "2"; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_for_in__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_for_in__entry.js.snap new file mode 100644 index 00000000..3de92d6b --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cf_for_in__entry.js.snap @@ -0,0 +1,18 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cf_for_in/entry.js +--- +//#region /entry.js +import { keys, f } from "./m.js"; +f(); +console.log(keys); + +//#region /m.js +export let keys = ""; +export function f() { + for (const k in { + a: 1, + b: 2 + }) keys += k; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_for_of_break__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_for_of_break__entry.js.snap new file mode 100644 index 00000000..0b0de88d --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cf_for_of_break__entry.js.snap @@ -0,0 +1,23 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cf_for_of_break/entry.js +--- +//#region /entry.js +import { seen, f } from "./m.js"; +f(); +console.log(seen); + +//#region /m.js +export let seen = ""; +export function f() { + for (const x of [ + 1, + 2, + 3, + , + ]) { + if (x === 3) break; + seen += x; + } +} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_labeled_continue__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_labeled_continue__entry.js.snap new file mode 100644 index 00000000..7377a6d9 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cf_labeled_continue__entry.js.snap @@ -0,0 +1,20 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cf_labeled_continue/entry.js +--- +//#region /entry.js +import { acc, f } from "./m.js"; +f(); +console.log(acc); + +//#region /m.js +export let acc = ""; +export function f() { + outer: for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + if (j === 1) continue outer; + acc += "" + i + j; + } + } +} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_switch_fallthrough__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_switch_fallthrough__entry.js.snap new file mode 100644 index 00000000..80b3959f --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cf_switch_fallthrough__entry.js.snap @@ -0,0 +1,20 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/entry.js +--- +//#region /entry.js +import { log, f } from "./m.js"; +f(); +console.log(log); + +//#region /m.js +export let log = ""; +export function f() { + switch (1) { + case 1: + log += "a"; + log += "b"; + break; + } +} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_ternary__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_ternary__entry.js.snap new file mode 100644 index 00000000..4984ac3f --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cf_ternary__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cf_ternary/entry.js +--- +//#region /entry.js +import { v, f } from "./m.js"; +f(); +console.log(v); + +//#region /m.js +export let v = "x"; +export function f() { + v = "no"; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_while_nested__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_while_nested__entry.js.snap new file mode 100644 index 00000000..face8c26 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cf_while_nested__entry.js.snap @@ -0,0 +1,23 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cf_while_nested/entry.js +--- +//#region /entry.js +import { t, f } from "./m.js"; +f(); +console.log(t); + +//#region /m.js +export let t = 0; +export function f() { + let i = 0; + while (i < 3) { + let j = 0; + while (j < 2) { + t += 1; + j++; + } + i++; + } +} diff --git a/crates/jsshaker/tests/snapshots/module_test@cls_inheritance_super__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cls_inheritance_super__entry.js.snap new file mode 100644 index 00000000..cc047069 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cls_inheritance_super__entry.js.snap @@ -0,0 +1,10 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cls_inheritance_super/entry.js +--- +//#region /entry.js +import {} from "./m.js"; +console.log("I am child!"); + +//#region /m.js diff --git a/crates/jsshaker/tests/snapshots/module_test@cls_static_block_order__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cls_static_block_order__entry.js.snap new file mode 100644 index 00000000..7b9ff5c2 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cls_static_block_order__entry.js.snap @@ -0,0 +1,20 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cls_static_block_order/entry.js +--- +//#region /entry.js +import { log, K } from "./m.js"; +new K(); +console.log(log); + +//#region /m.js +export let log = ""; +export class K { + static { + log += "static;"; + } + constructor() { + log += "ctor;"; + } +} diff --git a/crates/jsshaker/tests/snapshots/module_test@default_export_reassignment__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@default_export_reassignment__entry.js.snap new file mode 100644 index 00000000..871f21de --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@default_export_reassignment__entry.js.snap @@ -0,0 +1,17 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/default_export_reassignment/entry.js +--- +//#region /entry.js +import impl, { reassign } from "./mod.js"; +reassign(); +console.log(impl()); + +//#region /mod.js +export default function impl() { + return 1; +} +export function reassign() { + impl = () => 2; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_computed__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_computed__entry.js.snap new file mode 100644 index 00000000..a65cedf1 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@destructure_computed__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/destructure_computed/entry.js +--- +//#region /entry.js +import { v, f } from "./m.js"; +f(); +console.log(v); + +//#region /m.js +export let v = 0; +export function f() { + ({["a"]: v} = { a: 42 }); +} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_defaults__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_defaults__entry.js.snap new file mode 100644 index 00000000..bf98642b --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@destructure_defaults__entry.js.snap @@ -0,0 +1,16 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/destructure_defaults/entry.js +--- +//#region /entry.js +import { a, b, f } from "./m.js"; +f(); +console.log(a, b); + +//#region /m.js +export let a = 0, b = 0; +export function f() { + [a = 5, b = 6] = [void 0]; +} +var __unused__; diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_nested__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_nested__entry.js.snap new file mode 100644 index 00000000..3ca6adbf --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@destructure_nested__entry.js.snap @@ -0,0 +1,21 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/destructure_nested/entry.js +--- +//#region /entry.js +import { a, b, c, f } from "./m.js"; +f(); +console.log(a, b, c); + +//#region /m.js +export let a = 0, b = 0, c = 0; +export function f() { + ({a: a, b: {a: b, b: [c]}} = { + a: 1, + b: { + a: 2, + b: [3] + } + }); +} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_object_rest__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_object_rest__entry.js.snap new file mode 100644 index 00000000..73e8bbbb --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@destructure_object_rest__entry.js.snap @@ -0,0 +1,19 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/destructure_object_rest/entry.js +--- +//#region /entry.js +import { one, others, f } from "./m.js"; +f(); +console.log(one, JSON.stringify(others)); + +//#region /m.js +export let one = 0, others = {}; +export function f() { + ({one: one, ...others} = { + one: 1, + two: 2, + three: 3 + }); +} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_rest__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_rest__entry.js.snap new file mode 100644 index 00000000..091527ce --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@destructure_rest__entry.js.snap @@ -0,0 +1,19 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/destructure_rest/entry.js +--- +//#region /entry.js +import { head, rest, f } from "./m.js"; +f(); +console.log(head, JSON.stringify(rest)); + +//#region /m.js +export let head = 0, rest = []; +export function f() { + [head, ...rest] = [ + 1, + 2, + 3 + ]; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_swap__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_swap__entry.js.snap new file mode 100644 index 00000000..09064c1f --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@destructure_swap__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/destructure_swap/entry.js +--- +//#region /entry.js +import { a, b, f } from "./m.js"; +f(); +console.log(a, b); + +//#region /m.js +export let a = 1, b = 2; +export function f() { + [a, b] = [b, a]; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@exc_rethrow__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@exc_rethrow__entry.js.snap new file mode 100644 index 00000000..abc1f0a4 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@exc_rethrow__entry.js.snap @@ -0,0 +1,27 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/exc_rethrow/entry.js +--- +//#region /entry.js +import { v, f } from "./m.js"; +try { + f(); +} catch (e) { + console.log("caught", e.message); +} +console.log(v); + +//#region /m.js +export let v = ""; +export function f() { + try { + v = "try"; + throw new Error("boom"); + } catch (e) { + v += "|catch"; + throw e; + } finally { + v += "|finally"; + } +} diff --git a/crates/jsshaker/tests/snapshots/module_test@exc_throw_in_loop__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@exc_throw_in_loop__entry.js.snap new file mode 100644 index 00000000..b29e9594 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@exc_throw_in_loop__entry.js.snap @@ -0,0 +1,22 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/entry.js +--- +//#region /entry.js +import { count, f } from "./m.js"; +f(); +console.log(count); + +//#region /m.js +export let count = 0; +export function f() { + for (let i = 0; i < 5; i++) { + try { + if (i % 2) throw new Error(); + count += 10; + } catch { + count += 1; + } + } +} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_arguments_object__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_arguments_object__entry.js.snap new file mode 100644 index 00000000..68b9eb4c --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@fn_arguments_object__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/fn_arguments_object/entry.js +--- +//#region /entry.js +import { n, f } from "./m.js"; +f(1, 2, 3); +console.log(n); + +//#region /m.js +export let n = 0; +export function f() { + n = arguments.length + arguments[2]; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_default_params__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_default_params__entry.js.snap new file mode 100644 index 00000000..98121740 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@fn_default_params__entry.js.snap @@ -0,0 +1,16 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/fn_default_params/entry.js +--- +//#region /entry.js +import { v, f } from "./m.js"; +f(); +f(9); +console.log(v); + +//#region /m.js +export let v = ""; +export function f(x = 5) { + v += x; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_getter_setter__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_getter_setter__entry.js.snap new file mode 100644 index 00000000..ab30809b --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@fn_getter_setter__entry.js.snap @@ -0,0 +1,20 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/fn_getter_setter/entry.js +--- +//#region /entry.js +import { obj, backing } from "./m.js"; +obj.val = 5; +console.log(obj.val, backing); + +//#region /m.js +export let backing = 0; +export const obj = { + get val() { + return backing * 2; + }, + set val(__unused_447C) { + backing = 5; + } +}; diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_higher_order__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_higher_order__entry.js.snap new file mode 100644 index 00000000..d86d8c44 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@fn_higher_order__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/fn_higher_order/entry.js +--- +//#region /entry.js +import { v, apply } from "./m.js"; +apply((x) => x * 3); +console.log(v); + +//#region /m.js +export let v = 2; +export function apply(fn) { + v = fn(v); +} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_reassign_then_call__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_reassign_then_call__entry.js.snap new file mode 100644 index 00000000..18d6df22 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@fn_reassign_then_call__entry.js.snap @@ -0,0 +1,20 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/fn_reassign_then_call/entry.js +--- +//#region /entry.js +import { impl, swap, call } from "./m.js"; +console.log(call()); +swap(); +console.log(call()); +console.log(impl()); + +//#region /m.js +export let impl = () => "first"; +export function swap() { + impl = () => "second"; +} +export function call() { + return impl(); +} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_recursion__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_recursion__entry.js.snap new file mode 100644 index 00000000..9f889fee --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@fn_recursion__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/fn_recursion/entry.js +--- +//#region /entry.js +import { calls, fact } from "./m.js"; +console.log(fact(5), calls); + +//#region /m.js +export let calls = 0; +export function fact(n) { + calls++; + return n <= 1 ? 1 : n * fact(n - 1); +} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_rest_args__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_rest_args__entry.js.snap new file mode 100644 index 00000000..f706e792 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@fn_rest_args__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/fn_rest_args/entry.js +--- +//#region /entry.js +import { total, f } from "./m.js"; +f(1, 2, 3); +console.log(total); + +//#region /m.js +export let total = 0; +export function f(...xs) { + for (const x of xs) total += x; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@gen_early_return__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@gen_early_return__entry.js.snap new file mode 100644 index 00000000..6a9a0dac --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@gen_early_return__entry.js.snap @@ -0,0 +1,23 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/gen_early_return/entry.js +--- +//#region /entry.js +import { state, gen } from "./m.js"; +const it = gen(); +it.next(); +it.return(); +console.log(state); + +//#region /m.js +export let state = ""; +export function* gen() { + try { + state += "start;"; + yield 1; + state += "never;"; + } finally { + state += "cleanup;"; + } +} diff --git a/crates/jsshaker/tests/snapshots/module_test@live_binding__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@live_binding__entry.js.snap new file mode 100644 index 00000000..beabe107 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@live_binding__entry.js.snap @@ -0,0 +1,18 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/live_binding/entry.js +--- +//#region /entry.js +import { counter, increment, $window } from "./state.js"; +increment(); +increment(); +console.log(counter); +console.log($window); + +//#region /state.js +export let counter = 0; +export function increment() { + counter++; +} +export let $window; diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_json_roundtrip__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_json_roundtrip__entry.js.snap new file mode 100644 index 00000000..51cb6298 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@misc_json_roundtrip__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/entry.js +--- +//#region /entry.js +import { data, load } from "./m.js"; +load(); +console.log(JSON.stringify(data)); + +//#region /m.js +export let data = null; +export function load() { + data = JSON.parse("{\"k\":[1,2]}"); +} diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_many_writes__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_many_writes__entry.js.snap new file mode 100644 index 00000000..f6776077 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@misc_many_writes__entry.js.snap @@ -0,0 +1,18 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/misc_many_writes/entry.js +--- +//#region /entry.js +import { v, run } from "./m.js"; +run(); +console.log(v); + +//#region /m.js +export let v = 0; +export function run() { + v = 1; + v = 2; + v = 3; + v = v + 1; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_reassign_to_null__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_reassign_to_null__entry.js.snap new file mode 100644 index 00000000..af791fe3 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@misc_reassign_to_null__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/entry.js +--- +//#region /entry.js +import { v, clear } from "./m.js"; +clear(); +console.log(v, v === null); + +//#region /m.js +export let v = { a: 1 }; +export function clear() { + v = null; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_template_literal__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_template_literal__entry.js.snap new file mode 100644 index 00000000..347c7fc2 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@misc_template_literal__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/misc_template_literal/entry.js +--- +//#region /entry.js +import { name, rename } from "./m.js"; +rename(); +console.log(`hello ${name}`); + +//#region /m.js +export let name = "old"; +export function rename() { + name = "new"; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_typeof_export__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_typeof_export__entry.js.snap new file mode 100644 index 00000000..2ce04a43 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@misc_typeof_export__entry.js.snap @@ -0,0 +1,16 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/misc_typeof_export/entry.js +--- +//#region /entry.js +import { v, setUndef } from "./m.js"; +console.log(typeof v); +setUndef(); +console.log(typeof v); + +//#region /m.js +export let v = "str"; +export function setUndef() { + v = void 0; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@mod_default_class_reassign__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@mod_default_class_reassign__entry.js.snap new file mode 100644 index 00000000..0ddaf73b --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@mod_default_class_reassign__entry.js.snap @@ -0,0 +1,18 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/mod_default_class_reassign/entry.js +--- +//#region /entry.js +import D, { bump } from "./m.js"; +console.log(typeof D); +bump(); +console.log(typeof D, D); + +//#region /m.js +export default class D { + static tag = "cls"; +} +export function bump() { + D = 42; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@mod_diamond__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@mod_diamond__entry.js.snap new file mode 100644 index 00000000..274dfb5b --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@mod_diamond__entry.js.snap @@ -0,0 +1,32 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/mod_diamond/entry.js +--- +//#region /entry.js +import { bump, readL, readR } from "./l.js"; +bump(); +console.log(readL(), readR()); + +//#region /l.js +export { bump, readL } from "./left.js"; +export { readR } from "./right.js"; + +//#region /left.js +import { n } from "./shared.js"; +export { bump } from "./shared.js"; +export function readL() { + return n; +} + +//#region /right.js +import { n } from "./shared.js"; +export function readR() { + return n; +} + +//#region /shared.js +export let n = 1; +export function bump() { + n = 99; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@mod_import_order__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@mod_import_order__entry.js.snap new file mode 100644 index 00000000..2d12fcac --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@mod_import_order__entry.js.snap @@ -0,0 +1,19 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/mod_import_order/entry.js +--- +//#region /entry.js +import { log } from "./c.js"; +import "./b.js"; +console.log(log); + +//#region /b.js +import { add } from "./c.js"; +add(); + +//#region /c.js +export let log = ""; +export function add() { + log += "b;"; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@mod_reexport_default_bare__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@mod_reexport_default_bare__entry.js.snap new file mode 100644 index 00000000..7b8e7d42 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@mod_reexport_default_bare__entry.js.snap @@ -0,0 +1,14 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/mod_reexport_default_bare/entry.js +--- +//#region /entry.js +import {} from "./mid.js"; +console.log("passthrough"); + +//#region /mid.js +import "./src.js"; + +//#region /src.js +export default function() {} diff --git a/crates/jsshaker/tests/snapshots/module_test@ns_object_keys__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@ns_object_keys__entry.js.snap new file mode 100644 index 00000000..12d1f70c --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@ns_object_keys__entry.js.snap @@ -0,0 +1,13 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/ns_object_keys/entry.js +--- +//#region /entry.js +import * as ns from "./m.js"; +console.log(Object.keys(ns).sort().join(",")); + +//#region /m.js +export const a = 1; +export const b = 2; +export function c() {} diff --git a/crates/jsshaker/tests/snapshots/module_test@ns_spread__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@ns_spread__entry.js.snap new file mode 100644 index 00000000..e00821f8 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@ns_spread__entry.js.snap @@ -0,0 +1,13 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/ns_spread/entry.js +--- +//#region /entry.js +import * as ns from "./m.js"; +const copy = { ...ns }; +console.log(copy.a, copy.b); + +//#region /m.js +export const a = 1; +export const b = 2; diff --git a/crates/jsshaker/tests/snapshots/module_test@obj_getter_side_effect__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@obj_getter_side_effect__entry.js.snap new file mode 100644 index 00000000..564e56cc --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@obj_getter_side_effect__entry.js.snap @@ -0,0 +1,17 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/obj_getter_side_effect/entry.js +--- +//#region /entry.js +import { hits, o } from "./m.js"; +o.a; +o.a; +console.log(hits); + +//#region /m.js +export let hits = 0; +export const o = { get a() { + hits++; + return; +} }; diff --git a/crates/jsshaker/tests/snapshots/module_test@op_add_sub__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_add_sub__entry.js.snap new file mode 100644 index 00000000..79483dc8 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@op_add_sub__entry.js.snap @@ -0,0 +1,16 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/op_add_sub/entry.js +--- +//#region /entry.js +import { n, f } from "./m.js"; +f(); +console.log(n); + +//#region /m.js +export let n = 10; +export function f() { + n += 3; + n -= 1; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@op_exp_bitwise__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_exp_bitwise__entry.js.snap new file mode 100644 index 00000000..29e7cb36 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@op_exp_bitwise__entry.js.snap @@ -0,0 +1,18 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/op_exp_bitwise/entry.js +--- +//#region /entry.js +import { n, f } from "./m.js"; +f(); +console.log(n); + +//#region /m.js +export let n = 2; +export function f() { + n **= 5; + n &= 255; + n |= 1; + n ^= 3; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@op_logical_assign__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_logical_assign__entry.js.snap new file mode 100644 index 00000000..7157b2d2 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@op_logical_assign__entry.js.snap @@ -0,0 +1,17 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/op_logical_assign/entry.js +--- +//#region /entry.js +import { a, b, c, f } from "./m.js"; +f(); +console.log(a, b, c); + +//#region /m.js +export let a = null, b = 1, c = 0; +export function f() { + a ??= "set"; + b &&= 9; + c ||= 7; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@op_mul_div_mod__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_mul_div_mod__entry.js.snap new file mode 100644 index 00000000..8a354f51 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@op_mul_div_mod__entry.js.snap @@ -0,0 +1,17 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/op_mul_div_mod/entry.js +--- +//#region /entry.js +import { n, f } from "./m.js"; +f(); +console.log(n); + +//#region /m.js +export let n = 20; +export function f() { + n *= 3; + n /= 2; + n %= 7; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@op_shifts__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_shifts__entry.js.snap new file mode 100644 index 00000000..5e8e984e --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@op_shifts__entry.js.snap @@ -0,0 +1,17 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/op_shifts/entry.js +--- +//#region /entry.js +import { n, f } from "./m.js"; +f(); +console.log(n); + +//#region /m.js +export let n = -64; +export function f() { + n <<= 1; + n >>= 2; + n >>>= 0; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@reexported_binding__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@reexported_binding__entry.js.snap new file mode 100644 index 00000000..57ba0ee9 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@reexported_binding__entry.js.snap @@ -0,0 +1,18 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/reexported_binding/entry.js +--- +//#region /entry.js +import { value, bump } from "./middle.js"; +bump(); +console.log(value); + +//#region /middle.js +export { value, bump } from "./source.js"; + +//#region /source.js +export let value = 1; +export function bump() { + value += 1; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@tdz_circular_throw__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@tdz_circular_throw__entry.js.snap new file mode 100644 index 00000000..9d1bb976 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@tdz_circular_throw__entry.js.snap @@ -0,0 +1,18 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/tdz_circular_throw/entry.js +--- +//#region /entry.js +import "./a.js"; + +//#region /a.js +import { readEarly } from "./b.js"; +export let x = 1; +console.log(readEarly()); + +//#region /b.js +import { x } from "./a.js"; +export function readEarly() { + return x; +} diff --git a/crates/jsshaker/tests/snapshots/test@for_of_statement.js.snap b/crates/jsshaker/tests/snapshots/test@for_of_statement.js.snap index 360b161b..826671e0 100644 --- a/crates/jsshaker/tests/snapshots/test@for_of_statement.js.snap +++ b/crates/jsshaker/tests/snapshots/test@for_of_statement.js.snap @@ -1,6 +1,5 @@ --- source: crates/jsshaker/tests/mod.rs -assertion_line: 41 expression: do_tree_shake(input) input_file: crates/jsshaker/tests/fixtures/for_of_statement.js --- diff --git a/crates/jsshaker/tests/snapshots/test@throw_catch.js.snap b/crates/jsshaker/tests/snapshots/test@throw_catch.js.snap index d948ce78..98f6e219 100644 --- a/crates/jsshaker/tests/snapshots/test@throw_catch.js.snap +++ b/crates/jsshaker/tests/snapshots/test@throw_catch.js.snap @@ -1,6 +1,5 @@ --- source: crates/jsshaker/tests/mod.rs -assertion_line: 41 expression: do_tree_shake(input) input_file: crates/jsshaker/tests/fixtures/throw_catch.js --- From 40b1e572f575ca17742addf066a5130f190ca2d2 Mon Sep 17 00:00:00 2001 From: Mathi <11136549+Mathicha@users.noreply.github.com> Date: Fri, 21 Aug 2026 19:50:05 +0200 Subject: [PATCH 2/6] fix test? --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 4a0480e2..ae050f23 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ crates/jsshaker/tests/fixtures/** linguist-vendored text eol=lf crates/jsshaker/tests/snapshots/** linguist-generated text eol=lf +crates/jsshaker/tests/module_fixtures/** linguist-vendored text eol=lf From 0010cac2578e1e4a410bf44a17268aca00111eb4 Mon Sep 17 00:00:00 2001 From: Mathi <11136549+Mathicha@users.noreply.github.com> Date: Sat, 22 Aug 2026 17:40:09 +0200 Subject: [PATCH 3/6] fix: class super semantics, dynamic import, property writes --- crates/jsshaker/src/analyzer/conditional.rs | 2 +- .../builtins/globals/object_constructor.rs | 74 ++++++++++++------- crates/jsshaker/src/module.rs | 18 +++-- .../src/nodes/expr/await_expression.rs | 10 ++- .../src/nodes/expr/call_expression.rs | 4 + .../src/nodes/expr/import_expression.rs | 1 + .../src/nodes/expr/super_expression.rs | 7 +- crates/jsshaker/src/nodes/misc/callee.rs | 14 +--- crates/jsshaker/src/nodes/misc/class.rs | 51 +++++++++---- crates/jsshaker/src/nodes/misc/function.rs | 2 +- .../jsshaker/src/nodes/stmt/if_statement.rs | 8 +- crates/jsshaker/src/scope/call_scope.rs | 10 ++- crates/jsshaker/src/scope/variable_scope.rs | 6 +- crates/jsshaker/src/value/literal/mod.rs | 8 +- crates/jsshaker/src/value/literal/string.rs | 13 ++-- crates/jsshaker/src/value/primitive.rs | 13 ++-- .../tests/fixtures/class/super_static.js | 21 ++++++ .../tests/fixtures/define_property.js | 25 +++++++ crates/jsshaker/tests/mod.rs | 20 ++--- .../module_fixtures/async_sequential/entry.js | 5 +- .../module_fixtures/async_sequential/m.js | 3 + .../module_fixtures/cls_super_static/entry.js | 2 + .../module_fixtures/cls_super_static/m.js | 10 +++ .../mod_dynamic_import/entry.js | 2 + .../mod_dynamic_import/side.js | 2 + .../mod_dynamic_import_used/entry.js | 3 + .../mod_dynamic_import_used/used.js | 2 + .../mod_export_star_no_default/entry.js | 3 + .../mod_export_star_no_default/mid.js | 1 + .../mod_export_star_no_default/src.js | 2 + .../module_fixtures/mod_reexport_cycle/a.js | 2 + .../module_fixtures/mod_reexport_cycle/b.js | 1 + .../mod_reexport_cycle/entry.js | 2 + ...odule_test@async_sequential__entry.js.snap | 9 ++- ...odule_test@cls_super_static__entry.js.snap | 10 +++ ...ule_test@mod_dynamic_import__entry.js.snap | 11 +++ ...est@mod_dynamic_import_used__entry.js.snap | 13 ++++ ...@mod_export_star_no_default__entry.js.snap | 15 ++++ ...ule_test@mod_reexport_cycle__entry.js.snap | 14 ++++ .../test@assignment_expression.js.snap | 2 - .../snapshots/test@class__extends.js.snap | 2 +- .../test@class__super_static.js.snap | 7 ++ .../snapshots/test@class__traditional.js.snap | 2 +- .../snapshots/test@define_property.js.snap | 26 +++++++ .../snapshots/test@import_expression.js.snap | 9 ++- 45 files changed, 357 insertions(+), 110 deletions(-) create mode 100644 crates/jsshaker/tests/fixtures/class/super_static.js create mode 100644 crates/jsshaker/tests/fixtures/define_property.js create mode 100644 crates/jsshaker/tests/module_fixtures/cls_super_static/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cls_super_static/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_dynamic_import/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_dynamic_import/side.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/used.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/mid.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/src.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/a.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/b.js create mode 100644 crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/entry.js create mode 100644 crates/jsshaker/tests/snapshots/module_test@cls_super_static__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import_used__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@mod_export_star_no_default__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@mod_reexport_cycle__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/test@class__super_static.js.snap create mode 100644 crates/jsshaker/tests/snapshots/test@define_property.js.snap diff --git a/crates/jsshaker/src/analyzer/conditional.rs b/crates/jsshaker/src/analyzer/conditional.rs index 32ac05e3..f7fceac7 100644 --- a/crates/jsshaker/src/analyzer/conditional.rs +++ b/crates/jsshaker/src/analyzer/conditional.rs @@ -200,7 +200,7 @@ impl<'a> Analyzer<'a> { } pub fn note_conditional_branch_exit(&mut self, id: impl Into, dep: Option>) { - let Some(dep) = dep else { + let Some(dep) = dep.filter(|_| self.cf_scope().must_exited()) else { return; }; let id = id.into(); diff --git a/crates/jsshaker/src/builtins/globals/object_constructor.rs b/crates/jsshaker/src/builtins/globals/object_constructor.rs index f2e14168..cc61620b 100644 --- a/crates/jsshaker/src/builtins/globals/object_constructor.rs +++ b/crates/jsshaker/src/builtins/globals/object_constructor.rs @@ -1,6 +1,6 @@ use std::borrow::BorrowMut; -use oxc::allocator; +use oxc::{allocator, ast::ast::PropertyKind}; use crate::{ Analyzer, builtin_string, @@ -188,7 +188,8 @@ impl<'a> Builtins<'a> { } let enumerated = descriptor.enumerate_properties(analyzer, dep); let mut value = None; - let mut deps = vec![]; + let mut accessors = vec![]; + let mut attributes = vec![]; for (definite, key, value2) in enumerated.known.into_values() { if !definite { break 'trackable; @@ -201,38 +202,57 @@ impl<'a> Builtins<'a> { value = Some(self.factory.computed(value2, (key, value))); } "get" => { - // FIXME: This is not safe, but OK for now. - value = Some(self.factory.computed_unknown((value2, key, value))); + accessors.push((PropertyKind::Get, self.factory.computed(value2, key))); } - "set" | "enumerable" | "configurable" | "writable" => { + "set" => { + accessors.push((PropertyKind::Set, self.factory.computed(value2, key))); + } + "enumerable" | "configurable" | "writable" => { // TODO: actually handle these - deps.push(key); - deps.push(value2); + attributes.push(key); + attributes.push(value2); } _ => {} } } - if value.is_none() { - analyzer.push_non_det_cf_scope(); - } - object.set_property( - analyzer, - analyzer.factory.dep((enumerated.dep, descriptor.get_shallow_dep(analyzer.factory))), - key, - { - let value = value.unwrap_or(analyzer.factory.undefined); - if deps.is_empty() { - value - } else { - analyzer - .factory - .computed(value, allocator::Vec::from_iter_in(deps, analyzer.allocator)) - } - }, - ); - if value.is_none() { - analyzer.pop_cf_scope(); + if !accessors.is_empty() { + let Some(object_value) = object.value.as_object() else { + break 'trackable; + }; + let dep = self.factory.dep(( + enumerated.dep, + descriptor.get_shallow_dep(analyzer.factory), + value, + allocator::Vec::from_iter_in(attributes, analyzer.allocator), + )); + for (kind, accessor) in accessors { + let accessor = self.factory.computed(accessor, dep); + object_value.init_property(analyzer, kind, key, accessor, true); + } + } else { + if value.is_none() { + analyzer.push_non_det_cf_scope(); + } + + object.set_property( + analyzer, + analyzer.factory.dep((enumerated.dep, descriptor.get_shallow_dep(analyzer.factory))), + key, + { + let value = value.unwrap_or(analyzer.factory.undefined); + if attributes.is_empty() { + value + } else { + analyzer + .factory + .computed(value, allocator::Vec::from_iter_in(attributes, analyzer.allocator)) + } + }, + ); + if value.is_none() { + analyzer.pop_cf_scope(); + } } let deps = self.factory.dep(( diff --git a/crates/jsshaker/src/module.rs b/crates/jsshaker/src/module.rs index 9964f0c2..2983ef25 100644 --- a/crates/jsshaker/src/module.rs +++ b/crates/jsshaker/src/module.rs @@ -295,7 +295,7 @@ impl<'a> Analyzer<'a> { let reexport_all = module.reexport_all.iter().copied().collect::>(); self.include((call_id, default_export)); for named_export in named_exports { - let value = self.get_named_export_value(module_id, named_export); + let value = self.get_named_export_value(module_id, named_export, &mut FxHashSet::default()); self.include(value); } for reexport_module_id in reexport_all { @@ -307,6 +307,7 @@ impl<'a> Analyzer<'a> { &mut self, module_id: ModuleId, named_export: ExportedValue<'a>, + searched: &mut FxHashSet, ) -> Entity<'a> { match named_export { ExportedValue::Variable(scope, symbol, dep) => { @@ -325,9 +326,8 @@ impl<'a> Analyzer<'a> { ExportedValue::Function(entity, dep) => self.factory.computed(entity, dep), ExportedValue::Namespace(entity, dep) => self.factory.computed(entity, dep), ExportedValue::ReExport(module, name, dep) => { - let value = self - .get_export_value_by_name(module, name, &mut FxHashSet::default()) - .unwrap_or(self.factory.unknown); + let value = + self.get_export_value_by_name(module, name, searched).unwrap_or(self.factory.unknown); self.factory.computed(value, dep) } ExportedValue::Unknown(dep) => self.factory.computed_unknown(dep), @@ -350,11 +350,13 @@ impl<'a> Analyzer<'a> { return Some(default_export.unwrap_or(self.factory.unknown)); } if let Some(&exported_value) = module.named_exports.get(&name) { - return Some(self.get_named_export_value(module_id, exported_value)); + return Some(self.get_named_export_value(module_id, exported_value, searched)); } - for reexport_module_id in module.reexport_all.clone() { - if let Some(entity) = self.get_export_value_by_name(reexport_module_id, name, searched) { - return Some(entity); + if name != "default" { + for reexport_module_id in module.reexport_all.clone() { + if let Some(entity) = self.get_export_value_by_name(reexport_module_id, name, searched) { + return Some(entity); + } } } None diff --git a/crates/jsshaker/src/nodes/expr/await_expression.rs b/crates/jsshaker/src/nodes/expr/await_expression.rs index 972be01a..d055dbd3 100644 --- a/crates/jsshaker/src/nodes/expr/await_expression.rs +++ b/crates/jsshaker/src/nodes/expr/await_expression.rs @@ -1,6 +1,11 @@ use oxc::ast::ast::{AwaitExpression, Expression}; -use crate::{analyzer::Analyzer, entity::Entity, transformer::Transformer, utils::ast::AstKind2}; +use crate::{ + analyzer::Analyzer, + entity::Entity, + transformer::Transformer, + utils::{CalleeNode, ast::AstKind2}, +}; impl<'a> Analyzer<'a> { pub fn exec_await_expression(&mut self, node: &'a AwaitExpression<'a>) -> Entity<'a> { @@ -12,6 +17,9 @@ impl<'a> Analyzer<'a> { self.global_effect(); let value = self.exec_expression(&node.argument); + if !matches!(self.call_scope().callee.node, CalleeNode::Module | CalleeNode::Root) { + self.include(AstKind2::AwaitExpression(node)); + } value.r#await(self, AstKind2::AwaitExpression(node)) } } diff --git a/crates/jsshaker/src/nodes/expr/call_expression.rs b/crates/jsshaker/src/nodes/expr/call_expression.rs index 10fd8899..97ec4690 100644 --- a/crates/jsshaker/src/nodes/expr/call_expression.rs +++ b/crates/jsshaker/src/nodes/expr/call_expression.rs @@ -65,6 +65,10 @@ impl<'a> Analyzer<'a> { let ret_val = callee.call(self, callsite, this, args); self.scoping.current_callsite = AstKind2::ENVIRONMENT; + if matches!(&node.callee, Expression::Super(_)) { + self.init_pending_instance_fields(); + } + Ok((scope_count, ret_val, undefined)) } } diff --git a/crates/jsshaker/src/nodes/expr/import_expression.rs b/crates/jsshaker/src/nodes/expr/import_expression.rs index dc2fa7c7..ea84fcda 100644 --- a/crates/jsshaker/src/nodes/expr/import_expression.rs +++ b/crates/jsshaker/src/nodes/expr/import_expression.rs @@ -10,6 +10,7 @@ impl<'a> Analyzer<'a> { let specifier = self.exec_expression(&node.source).coerce_string(self); let options = node.options.as_ref().map(|option| self.exec_expression(option)); let dep = self.dep((AstKind2::ImportExpression(node), specifier, options)); + self.include(dep); if let Some(LiteralValue::String(specifier, _m)) = specifier.get_literal(self) && let Some(module_id) = self.resolve_and_parse_module(specifier) diff --git a/crates/jsshaker/src/nodes/expr/super_expression.rs b/crates/jsshaker/src/nodes/expr/super_expression.rs index 0870ebb0..d5e90c6b 100644 --- a/crates/jsshaker/src/nodes/expr/super_expression.rs +++ b/crates/jsshaker/src/nodes/expr/super_expression.rs @@ -5,7 +5,12 @@ use crate::{analyzer::Analyzer, builtin_string, entity::Entity, transformer::Tra impl<'a> Analyzer<'a> { pub fn exec_super(&mut self, _node: &'a Super) -> Entity<'a> { // Should only be called in member expression - self.get_super().get_property(self, self.factory.no_dep, builtin_string!("prototype")) + let (super_class, is_static) = self.get_super(); + if is_static { + super_class + } else { + super_class.get_property(self, self.factory.no_dep, builtin_string!("prototype")) + } } } diff --git a/crates/jsshaker/src/nodes/misc/callee.rs b/crates/jsshaker/src/nodes/misc/callee.rs index 0b2f1ca2..63cd5fe7 100644 --- a/crates/jsshaker/src/nodes/misc/callee.rs +++ b/crates/jsshaker/src/nodes/misc/callee.rs @@ -34,29 +34,23 @@ impl<'a> Analyzer<'a> { node: &'a Expression<'a>, ) -> Result<(usize, Entity<'a>, Option>, Entity<'a>), Entity<'a>> { if matches!(node, Expression::Super(_)) { - return Ok((0, self.get_super(), None, self.get_this())); + return Ok((0, self.get_super().0, None, self.get_this())); } let dep = AstKind2::Callee(node); if let Some((member_expr, same_chain)) = unwrap_to_member_expression(node) { - let this_of = |analyzer: &mut Analyzer<'a>, object: Entity<'a>| { - if matches!(member_expr.object(), Expression::Super(_)) { - analyzer.get_this() - } else { - object - } - }; + let super_receiver = matches!(member_expr.object(), Expression::Super(_)); if same_chain { let (scope_count, callee, undefined, (object, _)) = self.exec_member_expression_read_in_chain(member_expr, false)?; - let this = this_of(self, object); + let this = if super_receiver { self.get_this() } else { object }; Ok((scope_count, callee, undefined, self.factory.computed(this, dep))) } else { let result = self.exec_member_expression_read_in_chain(member_expr, false); Ok(match result { Ok((scope_count, value, undefined, (object, _))) => { self.pop_multiple_cf_scopes(scope_count); - let this = this_of(self, object); + let this = if super_receiver { self.get_this() } else { object }; ( 0, self.factory.optional_union(value, undefined), diff --git a/crates/jsshaker/src/nodes/misc/class.rs b/crates/jsshaker/src/nodes/misc/class.rs index 5f79cc00..37be2c5b 100644 --- a/crates/jsshaker/src/nodes/misc/class.rs +++ b/crates/jsshaker/src/nodes/misc/class.rs @@ -61,7 +61,7 @@ impl<'a> Analyzer<'a> { // Enter class statics scope self.variable_scope_mut().super_class = - Some(data.super_class.unwrap_or(self.factory.undefined)); + Some((data.super_class.unwrap_or(self.factory.undefined), true)); self.variable_scope_mut().this = Some(class.into()); // 2. Execute keys and find constructor @@ -90,10 +90,15 @@ impl<'a> Analyzer<'a> { MethodDefinitionKind::Get => PropertyKind::Get, MethodDefinitionKind::Set => PropertyKind::Set, }; - let value = self.exec_function(&node.value); if node.r#static { + let value = self.exec_function(&node.value); class.statics.init_property(self, kind, key.unwrap(), value, true); } else { + self.push_variable_scope(); + self.variable_scope_mut().super_class = + Some((data.super_class.unwrap_or(self.factory.undefined), false)); + let value = self.exec_function(&node.value); + self.pop_variable_scope(); prototype.init_property(self, kind, key.unwrap(), value, true); } } @@ -180,10 +185,29 @@ impl<'a> Analyzer<'a> { value } + fn init_instance_fields(&mut self, node: &'a Class<'a>, data: &ClassData<'a>, this: Entity<'a>) { + for (key, element) in data.keys.iter().zip(node.body.body.iter()) { + if let ClassElement::PropertyDefinition(node) = element + && !node.r#static + { + let value = self.exec_property_definition(node); + this.set_property(self, self.factory.no_dep, key.unwrap(), value); + } + } + } + + pub fn init_pending_instance_fields(&mut self) { + if let Some((node, data_cell, this)) = self.call_scope_mut().pending_instance_fields.take() + && let Ok(data) = data_cell.try_borrow() + { + self.init_instance_fields(node, &data, this); + } + } + pub fn call_class_constructor( &mut self, node: &'a Class<'a>, - data_cell: &RefCell>, + data_cell: &'a RefCell>, info: FnCallInfo<'a>, ) -> (Entity<'a>, FnCacheTrackingData<'a>) { let Ok(data) = data_cell.try_borrow() else { @@ -204,7 +228,7 @@ impl<'a> Analyzer<'a> { variable_scope.this = Some(info.this); variable_scope.arguments = Some((info.args, factory.vec(/* later filled by formal parameters */))); - variable_scope.super_class = Some(super_class); + variable_scope.super_class = Some((super_class, false)); if let Some(id) = &node.id { self.declare_binding_identifier(id, None, DeclarationKind::NamedFunctionInBody); @@ -215,23 +239,22 @@ impl<'a> Analyzer<'a> { ); } - // 1. Init properties - for (key, element) in data.keys.iter().zip(node.body.body.iter()) { - if let ClassElement::PropertyDefinition(node) = element - && !node.r#static - { - let value = self.exec_property_definition(node); - info.this.set_property(self, self.factory.no_dep, key.unwrap(), value); - } + // 1. Init properties, deferred past `super()` when derived + if data.super_class.is_none() { + self.init_instance_fields(node, &data, info.this); } // 2. Call constructor if let Some(constructor) = data.constructor { + if data.super_class.is_some() { + self.call_scope_mut().pending_instance_fields = Some((node, data_cell, info.this)); + } let function = constructor.value.as_ref(); let dep = self.factory.dep(AstKind2::Function(function)); self.cf_scope_mut().push_dep(dep); self.exec_formal_parameters(&function.params, info.args, DeclarationKind::FunctionParameter); self.exec_function_body(function.body.as_ref().unwrap()); + self.init_pending_instance_fields(); if info.include { self.include_return_values(); } @@ -239,8 +262,10 @@ impl<'a> Analyzer<'a> { let ret_val = self.factory.computed(ret_val, dep); (ret_val, FnCacheTrackingData::worst_case()) } else if let Some(super_class) = &data.super_class { - self.pop_call_scope(); + let super_class = *super_class; let ret_val = super_class.call(self, self.factory.no_dep, info.this, info.args); + self.init_instance_fields(node, &data, info.this); + self.pop_call_scope(); (ret_val, FnCacheTrackingData::worst_case()) } else { let (_, cache_tracking) = self.pop_call_scope(); diff --git a/crates/jsshaker/src/nodes/misc/function.rs b/crates/jsshaker/src/nodes/misc/function.rs index e0892559..4742b163 100644 --- a/crates/jsshaker/src/nodes/misc/function.rs +++ b/crates/jsshaker/src/nodes/misc/function.rs @@ -55,7 +55,7 @@ impl<'a> Analyzer<'a> { let factory = analyzer.factory; let variable_scope = analyzer.variable_scope_mut(); variable_scope.this = Some(info.this); - variable_scope.super_class = info.include.then_some(factory.unknown); + variable_scope.super_class = info.include.then_some((factory.unknown, false)); variable_scope.arguments = Some((info.args, factory.vec(/* later filled by formal parameters */))); diff --git a/crates/jsshaker/src/nodes/stmt/if_statement.rs b/crates/jsshaker/src/nodes/stmt/if_statement.rs index b4e312d7..b7d13061 100644 --- a/crates/jsshaker/src/nodes/stmt/if_statement.rs +++ b/crates/jsshaker/src/nodes/stmt/if_statement.rs @@ -34,9 +34,7 @@ impl<'a> Analyzer<'a> { node.alternate.is_some(), ); self.exec_statement(&node.consequent); - if self.cf_scope().must_exited() { - self.note_conditional_branch_exit(AstKind2::IfStatement(node), branch_dep); - } + self.note_conditional_branch_exit(AstKind2::IfStatement(node), branch_dep); let mut conditional_scope = self.pop_cf_scope(); if let CfScopeKind::ExitBlocker(Some(stopped_exit)) = &conditional_scope.kind { exit_target_inner = exit_target_inner.max(*stopped_exit); @@ -58,9 +56,7 @@ impl<'a> Analyzer<'a> { ); if let Some(alternate) = &node.alternate { self.exec_statement(alternate); - if self.cf_scope().must_exited() { - self.note_conditional_branch_exit(AstKind2::IfStatement(node), branch_dep); - } + self.note_conditional_branch_exit(AstKind2::IfStatement(node), branch_dep); let mut conditional_scope = self.pop_cf_scope(); if let CfScopeKind::ExitBlocker(Some(stopped_exit)) = &conditional_scope.kind { exit_target_inner = exit_target_inner.max(*stopped_exit); diff --git a/crates/jsshaker/src/scope/call_scope.rs b/crates/jsshaker/src/scope/call_scope.rs index adc095fa..8f0361e9 100644 --- a/crates/jsshaker/src/scope/call_scope.rs +++ b/crates/jsshaker/src/scope/call_scope.rs @@ -1,6 +1,6 @@ -use std::mem; +use std::{cell::RefCell, mem}; -use oxc::allocator; +use oxc::{allocator, ast::ast::Class}; use super::variable_scope::VariableScopeId; use crate::{ @@ -8,9 +8,11 @@ use crate::{ dep::DepTrait, entity::Entity, module::ModuleId, - utils::{CalleeInfo, ast::AstKind2}, + utils::{CalleeInfo, ClassData, ast::AstKind2}, }; +pub type PendingInstanceFields<'a> = (&'a Class<'a>, &'a RefCell>, Entity<'a>); + pub struct CallScope<'a> { pub callsite: AstKind2<'a>, pub callee: CalleeInfo<'a>, @@ -22,6 +24,7 @@ pub struct CallScope<'a> { pub is_async: bool, pub is_generator: bool, pub need_include_arguments: bool, + pub pending_instance_fields: Option>, #[cfg(feature = "flame")] pub scope_guard: flame::SpanGuard, @@ -49,6 +52,7 @@ impl<'a> CallScope<'a> { is_async, is_generator, need_include_arguments: false, + pending_instance_fields: None, #[cfg(feature = "flame")] scope_guard: flame::start_guard(callee.debug_name.to_string()), diff --git a/crates/jsshaker/src/scope/variable_scope.rs b/crates/jsshaker/src/scope/variable_scope.rs index f1b00e00..8eb302d8 100644 --- a/crates/jsshaker/src/scope/variable_scope.rs +++ b/crates/jsshaker/src/scope/variable_scope.rs @@ -41,7 +41,7 @@ pub struct VariableScope<'a> { pub variables: allocator::HashMap<'a, SymbolId, &'a RefCell>>, pub this: Option>, pub arguments: Option<(ArgumentsValue<'a>, allocator::Vec<'a, SymbolId>)>, - pub super_class: Option>, + pub super_class: Option<(Entity<'a>, bool)>, } impl fmt::Debug for VariableScope<'_> { @@ -464,12 +464,12 @@ impl<'a> Analyzer<'a> { unreachable!() } - pub fn get_super(&mut self) -> Entity<'a> { + pub fn get_super(&mut self) -> (Entity<'a>, bool) { for scope in self.scoping.variable.iter_rev() { if let Some(super_class) = scope.super_class { return super_class; } } - self.factory.unknown + (self.factory.unknown, false) } } diff --git a/crates/jsshaker/src/value/literal/mod.rs b/crates/jsshaker/src/value/literal/mod.rs index aee82ba3..2c7066cc 100644 --- a/crates/jsshaker/src/value/literal/mod.rs +++ b/crates/jsshaker/src/value/literal/mod.rs @@ -90,11 +90,11 @@ impl<'a> ValueTrait<'a> for LiteralValue<'a> { ) { if matches!(self, LiteralValue::Null | LiteralValue::Undefined) { analyzer.throw_builtin_error("Cannot set property of null or undefined"); - if analyzer.config.preserve_exceptions { - escaped::set_property(analyzer, dep, key, value) - } } else { - // No effect + analyzer.throw_builtin_error("Cannot create property on a primitive value"); + } + if analyzer.config.preserve_exceptions { + escaped::set_property(analyzer, dep, key, value) } } diff --git a/crates/jsshaker/src/value/literal/string.rs b/crates/jsshaker/src/value/literal/string.rs index be33470c..f1d74901 100644 --- a/crates/jsshaker/src/value/literal/string.rs +++ b/crates/jsshaker/src/value/literal/string.rs @@ -72,12 +72,15 @@ impl<'a> ValueTrait<'a> for Atom<'a> { fn set_property( &'a self, - _analyzer: &mut Analyzer<'a>, - _dep: Dep<'a>, - _key: Entity<'a>, - _value: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Dep<'a>, + key: Entity<'a>, + value: Entity<'a>, ) { - // No effect + analyzer.throw_builtin_error("Cannot create property on a primitive value"); + if analyzer.config.preserve_exceptions { + escaped::set_property(analyzer, dep, key, value) + } } fn enumerate_properties( diff --git a/crates/jsshaker/src/value/primitive.rs b/crates/jsshaker/src/value/primitive.rs index 04239d69..507a9cc9 100644 --- a/crates/jsshaker/src/value/primitive.rs +++ b/crates/jsshaker/src/value/primitive.rs @@ -46,12 +46,15 @@ impl<'a> ValueTrait<'a> for PrimitiveValue { fn set_property( &'a self, - _analyzer: &mut Analyzer<'a>, - _dep: Dep<'a>, - _key: Entity<'a>, - _value: Entity<'a>, + analyzer: &mut Analyzer<'a>, + dep: Dep<'a>, + key: Entity<'a>, + value: Entity<'a>, ) { - // No effect + analyzer.throw_builtin_error("Cannot create property on a primitive value"); + if analyzer.config.preserve_exceptions { + escaped::set_property(analyzer, dep, key, value) + } } fn enumerate_properties( diff --git a/crates/jsshaker/tests/fixtures/class/super_static.js b/crates/jsshaker/tests/fixtures/class/super_static.js new file mode 100644 index 00000000..ae6ee92c --- /dev/null +++ b/crates/jsshaker/tests/fixtures/class/super_static.js @@ -0,0 +1,21 @@ +class Base { + static tag = "static-base"; + static who() { + return "static " + this.tag; + } + who() { + return "instance"; + } +} +class Child extends Base { + static tag = "static-child"; + static viaStaticMethod() { + return super.who(); + } + static viaStaticBlockResult = (() => Base.tag)(); + viaInstanceMethod() { + return super.who(); + } +} +console.log(Child.viaStaticMethod()); +console.log(new Child().viaInstanceMethod()); diff --git a/crates/jsshaker/tests/fixtures/define_property.js b/crates/jsshaker/tests/fixtures/define_property.js new file mode 100644 index 00000000..e6edab02 --- /dev/null +++ b/crates/jsshaker/tests/fixtures/define_property.js @@ -0,0 +1,25 @@ +const o = {}; +let backing = 0; +Object.defineProperty(o, "p", { + get() { + return backing * 2; + }, + set(v) { + backing = v + 1; + }, + enumerable: true, + configurable: true, +}); +o.p = 4; +console.log(o.p, backing, JSON.stringify(Object.keys(o))); + +const ro = { z: 1 }; +Object.defineProperty(ro, "hidden", { + get() { + return "H"; + }, +}); +console.log(ro.hidden, JSON.stringify(Object.keys(ro)), JSON.stringify(ro)); + +const d = Object.getOwnPropertyDescriptor(o, "p"); +console.log(typeof d.get, typeof d.set, d.enumerable, d.configurable); diff --git a/crates/jsshaker/tests/mod.rs b/crates/jsshaker/tests/mod.rs index 165d9831..d6fb2647 100644 --- a/crates/jsshaker/tests/mod.rs +++ b/crates/jsshaker/tests/mod.rs @@ -54,8 +54,6 @@ fn test() { }); } -/// Loads every `.js` file under `dir` into the virtual file system, keyed by its -/// path relative to the fixture root (so `./dep.js` resolves from `/entry.js`). fn collect_modules(dir: &Path, prefix: &str, files: &mut HashMap) { let mut entries: Vec<_> = fs::read_dir(dir).unwrap().map(|entry| entry.unwrap()).collect(); entries.sort_by_key(|entry| entry.file_name()); @@ -64,7 +62,7 @@ fn collect_modules(dir: &Path, prefix: &str, files: &mut HashMap let path = entry.path(); if path.is_dir() { collect_modules(&path, &format!("{prefix}{name}/"), files); - } else if path.extension().is_some_and(|ext| ext == "js") { + } else if path.extension().is_some_and(|ext| ext == "js" || ext == "mjs" || ext == "cjs") { let key = normalize_str(&format!("/{prefix}{name}")); files.insert(key, fs::read_to_string(&path).unwrap()); } @@ -76,30 +74,28 @@ fn do_tree_shake_modules(dir: &Path) -> String { collect_modules(dir, "", &mut files); let entry = normalize_str("/entry.js"); - let input = files - .get(&entry) - .unwrap_or_else(|| panic!("module fixture {} must contain an entry.js", dir.display())) - .clone(); + if !files.contains_key(&entry) { + panic!("module fixture {} must contain an entry.js", dir.display()); + } + + let markers = files.values().cloned().collect::>().join("\n"); let result = tree_shake( JsShakerOptions { + config: config_for(&markers), + minify_options: minify_options(&markers), vfs: MultiModuleFs(files), - config: config_for(&input), - minify_options: minify_options(&input), codegen_options: codegen_options(), source_map: false, }, entry.clone(), ); - // Entry first, then the remaining modules alphabetically, so the snapshot is - // stable regardless of hash ordering. let mut modules: Vec<_> = result.codegen_return.iter().collect(); modules.sort_by(|(a, _), (b, _)| (**a != entry, *a).cmp(&(**b != entry, *b))); let mut output = String::new(); for (path, codegen) in modules { - // Normalize separators so snapshots match on Windows and Unix alike. output.push_str(&format!("//#region {}\n", path.replace('\\', "/"))); output.push_str(codegen.code.trim_end()); output.push_str("\n\n"); diff --git a/crates/jsshaker/tests/module_fixtures/async_sequential/entry.js b/crates/jsshaker/tests/module_fixtures/async_sequential/entry.js index 36b3d97c..414732dd 100644 --- a/crates/jsshaker/tests/module_fixtures/async_sequential/entry.js +++ b/crates/jsshaker/tests/module_fixtures/async_sequential/entry.js @@ -1,3 +1,6 @@ -import { order, run } from "./m.js"; +import { order, mark, run } from "./m.js"; + +Promise.resolve().then(() => mark("X")); +Promise.resolve().then(() => mark("Y")); await run(); console.log(order); diff --git a/crates/jsshaker/tests/module_fixtures/async_sequential/m.js b/crates/jsshaker/tests/module_fixtures/async_sequential/m.js index ed202476..773b5464 100644 --- a/crates/jsshaker/tests/module_fixtures/async_sequential/m.js +++ b/crates/jsshaker/tests/module_fixtures/async_sequential/m.js @@ -1,4 +1,7 @@ export let order = ""; +export function mark(s) { + order += s; +} export async function run() { order += "a"; await null; diff --git a/crates/jsshaker/tests/module_fixtures/cls_super_static/entry.js b/crates/jsshaker/tests/module_fixtures/cls_super_static/entry.js new file mode 100644 index 00000000..5c55cc64 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cls_super_static/entry.js @@ -0,0 +1,2 @@ +import { C } from "./m.js"; +console.log(C.make()); diff --git a/crates/jsshaker/tests/module_fixtures/cls_super_static/m.js b/crates/jsshaker/tests/module_fixtures/cls_super_static/m.js new file mode 100644 index 00000000..6f8ce7fc --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cls_super_static/m.js @@ -0,0 +1,10 @@ +export class B { + static who() { + return "B"; + } +} +export class C extends B { + static make() { + return super.who(); + } +} diff --git a/crates/jsshaker/tests/module_fixtures/mod_dynamic_import/entry.js b/crates/jsshaker/tests/module_fixtures/mod_dynamic_import/entry.js new file mode 100644 index 00000000..3e75e3c6 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_dynamic_import/entry.js @@ -0,0 +1,2 @@ +import("./side.js"); +export const x = 1; diff --git a/crates/jsshaker/tests/module_fixtures/mod_dynamic_import/side.js b/crates/jsshaker/tests/module_fixtures/mod_dynamic_import/side.js new file mode 100644 index 00000000..f7b13881 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_dynamic_import/side.js @@ -0,0 +1,2 @@ +console.log("side"); +export const y = 2; diff --git a/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/entry.js b/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/entry.js new file mode 100644 index 00000000..9b1c6267 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/entry.js @@ -0,0 +1,3 @@ +const m = await import("./used.js"); +console.log(m.v); +export const x = 1; diff --git a/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/used.js b/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/used.js new file mode 100644 index 00000000..c45d44ba --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/used.js @@ -0,0 +1,2 @@ +console.log("used"); +export const v = 3; diff --git a/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/entry.js b/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/entry.js new file mode 100644 index 00000000..6b1086cc --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/entry.js @@ -0,0 +1,3 @@ +import * as ns from "./mid.js"; +import { a } from "./mid.js"; +console.log(ns.default, a); diff --git a/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/mid.js b/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/mid.js new file mode 100644 index 00000000..38b60057 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/mid.js @@ -0,0 +1 @@ +export * from "./src.js"; diff --git a/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/src.js b/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/src.js new file mode 100644 index 00000000..f90484d1 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/src.js @@ -0,0 +1,2 @@ +export default 41; +export const a = 1; diff --git a/crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/a.js b/crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/a.js new file mode 100644 index 00000000..a98c49ea --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/a.js @@ -0,0 +1,2 @@ +export { x } from "./b.js"; +export const y = 1; diff --git a/crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/b.js b/crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/b.js new file mode 100644 index 00000000..e5c11ef0 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/b.js @@ -0,0 +1 @@ +export { x } from "./a.js"; diff --git a/crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/entry.js b/crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/entry.js new file mode 100644 index 00000000..5233a3ca --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/entry.js @@ -0,0 +1,2 @@ +import { x, y } from "./a.js"; +console.log(x, y); diff --git a/crates/jsshaker/tests/snapshots/module_test@async_sequential__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@async_sequential__entry.js.snap index 2cda2afe..da9ed58e 100644 --- a/crates/jsshaker/tests/snapshots/module_test@async_sequential__entry.js.snap +++ b/crates/jsshaker/tests/snapshots/module_test@async_sequential__entry.js.snap @@ -4,14 +4,21 @@ expression: do_tree_shake_modules(dir) input_file: crates/jsshaker/tests/module_fixtures/async_sequential/entry.js --- //#region /entry.js -import { order, run } from "./m.js"; +import { order, mark, run } from "./m.js"; +Promise.resolve().then(() => (mark("X"), void 0)); +Promise.resolve().then(() => (mark("Y"), void 0)); await run(); console.log(order); //#region /m.js export let order = ""; +export function mark(s) { + order += s; +} export async function run() { order += "a"; + await null; order += "b"; + await null; order += "c"; } diff --git a/crates/jsshaker/tests/snapshots/module_test@cls_super_static__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cls_super_static__entry.js.snap new file mode 100644 index 00000000..fc91ea3e --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cls_super_static__entry.js.snap @@ -0,0 +1,10 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cls_super_static/entry.js +--- +//#region /entry.js +import {} from "./m.js"; +console.log("B"); + +//#region /m.js diff --git a/crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import__entry.js.snap new file mode 100644 index 00000000..3b52dcea --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import__entry.js.snap @@ -0,0 +1,11 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/mod_dynamic_import/entry.js +--- +//#region /entry.js +import("./side.js"); +export const x = 1; + +//#region /side.js +console.log("side"); diff --git a/crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import_used__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import_used__entry.js.snap new file mode 100644 index 00000000..ff4d6494 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import_used__entry.js.snap @@ -0,0 +1,13 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/entry.js +--- +//#region /entry.js +const m = await import("./used.js"); +console.log(m.v); +export const x = 1; + +//#region /used.js +console.log("used"); +export const v = 3; diff --git a/crates/jsshaker/tests/snapshots/module_test@mod_export_star_no_default__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@mod_export_star_no_default__entry.js.snap new file mode 100644 index 00000000..026c47bf --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@mod_export_star_no_default__entry.js.snap @@ -0,0 +1,15 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/entry.js +--- +//#region /entry.js +import {} from "./mid.js"; +import {} from "./mid.js"; +console.log(void 0, 1); + +//#region /mid.js +export * from "./src.js"; + +//#region /src.js +export default 41; diff --git a/crates/jsshaker/tests/snapshots/module_test@mod_reexport_cycle__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@mod_reexport_cycle__entry.js.snap new file mode 100644 index 00000000..7fc588b7 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@mod_reexport_cycle__entry.js.snap @@ -0,0 +1,14 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/mod_reexport_cycle/entry.js +--- +//#region /entry.js +import { x } from "./a.js"; +console.log(x, 1); + +//#region /a.js +export { x } from "./b.js"; + +//#region /b.js +export { x } from "./a.js"; diff --git a/crates/jsshaker/tests/snapshots/test@assignment_expression.js.snap b/crates/jsshaker/tests/snapshots/test@assignment_expression.js.snap index 3646e46b..d457c5d6 100644 --- a/crates/jsshaker/tests/snapshots/test@assignment_expression.js.snap +++ b/crates/jsshaker/tests/snapshots/test@assignment_expression.js.snap @@ -23,8 +23,6 @@ export function f5(a, b) { export function f6(a, b) { a.p = b; global.p = 1; - effect; - return a; } export function f7(a) { ({x: __unused__, a: __unused__, b: {c: __unused__}, ...__unused__} = a); diff --git a/crates/jsshaker/tests/snapshots/test@class__extends.js.snap b/crates/jsshaker/tests/snapshots/test@class__extends.js.snap index c1c581b6..863e29ca 100644 --- a/crates/jsshaker/tests/snapshots/test@class__extends.js.snap +++ b/crates/jsshaker/tests/snapshots/test@class__extends.js.snap @@ -11,5 +11,5 @@ class Derived extends Base { } const a = new Derived(); console.log("Base1Derived"); -console.log(1, 2, 4); +console.log(1, 3, 4); a.a(); diff --git a/crates/jsshaker/tests/snapshots/test@class__super_static.js.snap b/crates/jsshaker/tests/snapshots/test@class__super_static.js.snap new file mode 100644 index 00000000..45c8517c --- /dev/null +++ b/crates/jsshaker/tests/snapshots/test@class__super_static.js.snap @@ -0,0 +1,7 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake(input) +input_file: crates/jsshaker/tests/fixtures/class/super_static.js +--- +console.log("static static-child"); +console.log("instance"); diff --git a/crates/jsshaker/tests/snapshots/test@class__traditional.js.snap b/crates/jsshaker/tests/snapshots/test@class__traditional.js.snap index 7134c395..6bbd5b28 100644 --- a/crates/jsshaker/tests/snapshots/test@class__traditional.js.snap +++ b/crates/jsshaker/tests/snapshots/test@class__traditional.js.snap @@ -10,6 +10,6 @@ A.a = function() { A.prototype.a = function() { return unknown(); }; -console.log(2, 10, 10, 21); +console.log(2, 10, 12, 21); const b = A.a(); b.a(); diff --git a/crates/jsshaker/tests/snapshots/test@define_property.js.snap b/crates/jsshaker/tests/snapshots/test@define_property.js.snap new file mode 100644 index 00000000..279d1e7c --- /dev/null +++ b/crates/jsshaker/tests/snapshots/test@define_property.js.snap @@ -0,0 +1,26 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake(input) +input_file: crates/jsshaker/tests/fixtures/define_property.js +--- +const o = {}; +let backing; +Object.defineProperty(o, "p", { + get() { + return backing * 2; + }, + set(v) { + backing = v + 1; + }, + enumerable: true, + configurable: true +}); +o.p = 4; +console.log(10, 5, JSON.stringify(Object.keys(o))); +const ro = { z: 1 }; +Object.defineProperty(ro, "hidden", { get() { + return "H"; +} }); +console.log("H", JSON.stringify(Object.keys(ro)), JSON.stringify(ro)); +const d = Object.getOwnPropertyDescriptor(o, "p"); +console.log(typeof d.get, typeof d.set, d.enumerable, d.configurable); diff --git a/crates/jsshaker/tests/snapshots/test@import_expression.js.snap b/crates/jsshaker/tests/snapshots/test@import_expression.js.snap index ef4f56e8..efbc7c56 100644 --- a/crates/jsshaker/tests/snapshots/test@import_expression.js.snap +++ b/crates/jsshaker/tests/snapshots/test@import_expression.js.snap @@ -1,9 +1,10 @@ --- -source: src/tests/mod.rs -expression: tree_shake(input) -input_file: src/tests/fixtures/import_expression.js +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake(input) +input_file: crates/jsshaker/tests/fixtures/import_expression.js --- export function main() { - effect(1), effect(2); + import("a"); + import(effect(1) + "b", { c: effect(2) }); effect(import("c", effect(3))); } From 0d66cba0fa5b5f3b8c1a10023768d7e64a4fc1d9 Mon Sep 17 00:00:00 2001 From: Mathi <11136549+Mathicha@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:33:09 +0200 Subject: [PATCH 4/6] simplify some stuff --- crates/jsshaker/src/analyzer/conditional.rs | 8 +- .../src/nodes/expr/chain_expression.rs | 20 +++-- .../src/nodes/expr/super_expression.rs | 8 +- crates/jsshaker/src/nodes/misc/callee.rs | 2 +- crates/jsshaker/src/nodes/misc/class.rs | 20 ++--- crates/jsshaker/src/nodes/misc/function.rs | 3 +- .../jsshaker/src/nodes/stmt/if_statement.rs | 11 +-- crates/jsshaker/src/scope/mod.rs | 2 +- crates/jsshaker/src/scope/variable_scope.rs | 22 +++++- crates/jsshaker/src/value/escaped.rs | 13 ++++ crates/jsshaker/src/value/literal/mod.rs | 12 ++- crates/jsshaker/src/value/literal/string.rs | 11 ++- crates/jsshaker/src/value/primitive.rs | 11 ++- .../tests/fixtures/chain_expression.js | 6 ++ crates/jsshaker/tests/mod.rs | 16 ++-- .../snapshots/test@chain_expression.js.snap | 10 ++- packages/napi/src/lib.rs | 73 +++++++++++++------ 17 files changed, 167 insertions(+), 81 deletions(-) diff --git a/crates/jsshaker/src/analyzer/conditional.rs b/crates/jsshaker/src/analyzer/conditional.rs index f7fceac7..91f1d82b 100644 --- a/crates/jsshaker/src/analyzer/conditional.rs +++ b/crates/jsshaker/src/analyzer/conditional.rs @@ -200,11 +200,11 @@ impl<'a> Analyzer<'a> { } pub fn note_conditional_branch_exit(&mut self, id: impl Into, dep: Option>) { - let Some(dep) = dep.filter(|_| self.cf_scope().must_exited()) else { + let Some(dep) = dep else { return }; + if !self.cf_scope().must_exited() { return; - }; - let id = id.into(); - if let Some(data) = self.conditional_data.node_to_data.get_mut(&id) { + } + if let Some(data) = self.conditional_data.node_to_data.get_mut(&id.into()) { data.exiting_branches.push(dep); } } diff --git a/crates/jsshaker/src/nodes/expr/chain_expression.rs b/crates/jsshaker/src/nodes/expr/chain_expression.rs index a9352780..29e09f2f 100644 --- a/crates/jsshaker/src/nodes/expr/chain_expression.rs +++ b/crates/jsshaker/src/nodes/expr/chain_expression.rs @@ -1,9 +1,12 @@ -use oxc::ast::{ - ast::{ChainElement, ChainExpression, Expression}, - match_member_expression, +use oxc::{ + ast::{ + ast::{ChainElement, ChainExpression, Expression}, + match_member_expression, + }, + span::GetSpan, }; -use crate::{analyzer::Analyzer, entity::Entity, transformer::Transformer}; +use crate::{analyzer::Analyzer, build_effect, entity::Entity, transformer::Transformer}; impl<'a> Analyzer<'a> { pub fn exec_chain_expression(&mut self, node: &'a ChainExpression<'a>) -> Entity<'a> { @@ -57,7 +60,14 @@ impl<'a> Transformer<'a> { ChainElement::CallExpression(node) => self.transform_call_expression_in_chain(node, need_val), node => self.transform_member_expression_read_in_chain(node.to_member_expression(), need_val), } - .unwrap_or_else(|v| v) + .unwrap_or_else(|effect| { + if need_val { + let span = node.span(); + Some(build_effect!(&self.ast, span, effect; self.build_undefined(span))) + } else { + effect + } + }) } pub fn transform_expression_in_chain( diff --git a/crates/jsshaker/src/nodes/expr/super_expression.rs b/crates/jsshaker/src/nodes/expr/super_expression.rs index d5e90c6b..0c333d3a 100644 --- a/crates/jsshaker/src/nodes/expr/super_expression.rs +++ b/crates/jsshaker/src/nodes/expr/super_expression.rs @@ -5,11 +5,11 @@ use crate::{analyzer::Analyzer, builtin_string, entity::Entity, transformer::Tra impl<'a> Analyzer<'a> { pub fn exec_super(&mut self, _node: &'a Super) -> Entity<'a> { // Should only be called in member expression - let (super_class, is_static) = self.get_super(); - if is_static { - super_class + let super_scope = self.get_super(); + if super_scope.is_static { + super_scope.class } else { - super_class.get_property(self, self.factory.no_dep, builtin_string!("prototype")) + super_scope.class.get_property(self, self.factory.no_dep, builtin_string!("prototype")) } } } diff --git a/crates/jsshaker/src/nodes/misc/callee.rs b/crates/jsshaker/src/nodes/misc/callee.rs index 63cd5fe7..99e85f96 100644 --- a/crates/jsshaker/src/nodes/misc/callee.rs +++ b/crates/jsshaker/src/nodes/misc/callee.rs @@ -34,7 +34,7 @@ impl<'a> Analyzer<'a> { node: &'a Expression<'a>, ) -> Result<(usize, Entity<'a>, Option>, Entity<'a>), Entity<'a>> { if matches!(node, Expression::Super(_)) { - return Ok((0, self.get_super().0, None, self.get_this())); + return Ok((0, self.get_super().class, None, self.get_this())); } let dep = AstKind2::Callee(node); diff --git a/crates/jsshaker/src/nodes/misc/class.rs b/crates/jsshaker/src/nodes/misc/class.rs index 37be2c5b..5b1a5e97 100644 --- a/crates/jsshaker/src/nodes/misc/class.rs +++ b/crates/jsshaker/src/nodes/misc/class.rs @@ -17,6 +17,7 @@ use crate::{ ast::{AstKind2, DeclarationKind}, dep::DepAtom, entity::Entity, + scope::SuperScope, transformer::Transformer, utils::{CalleeNode, ClassData}, value::{ObjectPrototype, cache::FnCacheTrackingData, call::FnCallInfo}, @@ -59,9 +60,10 @@ impl<'a> Analyzer<'a> { prototype.set_prototype(ObjectPrototype::ImplicitOrNull); }; + let super_class = data.super_class.unwrap_or(self.factory.undefined); + // Enter class statics scope - self.variable_scope_mut().super_class = - Some((data.super_class.unwrap_or(self.factory.undefined), true)); + self.variable_scope_mut().super_class = Some(SuperScope::statics(super_class)); self.variable_scope_mut().this = Some(class.into()); // 2. Execute keys and find constructor @@ -95,8 +97,7 @@ impl<'a> Analyzer<'a> { class.statics.init_property(self, kind, key.unwrap(), value, true); } else { self.push_variable_scope(); - self.variable_scope_mut().super_class = - Some((data.super_class.unwrap_or(self.factory.undefined), false)); + self.variable_scope_mut().super_class = Some(SuperScope::instance(super_class)); let value = self.exec_function(&node.value); self.pop_variable_scope(); prototype.init_property(self, kind, key.unwrap(), value, true); @@ -228,7 +229,7 @@ impl<'a> Analyzer<'a> { variable_scope.this = Some(info.this); variable_scope.arguments = Some((info.args, factory.vec(/* later filled by formal parameters */))); - variable_scope.super_class = Some((super_class, false)); + variable_scope.super_class = Some(SuperScope::instance(super_class)); if let Some(id) = &node.id { self.declare_binding_identifier(id, None, DeclarationKind::NamedFunctionInBody); @@ -240,15 +241,14 @@ impl<'a> Analyzer<'a> { } // 1. Init properties, deferred past `super()` when derived - if data.super_class.is_none() { + if data.super_class.is_some() { + self.call_scope_mut().pending_instance_fields = Some((node, data_cell, info.this)); + } else { self.init_instance_fields(node, &data, info.this); } // 2. Call constructor if let Some(constructor) = data.constructor { - if data.super_class.is_some() { - self.call_scope_mut().pending_instance_fields = Some((node, data_cell, info.this)); - } let function = constructor.value.as_ref(); let dep = self.factory.dep(AstKind2::Function(function)); self.cf_scope_mut().push_dep(dep); @@ -264,7 +264,7 @@ impl<'a> Analyzer<'a> { } else if let Some(super_class) = &data.super_class { let super_class = *super_class; let ret_val = super_class.call(self, self.factory.no_dep, info.this, info.args); - self.init_instance_fields(node, &data, info.this); + self.init_pending_instance_fields(); self.pop_call_scope(); (ret_val, FnCacheTrackingData::worst_case()) } else { diff --git a/crates/jsshaker/src/nodes/misc/function.rs b/crates/jsshaker/src/nodes/misc/function.rs index 4742b163..14cc0c0d 100644 --- a/crates/jsshaker/src/nodes/misc/function.rs +++ b/crates/jsshaker/src/nodes/misc/function.rs @@ -11,6 +11,7 @@ use crate::{ ast::{AstKind2, DeclarationKind}, dep::DepAtom, entity::Entity, + scope::SuperScope, transformer::Transformer, utils::CalleeNode, value::{cache::FnCacheTrackingData, call::FnCallInfo}, @@ -55,7 +56,7 @@ impl<'a> Analyzer<'a> { let factory = analyzer.factory; let variable_scope = analyzer.variable_scope_mut(); variable_scope.this = Some(info.this); - variable_scope.super_class = info.include.then_some((factory.unknown, false)); + variable_scope.super_class = info.include.then(|| SuperScope::instance(factory.unknown)); variable_scope.arguments = Some((info.args, factory.vec(/* later filled by formal parameters */))); diff --git a/crates/jsshaker/src/nodes/stmt/if_statement.rs b/crates/jsshaker/src/nodes/stmt/if_statement.rs index b7d13061..2db79c1e 100644 --- a/crates/jsshaker/src/nodes/stmt/if_statement.rs +++ b/crates/jsshaker/src/nodes/stmt/if_statement.rs @@ -8,6 +8,7 @@ use crate::{analyzer::Analyzer, ast::AstKind2, scope::CfScopeKind, transformer:: impl<'a> Analyzer<'a> { pub fn exec_if_statement(&mut self, node: &'a IfStatement) { let factory = self.factory; + let id = AstKind2::IfStatement(node); let test = self.exec_expression_no_fold(&node.test).coerce_primitive(self); @@ -25,7 +26,7 @@ impl<'a> Analyzer<'a> { if maybe_consequent { let branch_dep = self.push_if_like_branch_cf_scope( - AstKind2::IfStatement(node), + id, CfScopeKind::ExitBlocker(None), test, maybe_consequent, @@ -34,7 +35,7 @@ impl<'a> Analyzer<'a> { node.alternate.is_some(), ); self.exec_statement(&node.consequent); - self.note_conditional_branch_exit(AstKind2::IfStatement(node), branch_dep); + self.note_conditional_branch_exit(id, branch_dep); let mut conditional_scope = self.pop_cf_scope(); if let CfScopeKind::ExitBlocker(Some(stopped_exit)) = &conditional_scope.kind { exit_target_inner = exit_target_inner.max(*stopped_exit); @@ -46,7 +47,7 @@ impl<'a> Analyzer<'a> { } if maybe_alternate { let branch_dep = self.push_if_like_branch_cf_scope( - AstKind2::IfStatement(node), + id, CfScopeKind::ExitBlocker(None), test, maybe_consequent, @@ -56,7 +57,7 @@ impl<'a> Analyzer<'a> { ); if let Some(alternate) = &node.alternate { self.exec_statement(alternate); - self.note_conditional_branch_exit(AstKind2::IfStatement(node), branch_dep); + self.note_conditional_branch_exit(id, branch_dep); let mut conditional_scope = self.pop_cf_scope(); if let CfScopeKind::ExitBlocker(Some(stopped_exit)) = &conditional_scope.kind { exit_target_inner = exit_target_inner.max(*stopped_exit); @@ -83,7 +84,7 @@ impl<'a> Analyzer<'a> { } if self.config.branch_folding && !self.cf_scope().must_exited() { - let link = self.conditional_exit_link(AstKind2::IfStatement(node)); + let link = self.conditional_exit_link(id); self.cf_scope_mut().push_dep(link); } } diff --git a/crates/jsshaker/src/scope/mod.rs b/crates/jsshaker/src/scope/mod.rs index f53844e6..61d2d940 100644 --- a/crates/jsshaker/src/scope/mod.rs +++ b/crates/jsshaker/src/scope/mod.rs @@ -11,7 +11,7 @@ use cf_scope::CfScope; pub use cf_scope::{CfScopeId, CfScopeKind, CfScopeVer}; use stacked_tree::StackedTree; use variable_scope::VariableScope; -pub use variable_scope::VariableScopeId; +pub use variable_scope::{SuperScope, VariableScopeId}; use crate::{ analyzer::{Analyzer, Factory}, diff --git a/crates/jsshaker/src/scope/variable_scope.rs b/crates/jsshaker/src/scope/variable_scope.rs index 8eb302d8..e5f566ff 100644 --- a/crates/jsshaker/src/scope/variable_scope.rs +++ b/crates/jsshaker/src/scope/variable_scope.rs @@ -37,11 +37,27 @@ pub struct Variable<'a> { pub decl_node: AstKind2<'a>, } +#[derive(Debug, Clone, Copy)] +pub struct SuperScope<'a> { + pub class: Entity<'a>, + pub is_static: bool, +} + +impl<'a> SuperScope<'a> { + pub fn statics(class: Entity<'a>) -> Self { + SuperScope { class, is_static: true } + } + + pub fn instance(class: Entity<'a>) -> Self { + SuperScope { class, is_static: false } + } +} + pub struct VariableScope<'a> { pub variables: allocator::HashMap<'a, SymbolId, &'a RefCell>>, pub this: Option>, pub arguments: Option<(ArgumentsValue<'a>, allocator::Vec<'a, SymbolId>)>, - pub super_class: Option<(Entity<'a>, bool)>, + pub super_class: Option>, } impl fmt::Debug for VariableScope<'_> { @@ -464,12 +480,12 @@ impl<'a> Analyzer<'a> { unreachable!() } - pub fn get_super(&mut self) -> (Entity<'a>, bool) { + pub fn get_super(&mut self) -> SuperScope<'a> { for scope in self.scoping.variable.iter_rev() { if let Some(super_class) = scope.super_class { return super_class; } } - (self.factory.unknown, false) + SuperScope::instance(self.factory.unknown) } } diff --git a/crates/jsshaker/src/value/escaped.rs b/crates/jsshaker/src/value/escaped.rs index 6686cd79..2818b91b 100644 --- a/crates/jsshaker/src/value/escaped.rs +++ b/crates/jsshaker/src/value/escaped.rs @@ -31,6 +31,19 @@ pub fn set_property<'a>( analyzer.include((dep, key, value)); } +pub fn set_property_on_primitive<'a>( + analyzer: &mut Analyzer<'a>, + message: impl Into, + dep: Dep<'a>, + key: Entity<'a>, + value: Entity<'a>, +) { + analyzer.throw_builtin_error(message); + if analyzer.config.preserve_exceptions { + set_property(analyzer, dep, key, value); + } +} + pub fn enumerate_properties<'a>( target: Value<'a>, analyzer: &mut Analyzer<'a>, diff --git a/crates/jsshaker/src/value/literal/mod.rs b/crates/jsshaker/src/value/literal/mod.rs index 2c7066cc..3088953b 100644 --- a/crates/jsshaker/src/value/literal/mod.rs +++ b/crates/jsshaker/src/value/literal/mod.rs @@ -88,14 +88,12 @@ impl<'a> ValueTrait<'a> for LiteralValue<'a> { key: Entity<'a>, value: Entity<'a>, ) { - if matches!(self, LiteralValue::Null | LiteralValue::Undefined) { - analyzer.throw_builtin_error("Cannot set property of null or undefined"); + let message = if matches!(self, LiteralValue::Null | LiteralValue::Undefined) { + "Cannot set property of null or undefined" } else { - analyzer.throw_builtin_error("Cannot create property on a primitive value"); - } - if analyzer.config.preserve_exceptions { - escaped::set_property(analyzer, dep, key, value) - } + "Cannot create property on a primitive value" + }; + escaped::set_property_on_primitive(analyzer, message, dep, key, value); } fn enumerate_properties( diff --git a/crates/jsshaker/src/value/literal/string.rs b/crates/jsshaker/src/value/literal/string.rs index f1d74901..a6a4c601 100644 --- a/crates/jsshaker/src/value/literal/string.rs +++ b/crates/jsshaker/src/value/literal/string.rs @@ -77,10 +77,13 @@ impl<'a> ValueTrait<'a> for Atom<'a> { key: Entity<'a>, value: Entity<'a>, ) { - analyzer.throw_builtin_error("Cannot create property on a primitive value"); - if analyzer.config.preserve_exceptions { - escaped::set_property(analyzer, dep, key, value) - } + escaped::set_property_on_primitive( + analyzer, + "Cannot create property on a primitive value", + dep, + key, + value, + ); } fn enumerate_properties( diff --git a/crates/jsshaker/src/value/primitive.rs b/crates/jsshaker/src/value/primitive.rs index 507a9cc9..52563f23 100644 --- a/crates/jsshaker/src/value/primitive.rs +++ b/crates/jsshaker/src/value/primitive.rs @@ -51,10 +51,13 @@ impl<'a> ValueTrait<'a> for PrimitiveValue { key: Entity<'a>, value: Entity<'a>, ) { - analyzer.throw_builtin_error("Cannot create property on a primitive value"); - if analyzer.config.preserve_exceptions { - escaped::set_property(analyzer, dep, key, value) - } + escaped::set_property_on_primitive( + analyzer, + "Cannot create property on a primitive value", + dep, + key, + value, + ); } fn enumerate_properties( diff --git a/crates/jsshaker/tests/fixtures/chain_expression.js b/crates/jsshaker/tests/fixtures/chain_expression.js index e639e52e..a422d0df 100644 --- a/crates/jsshaker/tests/fixtures/chain_expression.js +++ b/crates/jsshaker/tests/fixtures/chain_expression.js @@ -4,3 +4,9 @@ export function main(unknown) { let a = { b: unknown }; effect(a?.b?.c, a.c?.d, a.c?.() + 1); } + +export async function short_circuit(effectful) { + const nullish = undefined; + await nullish?.a; + await (effectful(), nullish)?.b; +} diff --git a/crates/jsshaker/tests/mod.rs b/crates/jsshaker/tests/mod.rs index d6fb2647..ad4bf8b6 100644 --- a/crates/jsshaker/tests/mod.rs +++ b/crates/jsshaker/tests/mod.rs @@ -29,11 +29,13 @@ fn codegen_options() -> CodegenOptions { } fn do_tree_shake(input: String) -> String { + let config = config_for(&input); + let minify_options = minify_options(&input); let result = tree_shake( JsShakerOptions { - vfs: SingleFileFs(input.clone()), - config: config_for(&input), - minify_options: minify_options(&input), + vfs: SingleFileFs(input), + config, + minify_options, codegen_options: codegen_options(), source_map: false, }, @@ -74,11 +76,9 @@ fn do_tree_shake_modules(dir: &Path) -> String { collect_modules(dir, "", &mut files); let entry = normalize_str("/entry.js"); - if !files.contains_key(&entry) { - panic!("module fixture {} must contain an entry.js", dir.display()); - } + assert!(files.contains_key(&entry), "module fixture {} must contain an entry.js", dir.display()); - let markers = files.values().cloned().collect::>().join("\n"); + let markers = files.values().map(String::as_str).collect::>().join("\n"); let result = tree_shake( JsShakerOptions { @@ -92,7 +92,7 @@ fn do_tree_shake_modules(dir: &Path) -> String { ); let mut modules: Vec<_> = result.codegen_return.iter().collect(); - modules.sort_by(|(a, _), (b, _)| (**a != entry, *a).cmp(&(**b != entry, *b))); + modules.sort_by_key(|(path, _)| (**path != entry, *path)); let mut output = String::new(); for (path, codegen) in modules { diff --git a/crates/jsshaker/tests/snapshots/test@chain_expression.js.snap b/crates/jsshaker/tests/snapshots/test@chain_expression.js.snap index fa4a352a..99c6cf47 100644 --- a/crates/jsshaker/tests/snapshots/test@chain_expression.js.snap +++ b/crates/jsshaker/tests/snapshots/test@chain_expression.js.snap @@ -1,10 +1,14 @@ --- -source: src/tests/mod.rs -expression: tree_shake(input) -input_file: src/tests/fixtures/chain_expression.js +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake(input) +input_file: crates/jsshaker/tests/fixtures/chain_expression.js --- export function main(unknown) { g?.h.i?.j?.(o?.p)?.q.r; let a = { a: unknown }; effect(a.a?.c, void 0, NaN); } +export async function short_circuit(effectful) { + await undefined; + await (effectful(), undefined); +} diff --git a/packages/napi/src/lib.rs b/packages/napi/src/lib.rs index b6253c00..787d7ff2 100644 --- a/packages/napi/src/lib.rs +++ b/packages/napi/src/lib.rs @@ -14,7 +14,7 @@ pub struct Options { #[napi(ts_type = "'safest' | 'recommended' | 'smallest' | 'disabled'")] pub preset: Option, pub minify: Option, - #[napi(ts_type = "'react'")] + #[napi(ts_type = "'none' | 'react'")] pub jsx: Option, pub source_map: Option, @@ -24,6 +24,7 @@ pub struct Options { #[napi(ts_type = "'enabled' | 'disabled' | 'only'")] pub property_mangling: Option, pub branch_folding: Option, + pub max_folding_string_length: Option, pub max_recursion_depth: Option, pub remember_exhausted_variables: Option, @@ -31,6 +32,17 @@ pub struct Options { pub enable_fn_cache: Option, pub enable_fn_stats: Option, pub enable_mangling_stats: Option, + + pub unknown_global_side_effects: Option, + pub preserve_function_name: Option, + pub preserve_function_length: Option, + pub iterate_side_effects: Option, + pub unknown_property_read_side_effects: Option, + pub unmatched_prototype_property_as_undefined: Option, + pub preserve_exceptions: Option, + pub preserve_property_attributes: Option, + pub impure_json_stringify: Option, + pub precise_dynamic_prototype: Option, } #[napi(object)] @@ -80,6 +92,16 @@ impl From for Chunk { } } +macro_rules! apply_bool_options { + ($config:ident, $options:ident, $($field:ident),+ $(,)?) => { + $( + if let Some(value) = $options.$field { + $config.$field = value; + } + )+ + }; +} + fn resolve_options(vfs: F, options: Options) -> JsShakerOptions { let preset = options.preset.as_deref().unwrap_or("recommended"); @@ -90,8 +112,12 @@ fn resolve_options(vfs: F, options: Options) -> JsShakerOptions { "disabled" => TreeShakeConfig::disabled(), _ => panic!("Invalid tree shake option {:?}", preset), }; - if options.jsx.as_deref() == Some("react") { - config.jsx = TreeShakeJsxPreset::React; + if let Some(jsx) = options.jsx.as_deref() { + config.jsx = match jsx { + "none" => TreeShakeJsxPreset::None, + "react" => TreeShakeJsxPreset::React, + _ => panic!("Invalid jsx option {:?}", jsx), + }; } config.advanced = options.advanced.unwrap_or(false); @@ -111,28 +137,33 @@ fn resolve_options(vfs: F, options: Options) -> JsShakerOptions { _ => panic!("Invalid property_mangling option {:?}", property_mangling), }; } - if let Some(branch_folding) = options.branch_folding { - config.branch_folding = branch_folding; + if let Some(length) = options.max_folding_string_length { + config.max_folding_string_length = length as usize; } - if let Some(depth) = options.max_recursion_depth { config.max_recursion_depth = depth as usize; } - if let Some(remember) = options.remember_exhausted_variables { - config.remember_exhausted_variables = remember; - } - if let Some(eager) = options.eager_exhaustive_callbacks { - config.eager_exhaustive_callbacks = eager; - } - if let Some(enable) = options.enable_fn_cache { - config.enable_fn_cache = enable; - } - if let Some(enable) = options.enable_fn_stats { - config.enable_fn_stats = enable; - } - if let Some(enable) = options.enable_mangling_stats { - config.enable_mangling_stats = enable; - } + + apply_bool_options!( + config, + options, + branch_folding, + remember_exhausted_variables, + eager_exhaustive_callbacks, + enable_fn_cache, + enable_fn_stats, + enable_mangling_stats, + unknown_global_side_effects, + preserve_function_name, + preserve_function_length, + iterate_side_effects, + unknown_property_read_side_effects, + unmatched_prototype_property_as_undefined, + preserve_exceptions, + preserve_property_attributes, + impure_json_stringify, + precise_dynamic_prototype, + ); let minify = options.minify.unwrap_or(false); let minify_options = From 29c7a13bf687da0a57166645b00b215668aad736 Mon Sep 17 00:00:00 2001 From: Mathi <11136549+Mathicha@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:48:37 +0200 Subject: [PATCH 5/6] remove fixtures that were good before all my changes --- .../mod_dynamic_import_used/entry.js | 3 --- .../mod_dynamic_import_used/used.js | 2 -- .../mod_export_star_no_default/entry.js | 3 --- .../mod_export_star_no_default/mid.js | 1 - .../mod_export_star_no_default/src.js | 2 -- ...le_test@mod_dynamic_import_used__entry.js.snap | 13 ------------- ...test@mod_export_star_no_default__entry.js.snap | 15 --------------- 7 files changed, 39 deletions(-) delete mode 100644 crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/used.js delete mode 100644 crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/mid.js delete mode 100644 crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/src.js delete mode 100644 crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import_used__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@mod_export_star_no_default__entry.js.snap diff --git a/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/entry.js b/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/entry.js deleted file mode 100644 index 9b1c6267..00000000 --- a/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -const m = await import("./used.js"); -console.log(m.v); -export const x = 1; diff --git a/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/used.js b/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/used.js deleted file mode 100644 index c45d44ba..00000000 --- a/crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/used.js +++ /dev/null @@ -1,2 +0,0 @@ -console.log("used"); -export const v = 3; diff --git a/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/entry.js b/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/entry.js deleted file mode 100644 index 6b1086cc..00000000 --- a/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import * as ns from "./mid.js"; -import { a } from "./mid.js"; -console.log(ns.default, a); diff --git a/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/mid.js b/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/mid.js deleted file mode 100644 index 38b60057..00000000 --- a/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/mid.js +++ /dev/null @@ -1 +0,0 @@ -export * from "./src.js"; diff --git a/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/src.js b/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/src.js deleted file mode 100644 index f90484d1..00000000 --- a/crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/src.js +++ /dev/null @@ -1,2 +0,0 @@ -export default 41; -export const a = 1; diff --git a/crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import_used__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import_used__entry.js.snap deleted file mode 100644 index ff4d6494..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@mod_dynamic_import_used__entry.js.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/mod_dynamic_import_used/entry.js ---- -//#region /entry.js -const m = await import("./used.js"); -console.log(m.v); -export const x = 1; - -//#region /used.js -console.log("used"); -export const v = 3; diff --git a/crates/jsshaker/tests/snapshots/module_test@mod_export_star_no_default__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@mod_export_star_no_default__entry.js.snap deleted file mode 100644 index 026c47bf..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@mod_export_star_no_default__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/mod_export_star_no_default/entry.js ---- -//#region /entry.js -import {} from "./mid.js"; -import {} from "./mid.js"; -console.log(void 0, 1); - -//#region /mid.js -export * from "./src.js"; - -//#region /src.js -export default 41; From 2560add8b37775c6ad5e5204fdedc149d76d74e5 Mon Sep 17 00:00:00 2001 From: Mathi <11136549+Mathicha@users.noreply.github.com> Date: Wed, 26 Aug 2026 14:06:21 +0200 Subject: [PATCH 6/6] test262 should pass now, cleanup --- crates/jsshaker/src/analyzer/mod.rs | 4 +- .../builtins/globals/object_constructor.rs | 58 ++++++--------- crates/jsshaker/src/folding/mod.rs | 5 +- crates/jsshaker/src/module.rs | 1 + .../src/nodes/expr/await_expression.rs | 5 +- .../src/nodes/expr/member_expression.rs | 31 +++++++- .../src/nodes/expr/private_in_expression.rs | 7 +- .../src/nodes/expr/super_expression.rs | 8 +-- crates/jsshaker/src/nodes/misc/callee.rs | 3 +- crates/jsshaker/src/nodes/misc/class.rs | 53 +++++++------- crates/jsshaker/src/nodes/misc/function.rs | 2 +- .../src/nodes/misc/private_identifier.rs | 22 +++++- .../src/nodes/stmt/module_declaration.rs | 19 ++--- crates/jsshaker/src/scope/variable_scope.rs | 16 +---- .../src/utils/private_identifier_name.rs | 72 ++++++++++++++++++- crates/jsshaker/src/value/escaped.rs | 4 +- crates/jsshaker/src/value/literal/mod.rs | 22 +++--- crates/jsshaker/src/value/literal/string.rs | 2 +- crates/jsshaker/src/value/primitive.rs | 2 +- .../fixtures/class/private_brand_unused.js | 12 ++++ .../tests/fixtures/class/private_inherit.js | 33 +++++++++ .../tests/fixtures/class/private_shadow.js | 40 +++++++++++ .../cf_comma_sequence/entry.js | 3 - .../module_fixtures/cf_comma_sequence/m.js | 5 -- .../module_fixtures/cf_constructs/entry.js | 3 + .../tests/module_fixtures/cf_constructs/m.js | 62 ++++++++++++++++ .../module_fixtures/cf_early_return/entry.js | 4 -- .../module_fixtures/cf_early_return/m.js | 6 -- .../tests/module_fixtures/cf_for_in/entry.js | 3 - .../tests/module_fixtures/cf_for_in/m.js | 4 -- .../module_fixtures/cf_for_of_break/entry.js | 3 - .../module_fixtures/cf_for_of_break/m.js | 7 -- .../cf_labeled_continue/entry.js | 3 - .../module_fixtures/cf_labeled_continue/m.js | 9 --- .../cf_switch_fallthrough/entry.js | 3 - .../cf_switch_fallthrough/m.js | 12 ---- .../tests/module_fixtures/cf_ternary/entry.js | 3 - .../tests/module_fixtures/cf_ternary/m.js | 4 -- .../module_fixtures/cf_while_nested/entry.js | 3 - .../module_fixtures/cf_while_nested/m.js | 12 ---- .../destructure_computed/entry.js | 3 - .../module_fixtures/destructure_computed/m.js | 5 -- .../destructure_defaults/entry.js | 3 - .../module_fixtures/destructure_defaults/m.js | 5 -- .../destructure_nested/entry.js | 3 - .../module_fixtures/destructure_nested/m.js | 12 ---- .../destructure_object_rest/entry.js | 3 - .../destructure_object_rest/m.js | 5 -- .../module_fixtures/destructure_rest/entry.js | 3 - .../module_fixtures/destructure_rest/m.js | 5 -- .../module_fixtures/destructure_swap/entry.js | 3 - .../module_fixtures/destructure_swap/m.js | 5 -- .../destructure_targets/entry.js | 3 + .../module_fixtures/destructure_targets/m.js | 25 +++++++ .../tests/module_fixtures/exc_flow/entry.js | 8 +++ .../tests/module_fixtures/exc_flow/m.js | 25 +++++++ .../module_fixtures/exc_rethrow/entry.js | 7 -- .../tests/module_fixtures/exc_rethrow/m.js | 12 ---- .../exc_throw_in_loop/entry.js | 3 - .../module_fixtures/exc_throw_in_loop/m.js | 11 --- .../fn_arguments_object/entry.js | 3 - .../module_fixtures/fn_arguments_object/m.js | 4 -- .../fn_default_params/entry.js | 4 -- .../module_fixtures/fn_default_params/m.js | 4 -- .../module_fixtures/fn_higher_order/entry.js | 3 - .../module_fixtures/fn_higher_order/m.js | 4 -- .../module_fixtures/fn_param_forms/entry.js | 7 ++ .../tests/module_fixtures/fn_param_forms/m.js | 16 +++++ .../module_fixtures/fn_rest_args/entry.js | 3 - .../tests/module_fixtures/fn_rest_args/m.js | 4 -- .../misc_binding_reads/entry.js | 4 ++ .../module_fixtures/misc_binding_reads/m.js | 15 ++++ .../misc_json_roundtrip/entry.js | 3 - .../module_fixtures/misc_json_roundtrip/m.js | 4 -- .../module_fixtures/misc_many_writes/entry.js | 3 - .../module_fixtures/misc_many_writes/m.js | 7 -- .../misc_reassign_to_null/entry.js | 3 - .../misc_reassign_to_null/m.js | 4 -- .../misc_template_literal/entry.js | 3 - .../misc_template_literal/m.js | 4 -- .../misc_typeof_export/entry.js | 4 -- .../module_fixtures/misc_typeof_export/m.js | 4 -- .../tests/module_fixtures/op_add_sub/entry.js | 3 - .../tests/module_fixtures/op_add_sub/m.js | 5 -- .../op_compound_assign/entry.js | 3 + .../module_fixtures/op_compound_assign/m.js | 27 +++++++ .../module_fixtures/op_exp_bitwise/entry.js | 3 - .../tests/module_fixtures/op_exp_bitwise/m.js | 7 -- .../op_logical_assign/entry.js | 3 - .../module_fixtures/op_logical_assign/m.js | 8 --- .../module_fixtures/op_mul_div_mod/entry.js | 3 - .../tests/module_fixtures/op_mul_div_mod/m.js | 6 -- .../tests/module_fixtures/op_shifts/entry.js | 3 - .../tests/module_fixtures/op_shifts/m.js | 6 -- ...dule_test@cf_comma_sequence__entry.js.snap | 15 ---- .../module_test@cf_constructs__entry.js.snap | 67 +++++++++++++++++ ...module_test@cf_early_return__entry.js.snap | 18 ----- .../module_test@cf_for_in__entry.js.snap | 18 ----- ...module_test@cf_for_of_break__entry.js.snap | 23 ------ ...le_test@cf_labeled_continue__entry.js.snap | 20 ------ ..._test@cf_switch_fallthrough__entry.js.snap | 20 ------ .../module_test@cf_ternary__entry.js.snap | 15 ---- ...module_test@cf_while_nested__entry.js.snap | 23 ------ ...e_test@destructure_computed__entry.js.snap | 15 ---- ...e_test@destructure_defaults__entry.js.snap | 16 ----- ...ule_test@destructure_nested__entry.js.snap | 21 ------ ...est@destructure_object_rest__entry.js.snap | 19 ----- ...odule_test@destructure_rest__entry.js.snap | 19 ----- ...odule_test@destructure_swap__entry.js.snap | 15 ---- ...le_test@destructure_targets__entry.js.snap | 38 ++++++++++ .../module_test@exc_flow__entry.js.snap | 39 ++++++++++ .../module_test@exc_rethrow__entry.js.snap | 27 ------- ...dule_test@exc_throw_in_loop__entry.js.snap | 22 ------ ...le_test@fn_arguments_object__entry.js.snap | 15 ---- ...dule_test@fn_default_params__entry.js.snap | 16 ----- ...module_test@fn_higher_order__entry.js.snap | 15 ---- .../module_test@fn_param_forms__entry.js.snap | 30 ++++++++ .../module_test@fn_rest_args__entry.js.snap | 15 ---- ...ule_test@misc_binding_reads__entry.js.snap | 25 +++++++ ...le_test@misc_json_roundtrip__entry.js.snap | 15 ---- ...odule_test@misc_many_writes__entry.js.snap | 18 ----- ..._test@misc_reassign_to_null__entry.js.snap | 15 ---- ..._test@misc_template_literal__entry.js.snap | 15 ---- ...ule_test@misc_typeof_export__entry.js.snap | 16 ----- .../module_test@op_add_sub__entry.js.snap | 16 ----- ...ule_test@op_compound_assign__entry.js.snap | 31 ++++++++ .../module_test@op_exp_bitwise__entry.js.snap | 18 ----- ...dule_test@op_logical_assign__entry.js.snap | 17 ----- .../module_test@op_mul_div_mod__entry.js.snap | 17 ----- .../module_test@op_shifts__entry.js.snap | 17 ----- .../snapshots/test@chain_expression.js.snap | 4 +- .../test@class__private_brand_unused.js.snap | 14 ++++ .../test@class__private_inherit.js.snap | 25 +++++++ .../test@class__private_shadow.js.snap | 8 +++ packages/napi/src/lib.rs | 16 ++--- 135 files changed, 786 insertions(+), 948 deletions(-) create mode 100644 crates/jsshaker/tests/fixtures/class/private_brand_unused.js create mode 100644 crates/jsshaker/tests/fixtures/class/private_inherit.js create mode 100644 crates/jsshaker/tests/fixtures/class/private_shadow.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_comma_sequence/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_comma_sequence/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_constructs/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/cf_constructs/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_early_return/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_early_return/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_for_in/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_for_in/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_for_of_break/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_for_of_break/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_labeled_continue/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_labeled_continue/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_ternary/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_ternary/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_while_nested/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/cf_while_nested/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_computed/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_computed/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_defaults/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_defaults/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_nested/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_nested/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_object_rest/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_object_rest/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_rest/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_rest/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_swap/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/destructure_swap/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_targets/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/destructure_targets/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/exc_flow/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/exc_flow/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/exc_rethrow/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/exc_rethrow/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/fn_arguments_object/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/fn_arguments_object/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/fn_default_params/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/fn_default_params/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/fn_higher_order/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/fn_higher_order/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_param_forms/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/fn_param_forms/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/fn_rest_args/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/fn_rest_args/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_binding_reads/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/misc_binding_reads/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/misc_many_writes/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/misc_many_writes/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/misc_template_literal/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/misc_template_literal/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/misc_typeof_export/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/misc_typeof_export/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/op_add_sub/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/op_add_sub/m.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_compound_assign/entry.js create mode 100644 crates/jsshaker/tests/module_fixtures/op_compound_assign/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/op_exp_bitwise/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/op_exp_bitwise/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/op_logical_assign/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/op_logical_assign/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/op_mul_div_mod/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/op_mul_div_mod/m.js delete mode 100644 crates/jsshaker/tests/module_fixtures/op_shifts/entry.js delete mode 100644 crates/jsshaker/tests/module_fixtures/op_shifts/m.js delete mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_comma_sequence__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_constructs__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_early_return__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_for_in__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_for_of_break__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_labeled_continue__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_switch_fallthrough__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_ternary__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@cf_while_nested__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_computed__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_defaults__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_nested__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_object_rest__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_rest__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_swap__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@destructure_targets__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@exc_flow__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@exc_rethrow__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@exc_throw_in_loop__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_arguments_object__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_default_params__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_higher_order__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_param_forms__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@fn_rest_args__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_binding_reads__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_json_roundtrip__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_many_writes__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_reassign_to_null__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_template_literal__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@misc_typeof_export__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@op_add_sub__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/module_test@op_compound_assign__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@op_exp_bitwise__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@op_logical_assign__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@op_mul_div_mod__entry.js.snap delete mode 100644 crates/jsshaker/tests/snapshots/module_test@op_shifts__entry.js.snap create mode 100644 crates/jsshaker/tests/snapshots/test@class__private_brand_unused.js.snap create mode 100644 crates/jsshaker/tests/snapshots/test@class__private_inherit.js.snap create mode 100644 crates/jsshaker/tests/snapshots/test@class__private_shadow.js.snap diff --git a/crates/jsshaker/src/analyzer/mod.rs b/crates/jsshaker/src/analyzer/mod.rs index a28b8999..e0da469d 100644 --- a/crates/jsshaker/src/analyzer/mod.rs +++ b/crates/jsshaker/src/analyzer/mod.rs @@ -28,7 +28,7 @@ use crate::{ mangling::Mangler, module::{ModuleId, Modules}, scope::Scoping, - utils::ExtraData, + utils::{ExtraData, private_identifier_name::PrivateEnvs}, value::{FnStats, literal::symbol::SymbolRegistry}, vfs::Vfs, }; @@ -59,6 +59,7 @@ pub struct Analyzer<'a> { pub diagnostics: BTreeSet, pub fn_stats: Option>, pub symbol_registry: SymbolRegistry<'a>, + pub private_envs: PrivateEnvs<'a>, } impl<'a> Analyzer<'a> { @@ -92,6 +93,7 @@ impl<'a> Analyzer<'a> { pending_deps: Default::default(), diagnostics: Default::default(), fn_stats: config.enable_fn_stats.then(|| RefCell::new(FnStats::new())), + private_envs: Default::default(), } } diff --git a/crates/jsshaker/src/builtins/globals/object_constructor.rs b/crates/jsshaker/src/builtins/globals/object_constructor.rs index cc61620b..9f2dc162 100644 --- a/crates/jsshaker/src/builtins/globals/object_constructor.rs +++ b/crates/jsshaker/src/builtins/globals/object_constructor.rs @@ -216,51 +216,35 @@ impl<'a> Builtins<'a> { } } - if !accessors.is_empty() { + let attributes = allocator::Vec::from_iter_in(attributes, analyzer.allocator); + let descriptor_dep = descriptor.get_shallow_dep(analyzer.factory); + let write_dep = self.factory.dep((enumerated.dep, descriptor_dep)); + + if accessors.is_empty() { + let has_value = value.is_some(); + if !has_value { + analyzer.push_non_det_cf_scope(); + } + let value = value.unwrap_or(analyzer.factory.undefined); + let value = + if attributes.is_empty() { value } else { self.factory.computed(value, attributes) }; + object.set_property(analyzer, write_dep, key, value); + if !has_value { + analyzer.pop_cf_scope(); + } + } else { let Some(object_value) = object.value.as_object() else { break 'trackable; }; - let dep = self.factory.dep(( - enumerated.dep, - descriptor.get_shallow_dep(analyzer.factory), - value, - allocator::Vec::from_iter_in(attributes, analyzer.allocator), - )); + let accessor_dep = self.factory.dep((write_dep, value, attributes)); for (kind, accessor) in accessors { - let accessor = self.factory.computed(accessor, dep); + let accessor = self.factory.computed(accessor, accessor_dep); object_value.init_property(analyzer, kind, key, accessor, true); } - } else { - if value.is_none() { - analyzer.push_non_det_cf_scope(); - } - - object.set_property( - analyzer, - analyzer.factory.dep((enumerated.dep, descriptor.get_shallow_dep(analyzer.factory))), - key, - { - let value = value.unwrap_or(analyzer.factory.undefined); - if attributes.is_empty() { - value - } else { - analyzer - .factory - .computed(value, allocator::Vec::from_iter_in(attributes, analyzer.allocator)) - } - }, - ); - if value.is_none() { - analyzer.pop_cf_scope(); - } } - let deps = self.factory.dep(( - dep, - object.get_shallow_dep(analyzer.factory), - key, - descriptor.get_shallow_dep(analyzer.factory), - )); + let deps = + self.factory.dep((dep, object.get_shallow_dep(analyzer.factory), key, descriptor_dep)); analyzer.add_callsite_dep(deps); diff --git a/crates/jsshaker/src/folding/mod.rs b/crates/jsshaker/src/folding/mod.rs index 283ecfe2..815a8cc2 100644 --- a/crates/jsshaker/src/folding/mod.rs +++ b/crates/jsshaker/src/folding/mod.rs @@ -82,10 +82,7 @@ impl<'a> Analyzer<'a> { if let FoldingData::NonFoldable = self.folder.bump.get(data) { value } else if let Some(literal) = self.get_foldable_literal(value) { - let mangle_atom = match literal { - LiteralValue::String(_, Some(atom)) => Some(atom), - _ => None, - }; + let mangle_atom = literal.mangle_atom(); value.override_dep(self.factory.dep(FoldableDep { data, literal, value, mangle_atom })) } else { self.factory.computed(value, NonFoldableDep { data }) diff --git a/crates/jsshaker/src/module.rs b/crates/jsshaker/src/module.rs index 2983ef25..0dc9c95c 100644 --- a/crates/jsshaker/src/module.rs +++ b/crates/jsshaker/src/module.rs @@ -131,6 +131,7 @@ impl<'a> Analyzer<'a> { self.add_diagnostic(format!("[{}] {}", path, error)); } let semantic = SemanticBuilder::new().build(program).semantic; + self.private_envs.collect(program); let module_id = ModuleId::from_usize(self.modules.modules.len()); let variable_scope = self.push_variable_scope(); self.variable_scope_mut().this = Some(self.factory.unknown); diff --git a/crates/jsshaker/src/nodes/expr/await_expression.rs b/crates/jsshaker/src/nodes/expr/await_expression.rs index d055dbd3..e3c52d33 100644 --- a/crates/jsshaker/src/nodes/expr/await_expression.rs +++ b/crates/jsshaker/src/nodes/expr/await_expression.rs @@ -16,11 +16,12 @@ impl<'a> Analyzer<'a> { self.global_effect(); + let dep = AstKind2::AwaitExpression(node); let value = self.exec_expression(&node.argument); if !matches!(self.call_scope().callee.node, CalleeNode::Module | CalleeNode::Root) { - self.include(AstKind2::AwaitExpression(node)); + self.include((dep, value)); } - value.r#await(self, AstKind2::AwaitExpression(node)) + value.r#await(self, dep) } } diff --git a/crates/jsshaker/src/nodes/expr/member_expression.rs b/crates/jsshaker/src/nodes/expr/member_expression.rs index b209aefc..b8444ef8 100644 --- a/crates/jsshaker/src/nodes/expr/member_expression.rs +++ b/crates/jsshaker/src/nodes/expr/member_expression.rs @@ -1,13 +1,14 @@ use oxc::{ ast::ast::{ ComputedMemberExpression, Expression, MemberExpression, PrivateFieldExpression, - StaticMemberExpression, + PrivateIdentifier, StaticMemberExpression, }, span::GetSpan, }; use crate::{ analyzer::Analyzer, ast::AstKind2, build_effect, entity::Entity, transformer::Transformer, + value::LiteralValue, }; pub type MemberExpressionReadResult<'a> = @@ -72,6 +73,10 @@ impl<'a> Analyzer<'a> { let key = self.exec_key(node, will_write.then_some(object)); + if let MemberExpression::PrivateFieldExpression(private) = node { + self.check_private_brand(dep_id, object, key, &private.field); + } + let value = object.get_property(self, dep_id, key); Ok((scope_count, value, undefined, (object, key))) @@ -101,6 +106,30 @@ impl<'a> Analyzer<'a> { } } + fn check_private_brand( + &mut self, + dep_id: AstKind2<'a>, + object: Entity<'a>, + key: Entity<'a>, + field: &'a PrivateIdentifier<'a>, + ) { + let Some(LiteralValue::String(name, _)) = key.get_literal(self) else { return }; + let Some(keys) = object.get_keys(self, true) else { return }; + let absent = keys.into_iter().all(|(_, existing)| { + matches!(existing.get_literal(self), Some(LiteralValue::String(other, _)) if other != name) + }); + if !absent { + return; + } + self.throw_builtin_error( + "TypeError: Cannot access private member on an object that does not have it", + ); + if self.config.preserve_exceptions { + let key = self.link_private_declaration(field, key); + self.include((dep_id, object, key)); + } + } + fn exec_key(&mut self, node: &'a MemberExpression<'a>, dep: Option>) -> Entity<'a> { match node { MemberExpression::ComputedMemberExpression(node) => { diff --git a/crates/jsshaker/src/nodes/expr/private_in_expression.rs b/crates/jsshaker/src/nodes/expr/private_in_expression.rs index cdf43a81..2c42d056 100644 --- a/crates/jsshaker/src/nodes/expr/private_in_expression.rs +++ b/crates/jsshaker/src/nodes/expr/private_in_expression.rs @@ -4,8 +4,10 @@ use crate::{analyzer::Analyzer, entity::Entity, transformer::Transformer}; impl<'a> Analyzer<'a> { pub fn exec_private_in_expression(&mut self, node: &'a PrivateInExpression<'a>) -> Entity<'a> { + let left = self.exec_private_identifier(&node.left); + let left = self.link_private_declaration(&node.left, left); let right = self.exec_expression(&node.right); - self.factory.computed_unknown_boolean(right) + self.factory.computed_unknown_boolean((left, right)) } } @@ -20,7 +22,8 @@ impl<'a> Transformer<'a> { let right = self.transform_expression(right, need_val); if need_val { - Some(self.ast.expression_private_in(*span, left.clone(), right.unwrap())) + let left = self.transform_private_identifier(left, true).unwrap(); + Some(self.ast.expression_private_in(*span, left, right.unwrap())) } else { right } diff --git a/crates/jsshaker/src/nodes/expr/super_expression.rs b/crates/jsshaker/src/nodes/expr/super_expression.rs index 0c333d3a..3f4b6b4a 100644 --- a/crates/jsshaker/src/nodes/expr/super_expression.rs +++ b/crates/jsshaker/src/nodes/expr/super_expression.rs @@ -6,10 +6,10 @@ impl<'a> Analyzer<'a> { pub fn exec_super(&mut self, _node: &'a Super) -> Entity<'a> { // Should only be called in member expression let super_scope = self.get_super(); - if super_scope.is_static { - super_scope.class - } else { - super_scope.class.get_property(self, self.factory.no_dep, builtin_string!("prototype")) + match super_scope.super_class { + None => self.factory.unknown, + Some(class) if super_scope.is_static => class, + Some(class) => class.get_property(self, self.factory.no_dep, builtin_string!("prototype")), } } } diff --git a/crates/jsshaker/src/nodes/misc/callee.rs b/crates/jsshaker/src/nodes/misc/callee.rs index 99e85f96..925ea8ae 100644 --- a/crates/jsshaker/src/nodes/misc/callee.rs +++ b/crates/jsshaker/src/nodes/misc/callee.rs @@ -34,7 +34,8 @@ impl<'a> Analyzer<'a> { node: &'a Expression<'a>, ) -> Result<(usize, Entity<'a>, Option>, Entity<'a>), Entity<'a>> { if matches!(node, Expression::Super(_)) { - return Ok((0, self.get_super().class, None, self.get_this())); + let super_class = self.get_super().super_class.unwrap_or(self.factory.undefined); + return Ok((0, super_class, None, self.get_this())); } let dep = AstKind2::Callee(node); diff --git a/crates/jsshaker/src/nodes/misc/class.rs b/crates/jsshaker/src/nodes/misc/class.rs index 5b1a5e97..48253967 100644 --- a/crates/jsshaker/src/nodes/misc/class.rs +++ b/crates/jsshaker/src/nodes/misc/class.rs @@ -6,7 +6,7 @@ use oxc::{ NONE, ast::{ Class, ClassBody, ClassElement, ClassType, MethodDefinitionKind, PropertyDefinitionType, - PropertyKind, StaticBlock, + PropertyKey, PropertyKind, StaticBlock, }, }, span::GetSpan, @@ -60,16 +60,21 @@ impl<'a> Analyzer<'a> { prototype.set_prototype(ObjectPrototype::ImplicitOrNull); }; - let super_class = data.super_class.unwrap_or(self.factory.undefined); + let super_class = data.super_class; // Enter class statics scope - self.variable_scope_mut().super_class = Some(SuperScope::statics(super_class)); - self.variable_scope_mut().this = Some(class.into()); + self.push_variable_scope(); + let variable_scope = self.variable_scope_mut(); + variable_scope.super_class = Some(SuperScope { super_class, is_static: true }); + variable_scope.this = Some(class.into()); // 2. Execute keys and find constructor for element in &node.body.body { - let key = element.property_key().map(|key| self.exec_property_key(key, None)); - data.keys.push(key); + let property_key = element.property_key(); + if let Some(PropertyKey::PrivateIdentifier(name)) = property_key { + self.private_envs.declare(name); + } + data.keys.push(property_key.map(|key| self.exec_property_key(key, None))); if let ClassElement::MethodDefinition(method) = element && method.kind.is_constructor() @@ -97,7 +102,8 @@ impl<'a> Analyzer<'a> { class.statics.init_property(self, kind, key.unwrap(), value, true); } else { self.push_variable_scope(); - self.variable_scope_mut().super_class = Some(SuperScope::instance(super_class)); + self.variable_scope_mut().super_class = + Some(SuperScope { super_class, is_static: false }); let value = self.exec_function(&node.value); self.pop_variable_scope(); prototype.init_property(self, kind, key.unwrap(), value, true); @@ -159,6 +165,9 @@ impl<'a> Analyzer<'a> { } } + // Leave class statics scope + self.pop_variable_scope(); + // 5. Execute class decorators (ES2025 Stage 3) let class = class.into(); let class = if !node.decorators.is_empty() { @@ -186,7 +195,11 @@ impl<'a> Analyzer<'a> { value } - fn init_instance_fields(&mut self, node: &'a Class<'a>, data: &ClassData<'a>, this: Entity<'a>) { + pub fn init_pending_instance_fields(&mut self) { + let Some((node, data_cell, this)) = self.call_scope_mut().pending_instance_fields.take() else { + return; + }; + let Ok(data) = data_cell.try_borrow() else { return }; for (key, element) in data.keys.iter().zip(node.body.body.iter()) { if let ClassElement::PropertyDefinition(node) = element && !node.r#static @@ -197,14 +210,6 @@ impl<'a> Analyzer<'a> { } } - pub fn init_pending_instance_fields(&mut self) { - if let Some((node, data_cell, this)) = self.call_scope_mut().pending_instance_fields.take() - && let Ok(data) = data_cell.try_borrow() - { - self.init_instance_fields(node, &data, this); - } - } - pub fn call_class_constructor( &mut self, node: &'a Class<'a>, @@ -224,12 +229,12 @@ impl<'a> Analyzer<'a> { let factory = self.factory; self.push_call_scope(info, false, false); - let super_class = data.super_class.unwrap_or(self.factory.undefined); + let super_class = data.super_class; let variable_scope = self.variable_scope_mut(); variable_scope.this = Some(info.this); variable_scope.arguments = Some((info.args, factory.vec(/* later filled by formal parameters */))); - variable_scope.super_class = Some(SuperScope::instance(super_class)); + variable_scope.super_class = Some(SuperScope { super_class, is_static: false }); if let Some(id) = &node.id { self.declare_binding_identifier(id, None, DeclarationKind::NamedFunctionInBody); @@ -240,11 +245,10 @@ impl<'a> Analyzer<'a> { ); } - // 1. Init properties, deferred past `super()` when derived - if data.super_class.is_some() { - self.call_scope_mut().pending_instance_fields = Some((node, data_cell, info.this)); - } else { - self.init_instance_fields(node, &data, info.this); + // 1. Init properties + self.call_scope_mut().pending_instance_fields = Some((node, data_cell, info.this)); + if super_class.is_none() { + self.init_pending_instance_fields(); } // 2. Call constructor @@ -261,8 +265,7 @@ impl<'a> Analyzer<'a> { let (ret_val, _) = self.pop_call_scope(); let ret_val = self.factory.computed(ret_val, dep); (ret_val, FnCacheTrackingData::worst_case()) - } else if let Some(super_class) = &data.super_class { - let super_class = *super_class; + } else if let Some(super_class) = super_class { let ret_val = super_class.call(self, self.factory.no_dep, info.this, info.args); self.init_pending_instance_fields(); self.pop_call_scope(); diff --git a/crates/jsshaker/src/nodes/misc/function.rs b/crates/jsshaker/src/nodes/misc/function.rs index 14cc0c0d..d2005bf9 100644 --- a/crates/jsshaker/src/nodes/misc/function.rs +++ b/crates/jsshaker/src/nodes/misc/function.rs @@ -56,7 +56,7 @@ impl<'a> Analyzer<'a> { let factory = analyzer.factory; let variable_scope = analyzer.variable_scope_mut(); variable_scope.this = Some(info.this); - variable_scope.super_class = info.include.then(|| SuperScope::instance(factory.unknown)); + variable_scope.super_class = info.include.then_some(SuperScope::default()); variable_scope.arguments = Some((info.args, factory.vec(/* later filled by formal parameters */))); diff --git a/crates/jsshaker/src/nodes/misc/private_identifier.rs b/crates/jsshaker/src/nodes/misc/private_identifier.rs index eed20078..5c887101 100644 --- a/crates/jsshaker/src/nodes/misc/private_identifier.rs +++ b/crates/jsshaker/src/nodes/misc/private_identifier.rs @@ -4,10 +4,12 @@ use crate::{ analyzer::Analyzer, ast::AstKind2, entity::Entity, + mangling::MangleConstraint, transformer::Transformer, utils::private_identifier_name::{ escape_private_identifier_name, unescape_private_identifier_name, }, + value::LiteralValue, }; impl<'a> Analyzer<'a> { @@ -15,11 +17,29 @@ impl<'a> Analyzer<'a> { self.factory.computed( self.exec_mangable_static_string( AstKind2::PrivateIdentifier(node), - escape_private_identifier_name(node.name.as_str()), + escape_private_identifier_name(self.private_envs.get(node), node.name.as_str()), ), AstKind2::PrivateIdentifier(node), ) } + + pub fn link_private_declaration( + &mut self, + node: &'a PrivateIdentifier<'a>, + key: Entity<'a>, + ) -> Entity<'a> { + let Some(declaration) = self.private_envs.declaration(node) else { return key }; + let declared = self.exec_private_identifier(declaration); + let linked = self.factory.computed(key, declared); + match MangleConstraint::equality( + true, + key.get_literal(self).and_then(LiteralValue::mangle_atom), + declared.get_literal(self).and_then(LiteralValue::mangle_atom), + ) { + Some(constraint) => self.factory.mangable(linked, (key, declared), constraint), + None => linked, + } + } } impl<'a> Transformer<'a> { diff --git a/crates/jsshaker/src/nodes/stmt/module_declaration.rs b/crates/jsshaker/src/nodes/stmt/module_declaration.rs index 5b49468c..388e2fab 100644 --- a/crates/jsshaker/src/nodes/stmt/module_declaration.rs +++ b/crates/jsshaker/src/nodes/stmt/module_declaration.rs @@ -169,15 +169,18 @@ impl<'a> Analyzer<'a> { } } } else if let Some(resolved) = resolved { - let module = &self.modules.modules[resolved]; - match specifier { - ImportDeclarationSpecifier::ImportDefaultSpecifier(_node) => self - .get_export_value_by_name(resolved, Atom::from("default"), &mut Default::default()) - .unwrap_or(self.factory.unknown), - ImportDeclarationSpecifier::ImportNamespaceSpecifier(_node) => module.module_object, - ImportDeclarationSpecifier::ImportSpecifier(node) => self - .get_export_value_by_name(resolved, node.imported.name(), &mut Default::default()) + let name = match specifier { + ImportDeclarationSpecifier::ImportDefaultSpecifier(_node) => { + Some(Atom::from("default")) + } + ImportDeclarationSpecifier::ImportNamespaceSpecifier(_node) => None, + ImportDeclarationSpecifier::ImportSpecifier(node) => Some(node.imported.name()), + }; + match name { + Some(name) => self + .get_export_value_by_name(resolved, name, &mut Default::default()) .unwrap_or(self.factory.unknown), + None => self.modules.modules[resolved].module_object, } } else { self.builtins.factory.unknown diff --git a/crates/jsshaker/src/scope/variable_scope.rs b/crates/jsshaker/src/scope/variable_scope.rs index e5f566ff..bdf484b6 100644 --- a/crates/jsshaker/src/scope/variable_scope.rs +++ b/crates/jsshaker/src/scope/variable_scope.rs @@ -37,22 +37,12 @@ pub struct Variable<'a> { pub decl_node: AstKind2<'a>, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub struct SuperScope<'a> { - pub class: Entity<'a>, + pub super_class: Option>, pub is_static: bool, } -impl<'a> SuperScope<'a> { - pub fn statics(class: Entity<'a>) -> Self { - SuperScope { class, is_static: true } - } - - pub fn instance(class: Entity<'a>) -> Self { - SuperScope { class, is_static: false } - } -} - pub struct VariableScope<'a> { pub variables: allocator::HashMap<'a, SymbolId, &'a RefCell>>, pub this: Option>, @@ -486,6 +476,6 @@ impl<'a> Analyzer<'a> { return super_class; } } - SuperScope::instance(self.factory.unknown) + SuperScope::default() } } diff --git a/crates/jsshaker/src/utils/private_identifier_name.rs b/crates/jsshaker/src/utils/private_identifier_name.rs index ba3e8363..661b2acc 100644 --- a/crates/jsshaker/src/utils/private_identifier_name.rs +++ b/crates/jsshaker/src/utils/private_identifier_name.rs @@ -1,13 +1,22 @@ +use oxc::ast::ast::{Class, PrivateIdentifier, Program, PropertyKey}; +use oxc_ast_visit::{Visit, walk}; +use rustc_hash::FxHashMap; + use crate::value::PropertyKeyValue; const PRIVATE_IDENTIFIER_PREFIX: &str = "__#private__"; -pub fn escape_private_identifier_name(name: &str) -> String { - format!("{}{}", PRIVATE_IDENTIFIER_PREFIX, name) +pub type PrivateEnvId = u32; + +pub fn escape_private_identifier_name(env: PrivateEnvId, name: &str) -> String { + format!("{}{}#{}", PRIVATE_IDENTIFIER_PREFIX, env, name) } pub fn unescape_private_identifier_name(name: &str) -> &str { - if let Some(stripped) = name.strip_prefix(PRIVATE_IDENTIFIER_PREFIX) { stripped } else { name } + match name.strip_prefix(PRIVATE_IDENTIFIER_PREFIX) { + Some(rest) => rest.split_once('#').map_or(rest, |(_, name)| name), + None => name, + } } impl<'a> PropertyKeyValue<'a> { @@ -18,3 +27,60 @@ impl<'a> PropertyKeyValue<'a> { } } } + +#[derive(Default)] +pub struct PrivateEnvs<'a> { + nodes: FxHashMap<*const PrivateIdentifier<'a>, PrivateEnvId>, + declarations: FxHashMap<(PrivateEnvId, &'a str), &'a PrivateIdentifier<'a>>, + stack: Vec<(PrivateEnvId, Vec<&'a str>)>, + next_id: PrivateEnvId, +} + +impl<'a> PrivateEnvs<'a> { + pub fn collect(&mut self, program: &Program<'a>) { + self.visit_program(program); + } + + pub fn get(&self, node: &PrivateIdentifier<'a>) -> PrivateEnvId { + self.nodes.get(&std::ptr::from_ref(node)).copied().unwrap_or_default() + } + + pub fn declare(&mut self, node: &'a PrivateIdentifier<'a>) { + self.declarations.insert((self.get(node), node.name.as_str()), node); + } + + pub fn declaration(&self, node: &PrivateIdentifier<'a>) -> Option<&'a PrivateIdentifier<'a>> { + self.declarations.get(&(self.get(node), node.name.as_str())).copied() + } +} + +impl<'a> Visit<'a> for PrivateEnvs<'a> { + fn visit_class(&mut self, node: &Class<'a>) { + self.next_id += 1; + let id = self.next_id; + let declared = node + .body + .body + .iter() + .filter_map(|element| match element.property_key() { + Some(PropertyKey::PrivateIdentifier(key)) => Some(key.name.as_str()), + _ => None, + }) + .collect(); + + self.stack.push((id, declared)); + walk::walk_class(self, node); + self.stack.pop(); + } + + fn visit_private_identifier(&mut self, node: &PrivateIdentifier<'a>) { + let env = self + .stack + .iter() + .rev() + .find(|(_, declared)| declared.contains(&node.name.as_str())) + .or_else(|| self.stack.last()) + .map_or(0, |(id, _)| *id); + self.nodes.insert(std::ptr::from_ref(node), env); + } +} diff --git a/crates/jsshaker/src/value/escaped.rs b/crates/jsshaker/src/value/escaped.rs index 2818b91b..a596e6cc 100644 --- a/crates/jsshaker/src/value/escaped.rs +++ b/crates/jsshaker/src/value/escaped.rs @@ -31,9 +31,11 @@ pub fn set_property<'a>( analyzer.include((dep, key, value)); } +pub const PRIMITIVE_SET_PROPERTY_ERROR: &str = "Cannot create property on a primitive value"; + pub fn set_property_on_primitive<'a>( analyzer: &mut Analyzer<'a>, - message: impl Into, + message: &str, dep: Dep<'a>, key: Entity<'a>, value: Entity<'a>, diff --git a/crates/jsshaker/src/value/literal/mod.rs b/crates/jsshaker/src/value/literal/mod.rs index 3088953b..d130b789 100644 --- a/crates/jsshaker/src/value/literal/mod.rs +++ b/crates/jsshaker/src/value/literal/mod.rs @@ -91,7 +91,7 @@ impl<'a> ValueTrait<'a> for LiteralValue<'a> { let message = if matches!(self, LiteralValue::Null | LiteralValue::Undefined) { "Cannot set property of null or undefined" } else { - "Cannot create property on a primitive value" + escaped::PRIMITIVE_SET_PROPERTY_ERROR }; escaped::set_property_on_primitive(analyzer, message, dep, key, value); } @@ -184,10 +184,7 @@ impl<'a> ValueTrait<'a> for LiteralValue<'a> { analyzer .factory - .alloc(LiteralValue::String( - self.to_string(analyzer.allocator), - if let LiteralValue::String(_, Some(atom)) = self { Some(*atom) } else { None }, - )) + .alloc(LiteralValue::String(self.to_string(analyzer.allocator), self.mangle_atom())) .into() } @@ -320,6 +317,13 @@ impl<'a> ValueTrait<'a> for LiteralValue<'a> { } impl<'a> LiteralValue<'a> { + pub fn mangle_atom(self) -> Option { + match self { + LiteralValue::String(_, atom) => atom, + _ => None, + } + } + pub fn build_expr( &self, transformer: &Transformer<'a>, @@ -446,13 +450,7 @@ impl<'a> From> for PropertyKeyValue<'a> { impl<'a> From> for (PropertyKeyValue<'a>, Option) { fn from(val: LiteralValue<'a>) -> Self { - ( - val.into(), - match val { - LiteralValue::String(_, m) => m, - _ => None, - }, - ) + (val.into(), val.mangle_atom()) } } diff --git a/crates/jsshaker/src/value/literal/string.rs b/crates/jsshaker/src/value/literal/string.rs index a6a4c601..75a5801c 100644 --- a/crates/jsshaker/src/value/literal/string.rs +++ b/crates/jsshaker/src/value/literal/string.rs @@ -79,7 +79,7 @@ impl<'a> ValueTrait<'a> for Atom<'a> { ) { escaped::set_property_on_primitive( analyzer, - "Cannot create property on a primitive value", + escaped::PRIMITIVE_SET_PROPERTY_ERROR, dep, key, value, diff --git a/crates/jsshaker/src/value/primitive.rs b/crates/jsshaker/src/value/primitive.rs index 52563f23..2156a082 100644 --- a/crates/jsshaker/src/value/primitive.rs +++ b/crates/jsshaker/src/value/primitive.rs @@ -53,7 +53,7 @@ impl<'a> ValueTrait<'a> for PrimitiveValue { ) { escaped::set_property_on_primitive( analyzer, - "Cannot create property on a primitive value", + escaped::PRIMITIVE_SET_PROPERTY_ERROR, dep, key, value, diff --git a/crates/jsshaker/tests/fixtures/class/private_brand_unused.js b/crates/jsshaker/tests/fixtures/class/private_brand_unused.js new file mode 100644 index 00000000..8660e567 --- /dev/null +++ b/crates/jsshaker/tests/fixtures/class/private_brand_unused.js @@ -0,0 +1,12 @@ +const results = []; + +class Ghost { + #tag = 1; + static has(o) { + return #tag in o; + } +} + +results.push(Ghost.has({})); + +export default results; diff --git a/crates/jsshaker/tests/fixtures/class/private_inherit.js b/crates/jsshaker/tests/fixtures/class/private_inherit.js new file mode 100644 index 00000000..0404916c --- /dev/null +++ b/crates/jsshaker/tests/fixtures/class/private_inherit.js @@ -0,0 +1,33 @@ +const results = []; + +class Base { + #id; + constructor(id) { + this.#id = id; + } + getId() { + return this.#id; + } +} + +class Derived extends Base { + #id = "derived"; + constructor() { + super("base"); + } + both() { + return [super.getId(), this.#id]; + } +} + +const d = new Derived(); +results.push(d.getId()); +results.push(...d.both()); + +try { + Base.prototype.getId.call({}); +} catch (e) { + results.push(e.constructor.name); +} + +export default results; diff --git a/crates/jsshaker/tests/fixtures/class/private_shadow.js b/crates/jsshaker/tests/fixtures/class/private_shadow.js new file mode 100644 index 00000000..e1f74d84 --- /dev/null +++ b/crates/jsshaker/tests/fixtures/class/private_shadow.js @@ -0,0 +1,40 @@ +const results = []; + +class Outer { + #x = "outer"; + + readSelf() { + return this.#x; + } + + makeShadow() { + class Inner { + #x = "inner"; + read() { + return this.#x; + } + } + return new Inner().read(); + } + + makeReader() { + class Reader { + readFrom(o) { + return o.#x; + } + } + return new Reader().readFrom(this); + } +} + +class Other { + #x = "other"; + read() { + return this.#x; + } +} + +const o = new Outer(); +results.push(o.readSelf(), o.makeShadow(), o.makeReader(), new Other().read()); + +export default results; diff --git a/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/entry.js b/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/entry.js deleted file mode 100644 index fb07041f..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { a, b, f } from "./m.js"; -f(); -console.log(a, b); diff --git a/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/m.js b/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/m.js deleted file mode 100644 index 4dfc80ce..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_comma_sequence/m.js +++ /dev/null @@ -1,5 +0,0 @@ -export let a = 0, - b = 0; -export function f() { - a = ((b = 2), 3); -} diff --git a/crates/jsshaker/tests/module_fixtures/cf_constructs/entry.js b/crates/jsshaker/tests/module_fixtures/cf_constructs/entry.js new file mode 100644 index 00000000..28d0db93 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_constructs/entry.js @@ -0,0 +1,3 @@ +import { log, a, b, run } from "./m.js"; +run(); +console.log(log, a, b); diff --git a/crates/jsshaker/tests/module_fixtures/cf_constructs/m.js b/crates/jsshaker/tests/module_fixtures/cf_constructs/m.js new file mode 100644 index 00000000..6764a7b1 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/cf_constructs/m.js @@ -0,0 +1,62 @@ +export let log = ""; +export let a = 0, + b = 0; + +function bail(stop) { + log += "1"; + if (stop) return; + log += "2"; +} + +function pick(n) { + switch (n) { + case 1: + log += "a"; + case 2: + log += "b"; + break; + case 3: + log += "c"; + } +} + +export function run() { + bail(true); + bail(false); + log += ";"; + + log += (0 ? "yes" : "no") + ";"; + + pick(1); + log += ";"; + + for (const k in { a: 1, b: 2 }) log += k; + log += ";"; + + for (const x of [1, 2, 3, 4]) { + if (x === 3) break; + log += x; + } + log += ";"; + + outer: for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + if (j === 1) continue outer; + log += "" + i + j; + } + } + log += ";"; + + let i = 0; + while (i < 3) { + let j = 0; + while (j < 2) { + log += "w"; + j++; + } + i++; + } + log += ";"; + + a = ((b = 2), 3); +} diff --git a/crates/jsshaker/tests/module_fixtures/cf_early_return/entry.js b/crates/jsshaker/tests/module_fixtures/cf_early_return/entry.js deleted file mode 100644 index 36a4fcda..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_early_return/entry.js +++ /dev/null @@ -1,4 +0,0 @@ -import { v, f } from "./m.js"; -f(true); -f(false); -console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/cf_early_return/m.js b/crates/jsshaker/tests/module_fixtures/cf_early_return/m.js deleted file mode 100644 index 9cbd75d4..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_early_return/m.js +++ /dev/null @@ -1,6 +0,0 @@ -export let v = ""; -export function f(bail) { - v += "1"; - if (bail) return; - v += "2"; -} diff --git a/crates/jsshaker/tests/module_fixtures/cf_for_in/entry.js b/crates/jsshaker/tests/module_fixtures/cf_for_in/entry.js deleted file mode 100644 index 4248d55a..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_for_in/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { keys, f } from "./m.js"; -f(); -console.log(keys); diff --git a/crates/jsshaker/tests/module_fixtures/cf_for_in/m.js b/crates/jsshaker/tests/module_fixtures/cf_for_in/m.js deleted file mode 100644 index a072d137..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_for_in/m.js +++ /dev/null @@ -1,4 +0,0 @@ -export let keys = ""; -export function f() { - for (const k in { a: 1, b: 2 }) keys += k; -} diff --git a/crates/jsshaker/tests/module_fixtures/cf_for_of_break/entry.js b/crates/jsshaker/tests/module_fixtures/cf_for_of_break/entry.js deleted file mode 100644 index 0e1aa3ef..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_for_of_break/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { seen, f } from "./m.js"; -f(); -console.log(seen); diff --git a/crates/jsshaker/tests/module_fixtures/cf_for_of_break/m.js b/crates/jsshaker/tests/module_fixtures/cf_for_of_break/m.js deleted file mode 100644 index 31aeac6b..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_for_of_break/m.js +++ /dev/null @@ -1,7 +0,0 @@ -export let seen = ""; -export function f() { - for (const x of [1, 2, 3, 4]) { - if (x === 3) break; - seen += x; - } -} diff --git a/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/entry.js b/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/entry.js deleted file mode 100644 index 134cc3c4..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { acc, f } from "./m.js"; -f(); -console.log(acc); diff --git a/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/m.js b/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/m.js deleted file mode 100644 index 7d336e47..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_labeled_continue/m.js +++ /dev/null @@ -1,9 +0,0 @@ -export let acc = ""; -export function f() { - outer: for (let i = 0; i < 3; i++) { - for (let j = 0; j < 3; j++) { - if (j === 1) continue outer; - acc += "" + i + j; - } - } -} diff --git a/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/entry.js b/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/entry.js deleted file mode 100644 index 6825d6ed..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { log, f } from "./m.js"; -f(1); -console.log(log); diff --git a/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/m.js b/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/m.js deleted file mode 100644 index 4aa0de87..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/m.js +++ /dev/null @@ -1,12 +0,0 @@ -export let log = ""; -export function f(n) { - switch (n) { - case 1: - log += "a"; - case 2: - log += "b"; - break; - case 3: - log += "c"; - } -} diff --git a/crates/jsshaker/tests/module_fixtures/cf_ternary/entry.js b/crates/jsshaker/tests/module_fixtures/cf_ternary/entry.js deleted file mode 100644 index 7cfb508f..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_ternary/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { v, f } from "./m.js"; -f(0); -console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/cf_ternary/m.js b/crates/jsshaker/tests/module_fixtures/cf_ternary/m.js deleted file mode 100644 index 90f8927c..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_ternary/m.js +++ /dev/null @@ -1,4 +0,0 @@ -export let v = "x"; -export function f(n) { - v = n ? "yes" : "no"; -} diff --git a/crates/jsshaker/tests/module_fixtures/cf_while_nested/entry.js b/crates/jsshaker/tests/module_fixtures/cf_while_nested/entry.js deleted file mode 100644 index 4a023717..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_while_nested/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { t, f } from "./m.js"; -f(); -console.log(t); diff --git a/crates/jsshaker/tests/module_fixtures/cf_while_nested/m.js b/crates/jsshaker/tests/module_fixtures/cf_while_nested/m.js deleted file mode 100644 index 8fc2234a..00000000 --- a/crates/jsshaker/tests/module_fixtures/cf_while_nested/m.js +++ /dev/null @@ -1,12 +0,0 @@ -export let t = 0; -export function f() { - let i = 0; - while (i < 3) { - let j = 0; - while (j < 2) { - t += 1; - j++; - } - i++; - } -} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_computed/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_computed/entry.js deleted file mode 100644 index fcf6cb3a..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_computed/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { v, f } from "./m.js"; -f(); -console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_computed/m.js b/crates/jsshaker/tests/module_fixtures/destructure_computed/m.js deleted file mode 100644 index 2e4478d0..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_computed/m.js +++ /dev/null @@ -1,5 +0,0 @@ -export let v = 0; -const k = "dyn"; -export function f() { - ({ [k]: v } = { dyn: 42 }); -} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_defaults/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_defaults/entry.js deleted file mode 100644 index fb07041f..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_defaults/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { a, b, f } from "./m.js"; -f(); -console.log(a, b); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_defaults/m.js b/crates/jsshaker/tests/module_fixtures/destructure_defaults/m.js deleted file mode 100644 index a1c0a5ac..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_defaults/m.js +++ /dev/null @@ -1,5 +0,0 @@ -export let a = 0, - b = 0; -export function f() { - [a = 5, b = 6] = [undefined]; -} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_nested/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_nested/entry.js deleted file mode 100644 index 38bef853..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_nested/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { a, b, c, f } from "./m.js"; -f(); -console.log(a, b, c); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_nested/m.js b/crates/jsshaker/tests/module_fixtures/destructure_nested/m.js deleted file mode 100644 index fea97154..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_nested/m.js +++ /dev/null @@ -1,12 +0,0 @@ -export let a = 0, - b = 0, - c = 0; -export function f() { - ({ - x: a, - y: { - z: b, - w: [c], - }, - } = { x: 1, y: { z: 2, w: [3] } }); -} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_object_rest/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_object_rest/entry.js deleted file mode 100644 index 96566492..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_object_rest/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { one, others, f } from "./m.js"; -f(); -console.log(one, JSON.stringify(others)); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_object_rest/m.js b/crates/jsshaker/tests/module_fixtures/destructure_object_rest/m.js deleted file mode 100644 index 007a360a..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_object_rest/m.js +++ /dev/null @@ -1,5 +0,0 @@ -export let one = 0, - others = {}; -export function f() { - ({ one, ...others } = { one: 1, two: 2, three: 3 }); -} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_rest/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_rest/entry.js deleted file mode 100644 index 2302cb00..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_rest/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { head, rest, f } from "./m.js"; -f(); -console.log(head, JSON.stringify(rest)); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_rest/m.js b/crates/jsshaker/tests/module_fixtures/destructure_rest/m.js deleted file mode 100644 index e40ce9e9..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_rest/m.js +++ /dev/null @@ -1,5 +0,0 @@ -export let head = 0, - rest = []; -export function f() { - [head, ...rest] = [1, 2, 3]; -} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_swap/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_swap/entry.js deleted file mode 100644 index fb07041f..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_swap/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { a, b, f } from "./m.js"; -f(); -console.log(a, b); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_swap/m.js b/crates/jsshaker/tests/module_fixtures/destructure_swap/m.js deleted file mode 100644 index 33cb1f4c..00000000 --- a/crates/jsshaker/tests/module_fixtures/destructure_swap/m.js +++ /dev/null @@ -1,5 +0,0 @@ -export let a = 1, - b = 2; -export function f() { - [a, b] = [b, a]; -} diff --git a/crates/jsshaker/tests/module_fixtures/destructure_targets/entry.js b/crates/jsshaker/tests/module_fixtures/destructure_targets/entry.js new file mode 100644 index 00000000..a125bd4b --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_targets/entry.js @@ -0,0 +1,3 @@ +import { a, b, c, head, rest, one, others, dyn, f } from "./m.js"; +f(); +console.log(a, b, c, head, JSON.stringify(rest), one, JSON.stringify(others), dyn); diff --git a/crates/jsshaker/tests/module_fixtures/destructure_targets/m.js b/crates/jsshaker/tests/module_fixtures/destructure_targets/m.js new file mode 100644 index 00000000..3f9ed0d0 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/destructure_targets/m.js @@ -0,0 +1,25 @@ +export let a = 1, + b = 2, + c = 0; +export let head = 0, + rest = []; +export let one = 0, + others = {}; +export let dyn = 0; + +const k = "dyn"; + +export function f() { + [a, b] = [b, a]; + [a = 5, c = 6] = [undefined]; + ({ + x: b, + y: { + z: c, + w: [head], + }, + } = { x: 1, y: { z: 2, w: [3] } }); + [head, ...rest] = [1, 2, 3]; + ({ one, ...others } = { one: 1, two: 2, three: 3 }); + ({ [k]: dyn } = { dyn: 42 }); +} diff --git a/crates/jsshaker/tests/module_fixtures/exc_flow/entry.js b/crates/jsshaker/tests/module_fixtures/exc_flow/entry.js new file mode 100644 index 00000000..f3b58ace --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/exc_flow/entry.js @@ -0,0 +1,8 @@ +import { v, count, rethrow, loop } from "./m.js"; +try { + rethrow(); +} catch (e) { + console.log("caught", e.message); +} +loop(); +console.log(v, count); diff --git a/crates/jsshaker/tests/module_fixtures/exc_flow/m.js b/crates/jsshaker/tests/module_fixtures/exc_flow/m.js new file mode 100644 index 00000000..2354e0d6 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/exc_flow/m.js @@ -0,0 +1,25 @@ +export let v = ""; +export let count = 0; + +export function rethrow() { + try { + v = "try"; + throw new Error("boom"); + } catch (e) { + v += "|catch"; + throw e; + } finally { + v += "|finally"; + } +} + +export function loop() { + for (let i = 0; i < 5; i++) { + try { + if (i % 2) throw new Error(); + count += 10; + } catch { + count += 1; + } + } +} diff --git a/crates/jsshaker/tests/module_fixtures/exc_rethrow/entry.js b/crates/jsshaker/tests/module_fixtures/exc_rethrow/entry.js deleted file mode 100644 index 295013d6..00000000 --- a/crates/jsshaker/tests/module_fixtures/exc_rethrow/entry.js +++ /dev/null @@ -1,7 +0,0 @@ -import { v, f } from "./m.js"; -try { - f(); -} catch (e) { - console.log("caught", e.message); -} -console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/exc_rethrow/m.js b/crates/jsshaker/tests/module_fixtures/exc_rethrow/m.js deleted file mode 100644 index 531f8516..00000000 --- a/crates/jsshaker/tests/module_fixtures/exc_rethrow/m.js +++ /dev/null @@ -1,12 +0,0 @@ -export let v = ""; -export function f() { - try { - v = "try"; - throw new Error("boom"); - } catch (e) { - v += "|catch"; - throw e; - } finally { - v += "|finally"; - } -} diff --git a/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/entry.js b/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/entry.js deleted file mode 100644 index c58672f1..00000000 --- a/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { count, f } from "./m.js"; -f(); -console.log(count); diff --git a/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/m.js b/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/m.js deleted file mode 100644 index 2f8998b5..00000000 --- a/crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/m.js +++ /dev/null @@ -1,11 +0,0 @@ -export let count = 0; -export function f() { - for (let i = 0; i < 5; i++) { - try { - if (i % 2) throw new Error(); - count += 10; - } catch { - count += 1; - } - } -} diff --git a/crates/jsshaker/tests/module_fixtures/fn_arguments_object/entry.js b/crates/jsshaker/tests/module_fixtures/fn_arguments_object/entry.js deleted file mode 100644 index 1c205480..00000000 --- a/crates/jsshaker/tests/module_fixtures/fn_arguments_object/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { n, f } from "./m.js"; -f(1, 2, 3); -console.log(n); diff --git a/crates/jsshaker/tests/module_fixtures/fn_arguments_object/m.js b/crates/jsshaker/tests/module_fixtures/fn_arguments_object/m.js deleted file mode 100644 index 25d2af8f..00000000 --- a/crates/jsshaker/tests/module_fixtures/fn_arguments_object/m.js +++ /dev/null @@ -1,4 +0,0 @@ -export let n = 0; -export function f() { - n = arguments.length + arguments[2]; -} diff --git a/crates/jsshaker/tests/module_fixtures/fn_default_params/entry.js b/crates/jsshaker/tests/module_fixtures/fn_default_params/entry.js deleted file mode 100644 index 0132e0bb..00000000 --- a/crates/jsshaker/tests/module_fixtures/fn_default_params/entry.js +++ /dev/null @@ -1,4 +0,0 @@ -import { v, f } from "./m.js"; -f(); -f(9); -console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/fn_default_params/m.js b/crates/jsshaker/tests/module_fixtures/fn_default_params/m.js deleted file mode 100644 index 2210ecc9..00000000 --- a/crates/jsshaker/tests/module_fixtures/fn_default_params/m.js +++ /dev/null @@ -1,4 +0,0 @@ -export let v = ""; -export function f(x = 5) { - v += x; -} diff --git a/crates/jsshaker/tests/module_fixtures/fn_higher_order/entry.js b/crates/jsshaker/tests/module_fixtures/fn_higher_order/entry.js deleted file mode 100644 index d61c3f47..00000000 --- a/crates/jsshaker/tests/module_fixtures/fn_higher_order/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { v, apply } from "./m.js"; -apply((x) => x * 3); -console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/fn_higher_order/m.js b/crates/jsshaker/tests/module_fixtures/fn_higher_order/m.js deleted file mode 100644 index 994c0761..00000000 --- a/crates/jsshaker/tests/module_fixtures/fn_higher_order/m.js +++ /dev/null @@ -1,4 +0,0 @@ -export let v = 2; -export function apply(fn) { - v = fn(v); -} diff --git a/crates/jsshaker/tests/module_fixtures/fn_param_forms/entry.js b/crates/jsshaker/tests/module_fixtures/fn_param_forms/entry.js new file mode 100644 index 00000000..6692453a --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_param_forms/entry.js @@ -0,0 +1,7 @@ +import { v, total, n, withDefault, sum, viaArguments, apply } from "./m.js"; +withDefault(); +withDefault(9); +sum(1, 2, 3); +viaArguments(1, 2, 3); +apply((x) => x * 3); +console.log(v, total, n); diff --git a/crates/jsshaker/tests/module_fixtures/fn_param_forms/m.js b/crates/jsshaker/tests/module_fixtures/fn_param_forms/m.js new file mode 100644 index 00000000..9295397b --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/fn_param_forms/m.js @@ -0,0 +1,16 @@ +export let v = ""; +export let total = 0; +export let n = 2; + +export function withDefault(x = 5) { + v += x; +} +export function sum(...xs) { + for (const x of xs) total += x; +} +export function viaArguments() { + total += arguments.length + arguments[2]; +} +export function apply(fn) { + n = fn(n); +} diff --git a/crates/jsshaker/tests/module_fixtures/fn_rest_args/entry.js b/crates/jsshaker/tests/module_fixtures/fn_rest_args/entry.js deleted file mode 100644 index f13b2174..00000000 --- a/crates/jsshaker/tests/module_fixtures/fn_rest_args/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { total, f } from "./m.js"; -f(1, 2, 3); -console.log(total); diff --git a/crates/jsshaker/tests/module_fixtures/fn_rest_args/m.js b/crates/jsshaker/tests/module_fixtures/fn_rest_args/m.js deleted file mode 100644 index f81e7752..00000000 --- a/crates/jsshaker/tests/module_fixtures/fn_rest_args/m.js +++ /dev/null @@ -1,4 +0,0 @@ -export let total = 0; -export function f(...xs) { - for (const x of xs) total += x; -} diff --git a/crates/jsshaker/tests/module_fixtures/misc_binding_reads/entry.js b/crates/jsshaker/tests/module_fixtures/misc_binding_reads/entry.js new file mode 100644 index 00000000..97d3a303 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_binding_reads/entry.js @@ -0,0 +1,4 @@ +import { v, count, data, obj, run } from "./m.js"; +console.log(typeof v, `hello ${v}`); +run(); +console.log(typeof v, count, JSON.stringify(data), obj, obj === null); diff --git a/crates/jsshaker/tests/module_fixtures/misc_binding_reads/m.js b/crates/jsshaker/tests/module_fixtures/misc_binding_reads/m.js new file mode 100644 index 00000000..e9762e2f --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/misc_binding_reads/m.js @@ -0,0 +1,15 @@ +export let v = "str"; +export let count = 0; +export let data = null; +export let obj = { a: 1 }; + +export function run() { + count = 1; + count = 2; + count = 3; + count = count + 1; + + data = JSON.parse('{"k":[1,2]}'); + obj = null; + v = undefined; +} diff --git a/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/entry.js b/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/entry.js deleted file mode 100644 index a36d3806..00000000 --- a/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { data, load } from "./m.js"; -load(); -console.log(JSON.stringify(data)); diff --git a/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/m.js b/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/m.js deleted file mode 100644 index eab18a31..00000000 --- a/crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/m.js +++ /dev/null @@ -1,4 +0,0 @@ -export let data = null; -export function load() { - data = JSON.parse('{"k":[1,2]}'); -} diff --git a/crates/jsshaker/tests/module_fixtures/misc_many_writes/entry.js b/crates/jsshaker/tests/module_fixtures/misc_many_writes/entry.js deleted file mode 100644 index 52257677..00000000 --- a/crates/jsshaker/tests/module_fixtures/misc_many_writes/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { v, run } from "./m.js"; -run(); -console.log(v); diff --git a/crates/jsshaker/tests/module_fixtures/misc_many_writes/m.js b/crates/jsshaker/tests/module_fixtures/misc_many_writes/m.js deleted file mode 100644 index a308f85e..00000000 --- a/crates/jsshaker/tests/module_fixtures/misc_many_writes/m.js +++ /dev/null @@ -1,7 +0,0 @@ -export let v = 0; -export function run() { - v = 1; - v = 2; - v = 3; - v = v + 1; -} diff --git a/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/entry.js b/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/entry.js deleted file mode 100644 index 697fd36d..00000000 --- a/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { v, clear } from "./m.js"; -clear(); -console.log(v, v === null); diff --git a/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/m.js b/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/m.js deleted file mode 100644 index aec06d53..00000000 --- a/crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/m.js +++ /dev/null @@ -1,4 +0,0 @@ -export let v = { a: 1 }; -export function clear() { - v = null; -} diff --git a/crates/jsshaker/tests/module_fixtures/misc_template_literal/entry.js b/crates/jsshaker/tests/module_fixtures/misc_template_literal/entry.js deleted file mode 100644 index 6da74732..00000000 --- a/crates/jsshaker/tests/module_fixtures/misc_template_literal/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { name, rename } from "./m.js"; -rename(); -console.log(`hello ${name}`); diff --git a/crates/jsshaker/tests/module_fixtures/misc_template_literal/m.js b/crates/jsshaker/tests/module_fixtures/misc_template_literal/m.js deleted file mode 100644 index 1053aaa8..00000000 --- a/crates/jsshaker/tests/module_fixtures/misc_template_literal/m.js +++ /dev/null @@ -1,4 +0,0 @@ -export let name = "old"; -export function rename() { - name = "new"; -} diff --git a/crates/jsshaker/tests/module_fixtures/misc_typeof_export/entry.js b/crates/jsshaker/tests/module_fixtures/misc_typeof_export/entry.js deleted file mode 100644 index 096b96f1..00000000 --- a/crates/jsshaker/tests/module_fixtures/misc_typeof_export/entry.js +++ /dev/null @@ -1,4 +0,0 @@ -import { v, setUndef } from "./m.js"; -console.log(typeof v); -setUndef(); -console.log(typeof v); diff --git a/crates/jsshaker/tests/module_fixtures/misc_typeof_export/m.js b/crates/jsshaker/tests/module_fixtures/misc_typeof_export/m.js deleted file mode 100644 index 5aa25581..00000000 --- a/crates/jsshaker/tests/module_fixtures/misc_typeof_export/m.js +++ /dev/null @@ -1,4 +0,0 @@ -export let v = "str"; -export function setUndef() { - v = undefined; -} diff --git a/crates/jsshaker/tests/module_fixtures/op_add_sub/entry.js b/crates/jsshaker/tests/module_fixtures/op_add_sub/entry.js deleted file mode 100644 index b20111a9..00000000 --- a/crates/jsshaker/tests/module_fixtures/op_add_sub/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { n, f } from "./m.js"; -f(); -console.log(n); diff --git a/crates/jsshaker/tests/module_fixtures/op_add_sub/m.js b/crates/jsshaker/tests/module_fixtures/op_add_sub/m.js deleted file mode 100644 index 50a36b18..00000000 --- a/crates/jsshaker/tests/module_fixtures/op_add_sub/m.js +++ /dev/null @@ -1,5 +0,0 @@ -export let n = 10; -export function f() { - n += 3; - n -= 1; -} diff --git a/crates/jsshaker/tests/module_fixtures/op_compound_assign/entry.js b/crates/jsshaker/tests/module_fixtures/op_compound_assign/entry.js new file mode 100644 index 00000000..8e4bb69a --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_compound_assign/entry.js @@ -0,0 +1,3 @@ +import { n, m, a, b, c, f } from "./m.js"; +f(); +console.log(n, m, a, b, c); diff --git a/crates/jsshaker/tests/module_fixtures/op_compound_assign/m.js b/crates/jsshaker/tests/module_fixtures/op_compound_assign/m.js new file mode 100644 index 00000000..e10c0891 --- /dev/null +++ b/crates/jsshaker/tests/module_fixtures/op_compound_assign/m.js @@ -0,0 +1,27 @@ +export let n = 10; +export let m = 20; +export let a = null, + b = 1, + c = 0; + +export function f() { + n += 3; + n -= 1; + + m *= 3; + m /= 2; + m %= 7; + + n **= 2; + n &= 0xff; + n |= 1; + n ^= 3; + + n <<= 1; + n >>= 2; + n >>>= 0; + + a ??= "set"; + b &&= 9; + c ||= 7; +} diff --git a/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/entry.js b/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/entry.js deleted file mode 100644 index b20111a9..00000000 --- a/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { n, f } from "./m.js"; -f(); -console.log(n); diff --git a/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/m.js b/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/m.js deleted file mode 100644 index 72e43618..00000000 --- a/crates/jsshaker/tests/module_fixtures/op_exp_bitwise/m.js +++ /dev/null @@ -1,7 +0,0 @@ -export let n = 2; -export function f() { - n **= 5; - n &= 0xff; - n |= 1; - n ^= 3; -} diff --git a/crates/jsshaker/tests/module_fixtures/op_logical_assign/entry.js b/crates/jsshaker/tests/module_fixtures/op_logical_assign/entry.js deleted file mode 100644 index 38bef853..00000000 --- a/crates/jsshaker/tests/module_fixtures/op_logical_assign/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { a, b, c, f } from "./m.js"; -f(); -console.log(a, b, c); diff --git a/crates/jsshaker/tests/module_fixtures/op_logical_assign/m.js b/crates/jsshaker/tests/module_fixtures/op_logical_assign/m.js deleted file mode 100644 index e3c2a683..00000000 --- a/crates/jsshaker/tests/module_fixtures/op_logical_assign/m.js +++ /dev/null @@ -1,8 +0,0 @@ -export let a = null, - b = 1, - c = 0; -export function f() { - a ??= "set"; - b &&= 9; - c ||= 7; -} diff --git a/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/entry.js b/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/entry.js deleted file mode 100644 index b20111a9..00000000 --- a/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { n, f } from "./m.js"; -f(); -console.log(n); diff --git a/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/m.js b/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/m.js deleted file mode 100644 index 01532306..00000000 --- a/crates/jsshaker/tests/module_fixtures/op_mul_div_mod/m.js +++ /dev/null @@ -1,6 +0,0 @@ -export let n = 20; -export function f() { - n *= 3; - n /= 2; - n %= 7; -} diff --git a/crates/jsshaker/tests/module_fixtures/op_shifts/entry.js b/crates/jsshaker/tests/module_fixtures/op_shifts/entry.js deleted file mode 100644 index b20111a9..00000000 --- a/crates/jsshaker/tests/module_fixtures/op_shifts/entry.js +++ /dev/null @@ -1,3 +0,0 @@ -import { n, f } from "./m.js"; -f(); -console.log(n); diff --git a/crates/jsshaker/tests/module_fixtures/op_shifts/m.js b/crates/jsshaker/tests/module_fixtures/op_shifts/m.js deleted file mode 100644 index d1cba0c8..00000000 --- a/crates/jsshaker/tests/module_fixtures/op_shifts/m.js +++ /dev/null @@ -1,6 +0,0 @@ -export let n = -64; -export function f() { - n <<= 1; - n >>= 2; - n >>>= 0; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_comma_sequence__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_comma_sequence__entry.js.snap deleted file mode 100644 index cb9e362d..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@cf_comma_sequence__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/cf_comma_sequence/entry.js ---- -//#region /entry.js -import { a, b, f } from "./m.js"; -f(); -console.log(a, b); - -//#region /m.js -export let a = 0, b = 0; -export function f() { - a = (b = 2, 3); -} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_constructs__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_constructs__entry.js.snap new file mode 100644 index 00000000..83f0bdf5 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@cf_constructs__entry.js.snap @@ -0,0 +1,67 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/cf_constructs/entry.js +--- +//#region /entry.js +import { log, a, b, run } from "./m.js"; +run(); +console.log(log, a, b); + +//#region /m.js +export let log = ""; +export let a = 0, b = 0; +function bail(stop) { + log += "1"; + if (stop) return; + log += "2"; +} +function pick() { + switch (1) { + case 1: + log += "a"; + log += "b"; + break; + } +} +export function run() { + bail(true); + bail(false); + log += ";"; + log += "no;"; + pick(); + log += ";"; + for (const k in { + a: 1, + b: 2 + }) log += k; + log += ";"; + for (const x of [ + 1, + 2, + 3, + , + ]) { + if (x === 3) break; + log += x; + } + log += ";"; + outer: for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + if (j === 1) continue outer; + log += "" + i + j; + } + } + log += ";"; + let i = 0; + while (i < 3) { + let j = 0; + while (j < 2) { + log += "w"; + j++; + } + i++; + } + log += ";"; + a = (b = 2, 3); +} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_early_return__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_early_return__entry.js.snap deleted file mode 100644 index c7a800e0..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@cf_early_return__entry.js.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/cf_early_return/entry.js ---- -//#region /entry.js -import { v, f } from "./m.js"; -f(true); -f(false); -console.log(v); - -//#region /m.js -export let v = ""; -export function f(bail) { - v += "1"; - if (bail) return; - v += "2"; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_for_in__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_for_in__entry.js.snap deleted file mode 100644 index 3de92d6b..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@cf_for_in__entry.js.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/cf_for_in/entry.js ---- -//#region /entry.js -import { keys, f } from "./m.js"; -f(); -console.log(keys); - -//#region /m.js -export let keys = ""; -export function f() { - for (const k in { - a: 1, - b: 2 - }) keys += k; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_for_of_break__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_for_of_break__entry.js.snap deleted file mode 100644 index 0b0de88d..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@cf_for_of_break__entry.js.snap +++ /dev/null @@ -1,23 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/cf_for_of_break/entry.js ---- -//#region /entry.js -import { seen, f } from "./m.js"; -f(); -console.log(seen); - -//#region /m.js -export let seen = ""; -export function f() { - for (const x of [ - 1, - 2, - 3, - , - ]) { - if (x === 3) break; - seen += x; - } -} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_labeled_continue__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_labeled_continue__entry.js.snap deleted file mode 100644 index 7377a6d9..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@cf_labeled_continue__entry.js.snap +++ /dev/null @@ -1,20 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/cf_labeled_continue/entry.js ---- -//#region /entry.js -import { acc, f } from "./m.js"; -f(); -console.log(acc); - -//#region /m.js -export let acc = ""; -export function f() { - outer: for (let i = 0; i < 3; i++) { - for (let j = 0; j < 3; j++) { - if (j === 1) continue outer; - acc += "" + i + j; - } - } -} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_switch_fallthrough__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_switch_fallthrough__entry.js.snap deleted file mode 100644 index 80b3959f..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@cf_switch_fallthrough__entry.js.snap +++ /dev/null @@ -1,20 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/cf_switch_fallthrough/entry.js ---- -//#region /entry.js -import { log, f } from "./m.js"; -f(); -console.log(log); - -//#region /m.js -export let log = ""; -export function f() { - switch (1) { - case 1: - log += "a"; - log += "b"; - break; - } -} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_ternary__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_ternary__entry.js.snap deleted file mode 100644 index 4984ac3f..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@cf_ternary__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/cf_ternary/entry.js ---- -//#region /entry.js -import { v, f } from "./m.js"; -f(); -console.log(v); - -//#region /m.js -export let v = "x"; -export function f() { - v = "no"; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@cf_while_nested__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@cf_while_nested__entry.js.snap deleted file mode 100644 index face8c26..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@cf_while_nested__entry.js.snap +++ /dev/null @@ -1,23 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/cf_while_nested/entry.js ---- -//#region /entry.js -import { t, f } from "./m.js"; -f(); -console.log(t); - -//#region /m.js -export let t = 0; -export function f() { - let i = 0; - while (i < 3) { - let j = 0; - while (j < 2) { - t += 1; - j++; - } - i++; - } -} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_computed__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_computed__entry.js.snap deleted file mode 100644 index a65cedf1..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@destructure_computed__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/destructure_computed/entry.js ---- -//#region /entry.js -import { v, f } from "./m.js"; -f(); -console.log(v); - -//#region /m.js -export let v = 0; -export function f() { - ({["a"]: v} = { a: 42 }); -} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_defaults__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_defaults__entry.js.snap deleted file mode 100644 index bf98642b..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@destructure_defaults__entry.js.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/destructure_defaults/entry.js ---- -//#region /entry.js -import { a, b, f } from "./m.js"; -f(); -console.log(a, b); - -//#region /m.js -export let a = 0, b = 0; -export function f() { - [a = 5, b = 6] = [void 0]; -} -var __unused__; diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_nested__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_nested__entry.js.snap deleted file mode 100644 index 3ca6adbf..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@destructure_nested__entry.js.snap +++ /dev/null @@ -1,21 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/destructure_nested/entry.js ---- -//#region /entry.js -import { a, b, c, f } from "./m.js"; -f(); -console.log(a, b, c); - -//#region /m.js -export let a = 0, b = 0, c = 0; -export function f() { - ({a: a, b: {a: b, b: [c]}} = { - a: 1, - b: { - a: 2, - b: [3] - } - }); -} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_object_rest__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_object_rest__entry.js.snap deleted file mode 100644 index 73e8bbbb..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@destructure_object_rest__entry.js.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/destructure_object_rest/entry.js ---- -//#region /entry.js -import { one, others, f } from "./m.js"; -f(); -console.log(one, JSON.stringify(others)); - -//#region /m.js -export let one = 0, others = {}; -export function f() { - ({one: one, ...others} = { - one: 1, - two: 2, - three: 3 - }); -} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_rest__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_rest__entry.js.snap deleted file mode 100644 index 091527ce..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@destructure_rest__entry.js.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/destructure_rest/entry.js ---- -//#region /entry.js -import { head, rest, f } from "./m.js"; -f(); -console.log(head, JSON.stringify(rest)); - -//#region /m.js -export let head = 0, rest = []; -export function f() { - [head, ...rest] = [ - 1, - 2, - 3 - ]; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_swap__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_swap__entry.js.snap deleted file mode 100644 index 09064c1f..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@destructure_swap__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/destructure_swap/entry.js ---- -//#region /entry.js -import { a, b, f } from "./m.js"; -f(); -console.log(a, b); - -//#region /m.js -export let a = 1, b = 2; -export function f() { - [a, b] = [b, a]; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@destructure_targets__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@destructure_targets__entry.js.snap new file mode 100644 index 00000000..1eee7f19 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@destructure_targets__entry.js.snap @@ -0,0 +1,38 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/destructure_targets/entry.js +--- +//#region /entry.js +import { a, b, c, head, rest, one, others, dyn, f } from "./m.js"; +f(); +console.log(a, b, c, head, JSON.stringify(rest), one, JSON.stringify(others), dyn); + +//#region /m.js +export let a = 1, b = 2, c = 0; +export let head = 0, rest = []; +export let one = 0, others = {}; +export let dyn = 0; +export function f() { + [a, b] = [b, a]; + [a = 5, c = 6] = [void 0]; + ({a: b, b: {a: c, b: [head]}} = { + a: 1, + b: { + a: 2, + b: [3] + } + }); + [head, ...rest] = [ + 1, + 2, + 3 + ]; + ({one: one, ...others} = { + one: 1, + two: 2, + three: 3 + }); + ({["a"]: dyn} = { a: 42 }); +} +var __unused__; diff --git a/crates/jsshaker/tests/snapshots/module_test@exc_flow__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@exc_flow__entry.js.snap new file mode 100644 index 00000000..d2c9bf41 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@exc_flow__entry.js.snap @@ -0,0 +1,39 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/exc_flow/entry.js +--- +//#region /entry.js +import { v, count, rethrow, loop } from "./m.js"; +try { + rethrow(); +} catch (e) { + console.log("caught", e.message); +} +loop(); +console.log(v, count); + +//#region /m.js +export let v = ""; +export let count = 0; +export function rethrow() { + try { + v = "try"; + throw new Error("boom"); + } catch (e) { + v += "|catch"; + throw e; + } finally { + v += "|finally"; + } +} +export function loop() { + for (let i = 0; i < 5; i++) { + try { + if (i % 2) throw new Error(); + count += 10; + } catch { + count += 1; + } + } +} diff --git a/crates/jsshaker/tests/snapshots/module_test@exc_rethrow__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@exc_rethrow__entry.js.snap deleted file mode 100644 index abc1f0a4..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@exc_rethrow__entry.js.snap +++ /dev/null @@ -1,27 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/exc_rethrow/entry.js ---- -//#region /entry.js -import { v, f } from "./m.js"; -try { - f(); -} catch (e) { - console.log("caught", e.message); -} -console.log(v); - -//#region /m.js -export let v = ""; -export function f() { - try { - v = "try"; - throw new Error("boom"); - } catch (e) { - v += "|catch"; - throw e; - } finally { - v += "|finally"; - } -} diff --git a/crates/jsshaker/tests/snapshots/module_test@exc_throw_in_loop__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@exc_throw_in_loop__entry.js.snap deleted file mode 100644 index b29e9594..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@exc_throw_in_loop__entry.js.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/exc_throw_in_loop/entry.js ---- -//#region /entry.js -import { count, f } from "./m.js"; -f(); -console.log(count); - -//#region /m.js -export let count = 0; -export function f() { - for (let i = 0; i < 5; i++) { - try { - if (i % 2) throw new Error(); - count += 10; - } catch { - count += 1; - } - } -} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_arguments_object__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_arguments_object__entry.js.snap deleted file mode 100644 index 68b9eb4c..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@fn_arguments_object__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/fn_arguments_object/entry.js ---- -//#region /entry.js -import { n, f } from "./m.js"; -f(1, 2, 3); -console.log(n); - -//#region /m.js -export let n = 0; -export function f() { - n = arguments.length + arguments[2]; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_default_params__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_default_params__entry.js.snap deleted file mode 100644 index 98121740..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@fn_default_params__entry.js.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/fn_default_params/entry.js ---- -//#region /entry.js -import { v, f } from "./m.js"; -f(); -f(9); -console.log(v); - -//#region /m.js -export let v = ""; -export function f(x = 5) { - v += x; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_higher_order__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_higher_order__entry.js.snap deleted file mode 100644 index d86d8c44..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@fn_higher_order__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/fn_higher_order/entry.js ---- -//#region /entry.js -import { v, apply } from "./m.js"; -apply((x) => x * 3); -console.log(v); - -//#region /m.js -export let v = 2; -export function apply(fn) { - v = fn(v); -} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_param_forms__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_param_forms__entry.js.snap new file mode 100644 index 00000000..4b53c00c --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@fn_param_forms__entry.js.snap @@ -0,0 +1,30 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/fn_param_forms/entry.js +--- +//#region /entry.js +import { v, total, n, withDefault, sum, viaArguments, apply } from "./m.js"; +withDefault(); +withDefault(9); +sum(1, 2, 3); +viaArguments(1, 2, 3); +apply((x) => x * 3); +console.log(v, total, n); + +//#region /m.js +export let v = ""; +export let total = 0; +export let n = 2; +export function withDefault(x = 5) { + v += x; +} +export function sum(...xs) { + for (const x of xs) total += x; +} +export function viaArguments() { + total += arguments.length + arguments[2]; +} +export function apply(fn) { + n = fn(n); +} diff --git a/crates/jsshaker/tests/snapshots/module_test@fn_rest_args__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@fn_rest_args__entry.js.snap deleted file mode 100644 index f706e792..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@fn_rest_args__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/fn_rest_args/entry.js ---- -//#region /entry.js -import { total, f } from "./m.js"; -f(1, 2, 3); -console.log(total); - -//#region /m.js -export let total = 0; -export function f(...xs) { - for (const x of xs) total += x; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_binding_reads__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_binding_reads__entry.js.snap new file mode 100644 index 00000000..4904c71b --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@misc_binding_reads__entry.js.snap @@ -0,0 +1,25 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/misc_binding_reads/entry.js +--- +//#region /entry.js +import { v, count, data, obj, run } from "./m.js"; +console.log(typeof v, `hello ${v}`); +run(); +console.log(typeof v, count, JSON.stringify(data), obj, obj === null); + +//#region /m.js +export let v = "str"; +export let count = 0; +export let data = null; +export let obj = { a: 1 }; +export function run() { + count = 1; + count = 2; + count = 3; + count = count + 1; + data = JSON.parse("{\"k\":[1,2]}"); + obj = null; + v = void 0; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_json_roundtrip__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_json_roundtrip__entry.js.snap deleted file mode 100644 index 51cb6298..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@misc_json_roundtrip__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/misc_json_roundtrip/entry.js ---- -//#region /entry.js -import { data, load } from "./m.js"; -load(); -console.log(JSON.stringify(data)); - -//#region /m.js -export let data = null; -export function load() { - data = JSON.parse("{\"k\":[1,2]}"); -} diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_many_writes__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_many_writes__entry.js.snap deleted file mode 100644 index f6776077..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@misc_many_writes__entry.js.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/misc_many_writes/entry.js ---- -//#region /entry.js -import { v, run } from "./m.js"; -run(); -console.log(v); - -//#region /m.js -export let v = 0; -export function run() { - v = 1; - v = 2; - v = 3; - v = v + 1; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_reassign_to_null__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_reassign_to_null__entry.js.snap deleted file mode 100644 index af791fe3..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@misc_reassign_to_null__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/misc_reassign_to_null/entry.js ---- -//#region /entry.js -import { v, clear } from "./m.js"; -clear(); -console.log(v, v === null); - -//#region /m.js -export let v = { a: 1 }; -export function clear() { - v = null; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_template_literal__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_template_literal__entry.js.snap deleted file mode 100644 index 347c7fc2..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@misc_template_literal__entry.js.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/misc_template_literal/entry.js ---- -//#region /entry.js -import { name, rename } from "./m.js"; -rename(); -console.log(`hello ${name}`); - -//#region /m.js -export let name = "old"; -export function rename() { - name = "new"; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@misc_typeof_export__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@misc_typeof_export__entry.js.snap deleted file mode 100644 index 2ce04a43..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@misc_typeof_export__entry.js.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/misc_typeof_export/entry.js ---- -//#region /entry.js -import { v, setUndef } from "./m.js"; -console.log(typeof v); -setUndef(); -console.log(typeof v); - -//#region /m.js -export let v = "str"; -export function setUndef() { - v = void 0; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@op_add_sub__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_add_sub__entry.js.snap deleted file mode 100644 index 79483dc8..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@op_add_sub__entry.js.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/op_add_sub/entry.js ---- -//#region /entry.js -import { n, f } from "./m.js"; -f(); -console.log(n); - -//#region /m.js -export let n = 10; -export function f() { - n += 3; - n -= 1; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@op_compound_assign__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_compound_assign__entry.js.snap new file mode 100644 index 00000000..3538b181 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/module_test@op_compound_assign__entry.js.snap @@ -0,0 +1,31 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake_modules(dir) +input_file: crates/jsshaker/tests/module_fixtures/op_compound_assign/entry.js +--- +//#region /entry.js +import { n, m, a, b, c, f } from "./m.js"; +f(); +console.log(n, m, a, b, c); + +//#region /m.js +export let n = 10; +export let m = 20; +export let a = null, b = 1, c = 0; +export function f() { + n += 3; + n -= 1; + m *= 3; + m /= 2; + m %= 7; + n **= 2; + n &= 255; + n |= 1; + n ^= 3; + n <<= 1; + n >>= 2; + n >>>= 0; + a ??= "set"; + b &&= 9; + c ||= 7; +} diff --git a/crates/jsshaker/tests/snapshots/module_test@op_exp_bitwise__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_exp_bitwise__entry.js.snap deleted file mode 100644 index 29e7cb36..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@op_exp_bitwise__entry.js.snap +++ /dev/null @@ -1,18 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/op_exp_bitwise/entry.js ---- -//#region /entry.js -import { n, f } from "./m.js"; -f(); -console.log(n); - -//#region /m.js -export let n = 2; -export function f() { - n **= 5; - n &= 255; - n |= 1; - n ^= 3; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@op_logical_assign__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_logical_assign__entry.js.snap deleted file mode 100644 index 7157b2d2..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@op_logical_assign__entry.js.snap +++ /dev/null @@ -1,17 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/op_logical_assign/entry.js ---- -//#region /entry.js -import { a, b, c, f } from "./m.js"; -f(); -console.log(a, b, c); - -//#region /m.js -export let a = null, b = 1, c = 0; -export function f() { - a ??= "set"; - b &&= 9; - c ||= 7; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@op_mul_div_mod__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_mul_div_mod__entry.js.snap deleted file mode 100644 index 8a354f51..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@op_mul_div_mod__entry.js.snap +++ /dev/null @@ -1,17 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/op_mul_div_mod/entry.js ---- -//#region /entry.js -import { n, f } from "./m.js"; -f(); -console.log(n); - -//#region /m.js -export let n = 20; -export function f() { - n *= 3; - n /= 2; - n %= 7; -} diff --git a/crates/jsshaker/tests/snapshots/module_test@op_shifts__entry.js.snap b/crates/jsshaker/tests/snapshots/module_test@op_shifts__entry.js.snap deleted file mode 100644 index 5e8e984e..00000000 --- a/crates/jsshaker/tests/snapshots/module_test@op_shifts__entry.js.snap +++ /dev/null @@ -1,17 +0,0 @@ ---- -source: crates/jsshaker/tests/mod.rs -expression: do_tree_shake_modules(dir) -input_file: crates/jsshaker/tests/module_fixtures/op_shifts/entry.js ---- -//#region /entry.js -import { n, f } from "./m.js"; -f(); -console.log(n); - -//#region /m.js -export let n = -64; -export function f() { - n <<= 1; - n >>= 2; - n >>>= 0; -} diff --git a/crates/jsshaker/tests/snapshots/test@chain_expression.js.snap b/crates/jsshaker/tests/snapshots/test@chain_expression.js.snap index 99c6cf47..079ec165 100644 --- a/crates/jsshaker/tests/snapshots/test@chain_expression.js.snap +++ b/crates/jsshaker/tests/snapshots/test@chain_expression.js.snap @@ -9,6 +9,6 @@ export function main(unknown) { effect(a.a?.c, void 0, NaN); } export async function short_circuit(effectful) { - await undefined; - await (effectful(), undefined); + await void 0; + await (effectful(), void 0); } diff --git a/crates/jsshaker/tests/snapshots/test@class__private_brand_unused.js.snap b/crates/jsshaker/tests/snapshots/test@class__private_brand_unused.js.snap new file mode 100644 index 00000000..b0fd0ea2 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/test@class__private_brand_unused.js.snap @@ -0,0 +1,14 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake(input) +input_file: crates/jsshaker/tests/fixtures/class/private_brand_unused.js +--- +const results = []; +class Ghost { + #tag; + static a(o) { + return #tag in o; + } +} +results.push(Ghost.a({})); +export default results; diff --git a/crates/jsshaker/tests/snapshots/test@class__private_inherit.js.snap b/crates/jsshaker/tests/snapshots/test@class__private_inherit.js.snap new file mode 100644 index 00000000..068958f2 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/test@class__private_inherit.js.snap @@ -0,0 +1,25 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake(input) +input_file: crates/jsshaker/tests/fixtures/class/private_inherit.js +--- +const results = []; +class Base { + a() { + return; + } +} +class Derived extends Base { + a() { + return ["base", "derived"]; + } +} +const d = new Derived(); +results.push("base"); +results.push(...d.a()); +try { + Base.prototype.a.call(); +} catch (e) { + results.push(e.constructor.name); +} +export default results; diff --git a/crates/jsshaker/tests/snapshots/test@class__private_shadow.js.snap b/crates/jsshaker/tests/snapshots/test@class__private_shadow.js.snap new file mode 100644 index 00000000..90428218 --- /dev/null +++ b/crates/jsshaker/tests/snapshots/test@class__private_shadow.js.snap @@ -0,0 +1,8 @@ +--- +source: crates/jsshaker/tests/mod.rs +expression: do_tree_shake(input) +input_file: crates/jsshaker/tests/fixtures/class/private_shadow.js +--- +const results = []; +results.push("outer", "inner", "outer", "other"); +export default results; diff --git a/packages/napi/src/lib.rs b/packages/napi/src/lib.rs index 787d7ff2..9803730d 100644 --- a/packages/napi/src/lib.rs +++ b/packages/napi/src/lib.rs @@ -92,11 +92,11 @@ impl From for Chunk { } } -macro_rules! apply_bool_options { - ($config:ident, $options:ident, $($field:ident),+ $(,)?) => { +macro_rules! apply_options { + ($config:ident, $options:ident, $($field:ident $(as $ty:ty)?),+ $(,)?) => { $( if let Some(value) = $options.$field { - $config.$field = value; + $config.$field = value $(as $ty)?; } )+ }; @@ -137,16 +137,12 @@ fn resolve_options(vfs: F, options: Options) -> JsShakerOptions { _ => panic!("Invalid property_mangling option {:?}", property_mangling), }; } - if let Some(length) = options.max_folding_string_length { - config.max_folding_string_length = length as usize; - } - if let Some(depth) = options.max_recursion_depth { - config.max_recursion_depth = depth as usize; - } - apply_bool_options!( + apply_options!( config, options, + max_folding_string_length as usize, + max_recursion_depth as usize, branch_folding, remember_exhausted_variables, eager_exhaustive_callbacks,