├── .editorconfig ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── main.yml │ └── publish.yml ├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── modules.xml ├── prettier.xml ├── typescript-transform-paths.iml └── vcs.xml ├── .prettierignore ├── .yarnrc.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── eslint.config.mjs ├── nx-transformer.js ├── package.json ├── register.js ├── renovate.json ├── src ├── harmony │ ├── harmony-factory.ts │ ├── index.ts │ ├── utils.ts │ └── versions │ │ ├── four-seven.ts │ │ ├── index.ts │ │ └── three-eight.ts ├── index.ts ├── plugins │ ├── index.ts │ └── nx-transformer-plugin.ts ├── register-entry.ts ├── register.ts ├── transformer.ts ├── types.ts ├── utils │ ├── elide-import-export.ts │ ├── general-utils.ts │ ├── get-relative-path.ts │ ├── index.ts │ ├── resolve-module-name.ts │ ├── resolve-path-update-node.ts │ └── ts-helpers.ts └── visitor.ts ├── test ├── config.ts ├── jest.config.ts ├── package.json ├── prepare.mjs ├── projects │ ├── extras │ │ ├── package.json │ │ ├── src │ │ │ ├── id.ts │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── general │ │ ├── circular │ │ │ ├── a.ts │ │ │ └── b.ts │ │ ├── core │ │ │ └── index.ts │ │ ├── dynamic │ │ │ ├── logger.ts │ │ │ └── tester.ts │ │ ├── package.json │ │ ├── secondary │ │ │ └── hello.ts │ │ ├── tsconfig.json │ │ └── utils │ │ │ ├── index.ts │ │ │ ├── subs.ts │ │ │ ├── sum.ts │ │ │ ├── types-only.ts │ │ │ └── utils.native.ts │ ├── nx │ │ ├── nx.json │ │ ├── package.json │ │ ├── packages │ │ │ ├── library1 │ │ │ │ ├── package.json │ │ │ │ ├── project.json │ │ │ │ ├── src │ │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── tsconfig.lib.json │ │ │ └── library2 │ │ │ │ ├── package.json │ │ │ │ ├── project.json │ │ │ │ ├── src │ │ │ │ └── index.ts │ │ │ │ ├── tsconfig.json │ │ │ │ └── tsconfig.lib.json │ │ ├── tsconfig.base.json │ │ └── workspace.json │ ├── project-ref │ │ ├── a │ │ │ ├── index.ts │ │ │ └── tsconfig.json │ │ ├── b │ │ │ ├── index.ts │ │ │ ├── local │ │ │ │ └── index.ts │ │ │ └── tsconfig.json │ │ ├── package.json │ │ ├── tsconfig.base.json │ │ └── tsconfig.json │ └── specific │ │ ├── generated │ │ └── dir │ │ │ └── gen-file.ts │ │ ├── package.json │ │ ├── src │ │ ├── data.json │ │ ├── dir │ │ │ └── src-file.ts │ │ ├── excluded-file.ts │ │ ├── excluded │ │ │ └── ex.ts │ │ ├── general.ts │ │ ├── index.ts │ │ ├── module-augment.ts │ │ ├── packages │ │ │ ├── pkg-a │ │ │ │ ├── index.d.ts │ │ │ │ ├── index.js │ │ │ │ ├── package.json │ │ │ │ └── sub-pkg │ │ │ │ │ ├── main.d.ts │ │ │ │ │ ├── main.js │ │ │ │ │ └── package.json │ │ │ ├── pkg-b │ │ │ │ ├── package.json │ │ │ │ └── subdir │ │ │ │ │ ├── main.d.ts │ │ │ │ │ └── main.js │ │ │ └── pkg-c │ │ │ │ ├── main.d.ts │ │ │ │ ├── main.js │ │ │ │ └── package.json │ │ ├── sub-packages.ts │ │ ├── tags.ts │ │ └── type-elision │ │ │ ├── a.ts │ │ │ └── index.ts │ │ └── tsconfig.json ├── tests │ ├── extras.test.ts │ ├── get-match-portion.test.ts │ ├── nx.test.ts │ ├── project-ref.test.ts │ ├── register.test.ts │ └── transformer │ │ ├── general.test.ts │ │ └── specific.test.ts ├── tsconfig.json ├── utils │ ├── helpers.ts │ ├── index.ts │ └── module-not-found-error.ts └── yarn.lock ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | max_line_length = 120 10 | ij_visual_guides = 120 11 | ij_wrap_on_typing = false 12 | end_of_line = lf 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | [.editorconfig] 18 | ij_editorconfig_align_group_field_declarations = false 19 | ij_editorconfig_space_after_colon = false 20 | ij_editorconfig_space_after_comma = true 21 | ij_editorconfig_space_before_colon = false 22 | ij_editorconfig_space_before_comma = false 23 | ij_editorconfig_spaces_around_assignment_operators = true 24 | 25 | 26 | [{*.js, *.cjs}] 27 | tab_width = 2 28 | ij_continuation_indent_size = 2 29 | ij_javascript_align_imports = false 30 | ij_javascript_align_multiline_array_initializer_expression = false 31 | ij_javascript_align_multiline_binary_operation = false 32 | ij_javascript_align_multiline_chained_methods = false 33 | ij_javascript_align_multiline_extends_list = false 34 | ij_javascript_align_multiline_for = true 35 | ij_javascript_align_multiline_parameters = true 36 | ij_javascript_align_multiline_parameters_in_calls = false 37 | ij_javascript_align_multiline_ternary_operation = false 38 | ij_javascript_align_object_properties = 0 39 | ij_javascript_align_union_types = false 40 | ij_javascript_align_var_statements = 0 41 | ij_javascript_array_initializer_new_line_after_left_brace = false 42 | ij_javascript_array_initializer_right_brace_on_new_line = false 43 | ij_javascript_array_initializer_wrap = off 44 | ij_javascript_assignment_wrap = off 45 | ij_javascript_binary_operation_sign_on_next_line = false 46 | ij_javascript_binary_operation_wrap = off 47 | ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**/*, @angular/material, @angular/material/typings/** 48 | ij_javascript_blank_lines_after_imports = 2 49 | ij_javascript_blank_lines_around_class = 1 50 | ij_javascript_blank_lines_around_field = 0 51 | ij_javascript_blank_lines_around_function = 1 52 | ij_javascript_blank_lines_around_method = 0 53 | ij_javascript_block_brace_style = end_of_line 54 | ij_javascript_call_parameters_new_line_after_left_paren = false 55 | ij_javascript_call_parameters_right_paren_on_new_line = false 56 | ij_javascript_call_parameters_wrap = off 57 | ij_javascript_catch_on_new_line = true 58 | ij_javascript_chained_call_dot_on_new_line = true 59 | ij_javascript_class_brace_style = next_line_if_wrapped 60 | ij_javascript_comma_on_new_line = false 61 | ij_javascript_do_while_brace_force = never 62 | ij_javascript_else_on_new_line = false 63 | ij_javascript_enforce_trailing_comma = keep 64 | ij_javascript_extends_keyword_wrap = off 65 | ij_javascript_extends_list_wrap = off 66 | ij_javascript_field_prefix = _ 67 | ij_javascript_file_name_style = relaxed 68 | ij_javascript_finally_on_new_line = true 69 | ij_javascript_for_brace_force = never 70 | ij_javascript_for_statement_new_line_after_left_paren = false 71 | ij_javascript_for_statement_right_paren_on_new_line = false 72 | ij_javascript_for_statement_wrap = off 73 | ij_javascript_force_quote_style = false 74 | ij_javascript_force_semicolon_style = false 75 | ij_javascript_function_expression_brace_style = next_line_if_wrapped 76 | ij_javascript_if_brace_force = never 77 | ij_javascript_import_merge_members = global 78 | ij_javascript_import_prefer_absolute_path = global 79 | ij_javascript_import_sort_members = true 80 | ij_javascript_import_sort_module_name = false 81 | ij_javascript_import_use_node_resolution = true 82 | ij_javascript_imports_wrap = normal 83 | ij_javascript_indent_case_from_switch = true 84 | ij_javascript_indent_chained_calls = true 85 | ij_javascript_indent_package_children = 0 86 | ij_javascript_jsx_attribute_value = braces 87 | ij_javascript_keep_blank_lines_in_code = 2 88 | ij_javascript_keep_first_column_comment = true 89 | ij_javascript_keep_indents_on_empty_lines = false 90 | ij_javascript_keep_line_breaks = true 91 | ij_javascript_keep_simple_blocks_in_one_line = true 92 | ij_javascript_keep_simple_methods_in_one_line = true 93 | ij_javascript_line_comment_add_space = true 94 | ij_javascript_line_comment_at_first_column = false 95 | ij_javascript_method_brace_style = end_of_line 96 | ij_javascript_method_call_chain_wrap = off 97 | ij_javascript_method_parameters_new_line_after_left_paren = false 98 | ij_javascript_method_parameters_right_paren_on_new_line = false 99 | ij_javascript_method_parameters_wrap = off 100 | ij_javascript_object_literal_wrap = on_every_item 101 | ij_javascript_parentheses_expression_new_line_after_left_paren = false 102 | ij_javascript_parentheses_expression_right_paren_on_new_line = false 103 | ij_javascript_place_assignment_sign_on_next_line = false 104 | ij_javascript_prefer_as_type_cast = false 105 | ij_javascript_prefer_parameters_wrap = false 106 | ij_javascript_reformat_c_style_comments = false 107 | ij_javascript_space_after_colon = true 108 | ij_javascript_space_after_comma = true 109 | ij_javascript_space_after_dots_in_rest_parameter = false 110 | ij_javascript_space_after_generator_mult = true 111 | ij_javascript_space_after_property_colon = true 112 | ij_javascript_space_after_quest = true 113 | ij_javascript_space_after_type_colon = true 114 | ij_javascript_space_after_unary_not = false 115 | ij_javascript_space_before_async_arrow_lparen = true 116 | ij_javascript_space_before_catch_keyword = true 117 | ij_javascript_space_before_catch_left_brace = true 118 | ij_javascript_space_before_catch_parentheses = true 119 | ij_javascript_space_before_class_lbrace = true 120 | ij_javascript_space_before_class_left_brace = true 121 | ij_javascript_space_before_colon = true 122 | ij_javascript_space_before_comma = false 123 | ij_javascript_space_before_do_left_brace = true 124 | ij_javascript_space_before_else_keyword = true 125 | ij_javascript_space_before_else_left_brace = true 126 | ij_javascript_space_before_finally_keyword = true 127 | ij_javascript_space_before_finally_left_brace = true 128 | ij_javascript_space_before_for_left_brace = true 129 | ij_javascript_space_before_for_parentheses = true 130 | ij_javascript_space_before_for_semicolon = false 131 | ij_javascript_space_before_function_left_parenth = true 132 | ij_javascript_space_before_generator_mult = false 133 | ij_javascript_space_before_if_left_brace = true 134 | ij_javascript_space_before_if_parentheses = true 135 | ij_javascript_space_before_method_call_parentheses = false 136 | ij_javascript_space_before_method_left_brace = true 137 | ij_javascript_space_before_method_parentheses = false 138 | ij_javascript_space_before_property_colon = false 139 | ij_javascript_space_before_quest = true 140 | ij_javascript_space_before_switch_left_brace = true 141 | ij_javascript_space_before_switch_parentheses = true 142 | ij_javascript_space_before_try_left_brace = true 143 | ij_javascript_space_before_type_colon = false 144 | ij_javascript_space_before_unary_not = false 145 | ij_javascript_space_before_while_keyword = true 146 | ij_javascript_space_before_while_left_brace = true 147 | ij_javascript_space_before_while_parentheses = true 148 | ij_javascript_spaces_around_additive_operators = true 149 | ij_javascript_spaces_around_arrow_function_operator = true 150 | ij_javascript_spaces_around_assignment_operators = true 151 | ij_javascript_spaces_around_bitwise_operators = true 152 | ij_javascript_spaces_around_equality_operators = true 153 | ij_javascript_spaces_around_logical_operators = true 154 | ij_javascript_spaces_around_multiplicative_operators = true 155 | ij_javascript_spaces_around_relational_operators = true 156 | ij_javascript_spaces_around_shift_operators = true 157 | ij_javascript_spaces_around_unary_operator = false 158 | ij_javascript_spaces_within_array_initializer_brackets = true 159 | ij_javascript_spaces_within_brackets = false 160 | ij_javascript_spaces_within_catch_parentheses = false 161 | ij_javascript_spaces_within_for_parentheses = false 162 | ij_javascript_spaces_within_if_parentheses = false 163 | ij_javascript_spaces_within_imports = true 164 | ij_javascript_spaces_within_interpolation_expressions = false 165 | ij_javascript_spaces_within_method_call_parentheses = false 166 | ij_javascript_spaces_within_method_parentheses = false 167 | ij_javascript_spaces_within_object_literal_braces = true 168 | ij_javascript_spaces_within_object_type_braces = true 169 | ij_javascript_spaces_within_parentheses = false 170 | ij_javascript_spaces_within_switch_parentheses = false 171 | ij_javascript_spaces_within_type_assertion = false 172 | ij_javascript_spaces_within_union_types = true 173 | ij_javascript_spaces_within_while_parentheses = false 174 | ij_javascript_special_else_if_treatment = true 175 | ij_javascript_ternary_operation_signs_on_next_line = false 176 | ij_javascript_ternary_operation_wrap = off 177 | ij_javascript_union_types_wrap = on_every_item 178 | ij_javascript_use_chained_calls_group_indents = false 179 | ij_javascript_use_double_quotes = true 180 | ij_javascript_use_explicit_js_extension = global 181 | ij_javascript_use_path_mapping = always 182 | ij_javascript_use_public_modifier = false 183 | ij_javascript_use_semicolon_after_statement = true 184 | ij_javascript_var_declaration_wrap = normal 185 | ij_javascript_while_brace_force = never 186 | ij_javascript_while_on_new_line = false 187 | ij_javascript_wrap_comments = false 188 | 189 | 190 | [{*.zsh, *.bash, *.sh}] 191 | ij_shell_binary_ops_start_line = false 192 | ij_shell_keep_column_alignment_padding = false 193 | ij_shell_minify_program = false 194 | ij_shell_redirect_followed_by_space = false 195 | ij_shell_switch_cases_indented = false 196 | 197 | 198 | [{.babelrc, .prettierrc, .stylelintrc, .eslintrc, jest.config, *.json, *.jsb3, *.jsb2, *.bowerrc}] 199 | ij_json_keep_blank_lines_in_code = 2 200 | ij_json_keep_indents_on_empty_lines = false 201 | ij_json_keep_line_breaks = true 202 | ij_json_space_after_colon = true 203 | ij_json_space_after_comma = true 204 | ij_json_space_before_colon = true 205 | ij_json_space_before_comma = false 206 | ij_json_spaces_within_braces = true 207 | ij_json_spaces_within_brackets = true 208 | ij_json_wrap_long_lines = false 209 | 210 | [{*.ats, *.ts, *.tsx}] 211 | tab_width = 2 212 | ij_continuation_indent_size = 2 213 | ij_typescript_align_imports = false 214 | ij_typescript_align_multiline_array_initializer_expression = false 215 | ij_typescript_align_multiline_binary_operation = false 216 | ij_typescript_align_multiline_chained_methods = false 217 | ij_typescript_align_multiline_extends_list = false 218 | ij_typescript_align_multiline_for = true 219 | ij_typescript_align_multiline_parameters = true 220 | ij_typescript_align_multiline_parameters_in_calls = false 221 | ij_typescript_align_multiline_ternary_operation = false 222 | ij_typescript_align_object_properties = 0 223 | ij_typescript_align_union_types = false 224 | ij_typescript_align_var_statements = 0 225 | ij_typescript_array_initializer_new_line_after_left_brace = false 226 | ij_typescript_array_initializer_right_brace_on_new_line = false 227 | ij_typescript_array_initializer_wrap = off 228 | ij_typescript_assignment_wrap = off 229 | ij_typescript_binary_operation_sign_on_next_line = false 230 | ij_typescript_binary_operation_wrap = off 231 | ij_typescript_blacklist_imports = rxjs/Rx, node_modules/**/*, @angular/material, @angular/material/typings/** 232 | ij_typescript_blank_lines_after_imports = 2 233 | ij_typescript_blank_lines_around_class = 1 234 | ij_typescript_blank_lines_around_field = 0 235 | ij_typescript_blank_lines_around_field_in_interface = 0 236 | ij_typescript_blank_lines_around_function = 0 237 | ij_typescript_blank_lines_around_method = 0 238 | ij_typescript_blank_lines_around_method_in_interface = 0 239 | ij_typescript_block_brace_style = end_of_line 240 | ij_typescript_call_parameters_new_line_after_left_paren = false 241 | ij_typescript_call_parameters_right_paren_on_new_line = false 242 | ij_typescript_call_parameters_wrap = off 243 | ij_typescript_catch_on_new_line = true 244 | ij_typescript_chained_call_dot_on_new_line = true 245 | ij_typescript_class_brace_style = next_line_if_wrapped 246 | ij_typescript_comma_on_new_line = false 247 | ij_typescript_do_while_brace_force = never 248 | ij_typescript_else_on_new_line = false 249 | ij_typescript_enforce_trailing_comma = keep 250 | ij_typescript_extends_keyword_wrap = off 251 | ij_typescript_extends_list_wrap = off 252 | ij_typescript_field_prefix = _ 253 | ij_typescript_file_name_style = relaxed 254 | ij_typescript_finally_on_new_line = true 255 | ij_typescript_for_brace_force = never 256 | ij_typescript_for_statement_new_line_after_left_paren = false 257 | ij_typescript_for_statement_right_paren_on_new_line = false 258 | ij_typescript_for_statement_wrap = off 259 | ij_typescript_force_quote_style = true 260 | ij_typescript_force_semicolon_style = false 261 | ij_typescript_function_expression_brace_style = next_line_if_wrapped 262 | ij_typescript_if_brace_force = never 263 | ij_typescript_import_merge_members = global 264 | ij_typescript_import_prefer_absolute_path = global 265 | ij_typescript_import_sort_members = true 266 | ij_typescript_import_sort_module_name = false 267 | ij_typescript_import_use_node_resolution = true 268 | ij_typescript_imports_wrap = normal 269 | ij_typescript_indent_case_from_switch = true 270 | ij_typescript_indent_chained_calls = true 271 | ij_typescript_indent_package_children = 0 272 | ij_typescript_jsdoc_include_types = false 273 | ij_typescript_jsx_attribute_value = braces 274 | ij_typescript_keep_blank_lines_in_code = 2 275 | ij_typescript_keep_first_column_comment = true 276 | ij_typescript_keep_indents_on_empty_lines = false 277 | ij_typescript_keep_line_breaks = true 278 | ij_typescript_keep_simple_blocks_in_one_line = true 279 | ij_typescript_keep_simple_methods_in_one_line = true 280 | ij_typescript_line_comment_add_space = true 281 | ij_typescript_line_comment_at_first_column = false 282 | ij_typescript_method_brace_style = next_line_if_wrapped 283 | ij_typescript_method_call_chain_wrap = off 284 | ij_typescript_method_parameters_new_line_after_left_paren = false 285 | ij_typescript_method_parameters_right_paren_on_new_line = false 286 | ij_typescript_method_parameters_wrap = off 287 | ij_typescript_object_literal_wrap = on_every_item 288 | ij_typescript_parentheses_expression_new_line_after_left_paren = false 289 | ij_typescript_parentheses_expression_right_paren_on_new_line = false 290 | ij_typescript_place_assignment_sign_on_next_line = false 291 | ij_typescript_prefer_as_type_cast = false 292 | ij_typescript_prefer_parameters_wrap = false 293 | ij_typescript_reformat_c_style_comments = false 294 | ij_typescript_space_after_colon = true 295 | ij_typescript_space_after_comma = true 296 | ij_typescript_space_after_dots_in_rest_parameter = false 297 | ij_typescript_space_after_generator_mult = true 298 | ij_typescript_space_after_property_colon = true 299 | ij_typescript_space_after_quest = true 300 | ij_typescript_space_after_type_colon = true 301 | ij_typescript_space_after_unary_not = false 302 | ij_typescript_space_before_async_arrow_lparen = true 303 | ij_typescript_space_before_catch_keyword = true 304 | ij_typescript_space_before_catch_left_brace = true 305 | ij_typescript_space_before_catch_parentheses = true 306 | ij_typescript_space_before_class_lbrace = true 307 | ij_typescript_space_before_class_left_brace = true 308 | ij_typescript_space_before_colon = true 309 | ij_typescript_space_before_comma = false 310 | ij_typescript_space_before_do_left_brace = true 311 | ij_typescript_space_before_else_keyword = true 312 | ij_typescript_space_before_else_left_brace = true 313 | ij_typescript_space_before_finally_keyword = true 314 | ij_typescript_space_before_finally_left_brace = true 315 | ij_typescript_space_before_for_left_brace = true 316 | ij_typescript_space_before_for_parentheses = true 317 | ij_typescript_space_before_for_semicolon = false 318 | ij_typescript_space_before_function_left_parenth = true 319 | ij_typescript_space_before_generator_mult = false 320 | ij_typescript_space_before_if_left_brace = true 321 | ij_typescript_space_before_if_parentheses = true 322 | ij_typescript_space_before_method_call_parentheses = false 323 | ij_typescript_space_before_method_left_brace = true 324 | ij_typescript_space_before_method_parentheses = false 325 | ij_typescript_space_before_property_colon = false 326 | ij_typescript_space_before_quest = true 327 | ij_typescript_space_before_switch_left_brace = true 328 | ij_typescript_space_before_switch_parentheses = true 329 | ij_typescript_space_before_try_left_brace = true 330 | ij_typescript_space_before_type_colon = false 331 | ij_typescript_space_before_unary_not = false 332 | ij_typescript_space_before_while_keyword = true 333 | ij_typescript_space_before_while_left_brace = true 334 | ij_typescript_space_before_while_parentheses = true 335 | ij_typescript_spaces_around_additive_operators = true 336 | ij_typescript_spaces_around_arrow_function_operator = true 337 | ij_typescript_spaces_around_assignment_operators = true 338 | ij_typescript_spaces_around_bitwise_operators = true 339 | ij_typescript_spaces_around_equality_operators = true 340 | ij_typescript_spaces_around_logical_operators = true 341 | ij_typescript_spaces_around_multiplicative_operators = true 342 | ij_typescript_spaces_around_relational_operators = true 343 | ij_typescript_spaces_around_shift_operators = true 344 | ij_typescript_spaces_around_unary_operator = false 345 | ij_typescript_spaces_within_array_initializer_brackets = true 346 | ij_typescript_spaces_within_brackets = false 347 | ij_typescript_spaces_within_catch_parentheses = false 348 | ij_typescript_spaces_within_for_parentheses = false 349 | ij_typescript_spaces_within_if_parentheses = false 350 | ij_typescript_spaces_within_imports = true 351 | ij_typescript_spaces_within_interpolation_expressions = false 352 | ij_typescript_spaces_within_method_call_parentheses = false 353 | ij_typescript_spaces_within_method_parentheses = false 354 | ij_typescript_spaces_within_object_literal_braces = true 355 | ij_typescript_spaces_within_object_type_braces = true 356 | ij_typescript_spaces_within_parentheses = false 357 | ij_typescript_spaces_within_switch_parentheses = false 358 | ij_typescript_spaces_within_type_assertion = false 359 | ij_typescript_spaces_within_union_types = true 360 | ij_typescript_spaces_within_while_parentheses = false 361 | ij_typescript_special_else_if_treatment = true 362 | ij_typescript_ternary_operation_signs_on_next_line = false 363 | ij_typescript_ternary_operation_wrap = off 364 | ij_typescript_union_types_wrap = on_every_item 365 | ij_typescript_use_chained_calls_group_indents = false 366 | ij_typescript_use_double_quotes = false 367 | ij_typescript_use_explicit_js_extension = global 368 | ij_typescript_use_path_mapping = always 369 | ij_typescript_use_public_modifier = false 370 | ij_typescript_use_semicolon_after_statement = true 371 | ij_typescript_var_declaration_wrap = normal 372 | ij_typescript_while_brace_force = never 373 | ij_typescript_while_on_new_line = false 374 | ij_typescript_wrap_comments = false 375 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [nonara, danielpza] 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directories: 10 | - "/" 11 | - "/test" 12 | schedule: 13 | interval: "weekly" 14 | - package-ecosystem: "github-actions" # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 15 | directory: "/" 16 | schedule: 17 | # Check for updates to GitHub Actions every week 18 | interval: "weekly" 19 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - run: corepack enable 11 | - uses: actions/setup-node@v4 12 | with: 13 | node-version: 22 14 | cache: yarn 15 | cache-dependency-path: "**/yarn.lock" 16 | - run: yarn install 17 | - run: yarn lint 18 | 19 | build: 20 | runs-on: ${{ matrix.os }} 21 | strategy: 22 | matrix: 23 | os: [ubuntu-latest, windows-latest, macos-latest] 24 | node-version: [18, 20, 22] 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - run: corepack enable ${{ matrix.os == 'windows-latest' && '--install-directory C:\npm\prefix' || '' }} 30 | 31 | - name: Use Node.js ${{ matrix.node-version }} 32 | uses: actions/setup-node@v4 33 | with: 34 | node-version: ${{ matrix.node-version }} 35 | cache: yarn 36 | cache-dependency-path: "**/yarn.lock" 37 | 38 | - run: yarn install 39 | 40 | - run: yarn build 41 | 42 | - name: Install dependencies for testing 43 | run: yarn install 44 | working-directory: test 45 | 46 | - name: Test 47 | working-directory: test 48 | run: yarn test --runInBand=false --maxWorkers=2 --workerIdleMemoryLimit=2GB # https://github.com/facebook/jest/issues/11956 49 | env: 50 | NODE_OPTIONS: --max_old_space_size=4096 51 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | publishTag: 7 | description: "npm publish --tag " 8 | required: true 9 | default: "latest" 10 | type: choice 11 | options: 12 | - latest 13 | - next 14 | - test 15 | bump: 16 | description: "Changelogen bump flag" 17 | required: true 18 | default: "--bump" 19 | type: choice 20 | options: 21 | - --bump 22 | - --prerelease 23 | - --canary 24 | 25 | permissions: 26 | id-token: write 27 | contents: write 28 | 29 | jobs: 30 | publish: 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - uses: actions/checkout@v4 35 | with: 36 | fetch-depth: 0 37 | 38 | - name: Setup git 39 | run: | 40 | # https://github.com/actions/checkout?tab=readme-ov-file#push-a-commit-using-the-built-in-token 41 | git config user.name "github-actions[bot]" 42 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 43 | 44 | - run: corepack enable 45 | 46 | - uses: actions/setup-node@v4 47 | with: 48 | node-version: 22 49 | cache: yarn 50 | cache-dependency-path: "**/yarn.lock" 51 | registry-url: "https://registry.npmjs.org" 52 | 53 | - name: Install Packages 54 | run: yarn install 55 | 56 | - name: Bump Version 57 | run: yarn changelogen --release --push ${{ github.event.inputs.bump }} 58 | 59 | - name: Publish to NPM 60 | run: npm publish --provenance --tag ${{ github.event.inputs.publishTag }} 61 | env: 62 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 63 | YARN_NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Yarn 2 | .yarn-cache 3 | 4 | # Built 5 | *.js.map 6 | dist 7 | coverage 8 | package-lock.json 9 | *.tsbuildinfo 10 | 11 | # Extensions 12 | *.seed 13 | *.log 14 | *.csv 15 | *.dat 16 | *.out 17 | *.pid 18 | *.gz 19 | 20 | # Personal 21 | .env 22 | .vscode 23 | .idea/jsLibraryMappings.xml 24 | todo/ 25 | 26 | # Junk 27 | temp/ 28 | .DS_Store 29 | tmp 30 | node_modules 31 | 32 | ### JetBrains ### 33 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 34 | 35 | # User-specific stuff 36 | .idea/**/workspace.xml 37 | .idea/**/tasks.xml 38 | .idea/**/usage.statistics.xml 39 | .idea/**/dictionaries 40 | .idea/**/shelf 41 | 42 | # Generated files 43 | .idea/**/contentModel.xml 44 | 45 | # Sensitive or high-churn files 46 | .idea/**/dataSources/ 47 | .idea/**/dataSources.ids 48 | .idea/**/dataSources.local.xml 49 | .idea/**/sqlDataSources.xml 50 | .idea/**/dynamic.xml 51 | .idea/**/uiDesigner.xml 52 | .idea/**/dbnavigator.xml 53 | 54 | # Gradle 55 | .idea/**/gradle.xml 56 | .idea/**/libraries 57 | 58 | # Mongo Explorer plugin 59 | .idea/**/mongoSettings.xml 60 | 61 | # File-based project format 62 | *.iws 63 | 64 | # JIRA plugin 65 | atlassian-ide-plugin.xml 66 | 67 | # Cursive Clojure plugin 68 | .idea/replstate.xml 69 | 70 | # Crashlytics plugin (for Android Studio and IntelliJ) 71 | com_crashlytics_export_strings.xml 72 | crashlytics.properties 73 | crashlytics-build.properties 74 | fabric.properties 75 | 76 | # Editor-based Rest Client 77 | .idea/httpRequests 78 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/prettier.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/typescript-transform-paths.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | CHANGELOG.md 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## v3.5.5 6 | 7 | [compare changes](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.5.4...v3.5.5) 8 | 9 | ### 🩹 Fixes 10 | 11 | - When installed typescript version contains 0 it would cause error to be thrown "Expected version to be parsed" ([#399](https://github.com/LeDDGroup/typescript-transform-paths/pull/399)) 12 | 13 | ### ❤️ Contributors 14 | 15 | - Anthony Y. Zhu ([@anthony-y-zhu14](http://github.com/anthony-y-zhu14)) 16 | 17 | ## v3.5.4 18 | 19 | [compare changes](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.5.2...v3.5.4) 20 | 21 | ### 🩹 Fixes 22 | 23 | - Respect desired path on case-insensitive file systems ([#397](https://github.com/LeDDGroup/typescript-transform-paths/pull/397)) 24 | 25 | ### 📖 Documentation 26 | 27 | - Add version compatibility matrix in readme ([#338](https://github.com/LeDDGroup/typescript-transform-paths/pull/338)) 28 | 29 | ### 🏡 Chore 30 | 31 | - **deps-dev:** Bump @eslint/js from 9.9.1 to 9.15.0 ([#310](https://github.com/LeDDGroup/typescript-transform-paths/pull/310)) 32 | - **deps-dev:** Bump typescript-eslint from 8.13.0 to 8.15.0 ([#308](https://github.com/LeDDGroup/typescript-transform-paths/pull/308)) 33 | - **deps-dev:** Bump eslint from 9.10.0 to 9.15.0 ([#309](https://github.com/LeDDGroup/typescript-transform-paths/pull/309)) 34 | - **deps-dev:** Bump changelogen from 0.5.5 to 0.5.7 ([#316](https://github.com/LeDDGroup/typescript-transform-paths/pull/316)) 35 | - **deps-dev:** Bump @types/node from 22.8.7 to 22.9.3 ([#314](https://github.com/LeDDGroup/typescript-transform-paths/pull/314)) 36 | - **deps-dev:** Bump typescript from 5.6.3 to 5.7.2 in /test ([#317](https://github.com/LeDDGroup/typescript-transform-paths/pull/317)) 37 | - **deps-dev:** Bump typescript-eslint from 8.15.0 to 8.16.0 ([#313](https://github.com/LeDDGroup/typescript-transform-paths/pull/313)) 38 | - **deps-dev:** Bump eslint from 9.15.0 to 9.16.0 ([#319](https://github.com/LeDDGroup/typescript-transform-paths/pull/319)) 39 | - **deps-dev:** Bump typescript-eslint from 8.16.0 to 8.17.0 ([#320](https://github.com/LeDDGroup/typescript-transform-paths/pull/320)) 40 | - **deps-dev:** Update yarn from 4.4.0 to 4.5.3 ([f6ccab1](https://github.com/LeDDGroup/typescript-transform-paths/commit/f6ccab1)) 41 | - Update yarn.lock ([b958778](https://github.com/LeDDGroup/typescript-transform-paths/commit/b958778)) 42 | - **deps-dev:** Bump ts-patch from 3.2.1 to 3.3.0 ([#322](https://github.com/LeDDGroup/typescript-transform-paths/pull/322)) 43 | - **deps-dev:** Bump typescript-eslint from 8.17.0 to 8.18.0 ([#323](https://github.com/LeDDGroup/typescript-transform-paths/pull/323)) 44 | - **deps-dev:** Bump typescript from 5.5.4 to 5.7.2 ([#315](https://github.com/LeDDGroup/typescript-transform-paths/pull/315)) 45 | - **deps-dev:** Bump ts-patch from 3.2.1 to 3.3.0 in /test ([#324](https://github.com/LeDDGroup/typescript-transform-paths/pull/324)) 46 | - Configure Renovate ([#330](https://github.com/LeDDGroup/typescript-transform-paths/pull/330)) 47 | - Update renovate config to auto merge minor updates ([b3f3f31](https://github.com/LeDDGroup/typescript-transform-paths/commit/b3f3f31)) 48 | - **release:** V3.5.3 ([4c3a6c3](https://github.com/LeDDGroup/typescript-transform-paths/commit/4c3a6c3)) 49 | - **deps-dev:** Bump @types/jest from 29.5.12 to 29.5.14 in /test ([#346](https://github.com/LeDDGroup/typescript-transform-paths/pull/346)) 50 | 51 | ### 🤖 CI 52 | 53 | - Adjust publish workflow ([1039e07](https://github.com/LeDDGroup/typescript-transform-paths/commit/1039e07)) 54 | - Fix changelogen not pushing code ([221136a](https://github.com/LeDDGroup/typescript-transform-paths/commit/221136a)) 55 | - Update fetch-depth on publish workflow ([2fe790e](https://github.com/LeDDGroup/typescript-transform-paths/commit/2fe790e)) 56 | 57 | ### ❤️ Contributors 58 | 59 | - Denis Sokolov ([@denis-sokolov](http://github.com/denis-sokolov)) 60 | - Daniel Perez Alvarez 61 | 62 | ## v3.5.3 63 | 64 | [compare changes](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.5.2...v3.5.3) 65 | 66 | ### 📖 Documentation 67 | 68 | - Add version compatibility matrix in readme ([#338](https://github.com/LeDDGroup/typescript-transform-paths/pull/338)) 69 | 70 | ### 🏡 Chore 71 | 72 | - **deps-dev:** Bump @eslint/js from 9.9.1 to 9.15.0 ([#310](https://github.com/LeDDGroup/typescript-transform-paths/pull/310)) 73 | - **deps-dev:** Bump typescript-eslint from 8.13.0 to 8.15.0 ([#308](https://github.com/LeDDGroup/typescript-transform-paths/pull/308)) 74 | - **deps-dev:** Bump eslint from 9.10.0 to 9.15.0 ([#309](https://github.com/LeDDGroup/typescript-transform-paths/pull/309)) 75 | - **deps-dev:** Bump changelogen from 0.5.5 to 0.5.7 ([#316](https://github.com/LeDDGroup/typescript-transform-paths/pull/316)) 76 | - **deps-dev:** Bump @types/node from 22.8.7 to 22.9.3 ([#314](https://github.com/LeDDGroup/typescript-transform-paths/pull/314)) 77 | - **deps-dev:** Bump typescript from 5.6.3 to 5.7.2 in /test ([#317](https://github.com/LeDDGroup/typescript-transform-paths/pull/317)) 78 | - **deps-dev:** Bump typescript-eslint from 8.15.0 to 8.16.0 ([#313](https://github.com/LeDDGroup/typescript-transform-paths/pull/313)) 79 | - **deps-dev:** Bump eslint from 9.15.0 to 9.16.0 ([#319](https://github.com/LeDDGroup/typescript-transform-paths/pull/319)) 80 | - **deps-dev:** Bump typescript-eslint from 8.16.0 to 8.17.0 ([#320](https://github.com/LeDDGroup/typescript-transform-paths/pull/320)) 81 | - **deps-dev:** Update yarn from 4.4.0 to 4.5.3 ([f6ccab1](https://github.com/LeDDGroup/typescript-transform-paths/commit/f6ccab1)) 82 | - Update yarn.lock ([b958778](https://github.com/LeDDGroup/typescript-transform-paths/commit/b958778)) 83 | - **deps-dev:** Bump ts-patch from 3.2.1 to 3.3.0 ([#322](https://github.com/LeDDGroup/typescript-transform-paths/pull/322)) 84 | - **deps-dev:** Bump typescript-eslint from 8.17.0 to 8.18.0 ([#323](https://github.com/LeDDGroup/typescript-transform-paths/pull/323)) 85 | - **deps-dev:** Bump typescript from 5.5.4 to 5.7.2 ([#315](https://github.com/LeDDGroup/typescript-transform-paths/pull/315)) 86 | - **deps-dev:** Bump ts-patch from 3.2.1 to 3.3.0 in /test ([#324](https://github.com/LeDDGroup/typescript-transform-paths/pull/324)) 87 | - Configure Renovate ([#330](https://github.com/LeDDGroup/typescript-transform-paths/pull/330)) 88 | - Update renovate config to auto merge minor updates ([b3f3f31](https://github.com/LeDDGroup/typescript-transform-paths/commit/b3f3f31)) 89 | 90 | ### 🤖 CI 91 | 92 | - Adjust publish workflow ([1039e07](https://github.com/LeDDGroup/typescript-transform-paths/commit/1039e07)) 93 | - Fix changelogen not pushing code ([221136a](https://github.com/LeDDGroup/typescript-transform-paths/commit/221136a)) 94 | 95 | ### ❤️ Contributors 96 | 97 | - Daniel Perez Alvarez 98 | 99 | ## v3.5.2 100 | 101 | [compare changes](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.5.1...v3.5.2) 102 | 103 | ### 🩹 Fixes 104 | 105 | - Restore typescript 5.6 compatibility ([#301](https://github.com/LeDDGroup/typescript-transform-paths/pull/301)) 106 | 107 | ### 🏡 Chore 108 | 109 | - **deps-dev:** Bump eslint and @types/eslint ([#273](https://github.com/LeDDGroup/typescript-transform-paths/pull/273)) 110 | - **deps-dev:** Bump @types/node from 22.5.2 to 22.8.7 ([#303](https://github.com/LeDDGroup/typescript-transform-paths/pull/303)) 111 | - **deps-dev:** Bump typescript-eslint from 8.3.0 to 8.13.0 ([#302](https://github.com/LeDDGroup/typescript-transform-paths/pull/302)) 112 | 113 | ### ❤️ Contributors 114 | 115 | - Ben Limmer ([@blimmer](http://github.com/blimmer)) 116 | 117 | ## v3.5.1 118 | 119 | [compare changes](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.5.0...v3.5.1) 120 | 121 | ### 🩹 Fixes 122 | 123 | - Downgrade minimatch from 10.0.1 to 9.0.5 ([#272](https://github.com/LeDDGroup/typescript-transform-paths/pull/272)) 124 | 125 | ### 💅 Refactors 126 | 127 | - Always patch ([ed3ed1f](https://github.com/LeDDGroup/typescript-transform-paths/commit/ed3ed1f)) 128 | - Rename typescript-number to typescript-x ([36643ff](https://github.com/LeDDGroup/typescript-transform-paths/commit/36643ff)) 129 | - Use strictest typescript configuration ([a67e739](https://github.com/LeDDGroup/typescript-transform-paths/commit/a67e739)) 130 | - Enable @typescript-eslint/no-explicit-any warning ([1cddaac](https://github.com/LeDDGroup/typescript-transform-paths/commit/1cddaac)) 131 | - Add eslint-plugin-unicorn ([44ad2e0](https://github.com/LeDDGroup/typescript-transform-paths/commit/44ad2e0)) 132 | - Remove anys ([07e5c27](https://github.com/LeDDGroup/typescript-transform-paths/commit/07e5c27)) 133 | 134 | ### 🏡 Chore 135 | 136 | - Rename nx test project to avoid conflict with package name ([3ee03d1](https://github.com/LeDDGroup/typescript-transform-paths/commit/3ee03d1)) 137 | - **deps-dev:** Bump typescript-eslint from 8.1.0 to 8.2.0 ([#249](https://github.com/LeDDGroup/typescript-transform-paths/pull/249)) 138 | - **deps-dev:** Bump @types/node from 22.2.0 to 22.4.1 ([#248](https://github.com/LeDDGroup/typescript-transform-paths/pull/248)) 139 | - Add danielpza to funding.yml ([9aa6941](https://github.com/LeDDGroup/typescript-transform-paths/commit/9aa6941)) 140 | - **deps-dev:** Bump typescript-eslint from 8.2.0 to 8.3.0 ([#254](https://github.com/LeDDGroup/typescript-transform-paths/pull/254)) 141 | - **deps-dev:** Bump @types/node from 22.4.1 to 22.5.0 ([#253](https://github.com/LeDDGroup/typescript-transform-paths/pull/253)) 142 | - **deps-dev:** Update some dependencies ([164aeac](https://github.com/LeDDGroup/typescript-transform-paths/commit/164aeac)) 143 | - Remove eslint-plugin-unicorn ([8c6ccf4](https://github.com/LeDDGroup/typescript-transform-paths/commit/8c6ccf4)) 144 | 145 | ### ❤️ Contributors 146 | 147 | - Spdaley 148 | - Daniel Perez Alvarez 149 | 150 | ## v3.5.0 151 | 152 | [compare changes](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.11...v3.5.0) 153 | 154 | ### 🚀 Enhancements 155 | 156 | - Add package.json exports ([#218](https://github.com/LeDDGroup/typescript-transform-paths/pull/218)) 157 | 158 | ### 💅 Refactors 159 | 160 | - Move top level register and nx-transformer to src/ ([6f0a280](https://github.com/LeDDGroup/typescript-transform-paths/commit/6f0a280)) 161 | - Add prettier-plugin-jsdoc ([0b5fd4c](https://github.com/LeDDGroup/typescript-transform-paths/commit/0b5fd4c)) 162 | - Remove ts-expose-internals imports ([7b66926](https://github.com/LeDDGroup/typescript-transform-paths/commit/7b66926)) 163 | - Update ts-expose-internals -> @types/ts-expose-internals ([56d62d2](https://github.com/LeDDGroup/typescript-transform-paths/commit/56d62d2)) 164 | 165 | ### 🏡 Chore 166 | 167 | - **deps-dev:** Remove strip-ansi ([2fc9901](https://github.com/LeDDGroup/typescript-transform-paths/commit/2fc9901)) 168 | 169 | ### ❤️ Contributors 170 | 171 | - Daniel Perez Alvarez 172 | 173 | ## v3.4.11 174 | 175 | [compare changes](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.10...v3.4.11) 176 | 177 | ### 💅 Refactors 178 | 179 | - Remove namespaces in favor of modules ([#236](https://github.com/LeDDGroup/typescript-transform-paths/pull/236)) 180 | - Fix eslint warnings ([#238](https://github.com/LeDDGroup/typescript-transform-paths/pull/238)) 181 | - Fix no-require-imports eslint warning ([#239](https://github.com/LeDDGroup/typescript-transform-paths/pull/239)) 182 | - Enable no-unused-vars eslint rule ([2e3b9d6](https://github.com/LeDDGroup/typescript-transform-paths/commit/2e3b9d6)) 183 | - Remove tsconfig.base.json in favor of @tsconfig/node18 ([#235](https://github.com/LeDDGroup/typescript-transform-paths/pull/235)) 184 | - Unvendor typescript types definitions ([#234](https://github.com/LeDDGroup/typescript-transform-paths/pull/234)) 185 | - Remove hardcoded types ([#240](https://github.com/LeDDGroup/typescript-transform-paths/pull/240)) 186 | 187 | ### 📖 Documentation 188 | 189 | - Remove all contributors badge ([#230](https://github.com/LeDDGroup/typescript-transform-paths/pull/230)) 190 | - Remove articles section ([02d7d6c](https://github.com/LeDDGroup/typescript-transform-paths/commit/02d7d6c)) 191 | 192 | ### 🏡 Chore 193 | 194 | - Update eslint config to remove old src/declarations path ([0ab8ebc](https://github.com/LeDDGroup/typescript-transform-paths/commit/0ab8ebc)) 195 | - Update package.json contributors ([b79f96d](https://github.com/LeDDGroup/typescript-transform-paths/commit/b79f96d)) 196 | - **deps-dev:** Bump @types/node from 18.19.43 to 22.2.0 ([#241](https://github.com/LeDDGroup/typescript-transform-paths/pull/241)) 197 | - **deps-dev:** Bump typescript-eslint from 8.0.1 to 8.1.0 ([#243](https://github.com/LeDDGroup/typescript-transform-paths/pull/243)) 198 | - **deps-dev:** Bump @eslint/js from 9.8.0 to 9.9.0 ([#242](https://github.com/LeDDGroup/typescript-transform-paths/pull/242)) 199 | - **deps-dev:** Bump eslint from 9.8.0 to 9.9.0 ([#245](https://github.com/LeDDGroup/typescript-transform-paths/pull/245)) 200 | 201 | ### ✅ Tests 202 | 203 | - Remove yarn pack workaround ([#232](https://github.com/LeDDGroup/typescript-transform-paths/pull/232)) 204 | 205 | ### 🎨 Styles 206 | 207 | - Add eslint ([#237](https://github.com/LeDDGroup/typescript-transform-paths/pull/237)) 208 | 209 | ### ❤️ Contributors 210 | 211 | - Daniel Perez Alvarez 212 | 213 | ## v3.4.10 214 | 215 | [compare changes](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.9...v3.4.10) 216 | 217 | ### 🏡 Chore 218 | 219 | - Update dependencies ([#226](https://github.com/LeDDGroup/typescript-transform-paths/pull/226)) 220 | - Replace standard-version with changelogen ([#227](https://github.com/LeDDGroup/typescript-transform-paths/pull/227)) 221 | 222 | ### 🤖 CI 223 | 224 | - Generate provenance on npm publish ([#229](https://github.com/LeDDGroup/typescript-transform-paths/pull/229)) 225 | 226 | ### ❤️ Contributors 227 | 228 | - Daniel Perez Alvarez 229 | 230 | ### [3.4.9](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.8...v3.4.9) (2024-08-07) 231 | 232 | ### [3.4.8](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.7...v3.4.8) (2024-08-07) 233 | 234 | ### [3.4.7](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.6...v3.4.7) (2024-02-20) 235 | 236 | 237 | ### Bug Fixes 238 | 239 | * Numerous issues with elision due to new TS features in v5+ (fixes [#184](https://github.com/LeDDGroup/typescript-transform-paths/issues/184)) ([d4f89af](https://github.com/LeDDGroup/typescript-transform-paths/commit/d4f89afccb02daba0212380e31654b93d85ebe98)) 240 | 241 | ### [3.4.6](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.5...v3.4.6) (2023-01-06) 242 | 243 | 244 | ### Bug Fixes 245 | 246 | * Cover edge case issues with case-insensitive filesystems ([968ee7b](https://github.com/LeDDGroup/typescript-transform-paths/commit/968ee7b080d0421347e020161646bc1fc5e58008)) 247 | 248 | ### [3.4.5](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.4...v3.4.5) (2023-01-05) 249 | 250 | 251 | ### Bug Fixes 252 | 253 | * OSX paths can be rewritten improperly (fixes [#167](https://github.com/LeDDGroup/typescript-transform-paths/issues/167)) ([0ff48d7](https://github.com/LeDDGroup/typescript-transform-paths/commit/0ff48d740edc58bed72971f6ad0f2c7493c56def)) 254 | 255 | ### [3.4.4](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.3...v3.4.4) (2022-10-24) 256 | 257 | 258 | ### Bug Fixes 259 | 260 | * NX Transformer entry script not included in NPM package (fixes [#166](https://github.com/LeDDGroup/typescript-transform-paths/issues/166)) ([4396e0c](https://github.com/LeDDGroup/typescript-transform-paths/commit/4396e0c27e679cef1bfe6d5faf397cc1b2f11992)) 261 | 262 | ### [3.4.3](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.2...v3.4.3) (2022-10-21) 263 | 264 | 265 | ### Bug Fixes 266 | 267 | * More edge cases in ts-node (fixes [#163](https://github.com/LeDDGroup/typescript-transform-paths/issues/163)) ([b297431](https://github.com/LeDDGroup/typescript-transform-paths/commit/b297431b5bd88a9b9dce449ded9a241857aaf5c6)) 268 | 269 | ### [3.4.2](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.1...v3.4.2) (2022-10-21) 270 | 271 | 272 | ### Bug Fixes 273 | 274 | * Edge cases exist which causes ts-node to fail with register script (fixes [#162](https://github.com/LeDDGroup/typescript-transform-paths/issues/162)) ([0dbe06e](https://github.com/LeDDGroup/typescript-transform-paths/commit/0dbe06efbf659f8d67df22d25296930f4ecd203f)) 275 | 276 | ### [3.4.1](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.4.0...v3.4.1) (2022-10-20) 277 | 278 | 279 | ### Bug Fixes 280 | 281 | * ImportType node children do not transform (fixes [#150](https://github.com/LeDDGroup/typescript-transform-paths/issues/150)) ([70871d2](https://github.com/LeDDGroup/typescript-transform-paths/commit/70871d22e76999589cb54111577c5e08112b43ee)) 282 | 283 | ## [3.4.0](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.3.1...v3.4.0) (2022-10-20) 284 | 285 | 286 | ### Features 287 | 288 | * Added NX Transformer ([6619164](https://github.com/LeDDGroup/typescript-transform-paths/commit/66191641bcd023464198b1974a6c351c831dc803)) 289 | * Updated library to support latest TypeScript & ts-node ([cbeb29c](https://github.com/LeDDGroup/typescript-transform-paths/commit/cbeb29cd3a62c205d7116a0ee5f0120d1696bd68)) 290 | 291 | 292 | ### Bug Fixes 293 | 294 | * Fixed incorrect signature return type for transformer (fixes [#156](https://github.com/LeDDGroup/typescript-transform-paths/issues/156)) ([a40b378](https://github.com/LeDDGroup/typescript-transform-paths/commit/a40b3785b61894f6f37b48018a00101ee27bcb09)) 295 | 296 | ### [3.3.1](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.3.0...v3.3.1) (2021-08-16) 297 | 298 | 299 | ### Bug Fixes 300 | 301 | * External project references not resolved properly in composite projects (fixes [#125](https://github.com/LeDDGroup/typescript-transform-paths/issues/125)) ([4a16396](https://github.com/LeDDGroup/typescript-transform-paths/commit/4a16396199058b6a705e22dc84ddfe8e6491f6be)) 302 | 303 | ## [3.3.0](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.2.1...v3.3.0) (2021-08-10) 304 | 305 | 306 | ### Features 307 | 308 | * Added typescript-transform-paths/register script ([8c36b09](https://github.com/LeDDGroup/typescript-transform-paths/commit/8c36b098a837d1ed04c04a8fb8a39a03eb0bbadf)) 309 | 310 | ### [3.2.1](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.2.0...v3.2.1) (2021-08-05) 311 | 312 | 313 | ### Bug Fixes 314 | 315 | * Missing type-only modifier for ts-node type import (fixes [#130](https://github.com/LeDDGroup/typescript-transform-paths/issues/130)) ([0320574](https://github.com/LeDDGroup/typescript-transform-paths/commit/0320574a42acf2b54585562e57850bbab2deea05)) 316 | 317 | ## [3.2.0](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.1.0...v3.2.0) (2021-08-03) 318 | 319 | 320 | ### Features 321 | 322 | * Support transformation via ts-node transpileOnly and compiler API transformNodes (closes [#123](https://github.com/LeDDGroup/typescript-transform-paths/issues/123)) ([dd942fd](https://github.com/LeDDGroup/typescript-transform-paths/commit/dd942fdbf34afcdec8f976a1540746521a758c73)) 323 | 324 | 325 | ### Bug Fixes 326 | 327 | * Custom JSDoc tags not working for older TS (fixes [#126](https://github.com/LeDDGroup/typescript-transform-paths/issues/126)) ([d4280c3](https://github.com/LeDDGroup/typescript-transform-paths/commit/d4280c3dec4dc9f3834fc98be2e51109422bd9aa)) 328 | 329 | ## [3.1.0](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.0.2...v3.1.0) (2021-07-13) 330 | 331 | 332 | ### Features 333 | 334 | * Add support for module augmentation (closes [#122](https://github.com/LeDDGroup/typescript-transform-paths/issues/122)) ([f9d4994](https://github.com/LeDDGroup/typescript-transform-paths/commit/f9d49944d1fed7c1d6cbfa1cbd04c0c76abae1c0)) 335 | 336 | ### [3.0.2](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.0.1...v3.0.2) (2021-07-01) 337 | 338 | 339 | ### Features 340 | 341 | * Improved resolution strategy by using EmitHost ([87294b4](https://github.com/LeDDGroup/typescript-transform-paths/commit/87294b437dfa572d409aa0b59a144de8545a3672)) 342 | 343 | 344 | ### Bug Fixes 345 | 346 | * Accommodate TS 4.4 paths pattern caching (fixes [#114](https://github.com/LeDDGroup/typescript-transform-paths/issues/114)) ([#116](https://github.com/LeDDGroup/typescript-transform-paths/issues/116)) ([e9b2a9f](https://github.com/LeDDGroup/typescript-transform-paths/commit/e9b2a9f0240bafd1a8d4a97c2be7cda3587af303)) 347 | * Base Node was elided if could not resolve (fixes: [#119](https://github.com/LeDDGroup/typescript-transform-paths/issues/119)) ([1b97c80](https://github.com/LeDDGroup/typescript-transform-paths/commit/1b97c8026903ede676c831fe24f9d18c98d24b24)) 348 | 349 | ### [3.0.1](https://github.com/LeDDGroup/typescript-transform-paths/compare/v3.0.0...v3.0.1) (2021-06-28) 350 | 351 | ## [3.0.0](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.2.4...v3.0.0) (2021-06-16) 352 | 353 | 354 | ### Features 355 | 356 | * Rewrote resolution strategy + various improvements (see notes) ([ed1df79](https://github.com/LeDDGroup/typescript-transform-paths/commit/ed1df795063c4d08b2a29b8b229d6ac7d134b816)), closes [#109](https://github.com/LeDDGroup/typescript-transform-paths/issues/109) [#110](https://github.com/LeDDGroup/typescript-transform-paths/issues/110) [#106](https://github.com/LeDDGroup/typescript-transform-paths/issues/106) [#108](https://github.com/LeDDGroup/typescript-transform-paths/issues/108) [#107](https://github.com/LeDDGroup/typescript-transform-paths/issues/107) [#103](https://github.com/LeDDGroup/typescript-transform-paths/issues/103) 357 | 358 | ### [2.2.4](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.2.3...v2.2.4) (2021-05-20) 359 | 360 | 361 | ### Bug Fixes 362 | 363 | * Implicit 'index' was written to output filename for imports (fixes [#105](https://github.com/LeDDGroup/typescript-transform-paths/issues/105)) ([4803cd2](https://github.com/LeDDGroup/typescript-transform-paths/commit/4803cd212c20785c74e534366fddc5c62f3a4d89)) 364 | 365 | ### [2.2.3](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.2.2...v2.2.3) (2021-02-02) 366 | 367 | 368 | ### Bug Fixes 369 | 370 | * Prevent `.json` extension being stripped in output (fixes [#95](https://github.com/LeDDGroup/typescript-transform-paths/issues/95)) ([bcca436](https://github.com/LeDDGroup/typescript-transform-paths/commit/bcca43677d23ddea0a409ec3daff008313d17342)) 371 | 372 | ### [2.2.2](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.2.1...v2.2.2) (2021-01-11) 373 | 374 | 375 | ### Bug Fixes 376 | 377 | * Corrected explicit extensions fix from previous patch ([a90e550](https://github.com/LeDDGroup/typescript-transform-paths/commit/a90e550a3d0c4804ef3a4e27cbc4e32ce6971296)) 378 | 379 | ### [2.2.1](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.2.0...v2.2.1) (2021-01-10) 380 | 381 | 382 | ### Bug Fixes 383 | 384 | * Preserve explicit file extensions (closes [#89](https://github.com/LeDDGroup/typescript-transform-paths/issues/89)) ([b0627f8](https://github.com/LeDDGroup/typescript-transform-paths/commit/b0627f8f6cd49681ff58ffad48e04e96a9e9ba27)) 385 | * Rely on original node for getting comment tags (closes [#90](https://github.com/LeDDGroup/typescript-transform-paths/issues/90)) ([fa978c2](https://github.com/LeDDGroup/typescript-transform-paths/commit/fa978c2651f74ba3ce7d7363ac5a0b3677a8e90c)) 386 | 387 | ## [2.2.0](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.1.0...v2.2.0) (2021-01-04) 388 | 389 | 390 | ### Features 391 | 392 | * Add overwriteNodeModules option ([b4a483e](https://github.com/LeDDGroup/typescript-transform-paths/commit/b4a483e880e348971c15dde697ec6813f678fde2)) 393 | * Added `@transform-path` and `@no-transform-path` tags for custom statement level transformation ([8cab30d](https://github.com/LeDDGroup/typescript-transform-paths/commit/8cab30d25415596f19b162bcf50cf984256012e6)) 394 | * Added `exclude` option to allow excluding transformation of matching resolved paths ([b1fdb54](https://github.com/LeDDGroup/typescript-transform-paths/commit/b1fdb545c2d963cd4d82a6a0bedfa2d7d0107398)) 395 | 396 | 397 | ### Bug Fixes 398 | 399 | * Certain edge cases existed where type elision improperly elided full import / export declarations without named bindings (closes [#87](https://github.com/LeDDGroup/typescript-transform-paths/issues/87)) ([84a7866](https://github.com/LeDDGroup/typescript-transform-paths/commit/84a7866d354b49a7d2e8abaadb89f24b8bff07bc)) 400 | 401 | ## [2.1.0](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.0.3...v2.1.0) (2020-11-27) 402 | 403 | ### Features 404 | 405 | * Added proper type elision (Closes [#77](https://github.com/LeDDGroup/typescript-transform-paths/issues/77) [#78](https://github.com/LeDDGroup/typescript-transform-paths/issues/78)) ([cee93ec](https://github.com/LeDDGroup/typescript-transform-paths/commit/cee93ecc3dceb90474239787c216a8d26089b417)) 406 | 407 | ### Bug Fixes 408 | 409 | * Ensure we use the same typescript instance that called the plugin (Fixes [#80](https://github.com/LeDDGroup/typescript-transform-paths/issues/80)) 410 | 411 | ### Refactoring 412 | 413 | * Heavily refactored code base for better scale and readability 414 | * Refactored tests for modularity and multi-TS instance testing 415 | 416 | ### [2.0.4](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.0.3...v2.0.4) (2020-11-23) 417 | 418 | * Refactored from requiring _both_ to _at least one_ tsConfig option: baseUrl, paths ([34e4963](https://github.com/LeDDGroup/typescript-transform-paths/commit/34e49639f7248e38475efd854670c11ea65fc76e)) 419 | - Fixes issue [#39](https://github.com/LeDDGroup/typescript-transform-paths/issues/39) 420 | - Adds support for new [TS 4.1 - paths without baseUrl](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#paths-without-baseurl) 421 | 422 | ### [2.0.3](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.0.2...v2.0.3) (2020-11-17) 423 | 424 | 425 | ### Bug Fixes 426 | 427 | * Updated to more sound workaround for TS type-elision issue ([fb33832](https://github.com/LeDDGroup/typescript-transform-paths/commit/fb338322e5fcd3b3ee7d45269287006ddec1bdb6)) 428 | 429 | ### [2.0.2](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.0.1...v2.0.2) (2020-10-23) 430 | 431 | 432 | ### Bug Fixes 433 | 434 | * Leading comments elided from async import ([d29c52a](https://github.com/LeDDGroup/typescript-transform-paths/commit/d29c52adb11532327889fe49539797b65ba78e86)), closes [#58](https://github.com/LeDDGroup/typescript-transform-paths/issues/58) 435 | 436 | ### [2.0.1](https://github.com/LeDDGroup/typescript-transform-paths/compare/v2.0.0...v2.0.1) (2020-09-17) 437 | 438 | 439 | ### Bug Fixes 440 | 441 | * Support TS 4+ ([#69](https://github.com/LeDDGroup/typescript-transform-paths/issues/69)) ([2406346](https://github.com/LeDDGroup/typescript-transform-paths/commit/24063465c33c36a90d0ae8dd80374369d5f3ca8d)), closes [#68](https://github.com/LeDDGroup/typescript-transform-paths/issues/68) 442 | 443 | ## [2.0.0](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.15...v2.0.0) (2020-08-04) 444 | 445 | 446 | ### ⚠ BREAKING CHANGES 447 | 448 | * Re-written to rely on TS API (#66) 449 | 450 | ### Bug Fixes 451 | 452 | * Re-written to rely on TS API ([#66](https://github.com/LeDDGroup/typescript-transform-paths/issues/66)) ([e271f3e](https://github.com/LeDDGroup/typescript-transform-paths/commit/e271f3e973187743d5431b3582e44d553234d581)) 453 | 454 | ### [1.1.15](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.14...v1.1.15) (2020-07-30) 455 | 456 | 457 | ### Bug Fixes 458 | 459 | * multiple issue fixes ([#61](https://github.com/LeDDGroup/typescript-transform-paths/issues/61)) ([a4e2916](https://github.com/LeDDGroup/typescript-transform-paths/commit/a4e2916)), closes [#60](https://github.com/LeDDGroup/typescript-transform-paths/issues/60) [#24](https://github.com/LeDDGroup/typescript-transform-paths/issues/24) [#48](https://github.com/LeDDGroup/typescript-transform-paths/issues/48) 460 | 461 | 462 | 463 | ### [1.1.14](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.13...v1.1.14) (2019-12-27) 464 | 465 | 466 | ### Bug Fixes 467 | 468 | * add support for dynamic imports ([#46](https://github.com/LeDDGroup/typescript-transform-paths/issues/46)) ([88b6001](https://github.com/LeDDGroup/typescript-transform-paths/commit/88b6001)) 469 | 470 | 471 | 472 | ### [1.1.13](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.12...v1.1.13) (2019-11-11) 473 | 474 | 475 | ### Bug Fixes 476 | 477 | * another edge case for implicit * path ([#43](https://github.com/LeDDGroup/typescript-transform-paths/issues/43)) ([d0f4eb7](https://github.com/LeDDGroup/typescript-transform-paths/commit/d0f4eb7)), closes [#42](https://github.com/LeDDGroup/typescript-transform-paths/issues/42) 478 | 479 | 480 | 481 | ### [1.1.12](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.11...v1.1.12) (2019-11-09) 482 | 483 | 484 | ### Bug Fixes 485 | 486 | * handle tsconfig with out paths ([#41](https://github.com/LeDDGroup/typescript-transform-paths/issues/41)) ([1e936b8](https://github.com/LeDDGroup/typescript-transform-paths/commit/1e936b8)) 487 | 488 | 489 | 490 | ### [1.1.11](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.10...v1.1.11) (2019-10-09) 491 | 492 | 493 | ### Bug Fixes 494 | 495 | * not working with files with multiple extensions ([#37](https://github.com/LeDDGroup/typescript-transform-paths/issues/37)) ([97454c7](https://github.com/LeDDGroup/typescript-transform-paths/commit/97454c7)) 496 | 497 | ### [1.1.10](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.9...v1.1.10) (2019-08-20) 498 | 499 | 500 | ### Bug Fixes 501 | 502 | * do not transform relative paths ([dd57089](https://github.com/LeDDGroup/typescript-transform-paths/commit/dd57089)), closes [#30](https://github.com/LeDDGroup/typescript-transform-paths/issues/30) 503 | * resolve only if file exists ([e6c51e0](https://github.com/LeDDGroup/typescript-transform-paths/commit/e6c51e0)) 504 | 505 | 506 | 507 | ### [1.1.9](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.8...v1.1.9) (2019-08-20) 508 | 509 | 510 | ### Bug Fixes 511 | 512 | * urls not working ([4f9fbfa](https://github.com/LeDDGroup/typescript-transform-paths/commit/4f9fbfa)) 513 | 514 | 515 | 516 | ### [1.1.8](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.7...v1.1.8) (2019-08-01) 517 | 518 | 519 | ### Bug Fixes 520 | 521 | * not updating external module reference ([2323637](https://github.com/LeDDGroup/typescript-transform-paths/commit/2323637)) 522 | * revert [#27](https://github.com/LeDDGroup/typescript-transform-paths/issues/27) ([2d2cbeb](https://github.com/LeDDGroup/typescript-transform-paths/commit/2d2cbeb)) 523 | 524 | 525 | 526 | ### [1.1.7](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.6...v1.1.7) (2019-07-30) 527 | 528 | 529 | ### Bug Fixes 530 | 531 | * require statements not being transformed ([#27](https://github.com/LeDDGroup/typescript-transform-paths/issues/27)) ([822b65e](https://github.com/LeDDGroup/typescript-transform-paths/commit/822b65e)) 532 | 533 | 534 | 535 | ### [1.1.6](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.5...v1.1.6) (2019-07-09) 536 | 537 | 538 | ### Bug Fixes 539 | 540 | * not working explicits exports for types on declaration files ([0263f06](https://github.com/LeDDGroup/typescript-transform-paths/commit/0263f06)) 541 | 542 | 543 | 544 | ### [1.1.5](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.4...v1.1.5) (2019-07-09) 545 | 546 | 547 | ### Bug Fixes 548 | 549 | * declaration files inconsistencies ([d7f4074](https://github.com/LeDDGroup/typescript-transform-paths/commit/d7f4074)), closes [#24](https://github.com/LeDDGroup/typescript-transform-paths/issues/24) [#23](https://github.com/LeDDGroup/typescript-transform-paths/issues/23) [#22](https://github.com/LeDDGroup/typescript-transform-paths/issues/22) 550 | 551 | 552 | ### Tests 553 | 554 | * improve tests ([cf20a3f](https://github.com/LeDDGroup/typescript-transform-paths/commit/cf20a3f)) 555 | 556 | 557 | 558 | ### [1.1.4](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.3...v1.1.4) (2019-06-08) 559 | 560 | 561 | ### Bug Fixes 562 | 563 | * some exports not working ([cc2ba49](https://github.com/LeDDGroup/typescript-transform-paths/commit/cc2ba49)) 564 | 565 | 566 | 567 | ### [1.1.3](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.2...v1.1.3) (2019-05-24) 568 | 569 | 570 | ### Bug Fixes 571 | 572 | * not transforming declaration files ([bb269b5](https://github.com/LeDDGroup/typescript-transform-paths/commit/bb269b5)), closes [#13](https://github.com/LeDDGroup/typescript-transform-paths/issues/13) [#14](https://github.com/LeDDGroup/typescript-transform-paths/issues/14) 573 | 574 | 575 | ### Tests 576 | 577 | * use release build for tests ([6069d24](https://github.com/LeDDGroup/typescript-transform-paths/commit/6069d24)) 578 | 579 | 580 | 581 | ### [1.1.2](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.1...v1.1.2) (2019-05-21) 582 | 583 | 584 | * add some keywords to package.json 585 | * update license to MIT 586 | * add all contributor 587 | 588 | 589 | 590 | 591 | ## [1.1.1](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.1.0...v1.1.1) (2019-05-18) 592 | 593 | 594 | ### Bug Fixes 595 | 596 | * check for `paths` and `baseUrl` in tsconfig ([c3710c4](https://github.com/LeDDGroup/typescript-transform-paths/commit/c3710c4)) 597 | * type only import not deleted from result file ([73dd8e2](https://github.com/LeDDGroup/typescript-transform-paths/commit/73dd8e2)), closes [#9](https://github.com/LeDDGroup/typescript-transform-paths/issues/9) 598 | 599 | 600 | 601 | 602 | # [1.1.0](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.0.2...v1.1.0) (2019-05-01) 603 | 604 | 605 | ### Features 606 | 607 | * exports expressions ([546e610](https://github.com/LeDDGroup/typescript-transform-paths/commit/546e610)), closes [#7](https://github.com/LeDDGroup/typescript-transform-paths/issues/7) 608 | 609 | 610 | 611 | 612 | ## [1.0.2](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.0.1...v1.0.2) (2019-04-24) 613 | 614 | 615 | ### Bug Fixes 616 | 617 | * produce posix path in windows ([5059c3d](https://github.com/LeDDGroup/typescript-transform-paths/commit/5059c3d)), closes [#5](https://github.com/LeDDGroup/typescript-transform-paths/issues/5) 618 | 619 | 620 | 621 | 622 | ## [1.0.1](https://github.com/LeDDGroup/typescript-transform-paths/compare/v1.0.0...v1.0.1) (2019-04-12) 623 | 624 | 625 | ### Bug Fixes 626 | 627 | * not working for same or lower directory level ([a748d6a](https://github.com/LeDDGroup/typescript-transform-paths/commit/a748d6a)), closes [#2](https://github.com/LeDDGroup/typescript-transform-paths/issues/2) 628 | 629 | 630 | 631 | 632 | # 1.0.0 (2019-02-02) 633 | 634 | 635 | ### Features 636 | 637 | * make it work ([e774cf7](https://github.com/LeDDGroup/typescript-transform-paths/commit/e774cf7)) 638 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 LeddGroup 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # typescript-transform-paths 2 | 3 | [![npm version](https://img.shields.io/npm/v/typescript-transform-paths.svg)](https://www.npmjs.com/package/typescript-transform-paths) 4 | [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2FLeDDGroup%2Ftypescript-transform-paths%2Fbadge%3Fref%3Dmaster&style=flat)](https://actions-badge.atrox.dev/LeDDGroup/typescript-transform-paths/goto?ref=master) 5 | [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) 6 | [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) 7 | 8 | Transform compiled source module resolution paths using TypeScript's `paths` config, and/or custom resolution paths. 9 | 10 | ## Setup Steps 11 | 12 | ### 1. Install 13 | 14 | ```sh 15 | add -D typescript-transform-paths 16 | ``` 17 | 18 | ### 2. Configure 19 | 20 | Add it to _plugins_ in your _tsconfig.json_ 21 | 22 | #### Example Config 23 | 24 | ```jsonc 25 | { 26 | "compilerOptions": { 27 | "baseUrl": "./", 28 | // Configure your path mapping here 29 | "paths": { 30 | "@utils/*": ["utils/*"], 31 | }, 32 | // Note: To transform paths for both the output .js and .d.ts files, you need both of the below entries 33 | "plugins": [ 34 | // Transform paths in output .js files 35 | { "transform": "typescript-transform-paths" }, 36 | 37 | // Transform paths in output .d.ts files (Include this line if you output declarations files) 38 | { "transform": "typescript-transform-paths", "afterDeclarations": true }, 39 | ], 40 | }, 41 | } 42 | ``` 43 | 44 | #### Example result 45 | 46 | `core/index.ts` 47 | 48 | ```tsx 49 | // The following transforms path to '../utils/sum' 50 | import { sum } from "@utils/sum"; 51 | ``` 52 | 53 | ### 3. Usage 54 | 55 | - **Compile with `tsc`** — Use [ts-patch](https://github.com/nonara/ts-patch) 56 | 57 | - **Use with ts-node** — Add `typescript-transform-paths/register` to `require` config. 58 | 59 | `tsconfig.json` 60 | 61 | ```jsonc 62 | { 63 | "ts-node": { 64 | "transpileOnly": true, 65 | "require": [ "typescript-transform-paths/register" ], 66 | }, 67 | "compilerOptions" { /* ... */ } 68 | } 69 | ``` 70 | 71 | - **Use with node** — Use the register script: `node -r typescript-transform-paths/register src/index.ts` 72 | 73 | - **Use with NX** — Add the `typescript-transform-paths/nx-transformer` to project config 74 | 75 | `project.json` 76 | 77 | ```jsonc 78 | { 79 | /* ... */ 80 | "targets": { 81 | "build": { 82 | /* ... */ 83 | "options": { 84 | /* ... */ 85 | "transformers": [ 86 | { 87 | "name": "typescript-transform-paths/nx-transformer", 88 | "options": { "afterDeclarations": true }, 89 | }, 90 | ], 91 | }, 92 | }, 93 | }, 94 | } 95 | ``` 96 | 97 | ## Virtual Directories 98 | 99 | TS allows defining 100 | [virtual directories](https://www.typescriptlang.org/docs/handbook/module-resolution.html#virtual-directories-with-rootdirs) 101 | via the `rootDirs` compiler option. 102 | To enable virtual directory mapping, use the `useRootDirs` plugin option. 103 | 104 | ```jsonc 105 | { 106 | "compilerOptions": { 107 | "rootDirs": ["src", "generated"], 108 | "baseUrl": ".", 109 | "paths": { 110 | "#root/*": ["./src/*", "./generated/*"], 111 | }, 112 | "plugins": [ 113 | { "transform": "typescript-transform-paths", "useRootDirs": true }, 114 | { "transform": "typescript-transform-paths", "useRootDirs": true, "afterDeclarations": true }, 115 | ], 116 | }, 117 | } 118 | ``` 119 | 120 | #### Example 121 | 122 | ``` 123 | - src/ 124 | - subdir/ 125 | - sub-file.ts 126 | - file1.ts 127 | - generated/ 128 | - file2.ts 129 | ``` 130 | 131 | `src/file1.ts` 132 | 133 | ```ts 134 | import "#root/file2.ts"; // resolves to './file2' 135 | ``` 136 | 137 | `src/subdir/sub-file.ts` 138 | 139 | ```ts 140 | import "#root/file2.ts"; // resolves to '../file2' 141 | import "#root/file1.ts"; // resolves to '../file1' 142 | ``` 143 | 144 | ## Custom Control 145 | 146 | ### Exclusion patterns 147 | 148 | You can disable transformation for paths based on the resolved file path. The `exclude` option allows specifying glob 149 | patterns to match against resolved file path. 150 | 151 | For an example context in which this would be useful, see [Issue #83](https://github.com/LeDDGroup/typescript-transform-paths/issues/83) 152 | 153 | Example: 154 | 155 | ```jsonc 156 | { 157 | "compilerOptions": { 158 | "paths": { 159 | "sub-module1/*": ["../../node_modules/sub-module1/*"], 160 | "sub-module2/*": ["../../node_modules/sub-module2/*"], 161 | }, 162 | "plugins": [ 163 | { 164 | "transform": "typescript-transform-paths", 165 | "exclude": ["**/node_modules/**"], 166 | }, 167 | ], 168 | }, 169 | } 170 | ``` 171 | 172 | ```ts 173 | // This path will not be transformed 174 | import * as sm1 from "sub-module1/index"; 175 | ``` 176 | 177 | ### @transform-path tag 178 | 179 | Use the `@transform-path` tag to explicitly specify the output path for a single statement. 180 | 181 | ```ts 182 | // @transform-path https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js 183 | import react from "react"; // Output path will be the url above 184 | ``` 185 | 186 | ### @no-transform-path 187 | 188 | Use the `@no-transform-path` tag to explicitly disable transformation for a single statement. 189 | 190 | ```ts 191 | // @no-transform-path 192 | import "normally-transformed"; // This will remain 'normally-transformed', even though it has a different value in paths config 193 | ``` 194 | 195 | ## Version Compatibility 196 | 197 | | `typescript-transform-paths` | TypeScript | NodeJS | 198 | | ---------------------------- | --------------------- | ------ | 199 | | ^3.5.2 | >=3.6.5, >=4.x, >=5.x | >=18 | 200 | 201 | ## Project Guidelines for Contributors 202 | 203 | - Package Manager: `yarn` (`yarn install`) 204 | - Format and lint the code before commit: `prettier` (`yarn format && yarn lint`) 205 | - Commit messages: [Conventional Commit Specs](https://www.conventionalcommits.org/en/v1.0.0/) 206 | - Releases: `changelogen` (`yarn release`) 207 | 208 | ```shell 209 | GH_TOKEN=$(gh auth token) yarn release 210 | ``` 211 | 212 | ## Alternatives 213 | 214 | - [NodeJS subpath imports](https://nodejs.org/api/packages.html#subpath-imports) 215 | - [Yarn link: protocol](https://yarnpkg.com/protocol/link) 216 | 217 | ## Maintainers 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 |

