install gulp
This commit is contained in:
+116
@@ -0,0 +1,116 @@
|
||||
var TokenType = require('../token-types');
|
||||
|
||||
module.exports = (function() {
|
||||
/**
|
||||
* Mark whitespaces and comments
|
||||
*/
|
||||
function markSC(tokens) {
|
||||
var tokensLength = tokens.length,
|
||||
ws = -1, // flag for whitespaces
|
||||
sc = -1, // flag for whitespaces and comments
|
||||
t; // current token
|
||||
|
||||
// For every token in the token list, mark spaces and line breaks
|
||||
// as spaces (set both `ws` and `sc` flags). Mark multiline comments
|
||||
// with `sc` flag.
|
||||
// If there are several spaces or tabs or line breaks or multiline
|
||||
// comments in a row, group them: take the last one's index number
|
||||
// and save it to the first token in the group as a reference
|
||||
// (e.g., `ws_last = 7` for a group of whitespaces or `sc_last = 9`
|
||||
// for a group of whitespaces and comments):
|
||||
for (var i = 0; i < tokensLength; i++) {
|
||||
t = tokens[i];
|
||||
switch (t.type) {
|
||||
case TokenType.Space:
|
||||
case TokenType.Tab:
|
||||
case TokenType.Newline:
|
||||
t.ws = true;
|
||||
t.sc = true;
|
||||
|
||||
if (ws === -1) ws = i;
|
||||
if (sc === -1) sc = i;
|
||||
|
||||
break;
|
||||
case TokenType.CommentML:
|
||||
case TokenType.CommentSL:
|
||||
if (ws !== -1) {
|
||||
tokens[ws].ws_last = i - 1;
|
||||
ws = -1;
|
||||
}
|
||||
|
||||
t.sc = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
if (ws !== -1) {
|
||||
tokens[ws].ws_last = i - 1;
|
||||
ws = -1;
|
||||
}
|
||||
|
||||
if (sc !== -1) {
|
||||
tokens[sc].sc_last = i - 1;
|
||||
sc = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ws !== -1) tokens[ws].ws_last = i - 1;
|
||||
if (sc !== -1) tokens[sc].sc_last = i - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pair brackets
|
||||
*/
|
||||
function markBrackets(tokens) {
|
||||
var tokensLength = tokens.length;
|
||||
var ps = [], // parenthesis
|
||||
sbs = [], // square brackets
|
||||
cbs = [], // curly brackets
|
||||
t; // current token
|
||||
|
||||
// For every token in the token list, if we meet an opening (left)
|
||||
// bracket, push its index number to a corresponding array.
|
||||
// If we then meet a closing (right) bracket, look at the corresponding
|
||||
// array. If there are any elements (records about previously met
|
||||
// left brackets), take a token of the last left bracket (take
|
||||
// the last index number from the array and find a token with
|
||||
// this index number) and save right bracket's index as a reference:
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
t = tokens[i];
|
||||
switch(t.type) {
|
||||
case TokenType.LeftParenthesis:
|
||||
ps.push(i);
|
||||
break;
|
||||
case TokenType.RightParenthesis:
|
||||
if (ps.length) {
|
||||
t.left = ps.pop();
|
||||
tokens[t.left].right = i;
|
||||
}
|
||||
break;
|
||||
case TokenType.LeftSquareBracket:
|
||||
sbs.push(i);
|
||||
break;
|
||||
case TokenType.RightSquareBracket:
|
||||
if (sbs.length) {
|
||||
t.left = sbs.pop();
|
||||
tokens[t.left].right = i;
|
||||
}
|
||||
break;
|
||||
case TokenType.LeftCurlyBracket:
|
||||
cbs.push(i);
|
||||
break;
|
||||
case TokenType.RightCurlyBracket:
|
||||
if (cbs.length) {
|
||||
t.left = cbs.pop();
|
||||
tokens[t.left].right = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return function(tokens) {
|
||||
markBrackets(tokens);
|
||||
markSC(tokens);
|
||||
}
|
||||
})();
|
||||
+3168
File diff suppressed because it is too large
Load Diff
+95
@@ -0,0 +1,95 @@
|
||||
module.exports = function stringify(tree) {
|
||||
// TODO: Better error message
|
||||
if (!tree) throw new Error('We need tree to translate');
|
||||
|
||||
function _t(tree) {
|
||||
var type = tree.type;
|
||||
if (_unique[type]) return _unique[type](tree);
|
||||
if (typeof tree.content === 'string') return tree.content;
|
||||
if (Array.isArray(tree.content)) return _composite(tree.content);
|
||||
return '';
|
||||
}
|
||||
|
||||
function _composite(t, i) {
|
||||
if (!t) return '';
|
||||
|
||||
var s = '';
|
||||
i = i || 0;
|
||||
for (; i < t.length; i++) s += _t(t[i]);
|
||||
return s;
|
||||
}
|
||||
|
||||
var _unique = {
|
||||
'arguments': function(t) {
|
||||
return '(' + _composite(t.content) + ')';
|
||||
},
|
||||
'atkeyword': function(t) {
|
||||
return '@' + _composite(t.content);
|
||||
},
|
||||
'atruler': function(t) {
|
||||
return _t(t.content[0]) + _t(t.content[1]) + '{' + _t(t.content[2]) + '}';
|
||||
},
|
||||
'attribute': function(t) {
|
||||
return '[' + _composite(t.content) + ']';
|
||||
},
|
||||
'block': function(t) {
|
||||
return '{' + _composite(t.content) + '}';
|
||||
},
|
||||
'braces': function(t) {
|
||||
return t.content[0] + _composite(t.content.slice(2)) + t.content[1];
|
||||
},
|
||||
'class': function(t) {
|
||||
return '.' + _composite(t.content);
|
||||
},
|
||||
'color': function(t) {
|
||||
return '#' + t.content;
|
||||
},
|
||||
'escapedString': function(t) {
|
||||
return '~' + t.content;
|
||||
},
|
||||
'filter': function(t) {
|
||||
return _t(t.content[0]) + ':' + _t(t.content[1]);
|
||||
},
|
||||
'functionExpression': function(t) {
|
||||
return 'expression(' + t.content + ')';
|
||||
},
|
||||
'id': function (t) {
|
||||
return '#' + t.content;
|
||||
},
|
||||
'important': function(t) {
|
||||
return '!' + _composite(t.content) + 'important';
|
||||
},
|
||||
'interpolatedVariable': function(t) {
|
||||
return '@{' + _composite(t.content) + '}';
|
||||
},
|
||||
'multilineComment': function (t) {
|
||||
return '/*' + t.content + '*/';
|
||||
},
|
||||
'nthSelector': function(t) {
|
||||
return ':' + _t(t.content[0]) + '(' + _composite(t.content.slice(1)) + ')';
|
||||
},
|
||||
'percentage': function(t) {
|
||||
return _t(t.content) + '%';
|
||||
},
|
||||
'pseudoClass': function(t) {
|
||||
return ':' + _composite(t.content);
|
||||
},
|
||||
'pseudoElement': function(t) {
|
||||
return '::' + _composite(t.content);
|
||||
},
|
||||
'singlelineComment': function (t) {
|
||||
return '/' + '/' + t.content;
|
||||
},
|
||||
'uri': function(t) {
|
||||
return 'url(' + _composite(t.content) + ')';
|
||||
},
|
||||
'variable': function(t) {
|
||||
return '@' + _composite(t.content);
|
||||
},
|
||||
'variablesList': function(t) {
|
||||
return _composite(t.content) + '...';
|
||||
}
|
||||
};
|
||||
|
||||
return _t(tree);
|
||||
}
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
module.exports = function(css) {
|
||||
var TokenType = require('../token-types');
|
||||
|
||||
var tokens = [],
|
||||
urlMode = false,
|
||||
blockMode = 0,
|
||||
c, // current character
|
||||
cn, // next character
|
||||
pos = 0,
|
||||
tn = 0,
|
||||
ln = 1,
|
||||
col = 1;
|
||||
|
||||
var Punctuation = {
|
||||
' ': TokenType.Space,
|
||||
'\n': TokenType.Newline,
|
||||
'\r': TokenType.Newline,
|
||||
'\t': TokenType.Tab,
|
||||
'!': TokenType.ExclamationMark,
|
||||
'"': TokenType.QuotationMark,
|
||||
'#': TokenType.NumberSign,
|
||||
'$': TokenType.DollarSign,
|
||||
'%': TokenType.PercentSign,
|
||||
'&': TokenType.Ampersand,
|
||||
'\'': TokenType.Apostrophe,
|
||||
'(': TokenType.LeftParenthesis,
|
||||
')': TokenType.RightParenthesis,
|
||||
'*': TokenType.Asterisk,
|
||||
'+': TokenType.PlusSign,
|
||||
',': TokenType.Comma,
|
||||
'-': TokenType.HyphenMinus,
|
||||
'.': TokenType.FullStop,
|
||||
'/': TokenType.Solidus,
|
||||
':': TokenType.Colon,
|
||||
';': TokenType.Semicolon,
|
||||
'<': TokenType.LessThanSign,
|
||||
'=': TokenType.EqualsSign,
|
||||
'>': TokenType.GreaterThanSign,
|
||||
'?': TokenType.QuestionMark,
|
||||
'@': TokenType.CommercialAt,
|
||||
'[': TokenType.LeftSquareBracket,
|
||||
']': TokenType.RightSquareBracket,
|
||||
'^': TokenType.CircumflexAccent,
|
||||
'_': TokenType.LowLine,
|
||||
'{': TokenType.LeftCurlyBracket,
|
||||
'|': TokenType.VerticalLine,
|
||||
'}': TokenType.RightCurlyBracket,
|
||||
'~': TokenType.Tilde
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a token to the token list
|
||||
* @param {string} type
|
||||
* @param {string} value
|
||||
*/
|
||||
function pushToken(type, value, column) {
|
||||
tokens.push({
|
||||
tn: tn++,
|
||||
ln: ln,
|
||||
col: column,
|
||||
type: type,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a character is a decimal digit
|
||||
* @param {string} c Character
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isDecimalDigit(c) {
|
||||
return '0123456789'.indexOf(c) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse spaces
|
||||
* @param {string} css Unparsed part of CSS string
|
||||
*/
|
||||
function parseSpaces(css) {
|
||||
var start = pos;
|
||||
|
||||
// Read the string until we meet a non-space character:
|
||||
for (; pos < css.length; pos++) {
|
||||
if (css.charAt(pos) !== ' ') break;
|
||||
}
|
||||
|
||||
// Add a substring containing only spaces to tokens:
|
||||
pushToken(TokenType.Space, css.substring(start, pos--), col);
|
||||
col += (pos - start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string within quotes
|
||||
* @param {string} css Unparsed part of CSS string
|
||||
* @param {string} q Quote (either `'` or `"`)
|
||||
*/
|
||||
function parseString(css, q) {
|
||||
var start = pos;
|
||||
|
||||
// Read the string until we meet a matching quote:
|
||||
for (pos++; pos < css.length; pos++) {
|
||||
// Skip escaped quotes:
|
||||
if (css.charAt(pos) === '\\') pos++;
|
||||
else if (css.charAt(pos) === q) break;
|
||||
}
|
||||
|
||||
// Add the string (including quotes) to tokens:
|
||||
pushToken(q === '"' ? TokenType.StringDQ : TokenType.StringSQ, css.substring(start, pos + 1), col);
|
||||
col += (pos - start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse numbers
|
||||
* @param {string} css Unparsed part of CSS string
|
||||
*/
|
||||
function parseDecimalNumber(css) {
|
||||
var start = pos;
|
||||
|
||||
// Read the string until we meet a character that's not a digit:
|
||||
for (; pos < css.length; pos++) {
|
||||
if (!isDecimalDigit(css.charAt(pos))) break;
|
||||
}
|
||||
|
||||
// Add the number to tokens:
|
||||
pushToken(TokenType.DecimalNumber, css.substring(start, pos--), col);
|
||||
col += (pos - start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse identifier
|
||||
* @param {string} css Unparsed part of CSS string
|
||||
*/
|
||||
function parseIdentifier(css) {
|
||||
var start = pos;
|
||||
|
||||
// Skip all opening slashes:
|
||||
while (css.charAt(pos) === '/') pos++;
|
||||
|
||||
// Read the string until we meet a punctuation mark:
|
||||
for (; pos < css.length; pos++) {
|
||||
// Skip all '\':
|
||||
if (css.charAt(pos) === '\\') pos++;
|
||||
else if (css.charAt(pos) in Punctuation) break;
|
||||
}
|
||||
|
||||
var ident = css.substring(start, pos--);
|
||||
|
||||
// Enter url mode if parsed substring is `url`:
|
||||
urlMode = urlMode || ident === 'url';
|
||||
|
||||
// Add identifier to tokens:
|
||||
pushToken(TokenType.Identifier, ident, col);
|
||||
col += (pos - start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a multiline comment
|
||||
* @param {string} css Unparsed part of CSS string
|
||||
*/
|
||||
function parseMLComment(css) {
|
||||
var start = pos;
|
||||
|
||||
// Read the string until we meet `*/`.
|
||||
// Since we already know first 2 characters (`/*`), start reading
|
||||
// from `pos + 2`:
|
||||
for (pos = pos + 2; pos < css.length; pos++) {
|
||||
if (css.charAt(pos) === '*' && css.charAt(pos + 1) === '/') {
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add full comment (including `/*` and `*/`) to the list of tokens:
|
||||
var comment = css.substring(start, pos + 1);
|
||||
pushToken(TokenType.CommentML, comment, col);
|
||||
|
||||
var newlines = comment.split('\n');
|
||||
if (newlines.length > 1) {
|
||||
ln += newlines.length - 1;
|
||||
col = newlines[newlines.length - 1].length;
|
||||
} else {
|
||||
col += (pos - start);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single line comment
|
||||
* @param {string} css Unparsed part of CSS string
|
||||
*/
|
||||
function parseSLComment(css) {
|
||||
var start = pos;
|
||||
|
||||
// Read the string until we meet line break.
|
||||
// Since we already know first 2 characters (`//`), start reading
|
||||
// from `pos + 2`:
|
||||
for (pos+=2; pos < css.length; pos++) {
|
||||
if (css.charAt(pos) === '\n' || css.charAt(pos) === '\r') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add comment (including `//` and line break) to the list of tokens:
|
||||
pushToken(TokenType.CommentSL, css.substring(start, pos--), col);
|
||||
col += pos - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a CSS string to a list of tokens
|
||||
* @param {string} css CSS string
|
||||
* @returns {Array} List of tokens
|
||||
* @private
|
||||
*/
|
||||
function getTokens(css) {
|
||||
// Parse string, character by character:
|
||||
for (pos = 0; pos < css.length; col++, pos++) {
|
||||
c = css.charAt(pos);
|
||||
cn = css.charAt(pos + 1);
|
||||
|
||||
// If we meet `/*`, it's a start of a multiline comment.
|
||||
// Parse following characters as a multiline comment:
|
||||
if (c === '/' && cn === '*') {
|
||||
parseMLComment(css);
|
||||
}
|
||||
|
||||
// If we meet `//` and it is not a part of url:
|
||||
else if (!urlMode && c === '/' && cn === '/') {
|
||||
// If we're currently inside a block, treat `//` as a start
|
||||
// of identifier. Else treat `//` as a start of a single-line
|
||||
// comment:
|
||||
parseSLComment(css);
|
||||
}
|
||||
|
||||
// If current character is a double or single quote, it's a start
|
||||
// of a string:
|
||||
else if (c === '"' || c === "'") {
|
||||
parseString(css, c);
|
||||
}
|
||||
|
||||
// If current character is a space:
|
||||
else if (c === ' ') {
|
||||
parseSpaces(css);
|
||||
}
|
||||
|
||||
// If current character is a punctuation mark:
|
||||
else if (c in Punctuation) {
|
||||
// Add it to the list of tokens:
|
||||
pushToken(Punctuation[c], c, col);
|
||||
if (c === '\n' || c === '\r') {
|
||||
ln++;
|
||||
col = 0;
|
||||
} // Go to next line
|
||||
if (c === ')') urlMode = false; // exit url mode
|
||||
if (c === '{') blockMode++; // enter a block
|
||||
if (c === '}') blockMode--; // exit a block
|
||||
}
|
||||
|
||||
// If current character is a decimal digit:
|
||||
else if (isDecimalDigit(c)) {
|
||||
parseDecimalNumber(css);
|
||||
}
|
||||
|
||||
// If current character is anything else:
|
||||
else {
|
||||
parseIdentifier(css);
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
return getTokens(css);
|
||||
};
|
||||
Reference in New Issue
Block a user