1 var postcss = require('postcss');
2 var gonzales = require('gonzales-pe');
3 var Input = require('postcss/lib/input');
5 var DEFAULT_RAWS_ROOT = {
9 var DEFAULT_RAWS_RULE = {
14 var DEFAULT_RAWS_DECL = {
20 var DEFAULT_COMMENT_DECL = {
26 global.postcssSass = {};
34 if (node.type === 'stylesheet') {
35 // Create and set parameters for Root node
36 var root = postcss.root();
44 semicolon: DEFAULT_RAWS_ROOT.semicolon,
45 before: DEFAULT_RAWS_ROOT.before
47 // Store spaces before root (if exist)
48 global.postcssSass.before = '';
49 for (var i = 0; i < node.content.length; i++) {
50 process(source, node.content[i], root, input);
53 } else if (node.type === 'ruleset') {
54 // Loop to find the deepest ruleset node
55 var pseudoClassFirst = false;
56 // Define new selector
58 global.postcssSass.multiRuleProp = '';
59 for (var rContent = 0; rContent < node.content.length; rContent++ ) {
60 if (node.content[rContent].type === 'block') {
62 var rule = postcss.rule();
64 // Object to store raws for Rule
66 before: global.postcssSass.before ||
67 DEFAULT_RAWS_RULE.before,
68 between: DEFAULT_RAWS_RULE.between
71 /* Variable to store spaces and symbols
72 before declaration property */
73 global.postcssSass.before = '';
75 global.postcssSass.comment = false;
77 // Look up throw all nodes in current ruleset node
79 var rCurrentContent = 0;
80 rCurrentContent < node.content.length;
83 if (node.content[rCurrentContent].type === 'block') {
86 node.content[rCurrentContent],
93 if (rule.nodes.length !== 0) {
94 // Write selector to Rule, and remove last whitespace
95 rule.selector = selector;
96 // Set parameters for Rule node
104 parent.nodes.push(rule);
106 } else if (node.content[rContent].type === 'selector') {
107 // Creates selector for rule
109 var sCurrentContent = 0;
110 sCurrentContent < node.content[rContent].length;
113 if (node.content[rContent]
114 .content[sCurrentContent].type === 'id') {
116 } else if (node.content[rContent]
117 .content[sCurrentContent].type === 'class') {
119 } else if (node.content[rContent]
120 .content[sCurrentContent].type === 'typeSelector') {
121 if (node.content[rContent]
122 .content[sCurrentContent + 1] &&
123 node.content[rContent]
124 .content[sCurrentContent + 1]
125 .type === 'pseudoClass' &&
129 pseudoClassFirst = true;
131 } else if (node.content[rContent]
132 .content[sCurrentContent].type === 'pseudoClass') {
135 selector += node.content[rContent]
136 .content[sCurrentContent].content;
140 } else if (node.type === 'block') {
141 /* If nested rules exist,
142 wrap current rule in new rule node */
143 if (global.postcssSass.multiRule) {
144 var multiRule = postcss.rule();
147 line: node.start.line - 1,
148 column: node.start.column
153 multiRule.parent = parent;
154 multiRule.selector = global.postcssSass.multiRuleProp;
156 before: global.postcssSass.before || DEFAULT_RAWS_RULE.before,
157 between: DEFAULT_RAWS_RULE.between
159 parent.push(multiRule);
163 global.postcssSass.before = '';
165 // Looking for declaration node in block node
166 for (var bContent = 0; bContent < node.content.length; bContent++) {
169 node.content[bContent],
174 } else if (node.type === 'declaration') {
175 var isBlockInside = false;
176 // Create Declaration node
177 var decl = postcss.decl();
179 // Object to store raws for Declaration
181 before: global.postcssSass.before || DEFAULT_RAWS_DECL.before,
182 between: DEFAULT_RAWS_DECL.between,
183 semicolon: DEFAULT_RAWS_DECL.semicolon
186 global.postcssSass.property = false;
187 global.postcssSass.betweenBefore = false;
188 global.postcssSass.comment = false;
189 // Looking for property and value node in declaration node
190 for (var dContent = 0; dContent < node.content.length; dContent++) {
191 if (node.content[dContent].type === 'property') {
192 /* global.property to detect is property is
193 already defined in current object */
194 global.postcssSass.property = true;
195 global.postcssSass.multiRuleProp = node.content[dContent]
199 node.content[dContent],
203 } else if (node.content[dContent].type === 'propertyDelimiter') {
204 if (global.postcssSass.property &&
205 !global.postcssSass.betweenBefore) {
206 /* If property is already defined and
207 there's no ':' before it */
208 dRaws.between += node.content[dContent].content;
209 global.postcssSass.multiRuleProp += node.content[dContent]
212 /* If ':' goes before property declaration, like
214 global.postcssSass.betweenBefore = true;
215 dRaws.before += node.content[dContent].content;
216 global.postcssSass.multiRuleProp += node.content[dContent]
219 } else if (node.content[dContent].type === 'space') {
220 dRaws.between += node.content[dContent].content;
221 } else if (node.content[dContent].type === 'value') {
222 // Look up for a value for current property
223 if (node.content[dContent].content[0].type === 'block') {
224 isBlockInside = true;
225 // If nested rules exist
226 if (typeof node.content[dContent]
227 .content[0].content === 'object') {
228 global.postcssSass.multiRule = true;
232 node.content[dContent].content[0],
236 } else if (node.content[dContent]
237 .content[0].type === 'variable') {
241 node.content[dContent],
245 } else if (node.content[dContent].content[0].type === 'color') {
249 node.content[dContent],
253 } else if (node.content[dContent]
254 .content[0].type === 'number') {
255 if (node.content[dContent].content.length > 1) {
258 var dCurrentContent = 0;
259 dCurrentContent < node.content[dContent]
263 decl.value += node.content[dContent]
264 .content[dCurrentContent];
269 node.content[dContent],
277 node.content[dContent],
285 global.postcssSass.before = '';
287 if (!isBlockInside) {
288 // Set parameters for Declaration node
294 decl.parent = parent;
296 parent.nodes.push(decl);
298 } else if (node.type === 'property') {
299 // Set property for Declaration node
300 if (node.content[0].type === 'variable') {
303 parent.prop += node.content[0].content;
304 } else if (node.type === 'value') {
308 // Set value for Declaration node
309 if (node.content.length > 0) {
312 vContent < node.content.length;
315 if (node.content[vContent].type === 'important') {
316 parent.important = true;
317 } else if (node.content[vContent]
318 .content.constructor === Array ) {
320 var vContentParts = 0;
321 vContentParts < node.content[vContent]
325 parent.value += node.content[vContent]
326 .content[vContentParts];
329 parent.value += node.content[vContent].content;
332 } else if (node.content[0].content.constructor === Array) {
334 var vContentFirst = 0;
335 vContentFirst < node.content[0].content.length;
338 parent.value += node.content[0]
339 .content[vContentFirst].content;
342 parent.value += node.content[0].content;
344 } else if (node.type === 'singlelineComment' ||
345 node.type === 'multilineComment') {
346 // Create a new node for comment
347 var comment = postcss.comment();
348 var text = node.content;
349 // Clear comment text from spaces/symbols
350 var textClear = text.trim();
351 comment.text = textClear;
352 // Found spaces/symbols before comment
353 var left = text.search(/\S/);
354 global.postcssSass.comment = true;
355 // Found spaces/symbols after comment
356 var right = text.length - textClear.length - left;
357 // Raws for current comment node
359 before: global.postcssSass.before || DEFAULT_COMMENT_DECL.before,
360 left: new Array(left + 1).join(' '),
361 right: new Array(right + 1).join(' ')
363 // Define type of comment
364 if (node.type === 'singlelineComment') {
365 comment.raws.commentType = 'single';
366 } else if (node.type === 'multilineComment') {
367 comment.raws.commentType = 'multi';
369 parent.nodes.push(comment);
370 } else if (node.type === 'space') {
371 // Spaces before root and rule
372 if (parent.type === 'root') {
373 global.postcssSass.before += node.content;
374 } else if (parent.type === 'rule') {
375 if (global.postcssSass.comment) {
376 global.postcssSass.before = '\n' + node.content;
378 if (global.postcssSass.before === '') {
379 global.postcssSass.before = '\n';
381 global.postcssSass.before += node.content;
384 } else if (node.type === 'declarationDelimiter') {
385 global.postcssSass.before += node.content;
390 module.exports = function sassToPostCssTree(
395 node: gonzales.parse(source.toString('utf8'), { syntax: 'sass' }),
396 input: new Input(source, opts),