• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 18439433638

12 Oct 2025 05:01AM UTC coverage: 80.25%. First build
18439433638

Pull #22461

github

web-flow
Merge dade19c3d into 4846d0735
Pull Request #22461: Add typescript typechecking support

411 of 535 new or added lines in 10 files covered. (76.82%)

77635 of 96741 relevant lines covered (80.25%)

3.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.95
/src/python/pants/backend/javascript/nodejs_project_environment.py
1
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
from __future__ import annotations
12✔
4

5
import os.path
12✔
6
from collections.abc import Iterable
12✔
7
from dataclasses import dataclass, field
12✔
8

9
from pants.backend.javascript import package_json, resolve
12✔
10
from pants.backend.javascript.nodejs_project import NodeJSProject
12✔
11
from pants.backend.javascript.package_json import (
12✔
12
    NodePackageExtraEnvVarsField,
13
    NodePackageNameField,
14
    OwningNodePackage,
15
    OwningNodePackageRequest,
16
    find_owning_package,
17
)
18
from pants.backend.javascript.resolve import (
12✔
19
    ChosenNodeResolve,
20
    RequestNodeResolve,
21
    resolve_for_package,
22
)
23
from pants.backend.javascript.subsystems import nodejs
12✔
24
from pants.backend.javascript.subsystems.nodejs import NodeJSToolProcess, setup_node_tool_process
12✔
25
from pants.build_graph.address import Address
12✔
26
from pants.core.util_rules.env_vars import environment_vars_subset
12✔
27
from pants.engine.env_vars import EnvironmentVarsRequest
12✔
28
from pants.engine.internals.native_engine import EMPTY_DIGEST, Digest, MergeDigests
12✔
29
from pants.engine.internals.selectors import concurrently
12✔
30
from pants.engine.intrinsics import merge_digests, path_globs_to_digest
12✔
31
from pants.engine.process import Process
12✔
32
from pants.engine.rules import Rule, collect_rules, implicitly, rule
12✔
33
from pants.engine.target import Target
12✔
34
from pants.engine.unions import UnionRule
12✔
35
from pants.util.dirutil import fast_relpath
12✔
36
from pants.util.frozendict import FrozenDict
12✔
37
from pants.util.logging import LogLevel
12✔
38

39

40
@dataclass(frozen=True)
12✔
41
class NodeJSProjectEnvironmentRequest:
12✔
42
    address: Address
12✔
43

44

45
@dataclass(frozen=True)
12✔
46
class NodeJsProjectEnvironment:
12✔
47
    resolve: ChosenNodeResolve
12✔
48
    package: OwningNodePackage | None = None
12✔
49

50
    @classmethod
12✔
51
    def from_root(cls, project: NodeJSProject) -> NodeJsProjectEnvironment:
12✔
52
        return cls(resolve=ChosenNodeResolve(project), package=None)
×
53

54
    @property
12✔
55
    def project(self) -> NodeJSProject:
12✔
56
        return self.resolve.project
×
57

58
    @property
12✔
59
    def root_dir(self) -> str:
12✔
60
        return self.project.root_dir
×
61

62
    @property
12✔
63
    def node_modules_directories(self) -> Iterable[str]:
12✔
64
        yield "node_modules"
×
65
        if self.package and not self.project.single_workspace:
×
66
            yield os.path.join(self.relative_workspace_directory(), "node_modules")
×
67

68
    @property
12✔
69
    def target(self) -> Target | None:
12✔
70
        if self.package:
×
71
            return self.package.target
×
72
        else:
73
            return None
×
74

75
    def package_dir(self) -> str:
12✔
76
        if self.package and not self.project.single_workspace:
×
77
            return self.ensure_target().residence_dir
×
78
        else:
79
            return self.root_dir
×
80

81
    def relative_workspace_directory(self) -> str:
12✔
82
        target = self.ensure_target()
×
83
        from_root_to_workspace = fast_relpath(target.residence_dir, self.root_dir)
×
84
        return from_root_to_workspace
×
85

86
    def ensure_target(self) -> Target:
12✔
87
        if self.target:
×
88
            return self.target
×
89
        raise ValueError("")
×
90

91

92
@dataclass(frozen=True)
12✔
93
class NodeJsProjectEnvironmentProcess:
12✔
94
    env: NodeJsProjectEnvironment
