Skip to content

Commit 8c1f2ec

Browse files
andreypopptj
authored andcommitted
Initial support for source map generation
1 parent 08c07d6 commit 8c1f2ec

File tree

3 files changed

+131
-57
lines changed

3 files changed

+131
-57
lines changed

index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ module.exports = function(node, options){
2222
? new Compressed(options)
2323
: new Identity(options);
2424

25-
return compiler.compile(node);
25+
var code = compiler.compile(node);
26+
if (options.sourceMap)
27+
return {code: code, map: compiler.map}
28+
else
29+
return code;
2630
};
2731

lib/identity.js

Lines changed: 123 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
var SourceMapGenerator = require('source-map').SourceMapGenerator;
12

23
/**
34
* Expose compiler.
@@ -11,9 +12,56 @@ module.exports = Compiler;
1112

1213
function Compiler(options) {
1314
options = options || {};
15+
this.map = new SourceMapGenerator({file: options.filename || 'generated.css'});
16+
this.position = {line: 1, column: 0};
1417
this.indentation = options.indent;
1518
}
1619

20+
/**
21+
* Emit string and update current position
22+
*/
23+
24+
Compiler.prototype.updatePosition = function(str) {
25+
var lines = str.match(/\n/g);
26+
if (lines) this.position.line += lines.length;
27+
var i = str.lastIndexOf('\n');
28+
this.position.column = ~i ? str.length - i : this.position.column + str.length;
29+
}
30+
31+
Compiler.prototype.emit = function(str, pos, startOnly) {
32+
if (pos && pos.start) {
33+
this.map.addMapping({
34+
generated: {
35+
line: this.position.line,
36+
column: Math.max(this.position.column - 1, 0)
37+
},
38+
source: 'source.css',
39+
original: {
40+
line: pos.start.line,
41+
column: pos.start.column - 1
42+
}
43+
});
44+
}
45+
46+
this.updatePosition(str);
47+
48+
if (!startOnly && pos && pos.end) {
49+
this.map.addMapping({
50+
generated: {
51+
line: this.position.line,
52+
column: Math.max(this.position.column - 1, 0)
53+
},
54+
source: 'source.css',
55+
original: {
56+
line: pos.end.line,
57+
column: pos.end.column - 1
58+
}
59+
});
60+
}
61+
62+
return str;
63+
}
64+
1765
/**
1866
* Compile `node`.
1967
*/
@@ -30,44 +78,53 @@ Compiler.prototype.visit = function(node){
3078
return this[node.type](node);
3179
};
3280

81+
Compiler.prototype.visitMap = function(nodes, join){
82+
join = join || '';
83+
var result = '';
84+
for (var i = 0, length = nodes.length; i < length; i++) {
85+
result += this.visit(nodes[i]);
86+
if (i < length - 1) result += this.emit(join);
87+
}
88+
return result;
89+
};
90+
3391
/**
3492
* Visit stylesheet node.
3593
*/
3694

3795
Compiler.prototype.stylesheet = function(node){
38-
return node.stylesheet
39-
.rules.map(this.visit, this)
40-
.join('\n\n');
96+
return this.visitMap(node.stylesheet.rules, '\n\n');
4197
};
4298

4399
/**
44100
* Visit comment node.
45101
*/
46102

47103
Compiler.prototype.comment = function(node){
48-
return this.indent() + '/*' + node.comment + '*/';
104+
return this.emit(this.indent() + '/*' + node.comment + '*/', node.position);
49105
};
50106

51107
/**
52108
* Visit import node.
53109
*/
54110

55111
Compiler.prototype.import = function(node){
56-
return '@import ' + node.import + ';';
112+
return this.emit('@import ' + node.import + ';', node.position);
57113
};
58114

59115
/**
60116
* Visit media node.
61117
*/
62118

63119
Compiler.prototype.media = function(node){
64-
return '@media '
65-
+ node.media
66-
+ ' {\n'
67-
+ this.indent(1)
68-
+ node.rules.map(this.visit, this).join('\n\n')
69-
+ this.indent(-1)
70-
+ '\n}';
120+
return this.emit('@media ' + node.media, node.position, true)
121+
+ this.emit(
122+
' {\n'
123+
+ this.indent(1))
124+
+ this.visitMap(node.rules, '\n\n')
125+
+ this.emit(
126+
this.indent(-1)
127+
+ '\n}');
71128
};
72129

73130
/**
@@ -77,58 +134,61 @@ Compiler.prototype.media = function(node){
77134
Compiler.prototype.document = function(node){
78135
var doc = '@' + (node.vendor || '') + 'document ' + node.document;
79136

80-
return doc + ' '
81-
+ ' {\n'
82-
+ this.indent(1)
83-
+ node.rules.map(this.visit, this).join('\n\n')
84-
+ this.indent(-1)
85-
+ '\n}';
137+
return this.emit(doc, node.position, true)
138+
+ this.emit(
139+
' '
140+
+ ' {\n'
141+
+ this.indent(1))
142+
+ this.visitMap(node.rules, '\n\n')
143+
+ this.emit(
144+
this.indent(-1)
145+
+ '\n}');
86146
};
87147

88148
/**
89149
* Visit charset node.
90150
*/
91151

