Servicedock: Webgui for Docker Swarm. Manage Docker Swarm a a Service.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

246 lines
5.4 KiB

/*!
* Stylus - Renderer
* Copyright (c) Automattic <developer.wordpress.com>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var Parser = require('./parser')
, EventEmitter = require('events').EventEmitter
, Evaluator = require('./visitor/evaluator')
, Normalizer = require('./visitor/normalizer')
, events = new EventEmitter
, utils = require('./utils')
, nodes = require('./nodes')
, join = require('path').join;
/**
* Expose `Renderer`.
*/
module.exports = Renderer;
/**
* Initialize a new `Renderer` with the given `str` and `options`.
*
* @param {String} str
* @param {Object} options
* @api public
*/
function Renderer(str, options) {
options = options || {};
options.globals = options.globals || {};
options.functions = options.functions || {};
options.use = options.use || [];
options.use = Array.isArray(options.use) ? options.use : [options.use];
options.imports = [join(__dirname, 'functions')];
options.paths = options.paths || [];
options.filename = options.filename || 'stylus';
options.Evaluator = options.Evaluator || Evaluator;
this.options = options;
this.str = str;
this.events = events;
};
/**
* Inherit from `EventEmitter.prototype`.
*/
Renderer.prototype.__proto__ = EventEmitter.prototype;
/**
* Expose events explicitly.
*/
module.exports.events = events;
/**
* Parse and evaluate AST, then callback `fn(err, css, js)`.
*
* @param {Function} fn
* @api public
*/
Renderer.prototype.render = function(fn){
var parser = this.parser = new Parser(this.str, this.options);
// use plugin(s)
for (var i = 0, len = this.options.use.length; i < len; i++) {
this.use(this.options.use[i]);
}
try {
nodes.filename = this.options.filename;
// parse
var ast = parser.parse();
// evaluate
this.evaluator = new this.options.Evaluator(ast, this.options);
this.nodes = nodes;
this.evaluator.renderer = this;
ast = this.evaluator.evaluate();
// normalize
var normalizer = new Normalizer(ast, this.options);
ast = normalizer.normalize();
// compile
var compiler = this.options.sourcemap
? new (require('./visitor/sourcemapper'))(ast, this.options)
: new (require('./visitor/compiler'))(ast, this.options)
, css = compiler.compile();
// expose sourcemap
if (this.options.sourcemap) this.sourcemap = compiler.map.toJSON();
} catch (err) {
var options = {};
options.input = err.input || this.str;
options.filename = err.filename || this.options.filename;
options.lineno = err.lineno || parser.lexer.lineno;
options.column = err.column || parser.lexer.column;
if (!fn) throw utils.formatException(err, options);
return fn(utils.formatException(err, options));
}
// fire `end` event
var listeners = this.listeners('end');
if (fn) listeners.push(fn);
for (var i = 0, len = listeners.length; i < len; i++) {
var ret = listeners[i](null, css);
if (ret) css = ret;
}
if (!fn) return css;
};
/**
* Get dependencies of the compiled file.
*
* @param {String} [filename]
* @return {Array}
* @api public
*/
Renderer.prototype.deps = function(filename){
if (filename) this.options.filename = filename;
var DepsResolver = require('./visitor/deps-resolver')
, parser = new Parser(this.str, this.options);
try {
nodes.filename = this.options.filename;
// parse
var ast = parser.parse()
, resolver = new DepsResolver(ast, this.options);
// resolve dependencies
return resolver.resolve();
} catch (err) {
var options = {};
options.input = err.input || this.str;
options.filename = err.filename || this.options.filename;
options.lineno = err.lineno || parser.lexer.lineno;
options.column = err.column || parser.lexer.column;
throw utils.formatException(err, options);
}
};
/**
* Set option `key` to `val`.
*
* @param {String} key
* @param {Mixed} val
* @return {Renderer} for chaining
* @api public
*/
Renderer.prototype.set = function(key, val){
this.options[key] = val;
return this;
};
/**
* Get option `key`.
*
* @param {String} key
* @return {Mixed} val
* @api public
*/
Renderer.prototype.get = function(key){
return this.options[key];
};
/**
* Include the given `path` to the lookup paths array.
*
* @param {String} path
* @return {Renderer} for chaining
* @api public
*/
Renderer.prototype.include = function(path){
this.options.paths.push(path);
return this;
};
/**
* Use the given `fn`.
*
* This allows for plugins to alter the renderer in
* any way they wish, exposing paths etc.
*
* @param {Function}
* @return {Renderer} for chaining
* @api public
*/
Renderer.prototype.use = function(fn){
fn.call(this, this);
return this;
};
/**
* Define function or global var with the given `name`. Optionally
* the function may accept full expressions, by setting `raw`
* to `true`.
*
* @param {String} name
* @param {Function|Node} fn
* @return {Renderer} for chaining
* @api public
*/
Renderer.prototype.define = function(name, fn, raw){
fn = utils.coerce(fn, raw);
if (fn.nodeName) {
this.options.globals[name] = fn;
return this;
}
// function
this.options.functions[name] = fn;
if (undefined != raw) fn.raw = raw;
return this;
};
/**
* Import the given `file`.
*
* @param {String} file
* @return {Renderer} for chaining
* @api public
*/
Renderer.prototype.import = function(file){
this.options.imports.push(file);
return this;
};