Ron S.

Daniel Perez Alvarez
227 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import globals from "globals"; 3 | import pluginJs from "@eslint/js"; 4 | import tseslint from "typescript-eslint"; 5 | 6 | export default [ 7 | { files: ["**/*.{js,mjs,cjs,ts}"] }, 8 | { ignores: ["**/dist/", "test/projects/"] }, 9 | { languageOptions: { globals: globals.node } }, 10 | pluginJs.configs.recommended, 11 | ...tseslint.configs.recommended, 12 | { 13 | rules: { 14 | "@typescript-eslint/no-empty-object-type": ["error", { allowInterfaces: "with-single-extends" }], 15 | "@typescript-eslint/no-namespace": ["error", { allowDeclarations: true }], 16 | "no-empty": ["error", { allowEmptyCatch: true }], 17 | "prefer-const": ["error", { destructuring: "all" }], 18 | }, 19 | }, 20 | { 21 | // overrides for cjs files 22 | files: ["*.js"], 23 | rules: { 24 | "@typescript-eslint/no-require-imports": "off", 25 | }, 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /nx-transformer.js: -------------------------------------------------------------------------------- 1 | // Keeping register here in the root for backwards compatibiliy, TODO remove in the next major version 2 | console.warn( 3 | "typescript-transform-paths: Calling the top level nx-transformer file is deprecated and will be removed in the future. Use a tool that supports package.json exports", 4 | ); 5 | 6 | module.exports = require("./dist/plugins/nx-transfomer-plugin").default; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-transform-paths", 3 | "version": "3.5.5", 4 | "description": "Transforms module resolution paths using TypeScript path mapping and/or custom paths", 5 | "type": "commonjs", 6 | "main": "./dist/index.js", 7 | "types": "types/index.d.ts", 8 | "exports": { 9 | ".": { 10 | "types": "./dist/index.d.ts", 11 | "default": "./dist/index.js" 12 | }, 13 | "./register": "./dist/register-entry.js", 14 | "./nx-transformer": "./dist/plugins/nx-transformer-plugin.js" 15 | }, 16 | "files": [ 17 | "dist", 18 | "types", 19 | "README.md", 20 | "CHANGELOG.md", 21 | "register.js", 22 | "nx-transformer.js" 23 | ], 24 | "scripts": { 25 | "compile": "tsc", 26 | "build": "yarn clean && yarn compile", 27 | "test": "yarn test/ test", 28 | "release": "changelogen --release --push", 29 | "--------------": "", 30 | "format": "prettier --write .", 31 | "lint": "prettier --check . && eslint .", 32 | "clean": "rm -rf \"**/dist\"", 33 | "clean:all": "yarn clean && rm -rf node_modules \"**/node_modules\" \"**/yarn.lock\" yarn.lock", 34 | "reset": "yarn clean:all && yarn install && yarn build", 35 | "-------------- ": "", 36 | "prepack": "yarn build" 37 | }, 38 | "keywords": [ 39 | "typescript", 40 | "transform", 41 | "transformer", 42 | "plugin", 43 | "path", 44 | "paths", 45 | "virtual directory", 46 | "import", 47 | "require" 48 | ], 49 | "homepage": "https://github.com/LeDDGroup/typescript-transform-paths#readme", 50 | "bugs": { 51 | "url": "https://github.com/LeDDGroup/typescript-transform-paths/issues" 52 | }, 53 | "repository": { 54 | "type": "git", 55 | "url": "git+https://github.com/LeDDGroup/typescript-transform-paths.git" 56 | }, 57 | "license": "MIT", 58 | "contributors": [ 59 | "Daniel Perez", 60 | "Ron S. (https://twitter.com/Ron)" 61 | ], 62 | "devDependencies": { 63 | "@eslint/js": "^9.9.1", 64 | "@tsconfig/node18": "^18.2.4", 65 | "@tsconfig/strictest": "^2.0.5", 66 | "@types/eslint": "^9", 67 | "@types/minimatch": "^5.1.2", 68 | "@types/node": "^22.5.2", 69 | "@types/ts-expose-internals": "npm:ts-expose-internals@4.9.5", 70 | "@types/ts-node": "npm:ts-node@^10.9.2", 71 | "@types/typescript-3": "npm:typescript@3.x", 72 | "@types/typescript-4.7": "npm:typescript@4.9.x", 73 | "changelogen": "^0.5.5", 74 | "eslint": "9.x", 75 | "globals": "^15.9.0", 76 | "prettier": "^3.3.3", 77 | "prettier-plugin-jsdoc": "^1.3.0", 78 | "ts-patch": "^3.2.1", 79 | "typescript": "^5.5.4", 80 | "typescript-eslint": "^8.3.0" 81 | }, 82 | "peerDependencies": { 83 | "typescript": ">=3.6.5" 84 | }, 85 | "dependencies": { 86 | "minimatch": "^9.0.5" 87 | }, 88 | "prettier": { 89 | "plugins": [ 90 | "prettier-plugin-jsdoc" 91 | ] 92 | }, 93 | "packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538" 94 | } 95 | -------------------------------------------------------------------------------- /register.js: -------------------------------------------------------------------------------- 1 | // Keeping register here in the root for backwards compatibiliy, TODO remove in the next major version 2 | console.warn( 3 | "typescript-transform-paths: Calling the top level register file is deprecated and will be removed in the future. Use a tool that supports package.json exports", 4 | ); 5 | require("./dist/register-entry"); 6 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:recommended"], 4 | "packageRules": [ 5 | { 6 | "matchUpdateTypes": ["minor", "patch"], 7 | "matchCurrentVersion": "!/^0/", 8 | "automerge": true 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/harmony/harmony-factory.ts: -------------------------------------------------------------------------------- 1 | import TS from "typescript"; 2 | import { TsTransformPathsContext } from "../types"; 3 | import { TsFourSeven, TsThreeEight } from "./versions"; 4 | 5 | /* ****************************************************************************************************************** */ 6 | // region: Types 7 | /* ****************************************************************************************************************** */ 8 | 9 | export interface HarmonyFactory extends TS.NodeFactory {} 10 | 11 | // endregion 12 | 13 | /* ****************************************************************************************************************** */ 14 | // region: Utilities 15 | /* ****************************************************************************************************************** */ 16 | 17 | /** Creates a node factory compatible with TS v3+ */ 18 | export function createHarmonyFactory(context: TsTransformPathsContext): HarmonyFactory { 19 | return new Proxy(context.tsFactory ?? context.tsInstance, { 20 | get(target, prop) { 21 | if (TsThreeEight.predicate(context)) { 22 | return TsThreeEight.handler(context, prop); 23 | } else if (TsFourSeven.predicate(context)) { 24 | return TsFourSeven.handler(context, prop); 25 | } else { 26 | // @ts-expect-error TS(7053) FIXME: Element implicitly has an 'any' type because expression of type 'string | symbol' can't be used to index type 'typeof import("typescript") | NodeFactory'. 27 | return target[prop]; 28 | } 29 | }, 30 | }) as HarmonyFactory; 31 | } 32 | 33 | // endregion 34 | -------------------------------------------------------------------------------- /src/harmony/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./versions"; 2 | export * from "./harmony-factory"; 3 | -------------------------------------------------------------------------------- /src/harmony/utils.ts: -------------------------------------------------------------------------------- 1 | /* ****************************************************************************************************************** */ 2 | // region: Utility Types 3 | /* ****************************************************************************************************************** */ 4 | // @formatter:off 5 | 6 | // @prettier-ignore 7 | export type DownSampleTsTypes = { 8 | [i in keyof Tuple]: Tuple[i] extends unknown[] 9 | ? DownSampleTsTypes 10 | : DownSampleTsType; 11 | } & { 12 | length: Tuple["length"]; 13 | }; 14 | 15 | // @prettier-ignore 16 | type DownSampleTsType = 17 | T extends Exclude ? Extract[1] : T; 18 | 19 | // @formatter:on 20 | // endregion 21 | -------------------------------------------------------------------------------- /src/harmony/versions/four-seven.ts: -------------------------------------------------------------------------------- 1 | /** Changes after this point: https://github.com/microsoft/TypeScript/wiki/API-Breaking-Changes#typescript-48 */ 2 | import type { 3 | default as TsCurrentModule, 4 | AssertClause, 5 | ExportDeclaration, 6 | Expression, 7 | ImportClause, 8 | ImportDeclaration, 9 | Modifier, 10 | ModuleBody, 11 | ModuleDeclaration, 12 | ModuleName, 13 | NamedExportBindings, 14 | } from "typescript"; 15 | import type TsFourSevenModule from "typescript-4.7"; 16 | import type { TsTransformPathsContext } from "../../types"; 17 | import type { DownSampleTsTypes } from "../utils"; 18 | 19 | /* ****************************************************************************************************************** */ 20 | // region: Mapping 21 | /* ****************************************************************************************************************** */ 22 | 23 | export type TypeMap = [ 24 | [TsCurrentModule.ImportDeclaration, TsFourSevenModule.ImportDeclaration], 25 | [TsCurrentModule.Modifier, TsFourSevenModule.Modifier], 26 | [TsCurrentModule.ImportClause, TsFourSevenModule.ImportClause], 27 | [TsCurrentModule.Expression, TsFourSevenModule.Expression], 28 | [TsCurrentModule.AssertClause, TsFourSevenModule.AssertClause], 29 | [TsCurrentModule.ExportDeclaration, TsFourSevenModule.ExportDeclaration], 30 | [TsCurrentModule.NamedExportBindings, TsFourSevenModule.NamedExportBindings], 31 | [TsCurrentModule.ModuleDeclaration, TsFourSevenModule.ModuleDeclaration], 32 | [TsCurrentModule.ModuleName, TsFourSevenModule.ModuleName], 33 | [TsCurrentModule.ModuleBody, TsFourSevenModule.ModuleBody], 34 | ]; 35 | 36 | // endregion 37 | 38 | /* ****************************************************************************************************************** */ 39 | // region: Utils 40 | /* ****************************************************************************************************************** */ 41 | 42 | export const predicate = ({ tsVersionMajor, tsVersionMinor }: TsTransformPathsContext) => 43 | tsVersionMajor == 4 && tsVersionMinor < 8; 44 | 45 | export function handler(context: TsTransformPathsContext, prop: string | symbol) { 46 | const factory = context.tsFactory as unknown as TsFourSevenModule.NodeFactory; 47 | 48 | switch (prop) { 49 | case "updateImportDeclaration": { 50 | return function ( 51 | node: ImportDeclaration, 52 | _modifiers: readonly Modifier[] | undefined, 53 | importClause: ImportClause | undefined, 54 | moduleSpecifier: Expression, 55 | assertClause: AssertClause | undefined, 56 | ) { 57 | const [dsNode, dsImportClause, dsModuleSpecifier, dsAssertClause] = downSample( 58 | node, 59 | importClause, 60 | moduleSpecifier, 61 | assertClause, 62 | ); 63 | 64 | return factory.updateImportDeclaration( 65 | dsNode, 66 | dsNode.decorators, 67 | dsNode.modifiers, 68 | dsImportClause, 69 | dsModuleSpecifier, 70 | dsAssertClause, 71 | ); 72 | }; 73 | } 74 | case "updateExportDeclaration": { 75 | return function ( 76 | node: ExportDeclaration, 77 | _modifiers: readonly Modifier[] | undefined, 78 | isTypeOnly: boolean, 79 | exportClause: NamedExportBindings | undefined, 80 | moduleSpecifier: Expression | undefined, 81 | assertClause: AssertClause | undefined, 82 | ) { 83 | const [dsNode, dsExportClause, dsModuleSpecifier, dsAssertClause] = downSample( 84 | node, 85 | exportClause, 86 | moduleSpecifier, 87 | assertClause, 88 | ); 89 | 90 | return factory.updateExportDeclaration( 91 | dsNode, 92 | dsNode.decorators, 93 | dsNode.modifiers, 94 | isTypeOnly, 95 | dsExportClause, 96 | dsModuleSpecifier, 97 | dsAssertClause, 98 | ); 99 | }; 100 | } 101 | case "updateModuleDeclaration": { 102 | return function ( 103 | node: ModuleDeclaration, 104 | _modifiers: readonly Modifier[] | undefined, 105 | name: ModuleName, 106 | body: ModuleBody | undefined, 107 | ) { 108 | const [dsNode, dsName, dsBody] = downSample(node, name, body); 109 | 110 | return factory.updateModuleDeclaration(dsNode, dsNode.decorators, dsNode.modifiers, dsName, dsBody); 111 | }; 112 | } 113 | default: { 114 | // @ts-expect-error TS(7019) FIXME: Rest parameter 'args' implicitly has an 'any[]' type. 115 | return (...args) => factory[prop](...args); 116 | } 117 | } 118 | } 119 | 120 | export function downSample(...args: T): DownSampleTsTypes { 121 | // @ts-expect-error TS(2322) FIXME: Type 'T' is not assignable to type 'DownSampleTsTypes'. 122 | return args; 123 | } 124 | 125 | // endregion 126 | -------------------------------------------------------------------------------- /src/harmony/versions/index.ts: -------------------------------------------------------------------------------- 1 | export * as TsThreeEight from "./three-eight"; 2 | export * as TsFourSeven from "./four-seven"; 3 | -------------------------------------------------------------------------------- /src/harmony/versions/three-eight.ts: -------------------------------------------------------------------------------- 1 | /** Changes after this point: https://github.com/microsoft/TypeScript/wiki/API-Breaking-Changes#typescript-40 */ 2 | import type { 3 | default as TsCurrentModule, 4 | EntityName, 5 | ExportDeclaration, 6 | Expression, 7 | Identifier, 8 | ImportClause, 9 | ImportDeclaration, 10 | ImportTypeAssertionContainer, 11 | ImportTypeNode, 12 | Modifier, 13 | ModuleBody, 14 | ModuleDeclaration, 15 | ModuleName, 16 | NamedExportBindings, 17 | NamedImportBindings, 18 | TypeNode, 19 | } from "typescript"; 20 | import type TsThreeEightModule from "typescript-3"; 21 | import type { TsTransformPathsContext } from "../../types"; 22 | import type { DownSampleTsTypes } from "../utils"; 23 | 24 | /* ****************************************************************************************************************** */ 25 | // region: Mapping 26 | /* ****************************************************************************************************************** */ 27 | 28 | export type TypeMap = [ 29 | [TsCurrentModule.SourceFile, TsThreeEightModule.SourceFile], 30 | [TsCurrentModule.StringLiteral, TsThreeEightModule.StringLiteral], 31 | [TsCurrentModule.CompilerOptions, TsThreeEightModule.CompilerOptions], 32 | // @ts-expect-error typescript 3 doesn't export EmitResolver 33 | [TsCurrentModule.EmitResolver, TsThreeEightModule.EmitResolver], 34 | [TsCurrentModule.CallExpression, TsThreeEightModule.CallExpression], 35 | [TsCurrentModule.ExternalModuleReference, TsThreeEightModule.ExternalModuleReference], 36 | [TsCurrentModule.LiteralTypeNode, TsThreeEightModule.LiteralTypeNode], 37 | [TsCurrentModule.ExternalModuleReference, TsThreeEightModule.ExternalModuleReference], 38 | [TsCurrentModule.ImportTypeNode, TsThreeEightModule.ImportTypeNode], 39 | [TsCurrentModule.EntityName, TsThreeEightModule.EntityName], 40 | [TsCurrentModule.TypeNode, TsThreeEightModule.TypeNode], 41 | [readonly TsCurrentModule.TypeNode[], readonly TsThreeEightModule.TypeNode[]], 42 | [TsCurrentModule.LiteralTypeNode, TsThreeEightModule.LiteralTypeNode], 43 | [TsCurrentModule.ImportDeclaration, TsThreeEightModule.ImportDeclaration], 44 | [TsCurrentModule.ImportClause, TsThreeEightModule.ImportClause], 45 | [TsCurrentModule.Identifier, TsThreeEightModule.Identifier], 46 | [TsCurrentModule.NamedImportBindings, TsThreeEightModule.NamedImportBindings], 47 | [TsCurrentModule.ImportDeclaration, TsThreeEightModule.ImportDeclaration], 48 | [TsCurrentModule.ExportDeclaration, TsThreeEightModule.ExportDeclaration], 49 | [TsCurrentModule.ModuleDeclaration, TsThreeEightModule.ModuleDeclaration], 50 | [TsCurrentModule.Expression, TsThreeEightModule.Expression], 51 | [TsCurrentModule.ModuleBody, TsThreeEightModule.ModuleBody], 52 | [TsCurrentModule.ModuleName, TsThreeEightModule.ModuleName], 53 | [TsCurrentModule.ExportDeclaration["exportClause"], TsThreeEightModule.ExportDeclaration["exportClause"]], 54 | ]; 55 | 56 | // endregion 57 | 58 | /* ****************************************************************************************************************** */ 59 | // region: Utils 60 | /* ****************************************************************************************************************** */ 61 | 62 | export const predicate = (context: TsTransformPathsContext) => context.tsVersionMajor < 4; 63 | 64 | export function handler(context: TsTransformPathsContext, prop: string | symbol) { 65 | const ts = context.tsInstance as unknown as typeof TsThreeEightModule; 66 | 67 | switch (prop) { 68 | case "updateCallExpression": { 69 | // @ts-expect-error TS(7019) FIXME: Rest parameter 'args' implicitly has an 'any[]' type. 70 | return (...args) => ts.updateCall.apply(void 0, args); 71 | } 72 | case "updateImportClause": { 73 | return function ( 74 | node: ImportClause, 75 | _isTypeOnly: boolean, 76 | name: Identifier | undefined, 77 | namedBindings: NamedImportBindings | undefined, 78 | ) { 79 | // @ts-expect-error TODO investigate type issue 80 | return ts.updateImportClause.apply(void 0, downSample(node, name, namedBindings)); 81 | }; 82 | } 83 | case "updateImportDeclaration": { 84 | return function ( 85 | node: ImportDeclaration, 86 | _modifiers: readonly Modifier[] | undefined, 87 | importClause: ImportClause | undefined, 88 | moduleSpecifier: Expression, 89 | ) { 90 | const [dsNode, dsImportClause, dsModuleSpecifier] = downSample(node, importClause, moduleSpecifier); 91 | 92 | return ts.updateImportDeclaration( 93 | dsNode, 94 | dsNode.decorators, 95 | dsNode.modifiers, 96 | dsImportClause, 97 | dsModuleSpecifier, 98 | ); 99 | }; 100 | } 101 | case "updateExportDeclaration": { 102 | return function ( 103 | node: ExportDeclaration, 104 | _modifiers: readonly Modifier[] | undefined, 105 | _isTypeOnly: boolean, 106 | exportClause: NamedExportBindings | undefined, 107 | moduleSpecifier: Expression | undefined, 108 | ) { 109 | const [dsNode, dsModuleSpecifier, dsExportClause] = downSample(node, moduleSpecifier, exportClause); 110 | return ts.updateExportDeclaration( 111 | dsNode, 112 | dsNode.decorators, 113 | dsNode.modifiers, 114 | dsExportClause, 115 | dsModuleSpecifier, 116 | dsNode.isTypeOnly, 117 | ); 118 | }; 119 | } 120 | case "updateModuleDeclaration": { 121 | return function ( 122 | node: ModuleDeclaration, 123 | _modifiers: readonly Modifier[] | undefined, 124 | name: ModuleName, 125 | body: ModuleBody | undefined, 126 | ) { 127 | const [dsNode, dsName, dsBody] = downSample(node, name, body); 128 | 129 | return ts.updateModuleDeclaration(dsNode, dsNode.decorators, dsNode.modifiers, dsName, dsBody); 130 | }; 131 | } 132 | case "updateImportTypeNode": { 133 | return function ( 134 | node: ImportTypeNode, 135 | argument: TypeNode, 136 | _assertions: ImportTypeAssertionContainer | undefined, 137 | qualifier: EntityName | undefined, 138 | typeArguments: readonly TypeNode[] | undefined, 139 | isTypeOf?: boolean, 140 | ) { 141 | const [dsNode, dsArgument, dsQualifier, dsTypeArguments] = downSample(node, argument, qualifier, typeArguments); 142 | 143 | return ts.updateImportTypeNode(dsNode, dsArgument, dsQualifier, dsTypeArguments, isTypeOf); 144 | }; 145 | } 146 | default: { 147 | // @ts-expect-error TS(7019) FIXME: Rest parameter 'args' implicitly has an 'any[]' type. 148 | return (...args) => ts[prop](...args); 149 | } 150 | } 151 | } 152 | 153 | export function downSample(...args: T): DownSampleTsTypes { 154 | // @ts-expect-error TS(2322) FIXME: Type 'T' is not assignable to type 'DownSampleTsTypes'. 155 | return args; 156 | } 157 | 158 | // endregion 159 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export type { TsTransformPathsConfig } from "./types"; 2 | export { register } from "./register"; 3 | export { nxTransformerPlugin } from "./plugins"; 4 | 5 | export { default } from "./transformer"; 6 | -------------------------------------------------------------------------------- /src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | export * as nxTransformerPlugin from "./nx-transformer-plugin"; 2 | -------------------------------------------------------------------------------- /src/plugins/nx-transformer-plugin.ts: -------------------------------------------------------------------------------- 1 | import transformer from "../transformer"; 2 | import ts from "typescript"; 3 | 4 | export interface TsTransformPathsConfig { 5 | readonly useRootDirs?: boolean; 6 | readonly exclude?: string[]; 7 | readonly afterDeclarations?: boolean; 8 | readonly tsConfig?: string; 9 | readonly transform?: string; 10 | } 11 | 12 | export type NxTransformerFactory = ( 13 | config?: Omit, 14 | program?: ts.Program, 15 | ) => ts.TransformerFactory; 16 | 17 | export interface NxTransformerPlugin { 18 | before: NxTransformerFactory; 19 | afterDeclarations: NxTransformerFactory; 20 | } 21 | 22 | /* ****************************************************************************************************************** * 23 | * Locals 24 | * ****************************************************************************************************************** */ 25 | 26 | const voidTransformer: ts.TransformerFactory = () => (s: ts.SourceFile) => s; 27 | 28 | /* ****************************************************************************************************************** * 29 | * Transformer 30 | * ****************************************************************************************************************** */ 31 | 32 | export const before: NxTransformerFactory = (pluginConfig, program) => 33 | pluginConfig?.afterDeclarations ? voidTransformer : transformer(program, { ...pluginConfig }); 34 | 35 | export const afterDeclarations: NxTransformerFactory = (pluginConfig, program) => 36 | pluginConfig?.afterDeclarations ? transformer(program, { ...pluginConfig }) : voidTransformer; 37 | -------------------------------------------------------------------------------- /src/register-entry.ts: -------------------------------------------------------------------------------- 1 | let tsNode; 2 | try { 3 | // eslint-disable-next-line @typescript-eslint/no-require-imports 4 | tsNode = require("ts-node"); 5 | } catch { 6 | throw new Error( 7 | `Cannot resolve ts-node. Make sure ts-node is installed before using typescript-transform-paths/register`, 8 | ); 9 | } 10 | 11 | tsNode.register(); 12 | // eslint-disable-next-line @typescript-eslint/no-require-imports 13 | require("./").register(); 14 | -------------------------------------------------------------------------------- /src/register.ts: -------------------------------------------------------------------------------- 1 | import TSNode from "ts-node"; 2 | import type { REGISTER_INSTANCE } from "ts-node"; 3 | import ts from "typescript"; 4 | import transformer from "./transformer"; 5 | 6 | /* ****************************************************************************************************************** */ 7 | // region: Helpers 8 | /* ****************************************************************************************************************** */ 9 | 10 | type PluginExtended = ts.PluginImport & { 11 | transform?: string; 12 | after?: boolean; 13 | before?: boolean; 14 | afterDeclarations?: boolean; 15 | }; 16 | 17 | function getProjectTransformerConfig(pcl: ts.ParsedCommandLine) { 18 | const plugins = pcl.options.plugins as PluginExtended[]; 19 | if (!plugins) return; 20 | 21 | const res: { afterDeclarations?: PluginExtended; before?: PluginExtended } = {}; 22 | for (const plugin of plugins) { 23 | if (plugin.transform === "typescript-transform-paths" && !plugin.after) 24 | res[plugin.afterDeclarations ? "afterDeclarations" : "before"] = plugin; 25 | } 26 | 27 | return res; 28 | } 29 | 30 | function getTransformers( 31 | program?: ts.Program, 32 | beforeConfig?: PluginExtended, 33 | afterDeclarationsConfig?: PluginExtended, 34 | ): ts.CustomTransformers { 35 | return { 36 | ...(beforeConfig && { before: [transformer(program, beforeConfig)] }), 37 | ...(afterDeclarationsConfig && { afterDeclarations: [transformer(program, afterDeclarationsConfig)] }), 38 | } as ts.CustomTransformers; 39 | } 40 | 41 | export function mergeTransformers( 42 | baseTransformers: ts.CustomTransformers, 43 | transformers: ts.CustomTransformers, 44 | ): ts.CustomTransformers { 45 | const res = { 46 | ...((baseTransformers.before || transformers.before) && { 47 | before: [...(transformers.before ?? []), ...(baseTransformers.before ?? [])], 48 | }), 49 | ...((baseTransformers.afterDeclarations || transformers.afterDeclarations) && { 50 | afterDeclarations: [...(transformers.afterDeclarations ?? []), ...(baseTransformers.afterDeclarations ?? [])], 51 | }), 52 | }; 53 | 54 | const remainingBaseTransformers = { ...baseTransformers }; 55 | delete remainingBaseTransformers.before; 56 | delete remainingBaseTransformers.afterDeclarations; 57 | 58 | return Object.assign(res, remainingBaseTransformers); 59 | } 60 | 61 | // endregion 62 | 63 | /* ****************************************************************************************************************** */ 64 | // region: TsNode Registration Utility 65 | /* ****************************************************************************************************************** */ 66 | 67 | export function register(): TSNode.RegisterOptions | undefined { 68 | const { tsNodeInstance, tsNode } = register.initialize(); 69 | 70 | const transformerConfig = getProjectTransformerConfig(tsNodeInstance.config); 71 | if (!transformerConfig) return; 72 | 73 | const { before: beforeConfig, afterDeclarations: afterDeclarationsConfig } = transformerConfig; 74 | 75 | const registerOptions: TSNode.RegisterOptions = Object.assign({}, tsNodeInstance.options); 76 | if (registerOptions.transformers) { 77 | if (typeof registerOptions.transformers === "function") { 78 | const oldTransformersFactory = registerOptions.transformers; 79 | registerOptions.transformers = (program) => { 80 | const transformers = getTransformers(program, beforeConfig, afterDeclarationsConfig); 81 | const baseTransformers = oldTransformersFactory(program); 82 | return mergeTransformers(baseTransformers, transformers); 83 | }; 84 | } else { 85 | registerOptions.transformers = mergeTransformers( 86 | registerOptions.transformers, 87 | getTransformers(undefined, beforeConfig, afterDeclarationsConfig), 88 | ); 89 | } 90 | } else { 91 | registerOptions.transformers = getTransformers(undefined, beforeConfig, afterDeclarationsConfig); 92 | } 93 | 94 | // Re-register with new transformers 95 | tsNode.register(registerOptions); 96 | return registerOptions; 97 | } 98 | 99 | register.initialize = function initialize(): { 100 | tsNode: typeof TSNode; 101 | instanceSymbol: typeof REGISTER_INSTANCE; 102 | tsNodeInstance: TSNode.Service; 103 | } { 104 | let tsNode: typeof TSNode; 105 | try { 106 | // eslint-disable-next-line @typescript-eslint/no-require-imports 107 | tsNode = require("ts-node"); 108 | } catch { 109 | throw new Error( 110 | `Cannot resolve ts-node. Make sure ts-node is installed before using typescript-transform-paths/register`, 111 | ); 112 | } 113 | 114 | const instanceSymbol: typeof REGISTER_INSTANCE = tsNode["REGISTER_INSTANCE"]; 115 | 116 | let tsNodeInstance = global.process[instanceSymbol]; 117 | if (!tsNodeInstance) { 118 | tsNode.register(); // Register initially 119 | tsNodeInstance = global.process[instanceSymbol]; 120 | } 121 | if (!tsNodeInstance) throw new Error(`Could not register ts-node instance!`); 122 | 123 | return { tsNode, instanceSymbol, tsNodeInstance }; 124 | }; 125 | 126 | export default register; 127 | 128 | // endregion 129 | -------------------------------------------------------------------------------- /src/transformer.ts: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | import ts, { CompilerOptions } from "typescript"; 3 | import { RunMode, TsNodeState, TsTransformPathsConfig, TsTransformPathsContext, VisitorContext } from "./types"; 4 | import { nodeVisitor } from "./visitor"; 5 | import { createHarmonyFactory } from "./harmony"; 6 | import { Minimatch } from "minimatch"; 7 | import { createSyntheticEmitHost, getTsNodeRegistrationProperties } from "./utils/ts-helpers"; 8 | import { TransformerExtras } from "ts-patch"; 9 | 10 | function getTsProperties(args: Parameters) { 11 | let fileNames: readonly string[] | undefined; 12 | let compilerOptions: CompilerOptions; 13 | let runMode: RunMode; 14 | let tsNodeState: TsNodeState | undefined; 15 | 16 | const { 0: program, 2: extras, 3: manualTransformOptions } = args; 17 | 18 | const tsInstance = extras?.ts ?? ts; 19 | 20 | if (program) compilerOptions = program.getCompilerOptions(); 21 | const tsNodeProps = getTsNodeRegistrationProperties(tsInstance); 22 | 23 | /* Determine RunMode & Setup */ 24 | // Note: ts-node passes a Program with the paths property stripped, so we do some comparison to determine if it's the caller 25 | const isTsNode = 26 | tsNodeProps && (!program || compilerOptions!.configFilePath === tsNodeProps.compilerOptions.configFilePath); 27 | 28 | // RunMode: Program 29 | if (program && !isTsNode) { 30 | runMode = RunMode.Program; 31 | compilerOptions = compilerOptions!; 32 | } 33 | // RunMode: Manual 34 | else if (manualTransformOptions) { 35 | runMode = RunMode.Manual; 36 | fileNames = manualTransformOptions.fileNames; 37 | compilerOptions = manualTransformOptions.compilerOptions!; 38 | } 39 | // RunMode: TsNode 40 | else if (isTsNode) { 41 | fileNames = tsNodeProps.fileNames; 42 | runMode = RunMode.TsNode; 43 | 44 | tsNodeState = 45 | !program || 46 | (fileNames.length > 1 && program?.getRootFileNames().length === 1) || 47 | (!compilerOptions!.paths && tsNodeProps!.compilerOptions.paths) 48 | ? TsNodeState.Stripped 49 | : TsNodeState.Full; 50 | 51 | compilerOptions = 52 | tsNodeState === TsNodeState.Full 53 | ? compilerOptions! 54 | : { 55 | ...program?.getCompilerOptions(), 56 | ...tsNodeProps!.compilerOptions, 57 | }; 58 | } else { 59 | throw new Error( 60 | `Cannot transform without a Program, ts-node instance, or manual parameters supplied. ` + 61 | `Make sure you're using ts-patch or ts-node with transpileOnly.`, 62 | ); 63 | } 64 | 65 | return { tsInstance, compilerOptions, fileNames, runMode, tsNodeState }; 66 | } 67 | 68 | export default function transformer( 69 | program?: ts.Program, 70 | pluginConfig?: TsTransformPathsConfig, 71 | transformerExtras?: TransformerExtras, 72 | /** Supply if manually transforming with compiler API via 'transformNodes' / 'transformModule' */ 73 | manualTransformOptions?: { 74 | compilerOptions?: ts.CompilerOptions; 75 | fileNames?: string[]; 76 | }, 77 | ) { 78 | return (transformationContext: ts.TransformationContext) => { 79 | // prettier-ignore 80 | const { 81 | tsInstance, 82 | compilerOptions, 83 | fileNames, 84 | runMode, 85 | tsNodeState 86 | } = getTsProperties([ program, pluginConfig, transformerExtras, manualTransformOptions ]); 87 | 88 | const rootDirs = compilerOptions.rootDirs?.filter(path.isAbsolute); 89 | const config: TsTransformPathsConfig = pluginConfig ?? {}; 90 | const getCanonicalFileName = tsInstance.createGetCanonicalFileName(tsInstance.sys.useCaseSensitiveFileNames); 91 | 92 | /* Add supplements for various run modes */ 93 | let emitHost = transformationContext.getEmitHost(); 94 | if (!emitHost || tsNodeState === TsNodeState.Stripped) { 95 | if (!fileNames) 96 | throw new Error( 97 | `No EmitHost found and could not determine files to be processed. Please file an issue with a reproduction!`, 98 | ); 99 | emitHost = createSyntheticEmitHost(compilerOptions, tsInstance, getCanonicalFileName, fileNames as string[]); 100 | } 101 | 102 | /* Create Visitor Context */ 103 | const { configFile, paths } = compilerOptions; 104 | const { tryParsePatterns } = tsInstance; 105 | const [tsVersionMajor, tsVersionMinor] = tsInstance.versionMajorMinor.split(".").map((v) => +v); 106 | 107 | if (tsVersionMajor === undefined || tsVersionMinor === undefined) throw new Error("Expected version to be parsed"); 108 | 109 | const tsTransformPathsContext: TsTransformPathsContext = { 110 | compilerOptions, 111 | config, 112 | elisionMap: new Map(), 113 | tsFactory: transformationContext.factory, 114 | program, 115 | rootDirs, 116 | transformationContext, 117 | tsInstance, 118 | tsVersionMajor, 119 | tsVersionMinor, 120 | emitHost, 121 | runMode, 122 | tsNodeState, 123 | excludeMatchers: config.exclude?.map((globPattern) => new Minimatch(globPattern, { matchBase: true })), 124 | outputFileNamesCache: new Map(), 125 | // Get paths patterns appropriate for TS compiler version 126 | pathsPatterns: 127 | paths && 128 | (tryParsePatterns 129 | ? configFile?.configFileSpecs?.pathPatterns || tryParsePatterns(paths) 130 | : tsInstance.getOwnKeys(paths)), 131 | }; 132 | 133 | return (sourceFile: ts.SourceFile) => { 134 | const visitorContext: VisitorContext = { 135 | ...tsTransformPathsContext, 136 | sourceFile, 137 | isDeclarationFile: sourceFile.isDeclarationFile, 138 | originalSourceFile: ts.getParseTreeNode(sourceFile, ts.isSourceFile) || sourceFile, 139 | getVisitor() { 140 | return nodeVisitor.bind(this); 141 | }, 142 | factory: createHarmonyFactory(tsTransformPathsContext), 143 | }; 144 | 145 | return tsInstance.visitEachChild(sourceFile, visitorContext.getVisitor(), transformationContext); 146 | }; 147 | }; 148 | } 149 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import ts, { CompilerOptions, EmitHost, Pattern, SourceFile } from "typescript"; 2 | import { PluginConfig } from "ts-patch"; 3 | import { Minimatch } from "minimatch"; 4 | 5 | import { HarmonyFactory } from "./harmony"; 6 | 7 | /* ****************************************************************************************************************** */ 8 | // region: TS Types 9 | /* ****************************************************************************************************************** */ 10 | 11 | export type ImportOrExportDeclaration = ts.ImportDeclaration | ts.ExportDeclaration; 12 | export type ImportOrExportClause = ts.ImportDeclaration["importClause"] | ts.ExportDeclaration["exportClause"]; 13 | 14 | // endregion 15 | 16 | /* ****************************************************************************************************************** */ 17 | // region: Config 18 | /* ****************************************************************************************************************** */ 19 | 20 | export interface TsTransformPathsConfig extends PluginConfig { 21 | readonly useRootDirs?: boolean; 22 | readonly exclude?: string[]; 23 | } 24 | 25 | // endregion 26 | 27 | /* ****************************************************************************************************************** */ 28 | // region: Contexts 29 | /* ****************************************************************************************************************** */ 30 | 31 | export interface TsTransformPathsContext { 32 | /** TS Instance passed from ts-patch / ttypescript */ 33 | readonly tsInstance: typeof ts; 34 | readonly tsVersionMajor: number; 35 | readonly tsVersionMinor: number; 36 | readonly tsFactory?: ts.NodeFactory; 37 | readonly runMode: RunMode; 38 | readonly tsNodeState?: TsNodeState; 39 | readonly program?: ts.Program; 40 | readonly config: TsTransformPathsConfig; 41 | readonly compilerOptions: CompilerOptions; 42 | readonly elisionMap: Map>; 43 | readonly transformationContext: ts.TransformationContext; 44 | readonly rootDirs?: string[]; 45 | readonly excludeMatchers: Minimatch[] | undefined; 46 | readonly outputFileNamesCache: Map; 47 | readonly pathsPatterns: readonly (string | Pattern)[] | undefined; 48 | readonly emitHost: EmitHost; 49 | } 50 | 51 | export interface VisitorContext extends TsTransformPathsContext { 52 | readonly factory: HarmonyFactory; 53 | readonly sourceFile: ts.SourceFile; 54 | readonly isDeclarationFile: boolean; 55 | readonly originalSourceFile: ts.SourceFile; 56 | getVisitor(): (node: ts.Node) => ts.VisitResult; 57 | } 58 | 59 | // endregion 60 | 61 | /* ****************************************************************************************************************** */ 62 | // region: General 63 | /* ****************************************************************************************************************** */ 64 | 65 | export enum RunMode { 66 | TsNode = "ts-node", 67 | Manual = "manual", 68 | Program = "program", 69 | } 70 | 71 | export enum TsNodeState { 72 | Full, 73 | Stripped, 74 | } 75 | 76 | // endregion 77 | -------------------------------------------------------------------------------- /src/utils/elide-import-export.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * UPDATE: 3 | * 4 | * TODO - In next major version, we can remove this file entirely due to TS PR 57223 5 | * https://github.com/microsoft/TypeScript/pull/57223 6 | * 7 | * This file and its contents are due to an issue in TypeScript (affecting _at least_ up to 4.1) which causes type 8 | * elision to break during emit for nodes which have been transformed. Specifically, if the 'original' property is set, 9 | * elision functionality no longer works. 10 | * 11 | * This results in module specifiers for types being output in import/export declarations in the compiled _JS files_ 12 | * 13 | * The logic herein compensates for that issue by recreating type elision separately so that the transformer can update 14 | * the clause with the properly elided information 15 | * 16 | * Issues: 17 | * 18 | * - See https://github.com/LeDDGroup/typescript-transform-paths/issues/184 19 | * - See https://github.com/microsoft/TypeScript/issues/40603 20 | * - See https://github.com/microsoft/TypeScript/issues/31446 21 | * 22 | * @example 23 | * // a.ts 24 | * export type A = string; 25 | * export const B = 2; 26 | * 27 | * // b.ts 28 | * import { A, B } from "./b"; 29 | * export { A } from "./b"; 30 | * 31 | * // Expected output for b.js 32 | * import { B } from "./b"; 33 | * 34 | * // Actual output for b.js 35 | * import { A, B } from "./b"; 36 | * export { A } from "./b"; 37 | */ 38 | import { ImportOrExportDeclaration, VisitorContext } from "../types"; 39 | import { 40 | Debug, 41 | EmitResolver, 42 | ExportSpecifier, 43 | ImportClause, 44 | ImportsNotUsedAsValues, 45 | ImportSpecifier, 46 | isInJSFile, 47 | NamedExportBindings, 48 | NamedExports, 49 | NamedImportBindings, 50 | NamespaceExport, 51 | Node, 52 | StringLiteral, 53 | Visitor, 54 | VisitResult, 55 | } from "typescript"; 56 | 57 | /* ****************************************************************************************************************** */ 58 | // region: Utilities 59 | /* ****************************************************************************************************************** */ 60 | 61 | /** 62 | * Get import / export clause for node (replicates TS elision behaviour for js files) See notes in 63 | * get-import-export-clause.ts header for why this is necessary 64 | * 65 | * @returns Import or export clause or undefined if it entire declaration should be elided 66 | */ 67 | export function elideImportOrExportDeclaration( 68 | context: VisitorContext, 69 | node: T, 70 | newModuleSpecifier: StringLiteral, 71 | resolver: EmitResolver, 72 | ): T | undefined; 73 | 74 | export function elideImportOrExportDeclaration( 75 | context: VisitorContext, 76 | node: ImportOrExportDeclaration, 77 | newModuleSpecifier: StringLiteral, 78 | resolver: EmitResolver, 79 | ): ImportOrExportDeclaration | undefined { 80 | const { tsInstance, factory } = context; 81 | const { compilerOptions } = context; 82 | 83 | const { 84 | visitNode, 85 | isNamedImportBindings, 86 | isImportSpecifier, 87 | SyntaxKind, 88 | visitNodes, 89 | isNamedExportBindings, 90 | // 3.8 does not have this, so we have to define it ourselves 91 | // isNamespaceExport, 92 | isIdentifier, 93 | isExportSpecifier, 94 | } = tsInstance; 95 | 96 | const isNamespaceExport = 97 | tsInstance.isNamespaceExport ?? ((node: Node): node is NamespaceExport => node.kind === SyntaxKind.NamespaceExport); 98 | 99 | if (tsInstance.isImportDeclaration(node)) { 100 | // Do not elide a side-effect only import declaration. 101 | // import "foo"; 102 | if (!node.importClause) return node.importClause; 103 | 104 | // Always elide type-only imports 105 | if (node.importClause.isTypeOnly) return undefined; 106 | 107 | const importClause = visitNode(node.importClause, visitImportClause); 108 | 109 | if ( 110 | importClause || 111 | compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve || 112 | compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error 113 | ) 114 | return factory.updateImportDeclaration( 115 | node, 116 | /*modifiers*/ undefined, 117 | importClause, 118 | newModuleSpecifier, 119 | // This will be changed in the next release of TypeScript, but by that point we can drop elision entirely 120 | // @ts-expect-error TS(2339) FIXME: Property 'attributes' does not exist on type 'ImportDeclaration'. 121 | node.attributes || node.assertClause, 122 | ); 123 | else return undefined; 124 | } else { 125 | if (node.isTypeOnly) return undefined; 126 | 127 | if (!node.exportClause || node.exportClause.kind === SyntaxKind.NamespaceExport) { 128 | // never elide `export from ` declarations - 129 | // they should be kept for sideffects/untyped exports, even when the 130 | // type checker doesn't know about any exports 131 | return node; 132 | } 133 | 134 | const allowEmpty = 135 | !!compilerOptions["verbatimModuleSyntax"] || 136 | (!!node.moduleSpecifier && 137 | (compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve || 138 | compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error)); 139 | 140 | const exportClause = visitNode( 141 | node.exportClause, 142 | ((bindings: NamedExportBindings) => visitNamedExportBindings(bindings, allowEmpty)), 143 | isNamedExportBindings, 144 | ); 145 | 146 | return exportClause 147 | ? factory.updateExportDeclaration( 148 | node, 149 | /*modifiers*/ undefined, 150 | node.isTypeOnly, 151 | exportClause, 152 | newModuleSpecifier, 153 | // This will be changed in the next release of TypeScript, but by that point we can drop elision entirely 154 | // @ts-expect-error TS(2339) FIXME: Property 'attributes' does not exist on type 'ExportDeclaration'. 155 | node.attributes || node.assertClause, 156 | ) 157 | : undefined; 158 | } 159 | 160 | /* ********************************************************* * 161 | * Helpers 162 | * ********************************************************* */ 163 | 164 | // The following visitors are adapted from the TS source-base src/compiler/transformers/ts 165 | 166 | /** 167 | * Visits an import clause, eliding it if it is not referenced. 168 | * 169 | * @param node The import clause node. 170 | */ 171 | function visitImportClause(node: ImportClause): VisitResult { 172 | // Elide the import clause if we elide both its name and its named bindings. 173 | const name = shouldEmitAliasDeclaration(node) ? node.name : undefined; 174 | const namedBindings = visitNode(node.namedBindings, visitNamedImportBindings, isNamedImportBindings); 175 | return name || namedBindings 176 | ? factory.updateImportClause(node, /*isTypeOnly*/ false, name, namedBindings) 177 | : undefined; 178 | } 179 | 180 | /** 181 | * Visits named import bindings, eliding it if it is not referenced. 182 | * 183 | * @param node The named import bindings node. 184 | */ 185 | function visitNamedImportBindings(node: NamedImportBindings): VisitResult { 186 | if (node.kind === SyntaxKind.NamespaceImport) { 187 | // Elide a namespace import if it is not referenced. 188 | return shouldEmitAliasDeclaration(node) ? node : undefined; 189 | } else { 190 | // Elide named imports if all of its import specifiers are elided. 191 | const allowEmpty = 192 | compilerOptions["verbatimModuleSyntax"] || 193 | (compilerOptions.preserveValueImports && 194 | (compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve || 195 | compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error)); 196 | 197 | const elements = visitNodes(node.elements, visitImportSpecifier, isImportSpecifier); 198 | return allowEmpty || tsInstance.some(elements) ? factory.updateNamedImports(node, elements) : undefined; 199 | } 200 | } 201 | 202 | /** 203 | * Visits an import specifier, eliding it if it is not referenced. 204 | * 205 | * @param node The import specifier node. 206 | */ 207 | function visitImportSpecifier(node: ImportSpecifier): VisitResult { 208 | // Elide an import specifier if it is not referenced. 209 | return !node.isTypeOnly && shouldEmitAliasDeclaration(node) ? node : undefined; 210 | } 211 | 212 | /** Visits named exports, eliding it if it does not contain an export specifier that resolves to a value. */ 213 | function visitNamedExports(node: NamedExports, allowEmpty: boolean): VisitResult | undefined { 214 | // Elide the named exports if all of its export specifiers were elided. 215 | const elements = visitNodes(node.elements, visitExportSpecifier, isExportSpecifier); 216 | return allowEmpty || tsInstance.some(elements) ? factory.updateNamedExports(node, elements) : undefined; 217 | } 218 | 219 | function visitNamedExportBindings( 220 | node: NamedExportBindings, 221 | allowEmpty: boolean, 222 | ): VisitResult | undefined { 223 | return isNamespaceExport(node) ? visitNamespaceExports(node) : visitNamedExports(node, allowEmpty); 224 | } 225 | 226 | function visitNamespaceExports(node: NamespaceExport): VisitResult { 227 | // Note: This may not work entirely properly, more likely it's just extraneous, but this won't matter soon, 228 | // as we'll be removing elision entirely 229 | return factory.updateNamespaceExport(node, Debug.checkDefined(visitNode(node.name, (n) => n, isIdentifier))); 230 | } 231 | 232 | /** 233 | * Visits an export specifier, eliding it if it does not resolve to a value. 234 | * 235 | * @param node The export specifier node. 236 | */ 237 | function visitExportSpecifier(node: ExportSpecifier): VisitResult { 238 | // Elide an export specifier if it does not reference a value. 239 | return !node.isTypeOnly && (compilerOptions["verbatimModuleSyntax"] || resolver.isValueAliasDeclaration(node)) 240 | ? node 241 | : undefined; 242 | } 243 | 244 | function shouldEmitAliasDeclaration(node: Node): boolean { 245 | return ( 246 | !!compilerOptions["verbatimModuleSyntax"] || 247 | isInJSFile(node) || 248 | (compilerOptions.preserveValueImports 249 | ? resolver.isValueAliasDeclaration(node) 250 | : resolver.isReferencedAliasDeclaration(node)) 251 | ); 252 | } 253 | } 254 | 255 | // endregion 256 | -------------------------------------------------------------------------------- /src/utils/general-utils.ts: -------------------------------------------------------------------------------- 1 | import url from "node:url"; 2 | import path from "node:path"; 3 | 4 | /* ****************************************************************************************************************** * 5 | * General Utilities & Helpers 6 | * ****************************************************************************************************************** */ 7 | 8 | export const isURL = (s: string | undefined): s is string => !!s && (!!url.parse(s).host || !!url.parse(s).hostname); 9 | 10 | export const isBaseDir = (baseDir: string, testDir: string): boolean => { 11 | const relative = path.relative(baseDir, testDir); 12 | return relative ? !relative.startsWith("..") && !path.isAbsolute(relative) : true; 13 | }; 14 | 15 | export const maybeAddRelativeLocalPrefix = (p: string) => (p[0] === "." ? p : `./${p}`); 16 | -------------------------------------------------------------------------------- /src/utils/get-relative-path.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import * as os from "node:os"; 3 | import path from "node:path"; 4 | 5 | /* ****************************************************************************************************************** */ 6 | // region: Locals 7 | /* ****************************************************************************************************************** */ 8 | 9 | let isCaseSensitiveFilesystem: boolean | undefined; 10 | 11 | // endregion 12 | 13 | /* ****************************************************************************************************************** */ 14 | // region: Helpers 15 | /* ****************************************************************************************************************** */ 16 | 17 | function tryRmFile(fileName: string) { 18 | try { 19 | if (fs.existsSync(fileName)) fs.rmSync(fileName, { force: true }); 20 | } catch {} 21 | } 22 | 23 | function getIsFsCaseSensitive() { 24 | if (isCaseSensitiveFilesystem != undefined) return isCaseSensitiveFilesystem; 25 | 26 | for (let i = 0; i < 1000; i++) { 27 | const tmpFileName = path.join(os.tmpdir(), `tstp~${i}.tmp`); 28 | 29 | tryRmFile(tmpFileName); 30 | 31 | try { 32 | fs.writeFileSync(tmpFileName, ""); 33 | isCaseSensitiveFilesystem = !fs.existsSync(tmpFileName.replace("tstp", "TSTP")); 34 | return isCaseSensitiveFilesystem; 35 | } catch { 36 | } finally { 37 | tryRmFile(tmpFileName); 38 | } 39 | } 40 | 41 | console.warn( 42 | `Could not determine filesystem's case sensitivity. Please file a bug report with your system's details`, 43 | ); 44 | isCaseSensitiveFilesystem = false; 45 | 46 | return isCaseSensitiveFilesystem; 47 | } 48 | 49 | /** @private The Export is only for unit tests */ 50 | export function getMatchPortion(from: string, to: string) { 51 | const lowerFrom = from.toLocaleLowerCase(); 52 | const lowerTo = to.toLocaleLowerCase(); 53 | 54 | const maxLen = Math.max(lowerFrom.length, lowerTo.length); 55 | 56 | let i = 0; 57 | while (i < maxLen) { 58 | if (lowerFrom[i] !== lowerTo[i]) break; 59 | i++; 60 | } 61 | 62 | return to.slice(0, i); 63 | } 64 | 65 | // endregion 66 | 67 | /* ****************************************************************************************************************** */ 68 | // region: Utils 69 | /* ****************************************************************************************************************** */ 70 | 71 | export function getRelativePath(from: string, to: string) { 72 | try { 73 | from = fs.realpathSync.native(from); 74 | to = fs.realpathSync.native(to); 75 | } catch { 76 | if (!getIsFsCaseSensitive()) { 77 | const matchPortion = getMatchPortion(from, to); 78 | from = matchPortion + from.slice(matchPortion.length); 79 | to = matchPortion + to.slice(matchPortion.length); 80 | } 81 | } 82 | 83 | return path.relative(from, to); 84 | } 85 | 86 | // endregion 87 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./general-utils"; 2 | export * from "./elide-import-export"; 3 | export * from "./resolve-path-update-node"; 4 | -------------------------------------------------------------------------------- /src/utils/resolve-module-name.ts: -------------------------------------------------------------------------------- 1 | import { VisitorContext } from "../types"; 2 | import { isBaseDir, isURL, maybeAddRelativeLocalPrefix } from "./general-utils"; 3 | import * as path from "node:path"; 4 | import { removeFileExtension, removeSuffix, ResolvedModuleFull, SourceFile } from "typescript"; 5 | import { getOutputDirForSourceFile } from "./ts-helpers"; 6 | import { getRelativePath } from "./get-relative-path"; 7 | 8 | export interface ResolvedModule { 9 | /** Absolute path to resolved module */ 10 | resolvedPath: string | undefined; 11 | /** Output path */ 12 | outputPath: string; 13 | /** Resolved to URL */ 14 | isURL: boolean; 15 | } 16 | 17 | enum IndexType { 18 | NonIndex, 19 | Explicit, 20 | Implicit, 21 | ImplicitPackage, 22 | } 23 | 24 | function getPathDetail(moduleName: string, resolvedModule: ResolvedModuleFull) { 25 | const resolvedFileName = resolvedModule.originalPath ?? resolvedModule.resolvedFileName; 26 | const implicitPackageIndex = resolvedModule.packageId?.subModuleName; 27 | 28 | const resolvedDir = implicitPackageIndex 29 | ? removeSuffix(resolvedFileName, `/${implicitPackageIndex}`) 30 | : path.dirname(resolvedFileName); 31 | const resolvedBaseName = implicitPackageIndex ? void 0 : path.basename(resolvedFileName); 32 | const resolvedBaseNameNoExtension = resolvedBaseName && removeFileExtension(resolvedBaseName); 33 | const resolvedExtName = resolvedBaseName && path.extname(resolvedFileName); 34 | 35 | let baseName = implicitPackageIndex ? void 0 : path.basename(moduleName); 36 | let baseNameNoExtension = baseName && removeFileExtension(baseName); 37 | let extName = baseName && path.extname(moduleName); 38 | 39 | // Account for possible false extensions. Example scenario: 40 | // moduleName = './file.accounting' 41 | // resolvedBaseName = 'file.accounting.ts' 42 | // ('accounting' would be considered the extension) 43 | if (resolvedBaseNameNoExtension && baseName && resolvedBaseNameNoExtension === baseName) { 44 | baseNameNoExtension = baseName; 45 | extName = void 0; 46 | } 47 | 48 | // prettier-ignore 49 | const indexType = 50 | implicitPackageIndex ? IndexType.ImplicitPackage : 51 | baseNameNoExtension === 'index' && resolvedBaseNameNoExtension === 'index' ? IndexType.Explicit : 52 | baseNameNoExtension !== 'index' && resolvedBaseNameNoExtension === 'index' ? IndexType.Implicit : 53 | IndexType.NonIndex; 54 | 55 | if (indexType === IndexType.Implicit) { 56 | baseName = void 0; 57 | baseNameNoExtension = void 0; 58 | extName = void 0; 59 | } 60 | 61 | return { 62 | baseName, 63 | baseNameNoExtension, 64 | extName, 65 | resolvedBaseName, 66 | resolvedBaseNameNoExtension, 67 | resolvedExtName, 68 | resolvedDir, 69 | indexType, 70 | implicitPackageIndex, 71 | resolvedFileName, 72 | }; 73 | } 74 | 75 | function getResolvedSourceFile(context: VisitorContext, fileName: string): SourceFile { 76 | let res: SourceFile | undefined; 77 | const { program, tsInstance } = context; 78 | 79 | if (program) { 80 | /* Attempt to directly pull from Program */ 81 | res = program.getSourceFile(fileName) as SourceFile; 82 | if (res) return res; 83 | 84 | /* Attempt to find without extension */ 85 | res = (program.getSourceFiles() as SourceFile[]).find( 86 | (s) => removeFileExtension(s.fileName) === removeFileExtension(fileName), 87 | ); 88 | if (res) return res; 89 | } 90 | 91 | /* 92 | * Create basic synthetic SourceFile for use with compiler API - Applies if SourceFile not found in program due to 93 | * import being added by another transformer 94 | */ 95 | return tsInstance.createSourceFile(fileName, ``, tsInstance.ScriptTarget.ESNext, /* setParentNodes */ false); 96 | } 97 | 98 | /** Resolve a module name */ 99 | export function resolveModuleName(context: VisitorContext, moduleName: string): ResolvedModule | undefined { 100 | const { tsInstance, compilerOptions, sourceFile, config, rootDirs } = context; 101 | 102 | // Attempt to resolve with TS Compiler API 103 | const { resolvedModule, failedLookupLocations } = tsInstance.resolveModuleName( 104 | moduleName, 105 | sourceFile.fileName, 106 | compilerOptions, 107 | tsInstance.sys, 108 | ); 109 | 110 | // Handle non-resolvable module 111 | if (!resolvedModule) { 112 | const maybeURL = failedLookupLocations[0]; 113 | if (!isURL(maybeURL)) return undefined; 114 | return { 115 | isURL: true, 116 | resolvedPath: undefined, 117 | outputPath: maybeURL, 118 | }; 119 | } 120 | 121 | const resolvedSourceFile = getResolvedSourceFile(context, resolvedModule.resolvedFileName); 122 | 123 | const { indexType, resolvedBaseNameNoExtension, resolvedFileName, implicitPackageIndex, extName, resolvedDir } = 124 | getPathDetail(moduleName, resolvedModule); 125 | 126 | /* Determine output filename */ 127 | let outputBaseName = resolvedBaseNameNoExtension ?? ""; 128 | 129 | if (indexType === IndexType.Implicit) outputBaseName = outputBaseName.replace(/(\/index$)|(^index$)/, ""); 130 | if (outputBaseName && extName) outputBaseName = `${outputBaseName}${extName}`; 131 | 132 | /* Determine output dir */ 133 | let srcFileOutputDir = getOutputDirForSourceFile(context, sourceFile); 134 | let moduleFileOutputDir = implicitPackageIndex ? resolvedDir : getOutputDirForSourceFile(context, resolvedSourceFile); 135 | 136 | // Handle rootDirs remapping 137 | if (config.useRootDirs && rootDirs) { 138 | let fileRootDir = ""; 139 | let moduleRootDir = ""; 140 | for (const rootDir of rootDirs) { 141 | if (isBaseDir(rootDir, moduleFileOutputDir) && rootDir.length > moduleRootDir.length) moduleRootDir = rootDir; 142 | if (isBaseDir(rootDir, srcFileOutputDir) && rootDir.length > fileRootDir.length) fileRootDir = rootDir; 143 | } 144 | 145 | /* Remove base dirs to make relative to root */ 146 | if (fileRootDir && moduleRootDir) { 147 | srcFileOutputDir = getRelativePath(fileRootDir, srcFileOutputDir); 148 | moduleFileOutputDir = getRelativePath(moduleRootDir, moduleFileOutputDir); 149 | } 150 | } 151 | 152 | const outputDir = getRelativePath(srcFileOutputDir, moduleFileOutputDir); 153 | 154 | /* Compose final output path */ 155 | const outputPath = maybeAddRelativeLocalPrefix(tsInstance.normalizePath(path.join(outputDir, outputBaseName))); 156 | 157 | return { isURL: false, outputPath, resolvedPath: resolvedFileName }; 158 | } 159 | -------------------------------------------------------------------------------- /src/utils/resolve-path-update-node.ts: -------------------------------------------------------------------------------- 1 | import ts from "typescript"; 2 | import { VisitorContext } from "../types"; 3 | import { isURL, maybeAddRelativeLocalPrefix } from "./general-utils"; 4 | import { isModulePathsMatch } from "./ts-helpers"; 5 | import { resolveModuleName } from "./resolve-module-name"; 6 | 7 | /** Gets proper path and calls updaterFn to get the new node if it should be updated */ 8 | export function resolvePathAndUpdateNode( 9 | context: VisitorContext, 10 | node: ts.Node, 11 | moduleName: string, 12 | updaterFn: (newPath: ts.StringLiteral) => ts.Node | undefined, 13 | ): ts.Node | undefined { 14 | const { sourceFile, tsInstance, factory } = context; 15 | const { normalizePath } = tsInstance; 16 | 17 | /* Handle JSDoc statement tags */ 18 | const tags = getStatementTags(); 19 | 20 | // Skip if @no-transform-path specified 21 | if (tags.shouldSkip) return node; 22 | 23 | // Accommodate direct override via @transform-path tag 24 | if (tags.overridePath) { 25 | const transformedPath = isURL(tags.overridePath) 26 | ? tags.overridePath 27 | : maybeAddRelativeLocalPrefix(normalizePath(tags.overridePath)); 28 | return updaterFn(factory.createStringLiteral(transformedPath)); 29 | } 30 | 31 | /* Resolve Module */ 32 | // Skip if no paths match found 33 | if (!isModulePathsMatch(context, moduleName)) return node; 34 | 35 | const res = resolveModuleName(context, moduleName); 36 | if (!res) return node; 37 | 38 | const { outputPath, resolvedPath } = res; 39 | 40 | /* Skip if matches exclusion */ 41 | if (context.excludeMatchers) 42 | for (const matcher of context.excludeMatchers) 43 | if (matcher.match(outputPath) || (resolvedPath && matcher.match(resolvedPath))) return node; 44 | 45 | return updaterFn(factory.createStringLiteral(outputPath)); 46 | 47 | /* ********************************************************* * 48 | * Helpers 49 | * ********************************************************* */ 50 | 51 | function getStatementTags() { 52 | let targetNode = tsInstance.isStatement(node) 53 | ? node 54 | : (tsInstance.findAncestor(node, tsInstance.isStatement) ?? node); 55 | targetNode = tsInstance.getOriginalNode(targetNode); 56 | 57 | let jsDocTags: readonly ts.JSDocTag[] | undefined; 58 | try { 59 | jsDocTags = tsInstance.getJSDocTags(targetNode); 60 | } catch {} 61 | 62 | const commentTags = new Map(); 63 | if (targetNode.pos >= 0) { 64 | try { 65 | const trivia = targetNode.getFullText(sourceFile).slice(0, targetNode.getLeadingTriviaWidth(sourceFile)); 66 | const regex = /^\s*\/{2,3}\s*@(transform-path|no-transform-path)(?:[^\S\n\r](.+?))?$/gim; 67 | 68 | for (let match = regex.exec(trivia); match; match = regex.exec(trivia)) 69 | if (match[1]) commentTags.set(match[1], match[2]); 70 | } catch {} 71 | } 72 | 73 | const overridePath = findTag("transform-path"); 74 | const shouldSkip = findTag("no-transform-path"); 75 | 76 | return { 77 | overridePath: typeof overridePath === "string" ? overridePath : void 0, 78 | shouldSkip: !!shouldSkip, 79 | }; 80 | 81 | function findTag(expected: string): boolean | string | undefined { 82 | if (commentTags.has(expected)) return commentTags.get(expected) || true; 83 | if (!jsDocTags?.length) return undefined; 84 | 85 | for (const tag of jsDocTags) { 86 | const tagName = tag.tagName.text.toLowerCase(); 87 | if (tagName === expected) return typeof tag.comment === "string" ? tag.comment : true; 88 | 89 | /* The following handles older TS which splits tags at first hyphens */ 90 | if (typeof tag.comment !== "string" || tag.comment[0] !== "-") continue; 91 | 92 | const dashPos = expected.indexOf("-"); 93 | if (dashPos < 0) return void 0; 94 | 95 | if (tagName === expected.slice(0, dashPos)) { 96 | const comment = tag.comment; 97 | const choppedCommentTagName = comment.slice(0, expected.length - dashPos); 98 | return choppedCommentTagName === expected.slice(dashPos) 99 | ? comment.slice(choppedCommentTagName.length + 1).trim() || true 100 | : void 0; 101 | } 102 | } 103 | 104 | return undefined; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/utils/ts-helpers.ts: -------------------------------------------------------------------------------- 1 | import ts, { GetCanonicalFileName, SourceFile } from "typescript"; 2 | import path from "node:path"; 3 | import { VisitorContext } from "../types"; 4 | import type { REGISTER_INSTANCE } from "ts-node"; 5 | 6 | /* ****************************************************************************************************************** */ 7 | // region: TS Helpers 8 | /* ****************************************************************************************************************** */ 9 | 10 | /** Determine output file path for source file */ 11 | export function getOutputDirForSourceFile(context: VisitorContext, sourceFile: SourceFile): string { 12 | const { 13 | tsInstance, 14 | emitHost, 15 | outputFileNamesCache, 16 | compilerOptions, 17 | tsInstance: { getOwnEmitOutputFilePath, getOutputExtension }, 18 | } = context; 19 | 20 | if (outputFileNamesCache.has(sourceFile)) return outputFileNamesCache.get(sourceFile)!; 21 | 22 | // Note: In project references, resolved path is different from path. In that case, our output path is already 23 | // determined in resolvedPath 24 | const outputPath = 25 | sourceFile.path && sourceFile.resolvedPath && sourceFile.path !== sourceFile.resolvedPath 26 | ? sourceFile.resolvedPath 27 | : getOwnEmitOutputFilePath( 28 | sourceFile.fileName, 29 | emitHost, 30 | getOutputExtension(sourceFile.fileName, compilerOptions), 31 | ); 32 | 33 | if (!outputPath) 34 | throw new Error( 35 | `Could not resolve output path for ${sourceFile.fileName}. Please report a GH issue at: ` + 36 | `https://github.com/LeDDGroup/typescript-transform-paths/issues`, 37 | ); 38 | 39 | const res = path.dirname(outputPath); 40 | 41 | outputFileNamesCache.set(sourceFile, res); 42 | 43 | return tsInstance.normalizePath(res); 44 | } 45 | 46 | /** Determine if moduleName matches config in paths */ 47 | export function isModulePathsMatch(context: VisitorContext, moduleName: string): boolean { 48 | const { 49 | pathsPatterns, 50 | tsInstance: { matchPatternOrExact }, 51 | } = context; 52 | return !!(pathsPatterns && matchPatternOrExact(pathsPatterns as readonly string[], moduleName)); 53 | } 54 | 55 | /** Create barebones EmitHost (for no-Program transform) */ 56 | export function createSyntheticEmitHost( 57 | compilerOptions: ts.CompilerOptions, 58 | tsInstance: typeof ts, 59 | getCanonicalFileName: GetCanonicalFileName, 60 | fileNames: string[], 61 | ) { 62 | return { 63 | getCompilerOptions: () => compilerOptions, 64 | getCurrentDirectory: tsInstance.sys.getCurrentDirectory, 65 | getCommonSourceDirectory: () => 66 | tsInstance.getCommonSourceDirectoryOfConfig( 67 | { options: compilerOptions, fileNames: fileNames } as ts.ParsedCommandLine, 68 | !tsInstance.sys.useCaseSensitiveFileNames, 69 | ), 70 | getCanonicalFileName, 71 | } as ts.EmitHost; 72 | } 73 | 74 | /** Get ts-node register info */ 75 | export function getTsNodeRegistrationProperties(tsInstance: typeof ts) { 76 | let tsNodeSymbol: typeof REGISTER_INSTANCE; 77 | try { 78 | // eslint-disable-next-line @typescript-eslint/no-require-imports 79 | tsNodeSymbol = require("ts-node")?.["REGISTER_INSTANCE"]; 80 | } catch { 81 | return; 82 | } 83 | 84 | if (!global.process[tsNodeSymbol]) return; 85 | 86 | const { config, options } = global.process[tsNodeSymbol]!; 87 | 88 | const { configFilePath } = config.options; 89 | // @ts-expect-error TS(2345) FIXME: Argument of type 'System' is not assignable to parameter of type 'ParseConfigFileHost'. 90 | const pcl = configFilePath ? tsInstance.getParsedCommandLineOfConfigFile(configFilePath, {}, tsInstance.sys) : void 0; 91 | 92 | const fileNames = pcl?.fileNames || config.fileNames; 93 | const compilerOptions = Object.assign({}, config.options, options.compilerOptions, { outDir: pcl?.options.outDir }); 94 | 95 | return { compilerOptions, fileNames, tsNodeOptions: options }; 96 | } 97 | 98 | // endregion 99 | -------------------------------------------------------------------------------- /src/visitor.ts: -------------------------------------------------------------------------------- 1 | import ts from "typescript"; 2 | import { VisitorContext } from "./types"; 3 | import { elideImportOrExportDeclaration, resolvePathAndUpdateNode } from "./utils"; 4 | 5 | const isAsyncImport = ({ tsInstance }: VisitorContext, node: ts.Node): node is ts.CallExpression => 6 | tsInstance.isCallExpression(node) && 7 | node.expression.kind === tsInstance.SyntaxKind.ImportKeyword && 8 | !!node.arguments[0] && 9 | tsInstance.isStringLiteral(node.arguments[0]) && 10 | node.arguments.length === 1; 11 | 12 | const isRequire = ({ tsInstance }: VisitorContext, node: ts.Node): node is ts.CallExpression => 13 | tsInstance.isCallExpression(node) && 14 | tsInstance.isIdentifier(node.expression) && 15 | node.expression.text === "require" && 16 | !!node.arguments[0] && 17 | tsInstance.isStringLiteral(node.arguments[0]) && 18 | node.arguments.length === 1; 19 | 20 | /** Visit and replace nodes with module specifiers */ 21 | export function nodeVisitor(this: VisitorContext, node: ts.Node): ts.Node | undefined { 22 | const { factory, tsInstance, transformationContext } = this; 23 | 24 | /** 25 | * Update require / import functions 26 | * 27 | * @example 28 | * require("module"); 29 | * import("module"); 30 | */ 31 | if (isRequire(this, node) || isAsyncImport(this, node)) 32 | return resolvePathAndUpdateNode(this, node, (node.arguments[0]).text, (p) => { 33 | const res = factory.updateCallExpression(node, node.expression, node.typeArguments, [p]); 34 | 35 | /* Handle comments */ 36 | const textNode = node.arguments[0]; 37 | if (!textNode) throw new Error("Expected textNode"); 38 | const commentRanges = tsInstance.getLeadingCommentRanges(textNode.getFullText(), 0) ?? []; 39 | 40 | for (const range of commentRanges) { 41 | const { kind, pos, end, hasTrailingNewLine } = range; 42 | 43 | const caption = textNode 44 | .getFullText() 45 | .substring(pos, end) 46 | .replace( 47 | /* searchValue */ kind === tsInstance.SyntaxKind.MultiLineCommentTrivia 48 | ? // Comment range in a multi-line comment with more than one line erroneously 49 | // includes the node's text in the range. For that reason, we use the greedy 50 | // selector in capture group and dismiss anything after the final comment close tag 51 | /^\/\*(.+)\*\/.*/s 52 | : /^\/\/(.+)/s, 53 | /* replaceValue */ "$1", 54 | ); 55 | tsInstance.addSyntheticLeadingComment(p, kind, caption, hasTrailingNewLine); 56 | } 57 | 58 | return res; 59 | }); 60 | 61 | /** 62 | * Update ExternalModuleReference 63 | * 64 | * @example 65 | * import foo = require("foo"); 66 | */ 67 | if (tsInstance.isExternalModuleReference(node) && tsInstance.isStringLiteral(node.expression)) 68 | return resolvePathAndUpdateNode(this, node, node.expression.text, (p) => 69 | factory.updateExternalModuleReference(node, p), 70 | ); 71 | 72 | /** 73 | * Update ImportTypeNode 74 | * 75 | * @example 76 | * typeof import("./bar"); 77 | * import("package").MyType; 78 | */ 79 | if (tsInstance.isImportTypeNode(node)) { 80 | const argument = node.argument as ts.LiteralTypeNode; 81 | if (!tsInstance.isStringLiteral(argument.literal)) return node; 82 | 83 | const { text } = argument.literal; 84 | if (!text) return node; 85 | 86 | const res = resolvePathAndUpdateNode(this, node, text, (p) => 87 | factory.updateImportTypeNode( 88 | node, 89 | factory.updateLiteralTypeNode(argument, p), 90 | node.assertions, 91 | node.qualifier, 92 | node.typeArguments, 93 | node.isTypeOf, 94 | ), 95 | ); 96 | 97 | return tsInstance.visitEachChild(res, this.getVisitor(), transformationContext); 98 | } 99 | 100 | /** 101 | * Update ImportDeclaration 102 | * 103 | * @example 104 | * import ... 'module'; 105 | */ 106 | if (tsInstance.isImportDeclaration(node) && node.moduleSpecifier && tsInstance.isStringLiteral(node.moduleSpecifier)) 107 | return resolvePathAndUpdateNode(this, node, node.moduleSpecifier.text, (p) => { 108 | // TODO - In next major version, we can remove this entirely due to TS PR 57223 109 | // see: https://github.com/microsoft/TypeScript/pull/57223 110 | // We should at least skip this if doing a minor version update if the ts version is high enough to not need it 111 | if (!this.isDeclarationFile && node.importClause?.namedBindings) { 112 | const resolver = transformationContext.getEmitResolver(); 113 | // If run in "manual" mode without a Program, we won't have a resolver, so we can't elide 114 | if (resolver) return elideImportOrExportDeclaration(this, node, p, resolver); 115 | } 116 | 117 | return factory.updateImportDeclaration(node, node.modifiers, node.importClause, p, node.assertClause); 118 | }); 119 | 120 | /** 121 | * Update ExportDeclaration 122 | * 123 | * @example 124 | * export ... 'module'; 125 | */ 126 | if (tsInstance.isExportDeclaration(node) && node.moduleSpecifier && tsInstance.isStringLiteral(node.moduleSpecifier)) 127 | return resolvePathAndUpdateNode(this, node, node.moduleSpecifier.text, (p) => { 128 | // TODO - In next major version, we can remove this entirely due to TS PR 57223 129 | // see: https://github.com/microsoft/TypeScript/pull/57223 130 | // We should at least skip this if doing a minor version update if the ts version is high enough to not need it 131 | if (!this.isDeclarationFile && node.exportClause && tsInstance.isNamedExports(node.exportClause)) { 132 | const resolver = transformationContext.getEmitResolver(); 133 | // If run in "manual" mode without a Program, we won't have a resolver, so we can't elide 134 | if (resolver) return elideImportOrExportDeclaration(this, node, p, resolver); 135 | } 136 | 137 | return factory.updateExportDeclaration( 138 | node, 139 | node.modifiers, 140 | node.isTypeOnly, 141 | node.exportClause, 142 | p, 143 | node.assertClause, 144 | ); 145 | }); 146 | 147 | /** Update module augmentation */ 148 | if (tsInstance.isModuleDeclaration(node) && tsInstance.isStringLiteral(node.name)) 149 | return resolvePathAndUpdateNode(this, node, node.name.text, (p) => 150 | factory.updateModuleDeclaration(node, node.modifiers, p, node.body), 151 | ); 152 | 153 | return tsInstance.visitEachChild(node, this.getVisitor(), transformationContext); 154 | } 155 | -------------------------------------------------------------------------------- /test/config.ts: -------------------------------------------------------------------------------- 1 | import ts from "typescript"; 2 | import tsThree from "typescript-3"; 3 | import tsFourSeven from "typescript-4.7"; 4 | import tsFiveFive from "typescript-5.5"; 5 | import tsFiveSix from "typescript-5.6"; 6 | import path from "node:path"; 7 | 8 | export const tsModules = [ 9 | ["3.6.5", tsThree, "typescript-3"], 10 | ["4.7.4", tsFourSeven, "typescript-4.7"], 11 | ["5.5.4", tsFiveFive, "typescript-5.5"], 12 | ["5.6.3", tsFiveSix, "typescript-5.6"], 13 | ["Latest", ts, "typescript"], 14 | ]; 15 | 16 | export const projectsPaths = path.join(__dirname, "./projects"); 17 | export const transformerPath = require.resolve("typescript-transform-paths"); 18 | export const builtTransformerPath = require.resolve("typescript-transform-paths"); 19 | 20 | Error.stackTraceLimit = 120; 21 | 22 | export { default as ts } from "typescript"; 23 | -------------------------------------------------------------------------------- /test/jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { JestConfigWithTsJest } from "ts-jest"; 2 | 3 | const config: JestConfigWithTsJest = { 4 | testEnvironment: "node", 5 | preset: "ts-jest", 6 | testRegex: String.raw`.*(test|spec)\.tsx?$`, 7 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "license": "MIT", 4 | "scripts": { 5 | "test": "jest", 6 | "g:ts-node": "cd $INIT_CWD && ts-node", 7 | "postinstall": "node prepare.mjs" 8 | }, 9 | "devDependencies": { 10 | "@nrwl/cli": "^15.9.7", 11 | "@nrwl/js": "^15.9.7", 12 | "@nrwl/node": "^15.9.7", 13 | "@nrwl/workspace": "^15.9.7", 14 | "@tsconfig/node18": "^18.2.4", 15 | "@types/jest": "^29.5.14", 16 | "@types/ts-expose-internals": "npm:ts-expose-internals@^4.9.5", 17 | "jest": "^29.7.0", 18 | "nx": "^15.9.7", 19 | "ts-jest": "^29.2.4", 20 | "ts-node": "^10.9.2", 21 | "ts-patch": "^3.3.0", 22 | "tsp1": "npm:ts-patch@1.*.*", 23 | "tsp2": "npm:ts-patch@2.*.*", 24 | "typescript": "^5.7.2", 25 | "typescript-3": "npm:typescript@3.6.5", 26 | "typescript-4.7": "npm:typescript@4.7.4", 27 | "typescript-5.5": "npm:typescript@5.5.4", 28 | "typescript-5.6": "npm:typescript@5.6.3", 29 | "typescript-transform-paths": "portal:../" 30 | }, 31 | "workspaces": [ 32 | "projects/*" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /test/prepare.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { existsSync } from "node:fs"; 3 | import { symlink } from "node:fs/promises"; 4 | import { dirname, resolve } from "node:path"; 5 | import { fileURLToPath } from "node:url"; 6 | import { patch } from "ts-patch"; 7 | import { patch as patch1 } from "tsp1"; 8 | import { patch as patch2 } from "tsp2"; 9 | 10 | const __dirname = dirname(fileURLToPath(import.meta.url)); // https://stackoverflow.com/questions/46745014/alternative-for-dirname-in-node-js-when-using-es6-modules 11 | 12 | async function symlinkTsNode() { 13 | // we need to patch the root package node_modules in order for it to locate ts-node for the register tests. 14 | // installing ts-node as a depenency in the root is not an option since it would create two copies of ts-node 15 | // thus messing with the mocks in the tests 16 | const target = resolve(__dirname, "node_modules/ts-node"); 17 | const path = resolve(__dirname, "../node_modules/ts-node"); 18 | 19 | if (!existsSync(path)) await symlink(target, path, "junction"); 20 | } 21 | 22 | function patchTsModules() { 23 | const rootDir = __dirname; 24 | 25 | /** @param {string} moduleName */ 26 | function patchTypescript(moduleName, tspatch) { 27 | const basedir = resolve(rootDir, "node_modules", moduleName); 28 | tspatch(["tsc.js", "typescript.js"], { basedir, dir: basedir }); 29 | } 30 | 31 | patchTypescript("typescript-3", patch1); 32 | patchTypescript("typescript-4.7", patch2); 33 | patchTypescript("typescript-5.5", patch); 34 | patchTypescript("typescript-5.6", patch); 35 | patchTypescript("typescript", patch); 36 | } 37 | 38 | patchTsModules(); 39 | await symlinkTsNode(); 40 | -------------------------------------------------------------------------------- /test/projects/extras/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@tests/extras", 4 | "version": "0.0.0" 5 | } 6 | -------------------------------------------------------------------------------- /test/projects/extras/src/id.ts: -------------------------------------------------------------------------------- 1 | export const b = null; 2 | -------------------------------------------------------------------------------- /test/projects/extras/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "#identifier"; 2 | import { b } from "#identifier"; 3 | 4 | console.log(b); 5 | -------------------------------------------------------------------------------- /test/projects/extras/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | 4 | "ts-node": { 5 | "require": ["typescript-transform-paths/register"] 6 | }, 7 | 8 | "compilerOptions": { 9 | "noEmit": true, 10 | "outDir": "dist", 11 | 12 | "rootDir": ".", 13 | "module": "CommonJS", 14 | "esModuleInterop": true, 15 | "moduleResolution": "node", 16 | "declaration": true, 17 | 18 | "baseUrl": "./src", 19 | "paths": { 20 | "#identifier": ["./id.ts"] 21 | }, 22 | 23 | "plugins": [ 24 | { 25 | "transform": "typescript-transform-paths" 26 | }, 27 | { 28 | "transform": "typescript-transform-paths", 29 | "afterDeclarations": true 30 | } 31 | ] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/projects/general/circular/a.ts: -------------------------------------------------------------------------------- 1 | import { B } from "@circular/b"; 2 | 3 | export class A { 4 | constructor(public name: string) {} 5 | } 6 | 7 | export const b = new B(); 8 | b.print(new A("This is a random name")); 9 | -------------------------------------------------------------------------------- /test/projects/general/circular/b.ts: -------------------------------------------------------------------------------- 1 | import { A } from "@circular/a"; 2 | 3 | export class B { 4 | print(a: A) { 5 | console.log(a.name); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/projects/general/core/index.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import sum = require("@utils/sum"); 3 | export { sum } from "@utils/index"; 4 | export { g } from "#utils/hello"; 5 | export { sum as sum2 } from "#utils/sum"; 6 | export { NoRuntimecodeHere } from "@utils/types-only"; 7 | import { subs, NoRuntimecodeHere } from "@utils/index"; 8 | import "@circular/b"; 9 | import { A } from "@circular/a"; 10 | import * as path from "path"; 11 | import * as b from "circular/a"; 12 | import * as c from "../circular/a"; 13 | import { myNative } from "@utils/utils.native"; 14 | 15 | c.A; 16 | b.A; 17 | path.sep; 18 | myNative(); 19 | 20 | sum.sum(2, 3); 21 | 22 | const n: NoRuntimecodeHere = null as any; 23 | 24 | subs(2, 3); 25 | const a = new A(""); 26 | 27 | (async function () { 28 | const Logger = await (await import("@dynamic/logger")).Logger; 29 | const logger = new Logger(); 30 | 31 | logger.log("hi"); 32 | })(); 33 | 34 | (async function () { 35 | const Tester = (await import("@dynamic/tester")).Tester; 36 | 37 | const testerConst = (await import("@dynamic/tester")).tester; 38 | const testerClass = new Tester(); 39 | 40 | testerClass.test(12); 41 | testerConst.test("12"); 42 | })(); 43 | -------------------------------------------------------------------------------- /test/projects/general/dynamic/logger.ts: -------------------------------------------------------------------------------- 1 | type Tester = import("@dynamic/tester").Tester; 2 | 3 | export class Logger { 4 | level: string = "hi"; 5 | tester: Tester; 6 | 7 | public log(x: string): void { 8 | console.log(x); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/projects/general/dynamic/tester.ts: -------------------------------------------------------------------------------- 1 | export class Tester { 2 | public test(x: number): void { 3 | console.log(x); 4 | } 5 | } 6 | 7 | export const tester = { 8 | test: (x: string) => console.log(x), 9 | }; 10 | -------------------------------------------------------------------------------- /test/projects/general/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@tests/general", 4 | "version": "0.0.0" 5 | } 6 | -------------------------------------------------------------------------------- /test/projects/general/secondary/hello.ts: -------------------------------------------------------------------------------- 1 | export const g = "hello"; 2 | -------------------------------------------------------------------------------- /test/projects/general/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "ESNext", 5 | "outDir": "__built", 6 | "moduleResolution": "node", 7 | 8 | "declaration": true, 9 | "baseUrl": "./", 10 | "paths": { 11 | "path": ["https://external.url/path.js"], 12 | "@*": ["*"], 13 | "#utils/*": ["./utils/*", "./secondary/*"], 14 | "*": ["*"] 15 | }, 16 | 17 | "esModuleInterop": true, 18 | 19 | "plugins": [ 20 | { "transform": "typescript-transform-paths" }, 21 | { "transform": "typescript-transform-paths", "afterDeclarations": true } 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/projects/general/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@utils/sum"; 2 | export * from "@utils/subs"; 3 | export { NoRuntimecodeHere } from "@utils/types-only"; 4 | -------------------------------------------------------------------------------- /test/projects/general/utils/subs.ts: -------------------------------------------------------------------------------- 1 | import { sum } from "@utils/sum"; 2 | 3 | export function subs(a: number, b: number) { 4 | return sum(a, -b); 5 | } 6 | -------------------------------------------------------------------------------- /test/projects/general/utils/sum.ts: -------------------------------------------------------------------------------- 1 | export function sum(a: number, b: number) { 2 | return a + b; 3 | } 4 | -------------------------------------------------------------------------------- /test/projects/general/utils/types-only.ts: -------------------------------------------------------------------------------- 1 | export type NoRuntimecodeHere = "never gonna give you up!"; 2 | 3 | throw new Error("Not supposed to be!"); 4 | -------------------------------------------------------------------------------- /test/projects/general/utils/utils.native.ts: -------------------------------------------------------------------------------- 1 | export function myNative() {} 2 | -------------------------------------------------------------------------------- /test/projects/nx/nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@nrwl/workspace/presets/core.json", 3 | "npmScope": "project-nx", 4 | "affected": { 5 | "defaultBase": "main" 6 | }, 7 | "cli": { 8 | "defaultCollection": "@nrwl/workspace" 9 | }, 10 | "tasksRunnerOptions": { 11 | "default": { 12 | "runner": "@nrwl/workspace/tasks-runners/default", 13 | "options": { 14 | "cacheableOperations": ["build", "lint", "test", "e2e"], 15 | "useDaemonProcess": false 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/projects/nx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project-nx", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "build": "nx build library1 --skip-nx-cache" 6 | }, 7 | "private": true 8 | } 9 | -------------------------------------------------------------------------------- /test/projects/nx/packages/library1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "library1", 3 | "version": "0.0.1" 4 | } 5 | -------------------------------------------------------------------------------- /test/projects/nx/packages/library1/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "packages/library1", 3 | "sourceRoot": "packages/library1/src", 4 | "projectType": "library", 5 | "targets": { 6 | "build": { 7 | "executor": "@nrwl/node:webpack", 8 | "outputs": ["{options.outputPath}"], 9 | "options": { 10 | "outputPath": "dist/library1", 11 | "main": "packages/library1/src/index.ts", 12 | "outputFileName": "index.js", 13 | "tsConfig": "packages/library1/tsconfig.lib.json", 14 | "sourceMap": false, 15 | "transformers": [ 16 | { 17 | "name": "typescript-transform-paths/nx-transformer", 18 | "options": { 19 | "afterDeclarations": true 20 | } 21 | } 22 | ] 23 | }, 24 | "dependsOn": [] 25 | } 26 | }, 27 | "tags": [] 28 | } 29 | -------------------------------------------------------------------------------- /test/projects/nx/packages/library1/src/index.ts: -------------------------------------------------------------------------------- 1 | import { name as library2Name } from "library2"; 2 | export default library2Name; 3 | 4 | console.log(library2Name); 5 | -------------------------------------------------------------------------------- /test/projects/nx/packages/library1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/projects/nx/packages/library1/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "../../dist/library1", 6 | "declaration": true, 7 | "types": ["node"] 8 | }, 9 | "include": ["src/*.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /test/projects/nx/packages/library2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "library2", 3 | "version": "0.0.1" 4 | } 5 | -------------------------------------------------------------------------------- /test/projects/nx/packages/library2/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "packages/library2", 3 | "sourceRoot": "packages/library2/src", 4 | "projectType": "library", 5 | "targets": {}, 6 | "tags": [] 7 | } 8 | -------------------------------------------------------------------------------- /test/projects/nx/packages/library2/src/index.ts: -------------------------------------------------------------------------------- 1 | export const name = "library2"; 2 | -------------------------------------------------------------------------------- /test/projects/nx/packages/library2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "files": [], 4 | "include": [], 5 | "references": [ 6 | { 7 | "path": "./tsconfig.lib.json" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/projects/nx/packages/library2/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "../../dist/out-tsc", 6 | "declaration": true, 7 | "types": ["node"] 8 | }, 9 | "exclude": ["**/*.spec.ts", "**/*.test.ts"], 10 | "include": ["**/*.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /test/projects/nx/tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "importHelpers": true, 11 | "target": "es2015", 12 | "module": "esnext", 13 | "lib": ["es2017", "dom"], 14 | "skipLibCheck": true, 15 | "skipDefaultLibCheck": true, 16 | "baseUrl": ".", 17 | "paths": { 18 | "library1": ["packages/library1/src/index.ts"], 19 | "library2": ["packages/library2/src/index.ts"] 20 | } 21 | }, 22 | "exclude": ["node_modules", "tmp"] 23 | } 24 | -------------------------------------------------------------------------------- /test/projects/nx/workspace.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "projects": { 4 | "library1": "packages/library1", 5 | "library2": "packages/library2" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/projects/project-ref/a/index.ts: -------------------------------------------------------------------------------- 1 | export const AReffedConst = 43; 2 | -------------------------------------------------------------------------------- /test/projects/project-ref/a/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": ["index.ts"], 3 | "extends": "../tsconfig.base.json", 4 | "compilerOptions": { 5 | "outDir": "../lib/a", 6 | "declaration": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/projects/project-ref/b/index.ts: -------------------------------------------------------------------------------- 1 | export { AReffedConst } from "#a/index"; 2 | export { LocalConst } from "#b/local/index"; 3 | -------------------------------------------------------------------------------- /test/projects/project-ref/b/local/index.ts: -------------------------------------------------------------------------------- 1 | export const LocalConst = 55; 2 | -------------------------------------------------------------------------------- /test/projects/project-ref/b/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": ["index.ts", "local/index.ts"], 3 | "extends": "../tsconfig.base.json", 4 | "compilerOptions": { 5 | "outDir": "../lib/b" 6 | }, 7 | "references": [ 8 | { 9 | "path": "../a" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /test/projects/project-ref/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@tests/project-ref", 4 | "version": "0.0.0" 5 | } 6 | -------------------------------------------------------------------------------- /test/projects/project-ref/tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | "esModuleInterop": true, 5 | "module": "ESNext", 6 | "target": "ESNext", 7 | "composite": true, 8 | "declaration": true, 9 | 10 | "baseUrl": ".", 11 | "paths": { 12 | "#a/*": ["./a/*"], 13 | "#b/*": ["./b/*"] 14 | }, 15 | "plugins": [ 16 | { 17 | "transform": "typescript-transform-paths" 18 | }, 19 | { 20 | "transform": "typescript-transform-paths", 21 | "afterDeclarations": true 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/projects/project-ref/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { 4 | "path": "./a" 5 | }, 6 | { 7 | "path": "./b" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/projects/specific/generated/dir/gen-file.ts: -------------------------------------------------------------------------------- 1 | import "#root/dir/src-file"; 2 | 3 | export const b = 1; 4 | -------------------------------------------------------------------------------- /test/projects/specific/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@tests/specific", 4 | "version": "0.0.0" 5 | } 6 | -------------------------------------------------------------------------------- /test/projects/specific/src/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "JsonValue": "1" 3 | } 4 | -------------------------------------------------------------------------------- /test/projects/specific/src/dir/src-file.ts: -------------------------------------------------------------------------------- 1 | import "#root/dir/gen-file"; 2 | 3 | export const a = 2; 4 | export type A = string; 5 | export default A; 6 | -------------------------------------------------------------------------------- /test/projects/specific/src/excluded-file.ts: -------------------------------------------------------------------------------- 1 | export const dd = 1; 2 | -------------------------------------------------------------------------------- /test/projects/specific/src/excluded/ex.ts: -------------------------------------------------------------------------------- 1 | export const bb = 1; 2 | -------------------------------------------------------------------------------- /test/projects/specific/src/general.ts: -------------------------------------------------------------------------------- 1 | export type GeneralTypeA = number; 2 | export const GeneralConstA = null; 3 | export const GeneralConstB = null; 4 | export interface Expandable { 5 | a: string; 6 | } 7 | -------------------------------------------------------------------------------- /test/projects/specific/src/index.ts: -------------------------------------------------------------------------------- 1 | export { b } from "#root/dir/gen-file"; 2 | export { a } from "#root/dir/src-file"; 3 | 4 | import type { A as ATypeOnly } from "#root/dir/src-file"; 5 | import type ATypeOnlyDefault from "#root/dir/src-file"; 6 | export { ATypeOnly, ATypeOnlyDefault }; 7 | 8 | import(/* webpackChunkName: "Comment" */ "#root/dir/src-file"); 9 | import( 10 | // comment 1 11 | /* 12 | comment 2 13 | */ 14 | "#root/dir/src-file" 15 | ); 16 | 17 | export { bb } from "#exclusion/ex"; 18 | export { dd } from "#root/excluded-file"; 19 | 20 | export { JsonValue } from "#root/data.json"; 21 | export { GeneralConstA } from "#root/general"; 22 | export { GeneralConstB } from "#root/general.js"; 23 | 24 | export const b1 = 3; 25 | 26 | export { ConstB } from "#elision"; 27 | -------------------------------------------------------------------------------- /test/projects/specific/src/module-augment.ts: -------------------------------------------------------------------------------- 1 | import { Expandable } from "#root/general"; 2 | 3 | // Gets transformed 4 | declare module "#root/general" { 5 | interface Expandable { 6 | b: number; 7 | } 8 | } 9 | 10 | // Remains untransformed 11 | declare module "./excluded-file" { 12 | type B = null; 13 | } 14 | 15 | declare const b: Expandable; 16 | b.b && b.a; 17 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-a/index.d.ts: -------------------------------------------------------------------------------- 1 | export type PackageAType = null; 2 | export declare const packageAConst: PackageAType; 3 | export type PassThru = T | string; 4 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-a/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | packageAConst: null, 3 | }; 4 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-a/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pkg-a", 3 | "version": "0.0.0-alpha", 4 | "private": "true", 5 | "main": "./index.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-a/sub-pkg/main.d.ts: -------------------------------------------------------------------------------- 1 | export type SubPackageType = null; 2 | export declare const subPackageConst: SubPackageType; 3 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-a/sub-pkg/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | subPackageConst: null, 3 | }; 4 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-a/sub-pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sub-pkg", 3 | "version": "0.0.0-alpha", 4 | "private": "true", 5 | "main": "main.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-b/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pkg-b", 3 | "version": "0.0.0-alpha", 4 | "private": "true", 5 | "main": "./subdir/main.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-b/subdir/main.d.ts: -------------------------------------------------------------------------------- 1 | export type PackageBType = null; 2 | export declare const packageBConst: PackageBType; 3 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-b/subdir/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | packageBConst: null, 3 | }; 4 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-c/main.d.ts: -------------------------------------------------------------------------------- 1 | export type PackageCType = null; 2 | export declare const packageCConst: PackageCType; 3 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-c/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | packageCConst: null, 3 | }; 4 | -------------------------------------------------------------------------------- /test/projects/specific/src/packages/pkg-c/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pkg-c", 3 | "version": "0.0.0-alpha", 4 | "private": "true", 5 | "main": "./main.js" 6 | } 7 | -------------------------------------------------------------------------------- /test/projects/specific/src/sub-packages.ts: -------------------------------------------------------------------------------- 1 | export { packageAConst, PackageAType } from "#packages/pkg-a"; 2 | export { packageBConst, PackageBType } from "#packages/pkg-b"; 3 | export { packageCConst, PackageCType } from "#packages/pkg-c"; 4 | export { SubPackageType, subPackageConst } from "#packages/pkg-a/sub-pkg"; 5 | 6 | // This path should resolve to './packages/pkg-c/main' 7 | export { packageCConst as C2 } from "#packages/pkg-c/main"; 8 | // This path should resolve to './packages/pkg-c/main.js', due to explicit extension 9 | export { packageCConst as C3 } from "#packages/pkg-c/main.js"; 10 | // This path should resolve to './packages/pkg-a/sub-pkg/main' 11 | export { subPackageConst as C4 } from "#packages/pkg-a/sub-pkg/main"; 12 | // This path should resolve to './packages/pkg-a/sub-pkg/main.js', due to explicit extension 13 | export { subPackageConst as C5 } from "#packages/pkg-a/sub-pkg/main.js"; 14 | 15 | export type ImportWithChildren = import("#packages/pkg-a").PassThru; 16 | -------------------------------------------------------------------------------- /test/projects/specific/src/tags.ts: -------------------------------------------------------------------------------- 1 | /* ****************************************************************************************************************** * 2 | * JSDoc 3 | * ****************************************************************************************************************** */ 4 | 5 | /** @no-transform-path */ 6 | import * as skipTransform1 from "#root/index"; 7 | 8 | /** 9 | * @multi-tag1 10 | * @no-transform-path 11 | * @multi-tag2 12 | */ 13 | import * as skipTransform2 from "#root/index"; 14 | 15 | /** 16 | * @multi-tag1 17 | * @transform-path ./dir/src-file 18 | * @multi-tag2 19 | */ 20 | import * as explicitTransform1 from "./index"; 21 | 22 | /** 23 | * @multi-tag1 24 | * @transform-path http://www.go.com/react.js 25 | * @multi-tag2 26 | */ 27 | import * as explicitTransform2 from "./index"; 28 | 29 | /* ****************************************************************************************************************** * 30 | * JS Tag 31 | * ****************************************************************************************************************** */ 32 | 33 | // @no-transform-path 34 | import * as skipTransform3 from "#root/index"; 35 | 36 | // @multi-tag1 37 | // @no-transform-path 38 | // @multi-tag2 39 | import * as skipTransform4 from "#root/index"; 40 | 41 | // @multi-tag1 42 | // @transform-path ./dir/src-file 43 | // @multi-tag2 44 | import * as explicitTransform3 from "./index"; 45 | 46 | // @multi-tag1 47 | // @transform-path http://www.go.com/react.js 48 | // @multi-tag2 49 | import * as explicitTransform4 from "./index"; 50 | 51 | export { 52 | skipTransform1, 53 | skipTransform2, 54 | skipTransform3, 55 | skipTransform4, 56 | explicitTransform1, 57 | explicitTransform2, 58 | explicitTransform3, 59 | explicitTransform4, 60 | }; 61 | -------------------------------------------------------------------------------- /test/projects/specific/src/type-elision/a.ts: -------------------------------------------------------------------------------- 1 | export type TypeA = string; 2 | export const ConstB = "hello"; 3 | 4 | export type TypeAndConst = null; 5 | export const TypeAndConst = null; 6 | -------------------------------------------------------------------------------- /test/projects/specific/src/type-elision/index.ts: -------------------------------------------------------------------------------- 1 | import { ConstB, TypeA } from "#elision/a"; 2 | import { TypeA as TypeA2 } from "#elision/a"; 3 | export { ConstB, TypeA }; 4 | export { TypeA2 }; 5 | 6 | /* Import type */ 7 | import { type TypeAndConst, ConstB as __ } from "#elision/a"; 8 | export { TypeAndConst, __ }; 9 | 10 | /* Export type */ 11 | import { TypeAndConst as TypeAndConst2, ConstB as ___ } from "#elision/a"; 12 | export { type TypeAndConst2, ___ }; 13 | 14 | const b = TypeAndConst2; 15 | 16 | /* Unreferenced import type */ 17 | import { ConstB as ____, type TypeAndConst as TypeAndConst3 } from "#elision/a"; 18 | -------------------------------------------------------------------------------- /test/projects/specific/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "generated"], 3 | 4 | "compilerOptions": { 5 | "noEmit": true, 6 | 7 | "rootDir": ".", 8 | "module": "ESNext", 9 | "esModuleInterop": true, 10 | "moduleResolution": "node", 11 | "declaration": true, 12 | 13 | "rootDirs": ["src", "generated"], 14 | 15 | "baseUrl": ".", 16 | "paths": { 17 | "#root/*": ["./src/*", "./generated/*"], 18 | "#exclusion/*": ["./src/excluded/*"], 19 | "#elision": ["./src/type-elision"], 20 | "#elision/*": ["./src/type-elision/*"], 21 | "#packages/*": ["./src/packages/*"] 22 | }, 23 | "resolveJsonModule": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/tests/extras.test.ts: -------------------------------------------------------------------------------- 1 | import { createTsProgram, getEmitResultFromProgram, ModuleNotFoundError } from "../utils"; 2 | import { projectsPaths } from "../config"; 3 | import path from "node:path"; 4 | import ts from "typescript"; 5 | import * as config from "../config"; 6 | import { execSync } from "node:child_process"; 7 | 8 | /* ****************************************************************************************************************** * 9 | * Tests 10 | * ****************************************************************************************************************** */ 11 | 12 | describe(`Extra Tests`, () => { 13 | const projectRoot = ts.normalizePath(path.join(projectsPaths, "extras")); 14 | const indexFile = ts.normalizePath(path.join(projectRoot, "src/index.ts")); 15 | const tsConfigFile = ts.normalizePath(path.join(projectRoot, "tsconfig.json")); 16 | 17 | describe(`Built Tests`, () => { 18 | // see: https://github.com/LeDDGroup/typescript-transform-paths/issues/130 19 | test(`Transformer works without ts-node being present`, () => { 20 | jest.doMock( 21 | "ts-node", 22 | () => { 23 | throw new ModuleNotFoundError("ts-node"); 24 | }, 25 | { virtual: true }, 26 | ); 27 | try { 28 | const program = createTsProgram({ tsInstance: ts, tsConfigFile }, config.builtTransformerPath); 29 | const res = getEmitResultFromProgram(program); 30 | expect(res[indexFile].js).toMatch(`var _identifier_1 = require("./id")`); 31 | } finally { 32 | jest.dontMock("ts-node"); 33 | } 34 | }); 35 | 36 | describe(`ts-node register script`, () => { 37 | /** Yarn sometimes outputs bold text, which makes these tests flakey */ 38 | function stripAnsi(str: string) { 39 | // eslint-disable-next-line no-control-regex 40 | return str.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, ""); 41 | } 42 | 43 | test(`Works with --transpileOnly`, () => { 44 | const res = execSync("yarn g:ts-node --transpileOnly src/index.ts", { cwd: projectRoot }).toString(); 45 | expect(stripAnsi(res.trim())).toEqual("null"); 46 | }); 47 | 48 | test(`Works with --typeCheck`, () => { 49 | const res = execSync("yarn g:ts-node --typeCheck src/index.ts", { cwd: projectRoot }).toString(); 50 | expect(stripAnsi(res.trim())).toEqual("null"); 51 | }); 52 | }); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/tests/get-match-portion.test.ts: -------------------------------------------------------------------------------- 1 | import { getMatchPortion } from "../../src/utils/get-relative-path"; 2 | 3 | describe(`getMatchPortion`, () => { 4 | it("works in a simple case", () => { 5 | expect(getMatchPortion("/foo/bar", "/foo/quux")).toBe("/foo/"); 6 | }); 7 | 8 | // We use the function getMatchPortion to generate a new path for “to”, so let’s preserve 9 | // the case where possible. 10 | // Otherwise we are introducing inconsistency for our users, who may have had import from Foo, 11 | // their file is named Foo, but we rewrite the path to foo. 12 | // Although the file is still accessible in the file system, other tools might reasonably 13 | // complain about the unexpected case mismatch. 14 | it("prioritizes the casing of the “to” parameter", () => { 15 | expect(getMatchPortion("/foo/bar", "/foO/quux")).toBe("/foO/"); 16 | expect(getMatchPortion("/foo/bar", "/foo/Bonk")).toBe("/foo/B"); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/tests/nx.test.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "node:child_process"; 2 | import { readFileSync, rmSync } from "node:fs"; 3 | import path from "node:path"; 4 | 5 | import ts from "typescript"; 6 | 7 | import { nxTransformerPlugin } from "typescript-transform-paths"; 8 | import * as transformerModule from "../../dist/transformer"; 9 | 10 | import { projectsPaths } from "../config"; 11 | 12 | /* ****************************************************************************************************************** * 13 | * Tests 14 | * ****************************************************************************************************************** */ 15 | 16 | describe(`NX Transformer`, () => { 17 | describe("Plugin", () => { 18 | let mockedTransformer: jest.SpyInstance; 19 | 20 | const program = { x: 1 }; 21 | 22 | beforeAll(async () => { 23 | // @ts-expect-error TS(2345) FIXME: Argument of type '() => void' is not assignable to parameter of type '(transformationContext: TransformationContext) => (sourceFile: SourceFile) => SourceFile'. 24 | mockedTransformer = jest.spyOn(transformerModule, "default").mockReturnValue(() => {}); 25 | }); 26 | afterAll(() => { 27 | mockedTransformer.mockClear(); 28 | }); 29 | beforeEach(() => { 30 | mockedTransformer.mockReset(); 31 | }); 32 | 33 | test(`Before properly routes transform`, () => { 34 | const config = { a: 2 }; 35 | 36 | // @ts-expect-error TS(2559) FIXME: Type '{ a: number; }' has no properties in common with type 'Omit'. 37 | nxTransformerPlugin.before(config, program); 38 | 39 | expect(mockedTransformer).toHaveBeenCalledTimes(1); 40 | expect(mockedTransformer.mock.lastCall).toHaveLength(2); 41 | 42 | const [recProgram, recConfig] = mockedTransformer.mock.lastCall; 43 | expect(recProgram).toBe(program); 44 | expect(recConfig).toStrictEqual(config); 45 | }); 46 | 47 | test(`After properly routes transform`, () => { 48 | const config = { a: 2, afterDeclarations: true }; 49 | 50 | // @ts-expect-error TS(2345) FIXME: Argument of type '{ x: number; }' is not assignable to parameter of type 'Program'. 51 | nxTransformerPlugin.afterDeclarations(config, program); 52 | 53 | expect(mockedTransformer).toHaveBeenCalledTimes(1); 54 | expect(mockedTransformer.mock.lastCall).toHaveLength(2); 55 | 56 | const [recProgram, recConfig] = mockedTransformer.mock.lastCall; 57 | expect(recProgram).toBe(program); 58 | expect(recConfig).toStrictEqual({ ...config, afterDeclarations: true }); 59 | }); 60 | }); 61 | 62 | describe(`(e2e) Works in NX project`, () => { 63 | const projectRoot = ts.normalizePath(path.join(projectsPaths, "nx")); 64 | 65 | // TODO - Investigate ways to do without emit 66 | test(`Transformer works for emitted declaration`, () => { 67 | execSync("yarn run build", { cwd: projectRoot }); 68 | 69 | try { 70 | const file = readFileSync(`${projectRoot}/dist/library1/packages/library1/src/index.d.ts`, "utf8"); 71 | expect(file).toMatch(/import { name as library2Name } from "..\/..\/library2\/src";/); 72 | } finally { 73 | rmSync(`${projectRoot}/dist`, { recursive: true, force: true }); 74 | } 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /test/tests/project-ref.test.ts: -------------------------------------------------------------------------------- 1 | // noinspection ES6UnusedImports 2 | import * as path from "node:path"; 3 | import { createTsSolutionBuilder, EmittedFiles } from "../utils"; 4 | import { projectsPaths, ts } from "../config"; 5 | 6 | /* ****************************************************************************************************************** * 7 | * Config 8 | * ****************************************************************************************************************** */ 9 | 10 | /* File Paths */ 11 | const projectDir = ts.normalizePath(path.join(projectsPaths, "project-ref")); 12 | const indexFile = ts.normalizePath(path.join(projectDir, "lib/b/index.ts")); 13 | 14 | /* ****************************************************************************************************************** * 15 | * Tests 16 | * ****************************************************************************************************************** */ 17 | 18 | // see: https://github.com/LeDDGroup/typescript-transform-paths/issues/125 19 | describe(`Project References`, () => { 20 | let emittedFiles: EmittedFiles; 21 | 22 | beforeAll(() => { 23 | const builder = createTsSolutionBuilder({ tsInstance: ts, projectDir }); 24 | emittedFiles = builder.getEmitFiles(); 25 | }); 26 | 27 | test(`Specifier for referenced project file resolves properly`, () => { 28 | expect(emittedFiles[indexFile].js).toMatch(`export { AReffedConst } from "../a/index"`); 29 | expect(emittedFiles[indexFile].dts).toMatch(`export { AReffedConst } from "../a/index"`); 30 | }); 31 | 32 | test(`Specifier for local file resolves properly`, () => { 33 | expect(emittedFiles[indexFile].js).toMatch(`export { LocalConst } from "./local/index"`); 34 | expect(emittedFiles[indexFile].dts).toMatch(`export { LocalConst } from "./local/index"`); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/tests/register.test.ts: -------------------------------------------------------------------------------- 1 | import * as tsNode from "ts-node"; 2 | import { REGISTER_INSTANCE } from "ts-node"; 3 | import { PluginConfig } from "ts-patch"; 4 | import { CustomTransformers, PluginImport, Program } from "typescript"; 5 | 6 | import { register } from "typescript-transform-paths"; 7 | import * as transformerModule from "../../dist/transformer"; 8 | 9 | import { ModuleNotFoundError } from "../utils"; 10 | 11 | /* ****************************************************************************************************************** * 12 | * Config 13 | * ****************************************************************************************************************** */ 14 | 15 | const pluginOptions = { opt1: true, opt2: 3 }; 16 | const otherTransformer = { transform: "fake-transformer@23904" }; 17 | const configs = { 18 | "Implicit before": {}, 19 | "Explicit before": { before: true }, 20 | afterDeclarations: { afterDeclarations: true }, 21 | "Implicit before + afterDeclarations": [{}, { afterDeclarations: true }], 22 | "Explicit before + afterDeclarations": [{ before: true }, { afterDeclarations: true }], 23 | } as const; 24 | const configMap = Object.entries(configs).map(([label, cfg]) => { 25 | let hasBefore: boolean = false; 26 | let hasAfterDeclarations: boolean = false; 27 | const transformers = [ 28 | ...[cfg].flat().map((c) => { 29 | // @ts-expect-error TS(2339) FIXME: Property 'before' does not exist on type '{} | { readonly before: true; } | { readonly afterDeclarations: true; } | {} | { readonly afterDeclarations: true; } | { readonly before: true; } | { readonly afterDeclarations: true; }'. 30 | if (c.before || !c.afterDeclarations) hasBefore = true; 31 | // @ts-expect-error TS(2339) FIXME: Property 'afterDeclarations' does not exist on type '{} | { readonly before: true; } | { readonly afterDeclarations: true; } | {} | { readonly afterDeclarations: true; } | { readonly before: true; } | { readonly afterDeclarations: true; }'. 32 | if (c.afterDeclarations) hasAfterDeclarations = true; 33 | return { transform: "typescript-transform-paths", ...c, ...pluginOptions } as PluginConfig; 34 | }), 35 | otherTransformer, 36 | ] as PluginConfig[]; 37 | 38 | return { label, transformers, hasBefore, hasAfterDeclarations }; 39 | }); 40 | 41 | const instanceSymbol: typeof REGISTER_INSTANCE = tsNode["REGISTER_INSTANCE"]; 42 | 43 | /* ****************************************************************************************************************** * 44 | * Tests 45 | * ****************************************************************************************************************** */ 46 | 47 | describe(`Register script`, () => { 48 | describe(`Initialize`, () => { 49 | test(`Registers initial ts-node if none found`, () => { 50 | const originalTsNodeInstance = global.process[instanceSymbol]; 51 | global.process[instanceSymbol] = void 0; 52 | let registerSpy: jest.SpyInstance | undefined; 53 | try { 54 | registerSpy = jest.spyOn(tsNode, "register"); 55 | expect(global.process[instanceSymbol]).toBeUndefined(); 56 | 57 | register.initialize(); 58 | 59 | expect(registerSpy).toHaveBeenCalledTimes(1); 60 | expect(registerSpy.mock.calls[0]).toHaveLength(0); 61 | expect(global.process[instanceSymbol]).not.toBeUndefined(); 62 | } finally { 63 | global.process[instanceSymbol] = originalTsNodeInstance; 64 | registerSpy?.mockRestore(); 65 | } 66 | }); 67 | test(`Uses existing ts-node if found`, () => { 68 | const fakeInstance: unknown = {}; 69 | 70 | const originalTsNodeInstance = global.process[instanceSymbol]; 71 | // @ts-expect-error TS(2322) FIXME: Type 'unknown' is not assignable to type 'Service | undefined'. 72 | global.process[instanceSymbol] = fakeInstance; 73 | let registerSpy: jest.SpyInstance | undefined; 74 | try { 75 | registerSpy = jest.spyOn(tsNode, "register"); 76 | 77 | const { tsNodeInstance } = register.initialize(); 78 | 79 | expect(registerSpy).not.toHaveBeenCalled(); 80 | expect(tsNodeInstance).toBe(fakeInstance); 81 | } finally { 82 | global.process[instanceSymbol] = originalTsNodeInstance; 83 | registerSpy?.mockRestore(); 84 | } 85 | }); 86 | 87 | test(`Returns instance, tsNode, and symbol`, () => { 88 | const res = register.initialize(); 89 | expect(res.tsNode).toBe(tsNode); 90 | expect(res.tsNodeInstance).toBe(global.process[instanceSymbol]); 91 | expect(res.instanceSymbol).toBe(instanceSymbol); 92 | }); 93 | }); 94 | 95 | describe(`Register`, () => { 96 | test(`Throws without ts-node`, () => { 97 | jest.doMock( 98 | "ts-node", 99 | () => { 100 | throw new ModuleNotFoundError("ts-node"); 101 | }, 102 | { virtual: true }, 103 | ); 104 | expect(() => register()).toThrow(`Cannot resolve ts-node`); 105 | jest.dontMock("ts-node"); 106 | }); 107 | 108 | test(`Throws if can't register ts-node`, () => { 109 | jest.doMock("ts-node", () => ({ register: () => {} }), { virtual: true }); 110 | expect(() => register()).toThrow(`Could not register ts-node instance!`); 111 | jest.dontMock("ts-node"); 112 | }); 113 | 114 | test(`No transformers in tsConfig exits quietly`, () => { 115 | const originalInitialize = register.initialize; 116 | const initializeSpy = jest.spyOn(register, "initialize"); 117 | try { 118 | initializeSpy.mockImplementation(() => { 119 | const res = originalInitialize(); 120 | delete res.tsNodeInstance.config.options.plugins; 121 | return res; 122 | }); 123 | expect(register()).toBeUndefined(); 124 | } finally { 125 | initializeSpy.mockRestore(); 126 | } 127 | }); 128 | 129 | describe.each([ 130 | "Existing Transformer Config", 131 | "Existing Transformer Config Factory", 132 | "No Existing Transformers", 133 | ] as const)(`%s`, (configKind) => { 134 | // @ts-expect-error TS(2355) FIXME: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value. 135 | const fakeExistingTransformer = function fakeExistingTransformer(): unknown {}; 136 | // @ts-expect-error TS(2355) FIXME: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value. 137 | const fakeTransformer = function fakeTransformer(): unknown {}; 138 | const fakeTransformerConfig = { 139 | before: [fakeExistingTransformer], 140 | after: [fakeExistingTransformer], 141 | afterDeclarations: [fakeExistingTransformer], 142 | }; 143 | const transformerFactoryFn = jest.fn().mockReturnValue(fakeTransformerConfig); 144 | const fakeProgram: unknown = {}; 145 | 146 | let existingTransformers: CustomTransformers | ((p: Program) => CustomTransformers) | undefined; 147 | switch (configKind) { 148 | case "Existing Transformer Config Factory": { 149 | existingTransformers = transformerFactoryFn; 150 | break; 151 | } 152 | case "Existing Transformer Config": { 153 | // @ts-expect-error TS(2322) FIXME: Type '{ before: (() => unknown)[]; after: (() => unknown)[]; afterDeclarations: (() => unknown)[]; }' is not assignable to type 'CustomTransformers | ((p: Program) => CustomTransformers) | undefined'. 154 | existingTransformers = { ...fakeTransformerConfig }; 155 | break; 156 | } 157 | case "No Existing Transformers": { 158 | existingTransformers = void 0; 159 | } 160 | } 161 | 162 | describe.each(configMap)(`$label`, ({ transformers, hasBefore, hasAfterDeclarations }) => { 163 | let mockTransformer: jest.SpyInstance; 164 | let initializeSpy: jest.SpyInstance; 165 | let registerResult: tsNode.RegisterOptions; 166 | let instanceRegistrationResult: tsNode.Service; 167 | let mergedTransformers: CustomTransformers; 168 | 169 | beforeAll(() => { 170 | // @ts-expect-error TS(2345) FIXME: Argument of type '() => unknown' is not assignable to parameter of type '(transformationContext: TransformationContext) => (sourceFile: SourceFile) => SourceFile'. 171 | mockTransformer = jest.spyOn(transformerModule, "default").mockReturnValue(fakeTransformer); 172 | 173 | global.process[instanceSymbol] = void 0; 174 | 175 | const originalInitialize = register.initialize; 176 | initializeSpy = jest.spyOn(register, "initialize"); 177 | initializeSpy.mockImplementation(() => { 178 | const res = originalInitialize(); 179 | if (existingTransformers) res.tsNodeInstance.options.transformers = existingTransformers; 180 | else delete res.tsNodeInstance.options.transformers; 181 | 182 | res.tsNodeInstance.config.options.plugins = transformers as PluginImport[]; 183 | return res; 184 | }); 185 | 186 | const originalTsNodeInstance = global.process[instanceSymbol]; 187 | registerResult = register()!; 188 | instanceRegistrationResult = global.process[instanceSymbol]!; 189 | global.process[instanceSymbol] = originalTsNodeInstance; 190 | 191 | mergedTransformers = 192 | typeof registerResult.transformers === "function" 193 | ? // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to parameter of type 'Program'. 194 | registerResult.transformers(fakeProgram) 195 | : registerResult.transformers!; 196 | }); 197 | afterAll(() => { 198 | initializeSpy.mockRestore(); 199 | mockTransformer.mockRestore(); 200 | transformerFactoryFn.mockClear(); 201 | }); 202 | 203 | test(`Registers with ts-node`, () => { 204 | expect(registerResult?.transformers).not.toBeUndefined(); 205 | expect(instanceRegistrationResult.options).toStrictEqual(registerResult); 206 | }); 207 | 208 | if (existingTransformers === transformerFactoryFn) 209 | test(`Factory config instantiated with program`, () => { 210 | expect(transformerFactoryFn).toHaveBeenCalledTimes(1); 211 | expect(transformerFactoryFn).toHaveBeenCalledWith(fakeProgram); 212 | }); 213 | 214 | test(`Registers correct transformers`, () => { 215 | const expectedBefore = [ 216 | ...(hasBefore ? [fakeTransformer] : []), 217 | ...(existingTransformers ? fakeTransformerConfig.before : []), 218 | ]; 219 | const expectedAfter = existingTransformers ? fakeTransformerConfig.after : []; 220 | const expectedAfterDeclarations = [ 221 | ...(hasAfterDeclarations ? [fakeTransformer] : []), 222 | ...(existingTransformers ? fakeTransformerConfig.afterDeclarations : []), 223 | ]; 224 | 225 | const expected = { 226 | ...(expectedBefore.length && { before: expectedBefore }), 227 | ...(expectedAfter.length && { after: expectedAfter }), 228 | ...(expectedAfterDeclarations.length && { afterDeclarations: expectedAfterDeclarations }), 229 | }; 230 | 231 | expect(mergedTransformers).toStrictEqual(expected); 232 | }); 233 | 234 | test(`Transformer instantiated w/ proper config${ 235 | existingTransformers === transformerFactoryFn ? " & Program" : "" 236 | }`, () => { 237 | const callTimes = +hasBefore + +hasAfterDeclarations; 238 | expect(mockTransformer).toHaveBeenCalledTimes(callTimes); 239 | 240 | const afterDeclarationsConfig = transformers.find( 241 | (t) => t.transform === "typescript-transform-paths" && t.afterDeclarations, 242 | ); 243 | const beforeConfig = transformers.find( 244 | (t) => t.transform === "typescript-transform-paths" && !t.afterDeclarations, 245 | ); 246 | 247 | if (hasBefore) expect(beforeConfig).not.toBeUndefined(); 248 | if (hasAfterDeclarations) expect(afterDeclarationsConfig).not.toBeUndefined(); 249 | 250 | const expectedCfg = [beforeConfig, afterDeclarationsConfig].filter((c) => !!c); 251 | for (let i = 0; i < callTimes; i++) { 252 | expect(mockTransformer.mock.calls[i][0]).toBe( 253 | existingTransformers === transformerFactoryFn ? fakeProgram : void 0, 254 | ); 255 | expect(mockTransformer.mock.calls[i][1]).toBe(expectedCfg[i]); 256 | } 257 | }); 258 | }); 259 | }); 260 | }); 261 | }); 262 | -------------------------------------------------------------------------------- /test/tests/transformer/general.test.ts: -------------------------------------------------------------------------------- 1 | // noinspection ES6UnusedImports 2 | import * as path from "node:path"; 3 | import { createTsProgram, EmittedFiles, getEmitResultFromProgram } from "../../utils"; 4 | import { ts, tsModules, projectsPaths } from "../../config"; 5 | 6 | /* ****************************************************************************************************************** * 7 | * Helpers 8 | * ****************************************************************************************************************** */ 9 | 10 | const makeRelative = (tsInstance: typeof ts, fileName: string, p: string, rootDir: string) => { 11 | let rel = tsInstance.normalizePath(path.relative(path.dirname(fileName), path.join(rootDir, p))); 12 | if (rel[0] !== ".") rel = `./${rel}`; 13 | return `"${rel}"`; 14 | }; 15 | 16 | const getExpected = (tsInstance: typeof ts, fileName: string, original: string, rootDir: string): string => 17 | original 18 | .replaceAll(/"@(.*)"/g, (_, p) => makeRelative(tsInstance, fileName, p, rootDir)) 19 | .replaceAll(/"#utils\/(.*)"/g, (_, p) => 20 | makeRelative(tsInstance, fileName, path.join(p === "hello" ? "secondary" : "utils", p), rootDir), 21 | ) 22 | .replace('"path"', '"https://external.url/path.js"') 23 | .replace('"circular/a"', '"../circular/a"'); 24 | 25 | /* ****************************************************************************************************************** * 26 | * Tests 27 | * ****************************************************************************************************************** */ 28 | 29 | describe(`Transformer -> General Tests`, () => { 30 | const projectRoot = path.join(projectsPaths, "general"); 31 | const tsConfigFile = path.join(projectRoot, "tsconfig.json"); 32 | 33 | describe.each(tsModules)(`TypeScript %s`, (s, tsInstance) => { 34 | let originalFiles: EmittedFiles = {}; 35 | let transformedFiles: EmittedFiles = {}; 36 | 37 | const program = createTsProgram({ tsInstance: tsInstance as typeof ts, tsConfigFile, disablePlugin: true }); 38 | const programWithTransformer = createTsProgram({ tsInstance: tsInstance as typeof ts, tsConfigFile }); 39 | const fileNames = program.getRootFileNames() as string[]; 40 | 41 | beforeAll(() => { 42 | originalFiles = getEmitResultFromProgram(program); 43 | transformedFiles = getEmitResultFromProgram(programWithTransformer); 44 | }); 45 | 46 | describe.each(fileNames!.map((p) => [p.slice(projectRoot.length), p]))(`%s`, (_, file) => { 47 | let expected: EmittedFiles[string]; 48 | let transformed: EmittedFiles[string]; 49 | 50 | beforeAll(() => { 51 | transformed = transformedFiles[file]; 52 | expected = { 53 | // @ts-expect-error TS(2345) FIXME: Argument of type 'typeof ts | typeof ts | typeof import("typescript")' is not assignable to parameter of type 'typeof import("typescript")'. 54 | js: getExpected(tsInstance, file, originalFiles[file].js, projectRoot), 55 | // @ts-expect-error TS(2345) FIXME: Argument of type 'typeof ts | typeof ts | typeof import("typescript")' is not assignable to parameter of type 'typeof import("typescript")'. 56 | dts: getExpected(tsInstance, file, originalFiles[file].dts, projectRoot), 57 | }; 58 | }); 59 | 60 | test(`js matches`, () => expect(transformed.js).toEqual(expected.js)); 61 | test(`dts matches`, () => expect(transformed.dts).toEqual(expected.dts)); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/tests/transformer/specific.test.ts: -------------------------------------------------------------------------------- 1 | // noinspection ES6UnusedImports 2 | import * as path from "node:path"; 3 | import { 4 | createTsProgram, 5 | EmittedFiles, 6 | getEmitResultFromProgram, 7 | getManualEmitResult, 8 | getTsNodeEmitResult, 9 | } from "../../utils"; 10 | import { projectsPaths, ts, tsModules } from "../../config"; 11 | import { TsTransformPathsConfig } from "typescript-transform-paths"; 12 | import TS from "typescript"; 13 | 14 | /* ****************************************************************************************************************** * 15 | * Config 16 | * ****************************************************************************************************************** */ 17 | 18 | const baseConfig: TsTransformPathsConfig = { exclude: ["**/excluded/**", "excluded-file.*"] }; 19 | 20 | /* Test Mapping */ 21 | const modes = ["program", "manual", "ts-node"] as const; 22 | const testConfigs: { label: string; tsInstance: unknown; mode: (typeof modes)[number]; tsSpecifier: string }[] = []; 23 | for (const cfg of tsModules) 24 | testConfigs.push(...modes.map((mode) => ({ label: cfg[0], tsInstance: cfg[1], mode, tsSpecifier: cfg[2] }))); 25 | 26 | /* File Paths */ 27 | const projectRoot = ts.normalizePath(path.join(projectsPaths, "specific")); 28 | const tsConfigFile = ts.normalizePath(path.join(projectsPaths, "specific/tsconfig.json")); 29 | const genFile = ts.normalizePath(path.join(projectRoot, "generated/dir/gen-file.ts")); 30 | const srcFile = ts.normalizePath(path.join(projectRoot, "src/dir/src-file.ts")); 31 | const indexFile = ts.normalizePath(path.join(projectRoot, "src/index.ts")); 32 | const tagFile = ts.normalizePath(path.join(projectRoot, "src/tags.ts")); 33 | const typeElisionIndex = ts.normalizePath(path.join(projectRoot, "src/type-elision/index.ts")); 34 | const subPackagesFile = ts.normalizePath(path.join(projectRoot, "src/sub-packages.ts")); 35 | const moduleAugmentFile = ts.normalizePath(path.join(projectRoot, "src/module-augment.ts")); 36 | 37 | /* ****************************************************************************************************************** * 38 | * Types 39 | * ****************************************************************************************************************** */ 40 | 41 | declare global { 42 | namespace jest { 43 | // eslint-disable-next-line @typescript-eslint/no-unused-vars -- no way to extend type definitions without using the same declaration as the original types 44 | interface Matchers { 45 | transformedMatches(expected: RegExp | string, opt?: { base?: EmittedFiles[]; kind?: ("dts" | "js")[] }): void; 46 | } 47 | } 48 | } 49 | 50 | /* ****************************************************************************************************************** * 51 | * Tests 52 | * ****************************************************************************************************************** */ 53 | 54 | describe(`Specific Tests`, () => { 55 | describe.each(testConfigs)(`TypeScript $label - Mode: $mode`, ({ tsInstance, mode, tsSpecifier }) => { 56 | // @ts-expect-error TS(18046) FIXME: 'tsInstance' is of type 'unknown'. 57 | const tsVersion = +tsInstance.versionMajorMinor.split(".").slice(0, 2).join(""); 58 | let normalEmit: EmittedFiles; 59 | let rootDirsEmit: EmittedFiles; 60 | let skipDts = false; 61 | 62 | beforeAll(() => { 63 | switch (mode) { 64 | case "program": { 65 | const program = createTsProgram({ 66 | // @ts-expect-error TS(2322) FIXME: Type 'unknown' is not assignable to type 'typeof import("typescript")'. 67 | tsInstance, 68 | tsConfigFile, 69 | pluginOptions: { 70 | ...baseConfig, 71 | useRootDirs: false, 72 | }, 73 | }); 74 | normalEmit = getEmitResultFromProgram(program); 75 | 76 | const rootDirsProgram = createTsProgram({ 77 | // @ts-expect-error TS(2322) FIXME: Type 'unknown' is not assignable to type 'typeof import("typescript")'. 78 | tsInstance, 79 | tsConfigFile, 80 | pluginOptions: { 81 | ...baseConfig, 82 | useRootDirs: true, 83 | }, 84 | }); 85 | rootDirsEmit = getEmitResultFromProgram(rootDirsProgram); 86 | break; 87 | } 88 | case "manual": { 89 | skipDts = true; 90 | // @ts-expect-error TS(18046) FIXME: 'tsInstance' is of type 'unknown'. 91 | const pcl = tsInstance.getParsedCommandLineOfConfigFile( 92 | tsConfigFile, 93 | {}, 94 | // @ts-expect-error TS(18046) FIXME: 'tsInstance' is of type 'unknown'. 95 | tsInstance.sys, 96 | )! as TS.ParsedCommandLine; 97 | normalEmit = getManualEmitResult({ ...baseConfig, useRootDirs: false }, tsInstance, pcl); 98 | rootDirsEmit = getManualEmitResult({ ...baseConfig, useRootDirs: true }, tsInstance, pcl); 99 | break; 100 | } 101 | case "ts-node": { 102 | // @ts-expect-error TS(18046) FIXME: 'tsInstance' is of type 'unknown'. 103 | const pcl = tsInstance.getParsedCommandLineOfConfigFile( 104 | tsConfigFile, 105 | {}, 106 | // @ts-expect-error TS(18046) FIXME: 'tsInstance' is of type 'unknown'. 107 | tsInstance.sys, 108 | )! as TS.ParsedCommandLine; 109 | skipDts = true; 110 | normalEmit = getTsNodeEmitResult({ ...baseConfig, useRootDirs: false }, pcl, tsSpecifier); 111 | rootDirsEmit = getTsNodeEmitResult({ ...baseConfig, useRootDirs: true }, pcl, tsSpecifier); 112 | } 113 | } 114 | 115 | expect.extend({ 116 | transformedMatches( 117 | fileName: string, 118 | expected: RegExp | string, 119 | opt?: { base?: EmittedFiles[]; kind?: ("dts" | "js")[] }, 120 | ) { 121 | const bases = opt?.base ?? [normalEmit, rootDirsEmit]; 122 | const kinds = (opt?.kind ?? ["dts", "js"]).filter((k) => !skipDts || k !== "dts"); 123 | 124 | let failed: boolean = false; 125 | const messages: string[] = []; 126 | for (const base of bases) { 127 | for (const kind of kinds) { 128 | const content = base[fileName][kind]; 129 | const isValid = typeof expected === "string" ? content.includes(expected) : expected.test(content); 130 | if (!isValid) { 131 | failed = true; 132 | messages.push( 133 | `File: ${fileName}\nKind: ${kind}\nrootDirs: ${base === normalEmit}\n\n` + 134 | `Expected: \`${expected}\`\nReceived:\n\t${content.replaceAll(/(\r?\n)+/g, "$1\t")}`, 135 | ); 136 | } 137 | } 138 | } 139 | 140 | return { message: () => messages.join("\n\n"), pass: !failed }; 141 | }, 142 | }); 143 | }); 144 | 145 | describe(`Options`, () => { 146 | test(`(useRootDirs: true) Re-maps for rootDirs`, () => { 147 | expect(genFile).transformedMatches(`import "./src-file"`, { base: [rootDirsEmit] }); 148 | expect(srcFile).transformedMatches(`import "./gen-file"`, { base: [rootDirsEmit] }); 149 | expect(indexFile).transformedMatches(`export { b } from "./dir/gen-file"`, { base: [rootDirsEmit] }); 150 | expect(indexFile).transformedMatches(`export { a } from "./dir/src-file"`, { base: [rootDirsEmit] }); 151 | }); 152 | 153 | test(`(useRootDirs: false) Ignores rootDirs`, () => { 154 | expect(genFile).transformedMatches(`import "../../src/dir/src-file"`, { base: [normalEmit] }); 155 | expect(srcFile).transformedMatches(`import "../../generated/dir/gen-file"`, { base: [normalEmit] }); 156 | expect(indexFile).transformedMatches(`export { b } from "../generated/dir/gen-file"`, { base: [normalEmit] }); 157 | expect(indexFile).transformedMatches(`export { a } from "./dir/src-file"`, { base: [normalEmit] }); 158 | }); 159 | 160 | test(`(exclude) Doesn't transform for exclusion patterns`, () => { 161 | expect(indexFile).transformedMatches( 162 | /export { bb } from "#exclusion\/ex";\s*export { dd } from "#root\/excluded-file"/, 163 | ); 164 | }); 165 | }); 166 | 167 | describe(`Tags`, () => { 168 | test(`(@no-transform-path) Doesn't transform path`, () => { 169 | for (let i = 1; i <= 4; i++) 170 | expect(tagFile).transformedMatches(`import * as skipTransform${i} from "#root/index`); 171 | }); 172 | 173 | test(`(@transform-path) Transforms path with explicit value`, () => { 174 | expect(tagFile).transformedMatches(`import * as explicitTransform1 from "./dir/src-file"`); 175 | expect(tagFile).transformedMatches(`import * as explicitTransform2 from "http://www.go.com/react.js"`); 176 | expect(tagFile).transformedMatches(`import * as explicitTransform3 from "./dir/src-file"`); 177 | expect(tagFile).transformedMatches(`import * as explicitTransform4 from "http://www.go.com/react.js"`); 178 | }); 179 | }); 180 | 181 | (mode === "program" ? test : test.skip)(`Type elision works properly`, () => { 182 | expect(typeElisionIndex).transformedMatches(/import { ConstB } from "\.\/a";\s*export { ConstB };/, { 183 | kind: ["js"], 184 | }); 185 | expect(typeElisionIndex).transformedMatches( 186 | /import { ConstB, TypeA } from "\.\/a";\s*import { TypeA as TypeA2 } from "\.\/a";\s*export { ConstB, TypeA };\s*export { TypeA2 };/, 187 | { kind: ["dts"] }, 188 | ); 189 | 190 | if (tsVersion >= 50) { 191 | /* Import type-only keyword on import specifier */ 192 | expect(typeElisionIndex).transformedMatches(/import { ConstB as __ } from "\.\/a";\s*export { __ };/, { 193 | kind: ["js"], 194 | }); 195 | 196 | expect(typeElisionIndex).transformedMatches( 197 | /import { type TypeAndConst, ConstB as __ } from "\.\/a";\s*export { TypeAndConst, __ };/, 198 | { kind: ["dts"] }, 199 | ); 200 | 201 | /* Export Import type-only keyword on import specifier */ 202 | expect(typeElisionIndex).transformedMatches( 203 | /import { TypeAndConst as TypeAndConst2, ConstB as ___ } from "\.\/a";\s*export { type TypeAndConst2, ___ };/, 204 | { kind: ["dts"] }, 205 | ); 206 | 207 | expect(typeElisionIndex).transformedMatches( 208 | /import { TypeAndConst as TypeAndConst2, ConstB as ___ } from "\.\/a";\s*export { ___ };/, 209 | { kind: ["js"] }, 210 | ); 211 | 212 | /* Unreferenced w/ type-only keyword on import specifier */ 213 | expect(typeElisionIndex).not.transformedMatches( 214 | /import { ConstB as _{4}, type TypeAndConst as TypeAndConst3 } from "\.\/a";\s/, 215 | { kind: ["dts"] }, 216 | ); 217 | 218 | expect(typeElisionIndex).not.transformedMatches(/import { ConstB as _{4} } from "\.\/a";\s/, { kind: ["js"] }); 219 | } 220 | }); 221 | 222 | (!skipDts && tsVersion >= 38 ? test : test.skip)(`Import type-only transforms`, () => { 223 | expect(indexFile).transformedMatches(`import type { A as ATypeOnly } from "./dir/src-file"`, { kind: ["dts"] }); 224 | }); 225 | 226 | test(`Copies comments in async import`, () => { 227 | expect(indexFile).transformedMatches(`import(/* webpackChunkName: "Comment" */ "./dir/src-file");`, { 228 | kind: ["js"], 229 | }); 230 | expect(indexFile).transformedMatches( 231 | /\/\/ comment 1\r?\n\s*\/\*\r?\n\s*comment 2\r?\n\s*\*\/\r?\n\s*"\.\/dir\/src-file"/, 232 | { kind: ["js"] }, 233 | ); 234 | }); 235 | 236 | test(`Preserves explicit extensions`, () => { 237 | expect(indexFile).transformedMatches(`export { JsonValue } from "./data.json"`); 238 | expect(indexFile).transformedMatches(`export { GeneralConstA } from "./general"`); 239 | expect(indexFile).transformedMatches(`export { GeneralConstB } from "./general.js"`); 240 | }); 241 | 242 | test(`Does not output implicit index filenames`, () => { 243 | expect(indexFile).transformedMatches(`export { ConstB } from "./type-elision"`); 244 | }); 245 | 246 | test(`Resolves sub-modules properly`, () => { 247 | const a = { 248 | js: `export { packageAConst } from "./packages/pkg-a"`, 249 | full: `export { packageAConst, PackageAType } from "./packages/pkg-a"`, 250 | }; 251 | const b = { 252 | js: `export { packageBConst } from "./packages/pkg-b"`, 253 | full: `export { packageBConst, PackageBType } from "./packages/pkg-b"`, 254 | }; 255 | const c = { 256 | js: `export { packageCConst } from "./packages/pkg-c"`, 257 | full: `export { packageCConst, PackageCType } from "./packages/pkg-c"`, 258 | }; 259 | const sub = { 260 | js: `export { subPackageConst } from "./packages/pkg-a/sub-pkg"`, 261 | full: `export { SubPackageType, subPackageConst } from "./packages/pkg-a/sub-pkg"`, 262 | }; 263 | 264 | for (const exp of [a, b, c, sub]) { 265 | expect(subPackagesFile).transformedMatches(mode === "program" ? exp.js : exp.full, { kind: ["js"] }); 266 | if (!skipDts) expect(subPackagesFile).transformedMatches(exp.full, { kind: ["dts"] }); 267 | } 268 | 269 | expect(subPackagesFile).transformedMatches(`export { packageCConst as C2 } from "./packages/pkg-c/main"`); 270 | expect(subPackagesFile).transformedMatches(`export { packageCConst as C3 } from "./packages/pkg-c/main.js"`); 271 | expect(subPackagesFile).transformedMatches( 272 | `export { subPackageConst as C4 } from "./packages/pkg-a/sub-pkg/main"`, 273 | ); 274 | expect(subPackagesFile).transformedMatches( 275 | `export { subPackageConst as C5 } from "./packages/pkg-a/sub-pkg/main.js"`, 276 | ); 277 | }); 278 | 279 | (!skipDts && tsVersion >= 38 ? test : test.skip)(`Resolves nested imports`, () => { 280 | expect(subPackagesFile).transformedMatches( 281 | `export ${ 282 | tsVersion < 49 ? `declare ` : "" 283 | }type ImportWithChildren = import("./packages/pkg-a").PassThru`, 284 | { kind: ["dts"] }, 285 | ); 286 | }); 287 | 288 | (skipDts ? test.skip : test)(`Resolves module augmentation`, () => { 289 | expect(moduleAugmentFile).transformedMatches(`declare module "./general" {`, { kind: ["dts"] }); 290 | expect(moduleAugmentFile).transformedMatches(`declare module "./excluded-file" {`, { kind: ["dts"] }); 291 | }); 292 | }); 293 | }); 294 | -------------------------------------------------------------------------------- /test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node18", 3 | "include": ["tests", "utils"], 4 | 5 | "compilerOptions": { 6 | "types": ["jest", "node", "ts-expose-internals"], 7 | "noEmit": true, 8 | "strict": true, 9 | "esModuleInterop": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/utils/helpers.ts: -------------------------------------------------------------------------------- 1 | import { default as tstpTransform, TsTransformPathsConfig } from "typescript-transform-paths"; 2 | import fs from "node:fs"; 3 | import ts from "typescript"; 4 | import * as tsNode from "ts-node"; 5 | import * as config from "../config"; 6 | 7 | /* ****************************************************************************************************************** */ 8 | // region: Types 9 | /* ****************************************************************************************************************** */ 10 | 11 | export type EmittedFiles = { [fileName: string]: { js: string; dts: string } }; 12 | 13 | export interface CreateTsProgramOptions { 14 | tsInstance: typeof ts; 15 | files?: { [fileName: string]: /* data */ string }; 16 | tsConfigFile?: string; 17 | disablePlugin?: boolean; 18 | additionalOptions?: ts.CompilerOptions; 19 | pluginOptions?: TsTransformPathsConfig; 20 | } 21 | 22 | export interface CreateTsSolutionBuilderOptions { 23 | tsInstance: typeof ts; 24 | projectDir: string; 25 | } 26 | 27 | // endregion 28 | 29 | /* ****************************************************************************************************************** */ 30 | // region: Helpers 31 | /* ****************************************************************************************************************** */ 32 | 33 | function createWriteFile(outputFiles: EmittedFiles) { 34 | return (fileName: string, data: string) => { 35 | let { 1: rootName, 2: ext } = fileName.match(/(.+)\.((d.ts)|(js))$/) ?? []; 36 | if (!ext) return; 37 | rootName = `${rootName}.ts`; 38 | const key = ext.replace(".", "") as keyof EmittedFiles[string]; 39 | outputFiles[rootName] ??= {} as EmittedFiles[string]; 40 | outputFiles[rootName][key] = data; 41 | }; 42 | } 43 | 44 | function createReadFile( 45 | outputFiles: EmittedFiles, 46 | originalReadFile: (path: string, encoding?: string) => string | undefined, 47 | ) { 48 | return (fileName: string) => { 49 | let { 1: rootName, 2: ext } = fileName.match(/(.+)\.((d.ts)|(js))$/) ?? []; 50 | if (ext) { 51 | rootName = `${rootName}.ts`; 52 | const key = ext.replace(".", "") as keyof EmittedFiles[string]; 53 | const res = outputFiles[rootName]?.[key]; 54 | if (res) return res; 55 | } 56 | return originalReadFile(fileName); 57 | }; 58 | } 59 | 60 | // endregion 61 | 62 | /* ****************************************************************************************************************** */ 63 | // region: Utilities 64 | /* ****************************************************************************************************************** */ 65 | 66 | /** Create TS Program with faux files and options */ 67 | export function createTsProgram( 68 | opt: CreateTsProgramOptions, 69 | transformerPath: string = config.transformerPath, 70 | ): ts.Program { 71 | const { disablePlugin, additionalOptions, pluginOptions } = opt; 72 | const tsInstance: typeof ts = opt.tsInstance; 73 | 74 | if ((!opt.files && !opt.tsConfigFile) || (opt.files && opt.tsConfigFile)) 75 | throw new Error(`Must supply *either* files or tsConfigFile to createProgram`); 76 | 77 | const extendOptions = Object.assign({}, additionalOptions, { 78 | outDir: undefined, 79 | noEmit: false, 80 | plugins: disablePlugin 81 | ? [] 82 | : [ 83 | { transform: transformerPath, ...pluginOptions }, 84 | { 85 | transform: transformerPath, 86 | afterDeclarations: true, 87 | ...pluginOptions, 88 | }, 89 | ], 90 | }); 91 | 92 | let compilerOptions: ts.CompilerOptions = {}; 93 | let fileNames: string[]; 94 | let host: ts.CompilerHost | undefined; 95 | 96 | if (opt.tsConfigFile) { 97 | // @ts-expect-error TS(2345) FIXME: Argument of type 'System' is not assignable to parameter of type 'ParseConfigFileHost'. 98 | const pcl = tsInstance.getParsedCommandLineOfConfigFile(opt.tsConfigFile, extendOptions, tsInstance.sys)!; 99 | compilerOptions = pcl.options; 100 | fileNames = pcl.fileNames; 101 | } else { 102 | const files = Object.entries(compilerOptions.files!).reduce((p, [fileName, data]) => { 103 | // @ts-expect-error TS(7053) FIXME: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'. 104 | p[tsInstance.normalizePath(fileName)] = data; 105 | return p; 106 | }, {}); 107 | fileNames = Object.keys(files); 108 | 109 | host = tsInstance.createCompilerHost(compilerOptions); 110 | compilerOptions = extendOptions; 111 | 112 | /* Patch host to feed mock files */ 113 | const originalGetSourceFile: unknown = host.getSourceFile; 114 | host.getSourceFile = function (fileName: string, scriptTarget: ts.ScriptTarget, ...rest) { 115 | if (Object.keys(files).includes(fileName)) 116 | // @ts-expect-error TS(7053) FIXME: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'. 117 | return tsInstance.createSourceFile(fileName, files[fileName], scriptTarget); 118 | // @ts-expect-error TS(18046) FIXME: 'originalGetSourceFile' is of type 'unknown'. 119 | else originalGetSourceFile(fileName, scriptTarget, ...rest); 120 | }; 121 | } 122 | 123 | return tsInstance.createProgram({ options: compilerOptions, rootNames: fileNames, host }); 124 | } 125 | 126 | export function createTsSolutionBuilder( 127 | opt: CreateTsSolutionBuilderOptions, 128 | ): ts.SolutionBuilder & { getEmitFiles(): EmittedFiles } { 129 | const { tsInstance, projectDir } = opt; 130 | 131 | const outputFiles: EmittedFiles = {}; 132 | 133 | const host = tsInstance.createSolutionBuilderHost(); 134 | const originalReadFile = host.readFile; 135 | Object.assign(host, { 136 | readFile: createReadFile(outputFiles, originalReadFile), 137 | writeFile: createWriteFile(outputFiles), 138 | }); 139 | 140 | const builder = tsInstance.createSolutionBuilder(host, [projectDir], { force: true }); 141 | 142 | return Object.assign(builder, { 143 | getEmitFiles() { 144 | builder.build(); 145 | return outputFiles; 146 | }, 147 | }); 148 | } 149 | 150 | /** Get emitted files for program */ 151 | export function getEmitResultFromProgram(program: ts.Program): EmittedFiles { 152 | const outputFiles: EmittedFiles = {}; 153 | program.emit(undefined, createWriteFile(outputFiles)); 154 | return outputFiles; 155 | } 156 | 157 | export function getManualEmitResult( 158 | pluginConfig: TsTransformPathsConfig, 159 | tsInstance: unknown, 160 | pcl: ts.ParsedCommandLine, 161 | ) { 162 | const { options: compilerOptions, fileNames } = pcl; 163 | // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to parameter of type 'TransformerExtras | undefined'. 164 | const transformer = tstpTransform(void 0, pluginConfig, { ts: tsInstance } as unknown, { 165 | compilerOptions, 166 | fileNames, 167 | }); 168 | 169 | // @ts-expect-error TS(18046) FIXME: 'tsInstance' is of type 'unknown'. 170 | const { transformed } = tsInstance.transform( 171 | fileNames.map((f) => 172 | // @ts-expect-error TS(18046) FIXME: 'tsInstance' is of type 'unknown'. 173 | tsInstance.createSourceFile(f, fs.readFileSync(f, "utf8"), tsInstance.ScriptTarget.ESNext, true), 174 | ), 175 | [transformer], 176 | compilerOptions, 177 | ); 178 | 179 | // @ts-expect-error TS(18046) FIXME: 'tsInstance' is of type 'unknown'. 180 | const printer = tsInstance.createPrinter(); 181 | 182 | const res: EmittedFiles = {}; 183 | // @ts-expect-error TS(2322) FIXME: Type 'unknown' is not assignable to type '{ js: string; dts: string; }'. 184 | for (const sourceFile of transformed) res[sourceFile.fileName] = { js: printer.printFile(sourceFile) }; 185 | 186 | return res; 187 | } 188 | 189 | export function getTsNodeEmitResult( 190 | pluginConfig: TsTransformPathsConfig, 191 | pcl: ts.ParsedCommandLine, 192 | tsSpecifier: string, 193 | ) { 194 | const compiler = tsNode.create({ 195 | transpileOnly: true, 196 | transformers: { 197 | // @ts-expect-error TS(2345) FIXME: Argument of type 'unknown' is not assignable to parameter of type 'TransformerExtras | undefined'. 198 | // eslint-disable-next-line @typescript-eslint/no-require-imports 199 | before: [tstpTransform(void 0, pluginConfig, { ts: require(tsSpecifier) })], 200 | }, 201 | project: pcl.options.configFilePath, 202 | compiler: tsSpecifier, 203 | logError: true, 204 | ignoreDiagnostics: [1144, 1005], // Issues with old TS and type only imports 205 | }); 206 | 207 | const originalRegister = global.process[tsNode.REGISTER_INSTANCE]; 208 | global.process[tsNode.REGISTER_INSTANCE] = compiler; 209 | try { 210 | const res: EmittedFiles = {}; 211 | for (const fileName of pcl.fileNames.filter((f) => !/\.d\.ts$/.test(f))) { 212 | // @ts-expect-error TS(2322) FIXME: Type 'unknown' is not assignable to type '{ js: string; dts: string; }'. 213 | res[fileName] = { js: compiler.compile(fs.readFileSync(fileName, "utf8"), fileName) }; 214 | } 215 | 216 | return res; 217 | } finally { 218 | global.process[tsNode.REGISTER_INSTANCE] = originalRegister; 219 | } 220 | } 221 | 222 | // endregion 223 | -------------------------------------------------------------------------------- /test/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./helpers"; 2 | export * from "./module-not-found-error"; 3 | -------------------------------------------------------------------------------- /test/utils/module-not-found-error.ts: -------------------------------------------------------------------------------- 1 | /** Mimicks a module not found nodejs error, see https://nodejs.org/docs/v20.16.0/api/errors.html */ 2 | export class ModuleNotFoundError extends Error { 3 | code = "MODULE_NOT_FOUND"; 4 | 5 | constructor(packageName: string, options?: ErrorOptions) { 6 | super(`Uncaught Error: Cannot find module '${packageName}'`, options); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@tsconfig/strictest", "@tsconfig/node18"], 3 | "include": ["src"], 4 | "exclude": ["src/declarations"], 5 | "compilerOptions": { 6 | "rootDir": "src", 7 | "outDir": "dist", 8 | "declaration": true, 9 | "sourceMap": true, 10 | // less strict 11 | "exactOptionalPropertyTypes": false 12 | } 13 | } 14 | --------------------------------------------------------------------------------