92152
Compiler.prototype.charset = function(node){
93-
return '@charset ' + node.charset + ';\n';
153+
return this.emit('@charset ' + node.charset + ';', node.position);
94154
};
95155

96156
/**
97157
* Visit namespace node.
98158
*/
99159

100160
Compiler.prototype.namespace = function(node){
101-
return '@namespace ' + node.namespace + ';\n';
161+
return this.emit('@namespace ' + node.namespace + ';', node.position);
102162
};
103163

104164
/**
105165
* Visit supports node.
106166
*/
107167

108168
Compiler.prototype.supports = function(node){
109-
return '@supports '
110-
+ node.supports
111-
+ ' {\n'
112-
+ this.indent(1)
113-
+ node.rules.map(this.visit, this).join('\n\n')
114-
+ this.indent(-1)
115-
+ '\n}';
169+
return this.emit('@supports ' + node.supports, node.position, true)
170+
+ this.emit(
171+
' {\n'
172+
+ this.indent(1))
173+
+ this.visitMap(node.rules, '\n\n')
174+
+ this.emit(
175+
this.indent(-1)
176+
+ '\n}');
116177
};
117178

118179
/**
119180
* Visit keyframes node.
120181
*/
121182

122183
Compiler.prototype.keyframes = function(node){
123-
return '@'
124-
+ (node.vendor || '')
125-
+ 'keyframes '
126-
+ node.name
127-
+ ' {\n'
128-
+ this.indent(1)
129-
+ node.keyframes.map(this.visit, this).join('\n')
130-
+ this.indent(-1)
131-
+ '}';
184+
return this.emit('@' + (node.vendor || '') + 'keyframes ' + node.name, node.position, true)
185+
+ this.emit(
186+
' {\n'
187+
+ this.indent(1))
188+
+ this.visitMap(node.keyframes, '\n')
189+
+ this.emit(
190+
this.indent(-1)
191+
+ '}');
132192
};
133193

134194
/**
@@ -138,13 +198,16 @@ Compiler.prototype.keyframes = function(node){
138198
Compiler.prototype.keyframe = function(node){
139199
var decls = node.declarations;
140200

141-
return this.indent()
142-
+ node.values.join(', ')
143-
+ ' {\n'
144-
+ this.indent(1)
145-
+ decls.map(this.visit, this).join('\n')
146-
+ this.indent(-1)
147-
+ '\n' + this.indent() + '}\n';
201+
return this.emit(this.indent())
202+
+ this.emit(node.values.join(', '), node.position, true)
203+
+ this.emit(
204+
' {\n'
205+
+ this.indent(1))
206+
+ this.visitMap(decls, '\n')
207+
+ this.emit(
208+
this.indent(-1)
209+
+ '\n'
210+
+ this.indent() + '}\n');
148211
};
149212

150213
/**
@@ -156,12 +219,12 @@ Compiler.prototype.page = function(node){
156219
? node.selectors.join(', ') + ' '
157220
: '';
158221

159-
return '@page ' + sel
160-
+ '{\n'
161-
+ this.indent(1)
162-
+ node.declarations.map(this.visit, this).join('\n')
163-
+ this.indent(-1)
164-
+ '\n}';
222+
return this.emit('@page ' + sel, node.position, true)
223+
+ this.emit('{\n')
224+
+ this.emit(this.indent(1))
225+
+ this.visitMap(node.declarations, '\n')
226+
+ this.emit(this.indent(-1))
227+
+ this.emit('\n}');
165228
};
166229

167230
/**
@@ -173,20 +236,24 @@ Compiler.prototype.rule = function(node){
173236
var decls = node.declarations;
174237
if (!decls.length) return '';
175238

176-
return node.selectors.map(function(s){ return indent + s }).join(',\n')
177-
+ ' {\n'
178-
+ this.indent(1)
179-
+ decls.map(this.visit, this).join('\n')
180-
+ this.indent(-1)
181-
+ '\n' + this.indent() + '}';
239+
return this.emit(node.selectors.map(function(s){ return indent + s }).join(',\n'), node.position, true)
240+
+ this.emit(' {\n')
241+
+ this.emit(this.indent(1))
242+
+ this.visitMap(decls, '\n')
243+
+ this.emit(this.indent(-1))
244+
+ this.emit('\n' + this.indent() + '}');
182245
};
183246

184247
/**
185248
* Visit declaration node.
186249
*/
187250

188251
Compiler.prototype.declaration = function(node){
189-
return this.indent() + node.property + ': ' + node.value + ';';
252+
return this.emit(this.indent())
253+
+ this.emit(node.property, node.position)
254+
+ this.emit(': ')
255+
+ this.emit(node.value, node.position)
256+
+ this.emit(';');
190257
};
191258

192259
/**

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,8 @@
2020
},
2121
"scripts": {
2222
"test": "make test"
23+
},
24+
"dependencies": {
25+
"source-map": "~0.1.31"
2326
}
2427
}

0 commit comments

Comments
 (0)