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

pantsbuild / pants / 22740642519

05 Mar 2026 11:00PM UTC coverage: 52.677% (-40.3%) from 92.931%
22740642519

Pull #23157

github

web-flow
Merge 2aa18e6d4 into f0030f5e7
Pull Request #23157: [pants ng] Partition source files by config.

31678 of 60136 relevant lines covered (52.68%)

0.53 hits per line

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

87.3
/src/python/pants/backend/javascript/install_node_package.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
1✔
4

5
import os.path
1✔
6
from collections.abc import Iterable
1✔
7
from dataclasses import dataclass
1✔
8

9
from pants.backend.javascript import nodejs_project_environment
1✔
10
from pants.backend.javascript.dependency_inference.rules import rules as dependency_inference_rules
1✔
11
from pants.backend.javascript.nodejs_project_environment import (
1✔
12
    NodeJsProjectEnvironment,
13
    NodeJsProjectEnvironmentProcess,
14
    NodeJSProjectEnvironmentRequest,
15
    get_nodejs_environment,
16
)
17
from pants.backend.javascript.package_json import (
1✔
18
    NodePackageNameField,
19
    NodePackageVersionField,
20
    PackageJsonSourceField,
21
)
22
from pants.backend.javascript.package_manager import PackageManager
1✔
23
from pants.backend.javascript.subsystems import nodejs
1✔
24
from pants.backend.javascript.target_types import JSRuntimeSourceField
1✔
25
from pants.build_graph.address import Address
1✔
26
from pants.core.target_types import FileSourceField, ResourceSourceField
1✔
27
from pants.core.util_rules.source_files import (
1✔
28
    SourceFiles,
29
    SourceFilesRequest,
30
    determine_source_files,
31
)
32
from pants.engine.internals.graph import transitive_targets
1✔
33
from pants.engine.internals.native_engine import AddPrefix, Digest, MergeDigests
1✔
34
from pants.engine.intrinsics import add_prefix, merge_digests
1✔
35
from pants.engine.process import fallible_to_exec_result_or_raise
1✔
36
from pants.engine.rules import Rule, collect_rules, implicitly, rule
1✔
37
from pants.engine.target import SourcesField, Target, TransitiveTargetsRequest
1✔
38
from pants.engine.unions import UnionMembership, UnionRule
1✔
39

40

41
@dataclass(frozen=True)
1✔
42
class InstalledNodePackageRequest:
1✔
43
    address: Address
1✔
44

45

46
@dataclass(frozen=True)
1✔
47
class InstalledNodePackage:
1✔
48
    project_env: NodeJsProjectEnvironment
1✔
49
    digest: Digest
1✔
50

51
    @property
1✔
52
    def project_dir(self) -> str:
1✔
53
        return self.project_env.root_dir
1✔
54

55
    def join_relative_workspace_directory(self, path: str) -> str:
1✔
56
        return os.path.join(self.project_env.relative_workspace_directory(), path)
×
57

58
    @property
1✔
59
    def target(self) -> Target:
1✔
60
        return self.project_env.ensure_target()
×
61

62
    @property
1✔
63
    def package_manager(self) -> PackageManager:
1✔
64
        return self.project_env.project.package_manager
×
65

66

67
@dataclass(frozen=True)
1✔
68
class InstalledNodePackageWithSource(InstalledNodePackage):
1✔
69
    pass
1✔
70

71

72
async def _get_relevant_source_files(
1✔
73
    sources: Iterable[SourcesField], with_js: bool = False
74
) -> SourceFiles:
75
    return await determine_source_files(
1✔
76
        SourceFilesRequest(
77
            sources,
78
            for_sources_types=(PackageJsonSourceField, FileSourceField)
79
            + ((ResourceSourceField, JSRuntimeSourceField) if with_js else ()),
80
            enable_codegen=True,
81
        )
82
    )
83

84

85
@rule
1✔
86
async def install_node_packages_for_address(
1✔
87
    req: InstalledNodePackageRequest,
88
    union_membership: UnionMembership,
89
    nodejs: nodejs.NodeJS,
90
) -> InstalledNodePackage:
91
    project_env = await get_nodejs_environment(NodeJSProjectEnvironmentRequest(req.address))
1✔
92
    target = project_env.ensure_target()
1✔
93
    transitive_tgts = await transitive_targets(
1✔
94
        TransitiveTargetsRequest([target.address]), **implicitly()
95
    )
96

97
    source_files = await _get_relevant_source_files(
1✔
98
        (tgt[SourcesField] for tgt in transitive_tgts.closure if tgt.has_field(SourcesField)),
99
        with_js=False,
100
    )
101
    package_digest = source_files.snapshot.digest
1✔
102

103
    install_result = await fallible_to_exec_result_or_raise(
1✔
104
        **implicitly(
105
            NodeJsProjectEnvironmentProcess(
106
                project_env,
107
                project_env.project.immutable_install_args,
108
                description=f"Installing {target[NodePackageNameField].value}@{target[NodePackageVersionField].value}.",
109
                input_digest=package_digest,
110
                output_directories=tuple(project_env.node_modules_directories),
111
            )
112
        )
113
    )
114
    node_modules = await add_prefix(AddPrefix(install_result.output_digest, project_env.root_dir))
1✔
115

116
    return InstalledNodePackage(
1✔
117
        project_env,
118
        digest=await merge_digests(
119
            MergeDigests(
120
                [
121
                    package_digest,
122
                    node_modules,
123
                ]
124
            )
125
        ),
126
    )
127

128

129
@rule
1✔
130
async def add_sources_to_installed_node_package(
1✔
131
    req: InstalledNodePackageRequest,
132
) -> InstalledNodePackageWithSource:
133
    installation = await install_node_packages_for_address(req, **implicitly())
×
134
    transitive_tgts = await transitive_targets(
×
135
        TransitiveTargetsRequest([installation.target.address]), **implicitly()
136
    )
137

138
    source_files = await _get_relevant_source_files(
×
139
        (tgt[SourcesField] for tgt in transitive_tgts.dependencies if tgt.has_field(SourcesField)),
140
        with_js=True,
141
    )
142
    digest = await merge_digests(MergeDigests((installation.digest, source_files.snapshot.digest)))
×
143
    return InstalledNodePackageWithSource(installation.project_env, digest=digest)
×
144

145

146
def rules() -> Iterable[Rule | UnionRule]:
1✔
147
    return [
1✔
148
        *nodejs.rules(),
149
        *nodejs_project_environment.rules(),
150
        *dependency_inference_rules(),
151
        *collect_rules(),
152
    ]
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