aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzaverden <zaverden@yandex-team.com>2024-07-18 21:17:16 +0300
committerzaverden <zaverden@yandex-team.com>2024-07-18 21:27:44 +0300
commitbe0dfb42b3e15e4d8ac221200d48a55cead36dd8 (patch)
tree180249315a1fd25c3fb439ca9b13301b9eb710c0
parent0a7acacbbb400208d9bfe3610f1f77c38c3c0d7d (diff)
downloadydb-be0dfb42b3e15e4d8ac221200d48a55cead36dd8.tar.gz
feat(npm): build standalone npm module
f944a35c196f6f7b7d93b7d2e9716fcd57f85d9f
-rw-r--r--build/conf/ts/node_modules.conf10
-rw-r--r--build/conf/ts/ts.conf11
-rw-r--r--build/conf/ts/ts_test.conf9
-rw-r--r--build/conf/ts/ts_tsc.conf2
-rw-r--r--build/plugins/lib/nots/package_manager/__init__.py27
-rw-r--r--build/plugins/lib/nots/package_manager/base/package_manager.py24
-rw-r--r--build/plugins/lib/nots/package_manager/npm/__init__.py6
-rw-r--r--build/plugins/lib/nots/package_manager/npm/npm_constants.py6
-rw-r--r--build/plugins/lib/nots/package_manager/npm/npm_lockfile.py133
-rw-r--r--build/plugins/lib/nots/package_manager/npm/npm_package_manager.py134
-rw-r--r--build/plugins/lib/nots/package_manager/npm/npm_utils.py11
-rw-r--r--build/plugins/lib/nots/package_manager/npm/ya.make19
-rw-r--r--build/plugins/lib/nots/package_manager/pnpm/package_manager.py4
-rw-r--r--build/plugins/lib/nots/package_manager/ya.make2
-rw-r--r--build/plugins/nots.py55
15 files changed, 421 insertions, 32 deletions
diff --git a/build/conf/ts/node_modules.conf b/build/conf/ts/node_modules.conf
index dc282e6924..7e67059450 100644
--- a/build/conf/ts/node_modules.conf
+++ b/build/conf/ts/node_modules.conf
@@ -1,5 +1,12 @@
PNPM_ROOT=
PNPM_SCRIPT=$PNPM_ROOT/node_modules/pnpm/dist/pnpm.cjs
+
+NPM_ROOT=
+NPM_SCRIPT=$NPM_ROOT/node_modules/npm/bin/npm-cli.js
+
+PM_SCRIPT=
+PM_TYPE=
+
NPM_CONTRIBS_PATH=contrib/typescript
# combined input/outputs records as list of directives ${input;hide:<path>} ${output;hide:<path>}, used in builders
_NODE_MODULES_INOUTS=
@@ -99,7 +106,8 @@ module _PREPARE_DEPS_BASE: _BARE_UNIT {
DISABLE(START_TARGET)
# we read pnpm-lock.yaml and package.json during configuration
- SET_APPEND(_MAKEFILE_INCLUDE_LIKE_DEPS ${CURDIR}/pnpm-lock.yaml ${CURDIR}/package.json)
+ SET_APPEND(_MAKEFILE_INCLUDE_LIKE_DEPS ${CURDIR}/pnpm-lock.yaml ${CURDIR}/package-lock.json ${CURDIR}/package.json)
+ _SET_PACKAGE_MANAGER()
_PREPARE_DEPS_CONFIGURE()
}
diff --git a/build/conf/ts/ts.conf b/build/conf/ts/ts.conf
index 0556da4cb9..4fb763242b 100644
--- a/build/conf/ts/ts.conf
+++ b/build/conf/ts/ts.conf
@@ -6,7 +6,6 @@ TS_LOCAL_CLI=no
TS_YNDEXING=no
# Use outdir defined in tsconfig (actual not for bundlers, they use own way to define output directory)
TS_CONFIG_USE_OUTDIR=
-
NOTS_TOOL=${tool:"devtools/frontend_build_platform/nots/builder"}
@@ -29,7 +28,8 @@ NOTS_TOOL_BASE_ARGS=\
--moddir $MODDIR \
--local-cli $TS_LOCAL_CLI \
--nodejs-bin $NODEJS_BIN \
- --pnpm-script $PNPM_SCRIPT \
+ --pm-script $PM_SCRIPT \
+ --pm-type $PM_TYPE \
--contribs $NPM_CONTRIBS_PATH \
--trace $TS_TRACE \
--verbose $TS_LOG \
@@ -87,7 +87,8 @@ module _TS_BASE_UNIT: _BARE_UNIT {
SET_APPEND(_MAKEFILE_INCLUDE_LIKE_DEPS ${ARCADIA_ROOT}/$ERM_PACKAGES_PATH)
# PEERDIR that reads required version of tool from package.json
- _PEERDIR_TS_RESOURCE(nodejs pnpm)
+ _PEERDIR_TS_RESOURCE(nodejs)
+ _SET_PACKAGE_MANAGER()
}
# tag:test
@@ -124,8 +125,8 @@ TSYNDEXER_CMD=${cwd:ARCADIA_BUILD_ROOT} \
### _DO_TS_YNDEXING() # internal
###
-### Adds a command-node for TS-modules codenavigation indexing.
-### Output ydx.pb2 file will be processed along with other indexes for other modules and
+### Adds a command-node for TS-modules codenavigation indexing.
+### Output ydx.pb2 file will be processed along with other indexes for other modules and
### other languages in ya-bin.
### (see _gen_merge_node, _gen_upload_node in devtools/ya/build/graph.py) for details.
### The macro is called from nots.py plugin in case we have `TS_YNDEXING=yes` flag set.
diff --git a/build/conf/ts/ts_test.conf b/build/conf/ts/ts_test.conf
index 599121a200..69fef446b5 100644
--- a/build/conf/ts/ts_test.conf
+++ b/build/conf/ts/ts_test.conf
@@ -38,7 +38,7 @@ module TS_TEST_JEST_FOR: _TS_TEST_BASE {
# nots.py will decide if we need to depend on the testing target module output
_DEPENDS_ON_MOD()
- _PEERDIR_TS_RESOURCE(nodejs pnpm jest)
+ _PEERDIR_TS_RESOURCE(nodejs jest)
_TS_TEST_FOR_CONFIGURE(jest jest.config.js workspace_node_modules.tar)
_TS_ADD_NODE_MODULES_FOR_BUILDER()
@@ -70,7 +70,7 @@ module TS_TEST_HERMIONE_FOR: _TS_TEST_BASE {
TS_TEST_DEPENDS_ON_BUILD()
_DEPENDS_ON_MOD()
- _PEERDIR_TS_RESOURCE(nodejs pnpm typescript hermione)
+ _PEERDIR_TS_RESOURCE(nodejs typescript hermione)
_TS_TEST_FOR_CONFIGURE(hermione .hermione.conf.js workspace_node_modules.tar)
}
@@ -103,7 +103,7 @@ module TS_TEST_PLAYWRIGHT_FOR: _TS_TEST_BASE {
_DEPENDS_ON_MOD()
_TS_ADD_NODE_MODULES_FOR_BUILDER()
- _PEERDIR_TS_RESOURCE(nodejs pnpm playwright)
+ _PEERDIR_TS_RESOURCE(nodejs playwright)
_TS_TEST_FOR_CONFIGURE(playwright playwright.config.ts workspace_node_modules.tar)
}
@@ -126,6 +126,9 @@ module _TS_TEST_BASE: _BARE_UNIT {
# parse module args
_TS_TEST_FOR_ARGS($MODULE_ARGS_RAW)
+ # Set PM values from TS_TEST_FOR_DIR
+ _SET_PACKAGE_MANAGER()
+
# we don't want to have TS outputs for tests
DISABLE(TS_CONFIG_DEDUCE_OUT)
}
diff --git a/build/conf/ts/ts_tsc.conf b/build/conf/ts/ts_tsc.conf
index 6cd4a94290..2052a99ee1 100644
--- a/build/conf/ts/ts_tsc.conf
+++ b/build/conf/ts/ts_tsc.conf
@@ -35,7 +35,7 @@ multimodule TS_TSC {
_TS_CONFIGURE()
- SET_APPEND(_MAKEFILE_INCLUDE_LIKE_DEPS ${CURDIR}/package.json ${CURDIR}/pnpm-lock.yaml ${CURDIR}/${TS_CONFIG_PATH})
+ SET_APPEND(_MAKEFILE_INCLUDE_LIKE_DEPS ${CURDIR}/package.json ${CURDIR}/pnpm-lock.yaml ${CURDIR}/package-lock.json ${CURDIR}/${TS_CONFIG_PATH})
_TS_ADD_NODE_MODULES_FOR_BUILDER()
}
diff --git a/build/plugins/lib/nots/package_manager/__init__.py b/build/plugins/lib/nots/package_manager/__init__.py
index 11387ec27a..3e1de532e9 100644
--- a/build/plugins/lib/nots/package_manager/__init__.py
+++ b/build/plugins/lib/nots/package_manager/__init__.py
@@ -1,10 +1,35 @@
-from .base import bundle_node_modules, constants, extract_node_modules, PackageJson, utils, PackageManagerCommandError
+import typing
+
+from .base import (
+ bundle_node_modules,
+ constants,
+ extract_node_modules,
+ PackageJson,
+ utils,
+ PackageManagerCommandError,
+ BasePackageManager,
+ BaseLockfile,
+)
from .base.package_json import PackageJsonWorkspaceError
from .pnpm import PnpmPackageManager
+from .npm import NpmPackageManager
manager = PnpmPackageManager
+
+def get_package_manager_type(key: typing.Literal["pnpm", "npm"]) -> typing.Type[BasePackageManager]:
+ if key == "pnpm":
+ return PnpmPackageManager
+ if key == "npm":
+ return NpmPackageManager
+ raise ValueError(f"Invalid package manager key: {key}")
+
+
__all__ = [
+ "BaseLockfile",
+ "BasePackageManager",
+ "PnpmPackageManager",
+ "NpmPackageManager",
"PackageJson",
"PackageJsonWorkspaceError",
"PackageManagerCommandError",
diff --git a/build/plugins/lib/nots/package_manager/base/package_manager.py b/build/plugins/lib/nots/package_manager/base/package_manager.py
index 1c7cedb662..06a61ffebf 100644
--- a/build/plugins/lib/nots/package_manager/base/package_manager.py
+++ b/build/plugins/lib/nots/package_manager/base/package_manager.py
@@ -76,15 +76,25 @@ class BasePackageManager(object):
pass
@abstractmethod
- def create_node_modules(self):
+ def create_node_modules(self, yatool_prebuilder_path=None, local_cli=False, bundle=True):
pass
@abstractmethod
- def calc_node_modules_inouts(self):
+ def extract_packages_meta_from_lockfiles(self, lf_paths):
pass
@abstractmethod
- def extract_packages_meta_from_lockfiles(self, lf_paths):
+ def calc_prepare_deps_inouts_and_resources(
+ self, store_path: str, has_deps: bool
+ ) -> tuple[list[str], list[str], list[str]]:
+ pass
+
+ @abstractmethod
+ def calc_node_modules_inouts(self, local_cli=False) -> tuple[list[str], list[str]]:
+ pass
+
+ @abstractmethod
+ def build_workspace(self, tarballs_store: str):
pass
def get_local_peers_from_package_json(self):
@@ -104,7 +114,7 @@ class BasePackageManager(object):
return [p[prefix_len:] for p in pj.get_workspace_map(ignore_self=True).keys()]
- def _exec_command(self, args, include_defaults=True, script_path=None):
+ def _exec_command(self, args, include_defaults=True, script_path=None, env=None):
if not self.nodejs_bin_path:
raise PackageManagerError("Unable to execute command: nodejs_bin_path is not configured")
@@ -114,11 +124,7 @@ class BasePackageManager(object):
+ (self._get_default_options() if include_defaults else [])
)
p = subprocess.Popen(
- cmd,
- cwd=self.build_path,
- stdin=None,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
+ cmd, cwd=self.build_path, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env
)
stdout, stderr = p.communicate()
diff --git a/build/plugins/lib/nots/package_manager/npm/__init__.py b/build/plugins/lib/nots/package_manager/npm/__init__.py
new file mode 100644
index 0000000000..96e87ea8ce
--- /dev/null
+++ b/build/plugins/lib/nots/package_manager/npm/__init__.py
@@ -0,0 +1,6 @@
+from .npm_package_manager import NpmPackageManager
+
+
+__all__ = [
+ "NpmPackageManager",
+]
diff --git a/build/plugins/lib/nots/package_manager/npm/npm_constants.py b/build/plugins/lib/nots/package_manager/npm/npm_constants.py
new file mode 100644
index 0000000000..f61145705e
--- /dev/null
+++ b/build/plugins/lib/nots/package_manager/npm/npm_constants.py
@@ -0,0 +1,6 @@
+NPM_LOCKFILE_FILENAME = "package-lock.json"
+
+# This is a name of intermediate file that is used in TS_PREPARE_DEPS.
+# This file has a structure same to package-lock.json, but all tarballs
+# a set relative to the build root.
+NPM_PRE_LOCKFILE_FILENAME = "pre.package-lock.json"
diff --git a/build/plugins/lib/nots/package_manager/npm/npm_lockfile.py b/build/plugins/lib/nots/package_manager/npm/npm_lockfile.py
new file mode 100644
index 0000000000..f0ea05d2b7
--- /dev/null
+++ b/build/plugins/lib/nots/package_manager/npm/npm_lockfile.py
@@ -0,0 +1,133 @@
+import base64
+import json
+import os
+import io
+
+from six.moves.urllib import parse as urlparse
+from six import iteritems
+
+from ..base import BaseLockfile, LockfilePackageMeta, LockfilePackageMetaInvalidError
+
+LOCKFILE_VERSION_FIELD = "lockfileVersion"
+
+
+class NpmLockfile(BaseLockfile):
+ def read(self):
+ with io.open(self.path, "rb") as f:
+ self.data = json.load(f) or {LOCKFILE_VERSION_FIELD: 3}
+
+ lockfile_version = self.data.get(LOCKFILE_VERSION_FIELD, "<no-version>")
+ if lockfile_version != 3:
+ raise Exception(
+ f'Error of project configuration: {self.path} has lockfileVersion: {lockfile_version}. '
+ + f'This version is not supported. Please, delete {os.path.basename(self.path)} and regenerate it using "ya tool nots --clean install --lockfile-only --npm"'
+ )
+
+ def write(self, path=None):
+ """
+ :param path: path to store lockfile, defaults to original path
+ :type path: str
+ """
+ if path is None:
+ path = self.path
+
+ with open(path, "w") as f:
+ json.dump(self.data, f, indent=2)
+
+ def get_packages_meta(self):
+ """
+ Extracts packages meta from lockfile.
+ :rtype: list of LockfilePackageMeta
+ """
+ packages = self.data.get("packages", {})
+
+ for key, meta in packages.items():
+ if not key:
+ continue
+ yield _parse_package_meta(key, meta)
+
+ def update_tarball_resolutions(self, fn):
+ """
+ :param fn: maps `LockfilePackageMeta` instance to new `resolution.tarball` value
+ :type fn: lambda
+ """
+ packages = self.data.get("packages", {})
+
+ for key, meta in iteritems(packages):
+ if not key:
+ continue
+ meta["resolved"] = fn(_parse_package_meta(key, meta, allow_file_protocol=True))
+ packages[key] = meta
+
+ def get_requires_build_packages(self):
+ raise NotImplementedError()
+
+ def validate_has_addons_flags(self):
+ raise NotImplementedError()
+
+
+def _parse_package_meta(key, meta, allow_file_protocol=False):
+ """
+ :param key: uniq package key from lockfile
+ :type key: string
+ :param meta: package meta dict from lockfile
+ :type meta: dict
+ :rtype: LockfilePackageMetaInvalidError
+ """
+ try:
+ tarball_url = _parse_tarball_url(meta["resolved"], allow_file_protocol)
+ sky_id = _parse_sky_id_from_tarball_url(meta["resolved"])
+ integrity_algorithm, integrity = _parse_package_integrity(meta["integrity"])
+ except KeyError as e:
+ raise TypeError("Invalid package meta for key '{}', missing '{}' key".format(key, e))
+ except LockfilePackageMetaInvalidError as e:
+ raise TypeError("Invalid package meta for key '{}', parse error: '{}'".format(key, e))
+
+ return LockfilePackageMeta(key, tarball_url, sky_id, integrity, integrity_algorithm)
+
+
+def _parse_tarball_url(tarball_url, allow_file_protocol):
+ if tarball_url.startswith("file:") and not allow_file_protocol:
+ raise LockfilePackageMetaInvalidError("tarball cannot point to a file, got {}".format(tarball_url))
+ return tarball_url.split("?")[0]
+
+
+def _parse_sky_id_from_tarball_url(tarball_url):
+ """
+ :param tarball_url: tarball url
+ :type tarball_url: string
+ :rtype: string
+ """
+ if tarball_url.startswith("file:"):
+ return ""
+
+ rbtorrent_param = urlparse.parse_qs(urlparse.urlparse(tarball_url).query).get("rbtorrent")
+
+ if rbtorrent_param is None:
+ return ""
+
+ return "rbtorrent:{}".format(rbtorrent_param[0])
+
+
+def _parse_package_integrity(integrity):
+ """
+ Returns tuple of algorithm and hash (hex).
+ :param integrity: package integrity in format "{algo}-{base64_of_hash}"
+ :type integrity: string
+ :rtype: (str, str)
+ """
+ algo, hash_b64 = integrity.split("-", 1)
+
+ if algo not in ("sha1", "sha512"):
+ raise LockfilePackageMetaInvalidError(
+ f"Invalid package integrity algorithm, expected one of ('sha1', 'sha512'), got '{algo}'"
+ )
+
+ try:
+ base64.b64decode(hash_b64)
+ except TypeError as e:
+ raise LockfilePackageMetaInvalidError(
+ "Invalid package integrity encoding, integrity: {}, error: {}".format(integrity, e)
+ )
+
+ return (algo, hash_b64)
diff --git a/build/plugins/lib/nots/package_manager/npm/npm_package_manager.py b/build/plugins/lib/nots/package_manager/npm/npm_package_manager.py
new file mode 100644
index 0000000000..10c477c4e8
--- /dev/null
+++ b/build/plugins/lib/nots/package_manager/npm/npm_package_manager.py
@@ -0,0 +1,134 @@
+import os
+
+from ..base import BasePackageManager, PackageManagerError
+from ..base.constants import NODE_MODULES_WORKSPACE_BUNDLE_FILENAME
+from ..base.node_modules_bundler import bundle_node_modules
+from ..base.utils import b_rooted, build_nm_bundle_path, build_pj_path, s_rooted
+
+from .npm_lockfile import NpmLockfile
+from .npm_utils import build_lockfile_path, build_pre_lockfile_path
+
+
+class NpmPackageManager(BasePackageManager):
+ @classmethod
+ def load_lockfile(cls, path):
+ """
+ :param path: path to lockfile
+ :type path: str
+ :rtype: NpmLockfile
+ """
+ return NpmLockfile.load(path)
+
+ @classmethod
+ def load_lockfile_from_dir(cls, dir_path):
+ """
+ :param dir_path: path to directory with lockfile
+ :type dir_path: str
+ :rtype: NpmLockfile
+ """
+ return cls.load_lockfile(build_lockfile_path(dir_path))
+
+ def extract_packages_meta_from_lockfiles(self, lf_paths):
+ """
+ :type lf_paths: iterable of BaseLockfile
+ :rtype: iterable of LockfilePackageMeta
+ """
+ tarballs = set()
+ errors = []
+
+ for lf_path in lf_paths:
+ try:
+ for pkg in self.load_lockfile(lf_path).get_packages_meta():
+ if pkg.tarball_path not in tarballs:
+ tarballs.add(pkg.tarball_path)
+ yield pkg
+ except Exception as e:
+ errors.append("{}: {}".format(lf_path, e))
+
+ if errors:
+ raise PackageManagerError("Unable to process some lockfiles:\n{}".format("\n".join(errors)))
+
+ def calc_prepare_deps_inouts(self, store_path: str, has_deps: bool) -> tuple[list[str], list[str]]:
+ raise NotImplementedError("NPM does not support contrib/typescript flow.")
+
+ def calc_prepare_deps_inouts_and_resources(
+ self, store_path: str, has_deps: bool
+ ) -> tuple[list[str], list[str], list[str]]:
+ ins = [
+ s_rooted(build_pj_path(self.module_path)),
+ s_rooted(build_lockfile_path(self.module_path)),
+ ]
+ outs = [
+ b_rooted(build_pre_lockfile_path(self.module_path)),
+ ]
+ resources = []
+
+ if has_deps:
+ for dep_path in self.get_local_peers_from_package_json():
+ ins.append(b_rooted(build_pre_lockfile_path(dep_path)))
+
+ for pkg in self.extract_packages_meta_from_lockfiles([build_lockfile_path(self.sources_path)]):
+ resources.append(pkg.to_uri())
+ outs.append(b_rooted(self._tarballs_store_path(pkg, store_path)))
+
+ return ins, outs, resources
+
+ def calc_node_modules_inouts(self, local_cli=False) -> tuple[list[str], list[str]]:
+ """
+ Returns input and output paths for command that creates `node_modules` bundle.
+ It relies on .PEERDIRSELF=TS_PREPARE_DEPS
+ Inputs:
+ - source package.json
+ - merged pre-lockfiles and workspace configs of TS_PREPARE_DEPS
+ Outputs:
+ - created node_modules bundle
+ """
+ ins = [s_rooted(build_pj_path(self.module_path))]
+ outs = []
+
+ pj = self.load_package_json_from_dir(self.sources_path)
+ if pj.has_dependencies():
+ ins.append(b_rooted(build_pre_lockfile_path(self.module_path)))
+ if not local_cli:
+ outs.append(b_rooted(build_nm_bundle_path(self.module_path)))
+ for dep_path in self.get_local_peers_from_package_json():
+ ins.append(b_rooted(build_pj_path(dep_path)))
+
+ return ins, outs
+
+ def build_workspace(self, tarballs_store: str):
+ self._build_pre_lockfile(tarballs_store)
+
+ def _build_pre_lockfile(self, tarballs_store: str):
+ lf = self.load_lockfile_from_dir(self.sources_path)
+ # Change to the output path for correct path calcs on merging.
+ lf.path = build_pre_lockfile_path(self.build_path)
+ lf.update_tarball_resolutions(lambda p: self._tarballs_store_path(p, tarballs_store))
+
+ lf.write()
+
+ def create_node_modules(self, yatool_prebuilder_path=None, local_cli=False, bundle=True):
+ """
+ Creates node_modules directory according to the lockfile.
+ """
+ self._prepare_workspace()
+
+ install_cmd = ["clean-install", "--ignore-scripts", "--audit=false"]
+
+ env = os.environ.copy()
+ env.update({"NPM_CONFIG_CACHE": os.path.join(self.build_path, ".npm-cache")})
+
+ self._exec_command(install_cmd, env=env)
+
+ if not local_cli and bundle:
+ bundle_node_modules(
+ build_root=self.build_root,
+ node_modules_path=self._nm_path(),
+ peers=[],
+ bundle_path=os.path.join(self.build_path, NODE_MODULES_WORKSPACE_BUNDLE_FILENAME),
+ )
+
+ def _prepare_workspace(self):
+ lf = self.load_lockfile(build_pre_lockfile_path(self.build_path))
+ lf.update_tarball_resolutions(lambda p: "file:" + os.path.join(self.build_root, p.tarball_url))
+ lf.write(build_lockfile_path(self.build_path))
diff --git a/build/plugins/lib/nots/package_manager/npm/npm_utils.py b/build/plugins/lib/nots/package_manager/npm/npm_utils.py
new file mode 100644
index 0000000000..b58686b3b2
--- /dev/null
+++ b/build/plugins/lib/nots/package_manager/npm/npm_utils.py
@@ -0,0 +1,11 @@
+import os
+
+from .npm_constants import NPM_LOCKFILE_FILENAME, NPM_PRE_LOCKFILE_FILENAME
+
+
+def build_pre_lockfile_path(p):
+ return os.path.join(p, NPM_PRE_LOCKFILE_FILENAME)
+
+
+def build_lockfile_path(p):
+ return os.path.join(p, NPM_LOCKFILE_FILENAME)
diff --git a/build/plugins/lib/nots/package_manager/npm/ya.make b/build/plugins/lib/nots/package_manager/npm/ya.make
new file mode 100644
index 0000000000..1ab91b8d69
--- /dev/null
+++ b/build/plugins/lib/nots/package_manager/npm/ya.make
@@ -0,0 +1,19 @@
+SUBSCRIBER(g:frontend_build_platform)
+
+PY3_LIBRARY()
+
+STYLE_PYTHON()
+
+PY_SRCS(
+ __init__.py
+ npm_constants.py
+ npm_lockfile.py
+ npm_package_manager.py
+ npm_utils.py
+)
+
+PEERDIR(
+ build/plugins/lib/nots/package_manager/base
+)
+
+END()
diff --git a/build/plugins/lib/nots/package_manager/pnpm/package_manager.py b/build/plugins/lib/nots/package_manager/pnpm/package_manager.py
index 7022c2d9ed..ea82b3637f 100644
--- a/build/plugins/lib/nots/package_manager/pnpm/package_manager.py
+++ b/build/plugins/lib/nots/package_manager/pnpm/package_manager.py
@@ -142,7 +142,7 @@ class PnpmPackageManager(BasePackageManager):
It relies on .PEERDIRSELF=TS_PREPARE_DEPS
Inputs:
- source package.json
- - merged lockfiles and workspace configs of TS_PREPARE_DEPS
+ - merged pre-lockfiles and workspace configs of TS_PREPARE_DEPS
Outputs:
- created node_modules bundle
"""
@@ -187,7 +187,7 @@ class PnpmPackageManager(BasePackageManager):
return PnpmWorkspace.load(build_ws_config_path(self.build_path))
- def build_workspace(self, tarballs_store):
+ def build_workspace(self, tarballs_store: str):
"""
:rtype: PnpmWorkspace
"""
diff --git a/build/plugins/lib/nots/package_manager/ya.make b/build/plugins/lib/nots/package_manager/ya.make
index a77af04676..08a791eb7e 100644
--- a/build/plugins/lib/nots/package_manager/ya.make
+++ b/build/plugins/lib/nots/package_manager/ya.make
@@ -12,6 +12,7 @@ PY_SRCS(
PEERDIR(
build/plugins/lib/nots/package_manager/base
build/plugins/lib/nots/package_manager/pnpm
+ build/plugins/lib/nots/package_manager/npm
)
END()
@@ -19,4 +20,5 @@ END()
RECURSE(
base
pnpm
+ npm
)
diff --git a/build/plugins/nots.py b/build/plugins/nots.py
index 4a2f8474da..b99de011d4 100644
--- a/build/plugins/nots.py
+++ b/build/plugins/nots.py
@@ -4,7 +4,7 @@ import os
import ymake
import ytest
-from _common import resolve_common_const, get_norm_unit_path, rootrel_arc_src, to_yesno
+from _common import resolve_common_const, get_norm_unit_path, rootrel_arc_src, strip_roots, to_yesno
# 1 is 60 files per chunk for TIMEOUT(60) - default timeout for SIZE(SMALL)
@@ -118,16 +118,28 @@ def _build_cmd_input_paths(paths, hide=False, disable_include_processor=False):
return _build_directives("input", [hide_part, disable_ip_part], paths)
+def _get_pm_type(unit) -> str:
+ resolved = unit.get("PM_TYPE")
+ if not resolved:
+ raise Exception("PM_TYPE is not set yet. Macro _SET_PACKAGE_MANAGER() should be called before.")
+
+ return resolved
+
+
+def _get_source_path(unit):
+ sources_path = unit.get("TS_TEST_FOR_DIR") if unit.get("TS_TEST_FOR") else unit.path()
+ return sources_path
+
+
def _create_pm(unit):
- from lib.nots.package_manager import manager
+ from lib.nots.package_manager import get_package_manager_type
+
+ sources_path = _get_source_path(unit)
+ module_path = unit.get("TS_TEST_FOR_PATH") if unit.get("TS_TEST_FOR") else unit.get("MODDIR")
- sources_path = unit.path()
- module_path = unit.get("MODDIR")
- if unit.get("TS_TEST_FOR"):
- sources_path = unit.get("TS_TEST_FOR_DIR")
- module_path = unit.get("TS_TEST_FOR_PATH")
+ PackageManager = get_package_manager_type(_get_pm_type(unit))
- return manager(
+ return PackageManager(
sources_path=unit.resolve(sources_path),
build_root="$B",
build_path=unit.path().replace("$S", "$B", 1),
@@ -148,6 +160,25 @@ def _create_erm_json(unit):
@_with_report_configure_error
+def on_set_package_manager(unit):
+ pm_type = "pnpm" # projects without any lockfile are processed by pnpm
+
+ source_path = _get_source_path(unit)
+
+ for pm_key, lockfile_name in [("pnpm", "pnpm-lock.yaml"), ("npm", "package-lock.json")]:
+ lf_path = os.path.join(source_path, lockfile_name)
+ lf_path_resolved = unit.resolve_arc_path(strip_roots(lf_path))
+
+ if lf_path_resolved:
+ pm_type = pm_key
+ break
+
+ unit.on_peerdir_ts_resource(pm_type)
+ unit.set(["PM_TYPE", pm_type])
+ unit.set(["PM_SCRIPT", f"${pm_type.upper()}_SCRIPT"])
+
+
+@_with_report_configure_error
def on_set_append_with_directive(unit, var_name, dir, *values):
wrapped = ['${{{dir}:"{v}"}}'.format(dir=dir, v=v) for v in values]
__set_append(unit, var_name, " ".join(wrapped))
@@ -157,6 +188,9 @@ def on_set_append_with_directive(unit, var_name, dir, *values):
def on_from_npm_lockfiles(unit, *args):
from lib.nots.package_manager.base import PackageManagerError
+ # This is contrib with pnpm-lock.yaml files only
+ # Force set to pnpm
+ unit.set(["PM_TYPE", "pnpm"])
pm = _create_pm(unit)
lf_paths = []
@@ -192,8 +226,9 @@ def _check_nodejs_version(unit, major):
@_with_report_configure_error
def on_peerdir_ts_resource(unit, *resources):
- pm = _create_pm(unit)
- pj = pm.load_package_json_from_dir(pm.sources_path)
+ from lib.nots.package_manager import BasePackageManager
+
+ pj = BasePackageManager.load_package_json_from_dir(unit.resolve(_get_source_path(unit)))
erm_json = _create_erm_json(unit)
dirs = []