summaryrefslogtreecommitdiff
path: root/scss/legacy.py
blob: ec4fd72c527062269ff3bb660930ef4c4d7854cd (plain)
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division

import os
from pathlib import Path
from collections import namedtuple

import six

from scss.calculator import Calculator
from scss.compiler import Compiler
import scss.config as config
from scss.extension.bootstrap import BootstrapExtension
from scss.extension.core import CoreExtension
from scss.extension.compass import CompassExtension
from scss.extension.extra import ExtraExtension
from scss.extension.fonts import FontsExtension
from scss.namespace import Namespace
from scss.scss_meta import (
    BUILD_INFO, PROJECT, VERSION, REVISION, URL, AUTHOR, AUTHOR_EMAIL, LICENSE,
)
from scss.source import SourceFile
from scss.types import String


_default_scss_vars = {
    '$BUILD-INFO': String.unquoted(BUILD_INFO),
    '$PROJECT': String.unquoted(PROJECT),
    '$VERSION': String.unquoted(VERSION),
    '$REVISION': String.unquoted(REVISION),
    '$URL': String.unquoted(URL),
    '$AUTHOR': String.unquoted(AUTHOR),
    '$AUTHOR-EMAIL': String.unquoted(AUTHOR_EMAIL),
    '$LICENSE': String.unquoted(LICENSE),

    # unsafe chars will be hidden as vars
    '$--doubleslash': String.unquoted('//'),
    '$--bigcopen': String.unquoted('/*'),
    '$--bigcclose': String.unquoted('*/'),
    '$--doubledot': String.unquoted(':'),
    '$--semicolon': String.unquoted(';'),
    '$--curlybracketopen': String.unquoted('{'),
    '$--curlybracketclosed': String.unquoted('}'),
}

SourceFileTuple = namedtuple('SourceFileTuple', ('parent_dir', 'filename'))


# TODO using this should spew an actual deprecation warning
class Scss(object):
    """Original programmatic interface to the compiler.

    This class is now DEPRECATED.  See :mod:`scss.compiler` for the
    replacement.
    """
    def __init__(
            self, scss_vars=None, scss_opts=None, scss_files=None,
            super_selector='', live_errors=False,
            library=None, func_registry=None,
            search_paths=None):

        self.super_selector = super_selector

        self._scss_vars = {}
        if scss_vars:
            calculator = Calculator()
            for var_name, value in scss_vars.items():
                if isinstance(value, six.string_types):
                    scss_value = calculator.evaluate_expression(value)
                    if scss_value is None:
                        # TODO warning?
                        scss_value = String.unquoted(value)
                else:
                    scss_value = value
                self._scss_vars[var_name] = scss_value

        self._scss_opts = scss_opts or {}
        self._scss_files = scss_files
        self._library = func_registry or library
        self._search_paths = search_paths

        # If true, swallow compile errors and embed them in the output instead
        self.live_errors = live_errors

    def compile(
            self, scss_string=None, scss_file=None, source_files=None,
            super_selector=None, filename=None, is_sass=None,
            line_numbers=True, import_static_css=False):
        """Compile Sass to CSS.  Returns a single CSS string.

        This method is DEPRECATED; see :mod:`scss.compiler` instead.
        """
        # Derive our root namespace
        self.scss_vars = _default_scss_vars.copy()
        if self._scss_vars is not None:
            self.scss_vars.update(self._scss_vars)

        root_namespace = Namespace(
            variables=self.scss_vars,
            functions=self._library,
        )

        # Figure out search paths.  Fall back from provided explicitly to
        # defined globally to just searching the current directory
        search_paths = ['.']
        if self._search_paths is not None:
            assert not isinstance(self._search_paths, six.string_types), \
                "`search_paths` should be an iterable, not a string"
            search_paths.extend(self._search_paths)
        else:
            if config.LOAD_PATHS:
                if isinstance(config.LOAD_PATHS, six.string_types):
                    # Back-compat: allow comma-delimited
                    search_paths.extend(config.LOAD_PATHS.split(','))
                else:
                    search_paths.extend(config.LOAD_PATHS)

            search_paths.extend(self._scss_opts.get('load_paths', []))

        # Normalize a few old styles of options
        output_style = self._scss_opts.get('style', config.STYLE)
        if output_style is True:
            output_style = 'compressed'
        elif output_style is False:
            output_style = 'legacy'

        fixed_search_path = []
        for path in search_paths:
            if isinstance(path, six.string_types):
                fixed_search_path.append(Path(path))
            else:
                fixed_search_path.append(path)

        # Build the compiler
        compiler = Compiler(
            namespace=root_namespace,
            extensions=[
                CoreExtension,
                ExtraExtension,
                FontsExtension,
                CompassExtension,
                BootstrapExtension,
            ],
            search_path=fixed_search_path,
            import_static_css=import_static_css,
            live_errors=self.live_errors,
            generate_source_map=self._scss_opts.get('debug_info', False),
            output_style=output_style,
            warn_unused_imports=self._scss_opts.get('warn_unused', False),
            ignore_parse_errors=config.DEBUG,
            loops_have_own_scopes=config.CONTROL_SCOPING,
            undefined_variables_fatal=config.FATAL_UNDEFINED,
            super_selector=super_selector or self.super_selector,
        )
        # Gonna add the source files manually
        compilation = compiler.make_compilation()

        # Inject the files we know about
        # TODO how does this work with the expectation of absoluteness
        if source_files is not None:
            for source in source_files:
                compilation.add_source(source)
        elif scss_string is not None:
            source = SourceFile.from_string(
                scss_string,
                relpath=filename,
                is_sass=is_sass,
            )
            compilation.add_source(source)
        elif scss_file is not None:
            # This is now the only way to allow forcibly overriding the
            # filename a source "thinks" it is
            with open(scss_file, 'rb') as f:
                source = SourceFile.from_file(
                    f,
                    relpath=filename or scss_file,
                    is_sass=is_sass,
                )
            compilation.add_source(source)

        # Plus the ones from the constructor
        if self._scss_files:
            for name, contents in list(self._scss_files.items()):
                source = SourceFile.from_string(contents, relpath=name)
                compilation.add_source(source)

        compiled = compiler.call_and_catch_errors(compilation.run)
        self.source_files = list(SourceFileTuple(*os.path.split(s.path)) for s in compilation.source_index.values())
        return compiled

    # Old, old alias
    Compilation = compile

    def get_scss_constants(self):
        scss_vars = self.root_namespace.variables
        return dict(
            (k, v) for k, v in scss_vars.items()
            if k and (not k.startswith('$') or k[1].isupper())
        )

    def get_scss_vars(self):
        scss_vars = self.root_namespace.variables
        return dict(
            (k, v) for k, v in scss_vars.items()
            if k and not (not k.startswith('$') and k[1].isupper())
        )