install gulp
This commit is contained in:
+129
@@ -0,0 +1,129 @@
|
||||
var TokenType = require('../token-types');
|
||||
|
||||
/**
|
||||
* Mark whitespaces and comments
|
||||
* @param {Array} tokens
|
||||
*/
|
||||
function markSpacesAndComments(tokens) {
|
||||
var tokensLength = tokens.length;
|
||||
var lastSpaceIndex = -1;
|
||||
var lastSpaceOrCommentIndex = -1;
|
||||
var spaces = [-1, -1];
|
||||
var token; // current token
|
||||
var type; // current token's type
|
||||
|
||||
// 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++) {
|
||||
type = tokens[i].type;
|
||||
|
||||
if (type === TokenType.Space ||
|
||||
type === TokenType.Tab ||
|
||||
type === TokenType.Newline) {
|
||||
markSpace(tokens, i, spaces);
|
||||
} else if (type === TokenType.CommentML) {
|
||||
markComment(tokens, i, spaces);
|
||||
} else {
|
||||
markEndOfSpacesAndComments(tokens, i, spaces);
|
||||
}
|
||||
}
|
||||
|
||||
markEndOfSpacesAndComments(tokens, i, spaces);
|
||||
}
|
||||
|
||||
function markSpace(tokens, i, spaces) {
|
||||
var token = tokens[i];
|
||||
token.ws = true;
|
||||
token.sc = true;
|
||||
|
||||
if (spaces[0] === -1) spaces[0] = i;
|
||||
if (spaces[1] === -1) spaces[1] = i;
|
||||
}
|
||||
|
||||
function markComment(tokens, i, spaces) {
|
||||
var ws = spaces[0];
|
||||
tokens[i].sc = true;
|
||||
|
||||
if (ws !== -1) {
|
||||
tokens[ws].ws_last = i - 1;
|
||||
spaces[0] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function markEndOfSpacesAndComments(tokens, i, spaces) {
|
||||
var ws = spaces[0];
|
||||
var sc = spaces[1];
|
||||
if (ws !== -1) {
|
||||
tokens[ws].ws_last = i - 1;
|
||||
spaces[0] = -1;
|
||||
}
|
||||
if (sc !== -1) {
|
||||
tokens[sc].sc_last = i - 1;
|
||||
spaces[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pair brackets
|
||||
* @param {Array} tokens
|
||||
*/
|
||||
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];
|
||||
var type = t.type;
|
||||
|
||||
if (type === TokenType.LeftParenthesis) {
|
||||
ps.push(i);
|
||||
} else if (type === TokenType.RightParenthesis) {
|
||||
if (ps.length) {
|
||||
t.left = ps.pop();
|
||||
tokens[t.left].right = i;
|
||||
}
|
||||
} else if (type === TokenType.LeftSquareBracket) {
|
||||
sbs.push(i);
|
||||
} else if (type === TokenType.RightSquareBracket) {
|
||||
if (sbs.length) {
|
||||
t.left = sbs.pop();
|
||||
tokens[t.left].right = i;
|
||||
}
|
||||
} else if (type === TokenType.LeftCurlyBracket) {
|
||||
cbs.push(i);
|
||||
} else if (type === TokenType.RightCurlyBracket) {
|
||||
if (cbs.length) {
|
||||
t.left = cbs.pop();
|
||||
tokens[t.left].right = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} tokens
|
||||
*/
|
||||
function markTokens(tokens) {
|
||||
// Mark paired brackets:
|
||||
markBrackets(tokens);
|
||||
// Mark whitespaces and comments:
|
||||
markSpacesAndComments(tokens);
|
||||
}
|
||||
|
||||
module.exports = markTokens;
|
||||
+2688
File diff suppressed because it is too large
Load Diff
+80
@@ -0,0 +1,80 @@
|
||||
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;
|
||||
},
|
||||
'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';
|
||||
},
|
||||
'multilineComment': function(t) {
|
||||
return '/*' + t.content + '*/';
|
||||
},
|
||||
'nthSelector': function(t) {
|
||||
return ':' + _t(t.content[0]) + '(' + _composite(t.content.slice(1)) + ')';
|
||||
},
|
||||
'percentage': function(t) {
|
||||
return _composite(t.content) + '%';
|
||||
},
|
||||
'pseudoClass': function(t) {
|
||||
return ':' + _composite(t.content);
|
||||
},
|
||||
'pseudoElement': function(t) {
|
||||
return '::' + _composite(t.content);
|
||||
},
|
||||
'uri': function(t) {
|
||||
return 'url(' + _composite(t.content) + ')';
|
||||
}
|
||||
};
|
||||
|
||||
return _t(tree);
|
||||
}
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
module.exports = function(css) {
|
||||
var TokenType = require('../token-types');
|
||||
|
||||
var tokens = [],
|
||||
urlMode = false,
|
||||
blockMode = 0,
|
||||
pos = 0,
|
||||
tn = 0,
|
||||
ln = 1,
|
||||
col = 1;
|
||||
var cssLength = 0;
|
||||
|
||||
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 < cssLength; 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 < cssLength; 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 < cssLength; 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 < cssLength; pos++) {
|
||||
// Skip all '\':
|
||||
if (css.charAt(pos) === '\\') pos++;
|
||||
else if (Punctuation[css.charAt(pos)]) 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 < cssLength; 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);
|
||||
}
|
||||
}
|
||||
|
||||
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 < cssLength; 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) {
|
||||
var c; // current character
|
||||
var cn; // next character
|
||||
|
||||
cssLength = css.length;
|
||||
|
||||
// Parse string, character by character:
|
||||
for (pos = 0; pos < cssLength; 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:
|
||||
if (blockMode > 0) parseIdentifier(css);
|
||||
else 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 (Punctuation[c]) {
|
||||
// Add it to the list of tokens:
|
||||
pushToken(Punctuation[c], c, col);
|
||||
if (c === '\n' || c === '\r') {
|
||||
ln++;
|
||||
col = 0;
|
||||
} // Go to next line
|
||||
else if (c === ')') urlMode = false; // exit url mode
|
||||
else if (c === '{') blockMode++; // enter a block
|
||||
else 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