1 |
|
---|
2 | /*!
|
---|
3 | * Stylus - Renderer
|
---|
4 | * Copyright (c) Automattic <developer.wordpress.com>
|
---|
5 | * MIT Licensed
|
---|
6 | */
|
---|
7 |
|
---|
8 | /**
|
---|
9 | * Module dependencies.
|
---|
10 | */
|
---|
11 |
|
---|
12 | var Parser = require('./parser')
|
---|
13 | , EventEmitter = require('events').EventEmitter
|
---|
14 | , Evaluator = require('./visitor/evaluator')
|
---|
15 | , Normalizer = require('./visitor/normalizer')
|
---|
16 | , events = new EventEmitter
|
---|
17 | , utils = require('./utils')
|
---|
18 | , nodes = require('./nodes')
|
---|
19 | , join = require('path').join;
|
---|
20 |
|
---|
21 | /**
|
---|
22 | * Expose `Renderer`.
|
---|
23 | */
|
---|
24 |
|
---|
25 | module.exports = Renderer;
|
---|
26 |
|
---|
27 | /**
|
---|
28 | * Initialize a new `Renderer` with the given `str` and `options`.
|
---|
29 | *
|
---|
30 | * @param {String} str
|
---|
31 | * @param {Object} options
|
---|
32 | * @api public
|
---|
33 | */
|
---|
34 |
|
---|
35 | function Renderer(str, options) {
|
---|
36 | options = options || {};
|
---|
37 | options.globals = options.globals || {};
|
---|
38 | options.functions = options.functions || {};
|
---|
39 | options.use = options.use || [];
|
---|
40 | options.use = Array.isArray(options.use) ? options.use : [options.use];
|
---|
41 | options.imports = [join(__dirname, 'functions/index.styl')].concat(options.imports || []);
|
---|
42 | options.paths = options.paths || [];
|
---|
43 | options.filename = options.filename || 'stylus';
|
---|
44 | options.Evaluator = options.Evaluator || Evaluator;
|
---|
45 | this.options = options;
|
---|
46 | this.str = str;
|
---|
47 | this.events = events;
|
---|
48 | };
|
---|
49 |
|
---|
50 | /**
|
---|
51 | * Inherit from `EventEmitter.prototype`.
|
---|
52 | */
|
---|
53 |
|
---|
54 | Renderer.prototype.__proto__ = EventEmitter.prototype;
|
---|
55 |
|
---|
56 | /**
|
---|
57 | * Expose events explicitly.
|
---|
58 | */
|
---|
59 |
|
---|
60 | module.exports.events = events;
|
---|
61 |
|
---|
62 | /**
|
---|
63 | * Parse and evaluate AST, then callback `fn(err, css, js)`.
|
---|
64 | *
|
---|
65 | * @param {Function} fn
|
---|
66 | * @api public
|
---|
67 | */
|
---|
68 |
|
---|
69 | Renderer.prototype.render = function(fn){
|
---|
70 | var parser = this.parser = new Parser(this.str, this.options);
|
---|
71 |
|
---|
72 | // use plugin(s)
|
---|
73 | for (var i = 0, len = this.options.use.length; i < len; i++) {
|
---|
74 | this.use(this.options.use[i]);
|
---|
75 | }
|
---|
76 |
|
---|
77 | try {
|
---|
78 | nodes.filename = this.options.filename;
|
---|
79 | // parse
|
---|
80 | var ast = parser.parse();
|
---|
81 |
|
---|
82 | // evaluate
|
---|
83 | this.evaluator = new this.options.Evaluator(ast, this.options);
|
---|
84 | this.nodes = nodes;
|
---|
85 | this.evaluator.renderer = this;
|
---|
86 | ast = this.evaluator.evaluate();
|
---|
87 |
|
---|
88 | // normalize
|
---|
89 | var normalizer = new Normalizer(ast, this.options);
|
---|
90 | ast = normalizer.normalize();
|
---|
91 |
|
---|
92 | // compile
|
---|
93 | var compiler = this.options.sourcemap
|
---|
94 | ? new (require('./visitor/sourcemapper'))(ast, this.options)
|
---|
95 | : new (require('./visitor/compiler'))(ast, this.options)
|
---|
96 | , css = compiler.compile();
|
---|
97 |
|
---|
98 | // expose sourcemap
|
---|
99 | if (this.options.sourcemap) this.sourcemap = compiler.map.toJSON();
|
---|
100 | } catch (err) {
|
---|
101 | var options = {};
|
---|
102 | options.input = err.input || this.str;
|
---|
103 | options.filename = err.filename || this.options.filename;
|
---|
104 | options.lineno = err.lineno || parser.lexer.lineno;
|
---|
105 | options.column = err.column || parser.lexer.column;
|
---|
106 | if (!fn) throw utils.formatException(err, options);
|
---|
107 | return fn(utils.formatException(err, options));
|
---|
108 | }
|
---|
109 |
|
---|
110 | // fire `end` event
|
---|
111 | var listeners = this.listeners('end');
|
---|
112 | if (fn) listeners.push(fn);
|
---|
113 | for (var i = 0, len = listeners.length; i < len; i++) {
|
---|
114 | var ret = listeners[i](null, css);
|
---|
115 | if (ret) css = ret;
|
---|
116 | }
|
---|
117 | if (!fn) return css;
|
---|
118 | };
|
---|
119 |
|
---|
120 | /**
|
---|
121 | * Get dependencies of the compiled file.
|
---|
122 | *
|
---|
123 | * @param {String} [filename]
|
---|
124 | * @return {Array}
|
---|
125 | * @api public
|
---|
126 | */
|
---|
127 |
|
---|
128 | Renderer.prototype.deps = function(filename){
|
---|
129 | var opts = utils.merge({ cache: false }, this.options);
|
---|
130 | if (filename) opts.filename = filename;
|
---|
131 |
|
---|
132 | var DepsResolver = require('./visitor/deps-resolver')
|
---|
133 | , parser = new Parser(this.str, opts);
|
---|
134 |
|
---|
135 | try {
|
---|
136 | nodes.filename = opts.filename;
|
---|
137 | // parse
|
---|
138 | var ast = parser.parse()
|
---|
139 | , resolver = new DepsResolver(ast, opts);
|
---|
140 |
|
---|
141 | // resolve dependencies
|
---|
142 | return resolver.resolve();
|
---|
143 | } catch (err) {
|
---|
144 | var options = {};
|
---|
145 | options.input = err.input || this.str;
|
---|
146 | options.filename = err.filename || opts.filename;
|
---|
147 | options.lineno = err.lineno || parser.lexer.lineno;
|
---|
148 | options.column = err.column || parser.lexer.column;
|
---|
149 | throw utils.formatException(err, options);
|
---|
150 | }
|
---|
151 | };
|
---|
152 |
|
---|
153 | /**
|
---|
154 | * Set option `key` to `val`.
|
---|
155 | *
|
---|
156 | * @param {String} key
|
---|
157 | * @param {Mixed} val
|
---|
158 | * @return {Renderer} for chaining
|
---|
159 | * @api public
|
---|
160 | */
|
---|
161 |
|
---|
162 | Renderer.prototype.set = function(key, val){
|
---|
163 | this.options[key] = val;
|
---|
164 | return this;
|
---|
165 | };
|
---|
166 |
|
---|
167 | /**
|
---|
168 | * Get option `key`.
|
---|
169 | *
|
---|
170 | * @param {String} key
|
---|
171 | * @return {Mixed} val
|
---|
172 | * @api public
|
---|
173 | */
|
---|
174 |
|
---|
175 | Renderer.prototype.get = function(key){
|
---|
176 | return this.options[key];
|
---|
177 | };
|
---|
178 |
|
---|
179 | /**
|
---|
180 | * Include the given `path` to the lookup paths array.
|
---|
181 | *
|
---|
182 | * @param {String} path
|
---|
183 | * @return {Renderer} for chaining
|
---|
184 | * @api public
|
---|
185 | */
|
---|
186 |
|
---|
187 | Renderer.prototype.include = function(path){
|
---|
188 | this.options.paths.push(path);
|
---|
189 | return this;
|
---|
190 | };
|
---|
191 |
|
---|
192 | /**
|
---|
193 | * Use the given `fn`.
|
---|
194 | *
|
---|
195 | * This allows for plugins to alter the renderer in
|
---|
196 | * any way they wish, exposing paths etc.
|
---|
197 | *
|
---|
198 | * @param {Function}
|
---|
199 | * @return {Renderer} for chaining
|
---|
200 | * @api public
|
---|
201 | */
|
---|
202 |
|
---|
203 | Renderer.prototype.use = function(fn){
|
---|
204 | fn.call(this, this);
|
---|
205 | return this;
|
---|
206 | };
|
---|
207 |
|
---|
208 | /**
|
---|
209 | * Define function or global var with the given `name`. Optionally
|
---|
210 | * the function may accept full expressions, by setting `raw`
|
---|
211 | * to `true`.
|
---|
212 | *
|
---|
213 | * @param {String} name
|
---|
214 | * @param {Function|Node} fn
|
---|
215 | * @return {Renderer} for chaining
|
---|
216 | * @api public
|
---|
217 | */
|
---|
218 |
|
---|
219 | Renderer.prototype.define = function(name, fn, raw){
|
---|
220 | fn = utils.coerce(fn, raw);
|
---|
221 |
|
---|
222 | if (fn.nodeName) {
|
---|
223 | this.options.globals[name] = fn;
|
---|
224 | return this;
|
---|
225 | }
|
---|
226 |
|
---|
227 | // function
|
---|
228 | this.options.functions[name] = fn;
|
---|
229 | if (undefined != raw) fn.raw = raw;
|
---|
230 | return this;
|
---|
231 | };
|
---|
232 |
|
---|
233 | /**
|
---|
234 | * Import the given `file`.
|
---|
235 | *
|
---|
236 | * @param {String} file
|
---|
237 | * @return {Renderer} for chaining
|
---|
238 | * @api public
|
---|
239 | */
|
---|
240 |
|
---|
241 | Renderer.prototype.import = function(file){
|
---|
242 | this.options.imports.push(file);
|
---|
243 | return this;
|
---|
244 | };
|
---|
245 |
|
---|
246 |
|
---|