1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | var Compiler = require('./compiler')
|
12 | , SourceMapGenerator = require('source-map').SourceMapGenerator
|
13 | , basename = require('path').basename
|
14 | , extname = require('path').extname
|
15 | , dirname = require('path').dirname
|
16 | , join = require('path').join
|
17 | , relative = require('path').relative
|
18 | , sep = require('path').sep
|
19 | , fs = require('fs');
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | var SourceMapper = module.exports = function SourceMapper(root, options){
|
30 | options = options || {};
|
31 | this.column = 1;
|
32 | this.lineno = 1;
|
33 | this.contents = {};
|
34 | this.filename = options.filename;
|
35 | this.dest = options.dest;
|
36 |
|
37 | var sourcemap = options.sourcemap;
|
38 | this.basePath = sourcemap.basePath || '.';
|
39 | this.inline = sourcemap.inline;
|
40 | this.comment = sourcemap.comment;
|
41 | if (this.dest && extname(this.dest) === '.css') {
|
42 | this.basename = basename(this.dest);
|
43 | this.dest = dirname(this.dest);
|
44 | } else {
|
45 | this.basename = basename(this.filename, extname(this.filename)) + '.css';
|
46 | }
|
47 | this.utf8 = false;
|
48 |
|
49 | this.map = new SourceMapGenerator({
|
50 | file: this.basename,
|
51 | sourceRoot: sourcemap.sourceRoot || null
|
52 | });
|
53 | Compiler.call(this, root, options);
|
54 | };
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 | SourceMapper.prototype.__proto__ = Compiler.prototype;
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | var compile = Compiler.prototype.compile;
|
70 | SourceMapper.prototype.compile = function(){
|
71 | var css = compile.call(this)
|
72 | , out = this.basename + '.map'
|
73 | , url = this.normalizePath(this.dest
|
74 | ? join(this.dest, out)
|
75 | : join(dirname(this.filename), out))
|
76 | , map;
|
77 |
|
78 | if (this.inline) {
|
79 | map = this.map.toString();
|
80 | url = 'data:application/json;'
|
81 | + (this.utf8 ? 'charset=utf-8;' : '') + 'base64,'
|
82 | + new Buffer(map).toString('base64');
|
83 | }
|
84 | if (this.inline || false !== this.comment)
|
85 | css += '/*# sourceMappingURL=' + url + ' */';
|
86 | return css;
|
87 | };
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | SourceMapper.prototype.out = function(str, node){
|
99 | if (node && node.lineno) {
|
100 | var filename = this.normalizePath(node.filename);
|
101 |
|
102 | this.map.addMapping({
|
103 | original: {
|
104 | line: node.lineno,
|
105 | column: node.column - 1
|
106 | },
|
107 | generated: {
|
108 | line: this.lineno,
|
109 | column: this.column - 1
|
110 | },
|
111 | source: filename
|
112 | });
|
113 |
|
114 | if (this.inline && !this.contents[filename]) {
|
115 | this.map.setSourceContent(filename, fs.readFileSync(node.filename, 'utf-8'));
|
116 | this.contents[filename] = true;
|
117 | }
|
118 | }
|
119 |
|
120 | this.move(str);
|
121 | return str;
|
122 | };
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 | SourceMapper.prototype.move = function(str){
|
132 | var lines = str.match(/\n/g)
|
133 | , idx = str.lastIndexOf('\n');
|
134 |
|
135 | if (lines) this.lineno += lines.length;
|
136 | this.column = ~idx
|
137 | ? str.length - idx
|
138 | : this.column + str.length;
|
139 | };
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 | SourceMapper.prototype.normalizePath = function(path){
|
150 | path = relative(this.dest || this.basePath, path);
|
151 | if ('\\' == sep) {
|
152 | path = path.replace(/^[a-z]:\\/i, '/')
|
153 | .replace(/\\/g, '/');
|
154 | }
|
155 | return path;
|
156 | };
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 | var literal = Compiler.prototype.visitLiteral;
|
163 | SourceMapper.prototype.visitLiteral = function(lit){
|
164 | var val = literal.call(this, lit)
|
165 | , filename = this.normalizePath(lit.filename)
|
166 | , indentsRe = /^\s+/
|
167 | , lines = val.split('\n');
|
168 |
|
169 |
|
170 | if (lines.length > 1) {
|
171 | lines.forEach(function(line, i) {
|
172 | var indents = line.match(indentsRe)
|
173 | , column = indents && indents[0]
|
174 | ? indents[0].length
|
175 | : 0;
|
176 |
|
177 | if (lit.css) column += 2;
|
178 |
|
179 | this.map.addMapping({
|
180 | original: {
|
181 | line: lit.lineno + i,
|
182 | column: column
|
183 | },
|
184 | generated: {
|
185 | line: this.lineno + i,
|
186 | column: 0
|
187 | },
|
188 | source: filename
|
189 | });
|
190 | }, this);
|
191 | }
|
192 | return val;
|
193 | };
|
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 | var charset = Compiler.prototype.visitCharset;
|
200 | SourceMapper.prototype.visitCharset = function(node){
|
201 | this.utf8 = ('utf-8' == node.val.string.toLowerCase());
|
202 | return charset.call(this, node);
|
203 | };
|