Skip to content
This repository was archived by the owner on Oct 24, 2025. It is now read-only.

Commit a3b3b59

Browse files
committed
sassc -m/-g/--souremap option
1 parent 4ee3d6a commit a3b3b59

File tree

3 files changed

+118
-30
lines changed

3 files changed

+118
-30
lines changed

docs/changes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ To be released.
88

99
- Expose source maps support:
1010

11+
- :program:`sassc` has a new :option:`-m <sassc -m>`/:option:`-g
12+
<sassc -g>`/:option:`--sourcemap <sassc --sourcemap>` option.
1113
- :class:`~sassutils.wsgi.SassMiddleware` now also creates source map files
1214
with filenames followed by :file:`.map` suffix.
1315
- :meth:`Manifest.build_one() <sassutils.builder.Manifest.build_one>` method

sassc.py

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
.. sourcecode:: console
88
99
$ sassc
10-
Usage: sassc [options] SCSS_FILE...
10+
Usage: sassc [options] SCSS_FILE [CSS_FILE]
1111
1212
There are options as well:
1313
@@ -25,6 +25,14 @@
2525
2626
Path to find images. Default is the current directory (:file:`./`).
2727
28+
.. option:: -m, -g, --sourcemap
29+
30+
Emit source map. Requires the second argument (output CSS filename).
31+
The filename of source map will be the output CSS filename followed by
32+
:file:`.map`.
33+
34+
.. versionadded:: 0.4.0
35+
2836
.. option:: -v, --version
2937
3038
Prints the program version.
@@ -38,52 +46,85 @@
3846
"""
3947
from __future__ import print_function
4048

49+
import functools
4150
import optparse
4251
import sys
4352

4453
from sass import __version__ as VERSION, OUTPUT_STYLES, CompileError, compile
4554

4655

4756
def main(argv=sys.argv, stdout=sys.stdout, stderr=sys.stderr):
48-
parser = optparse.OptionParser(usage='%prog [options] SCSS_FILE...',
49-
version='%prog ' + VERSION)
57+
parser = optparse.OptionParser(
58+
usage='%prog [options] SCSS_FILE [OUT_CSS_FILE]',
59+
version='%prog ' + VERSION
60+
)
5061
output_styles = list(OUTPUT_STYLES)
5162
output_styles = ', '.join(output_styles[:-1]) + ', or ' + output_styles[-1]
5263
parser.add_option('-s', '--output-style', metavar='STYLE', type='choice',
5364
choices=list(OUTPUT_STYLES), default='nested',
5465
help='Coding style of the compiled result. Choose one '
5566
'of ' + output_styles + '. [default: %default]')
67+
parser.add_option('-m', '-g', '--sourcemap', dest='source_map',
68+
action='store_true', default=False,
69+
help='Emit source map. Requires the second argument '
70+
'(output css filename).')
5671
parser.add_option('-I', '--include-path', metavar='DIR',
5772
dest='include_paths', action='append',
5873
help='Path to find "@import"ed (S)CSS source files. '
5974
'Can be multiply used.')
6075
parser.add_option('-i', '--image-path', metavar='DIR', default='./',
6176
help='Path to find images. [default: %default]')
6277
options, args = parser.parse_args(argv[1:])
78+
error = functools.partial(print,
79+
parser.get_prog_name() + ': error:',
80+
file=stderr)
6381
if not args:
6482
parser.print_usage(stderr)
65-
print(parser.get_prog_name() + ': error:', 'too few arguments',
66-
file=stderr)
83+
error('too few arguments')
6784
return 2
68-
elif len(args) > 1:
85+
elif len(args) > 2:
6986
parser.print_usage(stderr)
70-
print(parser.get_prog_name() + ': error:', 'too many arguments',
71-
file=stderr)
87+
error('too many arguments')
7288
return 2
73-
for filename in args:
74-
try:
75-
css = compile(
89+
filename = args[0]
90+
if options.source_map and len(args) < 2:
91+
parser.print_usage(stderr)
92+
error('-m/-g/--sourcemap requires the second argument, the output '
93+
'css filename.')
94+
return 2
95+
try:
96+
if options.source_map:
97+
source_map_filename = args[1] + '.map' # FIXME
98+
css, source_map = compile(
7699
filename=filename,
77100
output_style=options.output_style,
101+
source_comments='map',
102+
source_map_filename=source_map_filename,
78103
include_paths=options.include_paths,
79104
image_path=options.image_path
80105
)
81-
except CompileError as e:
82-
print(parser.get_prog_name() + ': error:', e,
83-
file=stderr)
84-
return 1
85106
else:
107+
source_map_filename = None
108+
source_map = None
109+
css = compile(
110+
filename=filename,
111+
output_style=options.output_style,
112+
include_paths=options.include_paths,
113+
image_path=options.image_path
114+
)
115+
except CompileError as e:
116+
print(parser.get_prog_name() + ': error:', e,
117+
file=stderr)
118+
return 1
119+
else:
120+
if len(args) < 2:
86121
print(css, file=stdout)
122+
else:
123+
with open(args[1], 'w') as f:
124+
print(css, file=f)
125+
if source_map_filename:
126+
with open(source_map_filename, 'w') as f:
127+
f.write(source_map)
87128
return 0
88129

89130

sasstests.py

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@
3434
3535
/*# sourceMappingURL=a.sass.css.map */'''
3636

37+
A_EXPECTED_MAP = {
38+
'version': 3,
39+
'file': 'a.sass',
40+
'sources': ['test/a.sass'],
41+
'names': [],
42+
'mappings': 'AAKA;EAHE,kBAAkB;EAGpB,KAEE;IACE,OAAO'
43+
}
44+
3745
B_EXPECTED_CSS = '''\
3846
b i {
3947
font-size: 20px; }
@@ -163,13 +171,7 @@ def test_compile_source_map(self):
163171
)
164172
self.assertEqual(A_EXPECTED_CSS_WITH_MAP, actual)
165173
self.assertEqual(
166-
{
167-
'version': 3,
168-
'file': 'a.sass',
169-
'sources': ['test/a.sass'],
170-
'names': [],
171-
'mappings': 'AAKA;EAHE,kBAAkB;EAGpB,KAEE;IACE,OAAO'
172-
},
174+
A_EXPECTED_MAP,
173175
json.loads(source_map)
174176
)
175177

@@ -312,9 +314,9 @@ def test_no_args(self):
312314
'actual error message is: ' + repr(err)
313315
self.assertEqual('', self.out.getvalue())
314316

315-
def test_two_args(self):
317+
def test_three_args(self):
316318
exit_code = sassc.main(
317-
['sassc', 'a.scss', 'b.scss'],
319+
['sassc', 'a.scss', 'b.scss', 'c.scss'],
318320
self.out, self.err
319321
)
320322
self.assertEqual(2, exit_code)
@@ -323,15 +325,58 @@ def test_two_args(self):
323325
'actual error message is: ' + repr(err)
324326
self.assertEqual('', self.out.getvalue())
325327

326-
def test_sassc(self):
328+
def test_sassc_stdout(self):
327329
exit_code = sassc.main(['sassc', 'test/a.sass'], self.out, self.err)
328330
self.assertEqual(0, exit_code)
329331
self.assertEqual('', self.err.getvalue())
330-
self.assertEqual(
331-
'body {\n background-color: green; }\n'
332-
' body a {\n color: blue; }\n\n',
333-
self.out.getvalue()
334-
)
332+
self.assertEqual(A_EXPECTED_CSS.strip(), self.out.getvalue().strip())
333+
334+
def test_sassc_output(self):
335+
fd, tmp = tempfile.mkstemp('.css')
336+
try:
337+
os.close(fd)
338+
exit_code = sassc.main(['sassc', 'test/a.sass', tmp],
339+
self.out, self.err)
340+
self.assertEqual(0, exit_code)
341+
self.assertEqual('', self.err.getvalue())
342+
self.assertEqual('', self.out.getvalue())
343+
with open(tmp) as f:
344+
self.assertEqual(A_EXPECTED_CSS.strip(), f.read().strip())
345+
finally:
346+
os.remove(tmp)
347+
348+
def test_sassc_source_map_without_css_filename(self):
349+
exit_code = sassc.main(['sassc', '-m', 'a.scss'], self.out, self.err)
350+
self.assertEqual(2, exit_code)
351+
err = self.err.getvalue()
352+
assert err.strip().endswith('error: -m/-g/--sourcemap requires '
353+
'the second argument, the output css '
354+
'filename.'), \
355+
'actual error message is: ' + repr(err)
356+
self.assertEqual('', self.out.getvalue())
357+
358+
def test_sassc_sourcemap(self):
359+
fd, tmp = tempfile.mkstemp('.css')
360+
try:
361+
os.close(fd)
362+
exit_code = sassc.main(['sassc', '-m', 'test/a.sass', tmp],
363+
self.out, self.err)
364+
self.assertEqual(0, exit_code)
365+
self.assertEqual('', self.err.getvalue())
366+
self.assertEqual('', self.out.getvalue())
367+
with open(tmp) as f:
368+
self.assertEqual(
369+
A_EXPECTED_CSS + '\n/*# sourceMappingURL=' +
370+
os.path.basename(tmp) + '.map */',
371+
f.read().strip()
372+
)
373+
with open(tmp + '.map') as f:
374+
self.assertEqual(
375+
dict(A_EXPECTED_MAP, sources=None),
376+
dict(json.load(f), sources=None)
377+
)
378+
finally:
379+
os.remove(tmp)
335380

336381

337382
test_cases = [

0 commit comments

Comments
 (0)