Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add package support to typescript/javascript dependency inference #21556

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

jasondamour
Copy link

@jasondamour jasondamour commented Oct 18, 2024

This adds better dependency inference to typescript/javascript.

  1. Adds support for imports relative to tsconfig root
    a. i.e. utils/stringUtils -> frontend/src/utils/stringUtils.ts
  2. Adds support for namespaced package imports
    a. @apollo/client -> :package#__apollo/client
  3. Adds support for importing .tsx from .ts files
    a. This is a weird one, I had to add this to support my usecase but I'm not sure the "right" way to handle js/jsx/ts/tsx cross-imports.

I'm not how much of the javascript "spec" I covered

Sample file

# example.ts
import 'axios'
import '@apollo/client'
import 'utils/stringUtils'
import 'utils/tableUtils'
import '../utils/urlUtils'

Produces

PANTS_SOURCE=../../OSS/pants pants --no-pantsd dependecies example.ts
frontend/src/utils/stringUtils.ts:../../typescript
frontend/src/utils/tableUtils.tsx:../../tsx
frontend/src/utils/urlUtils.ts:../../typescript
frontend:package#__apollo/client
frontend:package#axios

@jasondamour jasondamour marked this pull request as ready for review October 18, 2024 23:11
@benjyw benjyw requested a review from tobni October 21, 2024 13:17
Copy link
Contributor

@huonw huonw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. Is it possible to add some tests to the adjacent rules_test.py files?

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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be good to ensure there's type annotations on these args if possible.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yep, meant to get back around to that and never did...

@@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're supporting TSX here, should we also support JSX? I'm not sure, just pattern matching.

Comment on lines +191 to +202
# 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))
Copy link
Contributor

@tobni tobni Nov 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@tobni tobni Nov 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might also explain why I find the if-clause ill-formed. I think you might have misunderstood or been tricked by a bug in what the intention of ParsedJavascriptDependencyCandidate was. The parser can produce candidates on either form or both, but the separation you do here assumes it knows which case it found. It doesn't. That is why the method is named _determine_import_from_candidates. But like I said, the parser or my assumptions on how to read tsconfig might be bugged.

for non_path_string in candidates.package_imports
addresses = Addresses(())
# 1. handle relative imports
if candidates.file_imports:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if-clause is ill-formed. It discards too much information.

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)),
Copy link
Contributor

@tobni tobni Nov 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Word of warning that this parses javascript and JSX files, and will fail to parse as soon as it encounters the type keyword, which is a valid qualifier for typescript imports but not JS.

@tobni
Copy link
Contributor

tobni commented Nov 3, 2024

You need to provide the tsconfig or full repro to be able to validate your example.

"Adds support for imports relative to tsconfig root", this is not a default of the ts resolution spec, but might be for your tooling. If it is, I'm going to encourage you set your tsconfig to match what the tooling is implicitly doing, instead of merging "1a" of this change.

You generally need to supply "paths" entries in the tsconfig that maps to the "any" match (*) to get behaviour like that, from what I understand. See https://www.typescriptlang.org/tsconfig/#paths and https://www.typescriptlang.org/docs/handbook/modules/reference.html#paths

If tests prove "2a" I think that would be a good bug fix.

3a should adhere to https://www.typescriptlang.org/docs/handbook/modules/reference.html#file-extension-substitution.

I just want to reiterate that the parser is a javascript parser, not a typescript parser. It is not up to the task of parsing any typescript specifics, so please tread lightly.

Comment on lines +212 to +217
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
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants