aboutsummaryrefslogtreecommitdiffstats
path: root/build/plugins/lib/nots/erm_json_lite.py
blob: e2927f8a9849fe25fd21db01f77c5df4f78f6c8d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import json
import re
from functools import cmp_to_key

from .semver import Version, VersionRange


class ErmJsonLite(object):
    """
    Basic implementation to read `erm-packages.json`.

    It doesn't use any models, works with only raw JSON types: lists, dicts, strings
    """

    class ResourceType(object):
        NPM_PACKAGE = "NPM_PACKAGE"
        NODE_JS = "NODE_JS"

    data = None

    @staticmethod
    def get_versions_of(er_resource):
        # type: (dict) -> list[Version]
        """
        Return all versions of the resource in ASC order (from older to latest)
        """
        unsorted = er_resource.get("versions").keys()
        # We have to sort because in python 2 the order of keys in a dict is not guaranteed
        versions = sorted(unsorted, key=cmp_to_key(Version.cmp))

        return [Version.from_str(v) for v in versions]

    @classmethod
    def load(cls, path):
        # type: (str) -> ErmJsonLite
        erm_json = cls()

        with open(path, encoding='utf-8') as f:
            erm_json.data = dict()
            for k, v in json.load(f).items():
                # Ignore comments (when key starts with `_`), used for banner
                if not k.startswith("_"):
                    erm_json.data[k] = v

        return erm_json

    @staticmethod
    def canonize_name(resource_name):
        # type: (str) -> str
        """
        Canonize resource name

        For example:
            hermione -> hermione
            super-package -> super_package
            @yatool/nots -> yatool_nots
        """
        return re.sub(r"\W+", "_", resource_name).strip("_")

    def get_resource(self, resource_name):
        # type: (str) -> dict
        """
        Return resource by his name
        """
        er_resource = self.data.get(resource_name)
        if not er_resource:
            raise Exception("Requested resource {} is not a toolchain item".format(resource_name))

        return er_resource

    def get_sb_resources(self, resource_name, version):
        # type: (str, Version) -> list[dict]
        """
        Return a list of SB resources for ER version
        """
        er_resource = self.get_resource(resource_name)

        return er_resource.get("versions").get(str(version)).get("resources")

    def is_resource_multiplatform(self, resource_name):
        # type: (str) -> bool
        """
        Return True if resource is multiplatform, False otherwise
        """
        er_resource = self.get_resource(resource_name)

        return er_resource.get("multiplatform", False)

    def list_npm_packages(self):
        # type: () -> list[str]
        """
        Returns a list of the names of the npm tools used in the toolchain
        """
        result = []
        for resource_name, resource in self.data.items():
            if resource.get("type") == self.ResourceType.NPM_PACKAGE:
                result.append(resource_name)

        return result

    def select_version_of(self, resource_name, range_str=None):
        # type: (str, str|None) -> Version|None
        er_resource = self.get_resource(resource_name)

        if range_str is None:
            return Version.from_str(er_resource.get("default"))

        version_range = VersionRange.from_str(range_str)

        # assuming the version list is sorted from the lowest to the highest version,
        # we stop the loop as early as possible and hence return the lowest compatible version
        for version in self.get_versions_of(er_resource):
            if version_range.is_satisfied_by(version):
                return version

        return None

    def use_resource_directly(self, resource_name):
        # type: (str) -> bool
        er_resource = self.get_resource(resource_name)

        return er_resource.get("useDirectly", False)