From 52fb0e36816695baca28de81a281655d05b79fc8 Mon Sep 17 00:00:00 2001 From: Jason D'Amour Date: Fri, 18 Oct 2024 16:10:51 -0700 Subject: [PATCH] Add package support to typescript/javascript dependency inference --- .../javascript/dependency_inference/rules.py | 81 +++++++++++++------ .../typescript/dependency_inference/rules.py | 20 ++--- 2 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/python/pants/backend/javascript/dependency_inference/rules.py b/src/python/pants/backend/javascript/dependency_inference/rules.py index 4a220c69957..334fd600f30 100644 --- a/src/python/pants/backend/javascript/dependency_inference/rules.py +++ b/src/python/pants/backend/javascript/dependency_inference/rules.py @@ -135,17 +135,13 @@ def _create_inference_metadata( ) -async def _prepare_inference_metadata(address: Address, file_path: str) -> InferenceMetadata: - owning_pkg, maybe_config = await concurrently( - find_owning_package(OwningNodePackageRequest(address)), - find_parent_ts_config(ParentTSConfigRequest(file_path, "jsconfig.json"), **implicitly()), - ) +async def _prepare_inference_metadata(owning_pkg, maybe_config, spec_path) -> InferenceMetadata: if not owning_pkg.target: return InferenceMetadata.javascript( ( os.path.dirname(maybe_config.ts_config.path) if maybe_config.ts_config - else address.spec_path + else spec_path ), {}, maybe_config.ts_config.resolution_root_dir if maybe_config.ts_config else None, @@ -173,29 +169,57 @@ def _add_extensions(file_imports: frozenset[str], file_extensions: tuple[str, .. async def _determine_import_from_candidates( candidates: ParsedJavascriptDependencyCandidate, package_candidate_map: NodePackageCandidateMap, + tsconfig: TSConfig | None, file_extensions: tuple[str, ...], ) -> Addresses: - paths = await path_globs_to_paths( - _add_extensions( - candidates.file_imports, - file_extensions, - ) - ) - local_owners = await Get(Owners, OwnersRequest(paths.files)) - - owning_targets = await Get(Targets, Addresses(local_owners)) - - addresses = Addresses(tgt.address for tgt in owning_targets) - if not local_owners: - non_path_string_bases = FrozenOrderedSet( - non_path_string.partition(os.path.sep)[0] - for non_path_string in candidates.package_imports + addresses = Addresses(()) + # 1. handle relative imports + if candidates.file_imports: + paths = await path_globs_to_paths( + _add_extensions( + candidates.file_imports, + file_extensions, + ) ) - addresses = Addresses( - package_candidate_map[pkg_name] - for pkg_name in non_path_string_bases - if pkg_name in package_candidate_map + local_owners = await Get(Owners, OwnersRequest(paths.files)) + owning_targets = await Get(Targets, Addresses(local_owners)) + addresses = Addresses(tgt.address for tgt in owning_targets) + + # 2. handle package imports + elif candidates.package_imports: + + # Try prepending the tsconfig root dir to the package import. + first_party_packge_imports = frozenset( + os.path.join(tsconfig.resolution_root_dir, pkg_import) + for pkg_import in candidates.package_imports + ) if tsconfig and tsconfig.resolution_root_dir else frozenset() + paths = await path_globs_to_paths( + _add_extensions( + first_party_packge_imports, + file_extensions, + ) ) + local_owners = await Get(Owners, OwnersRequest(paths.files)) + + # 2.a. check for first-party package imports + if local_owners: + owning_targets = await Get(Targets, Addresses(local_owners)) + addresses = Addresses(tgt.address for tgt in owning_targets) + + # 2.b. check for third-party package imports + else: + # If package name begins with @, then keep the subpackage name + non_path_string_bases = FrozenOrderedSet( + f"{non_path_string.split('/')[0]}/{non_path_string.split('/')[1]}" + if non_path_string.startswith("@") + else non_path_string.partition(os.path.sep)[0] + for non_path_string in candidates.package_imports + ) + addresses = Addresses( + package_candidate_map[pkg_name] + for pkg_name in non_path_string_bases + if pkg_name in package_candidate_map + ) return addresses @@ -243,7 +267,11 @@ async def infer_js_source_dependencies( sources = await Get( HydratedSources, HydrateSourcesRequest(source, for_sources_types=[JSRuntimeSourceField]) ) - metadata = await _prepare_inference_metadata(request.field_set.address, source.file_path) + owning_pkg, maybe_config = await concurrently( + find_owning_package(OwningNodePackageRequest(request.field_set.address)), + find_parent_ts_config(ParentTSConfigRequest(source.file_path, "jsconfig.json"), **implicitly()), + ) + metadata = await _prepare_inference_metadata(owning_pkg, maybe_config, request.field_set.address.spec_path) import_strings, candidate_pkgs = await concurrently( parse_javascript_deps(NativeDependenciesRequest(sources.snapshot.digest, metadata)), @@ -258,6 +286,7 @@ async def infer_js_source_dependencies( _determine_import_from_candidates( candidates, candidate_pkgs, + tsconfig=maybe_config.ts_config, file_extensions=JS_FILE_EXTENSIONS + JSX_FILE_EXTENSIONS, ) for string, candidates in import_strings.imports.items() diff --git a/src/python/pants/backend/typescript/dependency_inference/rules.py b/src/python/pants/backend/typescript/dependency_inference/rules.py index 5e5aaa9ef53..7b399ed8570 100644 --- a/src/python/pants/backend/typescript/dependency_inference/rules.py +++ b/src/python/pants/backend/typescript/dependency_inference/rules.py @@ -25,6 +25,7 @@ ) from pants.backend.javascript.subsystems.nodejs_infer import NodeJSInfer from pants.backend.javascript.target_types import JS_FILE_EXTENSIONS +from pants.backend.tsx.target_types import TSX_FILE_EXTENSIONS from pants.backend.typescript import tsconfig from pants.backend.typescript.target_types import ( TS_FILE_EXTENSIONS, @@ -77,18 +78,13 @@ def _create_inference_metadata( ) -async def _prepare_inference_metadata(address: Address, file_path: str) -> InferenceMetadata: - owning_pkg, maybe_config = await concurrently( - find_owning_package(OwningNodePackageRequest(address)), - find_parent_ts_config(ParentTSConfigRequest(file_path, "tsconfig.json"), **implicitly()), - ) - +async def _prepare_inference_metadata(owning_pkg, maybe_config, spec_path) -> InferenceMetadata: if not owning_pkg.target: return InferenceMetadata.javascript( ( os.path.dirname(maybe_config.ts_config.path) if maybe_config.ts_config - else address.spec_path + else spec_path ), {}, maybe_config.ts_config.resolution_root_dir if maybe_config.ts_config else None, @@ -99,7 +95,6 @@ async def _prepare_inference_metadata(address: Address, file_path: str) -> Infer maybe_config.ts_config, ) - @rule async def infer_typescript_source_dependencies( request: InferTypeScriptDependenciesRequest, @@ -112,7 +107,11 @@ async def infer_typescript_source_dependencies( sources = await Get( HydratedSources, HydrateSourcesRequest(source, for_sources_types=[TypeScriptSourceField]) ) - metadata = await _prepare_inference_metadata(request.field_set.address, source.file_path) + owning_pkg, maybe_config = await concurrently( + find_owning_package(OwningNodePackageRequest(request.field_set.address)), + find_parent_ts_config(ParentTSConfigRequest(source.file_path, "tsconfig.json"), **implicitly()), + ) + metadata = await _prepare_inference_metadata(owning_pkg, maybe_config, request.field_set.address.spec_path) import_strings, candidate_pkgs = await concurrently( parse_javascript_deps(NativeDependenciesRequest(sources.snapshot.digest, metadata)), @@ -128,7 +127,8 @@ async def infer_typescript_source_dependencies( _determine_import_from_candidates( candidates, candidate_pkgs, - file_extensions=TS_FILE_EXTENSIONS + JS_FILE_EXTENSIONS, + tsconfig=maybe_config.ts_config, + file_extensions=TS_FILE_EXTENSIONS + JS_FILE_EXTENSIONS + TSX_FILE_EXTENSIONS, ) for string, candidates in import_strings.imports.items() ),