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/3] 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/3] 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/3] 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))); }