Skip to content

Commit

Permalink
consider ts and tsx files on javascript dependency inference (#21840)
Browse files Browse the repository at this point in the history
# Description

Its possible to import `.ts` and `.tsx` files from a `.js` file on the
frontend world. The current js dependency inference wasn't considering
these file extensions.
  • Loading branch information
kevin-oliveira-zocdoc authored Jan 21, 2025
1 parent 7aa687e commit 02ca67a
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 14 deletions.
4 changes: 4 additions & 0 deletions docs/notes/2.25.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ def inject_my_custom_fields(request: MyCustomInjectFieldsRequest) -> InjectedNfp
return InjectedNfpmPackageFields(fields, address=request.target.address)
```

#### JavaScript

The dependency inference now considers `.ts` and `.tsx` file extensions.

### Plugin API changes

The version of Python used by Pants itself is now [3.11](https://docs.python.org/3/whatsnew/3.11.html) (up from 3.9).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
JSRuntimeSourceField,
)
from pants.backend.jsx.target_types import JSX_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
from pants.backend.typescript.tsconfig import ParentTSConfigRequest, TSConfig, find_parent_ts_config
from pants.build_graph.address import Address
from pants.core.util_rules.unowned_dependency_behavior import (
Expand Down Expand Up @@ -258,7 +260,12 @@ async def infer_js_source_dependencies(
_determine_import_from_candidates(
candidates,
candidate_pkgs,
file_extensions=JS_FILE_EXTENSIONS + JSX_FILE_EXTENSIONS,
file_extensions=(
JS_FILE_EXTENSIONS
+ JSX_FILE_EXTENSIONS
+ TS_FILE_EXTENSIONS
+ TSX_FILE_EXTENSIONS
),
)
for string, candidates in import_strings.imports.items()
),
Expand Down
127 changes: 114 additions & 13 deletions src/python/pants/backend/javascript/dependency_inference/rules_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
from pants.backend.javascript.dependency_inference.rules import rules as dependency_inference_rules
from pants.backend.javascript.package_json import AllPackageJson
from pants.backend.javascript.target_types import JSSourcesGeneratorTarget, JSSourceTarget
from pants.backend.jsx.target_types import JSXSourcesGeneratorTarget, JSXSourceTarget
from pants.backend.tsx.target_types import TSXSourcesGeneratorTarget, TSXSourceTarget
from pants.backend.typescript.target_types import (
TypeScriptSourcesGeneratorTarget,
TypeScriptSourceTarget,
)
from pants.build_graph.address import Address
from pants.core.util_rules.unowned_dependency_behavior import UnownedDependencyError
from pants.engine.internals.graph import Owners, OwnersRequest
Expand All @@ -36,7 +42,17 @@ def rule_runner() -> RuleRunner:
QueryRule(InferredDependencies, (InferNodePackageDependenciesRequest,)),
QueryRule(InferredDependencies, (InferJSDependenciesRequest,)),
],
target_types=[*package_json.target_types(), JSSourceTarget, JSSourcesGeneratorTarget],
target_types=[
*package_json.target_types(),
JSSourceTarget,
JSSourcesGeneratorTarget,
JSXSourceTarget,
JSXSourcesGeneratorTarget,
TSXSourceTarget,
TSXSourcesGeneratorTarget,
TypeScriptSourceTarget,
TypeScriptSourcesGeneratorTarget,
],
)
return rule_runner

Expand All @@ -61,14 +77,27 @@ def get_inferred_package_jsons_address(
def test_infers_esmodule_js_dependencies(rule_runner: RuleRunner) -> None:
rule_runner.write_files(
{
"src/js/BUILD": "javascript_sources()",
"src/js/BUILD": dedent(
"""\
javascript_sources()
jsx_sources(name='jsx')
typescript_sources(name='ts')
tsx_sources(name='tsx')
"""
),
"src/js/index.mjs": dedent(
"""\
import fs from "fs";
import { x } from "./xes.mjs";
import { x } from "./moduleA";
import { y } from "./moduleB";
import { z } from "./moduleC";
import { w } from "./moduleD";
"""
),
"src/js/xes.mjs": "",
"src/js/moduleA.mjs": "",
"src/js/moduleB.jsx": "",
"src/js/moduleC.ts": "",
"src/js/moduleD.tsx": "",
}
)

Expand All @@ -78,21 +107,39 @@ def test_infers_esmodule_js_dependencies(rule_runner: RuleRunner) -> None:
[InferJSDependenciesRequest(JSSourceInferenceFieldSet.create(index_tgt))],
).include

assert set(addresses) == {Address("src/js", relative_file_path="xes.mjs")}
assert set(addresses) == {
Address("src/js", relative_file_path="moduleA.mjs"),
Address("src/js", relative_file_path="moduleB.jsx", target_name="jsx"),
Address("src/js", relative_file_path="moduleC.ts", target_name="ts"),
Address("src/js", relative_file_path="moduleD.tsx", target_name="tsx"),
}


def test_infers_esmodule_js_dependencies_from_ancestor_files(rule_runner: RuleRunner) -> None:
rule_runner.write_files(
{
"src/js/BUILD": "javascript_sources()",
"src/js/BUILD": dedent(
"""\
javascript_sources()
jsx_sources(name='jsx')
typescript_sources(name='ts')
tsx_sources(name='tsx')
"""
),
"src/js/a/BUILD": "javascript_sources()",
"src/js/a/index.mjs": dedent(
"""\
import fs from "fs";
import { x } from "../xes.mjs";
import { x } from "../moduleA";
import { y } from "../moduleB";
import { z } from "../moduleC";
import { w } from "../moduleD";
"""
),
"src/js/xes.mjs": "",
"src/js/moduleA.mjs": "",
"src/js/moduleB.jsx": "",
"src/js/moduleC.ts": "",
"src/js/moduleD.tsx": "",
}
)

Expand All @@ -102,21 +149,39 @@ def test_infers_esmodule_js_dependencies_from_ancestor_files(rule_runner: RuleRu
[InferJSDependenciesRequest(JSSourceInferenceFieldSet.create(index_tgt))],
).include

assert set(addresses) == {Address("src/js", relative_file_path="xes.mjs")}
assert set(addresses) == {
Address("src/js", relative_file_path="moduleA.mjs"),
Address("src/js", relative_file_path="moduleB.jsx", target_name="jsx"),
Address("src/js", relative_file_path="moduleC.ts", target_name="ts"),
Address("src/js", relative_file_path="moduleD.tsx", target_name="tsx"),
}


def test_infers_commonjs_js_dependencies_from_ancestor_files(rule_runner: RuleRunner) -> None:
rule_runner.write_files(
{
"src/js/BUILD": "javascript_sources()",
"src/js/BUILD": dedent(
"""\
javascript_sources()
jsx_sources(name='jsx')
typescript_sources(name='ts')
tsx_sources(name='tsx')
"""
),
"src/js/a/BUILD": "javascript_sources()",
"src/js/a/index.cjs": dedent(
"""\
const fs = require("fs");
const { x } = require("../xes.cjs");
const { x } = require("../moduleA.cjs");
const { y } = require("../moduleB.jsx");
const { z } = require("../moduleC.ts");
const { w } = require("../moduleD.tsx");
"""
),
"src/js/xes.cjs": "",
"src/js/moduleA.cjs": "",
"src/js/moduleB.jsx": "",
"src/js/moduleC.ts": "",
"src/js/moduleD.tsx": "",
}
)

Expand All @@ -126,7 +191,12 @@ def test_infers_commonjs_js_dependencies_from_ancestor_files(rule_runner: RuleRu
[InferJSDependenciesRequest(JSSourceInferenceFieldSet.create(index_tgt))],
).include

assert set(addresses) == {Address("src/js", relative_file_path="xes.cjs")}
assert set(addresses) == {
Address("src/js", relative_file_path="moduleA.cjs"),
Address("src/js", relative_file_path="moduleB.jsx", target_name="jsx"),
Address("src/js", relative_file_path="moduleC.ts", target_name="ts"),
Address("src/js", relative_file_path="moduleD.tsx", target_name="tsx"),
}


def test_infers_js_dependencies_via_config(rule_runner: RuleRunner) -> None:
Expand Down Expand Up @@ -193,6 +263,37 @@ def test_infers_js_dependencies_via_config_and_extension_less_imports(
assert set(addresses) == {Address("root/project/src/components", relative_file_path="index.js")}


def test_infers_js_dependencies_with_compiled_typescript_modules(rule_runner: RuleRunner) -> None:
rule_runner.write_files(
{
"src/js/BUILD": dedent(
"""\
javascript_sources()
typescript_sources(name="ts")
"""
),
"src/js/index.js": dedent(
"""\
import { x } from "./moduleA";
"""
),
"src/js/moduleA.ts": "",
"src/js/moduleA.js": "", # Compiled output from tsc
}
)

index_tgt = rule_runner.get_target(Address("src/js", relative_file_path="index.js"))
addresses = rule_runner.request(
InferredDependencies,
[InferJSDependenciesRequest(JSSourceInferenceFieldSet.create(index_tgt))],
).include

assert set(addresses) == {
Address("src/js", relative_file_path="moduleA.js"),
Address("src/js", target_name="ts", relative_file_path="moduleA.ts"),
}


def test_unmatched_js_dependencies_and_error_unowned_behaviour(rule_runner: RuleRunner) -> None:
rule_runner.set_options(["--nodejs-infer-unowned-dependency-behavior=error"])
rule_runner.write_files(
Expand Down

0 comments on commit 02ca67a

Please sign in to comment.