# Copyright Kevin Deldycke <kevin@deldycke.com> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from __future__ import annotations
import json
import re
from functools import cached_property
from typing import Iterator
from extra_platforms import ALL_PLATFORMS
from meta_package_manager.base import Package, PackageManager
from meta_package_manager.capabilities import (
search_capabilities,
version_not_implemented,
)
[docs]
class Yarn(PackageManager):
name = "Node's yarn"
homepage_url = "https://yarnpkg.com"
platforms = ALL_PLATFORMS
requirement = "1.20.0"
"""
.. code-block:: shell-session
βΊ yarn --version
1.22.11
"""
@property
def installed(self) -> Iterator[Package]:
"""Fetch installed packages.
.. code-block:: shell-session
βΊ yarn global --json list --depth 0
{"type":"activityStart","data":{"id":0}}
{"type":"activityTick","data":{"id":0,"name":"awesome-lint@^0.18.0"}}
{"type":"activityTick","data":{"id":0,"name":"arrify@^2.0.1"}}
{"type":"activityTick","data":{"id":0,"name":"case@^1.6.3"}}
{"type":"activityTick","data":{"id":0,"name":"emoji-regex@^9.2.0"}}
(...)
{"type":"activityEnd","data":{"id":0}}
{"type":"progressStart","data":{"id":0,"total":327}}
{"type":"progressTick","data":{"id":0,"current":1}}
{"type":"progressTick","data":{"id":0,"current":2}}
{"type":"progressTick","data":{"id":0,"current":3}}
{"type":"progressTick","data":{"id":0,"current":4}}
{"type":"progressTick","data":{"id":0,"current":5}}
(...)
{"type":"progressFinish","data":{"id":0}}
{"type":"info","data":"\"awesome-lint@0.18.0\" has binaries:"}
{"type":"list","data":{"type":"bins-awesome-lint","items":["awesome-lint"]}}
.. code-block:: shell-session
βΊ yarn global list --depth 0
yarn global v1.22.19
info "awesome-lint@0.18.0" has binaries:
- awesome-lint
β¨ Done in 0.13s.
"""
output = self.run_cli("global", "--json", "list", "--depth", "0")
regexp = re.compile(
r"^.+\"data\":\"\\\"(?P<package_id>\S+)"
r"@(?P<version>\S+)\\\" has binaries:\"\}$",
re.MULTILINE,
)
for package_id, version in regexp.findall(output):
yield self.package(id=package_id, installed_version=version)
@cached_property
def global_dir(self) -> str:
"""Locate the global directory.
.. code-block:: shell-session
βΊ yarn global dir
~/.config/yarn/global
"""
return self.run_cli("global", "dir", force_exec=True).rstrip()
@property
def outdated(self) -> Iterator[Package]:
"""Fetch outdated packages.
.. code-block:: shell-session
βΊ yarn --json outdated --cwd ~/.config/yarn/global | jq
{"type":"warning","data":"package.json: No license field"}
{
"type": "info",
"data":
"Color legend : \n"
" \"<red>\" : Major Update backward-incompatible updates \n"
" \"<yellow>\" : Minor Update backward-compatible features \n"
" \"<green>\" : Patch Update backward-compatible bug fixes"
}
{
"type": "table",
"data": {
"head": [
"Package",
"Current",
"Wanted",
"Latest",
"Package Type",
"URL"
],
"body": [
[
"markdown",
"0.4.0",
"0.4.0",
"0.5.0",
"dependencies",
"git://github.com/evilstreak/markdown-js.git"
]
]
}
}
.. code-block:: shell-session
βΊ yarn outdated --cwd ~/.config/yarn/global
yarn outdated v1.22.19
warning package.json: No license field
info Color legend :
"<red>" : Major Update backward-incompatible updates
"<yellow>" : Minor Update backward-compatible features
"<green>" : Patch Update backward-compatible bug fixes
Package Current Wanted Latest Package Type URL
markdown 0.4.0 0.4.0 0.5.0 dependencies git://github.com/.../md-js.git
β¨ Done in 0.95s.
"""
output = self.run_cli("--json", "outdated", "--cwd", self.global_dir)
if output:
for line in output.splitlines():
if not line:
continue
obj = json.loads(line)
if obj["type"] == "table":
for package in obj["data"]["body"]:
if package[2] == "linked":
continue
yield self.package(
id=package[0],
installed_version=package[1],
latest_version=package[3],
)
[docs]
@search_capabilities(extended_support=False, exact_support=False)
def search(self, query: str, extended: bool, exact: bool) -> Iterator[Package]:
"""Fetch matching packages.
.. warning::
Yarn maintainers have `decided to not implement a dedicated search command
<https://github.com/yarnpkg/yarn/issues/778#issuecomment-253146299>`_.
Search is simulated by a direct call to ``yarn info``, and as a result only
works for exact match.
.. code-block:: shell-session
βΊ yarn --json info python | jq
{
"type": "inspect",
"data": {
"name": "python",
"description": "Interact with python child process",
"dist-tags": {
"latest": "0.0.4"
},
"versions": [
"0.0.0",
"0.0.1",
"0.0.2",
"0.0.3",
"0.0.4"
],
"maintainers": [
{
"name": "drderidder",
"email": "drderidder@gmail.com"
}
],
"time": {
"modified": "2017-09-16T05:26:13.151Z",
"created": "2011-07-11T01:59:04.362Z",
"0.0.0": "2011-07-11T01:59:05.137Z",
"0.0.1": "2011-07-17T05:23:33.166Z",
"0.0.2": "2011-07-20T03:42:50.379Z",
"0.0.3": "2014-06-08T00:39:08.562Z",
"0.0.4": "2015-01-25T02:48:07.820Z"
},
"author": {
"name": "Darren DeRidder"
},
"repository": {
"type": "git",
"url": "git://github.com/73rhodes/node-python.git"
},
"homepage": "https://github.com/73rhodes/node-python",
"bugs": {
"url": "https://github.com/73rhodes/node-python/issues"
},
"readmeFilename": "README.md",
"users": {
"dewang-mistry": true,
"goliatone": true,
"sapanbhuta": true,
"aditcmarix": true,
"imlucas": true,
"heyderpd": true,
"ukuli": true,
"chbardel": true,
"asaupup": true,
"nuwaio": true
},
"version": "0.0.4",
"main": "./lib/python.js",
"engines": {
"node": ">= 0.4.1"
},
"gitHead": "69754aaa57658193916a1bf5fc391198098f74f6",
"scripts": {},
"dist": {
"shasum": "3094e898ef17a33aa9c3e973b3848a38e47d1818",
"tarball": "https://registry.npmjs.org/python/-/python-1.tgz"
},
"directories": {}
}
}
"""
output = self.run_cli("--json", "info", query)
if output:
result = json.loads(output)
if result["type"] == "inspect":
package = result["data"]
yield self.package(
id=package["name"],
description=package["description"],
latest_version=package["version"],
)
@version_not_implemented
def install(self, package_id: str, version: str | None = None) -> str:
"""Install one package.
.. code-block:: shell-session
βΊ yarn global add awesome-lint
yarn global v1.22.19
[1/4] π Resolving packages...
[2/4] π Fetching packages...
[3/4] π Linking dependencies...
[4/4] π¨ Building fresh packages...
success Installed "awesome-lint@0.18.0" with binaries:
- awesome-lint
β¨ Done in 16.15s.
"""
return self.run_cli("global", "add", package_id)
[docs]
def upgrade_all_cli(self) -> tuple[str, ...]:
"""Generates the CLI to upgrade all packages (default) or only the one provided
as parameter.
.. code-block:: shell-session
βΊ yarn global upgrade --latest
yarn global v1.22.19
[1/4] π Resolving packages...
[2/4] π Fetching packages...
[3/4] π Linking dependencies...
[4/4] π¨ Rebuilding all packages...
success Saved lockfile.
success Saved 271 new dependencies.
info Direct dependencies
ββ awesome-lint@0.18.0
ββ markdown@0.5.0
info All dependencies
ββ @babel/code-frame@7.18.6
ββ @babel/helper-validator-identifier@7.18.6
ββ @nodelib/fs.scandir@2.1.5
ββ array-to-sentence@1.1.0
ββ array-union@2.1.0
ββ awesome-lint@0.18.0
ββ fs.realpath@1.0.0
(...)
ββ zwitch@1.0.5
β¨ Done in 19.89s.
"""
return self.build_cli("global", "upgrade", "--latest")
@version_not_implemented
def upgrade_one_cli(
self,
package_id: str,
version: str | None = None,
) -> tuple[str, ...]:
"""Generates the CLI to upgrade all packages (default) or only the one provided
as parameter.
.. code-block:: shell-session
βΊ yarn global upgrade markdown --latest
yarn global v1.22.19
[1/4] π Resolving packages...
[2/4] π Fetching packages...
[3/4] π Linking dependencies...
[4/4] π¨ Rebuilding all packages...
success Saved lockfile.
success Saved 2 new dependencies.
info Direct dependencies
ββ markdown@0.5.0
info All dependencies
ββ markdown@0.5.0
ββ nopt@2.1.2
β¨ Done in 1.77s.
"""
return self.build_cli("global", "upgrade", package_id, "--latest")
[docs]
def remove(self, package_id: str) -> str:
"""Remove one package.
.. code-block:: shell-session
βΊ yarn global remove awesome-lint
yarn global v1.22.19
[1/2] π Removing module awesome-lint...
[2/2] π¨ Regenerating lockfile and installing missing dependencies...
success Uninstalled packages.
β¨ Done in 0.21s.
"""
return self.run_cli("global", "remove", package_id)
[docs]
def cleanup(self) -> None:
"""Removes things we don't need anymore.
See: https://yarnpkg.com/cli/cache/clean
.. code-block:: shell-session
βΊ yarn cache clean --all
yarn cache v1.22.19
success Cleared cache.
β¨ Done in 0.35s.
"""
self.run_cli("cache", "clean", "--all")