1# coding: utf-8
2# Copyright (c) Jupyter Development Team.
3# Distributed under the terms of the Modified BSD License.
4
5import json
6import os.path as osp
7from itertools import filterfalse
8
9from .jlpmapp import HERE
10
11
12def pjoin(*args):
13    """Join paths to create a real path.
14    """
15    return osp.abspath(osp.join(*args))
16
17
18def _get_default_core_data():
19    """Get the data for the app template.
20    """
21    with open(pjoin(HERE, 'staging', 'package.json')) as fid:
22        return json.load(fid)
23
24
25def _is_lab_package(name):
26    """Whether a package name is in the lab namespace"""
27    return name.startswith('@jupyterlab/')
28
29
30def _only_nonlab(collection):
31    """Filter a dict/sequence to remove all lab packages
32
33    This is useful to take the default values of e.g. singletons and filter
34    away the '@jupyterlab/' namespace packages, but leave any others (e.g.
35    lumino and react).
36    """
37    if isinstance(collection, dict):
38        return dict(
39            (k, v) for (k, v) in collection.items()
40            if not _is_lab_package(k)
41        )
42    elif isinstance(collection, (list, tuple)):
43        return list(filterfalse(_is_lab_package, collection))
44    raise TypeError('collection arg should be either dict or list/tuple')
45
46
47class CoreConfig:
48    """An object representing a core config.
49
50    This enables custom lab application to override some parts of the core
51    configuration of the build system.
52    """
53    def __init__(self):
54        self._data = _get_default_core_data()
55
56    def add(self, name, semver, extension=False, mime_extension=False):
57        """Remove an extension/singleton.
58
59        If neither extension or mimeExtension is True (the default)
60        the package is added as a singleton dependency.
61
62        name: string
63            The npm package name
64        semver: string
65            The semver range for the package
66        extension: bool
67            Whether the package is an extension
68        mime_extension: bool
69            Whether the package is a MIME extension
70        """
71        data = self._data
72        if not name:
73            raise ValueError('Missing package name')
74        if not semver:
75            raise ValueError('Missing package semver')
76        if name in data['resolutions']:
77            raise ValueError('Package already present: %r' % (name,))
78        data['resolutions'][name] = semver
79
80        # If both mimeExtension and extensions are True, treat
81        # as mime extension
82        if mime_extension:
83            data['jupyterlab']['mimeExtensions'][name] = ""
84            data['dependencies'][name] = semver
85        elif extension:
86            data['jupyterlab']['extensions'][name] = ""
87            data['dependencies'][name] = semver
88        else:
89            data['jupyterlab']['singletonPackages'].append(name)
90
91    def remove(self, name):
92        """Remove a package/extension.
93
94        name: string
95            The npm package name
96        """
97        data = self._data
98        maps = (
99            data['dependencies'],
100            data['resolutions'],
101            data['jupyterlab']['extensions'],
102            data['jupyterlab']['mimeExtensions'],
103        )
104        for m in maps:
105            try:
106                del m[name]
107            except KeyError:
108                pass
109
110        data['jupyterlab']['singletonPackages'].remove(name)
111
112    def clear_packages(self, lab_only=True):
113        """Clear the packages/extensions.
114        """
115        data = self._data
116        # Clear all dependencies
117        if lab_only:
118            # Clear all "@jupyterlab/" dependencies
119            data['dependencies'] = _only_nonlab(data['dependencies'])
120            data['resolutions'] = _only_nonlab(data['resolutions'])
121            data['jupyterlab']['extensions'] = _only_nonlab(
122                data['jupyterlab']['extensions'])
123            data['jupyterlab']['mimeExtensions'] = _only_nonlab(
124                data['jupyterlab']['mimeExtensions'])
125            data['jupyterlab']['singletonPackages'] = _only_nonlab(
126                data['jupyterlab']['singletonPackages'])
127        else:
128            data['dependencies'] = {}
129            data['resolutions'] = {}
130            data['jupyterlab']['extensions'] = {}
131            data['jupyterlab']['mimeExtensions'] = {}
132            data['jupyterlab']['singletonPackages'] = []
133
134    @property
135    def extensions(self):
136        """A dict mapping all extension names to their semver"""
137        data = self._data
138        return dict(
139            (k, data['resolutions'][k])
140            for k in data['jupyterlab']['extensions'].keys())
141
142    @property
143    def mime_extensions(self):
144        """A dict mapping all MIME extension names to their semver"""
145        data = self._data
146        return dict(
147            (k, data['resolutions'][k])
148            for k in data['jupyterlab']['mimeExtensions'].keys())
149
150    @property
151    def singletons(self):
152        """A dict mapping all singleton names to their semver"""
153        data = self._data
154        return dict(
155            (k, data['resolutions'].get(k, None))
156            for k in data['jupyterlab']['singletonPackages'])
157
158    @property
159    def static_dir(self):
160        return self._data['jupyterlab']['staticDir']
161
162    @static_dir.setter
163    def static_dir(self, static_dir):
164        self._data['jupyterlab']['staticDir'] = static_dir
165