conversion from php to nodejs started
This commit is contained in:
568
nodejs/node_modules/stylus/lib/visitor/compiler.js
generated
vendored
Normal file
568
nodejs/node_modules/stylus/lib/visitor/compiler.js
generated
vendored
Normal file
@@ -0,0 +1,568 @@
|
||||
/*!
|
||||
* Stylus - Compiler
|
||||
* Copyright (c) Automattic <developer.wordpress.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Visitor = require('./')
|
||||
, utils = require('../utils')
|
||||
, fs = require('fs');
|
||||
|
||||
/**
|
||||
* Initialize a new `Compiler` with the given `root` Node
|
||||
* and the following `options`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `compress` Compress the CSS output (default: false)
|
||||
*
|
||||
* @param {Node} root
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var Compiler = module.exports = function Compiler(root, options) {
|
||||
options = options || {};
|
||||
this.compress = options.compress;
|
||||
this.firebug = options.firebug;
|
||||
this.linenos = options.linenos;
|
||||
this.spaces = options['indent spaces'] || 2;
|
||||
this.includeCSS = options['include css'];
|
||||
this.indents = 1;
|
||||
Visitor.call(this, root);
|
||||
this.stack = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `Visitor.prototype`.
|
||||
*/
|
||||
|
||||
Compiler.prototype.__proto__ = Visitor.prototype;
|
||||
|
||||
/**
|
||||
* Compile to css, and return a string of CSS.
|
||||
*
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Compiler.prototype.compile = function(){
|
||||
return this.visit(this.root);
|
||||
};
|
||||
|
||||
/**
|
||||
* Output `str`
|
||||
*
|
||||
* @param {String} str
|
||||
* @param {Node} node
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Compiler.prototype.out = function(str, node){
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return indentation string.
|
||||
*
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Compiler.prototype.__defineGetter__('indent', function(){
|
||||
if (this.compress) return '';
|
||||
return new Array(this.indents).join(Array(this.spaces + 1).join(' '));
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if given `node` needs brackets.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Compiler.prototype.needBrackets = function(node){
|
||||
return 1 == this.indents
|
||||
|| 'atrule' != node.nodeName
|
||||
|| node.hasOnlyProperties;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Root.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitRoot = function(block){
|
||||
this.buf = '';
|
||||
for (var i = 0, len = block.nodes.length; i < len; ++i) {
|
||||
var node = block.nodes[i];
|
||||
if (this.linenos || this.firebug) this.debugInfo(node);
|
||||
var ret = this.visit(node);
|
||||
if (ret) this.buf += this.out(ret + '\n', node);
|
||||
}
|
||||
return this.buf;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Block.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitBlock = function(block){
|
||||
var node
|
||||
, separator = this.compress ? '' : '\n'
|
||||
, needBrackets;
|
||||
|
||||
if (block.hasProperties && !block.lacksRenderedSelectors) {
|
||||
needBrackets = this.needBrackets(block.node);
|
||||
|
||||
if (needBrackets) {
|
||||
this.buf += this.out(this.compress ? '{' : ' {\n');
|
||||
++this.indents;
|
||||
}
|
||||
for (var i = 0, len = block.nodes.length; i < len; ++i) {
|
||||
this.last = len - 1 == i;
|
||||
node = block.nodes[i];
|
||||
switch (node.nodeName) {
|
||||
case 'null':
|
||||
case 'expression':
|
||||
case 'function':
|
||||
case 'group':
|
||||
case 'block':
|
||||
case 'unit':
|
||||
case 'media':
|
||||
case 'keyframes':
|
||||
case 'atrule':
|
||||
case 'supports':
|
||||
continue;
|
||||
// inline comments
|
||||
case !this.compress && node.inline && 'comment':
|
||||
this.buf = this.buf.slice(0, -1);
|
||||
this.buf += this.out(' ' + this.visit(node) + '\n', node);
|
||||
break;
|
||||
case 'property':
|
||||
var ret = this.visit(node) + separator;
|
||||
this.buf += this.compress ? ret : this.out(ret, node);
|
||||
break;
|
||||
default:
|
||||
this.buf += this.out(this.visit(node) + separator, node);
|
||||
}
|
||||
}
|
||||
if (needBrackets) {
|
||||
--this.indents;
|
||||
this.buf += this.out(this.indent + '}' + separator);
|
||||
}
|
||||
}
|
||||
|
||||
// Nesting
|
||||
for (var i = 0, len = block.nodes.length; i < len; ++i) {
|
||||
node = block.nodes[i];
|
||||
switch (node.nodeName) {
|
||||
case 'group':
|
||||
case 'block':
|
||||
case 'keyframes':
|
||||
if (this.linenos || this.firebug) this.debugInfo(node);
|
||||
this.visit(node);
|
||||
break;
|
||||
case 'media':
|
||||
case 'import':
|
||||
case 'atrule':
|
||||
case 'supports':
|
||||
this.visit(node);
|
||||
break;
|
||||
case 'comment':
|
||||
// only show unsuppressed comments
|
||||
if (!node.suppress) {
|
||||
this.buf += this.out(this.indent + this.visit(node) + '\n', node);
|
||||
}
|
||||
break;
|
||||
case 'charset':
|
||||
case 'literal':
|
||||
case 'namespace':
|
||||
this.buf += this.out(this.visit(node) + '\n', node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Keyframes.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitKeyframes = function(node){
|
||||
if (!node.frames) return;
|
||||
|
||||
var prefix = 'official' == node.prefix
|
||||
? ''
|
||||
: '-' + node.prefix + '-';
|
||||
|
||||
this.buf += this.out('@' + prefix + 'keyframes '
|
||||
+ this.visit(node.val)
|
||||
+ (this.compress ? '{' : ' {\n'), node);
|
||||
|
||||
this.keyframe = true;
|
||||
++this.indents;
|
||||
this.visit(node.block);
|
||||
--this.indents;
|
||||
this.keyframe = false;
|
||||
|
||||
this.buf += this.out('}' + (this.compress ? '' : '\n'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Media.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitMedia = function(media){
|
||||
var val = media.val;
|
||||
if (!media.hasOutput || !val.nodes.length) return;
|
||||
|
||||
this.buf += this.out('@media ', media);
|
||||
this.visit(val);
|
||||
this.buf += this.out(this.compress ? '{' : ' {\n');
|
||||
++this.indents;
|
||||
this.visit(media.block);
|
||||
--this.indents;
|
||||
this.buf += this.out('}' + (this.compress ? '' : '\n'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit QueryList.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitQueryList = function(queries){
|
||||
for (var i = 0, len = queries.nodes.length; i < len; ++i) {
|
||||
this.visit(queries.nodes[i]);
|
||||
if (len - 1 != i) this.buf += this.out(',' + (this.compress ? '' : ' '));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Query.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitQuery = function(node){
|
||||
var len = node.nodes.length;
|
||||
if (node.predicate) this.buf += this.out(node.predicate + ' ');
|
||||
if (node.type) this.buf += this.out(node.type + (len ? ' and ' : ''));
|
||||
for (var i = 0; i < len; ++i) {
|
||||
this.buf += this.out(this.visit(node.nodes[i]));
|
||||
if (len - 1 != i) this.buf += this.out(' and ');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Feature.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitFeature = function(node){
|
||||
if (!node.expr) {
|
||||
return node.name;
|
||||
} else if (node.expr.isEmpty) {
|
||||
return '(' + node.name + ')';
|
||||
} else {
|
||||
return '(' + node.name + ':' + (this.compress ? '' : ' ') + this.visit(node.expr) + ')';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Import.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitImport = function(imported){
|
||||
this.buf += this.out('@import ' + this.visit(imported.path) + ';\n', imported);
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Atrule.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitAtrule = function(atrule){
|
||||
var newline = this.compress ? '' : '\n';
|
||||
|
||||
this.buf += this.out(this.indent + '@' + atrule.type, atrule);
|
||||
|
||||
if (atrule.val) this.buf += this.out(' ' + atrule.val.trim());
|
||||
|
||||
if (atrule.block) {
|
||||
if (atrule.hasOnlyProperties) {
|
||||
this.visit(atrule.block);
|
||||
} else {
|
||||
this.buf += this.out(this.compress ? '{' : ' {\n');
|
||||
++this.indents;
|
||||
this.visit(atrule.block);
|
||||
--this.indents;
|
||||
this.buf += this.out(this.indent + '}' + newline);
|
||||
}
|
||||
} else {
|
||||
this.buf += this.out(';' + newline);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Supports.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitSupports = function(node){
|
||||
if (!node.hasOutput) return;
|
||||
|
||||
this.buf += this.out(this.indent + '@supports ', node);
|
||||
this.isCondition = true;
|
||||
this.buf += this.out(this.visit(node.condition));
|
||||
this.isCondition = false;
|
||||
this.buf += this.out(this.compress ? '{' : ' {\n');
|
||||
++this.indents;
|
||||
this.visit(node.block);
|
||||
--this.indents;
|
||||
this.buf += this.out(this.indent + '}' + (this.compress ? '' : '\n'));
|
||||
},
|
||||
|
||||
/**
|
||||
* Visit Comment.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitComment = function(comment){
|
||||
return this.compress
|
||||
? comment.suppress
|
||||
? ''
|
||||
: comment.str
|
||||
: comment.str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Function.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitFunction = function(fn){
|
||||
return fn.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Charset.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitCharset = function(charset){
|
||||
return '@charset ' + this.visit(charset.val) + ';';
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Namespace.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitNamespace = function(namespace){
|
||||
return '@namespace '
|
||||
+ (namespace.prefix ? this.visit(namespace.prefix) + ' ' : '')
|
||||
+ this.visit(namespace.val) + ';';
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Literal.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitLiteral = function(lit){
|
||||
var val = lit.val;
|
||||
if (lit.css) val = val.replace(/^ /gm, '');
|
||||
return val;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Boolean.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitBoolean = function(bool){
|
||||
return bool.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit RGBA.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitRGBA = function(rgba){
|
||||
return rgba.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit HSLA.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitHSLA = function(hsla){
|
||||
return hsla.rgba.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Unit.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitUnit = function(unit){
|
||||
var type = unit.type || ''
|
||||
, n = unit.val
|
||||
, float = n != (n | 0);
|
||||
|
||||
// Compress
|
||||
if (this.compress) {
|
||||
// Always return '0' unless the unit is a percentage or time
|
||||
if ('%' != type && 's' != type && 'ms' != type && 0 == n) return '0';
|
||||
// Omit leading '0' on floats
|
||||
if (float && n < 1 && n > -1) {
|
||||
return n.toString().replace('0.', '.') + type;
|
||||
}
|
||||
}
|
||||
|
||||
return (float ? parseFloat(n.toFixed(15)) : n).toString() + type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Group.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitGroup = function(group){
|
||||
var stack = this.keyframe ? [] : this.stack
|
||||
, comma = this.compress ? ',' : ',\n';
|
||||
|
||||
stack.push(group.nodes);
|
||||
|
||||
// selectors
|
||||
if (group.block.hasProperties) {
|
||||
var selectors = utils.compileSelectors.call(this, stack)
|
||||
, len = selectors.length;
|
||||
|
||||
if (len) {
|
||||
if (this.keyframe) comma = this.compress ? ',' : ', ';
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
var selector = selectors[i]
|
||||
, last = (i == len - 1);
|
||||
|
||||
// keyframe blocks (10%, 20% { ... })
|
||||
if (this.keyframe) selector = i ? selector.trim() : selector;
|
||||
|
||||
this.buf += this.out(selector + (last ? '' : comma), group.nodes[i]);
|
||||
}
|
||||
} else {
|
||||
group.block.lacksRenderedSelectors = true;
|
||||
}
|
||||
}
|
||||
|
||||
// output block
|
||||
this.visit(group.block);
|
||||
stack.pop();
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Ident.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitIdent = function(ident){
|
||||
return ident.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit String.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitString = function(string){
|
||||
return this.isURL
|
||||
? string.val
|
||||
: string.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Null.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitNull = function(node){
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Call.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitCall = function(call){
|
||||
this.isURL = 'url' == call.name;
|
||||
var args = call.args.nodes.map(function(arg){
|
||||
return this.visit(arg);
|
||||
}, this).join(this.compress ? ',' : ', ');
|
||||
if (this.isURL) args = '"' + args + '"';
|
||||
this.isURL = false;
|
||||
return call.name + '(' + args + ')';
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Expression.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitExpression = function(expr){
|
||||
var buf = []
|
||||
, self = this
|
||||
, len = expr.nodes.length
|
||||
, nodes = expr.nodes.map(function(node){ return self.visit(node); });
|
||||
|
||||
nodes.forEach(function(node, i){
|
||||
var last = i == len - 1;
|
||||
buf.push(node);
|
||||
if ('/' == nodes[i + 1] || '/' == node) return;
|
||||
if (last) return;
|
||||
|
||||
var space = self.isURL || (self.isCondition
|
||||
&& (')' == nodes[i + 1] || '(' == node))
|
||||
? '' : ' ';
|
||||
|
||||
buf.push(expr.isList
|
||||
? (self.compress ? ',' : ', ')
|
||||
: space);
|
||||
});
|
||||
|
||||
return buf.join('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Arguments.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitArguments = Compiler.prototype.visitExpression;
|
||||
|
||||
/**
|
||||
* Visit Property.
|
||||
*/
|
||||
|
||||
Compiler.prototype.visitProperty = function(prop){
|
||||
var val = this.visit(prop.expr).trim()
|
||||
, name = (prop.name || prop.segments.join(''))
|
||||
, arr = [];
|
||||
arr.push(
|
||||
this.out(this.indent),
|
||||
this.out(name + (this.compress ? ':' : ': '), prop),
|
||||
this.out(val, prop.expr),
|
||||
this.out(this.compress ? (this.last ? '' : ';') : ';')
|
||||
);
|
||||
return arr.join('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Debug info.
|
||||
*/
|
||||
|
||||
Compiler.prototype.debugInfo = function(node){
|
||||
|
||||
var path = node.filename == 'stdin' ? 'stdin' : fs.realpathSync(node.filename)
|
||||
, line = (node.nodes && node.nodes.length ? node.nodes[0].lineno : node.lineno) || 1;
|
||||
|
||||
if (this.linenos){
|
||||
this.buf += '\n/* ' + 'line ' + line + ' : ' + path + ' */\n';
|
||||
}
|
||||
|
||||
if (this.firebug){
|
||||
// debug info for firebug, the crazy formatting is needed
|
||||
path = 'file\\\:\\\/\\\/' + path.replace(/([.:/\\])/g, function(m) {
|
||||
return '\\' + (m === '\\' ? '\/' : m)
|
||||
});
|
||||
line = '\\00003' + line;
|
||||
this.buf += '\n@media -stylus-debug-info'
|
||||
+ '{filename{font-family:' + path
|
||||
+ '}line{font-family:' + line + '}}\n';
|
||||
}
|
||||
}
|
169
nodejs/node_modules/stylus/lib/visitor/deps-resolver.js
generated
vendored
Normal file
169
nodejs/node_modules/stylus/lib/visitor/deps-resolver.js
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Visitor = require('./')
|
||||
, Parser = require('../parser')
|
||||
, nodes = require('../nodes')
|
||||
, utils = require('../utils')
|
||||
, dirname = require('path').dirname
|
||||
, fs = require('fs');
|
||||
|
||||
/**
|
||||
* Initialize a new `DepsResolver` with the given `root` Node
|
||||
* and the `options`.
|
||||
*
|
||||
* @param {Node} root
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var DepsResolver = module.exports = function DepsResolver(root, options) {
|
||||
this.root = root;
|
||||
this.filename = options.filename;
|
||||
this.paths = options.paths || [];
|
||||
this.paths.push(dirname(options.filename || '.'));
|
||||
this.options = options;
|
||||
this.functions = {};
|
||||
this.deps = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `Visitor.prototype`.
|
||||
*/
|
||||
|
||||
DepsResolver.prototype.__proto__ = Visitor.prototype;
|
||||
|
||||
var visit = DepsResolver.prototype.visit;
|
||||
|
||||
DepsResolver.prototype.visit = function(node) {
|
||||
switch (node.nodeName) {
|
||||
case 'root':
|
||||
case 'block':
|
||||
case 'expression':
|
||||
this.visitRoot(node);
|
||||
break;
|
||||
case 'group':
|
||||
case 'media':
|
||||
case 'atblock':
|
||||
case 'atrule':
|
||||
case 'keyframes':
|
||||
case 'each':
|
||||
case 'supports':
|
||||
this.visit(node.block);
|
||||
break;
|
||||
default:
|
||||
visit.call(this, node);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Root.
|
||||
*/
|
||||
|
||||
DepsResolver.prototype.visitRoot = function(block) {
|
||||
for (var i = 0, len = block.nodes.length; i < len; ++i) {
|
||||
this.visit(block.nodes[i]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Ident.
|
||||
*/
|
||||
|
||||
DepsResolver.prototype.visitIdent = function(ident) {
|
||||
this.visit(ident.val);
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit If.
|
||||
*/
|
||||
|
||||
DepsResolver.prototype.visitIf = function(node) {
|
||||
this.visit(node.block);
|
||||
this.visit(node.cond);
|
||||
for (var i = 0, len = node.elses.length; i < len; ++i) {
|
||||
this.visit(node.elses[i]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Function.
|
||||
*/
|
||||
|
||||
DepsResolver.prototype.visitFunction = function(fn) {
|
||||
this.functions[fn.name] = fn.block;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Call.
|
||||
*/
|
||||
|
||||
DepsResolver.prototype.visitCall = function(call) {
|
||||
if (call.name in this.functions) this.visit(this.functions[call.name]);
|
||||
if (call.block) this.visit(call.block);
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Import.
|
||||
*/
|
||||
|
||||
DepsResolver.prototype.visitImport = function(node) {
|
||||
var path = node.path.first.val
|
||||
, literal, found, oldPath;
|
||||
|
||||
if (!path) return;
|
||||
|
||||
literal = /\.css(?:"|$)/.test(path);
|
||||
|
||||
// support optional .styl
|
||||
if (!literal && !/\.styl$/i.test(path)) {
|
||||
oldPath = path;
|
||||
path += '.styl';
|
||||
}
|
||||
|
||||
// Lookup
|
||||
found = utils.find(path, this.paths, this.filename);
|
||||
|
||||
// support optional index
|
||||
if (!found && oldPath) found = utils.lookupIndex(oldPath, this.paths, this.filename);
|
||||
|
||||
if (!found) return;
|
||||
|
||||
this.deps = this.deps.concat(found);
|
||||
|
||||
if (literal) return;
|
||||
|
||||
// nested imports
|
||||
for (var i = 0, len = found.length; i < len; ++i) {
|
||||
var file = found[i]
|
||||
, dir = dirname(file)
|
||||
, str = fs.readFileSync(file, 'utf-8')
|
||||
, block = new nodes.Block
|
||||
, parser = new Parser(str, utils.merge({ root: block }, this.options));
|
||||
|
||||
if (!~this.paths.indexOf(dir)) this.paths.push(dir);
|
||||
|
||||
try {
|
||||
block = parser.parse();
|
||||
} catch (err) {
|
||||
err.filename = file;
|
||||
err.lineno = parser.lexer.lineno;
|
||||
err.column = parser.lexer.column;
|
||||
err.input = str;
|
||||
throw err;
|
||||
}
|
||||
|
||||
this.visit(block);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get dependencies.
|
||||
*/
|
||||
|
||||
DepsResolver.prototype.resolve = function() {
|
||||
this.visit(this.root);
|
||||
return utils.uniq(this.deps);
|
||||
};
|
1596
nodejs/node_modules/stylus/lib/visitor/evaluator.js
generated
vendored
Normal file
1596
nodejs/node_modules/stylus/lib/visitor/evaluator.js
generated
vendored
Normal file
@@ -0,0 +1,1596 @@
|
||||
|
||||
/*!
|
||||
* Stylus - Evaluator
|
||||
* Copyright (c) Automattic <developer.wordpress.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Visitor = require('./')
|
||||
, units = require('../units')
|
||||
, nodes = require('../nodes')
|
||||
, Stack = require('../stack')
|
||||
, Frame = require('../stack/frame')
|
||||
, utils = require('../utils')
|
||||
, bifs = require('../functions')
|
||||
, dirname = require('path').dirname
|
||||
, colors = require('../colors')
|
||||
, debug = require('debug')('stylus:evaluator')
|
||||
, fs = require('fs');
|
||||
|
||||
/**
|
||||
* Import `file` and return Block node.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
function importFile(node, file, literal) {
|
||||
var importStack = this.importStack
|
||||
, Parser = require('../parser')
|
||||
, stat;
|
||||
|
||||
// Handling the `require`
|
||||
if (node.once) {
|
||||
if (this.requireHistory[file]) return nodes.null;
|
||||
this.requireHistory[file] = true;
|
||||
|
||||
if (literal && !this.includeCSS) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
// Expose imports
|
||||
node.path = file;
|
||||
node.dirname = dirname(file);
|
||||
// Store the modified time
|
||||
stat = fs.statSync(file);
|
||||
node.mtime = stat.mtime;
|
||||
this.paths.push(node.dirname);
|
||||
|
||||
// Avoid overflows from importing the same file over again
|
||||
if (~importStack.indexOf(file))
|
||||
throw new Error('import loop has been found');
|
||||
|
||||
if (this.options._imports) this.options._imports.push(node.clone());
|
||||
|
||||
// Parse the file
|
||||
importStack.push(file);
|
||||
nodes.filename = file;
|
||||
|
||||
var str = fs.readFileSync(file, 'utf8');
|
||||
if (literal) {
|
||||
literal = new nodes.Literal(str.replace(/\r\n?/g, '\n'));
|
||||
literal.lineno = literal.column = 1;
|
||||
if (!this.resolveURL) return literal;
|
||||
}
|
||||
|
||||
// parse
|
||||
var block = new nodes.Block
|
||||
, parser = new Parser(str, utils.merge({ root: block }, this.options));
|
||||
|
||||
try {
|
||||
block = parser.parse();
|
||||
} catch (err) {
|
||||
var line = parser.lexer.lineno
|
||||
, column = parser.lexer.column;
|
||||
|
||||
if (this.includeCSS && this.resolveURL) {
|
||||
this.warn('ParseError: ' + file + ':' + line + ':' + column + '. This file included as-is');
|
||||
return literal;
|
||||
} else {
|
||||
err.filename = file;
|
||||
err.lineno = line;
|
||||
err.column = column;
|
||||
err.input = str;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate imported "root"
|
||||
block = block.clone(this.currentBlock);
|
||||
block.parent = this.currentBlock;
|
||||
block.scope = false;
|
||||
var ret = this.visit(block);
|
||||
importStack.pop();
|
||||
if (!this.resolveURL || this.resolveURL.nocheck) this.paths.pop();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new `Evaluator` with the given `root` Node
|
||||
* and the following `options`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `compress` Compress the css output, defaults to false
|
||||
* - `warn` Warn the user of duplicate function definitions etc
|
||||
*
|
||||
* @param {Node} root
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Evaluator = module.exports = function Evaluator(root, options) {
|
||||
options = options || {};
|
||||
Visitor.call(this, root);
|
||||
var functions = this.functions = options.functions || {};
|
||||
this.stack = new Stack;
|
||||
this.imports = options.imports || [];
|
||||
this.globals = options.globals || {};
|
||||
this.paths = options.paths || [];
|
||||
this.prefix = options.prefix || '';
|
||||
this.filename = options.filename;
|
||||
this.includeCSS = options['include css'];
|
||||
this.resolveURL = functions.url
|
||||
&& 'resolver' == functions.url.name
|
||||
&& functions.url.options;
|
||||
this.paths.push(dirname(options.filename || '.'));
|
||||
this.stack.push(this.global = new Frame(root));
|
||||
this.warnings = options.warn;
|
||||
this.options = options;
|
||||
this.calling = []; // TODO: remove, use stack
|
||||
this.importStack = [];
|
||||
this.requireHistory = {};
|
||||
this.return = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `Visitor.prototype`.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.__proto__ = Visitor.prototype;
|
||||
|
||||
/**
|
||||
* Proxy visit to expose node line numbers.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Node}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var visit = Visitor.prototype.visit;
|
||||
Evaluator.prototype.visit = function(node){
|
||||
try {
|
||||
return visit.call(this, node);
|
||||
} catch (err) {
|
||||
if (err.filename) throw err;
|
||||
err.lineno = node.lineno;
|
||||
err.column = node.column;
|
||||
err.filename = node.filename;
|
||||
err.stylusStack = this.stack.toString();
|
||||
try {
|
||||
err.input = fs.readFileSync(err.filename, 'utf8');
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform evaluation setup:
|
||||
*
|
||||
* - populate global scope
|
||||
* - iterate imports
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.setup = function(){
|
||||
var root = this.root;
|
||||
var imports = [];
|
||||
|
||||
this.populateGlobalScope();
|
||||
this.imports.forEach(function(file){
|
||||
var expr = new nodes.Expression;
|
||||
expr.push(new nodes.String(file));
|
||||
imports.push(new nodes.Import(expr));
|
||||
}, this);
|
||||
|
||||
root.nodes = imports.concat(root.nodes);
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate the global scope with:
|
||||
*
|
||||
* - css colors
|
||||
* - user-defined globals
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.populateGlobalScope = function(){
|
||||
var scope = this.global.scope;
|
||||
|
||||
// colors
|
||||
Object.keys(colors).forEach(function(name){
|
||||
var color = colors[name]
|
||||
, rgba = new nodes.RGBA(color[0], color[1], color[2], color[3])
|
||||
, node = new nodes.Ident(name, rgba);
|
||||
rgba.name = name;
|
||||
scope.add(node);
|
||||
});
|
||||
|
||||
// user-defined globals
|
||||
var globals = this.globals;
|
||||
Object.keys(globals).forEach(function(name){
|
||||
scope.add(new nodes.Ident(name, globals[name]));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate the tree.
|
||||
*
|
||||
* @return {Node}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.evaluate = function(){
|
||||
debug('eval %s', this.filename);
|
||||
this.setup();
|
||||
return this.visit(this.root);
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Group.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitGroup = function(group){
|
||||
group.nodes = group.nodes.map(function(selector){
|
||||
selector.val = this.interpolate(selector);
|
||||
debug('ruleset %s', selector.val);
|
||||
return selector;
|
||||
}, this);
|
||||
|
||||
group.block = this.visit(group.block);
|
||||
return group;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Return.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitReturn = function(ret){
|
||||
ret.expr = this.visit(ret.expr);
|
||||
throw ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Media.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitMedia = function(media){
|
||||
media.block = this.visit(media.block);
|
||||
media.val = this.visit(media.val);
|
||||
return media;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit QueryList.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitQueryList = function(queries){
|
||||
var val, query;
|
||||
queries.nodes.forEach(this.visit, this);
|
||||
|
||||
if (1 == queries.nodes.length) {
|
||||
query = queries.nodes[0];
|
||||
if (val = this.lookup(query.type)) {
|
||||
val = val.first.string;
|
||||
if (!val) return queries;
|
||||
var Parser = require('../parser')
|
||||
, parser = new Parser(val, this.options);
|
||||
queries = this.visit(parser.queries());
|
||||
}
|
||||
}
|
||||
return queries;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Query.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitQuery = function(node){
|
||||
node.predicate = this.visit(node.predicate);
|
||||
node.type = this.visit(node.type);
|
||||
node.nodes.forEach(this.visit, this);
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Feature.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitFeature = function(node){
|
||||
node.name = this.interpolate(node);
|
||||
if (node.expr) {
|
||||
this.return++;
|
||||
node.expr = this.visit(node.expr);
|
||||
this.return--;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Object.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitObject = function(obj){
|
||||
for (var key in obj.vals) {
|
||||
obj.vals[key] = this.visit(obj.vals[key]);
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Member.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitMember = function(node){
|
||||
var left = node.left
|
||||
, right = node.right
|
||||
, obj = this.visit(left).first;
|
||||
|
||||
if ('object' != obj.nodeName) {
|
||||
throw new Error(left.toString() + ' has no property .' + right);
|
||||
}
|
||||
if (node.val) {
|
||||
this.return++;
|
||||
obj.set(right.name, this.visit(node.val));
|
||||
this.return--;
|
||||
}
|
||||
return obj.get(right.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Keyframes.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitKeyframes = function(keyframes){
|
||||
var val;
|
||||
if (keyframes.fabricated) return keyframes;
|
||||
keyframes.val = this.interpolate(keyframes).trim();
|
||||
if (val = this.lookup(keyframes.val)) {
|
||||
keyframes.val = val.first.string || val.first.name;
|
||||
}
|
||||
keyframes.block = this.visit(keyframes.block);
|
||||
|
||||
if ('official' != keyframes.prefix) return keyframes;
|
||||
|
||||
this.vendors.forEach(function(prefix){
|
||||
// IE never had prefixes for keyframes
|
||||
if ('ms' == prefix) return;
|
||||
var node = keyframes.clone();
|
||||
node.val = keyframes.val;
|
||||
node.prefix = prefix;
|
||||
node.block = keyframes.block;
|
||||
node.fabricated = true;
|
||||
this.currentBlock.push(node);
|
||||
}, this);
|
||||
|
||||
return nodes.null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Function.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitFunction = function(fn){
|
||||
// check local
|
||||
var local = this.stack.currentFrame.scope.lookup(fn.name);
|
||||
if (local) this.warn('local ' + local.nodeName + ' "' + fn.name + '" previously defined in this scope');
|
||||
|
||||
// user-defined
|
||||
var user = this.functions[fn.name];
|
||||
if (user) this.warn('user-defined function "' + fn.name + '" is already defined');
|
||||
|
||||
// BIF
|
||||
var bif = bifs[fn.name];
|
||||
if (bif) this.warn('built-in function "' + fn.name + '" is already defined');
|
||||
|
||||
return fn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Each.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitEach = function(each){
|
||||
this.return++;
|
||||
var expr = utils.unwrap(this.visit(each.expr))
|
||||
, len = expr.nodes.length
|
||||
, val = new nodes.Ident(each.val)
|
||||
, key = new nodes.Ident(each.key || '__index__')
|
||||
, scope = this.currentScope
|
||||
, block = this.currentBlock
|
||||
, vals = []
|
||||
, self = this
|
||||
, body
|
||||
, obj;
|
||||
this.return--;
|
||||
|
||||
each.block.scope = false;
|
||||
|
||||
function visitBody(key, val) {
|
||||
scope.add(val);
|
||||
scope.add(key);
|
||||
body = self.visit(each.block.clone());
|
||||
vals = vals.concat(body.nodes);
|
||||
}
|
||||
|
||||
// for prop in obj
|
||||
if (1 == len && 'object' == expr.nodes[0].nodeName) {
|
||||
obj = expr.nodes[0];
|
||||
for (var prop in obj.vals) {
|
||||
val.val = new nodes.String(prop);
|
||||
key.val = obj.get(prop);
|
||||
visitBody(key, val);
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < len; ++i) {
|
||||
val.val = expr.nodes[i];
|
||||
key.val = new nodes.Unit(i);
|
||||
visitBody(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
this.mixin(vals, block);
|
||||
return vals[vals.length - 1] || nodes.null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Call.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitCall = function(call){
|
||||
debug('call %s', call);
|
||||
var fn = this.lookup(call.name)
|
||||
, literal
|
||||
, ret;
|
||||
|
||||
// url()
|
||||
this.ignoreColors = 'url' == call.name;
|
||||
|
||||
// Variable function
|
||||
if (fn && 'expression' == fn.nodeName) {
|
||||
fn = fn.nodes[0];
|
||||
}
|
||||
|
||||
// Not a function? try user-defined or built-ins
|
||||
if (fn && 'function' != fn.nodeName) {
|
||||
fn = this.lookupFunction(call.name);
|
||||
}
|
||||
|
||||
// Undefined function? render literal CSS
|
||||
if (!fn || fn.nodeName != 'function') {
|
||||
debug('%s is undefined', call);
|
||||
// Special case for `calc`
|
||||
if ('calc' == this.unvendorize(call.name)) {
|
||||
literal = call.args.nodes && call.args.nodes[0];
|
||||
if (literal) ret = new nodes.Literal(call.name + literal);
|
||||
} else {
|
||||
ret = this.literalCall(call);
|
||||
}
|
||||
this.ignoreColors = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
this.calling.push(call.name);
|
||||
|
||||
// Massive stack
|
||||
if (this.calling.length > 200) {
|
||||
throw new RangeError('Maximum stylus call stack size exceeded');
|
||||
}
|
||||
|
||||
// First node in expression
|
||||
if ('expression' == fn.nodeName) fn = fn.first;
|
||||
|
||||
// Evaluate arguments
|
||||
this.return++;
|
||||
var args = this.visit(call.args);
|
||||
|
||||
for (var key in args.map) {
|
||||
args.map[key] = this.visit(args.map[key].clone());
|
||||
}
|
||||
this.return--;
|
||||
|
||||
// Built-in
|
||||
if (fn.fn) {
|
||||
debug('%s is built-in', call);
|
||||
ret = this.invokeBuiltin(fn.fn, args);
|
||||
// User-defined
|
||||
} else if ('function' == fn.nodeName) {
|
||||
debug('%s is user-defined', call);
|
||||
// Evaluate mixin block
|
||||
if (call.block) call.block = this.visit(call.block);
|
||||
ret = this.invokeFunction(fn, args, call.block);
|
||||
}
|
||||
|
||||
this.calling.pop();
|
||||
this.ignoreColors = false;
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Ident.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitIdent = function(ident){
|
||||
var prop;
|
||||
// Property lookup
|
||||
if (ident.property) {
|
||||
if (prop = this.lookupProperty(ident.name)) {
|
||||
return this.visit(prop.expr.clone());
|
||||
}
|
||||
return nodes.null;
|
||||
// Lookup
|
||||
} else if (ident.val.isNull) {
|
||||
var val = this.lookup(ident.name);
|
||||
// Object or Block mixin
|
||||
if (val && ident.mixin) this.mixinNode(val);
|
||||
return val ? this.visit(val) : ident;
|
||||
// Assign
|
||||
} else {
|
||||
this.return++;
|
||||
ident.val = this.visit(ident.val);
|
||||
this.return--;
|
||||
this.currentScope.add(ident);
|
||||
return ident.val;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit BinOp.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitBinOp = function(binop){
|
||||
// Special-case "is defined" pseudo binop
|
||||
if ('is defined' == binop.op) return this.isDefined(binop.left);
|
||||
|
||||
this.return++;
|
||||
// Visit operands
|
||||
var op = binop.op
|
||||
, left = this.visit(binop.left)
|
||||
, right = ('||' == op || '&&' == op)
|
||||
? binop.right : this.visit(binop.right);
|
||||
|
||||
// HACK: ternary
|
||||
var val = binop.val
|
||||
? this.visit(binop.val)
|
||||
: null;
|
||||
this.return--;
|
||||
|
||||
// Operate
|
||||
try {
|
||||
return this.visit(left.operate(op, right, val));
|
||||
} catch (err) {
|
||||
// disregard coercion issues in equality
|
||||
// checks, and simply return false
|
||||
if ('CoercionError' == err.name) {
|
||||
switch (op) {
|
||||
case '==':
|
||||
return nodes.false;
|
||||
case '!=':
|
||||
return nodes.true;
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit UnaryOp.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitUnaryOp = function(unary){
|
||||
var op = unary.op
|
||||
, node = this.visit(unary.expr);
|
||||
|
||||
if ('!' != op) {
|
||||
node = node.first.clone();
|
||||
utils.assertType(node, 'unit');
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case '-':
|
||||
node.val = -node.val;
|
||||
break;
|
||||
case '+':
|
||||
node.val = +node.val;
|
||||
break;
|
||||
case '~':
|
||||
node.val = ~node.val;
|
||||
break;
|
||||
case '!':
|
||||
return node.toBoolean().negate();
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit TernaryOp.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitTernary = function(ternary){
|
||||
var ok = this.visit(ternary.cond).toBoolean();
|
||||
return ok.isTrue
|
||||
? this.visit(ternary.trueExpr)
|
||||
: this.visit(ternary.falseExpr);
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Expression.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitExpression = function(expr){
|
||||
for (var i = 0, len = expr.nodes.length; i < len; ++i) {
|
||||
expr.nodes[i] = this.visit(expr.nodes[i]);
|
||||
}
|
||||
|
||||
// support (n * 5)px etc
|
||||
if (this.castable(expr)) expr = this.cast(expr);
|
||||
|
||||
return expr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Arguments.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitArguments = Evaluator.prototype.visitExpression;
|
||||
|
||||
/**
|
||||
* Visit Property.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitProperty = function(prop){
|
||||
var name = this.interpolate(prop)
|
||||
, fn = this.lookup(name)
|
||||
, call = fn && 'function' == fn.first.nodeName
|
||||
, literal = ~this.calling.indexOf(name)
|
||||
, _prop = this.property;
|
||||
|
||||
// Function of the same name
|
||||
if (call && !literal && !prop.literal) {
|
||||
var args = nodes.Arguments.fromExpression(utils.unwrap(prop.expr.clone()));
|
||||
prop.name = name;
|
||||
this.property = prop;
|
||||
this.return++;
|
||||
this.property.expr = this.visit(prop.expr);
|
||||
this.return--;
|
||||
var ret = this.visit(new nodes.Call(name, args));
|
||||
this.property = _prop;
|
||||
return ret;
|
||||
// Regular property
|
||||
} else {
|
||||
this.return++;
|
||||
prop.name = name;
|
||||
prop.literal = true;
|
||||
this.property = prop;
|
||||
prop.expr = this.visit(prop.expr);
|
||||
this.property = _prop;
|
||||
this.return--;
|
||||
return prop;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Root.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitRoot = function(block){
|
||||
// normalize cached imports
|
||||
if (block != this.root) {
|
||||
block.constructor = nodes.Block;
|
||||
return this.visit(block);
|
||||
}
|
||||
|
||||
for (var i = 0; i < block.nodes.length; ++i) {
|
||||
block.index = i;
|
||||
block.nodes[i] = this.visit(block.nodes[i]);
|
||||
}
|
||||
return block;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Block.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitBlock = function(block){
|
||||
this.stack.push(new Frame(block));
|
||||
for (block.index = 0; block.index < block.nodes.length; ++block.index) {
|
||||
try {
|
||||
block.nodes[block.index] = this.visit(block.nodes[block.index]);
|
||||
} catch (err) {
|
||||
if ('return' == err.nodeName) {
|
||||
if (this.return) {
|
||||
this.stack.pop();
|
||||
throw err;
|
||||
} else {
|
||||
block.nodes[block.index] = err;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.stack.pop();
|
||||
return block;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Atblock.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitAtblock = function(atblock){
|
||||
atblock.block = this.visit(atblock.block);
|
||||
return atblock;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Atrule.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitAtrule = function(atrule){
|
||||
atrule.val = this.interpolate(atrule);
|
||||
if (atrule.block) atrule.block = this.visit(atrule.block);
|
||||
return atrule;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Supports.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitSupports = function(node){
|
||||
var condition = node.condition
|
||||
, val;
|
||||
|
||||
this.return++;
|
||||
node.condition = this.visit(condition);
|
||||
this.return--;
|
||||
|
||||
val = condition.first;
|
||||
if (1 == condition.nodes.length
|
||||
&& 'string' == val.nodeName) {
|
||||
node.condition = val.string;
|
||||
}
|
||||
node.block = this.visit(node.block);
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit If.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitIf = function(node){
|
||||
var ret
|
||||
, block = this.currentBlock
|
||||
, negate = node.negate;
|
||||
|
||||
this.return++;
|
||||
var ok = this.visit(node.cond).first.toBoolean();
|
||||
this.return--;
|
||||
|
||||
node.block.scope = node.block.hasMedia;
|
||||
|
||||
// Evaluate body
|
||||
if (negate) {
|
||||
// unless
|
||||
if (ok.isFalse) {
|
||||
ret = this.visit(node.block);
|
||||
}
|
||||
} else {
|
||||
// if
|
||||
if (ok.isTrue) {
|
||||
ret = this.visit(node.block);
|
||||
// else
|
||||
} else if (node.elses.length) {
|
||||
var elses = node.elses
|
||||
, len = elses.length
|
||||
, cond;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
// else if
|
||||
if (elses[i].cond) {
|
||||
elses[i].block.scope = elses[i].block.hasMedia;
|
||||
this.return++;
|
||||
cond = this.visit(elses[i].cond).first.toBoolean();
|
||||
this.return--;
|
||||
if (cond.isTrue) {
|
||||
ret = this.visit(elses[i].block);
|
||||
break;
|
||||
}
|
||||
// else
|
||||
} else {
|
||||
elses[i].scope = elses[i].hasMedia;
|
||||
ret = this.visit(elses[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mixin conditional statements within
|
||||
// a selector group or at-rule
|
||||
if (ret && !node.postfix && block.node
|
||||
&& ~['group'
|
||||
, 'atrule'
|
||||
, 'media'
|
||||
, 'supports'
|
||||
, 'keyframes'].indexOf(block.node.nodeName)) {
|
||||
this.mixin(ret.nodes, block);
|
||||
return nodes.null;
|
||||
}
|
||||
|
||||
return ret || nodes.null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Extend.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitExtend = function(extend){
|
||||
var block = this.currentBlock;
|
||||
if ('group' != block.node.nodeName) block = this.closestGroup;
|
||||
extend.selectors.forEach(function(selector){
|
||||
block.node.extends.push({
|
||||
// Cloning the selector for when we are in a loop and don't want it to affect
|
||||
// the selector nodes and cause the values to be different to expected
|
||||
selector: this.interpolate(selector.clone()).trim(),
|
||||
optional: selector.optional,
|
||||
lineno: selector.lineno,
|
||||
column: selector.column
|
||||
});
|
||||
}, this);
|
||||
return nodes.null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Import.
|
||||
*/
|
||||
|
||||
Evaluator.prototype.visitImport = function(imported){
|
||||
this.return++;
|
||||
|
||||
var path = this.visit(imported.path).first
|
||||
, nodeName = imported.once ? 'require' : 'import'
|
||||
, found
|
||||
, literal;
|
||||
|
||||
this.return--;
|
||||
debug('import %s', path);
|
||||
|
||||
// url() passed
|
||||
if ('url' == path.name) {
|
||||
if (imported.once) throw new Error('You cannot @require a url');
|
||||
|
||||
return imported;
|
||||
}
|
||||
|
||||
// Ensure string
|
||||
if (!path.string) throw new Error('@' + nodeName + ' string expected');
|
||||
|
||||
var name = path = path.string;
|
||||
|
||||
// Absolute URL or hash
|
||||
if (/(?:url\s*\(\s*)?['"]?(?:#|(?:https?:)?\/\/)/i.test(path)) {
|
||||
if (imported.once) throw new Error('You cannot @require a url');
|
||||
return imported;
|
||||
}
|
||||
|
||||
// Literal
|
||||
if (/\.css(?:"|$)/.test(path)) {
|
||||
literal = true;
|
||||
if (!imported.once && !this.includeCSS) {
|
||||
return imported;
|
||||
}
|
||||
}
|
||||
|
||||
// support optional .styl
|
||||
if (!literal && !/\.styl$/i.test(path)) path += '.styl';
|
||||
|
||||
// Lookup
|
||||
found = utils.find(path, this.paths, this.filename);
|
||||
if (!found) {
|
||||
found = utils.lookupIndex(name, this.paths, this.filename);
|
||||
}
|
||||
|
||||
// Throw if import failed
|
||||
if (!found) throw new Error('failed to locate @' + nodeName + ' file ' + path);
|
||||
|
||||
var block = new nodes.Block;
|
||||
|
||||
for (var i = 0, len = found.length; i < len; ++i) {
|
||||
block.push(importFile.call(this, imported, found[i], literal));
|
||||
}
|
||||
|
||||
return block;
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke `fn` with `args`.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @param {Array} args
|
||||
* @return {Node}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.invokeFunction = function(fn, args, content){
|
||||
var block = new nodes.Block(fn.block.parent);
|
||||
|
||||
// Clone the function body
|
||||
// to prevent mutation of subsequent calls
|
||||
var body = fn.block.clone(block);
|
||||
|
||||
// mixin block
|
||||
var mixinBlock = this.stack.currentFrame.block;
|
||||
|
||||
// new block scope
|
||||
this.stack.push(new Frame(block));
|
||||
var scope = this.currentScope;
|
||||
|
||||
// normalize arguments
|
||||
if ('arguments' != args.nodeName) {
|
||||
var expr = new nodes.Expression;
|
||||
expr.push(args);
|
||||
args = nodes.Arguments.fromExpression(expr);
|
||||
}
|
||||
|
||||
// arguments local
|
||||
scope.add(new nodes.Ident('arguments', args));
|
||||
|
||||
// mixin scope introspection
|
||||
scope.add(new nodes.Ident('mixin', this.return
|
||||
? nodes.false
|
||||
: new nodes.String(mixinBlock.nodeName)));
|
||||
|
||||
// current property
|
||||
if (this.property) {
|
||||
var prop = this.propertyExpression(this.property, fn.name);
|
||||
scope.add(new nodes.Ident('current-property', prop));
|
||||
} else {
|
||||
scope.add(new nodes.Ident('current-property', nodes.null));
|
||||
}
|
||||
|
||||
// current call stack
|
||||
var expr = new nodes.Expression;
|
||||
for (var i = this.calling.length - 1; i-- ; ) {
|
||||
expr.push(new nodes.Literal(this.calling[i]));
|
||||
};
|
||||
scope.add(new nodes.Ident('called-from', expr));
|
||||
|
||||
// inject arguments as locals
|
||||
var i = 0
|
||||
, len = args.nodes.length;
|
||||
fn.params.nodes.forEach(function(node){
|
||||
// rest param support
|
||||
if (node.rest) {
|
||||
node.val = new nodes.Expression;
|
||||
for (; i < len; ++i) node.val.push(args.nodes[i]);
|
||||
node.val.preserve = true;
|
||||
node.val.isList = args.isList;
|
||||
// argument default support
|
||||
} else {
|
||||
var arg = args.map[node.name] || args.nodes[i++];
|
||||
node = node.clone();
|
||||
if (arg) {
|
||||
arg.isEmpty ? args.nodes[i - 1] = node.val : node.val = arg;
|
||||
} else {
|
||||
args.push(node.val);
|
||||
}
|
||||
|
||||
// required argument not satisfied
|
||||
if (node.val.isNull) {
|
||||
throw new Error('argument "' + node + '" required for ' + fn);
|
||||
}
|
||||
}
|
||||
|
||||
scope.add(node);
|
||||
});
|
||||
|
||||
// mixin block
|
||||
if (content) scope.add(new nodes.Ident('block', content, true));
|
||||
|
||||
// invoke
|
||||
return this.invoke(body, true, fn.filename);
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke built-in `fn` with `args`.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @param {Array} args
|
||||
* @return {Node}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.invokeBuiltin = function(fn, args){
|
||||
// Map arguments to first node
|
||||
// providing a nicer js api for
|
||||
// BIFs. Functions may specify that
|
||||
// they wish to accept full expressions
|
||||
// via .raw
|
||||
if (fn.raw) {
|
||||
args = args.nodes;
|
||||
} else {
|
||||
args = utils.params(fn).reduce(function(ret, param){
|
||||
var arg = args.map[param] || args.nodes.shift()
|
||||
if (arg) {
|
||||
arg = utils.unwrap(arg);
|
||||
var len = arg.nodes.length;
|
||||
if (len > 1) {
|
||||
for (var i = 0; i < len; ++i) {
|
||||
ret.push(utils.unwrap(arg.nodes[i].first));
|
||||
}
|
||||
} else {
|
||||
ret.push(arg.first);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}, []);
|
||||
}
|
||||
|
||||
// Invoke the BIF
|
||||
var body = utils.coerce(fn.apply(this, args));
|
||||
|
||||
// Always wrapping allows js functions
|
||||
// to return several values with a single
|
||||
// Expression node
|
||||
var expr = new nodes.Expression;
|
||||
expr.push(body);
|
||||
body = expr;
|
||||
|
||||
// Invoke
|
||||
return this.invoke(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke the given function `body`.
|
||||
*
|
||||
* @param {Block} body
|
||||
* @return {Node}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.invoke = function(body, stack, filename){
|
||||
var self = this
|
||||
, ret;
|
||||
|
||||
if (filename) this.paths.push(dirname(filename));
|
||||
|
||||
// Return
|
||||
if (this.return) {
|
||||
ret = this.eval(body.nodes);
|
||||
if (stack) this.stack.pop();
|
||||
// Mixin
|
||||
} else {
|
||||
body = this.visit(body);
|
||||
if (stack) this.stack.pop();
|
||||
this.mixin(body.nodes, this.currentBlock);
|
||||
ret = nodes.null;
|
||||
}
|
||||
|
||||
if (filename) this.paths.pop();
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Mixin the given `nodes` to the given `block`.
|
||||
*
|
||||
* @param {Array} nodes
|
||||
* @param {Block} block
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.mixin = function(nodes, block){
|
||||
if (!nodes.length) return;
|
||||
var len = block.nodes.length
|
||||
, head = block.nodes.slice(0, block.index)
|
||||
, tail = block.nodes.slice(block.index + 1, len);
|
||||
this._mixin(nodes, head, block);
|
||||
block.index = 0;
|
||||
block.nodes = head.concat(tail);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mixin the given `items` to the `dest` array.
|
||||
*
|
||||
* @param {Array} items
|
||||
* @param {Array} dest
|
||||
* @param {Block} block
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype._mixin = function(items, dest, block){
|
||||
var node
|
||||
, len = items.length;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
switch ((node = items[i]).nodeName) {
|
||||
case 'return':
|
||||
return;
|
||||
case 'block':
|
||||
this._mixin(node.nodes, dest, block);
|
||||
break;
|
||||
case 'media':
|
||||
// fix link to the parent block
|
||||
var parentNode = node.block.parent.node;
|
||||
if (parentNode && 'call' != parentNode.nodeName) {
|
||||
node.block.parent = block;
|
||||
}
|
||||
case 'property':
|
||||
var val = node.expr;
|
||||
// prevent `block` mixin recursion
|
||||
if (node.literal && 'block' == val.first.name) {
|
||||
val = utils.unwrap(val);
|
||||
val.nodes[0] = new nodes.Literal('block');
|
||||
}
|
||||
default:
|
||||
dest.push(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mixin the given `node` to the current block.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.mixinNode = function(node){
|
||||
node = this.visit(node.first);
|
||||
switch (node.nodeName) {
|
||||
case 'object':
|
||||
this.mixinObject(node);
|
||||
return nodes.null;
|
||||
case 'block':
|
||||
case 'atblock':
|
||||
this.mixin(node.nodes, this.currentBlock);
|
||||
return nodes.null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mixin the given `object` to the current block.
|
||||
*
|
||||
* @param {Object} object
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.mixinObject = function(object){
|
||||
var Parser = require('../parser')
|
||||
, root = this.root
|
||||
, str = '$block ' + object.toBlock()
|
||||
, parser = new Parser(str, utils.merge({ root: block }, this.options))
|
||||
, block;
|
||||
|
||||
try {
|
||||
block = parser.parse();
|
||||
} catch (err) {
|
||||
err.filename = this.filename;
|
||||
err.lineno = parser.lexer.lineno;
|
||||
err.column = parser.lexer.column;
|
||||
err.input = str;
|
||||
throw err;
|
||||
}
|
||||
|
||||
block.parent = root;
|
||||
block.scope = false;
|
||||
var ret = this.visit(block)
|
||||
, vals = ret.first.nodes;
|
||||
for (var i = 0, len = vals.length; i < len; ++i) {
|
||||
if (vals[i].block) {
|
||||
this.mixin(vals[i].block.nodes, this.currentBlock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate the given `vals`.
|
||||
*
|
||||
* @param {Array} vals
|
||||
* @return {Node}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.eval = function(vals){
|
||||
if (!vals) return nodes.null;
|
||||
var len = vals.length
|
||||
, node = nodes.null;
|
||||
|
||||
try {
|
||||
for (var i = 0; i < len; ++i) {
|
||||
node = vals[i];
|
||||
switch (node.nodeName) {
|
||||
case 'if':
|
||||
if ('block' != node.block.nodeName) {
|
||||
node = this.visit(node);
|
||||
break;
|
||||
}
|
||||
case 'each':
|
||||
case 'block':
|
||||
node = this.visit(node);
|
||||
if (node.nodes) node = this.eval(node.nodes);
|
||||
break;
|
||||
default:
|
||||
node = this.visit(node);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if ('return' == err.nodeName) {
|
||||
return err.expr;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Literal function `call`.
|
||||
*
|
||||
* @param {Call} call
|
||||
* @return {call}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.literalCall = function(call){
|
||||
call.args = this.visit(call.args);
|
||||
return call;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup property `name`.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Property}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.lookupProperty = function(name){
|
||||
var i = this.stack.length
|
||||
, index = this.currentBlock.index
|
||||
, top = i
|
||||
, nodes
|
||||
, block
|
||||
, len
|
||||
, other;
|
||||
|
||||
while (i--) {
|
||||
block = this.stack[i].block;
|
||||
if (!block.node) continue;
|
||||
switch (block.node.nodeName) {
|
||||
case 'group':
|
||||
case 'function':
|
||||
case 'if':
|
||||
case 'each':
|
||||
case 'atrule':
|
||||
case 'media':
|
||||
case 'atblock':
|
||||
case 'call':
|
||||
nodes = block.nodes;
|
||||
// scan siblings from the property index up
|
||||
if (i + 1 == top) {
|
||||
while (index--) {
|
||||
// ignore current property
|
||||
if (this.property == nodes[index]) continue;
|
||||
other = this.interpolate(nodes[index]);
|
||||
if (name == other) return nodes[index].clone();
|
||||
}
|
||||
// sequential lookup for non-siblings (for now)
|
||||
} else {
|
||||
len = nodes.length;
|
||||
while (len--) {
|
||||
if ('property' != nodes[len].nodeName
|
||||
|| this.property == nodes[len]) continue;
|
||||
other = this.interpolate(nodes[len]);
|
||||
if (name == other) return nodes[len].clone();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nodes.null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the closest mixin-able `Block`.
|
||||
*
|
||||
* @return {Block}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.__defineGetter__('closestBlock', function(){
|
||||
var i = this.stack.length
|
||||
, block;
|
||||
while (i--) {
|
||||
block = this.stack[i].block;
|
||||
if (block.node) {
|
||||
switch (block.node.nodeName) {
|
||||
case 'group':
|
||||
case 'keyframes':
|
||||
case 'atrule':
|
||||
case 'atblock':
|
||||
case 'media':
|
||||
case 'call':
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the closest group block.
|
||||
*
|
||||
* @return {Block}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.__defineGetter__('closestGroup', function(){
|
||||
var i = this.stack.length
|
||||
, block;
|
||||
while (i--) {
|
||||
block = this.stack[i].block;
|
||||
if (block.node && 'group' == block.node.nodeName) {
|
||||
return block;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the current selectors stack.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.__defineGetter__('selectorStack', function(){
|
||||
var block
|
||||
, stack = [];
|
||||
for (var i = 0, len = this.stack.length; i < len; ++i) {
|
||||
block = this.stack[i].block;
|
||||
if (block.node && 'group' == block.node.nodeName) {
|
||||
block.node.nodes.forEach(function(selector) {
|
||||
if (!selector.val) selector.val = this.interpolate(selector);
|
||||
}, this);
|
||||
stack.push(block.node.nodes);
|
||||
}
|
||||
}
|
||||
return stack;
|
||||
});
|
||||
|
||||
/**
|
||||
* Lookup `name`, with support for JavaScript
|
||||
* functions, and BIFs.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Node}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.lookup = function(name){
|
||||
var val;
|
||||
if (this.ignoreColors && name in colors) return;
|
||||
if (val = this.stack.lookup(name)) {
|
||||
return utils.unwrap(val);
|
||||
} else {
|
||||
return this.lookupFunction(name);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Map segments in `node` returning a string.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.interpolate = function(node){
|
||||
var self = this
|
||||
, isSelector = ('selector' == node.nodeName);
|
||||
function toString(node) {
|
||||
switch (node.nodeName) {
|
||||
case 'function':
|
||||
case 'ident':
|
||||
return node.name;
|
||||
case 'literal':
|
||||
case 'string':
|
||||
if (self.prefix && !node.prefixed && !node.val.nodeName) {
|
||||
node.val = node.val.replace(/\./g, '.' + self.prefix);
|
||||
node.prefixed = true;
|
||||
}
|
||||
return node.val;
|
||||
case 'unit':
|
||||
// Interpolation inside keyframes
|
||||
return '%' == node.type ? node.val + '%' : node.val;
|
||||
case 'member':
|
||||
return toString(self.visit(node));
|
||||
case 'expression':
|
||||
// Prevent cyclic `selector()` calls.
|
||||
if (self.calling && ~self.calling.indexOf('selector') && self._selector) return self._selector;
|
||||
self.return++;
|
||||
var ret = toString(self.visit(node).first);
|
||||
self.return--;
|
||||
if (isSelector) self._selector = ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.segments) {
|
||||
return node.segments.map(toString).join('');
|
||||
} else {
|
||||
return toString(node);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup JavaScript user-defined or built-in function.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.lookupFunction = function(name){
|
||||
var fn = this.functions[name] || bifs[name];
|
||||
if (fn) return new nodes.Function(name, fn);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given `node` is an ident, and if it is defined.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.isDefined = function(node){
|
||||
if ('ident' == node.nodeName) {
|
||||
return nodes.Boolean(this.lookup(node.name));
|
||||
} else {
|
||||
throw new Error('invalid "is defined" check on non-variable ' + node);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return `Expression` based on the given `prop`,
|
||||
* replacing cyclic calls to the given function `name`
|
||||
* with "__CALL__".
|
||||
*
|
||||
* @param {Property} prop
|
||||
* @param {String} name
|
||||
* @return {Expression}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.propertyExpression = function(prop, name){
|
||||
var expr = new nodes.Expression
|
||||
, val = prop.expr.clone();
|
||||
|
||||
// name
|
||||
expr.push(new nodes.String(prop.name));
|
||||
|
||||
// replace cyclic call with __CALL__
|
||||
function replace(node) {
|
||||
if ('call' == node.nodeName && name == node.name) {
|
||||
return new nodes.Literal('__CALL__');
|
||||
}
|
||||
|
||||
if (node.nodes) node.nodes = node.nodes.map(replace);
|
||||
return node;
|
||||
}
|
||||
|
||||
replace(val);
|
||||
expr.push(val);
|
||||
return expr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cast `expr` to the trailing ident.
|
||||
*
|
||||
* @param {Expression} expr
|
||||
* @return {Unit}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.cast = function(expr){
|
||||
return new nodes.Unit(expr.first.val, expr.nodes[1].name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `expr` is castable.
|
||||
*
|
||||
* @param {Expression} expr
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.castable = function(expr){
|
||||
return 2 == expr.nodes.length
|
||||
&& 'unit' == expr.first.nodeName
|
||||
&& ~units.indexOf(expr.nodes[1].name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Warn with the given `msg`.
|
||||
*
|
||||
* @param {String} msg
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.warn = function(msg){
|
||||
if (!this.warnings) return;
|
||||
console.warn('\u001b[33mWarning:\u001b[0m ' + msg);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the current `Block`.
|
||||
*
|
||||
* @return {Block}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.__defineGetter__('currentBlock', function(){
|
||||
return this.stack.currentFrame.block;
|
||||
});
|
||||
|
||||
/**
|
||||
* Return an array of vendor names.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.__defineGetter__('vendors', function(){
|
||||
return this.lookup('vendors').nodes.map(function(node){
|
||||
return node.string;
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the property name without vendor prefix.
|
||||
*
|
||||
* @param {String} prop
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Evaluator.prototype.unvendorize = function(prop){
|
||||
for (var i = 0, len = this.vendors.length; i < len; i++) {
|
||||
if ('official' != this.vendors[i]) {
|
||||
var vendor = '-' + this.vendors[i] + '-';
|
||||
if (~prop.indexOf(vendor)) return prop.replace(vendor, '');
|
||||
}
|
||||
}
|
||||
return prop;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the current frame `Scope`.
|
||||
*
|
||||
* @return {Scope}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.__defineGetter__('currentScope', function(){
|
||||
return this.stack.currentFrame.scope;
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the current `Frame`.
|
||||
*
|
||||
* @return {Frame}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Evaluator.prototype.__defineGetter__('currentFrame', function(){
|
||||
return this.stack.currentFrame;
|
||||
});
|
31
nodejs/node_modules/stylus/lib/visitor/index.js
generated
vendored
Normal file
31
nodejs/node_modules/stylus/lib/visitor/index.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
/*!
|
||||
* Stylus - Visitor
|
||||
* Copyright (c) Automattic <developer.wordpress.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize a new `Visitor` with the given `root` Node.
|
||||
*
|
||||
* @param {Node} root
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Visitor = module.exports = function Visitor(root) {
|
||||
this.root = root;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit the given `node`.
|
||||
*
|
||||
* @param {Node|Array} node
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Visitor.prototype.visit = function(node, fn){
|
||||
var method = 'visit' + node.constructor.name;
|
||||
if (this[method]) return this[method](node);
|
||||
return node;
|
||||
};
|
||||
|
424
nodejs/node_modules/stylus/lib/visitor/normalizer.js
generated
vendored
Normal file
424
nodejs/node_modules/stylus/lib/visitor/normalizer.js
generated
vendored
Normal file
@@ -0,0 +1,424 @@
|
||||
|
||||
/*!
|
||||
* Stylus - Normalizer
|
||||
* Copyright (c) Automattic <developer.wordpress.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Visitor = require('./')
|
||||
, nodes = require('../nodes')
|
||||
, utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Initialize a new `Normalizer` with the given `root` Node.
|
||||
*
|
||||
* This visitor implements the first stage of the duel-stage
|
||||
* compiler, tasked with stripping the "garbage" from
|
||||
* the evaluated nodes, ditching null rules, resolving
|
||||
* ruleset selectors etc. This step performs the logic
|
||||
* necessary to facilitate the "@extend" functionality,
|
||||
* as these must be resolved _before_ buffering output.
|
||||
*
|
||||
* @param {Node} root
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var Normalizer = module.exports = function Normalizer(root, options) {
|
||||
options = options || {};
|
||||
Visitor.call(this, root);
|
||||
this.hoist = options['hoist atrules'];
|
||||
this.stack = [];
|
||||
this.map = {};
|
||||
this.imports = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `Visitor.prototype`.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.__proto__ = Visitor.prototype;
|
||||
|
||||
/**
|
||||
* Normalize the node tree.
|
||||
*
|
||||
* @return {Node}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Normalizer.prototype.normalize = function(){
|
||||
var ret = this.visit(this.root);
|
||||
|
||||
if (this.hoist) {
|
||||
// hoist @import
|
||||
if (this.imports.length) ret.nodes = this.imports.concat(ret.nodes);
|
||||
|
||||
// hoist @charset
|
||||
if (this.charset) ret.nodes = [this.charset].concat(ret.nodes);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bubble up the given `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Normalizer.prototype.bubble = function(node){
|
||||
var props = []
|
||||
, other = []
|
||||
, self = this;
|
||||
|
||||
function filterProps(block) {
|
||||
block.nodes.forEach(function(node) {
|
||||
node = self.visit(node);
|
||||
|
||||
switch (node.nodeName) {
|
||||
case 'property':
|
||||
props.push(node);
|
||||
break;
|
||||
case 'block':
|
||||
filterProps(node);
|
||||
break;
|
||||
default:
|
||||
other.push(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
filterProps(node.block);
|
||||
|
||||
if (props.length) {
|
||||
var selector = new nodes.Selector([new nodes.Literal('&')]);
|
||||
selector.lineno = node.lineno;
|
||||
selector.column = node.column;
|
||||
selector.filename = node.filename;
|
||||
selector.val = '&';
|
||||
|
||||
var group = new nodes.Group;
|
||||
group.lineno = node.lineno;
|
||||
group.column = node.column;
|
||||
group.filename = node.filename;
|
||||
|
||||
var block = new nodes.Block(node.block, group);
|
||||
block.lineno = node.lineno;
|
||||
block.column = node.column;
|
||||
block.filename = node.filename;
|
||||
|
||||
props.forEach(function(prop){
|
||||
block.push(prop);
|
||||
});
|
||||
|
||||
group.push(selector);
|
||||
group.block = block;
|
||||
|
||||
node.block.nodes = [];
|
||||
node.block.push(group);
|
||||
other.forEach(function(n){
|
||||
node.block.push(n);
|
||||
});
|
||||
|
||||
var group = this.closestGroup(node.block);
|
||||
if (group) node.group = group.clone();
|
||||
|
||||
node.bubbled = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return group closest to the given `block`.
|
||||
*
|
||||
* @param {Block} block
|
||||
* @return {Group}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Normalizer.prototype.closestGroup = function(block){
|
||||
var parent = block.parent
|
||||
, node;
|
||||
while (parent && (node = parent.node)) {
|
||||
if ('group' == node.nodeName) return node;
|
||||
parent = node.block && node.block.parent;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Root.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitRoot = function(block){
|
||||
var ret = new nodes.Root
|
||||
, node;
|
||||
|
||||
for (var i = 0; i < block.nodes.length; ++i) {
|
||||
node = block.nodes[i];
|
||||
switch (node.nodeName) {
|
||||
case 'null':
|
||||
case 'expression':
|
||||
case 'function':
|
||||
case 'unit':
|
||||
case 'atblock':
|
||||
continue;
|
||||
default:
|
||||
this.rootIndex = i;
|
||||
ret.push(this.visit(node));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Property.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitProperty = function(prop){
|
||||
this.visit(prop.expr);
|
||||
return prop;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Expression.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitExpression = function(expr){
|
||||
expr.nodes = expr.nodes.map(function(node){
|
||||
// returns `block` literal if mixin's block
|
||||
// is used as part of a property value
|
||||
if ('block' == node.nodeName) {
|
||||
var literal = new nodes.Literal('block');
|
||||
literal.lineno = expr.lineno;
|
||||
literal.column = expr.column;
|
||||
return literal;
|
||||
}
|
||||
return node;
|
||||
});
|
||||
return expr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Block.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitBlock = function(block){
|
||||
var node;
|
||||
|
||||
if (block.hasProperties) {
|
||||
for (var i = 0, len = block.nodes.length; i < len; ++i) {
|
||||
node = block.nodes[i];
|
||||
switch (node.nodeName) {
|
||||
case 'null':
|
||||
case 'expression':
|
||||
case 'function':
|
||||
case 'group':
|
||||
case 'unit':
|
||||
case 'atblock':
|
||||
continue;
|
||||
default:
|
||||
block.nodes[i] = this.visit(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nesting
|
||||
for (var i = 0, len = block.nodes.length; i < len; ++i) {
|
||||
node = block.nodes[i];
|
||||
block.nodes[i] = this.visit(node);
|
||||
}
|
||||
|
||||
return block;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Group.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitGroup = function(group){
|
||||
var stack = this.stack
|
||||
, map = this.map
|
||||
, parts;
|
||||
|
||||
// normalize interpolated selectors with comma
|
||||
group.nodes.forEach(function(selector, i){
|
||||
if (!~selector.val.indexOf(',')) return;
|
||||
if (~selector.val.indexOf('\\,')) {
|
||||
selector.val = selector.val.replace(/\\,/g, ',');
|
||||
return;
|
||||
}
|
||||
parts = selector.val.split(',');
|
||||
var root = '/' == selector.val.charAt(0)
|
||||
, part, s;
|
||||
for (var k = 0, len = parts.length; k < len; ++k){
|
||||
part = parts[k].trim();
|
||||
if (root && k > 0 && !~part.indexOf('&')) {
|
||||
part = '/' + part;
|
||||
}
|
||||
s = new nodes.Selector([new nodes.Literal(part)]);
|
||||
s.val = part;
|
||||
s.block = group.block;
|
||||
group.nodes[i++] = s;
|
||||
}
|
||||
});
|
||||
stack.push(group.nodes);
|
||||
|
||||
var selectors = utils.compileSelectors(stack, true);
|
||||
|
||||
// map for extension lookup
|
||||
selectors.forEach(function(selector){
|
||||
map[selector] = map[selector] || [];
|
||||
map[selector].push(group);
|
||||
});
|
||||
|
||||
// extensions
|
||||
this.extend(group, selectors);
|
||||
|
||||
stack.pop();
|
||||
return group;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Function.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitFunction = function(){
|
||||
return nodes.null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Media.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitMedia = function(media){
|
||||
var medias = []
|
||||
, group = this.closestGroup(media.block)
|
||||
, parent;
|
||||
|
||||
function mergeQueries(block) {
|
||||
block.nodes.forEach(function(node, i){
|
||||
switch (node.nodeName) {
|
||||
case 'media':
|
||||
node.val = media.val.merge(node.val);
|
||||
medias.push(node);
|
||||
block.nodes[i] = nodes.null;
|
||||
break;
|
||||
case 'block':
|
||||
mergeQueries(node);
|
||||
break;
|
||||
default:
|
||||
if (node.block && node.block.nodes)
|
||||
mergeQueries(node.block);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mergeQueries(media.block);
|
||||
this.bubble(media);
|
||||
|
||||
if (medias.length) {
|
||||
medias.forEach(function(node){
|
||||
if (group) {
|
||||
group.block.push(node);
|
||||
} else {
|
||||
this.root.nodes.splice(++this.rootIndex, 0, node);
|
||||
}
|
||||
node = this.visit(node);
|
||||
parent = node.block.parent;
|
||||
if (node.bubbled && (!group || 'group' == parent.node.nodeName)) {
|
||||
node.group.block = node.block.nodes[0].block;
|
||||
node.block.nodes[0] = node.group;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
return media;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Supports.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitSupports = function(node){
|
||||
this.bubble(node);
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Atrule.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitAtrule = function(node){
|
||||
if (node.block) node.block = this.visit(node.block);
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Keyframes.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitKeyframes = function(node){
|
||||
var frames = node.block.nodes.filter(function(frame){
|
||||
return frame.block && frame.block.hasProperties;
|
||||
});
|
||||
node.frames = frames.length;
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Import.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitImport = function(node){
|
||||
this.imports.push(node);
|
||||
return this.hoist ? nodes.null : node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Charset.
|
||||
*/
|
||||
|
||||
Normalizer.prototype.visitCharset = function(node){
|
||||
this.charset = node;
|
||||
return this.hoist ? nodes.null : node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply `group` extensions.
|
||||
*
|
||||
* @param {Group} group
|
||||
* @param {Array} selectors
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Normalizer.prototype.extend = function(group, selectors){
|
||||
var map = this.map
|
||||
, self = this
|
||||
, parent = this.closestGroup(group.block);
|
||||
|
||||
group.extends.forEach(function(extend){
|
||||
var groups = map[extend.selector];
|
||||
if (!groups) {
|
||||
if (extend.optional) return;
|
||||
var err = new Error('Failed to @extend "' + extend.selector + '"');
|
||||
err.lineno = extend.lineno;
|
||||
err.column = extend.column;
|
||||
throw err;
|
||||
}
|
||||
selectors.forEach(function(selector){
|
||||
var node = new nodes.Selector;
|
||||
node.val = selector;
|
||||
node.inherits = false;
|
||||
groups.forEach(function(group){
|
||||
// prevent recursive extend
|
||||
if (!parent || (parent != group)) self.extend(group, selectors);
|
||||
group.push(node);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group.block = this.visit(group.block);
|
||||
};
|
202
nodejs/node_modules/stylus/lib/visitor/sourcemapper.js
generated
vendored
Normal file
202
nodejs/node_modules/stylus/lib/visitor/sourcemapper.js
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
/*!
|
||||
* Stylus - SourceMapper
|
||||
* Copyright (c) Automattic <developer.wordpress.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Compiler = require('./compiler')
|
||||
, SourceMapGenerator = require('source-map').SourceMapGenerator
|
||||
, basename = require('path').basename
|
||||
, extname = require('path').extname
|
||||
, dirname = require('path').dirname
|
||||
, join = require('path').join
|
||||
, relative = require('path').relative
|
||||
, sep = require('path').sep
|
||||
, fs = require('fs');
|
||||
|
||||
/**
|
||||
* Initialize a new `SourceMapper` generator with the given `root` Node
|
||||
* and the following `options`.
|
||||
*
|
||||
* @param {Node} root
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var SourceMapper = module.exports = function SourceMapper(root, options){
|
||||
options = options || {};
|
||||
this.column = 1;
|
||||
this.lineno = 1;
|
||||
this.contents = {};
|
||||
this.filename = options.filename;
|
||||
this.dest = options.dest;
|
||||
|
||||
var sourcemap = options.sourcemap;
|
||||
this.basePath = sourcemap.basePath || '.';
|
||||
this.inline = sourcemap.inline;
|
||||
this.comment = sourcemap.comment;
|
||||
if (extname(this.dest) === '.css') {
|
||||
this.basename = basename(this.dest);
|
||||
} else {
|
||||
this.basename = basename(this.filename, extname(this.filename)) + '.css';
|
||||
}
|
||||
this.utf8 = false;
|
||||
|
||||
this.map = new SourceMapGenerator({
|
||||
file: this.basename,
|
||||
sourceRoot: sourcemap.sourceRoot || null
|
||||
});
|
||||
Compiler.call(this, root, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `Compiler.prototype`.
|
||||
*/
|
||||
|
||||
SourceMapper.prototype.__proto__ = Compiler.prototype;
|
||||
|
||||
/**
|
||||
* Generate and write source map.
|
||||
*
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var compile = Compiler.prototype.compile;
|
||||
SourceMapper.prototype.compile = function(){
|
||||
var css = compile.call(this)
|
||||
, out = this.basename + '.map'
|
||||
, url = this.normalizePath(this.dest
|
||||
? join(this.dest, out)
|
||||
: join(dirname(this.filename), out))
|
||||
, map;
|
||||
|
||||
if (this.inline) {
|
||||
map = this.map.toString();
|
||||
url = 'data:application/json;'
|
||||
+ (this.utf8 ? 'charset=utf-8;' : '') + 'base64,'
|
||||
+ new Buffer(map).toString('base64');
|
||||
}
|
||||
if (this.inline || false !== this.comment)
|
||||
css += '/*# sourceMappingURL=' + url + ' */';
|
||||
return css;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add mapping information.
|
||||
*
|
||||
* @param {String} str
|
||||
* @param {Node} node
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
SourceMapper.prototype.out = function(str, node){
|
||||
if (node && node.lineno) {
|
||||
var filename = this.normalizePath(node.filename);
|
||||
|
||||
this.map.addMapping({
|
||||
original: {
|
||||
line: node.lineno,
|
||||
column: node.column - 1
|
||||
},
|
||||
generated: {
|
||||
line: this.lineno,
|
||||
column: this.column - 1
|
||||
},
|
||||
source: filename
|
||||
});
|
||||
|
||||
if (this.inline && !this.contents[filename]) {
|
||||
this.map.setSourceContent(filename, fs.readFileSync(node.filename, 'utf-8'));
|
||||
this.contents[filename] = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.move(str);
|
||||
return str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Move current line and column position.
|
||||
*
|
||||
* @param {String} str
|
||||
* @api private
|
||||
*/
|
||||
|
||||
SourceMapper.prototype.move = function(str){
|
||||
var lines = str.match(/\n/g)
|
||||
, idx = str.lastIndexOf('\n');
|
||||
|
||||
if (lines) this.lineno += lines.length;
|
||||
this.column = ~idx
|
||||
? str.length - idx
|
||||
: this.column + str.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize the given `path`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
SourceMapper.prototype.normalizePath = function(path){
|
||||
path = relative(this.dest || this.basePath, path);
|
||||
if ('\\' == sep) {
|
||||
path = path.replace(/^[a-z]:\\/i, '/')
|
||||
.replace(/\\/g, '/');
|
||||
}
|
||||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Literal.
|
||||
*/
|
||||
|
||||
var literal = Compiler.prototype.visitLiteral;
|
||||
SourceMapper.prototype.visitLiteral = function(lit){
|
||||
var val = literal.call(this, lit)
|
||||
, filename = this.normalizePath(lit.filename)
|
||||
, indentsRe = /^\s+/
|
||||
, lines = val.split('\n');
|
||||
|
||||
// add mappings for multiline literals
|
||||
if (lines.length > 1) {
|
||||
lines.forEach(function(line, i) {
|
||||
var indents = line.match(indentsRe)
|
||||
, column = indents && indents[0]
|
||||
? indents[0].length
|
||||
: 0;
|
||||
|
||||
if (lit.css) column += 2;
|
||||
|
||||
this.map.addMapping({
|
||||
original: {
|
||||
line: lit.lineno + i,
|
||||
column: column
|
||||
},
|
||||
generated: {
|
||||
line: this.lineno + i,
|
||||
column: 0
|
||||
},
|
||||
source: filename
|
||||
});
|
||||
}, this);
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visit Charset.
|
||||
*/
|
||||
|
||||
var charset = Compiler.prototype.visitCharset;
|
||||
SourceMapper.prototype.visitCharset = function(node){
|
||||
this.utf8 = ('utf-8' == node.val.string.toLowerCase());
|
||||
return charset.call(this, node);
|
||||
};
|
Reference in New Issue
Block a user