install gulp

This commit is contained in:
2018-05-28 13:26:25 +02:00
parent 2de92c908b
commit 04fab6ba46
8032 changed files with 709336 additions and 3100 deletions
+129
View File
@@ -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;
File diff suppressed because it is too large Load Diff
@@ -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);
}
@@ -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);
};