12✔
95
    args: Iterable[str]
12✔
96
    description: str
12✔
97
    level: LogLevel = LogLevel.INFO
12✔
98
    input_digest: Digest = EMPTY_DIGEST
12✔
99
    output_files: tuple[str, ...] = ()
12✔
100
    output_directories: tuple[str, ...] = ()
12✔
101
    per_package_caches: FrozenDict[str, str] = field(default_factory=FrozenDict)
12✔
102
    project_caches: FrozenDict[str, str] = field(default_factory=FrozenDict)
12✔
103
    timeout_seconds: int | None = None
12✔
104
    extra_env: FrozenDict[str, str] = field(default_factory=FrozenDict)
12✔
105

106
    def targeted_args(self) -> tuple[str, ...]:
12✔
107
        if (
×
108
            not self.env.project.single_workspace
109
            and self.env.target
110
            and self.env.root_dir != self.env.package_dir()
111
        ):
112
            target = self.env.ensure_target()
×
113
            return (
×
114
                self.env.project.workspace_specifier_arg,
115
                target[NodePackageNameField].value,
116
                *self.args,
117
            )
118
        else:
119
            return tuple(self.args)
×
120

121

122
@rule(desc="Assembling nodejs project environment")
12✔
123
async def get_nodejs_environment(req: NodeJSProjectEnvironmentRequest) -> NodeJsProjectEnvironment:
12✔
124
    node_resolve, owning_tgt = await concurrently(
×
125
        resolve_for_package(RequestNodeResolve(req.address), **implicitly()),
126
        find_owning_package(OwningNodePackageRequest(req.address)),
127
    )
128
    assert owning_tgt.target, f"Already ensured to exist by {ChosenNodeResolve.__name__}."
×
129

130
    return NodeJsProjectEnvironment(node_resolve, owning_tgt)
×
131

132

133
@rule
12✔
134
async def setup_nodejs_project_environment_process(
12✔
135
    req: NodeJsProjectEnvironmentProcess,
136
    nodejs: nodejs.NodeJS,
137
) -> Process:
138
    target_env_vars = (
×
139
        req.env.target.get(NodePackageExtraEnvVarsField).value or () if req.env.target else ()
140
    )
141

142
    lockfile_digest, project_digest, subsystem_env_vars, env_vars = await concurrently(
×
143
        path_globs_to_digest(req.env.resolve.get_lockfile_glob()),
144
        merge_digests(req.env.project.get_project_digest()),
145
        environment_vars_subset(EnvironmentVarsRequest(nodejs.extra_env_vars), **implicitly()),
146
        environment_vars_subset(EnvironmentVarsRequest(target_env_vars), **implicitly()),
147
    )
148
    merged = await merge_digests(MergeDigests((req.input_digest, lockfile_digest, project_digest)))
×
149

150
    args = req.targeted_args()
×
151
    output_files = req.output_files
×
152
    output_directories = req.output_directories
×
153
    per_package_caches = FrozenDict(
×
154
        {
155
            key: os.path.join(req.env.package_dir(), value)
156
            for key, value in req.per_package_caches.items()
157
        }
158
    )
NEW
159
    append_only_caches: FrozenDict[str, str] = FrozenDict(
×
160
        **per_package_caches, **req.project_caches, **req.env.project.extra_caches()
161
    )
162

163
    return await setup_node_tool_process(
×
164
        **implicitly(
165
            NodeJSToolProcess(
166
                tool=req.env.project.package_manager.name,
167
                tool_version=req.env.project.package_manager.version,
168
                args=args,
169
                description=req.description,
170
                level=req.level,
171
                input_digest=merged,
172
                working_directory=req.env.root_dir,
173
                output_files=output_files,
174
                output_directories=output_directories,
175
                append_only_caches=append_only_caches,
176
                timeout_seconds=req.timeout_seconds,
177
                project_digest=project_digest,
178
                extra_env=FrozenDict(
179
                    {
180
                        **subsystem_env_vars,
181
                        **env_vars,
182
                        **req.extra_env,
183
                        **req.env.project.extra_env(),
184
                    }
185
                ),
186
            )
187
        )
188
    )
189

190

191
def rules() -> Iterable[Rule | UnionRule]:
12✔
192
    return [*collect_rules(), *nodejs.rules(), *resolve.rules(), *package_json.rules()]
12✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc