notes/node_modules/postcss-value-parser/lib/parse.js

243 lines
7.4 KiB
JavaScript

var openParentheses = '('.charCodeAt(0);
var closeParentheses = ')'.charCodeAt(0);
var singleQuote = '\''.charCodeAt(0);
var doubleQuote = '"'.charCodeAt(0);
var backslash = '\\'.charCodeAt(0);
var slash = '/'.charCodeAt(0);
var comma = ','.charCodeAt(0);
var colon = ':'.charCodeAt(0);
var star = '*'.charCodeAt(0);
module.exports = function (input) {
var tokens = [];
var value = input;
var next, quote, prev, token, escape, escapePos, whitespacePos;
var pos = 0;
var code = value.charCodeAt(pos);
var max = value.length;
var stack = [{ nodes: tokens }];
var balanced = 0;
var parent;
var name = '';
var before = '';
var after = '';
while (pos < max) {
// Whitespaces
if (code <= 32) {
next = pos;
do {
next += 1;
code = value.charCodeAt(next);
} while (code <= 32);
token = value.slice(pos, next);
prev = tokens[tokens.length - 1];
if (code === closeParentheses && balanced) {
after = token;
} else if (prev && prev.type === 'div') {
prev.after = token;
} else if (code === comma || code === colon || code === slash && value.charCodeAt(next + 1) !== star) {
before = token;
} else {
tokens.push({
type: 'space',
sourceIndex: pos,
value: token
});
}
pos = next;
// Quotes
} else if (code === singleQuote || code === doubleQuote) {
next = pos;
quote = code === singleQuote ? '\'' : '"';
token = {
type: 'string',
sourceIndex: pos,
quote: quote
};
do {
escape = false;
next = value.indexOf(quote, next + 1);
if (~next) {
escapePos = next;
while (value.charCodeAt(escapePos - 1) === backslash) {
escapePos -= 1;
escape = !escape;
}
} else {
value += quote;
next = value.length - 1;
token.unclosed = true;
}
} while (escape);
token.value = value.slice(pos + 1, next);
tokens.push(token);
pos = next + 1;
code = value.charCodeAt(pos);
// Comments
} else if (code === slash && value.charCodeAt(pos + 1) === star) {
token = {
type: 'comment',
sourceIndex: pos
};
next = value.indexOf('*/', pos);
if (next === -1) {
token.unclosed = true;
next = value.length;
}
token.value = value.slice(pos + 2, next);
tokens.push(token);
pos = next + 2;
code = value.charCodeAt(pos);
// Dividers
} else if (code === slash || code === comma || code === colon) {
token = value[pos];
tokens.push({
type: 'div',
sourceIndex: pos - before.length,
value: token,
before: before,
after: ''
});
before = '';
pos += 1;
code = value.charCodeAt(pos);
// Open parentheses
} else if (openParentheses === code) {
// Whitespaces after open parentheses
next = pos;
do {
next += 1;
code = value.charCodeAt(next);
} while (code <= 32);
token = {
type: 'function',
sourceIndex: pos - name.length,
value: name,
before: value.slice(pos + 1, next)
};
pos = next;
if (name === 'url' && code !== singleQuote && code !== doubleQuote) {
next -= 1;
do {
escape = false;
next = value.indexOf(')', next + 1);
if (~next) {
escapePos = next;
while (value.charCodeAt(escapePos - 1) === backslash) {
escapePos -= 1;
escape = !escape;
}
} else {
value += ')';
next = value.length - 1;
token.unclosed = true;
}
} while (escape);
// Whitespaces before closed
whitespacePos = next;
do {
whitespacePos -= 1;
code = value.charCodeAt(whitespacePos);
} while (code <= 32);
if (pos !== whitespacePos + 1) {
token.nodes = [{
type: 'word',
sourceIndex: pos,
value: value.slice(pos, whitespacePos + 1)
}];
} else {
token.nodes = [];
}
if (token.unclosed && whitespacePos + 1 !== next) {
token.after = '';
token.nodes.push({
type: 'space',
sourceIndex: whitespacePos + 1,
value: value.slice(whitespacePos + 1, next)
});
} else {
token.after = value.slice(whitespacePos + 1, next);
}
pos = next + 1;
code = value.charCodeAt(pos);
tokens.push(token);
} else {
balanced += 1;
token.after = '';
tokens.push(token);
stack.push(token);
tokens = token.nodes = [];
parent = token;
}
name = '';
// Close parentheses
} else if (closeParentheses === code && balanced) {
pos += 1;
code = value.charCodeAt(pos);
parent.after = after;
after = '';
balanced -= 1;
stack.pop();
parent = stack[balanced];
tokens = parent.nodes;
// Words
} else {
next = pos;
do {
if (code === backslash) {
next += 1;
}
next += 1;
code = value.charCodeAt(next);
} while (next < max && !(
code <= 32 ||
code === singleQuote ||
code === doubleQuote ||
code === comma ||
code === colon ||
code === slash ||
code === openParentheses ||
code === closeParentheses && balanced
));
token = value.slice(pos, next);
if (openParentheses === code) {
name = token;
} else {
tokens.push({
type: 'word',
sourceIndex: pos,
value: token
});
}
pos = next;
}
}
for (pos = stack.length - 1; pos; pos -= 1) {
stack[pos].unclosed = true;
}
return stack[0].nodes;
};