Skip to content

Commit dc7f9da

Browse files
author
Andrey Fedoseev
committed
Add source maps support for LESS
1 parent 1f08343 commit dc7f9da

File tree

4 files changed

+55
-7
lines changed

4 files changed

+55
-7
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Dev
66
===
77

88
- Add source maps support for SASS/SCSS
9+
- Add source maps support for LESS
910

1011

1112
1.0.1

README.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,13 @@ LESS
222222
``executable``
223223
Path to LESS compiler executable. Default: ``"lessc"``.
224224

225+
``sourcemap_enabled``
226+
Boolean. Set to ``True`` to enable source maps. Default: ``False``
227+
225228
Example::
226229

227230
STATIC_PRECOMPILER_COMPILERS = (
228-
('static_precompiler.compilers.LESS', {"executable": "/usr/bin/lessc"),
231+
('static_precompiler.compilers.LESS', {"executable": "/usr/bin/lessc", "sourcemap_enabled": True),
229232
)
230233

231234

static_precompiler/compilers/less.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import os
23
import posixpath
34
import re
@@ -21,8 +22,9 @@ class LESS(base.BaseCompiler):
2122
IMPORT_RE = re.compile(r"@import\s+(.+?)\s*;", re.DOTALL)
2223
IMPORT_ITEM_RE = re.compile(r"([\"'])(.+?)\1")
2324

24-
def __init__(self, executable=settings.LESS_EXECUTABLE):
25+
def __init__(self, executable=settings.LESS_EXECUTABLE, sourcemap_enabled=False):
2526
self.executable = executable
27+
self.is_sourcemap_enabled = sourcemap_enabled
2628
super(LESS, self).__init__()
2729

2830
def should_compile(self, source_path, from_management=False):
@@ -34,20 +36,46 @@ def should_compile(self, source_path, from_management=False):
3436
def compile_file(self, source_path):
3537
full_source_path = self.get_full_source_path(source_path)
3638
full_output_path = self.get_full_output_path(source_path)
37-
args = [
38-
self.executable,
39-
self.get_full_source_path(source_path),
40-
full_output_path,
41-
]
39+
4240
# `cwd` is a directory containing `source_path`.
4341
# Ex: source_path = '1/2/3', full_source_path = '/abc/1/2/3' -> cwd = '/abc'
4442
cwd = os.path.normpath(os.path.join(full_source_path, *([".."] * len(source_path.split("/")))))
43+
44+
args = [
45+
self.executable
46+
]
47+
if self.is_sourcemap_enabled:
48+
args.extend([
49+
"--source-map"
50+
])
51+
52+
args.extend([
53+
self.get_full_source_path(source_path),
54+
full_output_path,
55+
])
4556
out, errors = utils.run_command(args, cwd=cwd)
4657
if errors:
4758
raise exceptions.StaticCompilationError(errors)
4859

4960
utils.convert_urls(full_output_path, source_path)
5061

62+
if self.is_sourcemap_enabled:
63+
sourcemap_full_path = full_output_path + ".map"
64+
65+
with open(sourcemap_full_path) as sourcemap_file:
66+
sourcemap = json.loads(sourcemap_file.read())
67+
68+
# LESS, unlike SASS, can't add correct relative paths in source map when the compiled file
69+
# is not in the same dir as the source file. We fix it here.
70+
sourcemap["sources"] = [
71+
"../" * len(source_path.split("/")) + posixpath.dirname(source_path) + "/" + source
72+
for source in sourcemap["sources"]
73+
]
74+
sourcemap["file"] = posixpath.basename(source_path)
75+
76+
with open(sourcemap_full_path, "w") as sourcemap_file:
77+
sourcemap_file.write(json.dumps(sourcemap))
78+
5179
return self.get_output_path(source_path)
5280

5381
def compile_source(self, source):

static_precompiler/tests/test_less.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ def test_compile_file(monkeypatch, tmpdir):
3434
"""
3535

3636

37+
def test_sourcemap(monkeypatch, tmpdir):
38+
39+
monkeypatch.setattr("static_precompiler.settings.ROOT", tmpdir.strpath)
40+
monkeypatch.setattr("static_precompiler.utils.convert_urls", lambda *args: None)
41+
42+
compiler = compilers.LESS(sourcemap_enabled=False)
43+
compiler.compile_file("styles/test.less") == "COMPILED/styles/test.css"
44+
full_output_path = compiler.get_full_output_path("styles/test.less")
45+
assert not os.path.exists(full_output_path + ".map")
46+
47+
compiler = compilers.LESS(sourcemap_enabled=True)
48+
compiler.compile_file("styles/test.less") == "COMPILED/styles/test.css"
49+
full_output_path = compiler.get_full_output_path("styles/test.less")
50+
assert os.path.exists(full_output_path + ".map")
51+
52+
3753
def test_compile_source():
3854
compiler = compilers.LESS()
3955

0 commit comments

Comments
 (0)