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

pantsbuild / pants / 25443604553

06 May 2026 03:05PM UTC coverage: 92.879% (-0.04%) from 92.915%
25443604553

push

github

web-flow
[pants_ng] Scaffolding for a pants_ng mode. (#23319)

In this mode the command line is parsed as an
NG invocation, and dispatched appropriately.

Of course at the moment there are no
implementations to dispatch to. That will follow.

This does expose a new option, `pants_ng` to users. 
There is a big warning not to set it, but we're not trying
to hide that we're working on a new thing, so I am
comfortable with this.

25 of 76 new or added lines in 9 files covered. (32.89%)

1294 existing lines in 76 files now uncovered.

92234 of 99306 relevant lines covered (92.88%)

4.05 hits per line

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

98.97
/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
11✔
4

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

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

39

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

44

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

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

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

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

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

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

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

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

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

91

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

106
    def targeted_args(self) -> tuple[str, ...]:
11✔
107
        if (
8✔
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()
2✔
113
            return (
2✔
114
                self.env.project.workspace_specifier_arg,
115
                target[NodePackageNameField].value,
116
                *self.args,
117
            )
118
        else:
119
            return tuple(self.args)
8✔
120

121

122
@rule(desc="Assembling nodejs project environment")
11✔
123
async def get_nodejs_environment(req: NodeJSProjectEnvironmentRequest) -> NodeJsProjectEnvironment:
11✔
124
    node_resolve, owning_tgt = await concurrently(
8✔
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__}."
8✔
129

130
    return NodeJsProjectEnvironment(node_resolve, owning_tgt)
8✔
131

132

133
@rule
11✔
134
async def setup_nodejs_project_environment_process(
11✔
135
    req: NodeJsProjectEnvironmentProcess,
136
    nodejs: nodejs.NodeJS,
137
) -> Process:
138
    target_env_vars = (
8✔
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(
8✔
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)))
8✔
149

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

163
    return await setup_node_tool_process(
8✔
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]:
11✔
192
    return [*collect_rules(), *nodejs.rules(), *resolve.rules(), *package_json.rules()]
11✔
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

© 2026 Coveralls, Inc