From 5be8c16548ced9f1b648cecaa6cd5d8cf2045594 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:01:26 +0200 Subject: [PATCH 01/68] use classes in errors --- lib/errors.js | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/lib/errors.js b/lib/errors.js index 7fa9a3506..5b774a842 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -5,13 +5,6 @@ * MIT Licensed */ -/** - * Expose constructors. - */ - -exports.ParseError = ParseError; -exports.SyntaxError = SyntaxError; - /** * Initialize a new `ParseError` with the given `msg`. * @@ -19,20 +12,17 @@ exports.SyntaxError = SyntaxError; * @api private */ -function ParseError(msg) { - this.name = 'ParseError'; - this.message = msg; - if (Error.captureStackTrace) { - Error.captureStackTrace(this, ParseError); +class ParseError extends Error { + constructor(msg) { + super(); + this.name = 'ParseError'; + this.message = msg; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ParseError); + } } } -/** - * Inherit from `Error.prototype`. - */ - -ParseError.prototype.__proto__ = Error.prototype; - /** * Initialize a new `SyntaxError` with the given `msg`. * @@ -40,16 +30,20 @@ ParseError.prototype.__proto__ = Error.prototype; * @api private */ -function SyntaxError(msg) { - this.name = 'SyntaxError'; - this.message = msg; - if (Error.captureStackTrace) { - Error.captureStackTrace(this, ParseError); +class SyntaxError extends Error { + constructor(msg) { + super(); + this.name = 'SyntaxError'; + this.message = msg; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ParseError); + } } } /** - * Inherit from `Error.prototype`. + * Expose constructors. */ -SyntaxError.prototype.__proto__ = Error.prototype; +exports.ParseError = ParseError; +exports.SyntaxError = SyntaxError; From 42377e532eb67ae321bd3cfe94b79c1c035f98eb Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:07:16 +0200 Subject: [PATCH 02/68] use classes in lib/renderer --- lib/renderer.js | 405 ++++++++++++++++++++++++------------------------ 1 file changed, 200 insertions(+), 205 deletions(-) diff --git a/lib/renderer.js b/lib/renderer.js index dd478b3fb..c299ffa6c 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -18,12 +18,6 @@ var Parser = require('./parser') , nodes = require('./nodes') , join = require('path').join; -/** - * Expose `Renderer`. - */ - -module.exports = Renderer; - /** * Initialize a new `Renderer` with the given `str` and `options`. * @@ -32,215 +26,216 @@ module.exports = Renderer; * @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/index.styl')].concat(options.imports || []); - 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]); +class Renderer extends EventEmitter { + constructor(str, options) { + super(); + 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/index.styl')].concat(options.imports || []); + options.paths = options.paths || []; + options.filename = options.filename || 'stylus'; + options.Evaluator = options.Evaluator || Evaluator; + this.options = options; + this.str = str; + this.events = events; } - 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)); + /** + * Parse and evaluate AST, then callback `fn(err, css, js)`. + * + * @param {Function} fn + * @api public + */ + + render(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; } - // 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){ - var opts = utils.merge({ cache: false }, this.options); - if (filename) opts.filename = filename; - - var DepsResolver = require('./visitor/deps-resolver') - , parser = new Parser(this.str, opts); - - try { - nodes.filename = opts.filename; - // parse - var ast = parser.parse() - , resolver = new DepsResolver(ast, opts); - - // resolve dependencies - return resolver.resolve(); - } catch (err) { - var options = {}; - options.input = err.input || this.str; - options.filename = err.filename || opts.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; + /** + * Get dependencies of the compiled file. + * + * @param {String} [filename] + * @return {Array} + * @api public + */ + + deps(filename) { + var opts = utils.merge({ cache: false }, this.options); + if (filename) opts.filename = filename; + + var DepsResolver = require('./visitor/deps-resolver') + , parser = new Parser(this.str, opts); + + try { + nodes.filename = opts.filename; + // parse + var ast = parser.parse() + , resolver = new DepsResolver(ast, opts); + + // resolve dependencies + return resolver.resolve(); + } catch (err) { + var options = {}; + options.input = err.input || this.str; + options.filename = err.filename || opts.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 + */ + + set(key, val) { + this.options[key] = val; + return this; + }; + + /** + * Get option `key`. + * + * @param {String} key + * @return {Mixed} val + * @api public + */ + + get(key) { + return this.options[key]; + }; + + /** + * Include the given `path` to the lookup paths array. + * + * @param {String} path + * @return {Renderer} for chaining + * @api public + */ + + include(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 + */ + + use(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 + */ + + define(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 + */ + + import(file) { + this.options.imports.push(file); + 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 + * Expose events explicitly. */ -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; -}; +module.exports.events = events; /** - * Import the given `file`. - * - * @param {String} file - * @return {Renderer} for chaining - * @api public + * Expose `Renderer`. */ -Renderer.prototype.import = function(file){ - this.options.imports.push(file); - return this; -}; - - +module.exports = Renderer; From 47171ec5bc8d02eaa77245d8463f9cdd7ed91f33 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:11:32 +0200 Subject: [PATCH 03/68] use classes in lib/arguments --- lib/nodes/arguments.js | 125 +++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/lib/nodes/arguments.js b/lib/nodes/arguments.js index c774a6fe4..c3569cc45 100644 --- a/lib/nodes/arguments.js +++ b/lib/nodes/arguments.js @@ -17,74 +17,75 @@ var nodes = require('../nodes'); * @api public */ -var Arguments = module.exports = function Arguments(){ - nodes.Expression.call(this); - this.map = {}; -}; +module.exports = class Arguments extends nodes.Expression { + constructor() { + super() + nodes.Expression.call(this); + this.map = {}; + } -/** - * Inherit from `nodes.Expression.prototype`. - */ + /** + * Initialize an `Arguments` object with the nodes + * from the given `expr`. + * + * @param {Expression} expr + * @return {Arguments} + * @api public + */ -Arguments.prototype.__proto__ = nodes.Expression.prototype; + static fromExpression(expr) { + var args = new Arguments + , len = expr.nodes.length; + args.lineno = expr.lineno; + args.column = expr.column; + args.isList = expr.isList; + for (var i = 0; i < len; ++i) { + args.push(expr.nodes[i]); + } + return args; + }; -/** - * Initialize an `Arguments` object with the nodes - * from the given `expr`. - * - * @param {Expression} expr - * @return {Arguments} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Arguments.fromExpression = function(expr){ - var args = new Arguments - , len = expr.nodes.length; - args.lineno = expr.lineno; - args.column = expr.column; - args.isList = expr.isList; - for (var i = 0; i < len; ++i) { - args.push(expr.nodes[i]); - } - return args; -}; + clone(parent) { + var clone = nodes.Expression.prototype.clone.call(this, parent); + clone.map = {}; + for (var key in this.map) { + clone.map[key] = this.map[key].clone(parent, clone); + } + clone.isList = this.isList; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Arguments', + map: this.map, + isList: this.isList, + preserve: this.preserve, + lineno: this.lineno, + column: this.column, + filename: this.filename, + nodes: this.nodes + }; + }; -Arguments.prototype.clone = function(parent){ - var clone = nodes.Expression.prototype.clone.call(this, parent); - clone.map = {}; - for (var key in this.map) { - clone.map[key] = this.map[key].clone(parent, clone); - } - clone.isList = this.isList; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ -Arguments.prototype.toJSON = function(){ - return { - __type: 'Arguments', - map: this.map, - isList: this.isList, - preserve: this.preserve, - lineno: this.lineno, - column: this.column, - filename: this.filename, - nodes: this.nodes - }; -}; + From ff47681474953f2b96ea538894f7f559bc610d25 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:15:37 +0200 Subject: [PATCH 04/68] remove double initializer --- lib/nodes/arguments.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/nodes/arguments.js b/lib/nodes/arguments.js index c3569cc45..505417caa 100644 --- a/lib/nodes/arguments.js +++ b/lib/nodes/arguments.js @@ -19,8 +19,7 @@ var nodes = require('../nodes'); module.exports = class Arguments extends nodes.Expression { constructor() { - super() - nodes.Expression.call(this); + super(); this.map = {}; } From 2515a6b7dadf5f13c326e0fed3a9575cc047c0c4 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:16:02 +0200 Subject: [PATCH 05/68] use classes in lib/nodes/atblock --- lib/nodes/atblock.js | 96 ++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/lib/nodes/atblock.js b/lib/nodes/atblock.js index 03a191cf2..567061aaa 100644 --- a/lib/nodes/atblock.js +++ b/lib/nodes/atblock.js @@ -16,64 +16,56 @@ var Node = require('./node'); * @api public */ -var Atblock = module.exports = function Atblock(){ - Node.call(this); -}; - -/** - * Return `block` nodes. - */ - -Atblock.prototype.__defineGetter__('nodes', function(){ - return this.block.nodes; -}); - -/** - * Inherit from `Node.prototype`. - */ +module.exports = class Atblock extends Node { + /** + * Return `block` nodes. + */ -Atblock.prototype.__proto__ = Node.prototype; + get nodes() { + return this.block.nodes; + } -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Atblock.prototype.clone = function(parent){ - var clone = new Atblock; - clone.block = this.block.clone(parent, clone); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new Atblock; + clone.block = this.block.clone(parent, clone); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return @block. - * - * @return {String} - * @api public - */ + /** + * Return @block. + * + * @return {String} + * @api public + */ -Atblock.prototype.toString = function(){ - return '@block'; -}; + toString() { + return '@block'; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Atblock.prototype.toJSON = function(){ - return { - __type: 'Atblock', - block: this.block, - lineno: this.lineno, - column: this.column, - fileno: this.fileno + toJSON() { + return { + __type: 'Atblock', + block: this.block, + lineno: this.lineno, + column: this.column, + fileno: this.fileno + }; }; }; From 9e245baeb166a7f4336ceb0f1122196339298859 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:21:11 +0200 Subject: [PATCH 06/68] fix constructor jsdoc in wrong place --- lib/nodes/arguments.js | 12 ++++++------ lib/nodes/atblock.js | 16 ++++++++++------ lib/renderer.js | 16 ++++++++-------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/lib/nodes/arguments.js b/lib/nodes/arguments.js index 505417caa..07304cdec 100644 --- a/lib/nodes/arguments.js +++ b/lib/nodes/arguments.js @@ -11,13 +11,13 @@ var nodes = require('../nodes'); -/** - * Initialize a new `Arguments`. - * - * @api public - */ - module.exports = class Arguments extends nodes.Expression { + /** + * Initialize a new `Arguments`. + * + * @api public + */ + constructor() { super(); this.map = {}; diff --git a/lib/nodes/atblock.js b/lib/nodes/atblock.js index 567061aaa..460e1e5c4 100644 --- a/lib/nodes/atblock.js +++ b/lib/nodes/atblock.js @@ -10,13 +10,17 @@ var Node = require('./node'); -/** - * Initialize a new `@block` node. - * - * @api public - */ - module.exports = class Atblock extends Node { + /** + * Initialize a new `@block` node. + * + * @api public + */ + + constructor() { + super(); + } + /** * Return `block` nodes. */ diff --git a/lib/renderer.js b/lib/renderer.js index c299ffa6c..37b00962a 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -18,15 +18,15 @@ var Parser = require('./parser') , nodes = require('./nodes') , join = require('path').join; -/** - * Initialize a new `Renderer` with the given `str` and `options`. - * - * @param {String} str - * @param {Object} options - * @api public - */ - class Renderer extends EventEmitter { + /** + * Initialize a new `Renderer` with the given `str` and `options`. + * + * @param {String} str + * @param {Object} options + * @api public + */ + constructor(str, options) { super(); options = options || {}; From 595d0759a6997a2a614ba9c4ff6e75344b6f0777 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:30:00 +0200 Subject: [PATCH 07/68] convert atrule and subclasses to use classes --- lib/nodes/atrule.js | 180 ++++++++++++++++++++--------------------- lib/nodes/keyframes.js | 115 +++++++++++++------------- lib/nodes/media.js | 104 ++++++++++++------------ lib/nodes/supports.js | 104 ++++++++++++------------ 4 files changed, 244 insertions(+), 259 deletions(-) diff --git a/lib/nodes/atrule.js b/lib/nodes/atrule.js index 43301b500..9e175907a 100644 --- a/lib/nodes/atrule.js +++ b/lib/nodes/atrule.js @@ -10,118 +10,114 @@ var Node = require('./node'); -/** - * Initialize a new at-rule node. - * - * @param {String} type - * @api public - */ - -var Atrule = module.exports = function Atrule(type){ - Node.call(this); - this.type = type; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Atrule.prototype.__proto__ = Node.prototype; - -/** - * Check if at-rule's block has only properties. - * - * @return {Boolean} - * @api public - */ - -Atrule.prototype.__defineGetter__('hasOnlyProperties', function(){ - if (!this.block) return false; +module.exports = class Atrule extends Node { + /** + * Initialize a new at-rule node. + * + * @param {String} type + * @api public + */ + + constructor(type) { + super() + this.type = type; + } - var nodes = this.block.nodes; - for (var i = 0, len = nodes.length; i < len; ++i) { - var nodeName = nodes[i].nodeName; - switch(nodes[i].nodeName) { - case 'property': - case 'expression': - case 'comment': - continue; - default: - return false; + /** + * Check if at-rule's block has only properties. + * + * @return {Boolean} + * @api public + */ + + get hasOnlyProperties() { + if (!this.block) return false; + + var nodes = this.block.nodes; + for (var i = 0, len = nodes.length; i < len; ++i) { + var nodeName = nodes[i].nodeName; + switch (nodes[i].nodeName) { + case 'property': + case 'expression': + case 'comment': + continue; + default: + return false; + } } + return true; } - return true; -}); -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone(parent) { + var clone = new Atrule(this.type); + if (this.block) clone.block = this.block.clone(parent, clone); + clone.segments = this.segments.map(function (node) { return node.clone(parent, clone); }); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -Atrule.prototype.clone = function(parent){ - var clone = new Atrule(this.type); - if (this.block) clone.block = this.block.clone(parent, clone); - clone.segments = this.segments.map(function(node){ return node.clone(parent, clone); }); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + var json = { + __type: 'Atrule', + type: this.type, + segments: this.segments, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + if (this.block) json.block = this.block; + return json; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return @. + * + * @return {String} + * @api public + */ -Atrule.prototype.toJSON = function(){ - var json = { - __type: 'Atrule', - type: this.type, - segments: this.segments, - lineno: this.lineno, - column: this.column, - filename: this.filename + toString() { + return '@' + this.type; }; - if (this.block) json.block = this.block; - return json; -}; -/** - * Return @. - * - * @return {String} - * @api public - */ + /** + * Check if the at-rule's block has output nodes. + * + * @return {Boolean} + * @api public + */ -Atrule.prototype.toString = function(){ - return '@' + this.type; + get hasOutput() { + return !!this.block && hasOutput(this.block); + }; }; -/** - * Check if the at-rule's block has output nodes. - * - * @return {Boolean} - * @api public - */ - -Atrule.prototype.__defineGetter__('hasOutput', function(){ - return !!this.block && hasOutput(this.block); -}); - function hasOutput(block) { var nodes = block.nodes; // only placeholder selectors - if (nodes.every(function(node){ + if (nodes.every(function (node) { return 'group' == node.nodeName && node.hasOnlyPlaceholders; })) return false; // something visible - return nodes.some(function(node) { + return nodes.some(function (node) { switch (node.nodeName) { case 'property': case 'literal': diff --git a/lib/nodes/keyframes.js b/lib/nodes/keyframes.js index e4e1271a7..001524d41 100644 --- a/lib/nodes/keyframes.js +++ b/lib/nodes/keyframes.js @@ -11,71 +11,68 @@ var Atrule = require('./atrule'); -/** - * Initialize a new `Keyframes` with the given `segs`, - * and optional vendor `prefix`. - * - * @param {Array} segs - * @param {String} prefix - * @api public - */ +module.exports = class Keyframes extends Atrule { + /** + * Initialize a new `Keyframes` with the given `segs`, + * and optional vendor `prefix`. + * + * @param {Array} segs + * @param {String} prefix + * @api public + */ -var Keyframes = module.exports = function Keyframes(segs, prefix){ - Atrule.call(this, 'keyframes'); - this.segments = segs; - this.prefix = prefix || 'official'; -}; + constructor(segs, prefix) { + super('keyframes') + this.segments = segs; + this.prefix = prefix || 'official'; + } -/** - * Inherit from `Atrule.prototype`. - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Keyframes.prototype.__proto__ = Atrule.prototype; + clone(parent) { + var clone = new Keyframes; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + clone.segments = this.segments.map(function (node) { return node.clone(parent, clone); }); + clone.prefix = this.prefix; + clone.block = this.block.clone(parent, clone); + return clone; + }; -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Keyframes.prototype.clone = function(parent){ - var clone = new Keyframes; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - clone.segments = this.segments.map(function(node) { return node.clone(parent, clone); }); - clone.prefix = this.prefix; - clone.block = this.block.clone(parent, clone); - return clone; -}; + toJSON() { + return { + __type: 'Keyframes', + segments: this.segments, + prefix: this.prefix, + block: this.block, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return `@keyframes name`. + * + * @return {String} + * @api public + */ -Keyframes.prototype.toJSON = function(){ - return { - __type: 'Keyframes', - segments: this.segments, - prefix: this.prefix, - block: this.block, - lineno: this.lineno, - column: this.column, - filename: this.filename + toString() { + return '@keyframes ' + this.segments.join(''); }; -}; - -/** - * Return `@keyframes name`. - * - * @return {String} - * @api public - */ -Keyframes.prototype.toString = function(){ - return '@keyframes ' + this.segments.join(''); -}; +}; \ No newline at end of file diff --git a/lib/nodes/media.js b/lib/nodes/media.js index 65fdf7ba4..fdaec7d64 100644 --- a/lib/nodes/media.js +++ b/lib/nodes/media.js @@ -11,66 +11,62 @@ var Atrule = require('./atrule'); -/** - * Initialize a new `Media` with the given `val` - * - * @param {String} val - * @api public - */ +module.exports = class Media extends Atrule { + /** + * Initialize a new `Media` with the given `val` + * + * @param {String} val + * @api public + */ -var Media = module.exports = function Media(val){ - Atrule.call(this, 'media'); - this.val = val; -}; + constructor(val) { + super('media'); + this.val = val; + } -/** - * Inherit from `Atrule.prototype`. - */ - -Media.prototype.__proto__ = Atrule.prototype; - -/** - * Clone this node. - * - * @return {Media} - * @api public - */ + /** + * Clone this node. + * + * @return {Media} + * @api public + */ -Media.prototype.clone = function(parent){ - var clone = new Media; - clone.val = this.val.clone(parent, clone); - clone.block = this.block.clone(parent, clone); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new Media; + clone.val = this.val.clone(parent, clone); + clone.block = this.block.clone(parent, clone); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Media.prototype.toJSON = function(){ - return { - __type: 'Media', - val: this.val, - block: this.block, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + return { + __type: 'Media', + val: this.val, + block: this.block, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; -}; -/** - * Return @media "val". - * - * @return {String} - * @api public - */ + /** + * Return @media "val". + * + * @return {String} + * @api public + */ -Media.prototype.toString = function(){ - return '@media ' + this.val; + toString() { + return '@media ' + this.val; + }; }; diff --git a/lib/nodes/supports.js b/lib/nodes/supports.js index ab0587837..f603d3b53 100644 --- a/lib/nodes/supports.js +++ b/lib/nodes/supports.js @@ -10,66 +10,62 @@ var Atrule = require('./atrule'); -/** - * Initialize a new supports node. - * - * @param {Expression} condition - * @api public - */ +module.exports = class Supports extends Atrule { + /** + * Initialize a new supports node. + * + * @param {Expression} condition + * @api public + */ -var Supports = module.exports = function Supports(condition){ - Atrule.call(this, 'supports'); - this.condition = condition; -}; + constructor(condition) { + super('supports'); + this.condition = condition; + } -/** - * Inherit from `Atrule.prototype`. - */ - -Supports.prototype.__proto__ = Atrule.prototype; - -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Supports.prototype.clone = function(parent){ - var clone = new Supports; - clone.condition = this.condition.clone(parent, clone); - clone.block = this.block.clone(parent, clone); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new Supports; + clone.condition = this.condition.clone(parent, clone); + clone.block = this.block.clone(parent, clone); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Supports.prototype.toJSON = function(){ - return { - __type: 'Supports', - condition: this.condition, - block: this.block, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + return { + __type: 'Supports', + condition: this.condition, + block: this.block, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; -}; -/** - * Return @supports - * - * @return {String} - * @api public - */ + /** + * Return @supports + * + * @return {String} + * @api public + */ -Supports.prototype.toString = function(){ - return '@supports ' + this.condition; + toString() { + return '@supports ' + this.condition; + }; }; From beb344c1ddf7c0bbbb9f2b3f07d34fb4f0fdddbc Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:37:13 +0200 Subject: [PATCH 08/68] use classes in lib/node --- lib/nodes/node.js | 89 +++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/lib/nodes/node.js b/lib/nodes/node.js index 6466177f7..e3fb02000 100644 --- a/lib/nodes/node.js +++ b/lib/nodes/node.js @@ -13,41 +13,37 @@ var Evaluator = require('../visitor/evaluator') , utils = require('../utils') , nodes = require('./'); -/** - * Initialize a new `CoercionError` with the given `msg`. - * - * @param {String} msg - * @api private - */ +class CoercionError extends Error { + /** + * Initialize a new `CoercionError` with the given `msg`. + * + * @param {String} msg + * @api private + */ -function CoercionError(msg) { - this.name = 'CoercionError' - this.message = msg - if (Error.captureStackTrace) { - Error.captureStackTrace(this, CoercionError); + constructor(msg) { + this.name = 'CoercionError' + this.message = msg + if (Error.captureStackTrace) { + Error.captureStackTrace(this, CoercionError); + } } } -/** - * Inherit from `Error.prototype`. - */ - -CoercionError.prototype.__proto__ = Error.prototype; -/** - * Node constructor. - * - * @api public - */ -var Node = module.exports = function Node(){ - this.lineno = nodes.lineno || 1; - this.column = nodes.column || 1; - this.filename = nodes.filename; -}; +module.exports = class Node { + /** + * Node constructor. + * + * @api public + */ -Node.prototype = { - constructor: Node, + constructor() { + this.lineno = nodes.lineno || 1; + this.column = nodes.column || 1; + this.filename = nodes.filename; + } /** * Return this node. @@ -58,7 +54,7 @@ Node.prototype = { get first() { return this; - }, + } /** * Return hash. @@ -69,7 +65,7 @@ Node.prototype = { get hash() { return this.val; - }, + } /** * Return node name. @@ -80,7 +76,7 @@ Node.prototype = { get nodeName() { return this.constructor.name.toLowerCase(); - }, + } /** * Return this node. @@ -89,9 +85,9 @@ Node.prototype = { * @api public */ - clone: function(){ + clone() { return this; - }, + } /** * Return a JSON representation of this node. @@ -100,13 +96,13 @@ Node.prototype = { * @api public */ - toJSON: function(){ + toJSON() { return { lineno: this.lineno, column: this.column, filename: this.filename }; - }, + } /** * Nodes by default evaluate to themselves. @@ -115,9 +111,9 @@ Node.prototype = { * @api public */ - eval: function(){ + eval() { return new Evaluator(this).evaluate(); - }, + } /** * Return true. @@ -126,9 +122,9 @@ Node.prototype = { * @api public */ - toBoolean: function(){ + toBoolean() { return nodes.true; - }, + } /** * Return the expression, or wrap this node in an expression. @@ -137,12 +133,12 @@ Node.prototype = { * @api public */ - toExpression: function(){ + toExpression() { if ('expression' == this.nodeName) return this; var expr = new nodes.Expression; expr.push(this); return expr; - }, + } /** * Return false if `op` is generally not coerced. @@ -152,7 +148,7 @@ Node.prototype = { * @api private */ - shouldCoerce: function(op){ + shouldCoerce(op) { switch (op) { case 'is a': case 'in': @@ -162,7 +158,7 @@ Node.prototype = { default: return true; } - }, + } /** * Operate on `right` with the given `op`. @@ -173,7 +169,7 @@ Node.prototype = { * @api public */ - operate: function(op, right){ + operate(op, right) { switch (op) { case 'is a': if ('string' == right.first.nodeName) { @@ -235,7 +231,7 @@ Node.prototype = { } throw new Error(msg); } - }, + } /** * Default coercion throws. @@ -245,8 +241,9 @@ Node.prototype = { * @api public */ - coerce: function(other){ + coerce(other) { if (other.nodeName == this.nodeName) return other; throw new CoercionError('cannot coerce ' + other + ' to ' + this.nodeName); } }; + From 06196cfe27f3919e94ee2feb7157b330a3c8a037 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:37:19 +0200 Subject: [PATCH 09/68] use classes in lib/nodes/boolean --- lib/nodes/boolean.js | 172 +++++++++++++++++++++---------------------- 1 file changed, 84 insertions(+), 88 deletions(-) diff --git a/lib/nodes/boolean.js b/lib/nodes/boolean.js index 97c11971b..6c7a2acbf 100644 --- a/lib/nodes/boolean.js +++ b/lib/nodes/boolean.js @@ -12,106 +12,102 @@ var Node = require('./node') , nodes = require('./'); -/** - * Initialize a new `Boolean` node with the given `val`. - * - * @param {Boolean} val - * @api public - */ - -var Boolean = module.exports = function Boolean(val){ - Node.call(this); - if (this.nodeName) { - this.val = !!val; - } else { - return new Boolean(val); +module.exports = class Boolean extends Node { + /** + * Initialize a new `Boolean` node with the given `val`. + * + * @param {Boolean} val + * @api public + */ + + constructor(val) { + super(); + if (this.nodeName) { + this.val = !!val; + } else { + return new Boolean(val); + } } -}; -/** - * Inherit from `Node.prototype`. - */ - -Boolean.prototype.__proto__ = Node.prototype; + /** + * Return `this` node. + * + * @return {Boolean} + * @api public + */ -/** - * Return `this` node. - * - * @return {Boolean} - * @api public - */ - -Boolean.prototype.toBoolean = function(){ - return this; -}; - -/** - * Return `true` if this node represents `true`. - * - * @return {Boolean} - * @api public - */ - -Boolean.prototype.__defineGetter__('isTrue', function(){ - return this.val; -}); + toBoolean() { + return this; + }; -/** - * Return `true` if this node represents `false`. - * - * @return {Boolean} - * @api public - */ + /** + * Return `true` if this node represents `true`. + * + * @return {Boolean} + * @api public + */ -Boolean.prototype.__defineGetter__('isFalse', function(){ - return ! this.val; -}); + get isTrue() { + return this.val; + }; -/** - * Negate the value. - * - * @return {Boolean} - * @api public - */ + /** + * Return `true` if this node represents `false`. + * + * @return {Boolean} + * @api public + */ -Boolean.prototype.negate = function(){ - return new Boolean(!this.val); -}; + get isFalse() { + return !this.val; + }; -/** - * Return 'Boolean'. - * - * @return {String} - * @api public - */ + /** + * Negate the value. + * + * @return {Boolean} + * @api public + */ -Boolean.prototype.inspect = function(){ - return '[Boolean ' + this.val + ']'; -}; + negate() { + return new Boolean(!this.val); + }; -/** - * Return 'true' or 'false'. - * - * @return {String} - * @api public - */ + /** + * Return 'Boolean'. + * + * @return {String} + * @api public + */ -Boolean.prototype.toString = function(){ - return this.val - ? 'true' - : 'false'; -}; + inspect() { + return '[Boolean ' + this.val + ']'; + }; -/** - * Return a JSON representaiton of this node. - * - * @return {Object} - * @api public - */ + /** + * Return 'true' or 'false'. + * + * @return {String} + * @api public + */ + + toString() { + return this.val + ? 'true' + : 'false'; + }; -Boolean.prototype.toJSON = function(){ - return { - __type: 'Boolean', - val: this.val + /** + * Return a JSON representaiton of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Boolean', + val: this.val + }; }; }; From bee5a1558b17e4c91da92246387f8a999d611b2e Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:39:17 +0200 Subject: [PATCH 10/68] use classes in lib/nodes/ident --- lib/nodes/ident.js | 256 ++++++++++++++++++++++----------------------- 1 file changed, 126 insertions(+), 130 deletions(-) diff --git a/lib/nodes/ident.js b/lib/nodes/ident.js index 181f738aa..97754e3c5 100644 --- a/lib/nodes/ident.js +++ b/lib/nodes/ident.js @@ -12,145 +12,141 @@ var Node = require('./node') , nodes = require('./'); -/** - * Initialize a new `Ident` by `name` with the given `val` node. - * - * @param {String} name - * @param {Node} val - * @api public - */ - -var Ident = module.exports = function Ident(name, val, mixin){ - Node.call(this); - this.name = name; - this.string = name; - this.val = val || nodes.null; - this.mixin = !!mixin; -}; - -/** - * Check if the variable has a value. - * - * @return {Boolean} - * @api public - */ - -Ident.prototype.__defineGetter__('isEmpty', function(){ - return undefined == this.val; -}); - -/** - * Return hash. - * - * @return {String} - * @api public - */ - -Ident.prototype.__defineGetter__('hash', function(){ - return this.name; -}); - -/** - * Inherit from `Node.prototype`. - */ +module.exports = class Ident extends Node { + /** + * Initialize a new `Ident` by `name` with the given `val` node. + * + * @param {String} name + * @param {Node} val + * @api public + */ + + constructor(name, val, mixin) { + super(); + this.name = name; + this.string = name; + this.val = val || nodes.null; + this.mixin = !!mixin; + } -Ident.prototype.__proto__ = Node.prototype; + /** + * Check if the variable has a value. + * + * @return {Boolean} + * @api public + */ -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -Ident.prototype.clone = function(parent){ - var clone = new Ident(this.name); - clone.val = this.val.clone(parent, clone); - clone.mixin = this.mixin; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - clone.property = this.property; - clone.rest = this.rest; - return clone; -}; + get isEmpty() { + return undefined == this.val; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return hash. + * + * @return {String} + * @api public + */ -Ident.prototype.toJSON = function(){ - return { - __type: 'Ident', - name: this.name, - val: this.val, - mixin: this.mixin, - property: this.property, - rest: this.rest, - lineno: this.lineno, - column: this.column, - filename: this.filename + get hash() { + return this.name; }; -}; -/** - * Return . - * - * @return {String} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone(parent) { + var clone = new Ident(this.name); + clone.val = this.val.clone(parent, clone); + clone.mixin = this.mixin; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + clone.property = this.property; + clone.rest = this.rest; + return clone; + }; -Ident.prototype.toString = function(){ - return this.name; -}; + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Ident', + name: this.name, + val: this.val, + mixin: this.mixin, + property: this.property, + rest: this.rest, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + }; -/** - * Coerce `other` to an ident. - * - * @param {Node} other - * @return {String} - * @api public - */ + /** + * Return . + * + * @return {String} + * @api public + */ -Ident.prototype.coerce = function(other){ - switch (other.nodeName) { - case 'ident': - case 'string': - case 'literal': - return new Ident(other.string); - case 'unit': - return new Ident(other.toString()); - default: - return Node.prototype.coerce.call(this, other); - } -}; + toString() { + return this.name; + }; -/** - * Operate on `right` with the given `op`. - * - * @param {String} op - * @param {Node} right - * @return {Node} - * @api public - */ + /** + * Coerce `other` to an ident. + * + * @param {Node} other + * @return {String} + * @api public + */ + + coerce(other) { + switch (other.nodeName) { + case 'ident': + case 'string': + case 'literal': + return new Ident(other.string); + case 'unit': + return new Ident(other.toString()); + default: + return Node.prototype.coerce.call(this, other); + } + }; -Ident.prototype.operate = function(op, right){ - var val = right.first; - switch (op) { - case '-': - if ('unit' == val.nodeName) { - var expr = new nodes.Expression; - val = val.clone(); - val.val = -val.val; - expr.push(this); - expr.push(val); - return expr; - } - case '+': - return new nodes.Ident(this.string + this.coerce(val).string); - } - return Node.prototype.operate.call(this, op, right); + /** + * Operate on `right` with the given `op`. + * + * @param {String} op + * @param {Node} right + * @return {Node} + * @api public + */ + + operate(op, right) { + var val = right.first; + switch (op) { + case '-': + if ('unit' == val.nodeName) { + var expr = new nodes.Expression; + val = val.clone(); + val.val = -val.val; + expr.push(this); + expr.push(val); + return expr; + } + case '+': + return new nodes.Ident(this.string + this.coerce(val).string); + } + return Node.prototype.operate.call(this, op, right); + }; }; From eb2bd0333d0081a9b45f9841dc1987253a7e70b5 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:42:35 +0200 Subject: [PATCH 11/68] use classes in lib/nodes/expression --- lib/nodes/expression.js | 378 ++++++++++++++++++++-------------------- 1 file changed, 187 insertions(+), 191 deletions(-) diff --git a/lib/nodes/expression.js b/lib/nodes/expression.js index 92293f32f..310cfc78c 100644 --- a/lib/nodes/expression.js +++ b/lib/nodes/expression.js @@ -13,208 +13,204 @@ var Node = require('./node') , nodes = require('../nodes') , utils = require('../utils'); -/** - * Initialize a new `Expression`. - * - * @param {Boolean} isList - * @api public - */ - -var Expression = module.exports = function Expression(isList){ - Node.call(this); - this.nodes = []; - this.isList = isList; -}; - -/** - * Check if the variable has a value. - * - * @return {Boolean} - * @api public - */ - -Expression.prototype.__defineGetter__('isEmpty', function(){ - return !this.nodes.length; -}); - -/** - * Return the first node in this expression. - * - * @return {Node} - * @api public - */ - -Expression.prototype.__defineGetter__('first', function(){ - return this.nodes[0] - ? this.nodes[0].first - : nodes.null; -}); - -/** - * Hash all the nodes in order. - * - * @return {String} - * @api public - */ - -Expression.prototype.__defineGetter__('hash', function(){ - return this.nodes.map(function(node){ - return node.hash; - }).join('::'); -}); +module.exports = class Expression extends Node { + /** + * Initialize a new `Expression`. + * + * @param {Boolean} isList + * @api public + */ + + constructor(isList) { + super(); + this.nodes = []; + this.isList = isList; + } -/** - * Inherit from `Node.prototype`. - */ + /** + * Check if the variable has a value. + * + * @return {Boolean} + * @api public + */ -Expression.prototype.__proto__ = Node.prototype; + isEmpty() { + return !this.nodes.length; + }; -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return the first node in this expression. + * + * @return {Node} + * @api public + */ + + first() { + return this.nodes[0] + ? this.nodes[0].first + : nodes.null; + }; -Expression.prototype.clone = function(parent){ - var clone = new this.constructor(this.isList); - clone.preserve = this.preserve; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - clone.nodes = this.nodes.map(function(node) { - return node.clone(parent, clone); - }); - return clone; -}; + /** + * Hash all the nodes in order. + * + * @return {String} + * @api public + */ + + hash() { + return this.nodes.map(function (node) { + return node.hash; + }).join('::'); + }; -/** - * Push the given `node`. - * - * @param {Node} node - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone(parent) { + var clone = new this.constructor(this.isList); + clone.preserve = this.preserve; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + clone.nodes = this.nodes.map(function (node) { + return node.clone(parent, clone); + }); + return clone; + }; -Expression.prototype.push = function(node){ - this.nodes.push(node); -}; + /** + * Push the given `node`. + * + * @param {Node} node + * @api public + */ -/** - * Operate on `right` with the given `op`. - * - * @param {String} op - * @param {Node} right - * @return {Node} - * @api public - */ + push(node) { + this.nodes.push(node); + }; -Expression.prototype.operate = function(op, right, val){ - switch (op) { - case '[]=': - var self = this - , range = utils.unwrap(right).nodes - , val = utils.unwrap(val) - , len - , node; - range.forEach(function(unit){ - len = self.nodes.length; - if ('unit' == unit.nodeName) { - var i = unit.val < 0 ? len + unit.val : unit.val - , n = i; - while (i-- > len) self.nodes[i] = nodes.null; - self.nodes[n] = val; - } else if (unit.string) { - node = self.nodes[0]; - if (node && 'object' == node.nodeName) node.set(unit.string, val.clone()); - } - }); - return val; - case '[]': - var expr = new nodes.Expression - , vals = utils.unwrap(this).nodes - , range = utils.unwrap(right).nodes - , node; - range.forEach(function(unit){ - if ('unit' == unit.nodeName) { - node = vals[unit.val < 0 ? vals.length + unit.val : unit.val]; - } else if ('object' == vals[0].nodeName) { - node = vals[0].get(unit.string); + /** + * Operate on `right` with the given `op`. + * + * @param {String} op + * @param {Node} right + * @return {Node} + * @api public + */ + + operate(op, right, val) { + switch (op) { + case '[]=': + var self = this + , range = utils.unwrap(right).nodes + , val = utils.unwrap(val) + , len + , node; + range.forEach(function (unit) { + len = self.nodes.length; + if ('unit' == unit.nodeName) { + var i = unit.val < 0 ? len + unit.val : unit.val + , n = i; + while (i-- > len) self.nodes[i] = nodes.null; + self.nodes[n] = val; + } else if (unit.string) { + node = self.nodes[0]; + if (node && 'object' == node.nodeName) node.set(unit.string, val.clone()); + } + }); + return val; + case '[]': + var expr = new nodes.Expression + , vals = utils.unwrap(this).nodes + , range = utils.unwrap(right).nodes + , node; + range.forEach(function (unit) { + if ('unit' == unit.nodeName) { + node = vals[unit.val < 0 ? vals.length + unit.val : unit.val]; + } else if ('object' == vals[0].nodeName) { + node = vals[0].get(unit.string); + } + if (node) expr.push(node); + }); + return expr.isEmpty + ? nodes.null + : utils.unwrap(expr); + case '||': + return this.toBoolean().isTrue + ? this + : right; + case 'in': + return Node.prototype.operate.call(this, op, right); + case '!=': + return this.operate('==', right, val).negate(); + case '==': + var len = this.nodes.length + , right = right.toExpression() + , a + , b; + if (len != right.nodes.length) return nodes.false; + for (var i = 0; i < len; ++i) { + a = this.nodes[i]; + b = right.nodes[i]; + if (a.operate(op, b).isTrue) continue; + return nodes.false; } - if (node) expr.push(node); - }); - return expr.isEmpty - ? nodes.null - : utils.unwrap(expr); - case '||': - return this.toBoolean().isTrue - ? this - : right; - case 'in': - return Node.prototype.operate.call(this, op, right); - case '!=': - return this.operate('==', right, val).negate(); - case '==': - var len = this.nodes.length - , right = right.toExpression() - , a - , b; - if (len != right.nodes.length) return nodes.false; - for (var i = 0; i < len; ++i) { - a = this.nodes[i]; - b = right.nodes[i]; - if (a.operate(op, b).isTrue) continue; - return nodes.false; - } - return nodes.true; - break; - default: - return this.first.operate(op, right, val); - } -}; - -/** - * Expressions with length > 1 are truthy, - * otherwise the first value's toBoolean() - * method is invoked. - * - * @return {Boolean} - * @api public - */ - -Expression.prototype.toBoolean = function(){ - if (this.nodes.length > 1) return nodes.true; - return this.first.toBoolean(); -}; - -/** - * Return " " or ", , " if - * the expression represents a list. - * - * @return {String} - * @api public - */ + return nodes.true; + break; + default: + return this.first.operate(op, right, val); + } + }; -Expression.prototype.toString = function(){ - return '(' + this.nodes.map(function(node){ - return node.toString(); - }).join(this.isList ? ', ' : ' ') + ')'; -}; + /** + * Expressions with length > 1 are truthy, + * otherwise the first value's toBoolean() + * method is invoked. + * + * @return {Boolean} + * @api public + */ + + toBoolean() { + if (this.nodes.length > 1) return nodes.true; + return this.first.toBoolean(); + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return " " or ", , " if + * the expression represents a list. + * + * @return {String} + * @api public + */ + + toString() { + return '(' + this.nodes.map(function (node) { + return node.toString(); + }).join(this.isList ? ', ' : ' ') + ')'; + }; -Expression.prototype.toJSON = function(){ - return { - __type: 'Expression', - isList: this.isList, - preserve: this.preserve, - lineno: this.lineno, - column: this.column, - filename: this.filename, - nodes: this.nodes + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Expression', + isList: this.isList, + preserve: this.preserve, + lineno: this.lineno, + column: this.column, + filename: this.filename, + nodes: this.nodes + }; }; }; From a17ed5aa3e4e71a2ec0e7027bb91080ed39641c7 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:51:49 +0200 Subject: [PATCH 12/68] fix getter typed as function --- lib/nodes/expression.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nodes/expression.js b/lib/nodes/expression.js index 310cfc78c..af95c45a3 100644 --- a/lib/nodes/expression.js +++ b/lib/nodes/expression.js @@ -34,7 +34,7 @@ module.exports = class Expression extends Node { * @api public */ - isEmpty() { + get isEmpty() { return !this.nodes.length; }; From 3da652b14a41bf98106d668435c6a81d99312d6c Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:52:10 +0200 Subject: [PATCH 13/68] use classes in lib/nodes/rgba --- lib/nodes/rgba.js | 535 +++++++++++++++++++++++----------------------- 1 file changed, 266 insertions(+), 269 deletions(-) diff --git a/lib/nodes/rgba.js b/lib/nodes/rgba.js index a961dc9cd..d48b457db 100644 --- a/lib/nodes/rgba.js +++ b/lib/nodes/rgba.js @@ -15,298 +15,294 @@ var Node = require('./node') , adjust = functions.adjust , nodes = require('./'); -/** - * Initialize a new `RGBA` with the given r,g,b,a component values. - * - * @param {Number} r - * @param {Number} g - * @param {Number} b - * @param {Number} a - * @api public - */ - -var RGBA = exports = module.exports = function RGBA(r,g,b,a){ - Node.call(this); - this.r = clamp(r); - this.g = clamp(g); - this.b = clamp(b); - this.a = clampAlpha(a); - this.name = ''; - this.rgba = this; -}; - -/** - * Inherit from `Node.prototype`. - */ - -RGBA.prototype.__proto__ = Node.prototype; - -/** - * Return an `RGBA` without clamping values. - * - * @param {Number} r - * @param {Number} g - * @param {Number} b - * @param {Number} a - * @return {RGBA} - * @api public - */ +exports = module.exports = class RGBA extends Node { + /** + * Initialize a new `RGBA` with the given r,g,b,a component values. + * + * @param {Number} r + * @param {Number} g + * @param {Number} b + * @param {Number} a + * @api public + */ + + constructor(r, g, b, a) { + super(); + this.r = clamp(r); + this.g = clamp(g); + this.b = clamp(b); + this.a = clampAlpha(a); + this.name = ''; + this.rgba = this; + } -RGBA.withoutClamping = function(r,g,b,a){ - var rgba = new RGBA(0,0,0,0); - rgba.r = r; - rgba.g = g; - rgba.b = b; - rgba.a = a; - return rgba; -}; + /** + * Return an `RGBA` without clamping values. + * + * @param {Number} r + * @param {Number} g + * @param {Number} b + * @param {Number} a + * @return {RGBA} + * @api public + */ + + static withoutClamping(r, g, b, a) { + var rgba = new RGBA(0, 0, 0, 0); + rgba.r = r; + rgba.g = g; + rgba.b = b; + rgba.a = a; + return rgba; + }; -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -RGBA.prototype.clone = function(){ - var clone = new RGBA( + clone() { + var clone = new RGBA( this.r - , this.g - , this.b - , this.a); - clone.raw = this.raw; - clone.name = this.name; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; - -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ - -RGBA.prototype.toJSON = function(){ - return { - __type: 'RGBA', - r: this.r, - g: this.g, - b: this.b, - a: this.a, - raw: this.raw, - name: this.name, - lineno: this.lineno, - column: this.column, - filename: this.filename + , this.g + , this.b + , this.a); + clone.raw = this.raw; + clone.name = this.name; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; }; -}; -/** - * Return true. - * - * @return {Boolean} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'RGBA', + r: this.r, + g: this.g, + b: this.b, + a: this.a, + raw: this.raw, + name: this.name, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + }; -RGBA.prototype.toBoolean = function(){ - return nodes.true; -}; + /** + * Return true. + * + * @return {Boolean} + * @api public + */ -/** - * Return `HSLA` representation. - * - * @return {HSLA} - * @api public - */ + toBoolean() { + return nodes.true; + }; -RGBA.prototype.__defineGetter__('hsla', function(){ - return HSLA.fromRGBA(this); -}); + /** + * Return `HSLA` representation. + * + * @return {HSLA} + * @api public + */ -/** - * Return hash. - * - * @return {String} - * @api public - */ + hsla() { + return HSLA.fromRGBA(this); + }; -RGBA.prototype.__defineGetter__('hash', function(){ - return this.toString(); -}); + /** + * Return hash. + * + * @return {String} + * @api public + */ -/** - * Add r,g,b,a to the current component values. - * - * @param {Number} r - * @param {Number} g - * @param {Number} b - * @param {Number} a - * @return {RGBA} new node - * @api public - */ + hash() { + return this.toString(); + }; -RGBA.prototype.add = function(r,g,b,a){ - return new RGBA( + /** + * Add r,g,b,a to the current component values. + * + * @param {Number} r + * @param {Number} g + * @param {Number} b + * @param {Number} a + * @return {RGBA} new node + * @api public + */ + + add(r, g, b, a) { + return new RGBA( this.r + r - , this.g + g - , this.b + b - , this.a + a); -}; - -/** - * Subtract r,g,b,a from the current component values. - * - * @param {Number} r - * @param {Number} g - * @param {Number} b - * @param {Number} a - * @return {RGBA} new node - * @api public - */ + , this.g + g + , this.b + b + , this.a + a); + }; -RGBA.prototype.sub = function(r,g,b,a){ - return new RGBA( + /** + * Subtract r,g,b,a from the current component values. + * + * @param {Number} r + * @param {Number} g + * @param {Number} b + * @param {Number} a + * @return {RGBA} new node + * @api public + */ + + sub(r, g, b, a) { + return new RGBA( this.r - r - , this.g - g - , this.b - b - , a == 1 ? this.a : this.a - a); -}; + , this.g - g + , this.b - b + , a == 1 ? this.a : this.a - a); + }; -/** - * Multiply rgb components by `n`. - * - * @param {String} n - * @return {RGBA} new node - * @api public - */ + /** + * Multiply rgb components by `n`. + * + * @param {String} n + * @return {RGBA} new node + * @api public + */ -RGBA.prototype.multiply = function(n){ - return new RGBA( + multiply(n) { + return new RGBA( this.r * n - , this.g * n - , this.b * n - , this.a); -}; + , this.g * n + , this.b * n + , this.a); + }; -/** - * Divide rgb components by `n`. - * - * @param {String} n - * @return {RGBA} new node - * @api public - */ + /** + * Divide rgb components by `n`. + * + * @param {String} n + * @return {RGBA} new node + * @api public + */ -RGBA.prototype.divide = function(n){ - return new RGBA( + divide(n) { + return new RGBA( this.r / n - , this.g / n - , this.b / n - , this.a); -}; - -/** - * Operate on `right` with the given `op`. - * - * @param {String} op - * @param {Node} right - * @return {Node} - * @api public - */ - -RGBA.prototype.operate = function(op, right){ - if ('in' != op) right = right.first - - switch (op) { - case 'is a': - if ('string' == right.nodeName && 'color' == right.string) { - return nodes.true; - } - break; - case '+': - switch (right.nodeName) { - case 'unit': - var n = right.val; - switch (right.type) { - case '%': return adjust(this, new nodes.String('lightness'), right); - case 'deg': return this.hsla.adjustHue(n).rgba; - default: return this.add(n,n,n,0); - } - case 'rgba': - return this.add(right.r, right.g, right.b, right.a); - case 'hsla': - return this.hsla.add(right.h, right.s, right.l); - } - break; - case '-': - switch (right.nodeName) { - case 'unit': - var n = right.val; - switch (right.type) { - case '%': return adjust(this, new nodes.String('lightness'), new nodes.Unit(-n, '%')); - case 'deg': return this.hsla.adjustHue(-n).rgba; - default: return this.sub(n,n,n,0); - } - case 'rgba': - return this.sub(right.r, right.g, right.b, right.a); - case 'hsla': - return this.hsla.sub(right.h, right.s, right.l); - } - break; - case '*': - switch (right.nodeName) { - case 'unit': - return this.multiply(right.val); - } - break; - case '/': - switch (right.nodeName) { - case 'unit': - return this.divide(right.val); - } - break; - } - return Node.prototype.operate.call(this, op, right); -}; + , this.g / n + , this.b / n + , this.a); + }; -/** - * Return #nnnnnn, #nnn, or rgba(n,n,n,n) string representation of the color. - * - * @return {String} - * @api public - */ + /** + * Operate on `right` with the given `op`. + * + * @param {String} op + * @param {Node} right + * @return {Node} + * @api public + */ + + operate(op, right) { + if ('in' != op) right = right.first + + switch (op) { + case 'is a': + if ('string' == right.nodeName && 'color' == right.string) { + return nodes.true; + } + break; + case '+': + switch (right.nodeName) { + case 'unit': + var n = right.val; + switch (right.type) { + case '%': return adjust(this, new nodes.String('lightness'), right); + case 'deg': return this.hsla.adjustHue(n).rgba; + default: return this.add(n, n, n, 0); + } + case 'rgba': + return this.add(right.r, right.g, right.b, right.a); + case 'hsla': + return this.hsla.add(right.h, right.s, right.l); + } + break; + case '-': + switch (right.nodeName) { + case 'unit': + var n = right.val; + switch (right.type) { + case '%': return adjust(this, new nodes.String('lightness'), new nodes.Unit(-n, '%')); + case 'deg': return this.hsla.adjustHue(-n).rgba; + default: return this.sub(n, n, n, 0); + } + case 'rgba': + return this.sub(right.r, right.g, right.b, right.a); + case 'hsla': + return this.hsla.sub(right.h, right.s, right.l); + } + break; + case '*': + switch (right.nodeName) { + case 'unit': + return this.multiply(right.val); + } + break; + case '/': + switch (right.nodeName) { + case 'unit': + return this.divide(right.val); + } + break; + } + return Node.prototype.operate.call(this, op, right); + }; -RGBA.prototype.toString = function(){ - function pad(n) { - return n < 16 - ? '0' + n.toString(16) - : n.toString(16); - } + /** + * Return #nnnnnn, #nnn, or rgba(n,n,n,n) string representation of the color. + * + * @return {String} + * @api public + */ + + toString() { + function pad(n) { + return n < 16 + ? '0' + n.toString(16) + : n.toString(16); + } - // special case for transparent named color - if ('transparent' == this.name) - return this.name; + // special case for transparent named color + if ('transparent' == this.name) + return this.name; - if (1 == this.a) { - var r = pad(this.r) - , g = pad(this.g) - , b = pad(this.b); + if (1 == this.a) { + var r = pad(this.r) + , g = pad(this.g) + , b = pad(this.b); - // Compress - if (r[0] == r[1] && g[0] == g[1] && b[0] == b[1]) { - return '#' + r[0] + g[0] + b[0]; + // Compress + if (r[0] == r[1] && g[0] == g[1] && b[0] == b[1]) { + return '#' + r[0] + g[0] + b[0]; + } else { + return '#' + r + g + b; + } } else { - return '#' + r + g + b; + return 'rgba(' + + this.r + ',' + + this.g + ',' + + this.b + ',' + + (+this.a.toFixed(3)) + ')'; } - } else { - return 'rgba(' - + this.r + ',' - + this.g + ',' - + this.b + ',' - + (+this.a.toFixed(3)) + ')'; - } + }; }; /** @@ -317,7 +313,7 @@ RGBA.prototype.toString = function(){ * @api public */ -exports.fromHSLA = function(hsla){ +exports.fromHSLA = function (hsla) { var h = hsla.h / 360 , s = hsla.s / 100 , l = hsla.l / 100 @@ -326,20 +322,21 @@ exports.fromHSLA = function(hsla){ var m2 = l <= .5 ? l * (s + 1) : l + s - l * s , m1 = l * 2 - m2; - var r = hue(h + 1/3) * 0xff + var r = hue(h + 1 / 3) * 0xff , g = hue(h) * 0xff - , b = hue(h - 1/3) * 0xff; + , b = hue(h - 1 / 3) * 0xff; function hue(h) { if (h < 0) ++h; if (h > 1) --h; if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; if (h * 2 < 1) return m2; - if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6; + if (h * 3 < 2) return m1 + (m2 - m1) * (2 / 3 - h) * 6; return m1; } - - return new RGBA(r,g,b,a); + + + return new RGBA(r, g, b, a); }; /** From cb3916ec80eb81dba04ab40bbdd89e5ca4def5ff Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:55:05 +0200 Subject: [PATCH 14/68] use classes in lib/nodes/call --- lib/nodes/call.js | 120 ++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 62 deletions(-) diff --git a/lib/nodes/call.js b/lib/nodes/call.js index 5cf655df3..42a88b796 100644 --- a/lib/nodes/call.js +++ b/lib/nodes/call.js @@ -11,75 +11,71 @@ var Node = require('./node'); -/** - * Initialize a new `Call` with `name` and `args`. - * - * @param {String} name - * @param {Expression} args - * @api public - */ +module.exports = class Call extends Node { + /** + * Initialize a new `Call` with `name` and `args`. + * + * @param {String} name + * @param {Expression} args + * @api public + */ -var Call = module.exports = function Call(name, args){ - Node.call(this); - this.name = name; - this.args = args; -}; + constructor(name, args) { + super(); + this.name = name; + this.args = args; + } -/** - * Inherit from `Node.prototype`. - */ - -Call.prototype.__proto__ = Node.prototype; - -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Call.prototype.clone = function(parent){ - var clone = new Call(this.name); - clone.args = this.args.clone(parent, clone); - if (this.block) clone.block = this.block.clone(parent, clone); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new Call(this.name); + clone.args = this.args.clone(parent, clone); + if (this.block) clone.block = this.block.clone(parent, clone); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return (param1, param2, ...). - * - * @return {String} - * @api public - */ + /** + * Return (param1, param2, ...). + * + * @return {String} + * @api public + */ -Call.prototype.toString = function(){ - var args = this.args.nodes.map(function(node) { - var str = node.toString(); - return str.slice(1, str.length - 1); - }).join(', '); + toString() { + var args = this.args.nodes.map(function (node) { + var str = node.toString(); + return str.slice(1, str.length - 1); + }).join(', '); - return this.name + '(' + args + ')'; -}; + return this.name + '(' + args + ')'; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Call.prototype.toJSON = function(){ - var json = { - __type: 'Call', - name: this.name, - args: this.args, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + var json = { + __type: 'Call', + name: this.name, + args: this.args, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + if (this.block) json.block = this.block; + return json; }; - if (this.block) json.block = this.block; - return json; }; From f3bf083527373a8420b691950afe4b8be3b6c090 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:56:54 +0200 Subject: [PATCH 15/68] use classes in lib/nodes/group --- lib/nodes/group.js | 168 ++++++++++++++++++++++----------------------- 1 file changed, 82 insertions(+), 86 deletions(-) diff --git a/lib/nodes/group.js b/lib/nodes/group.js index 8c1a4ea35..66215d650 100644 --- a/lib/nodes/group.js +++ b/lib/nodes/group.js @@ -11,100 +11,96 @@ var Node = require('./node'); -/** - * Initialize a new `Group`. - * - * @api public - */ - -var Group = module.exports = function Group(){ - Node.call(this); - this.nodes = []; - this.extends = []; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Group.prototype.__proto__ = Node.prototype; - -/** - * Push the given `selector` node. - * - * @param {Selector} selector - * @api public - */ - -Group.prototype.push = function(selector){ - this.nodes.push(selector); -}; - -/** - * Return this set's `Block`. - */ +module.exports = class Group extends Node { + /** + * Initialize a new `Group`. + * + * @api public + */ + + constructor() { + super(); + this.nodes = []; + this.extends = []; + } -Group.prototype.__defineGetter__('block', function(){ - return this.nodes[0].block; -}); + /** + * Push the given `selector` node. + * + * @param {Selector} selector + * @api public + */ -/** - * Assign `block` to each selector in this set. - * - * @param {Block} block - * @api public - */ + push(selector) { + this.nodes.push(selector); + }; -Group.prototype.__defineSetter__('block', function(block){ - for (var i = 0, len = this.nodes.length; i < len; ++i) { - this.nodes[i].block = block; - } -}); + /** + * Return this set's `Block`. + */ -/** - * Check if this set has only placeholders. - * - * @return {Boolean} - * @api public - */ + get block() { + return this.nodes[0].block; + }; -Group.prototype.__defineGetter__('hasOnlyPlaceholders', function(){ - return this.nodes.every(function(selector) { return selector.isPlaceholder; }); -}); + /** + * Assign `block` to each selector in this set. + * + * @param {Block} block + * @api public + */ + + set block(block) { + for (var i = 0, len = this.nodes.length; i < len; ++i) { + this.nodes[i].block = block; + } + }; -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Check if this set has only placeholders. + * + * @return {Boolean} + * @api public + */ -Group.prototype.clone = function(parent){ - var clone = new Group; - clone.lineno = this.lineno; - clone.column = this.column; - this.nodes.forEach(function(node){ - clone.push(node.clone(parent, clone)); - }); - clone.filename = this.filename; - clone.block = this.block.clone(parent, clone); - return clone; -}; + get hasOnlyPlaceholders() { + return this.nodes.every(function (selector) { return selector.isPlaceholder; }); + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone(parent) { + var clone = new Group; + clone.lineno = this.lineno; + clone.column = this.column; + this.nodes.forEach(function (node) { + clone.push(node.clone(parent, clone)); + }); + clone.filename = this.filename; + clone.block = this.block.clone(parent, clone); + return clone; + }; -Group.prototype.toJSON = function(){ - return { - __type: 'Group', - nodes: this.nodes, - block: this.block, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Group', + nodes: this.nodes, + block: this.block, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; }; From 7a9bd09b843ffc6f5f26c6d00dbb8da76d91b96e Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:58:29 +0200 Subject: [PATCH 16/68] use classes in lib/nodes/literal --- lib/nodes/literal.js | 175 +++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 89 deletions(-) diff --git a/lib/nodes/literal.js b/lib/nodes/literal.js index 9c6e86653..68c406df9 100644 --- a/lib/nodes/literal.js +++ b/lib/nodes/literal.js @@ -12,101 +12,98 @@ var Node = require('./node') , nodes = require('./'); -/** - * Initialize a new `Literal` with the given `str`. - * - * @param {String} str - * @api public - */ - -var Literal = module.exports = function Literal(str){ - Node.call(this); - this.val = str; - this.string = str; - this.prefixed = false; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Literal.prototype.__proto__ = Node.prototype; - -/** - * Return hash. - * - * @return {String} - * @api public - */ - -Literal.prototype.__defineGetter__('hash', function(){ - return this.val; -}); - -/** - * Return literal value. - * - * @return {String} - * @api public - */ +module.exports = class Literal extends Node { + /** + * Initialize a new `Literal` with the given `str`. + * + * @param {String} str + * @api public + */ + + constructor(str) { + super(); + this.val = str; + this.string = str; + this.prefixed = false; + } -Literal.prototype.toString = function(){ - return this.val.toString(); -}; + /** + * Return hash. + * + * @return {String} + * @api public + */ -/** - * Coerce `other` to a literal. - * - * @param {Node} other - * @return {String} - * @api public - */ + get hash() { + return this.val; + }; -Literal.prototype.coerce = function(other){ - switch (other.nodeName) { - case 'ident': - case 'string': - case 'literal': - return new Literal(other.string); - default: - return Node.prototype.coerce.call(this, other); - } -}; + /** + * Return literal value. + * + * @return {String} + * @api public + */ -/** - * Operate on `right` with the given `op`. - * - * @param {String} op - * @param {Node} right - * @return {Node} - * @api public - */ + toString() { + return this.val.toString(); + }; -Literal.prototype.operate = function(op, right){ - var val = right.first; - switch (op) { - case '+': - return new nodes.Literal(this.string + this.coerce(val).string); - default: - return Node.prototype.operate.call(this, op, right); - } -}; + /** + * Coerce `other` to a literal. + * + * @param {Node} other + * @return {String} + * @api public + */ + + coerce(other) { + switch (other.nodeName) { + case 'ident': + case 'string': + case 'literal': + return new Literal(other.string); + default: + return Node.prototype.coerce.call(this, other); + } + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Operate on `right` with the given `op`. + * + * @param {String} op + * @param {Node} right + * @return {Node} + * @api public + */ + + operate(op, right) { + var val = right.first; + switch (op) { + case '+': + return new nodes.Literal(this.string + this.coerce(val).string); + default: + return Node.prototype.operate.call(this, op, right); + } + }; -Literal.prototype.toJSON = function(){ - return { - __type: 'Literal', - val: this.val, - string: this.string, - prefixed: this.prefixed, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Literal', + val: this.val, + string: this.string, + prefixed: this.prefixed, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; + }; From 0d9bab982dcc9dc393814d64603ce5ba66c2c391 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 10:59:57 +0200 Subject: [PATCH 17/68] use class in lib/nodes/selector --- lib/nodes/selector.js | 135 +++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 69 deletions(-) diff --git a/lib/nodes/selector.js b/lib/nodes/selector.js index db183cc7f..7df0398df 100644 --- a/lib/nodes/selector.js +++ b/lib/nodes/selector.js @@ -12,83 +12,80 @@ var Block = require('./block') , Node = require('./node'); -/** - * Initialize a new `Selector` with the given `segs`. - * - * @param {Array} segs - * @api public - */ - -var Selector = module.exports = function Selector(segs){ - Node.call(this); - this.inherits = true; - this.segments = segs; - this.optional = false; -}; +module.exports = class Selector extends Node { + /** + * Initialize a new `Selector` with the given `segs`. + * + * @param {Array} segs + * @api public + */ -/** - * Inherit from `Node.prototype`. - */ + constructor(segs) { + super(); + this.inherits = true; + this.segments = segs; + this.optional = false; + } -Selector.prototype.__proto__ = Node.prototype; + /** + * Return the selector string. + * + * @return {String} + * @api public + */ -/** - * Return the selector string. - * - * @return {String} - * @api public - */ + toString() { + return this.segments.join('') + (this.optional ? ' !optional' : ''); + }; -Selector.prototype.toString = function(){ - return this.segments.join('') + (this.optional ? ' !optional' : ''); -}; + /** + * Check if this is placeholder selector. + * + * @return {Boolean} + * @api public + */ -/** - * Check if this is placeholder selector. - * - * @return {Boolean} - * @api public - */ - -Selector.prototype.__defineGetter__('isPlaceholder', function(){ - return this.val && ~this.val.substr(0, 2).indexOf('$'); -}); + get isPlaceholder() { + return this.val && ~this.val.substr(0, 2).indexOf('$'); + }; -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Selector.prototype.clone = function(parent){ - var clone = new Selector; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - clone.inherits = this.inherits; - clone.val = this.val; - clone.segments = this.segments.map(function(node){ return node.clone(parent, clone); }); - clone.optional = this.optional; - return clone; -}; + clone(parent) { + var clone = new Selector; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + clone.inherits = this.inherits; + clone.val = this.val; + clone.segments = this.segments.map(function (node) { return node.clone(parent, clone); }); + clone.optional = this.optional; + return clone; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Selector.prototype.toJSON = function(){ - return { - __type: 'Selector', - inherits: this.inherits, - segments: this.segments, - optional: this.optional, - val: this.val, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + return { + __type: 'Selector', + inherits: this.inherits, + segments: this.segments, + optional: this.optional, + val: this.val, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; + }; From ba6b8d4a5b630e6f7b205b043b648a491e15185b Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:01:39 +0200 Subject: [PATCH 18/68] use class in lib/nodes/block --- lib/nodes/block.js | 201 ++++++++++++++++++++++----------------------- 1 file changed, 99 insertions(+), 102 deletions(-) diff --git a/lib/nodes/block.js b/lib/nodes/block.js index b80af817b..082cc9097 100644 --- a/lib/nodes/block.js +++ b/lib/nodes/block.js @@ -11,117 +11,114 @@ var Node = require('./node'); -/** - * Initialize a new `Block` node with `parent` Block. - * - * @param {Block} parent - * @api public - */ - -var Block = module.exports = function Block(parent, node){ - Node.call(this); - this.nodes = []; - this.parent = parent; - this.node = node; - this.scope = true; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Block.prototype.__proto__ = Node.prototype; - -/** - * Check if this block has properties.. - * - * @return {Boolean} - * @api public - */ - -Block.prototype.__defineGetter__('hasProperties', function(){ - for (var i = 0, len = this.nodes.length; i < len; ++i) { - if ('property' == this.nodes[i].nodeName) { - return true; - } +module.exports = class Block extends Node { + /** + * Initialize a new `Block` node with `parent` Block. + * + * @param {Block} parent + * @api public + */ + + constructor(parent, node) { + super(); + this.nodes = []; + this.parent = parent; + this.node = node; + this.scope = true; } -}); -/** - * Check if this block has @media nodes. - * - * @return {Boolean} - * @api public - */ - -Block.prototype.__defineGetter__('hasMedia', function(){ - for (var i = 0, len = this.nodes.length; i < len; ++i) { - var nodeName = this.nodes[i].nodeName; - if ('media' == nodeName) { - return true; + /** + * Check if this block has properties.. + * + * @return {Boolean} + * @api public + */ + + get hasProperties() { + for (var i = 0, len = this.nodes.length; i < len; ++i) { + if ('property' == this.nodes[i].nodeName) { + return true; + } } - } - return false; -}); - -/** - * Check if this block is empty. - * - * @return {Boolean} - * @api public - */ + }; -Block.prototype.__defineGetter__('isEmpty', function(){ - return !this.nodes.length || this.nodes.every(function(n){return n.nodeName == 'comment'}); -}); + /** + * Check if this block has @media nodes. + * + * @return {Boolean} + * @api public + */ + + get hasMedia() { + for (var i = 0, len = this.nodes.length; i < len; ++i) { + var nodeName = this.nodes[i].nodeName; + if ('media' == nodeName) { + return true; + } + } + return false; + }; -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Check if this block is empty. + * + * @return {Boolean} + * @api public + */ -Block.prototype.clone = function(parent, node){ - parent = parent || this.parent; - var clone = new Block(parent, node || this.node); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - clone.scope = this.scope; - this.nodes.forEach(function(node){ - clone.push(node.clone(clone, clone)); - }); - return clone; -}; + get isEmpty() { + return !this.nodes.length || this.nodes.every(function (n) { return n.nodeName == 'comment' }); + }; -/** - * Push a `node` to this block. - * - * @param {Node} node - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone(parent, node) { + parent = parent || this.parent; + var clone = new Block(parent, node || this.node); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + clone.scope = this.scope; + this.nodes.forEach(function (node) { + clone.push(node.clone(clone, clone)); + }); + return clone; + }; -Block.prototype.push = function(node){ - this.nodes.push(node); -}; + /** + * Push a `node` to this block. + * + * @param {Node} node + * @api public + */ -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + push(node) { + this.nodes.push(node); + }; -Block.prototype.toJSON = function(){ - return { - __type: 'Block', - // parent: this.parent, - // node: this.node, - scope: this.scope, - lineno: this.lineno, - column: this.column, - filename: this.filename, - nodes: this.nodes + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Block', + // parent: this.parent, + // node: this.node, + scope: this.scope, + lineno: this.lineno, + column: this.column, + filename: this.filename, + nodes: this.nodes + }; }; + }; From 897db88a4164340d9cff13003a1521e3cedc7e17 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:02:53 +0200 Subject: [PATCH 19/68] use class in lib/nodes/property --- lib/nodes/property.js | 140 ++++++++++++++++++++---------------------- 1 file changed, 68 insertions(+), 72 deletions(-) diff --git a/lib/nodes/property.js b/lib/nodes/property.js index 215785ac3..ef738bca1 100644 --- a/lib/nodes/property.js +++ b/lib/nodes/property.js @@ -11,86 +11,82 @@ var Node = require('./node'); -/** - * Initialize a new `Property` with the given `segs` and optional `expr`. - * - * @param {Array} segs - * @param {Expression} expr - * @api public - */ - -var Property = module.exports = function Property(segs, expr){ - Node.call(this); - this.segments = segs; - this.expr = expr; -}; +module.exports = class Property extends Node { + /** + * Initialize a new `Property` with the given `segs` and optional `expr`. + * + * @param {Array} segs + * @param {Expression} expr + * @api public + */ -/** - * Inherit from `Node.prototype`. - */ + constructor(segs, expr) { + super(); + this.segments = segs; + this.expr = expr; + } -Property.prototype.__proto__ = Node.prototype; + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + clone(parent) { + var clone = new Property(this.segments); + clone.name = this.name; + if (this.literal) clone.literal = this.literal; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + clone.segments = this.segments.map(function (node) { return node.clone(parent, clone); }); + if (this.expr) clone.expr = this.expr.clone(parent, clone); + return clone; + }; -Property.prototype.clone = function(parent){ - var clone = new Property(this.segments); - clone.name = this.name; - if (this.literal) clone.literal = this.literal; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - clone.segments = this.segments.map(function(node){ return node.clone(parent, clone); }); - if (this.expr) clone.expr = this.expr.clone(parent, clone); - return clone; -}; + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ - -Property.prototype.toJSON = function(){ - var json = { - __type: 'Property', - segments: this.segments, - name: this.name, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + var json = { + __type: 'Property', + segments: this.segments, + name: this.name, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + if (this.expr) json.expr = this.expr; + if (this.literal) json.literal = this.literal; + return json; }; - if (this.expr) json.expr = this.expr; - if (this.literal) json.literal = this.literal; - return json; -}; -/** - * Return string representation of this node. - * - * @return {String} - * @api public - */ + /** + * Return string representation of this node. + * + * @return {String} + * @api public + */ -Property.prototype.toString = function(){ - return 'property(' + this.segments.join('') + ', ' + this.expr + ')'; -}; + toString() { + return 'property(' + this.segments.join('') + ', ' + this.expr + ')'; + }; -/** - * Operate on the property expression. - * - * @param {String} op - * @param {Node} right - * @return {Node} - * @api public - */ + /** + * Operate on the property expression. + * + * @param {String} op + * @param {Node} right + * @return {Node} + * @api public + */ -Property.prototype.operate = function(op, right, val){ - return this.expr.operate(op, right, val); + operate(op, right, val) { + return this.expr.operate(op, right, val); + }; }; From 5eaab949ae1eca5621950c407fcde35ae4cecad1 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:04:40 +0200 Subject: [PATCH 20/68] use class in lib/nodes/unit --- lib/nodes/unit.js | 346 +++++++++++++++++++++++----------------------- 1 file changed, 171 insertions(+), 175 deletions(-) diff --git a/lib/nodes/unit.js b/lib/nodes/unit.js index 2d38d84fa..4390c93bd 100644 --- a/lib/nodes/unit.js +++ b/lib/nodes/unit.js @@ -17,198 +17,194 @@ var Node = require('./node') */ var FACTOR_TABLE = { - 'mm': {val: 1, label: 'mm'}, - 'cm': {val: 10, label: 'mm'}, - 'in': {val: 25.4, label: 'mm'}, - 'pt': {val: 25.4/72, label: 'mm'}, - 'ms': {val: 1, label: 'ms'}, - 's': {val: 1000, label: 'ms'}, - 'Hz': {val: 1, label: 'Hz'}, - 'kHz': {val: 1000, label: 'Hz'} + 'mm': { val: 1, label: 'mm' }, + 'cm': { val: 10, label: 'mm' }, + 'in': { val: 25.4, label: 'mm' }, + 'pt': { val: 25.4 / 72, label: 'mm' }, + 'ms': { val: 1, label: 'ms' }, + 's': { val: 1000, label: 'ms' }, + 'Hz': { val: 1, label: 'Hz' }, + 'kHz': { val: 1000, label: 'Hz' } }; -/** - * Initialize a new `Unit` with the given `val` and unit `type` - * such as "px", "pt", "in", etc. - * - * @param {String} val - * @param {String} type - * @api public - */ - -var Unit = module.exports = function Unit(val, type){ - Node.call(this); - this.val = val; - this.type = type; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Unit.prototype.__proto__ = Node.prototype; +module.exports = class Unit extends Node { + /** + * Initialize a new `Unit` with the given `val` and unit `type` + * such as "px", "pt", "in", etc. + * + * @param {String} val + * @param {String} type + * @api public + */ + + constructor(val, type) { + super(); + this.val = val; + this.type = type; + } -/** - * Return Boolean based on the unit value. - * - * @return {Boolean} - * @api public - */ + /** + * Return Boolean based on the unit value. + * + * @return {Boolean} + * @api public + */ -Unit.prototype.toBoolean = function(){ - return nodes.Boolean(this.type + toBoolean() { + return nodes.Boolean(this.type ? true : this.val); -}; - -/** - * Return unit string. - * - * @return {String} - * @api public - */ - -Unit.prototype.toString = function(){ - return this.val + (this.type || ''); -}; - -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -Unit.prototype.clone = function(){ - var clone = new Unit(this.val, this.type); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return unit string. + * + * @return {String} + * @api public + */ -Unit.prototype.toJSON = function(){ - return { - __type: 'Unit', - val: this.val, - type: this.type, - lineno: this.lineno, - column: this.column, - filename: this.filename + toString() { + return this.val + (this.type || ''); }; -}; -/** - * Operate on `right` with the given `op`. - * - * @param {String} op - * @param {Node} right - * @return {Node} - * @api public - */ - -Unit.prototype.operate = function(op, right){ - var type = this.type || right.first.type; + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone() { + var clone = new Unit(this.val, this.type); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; - // swap color - if ('rgba' == right.nodeName || 'hsla' == right.nodeName) { - return right.operate(op, this); - } + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Unit', + val: this.val, + type: this.type, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + }; - // operate - if (this.shouldCoerce(op)) { - right = right.first; - // percentages - if ('%' != this.type && ('-' == op || '+' == op) && '%' == right.type) { - right = new Unit(this.val * (right.val / 100), '%'); - } else { - right = this.coerce(right); + /** + * Operate on `right` with the given `op`. + * + * @param {String} op + * @param {Node} right + * @return {Node} + * @api public + */ + + operate(op, right) { + var type = this.type || right.first.type; + + // swap color + if ('rgba' == right.nodeName || 'hsla' == right.nodeName) { + return right.operate(op, this); } - switch (op) { - case '-': - return new Unit(this.val - right.val, type); - case '+': - // keyframes interpolation - type = type || (right.type == '%' && right.type); - return new Unit(this.val + right.val, type); - case '/': - return new Unit(this.val / right.val, type); - case '*': - return new Unit(this.val * right.val, type); - case '%': - return new Unit(this.val % right.val, type); - case '**': - return new Unit(Math.pow(this.val, right.val), type); - case '..': - case '...': - var start = this.val - , end = right.val - , expr = new nodes.Expression - , inclusive = '..' == op; - if (start < end) { - do { - expr.push(new nodes.Unit(start)); - } while (inclusive ? ++start <= end : ++start < end); - } else { - do { - expr.push(new nodes.Unit(start)); - } while (inclusive ? --start >= end : --start > end); - } - return expr; + // operate + if (this.shouldCoerce(op)) { + right = right.first; + // percentages + if ('%' != this.type && ('-' == op || '+' == op) && '%' == right.type) { + right = new Unit(this.val * (right.val / 100), '%'); + } else { + right = this.coerce(right); + } + + switch (op) { + case '-': + return new Unit(this.val - right.val, type); + case '+': + // keyframes interpolation + type = type || (right.type == '%' && right.type); + return new Unit(this.val + right.val, type); + case '/': + return new Unit(this.val / right.val, type); + case '*': + return new Unit(this.val * right.val, type); + case '%': + return new Unit(this.val % right.val, type); + case '**': + return new Unit(Math.pow(this.val, right.val), type); + case '..': + case '...': + var start = this.val + , end = right.val + , expr = new nodes.Expression + , inclusive = '..' == op; + if (start < end) { + do { + expr.push(new nodes.Unit(start)); + } while (inclusive ? ++start <= end : ++start < end); + } else { + do { + expr.push(new nodes.Unit(start)); + } while (inclusive ? --start >= end : --start > end); + } + return expr; + } } - } - - return Node.prototype.operate.call(this, op, right); -}; - -/** - * Coerce `other` unit to the same type as `this` unit. - * - * Supports: - * - * mm -> cm | in - * cm -> mm | in - * in -> mm | cm - * - * ms -> s - * s -> ms - * - * Hz -> kHz - * kHz -> Hz - * - * @param {Unit} other - * @return {Unit} - * @api public - */ -Unit.prototype.coerce = function(other){ - if ('unit' == other.nodeName) { - var a = this - , b = other - , factorA = FACTOR_TABLE[a.type] - , factorB = FACTOR_TABLE[b.type]; + return Node.prototype.operate.call(this, op, right); + }; - if (factorA && factorB && (factorA.label == factorB.label)) { - var bVal = b.val * (factorB.val / factorA.val); - return new nodes.Unit(bVal, a.type); + /** + * Coerce `other` unit to the same type as `this` unit. + * + * Supports: + * + * mm -> cm | in + * cm -> mm | in + * in -> mm | cm + * + * ms -> s + * s -> ms + * + * Hz -> kHz + * kHz -> Hz + * + * @param {Unit} other + * @return {Unit} + * @api public + */ + + coerce(other) { + if ('unit' == other.nodeName) { + var a = this + , b = other + , factorA = FACTOR_TABLE[a.type] + , factorB = FACTOR_TABLE[b.type]; + + if (factorA && factorB && (factorA.label == factorB.label)) { + var bVal = b.val * (factorB.val / factorA.val); + return new nodes.Unit(bVal, a.type); + } else { + return new nodes.Unit(b.val, a.type); + } + } else if ('string' == other.nodeName) { + // keyframes interpolation + if ('%' == other.val) return new nodes.Unit(0, '%'); + var val = parseFloat(other.val); + if (isNaN(val)) Node.prototype.coerce.call(this, other); + return new nodes.Unit(val); } else { - return new nodes.Unit(b.val, a.type); + return Node.prototype.coerce.call(this, other); } - } else if ('string' == other.nodeName) { - // keyframes interpolation - if ('%' == other.val) return new nodes.Unit(0, '%'); - var val = parseFloat(other.val); - if (isNaN(val)) Node.prototype.coerce.call(this, other); - return new nodes.Unit(val); - } else { - return Node.prototype.coerce.call(this, other); - } + }; }; From c14d66aa67888e1730e880bb97c64bd4e119a75c Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:05:51 +0200 Subject: [PATCH 21/68] use class in lib/nodes/binop --- lib/nodes/binop.js | 121 ++++++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 62 deletions(-) diff --git a/lib/nodes/binop.js b/lib/nodes/binop.js index c56aee398..f6b5741d4 100644 --- a/lib/nodes/binop.js +++ b/lib/nodes/binop.js @@ -11,73 +11,70 @@ var Node = require('./node'); -/** - * Initialize a new `BinOp` with `op`, `left` and `right`. - * - * @param {String} op - * @param {Node} left - * @param {Node} right - * @api public - */ +module.exports = class BinOp extends Node { + /** + * Initialize a new `BinOp` with `op`, `left` and `right`. + * + * @param {String} op + * @param {Node} left + * @param {Node} right + * @api public + */ -var BinOp = module.exports = function BinOp(op, left, right){ - Node.call(this); - this.op = op; - this.left = left; - this.right = right; -}; + constructor(op, left, right) { + super(); + this.op = op; + this.left = left; + this.right = right; + } -/** - * Inherit from `Node.prototype`. - */ - -BinOp.prototype.__proto__ = Node.prototype; - -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -BinOp.prototype.clone = function(parent){ - var clone = new BinOp(this.op); - clone.left = this.left.clone(parent, clone); - clone.right = this.right && this.right.clone(parent, clone); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - if (this.val) clone.val = this.val.clone(parent, clone); - return clone; -}; + clone(parent) { + var clone = new BinOp(this.op); + clone.left = this.left.clone(parent, clone); + clone.right = this.right && this.right.clone(parent, clone); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + if (this.val) clone.val = this.val.clone(parent, clone); + return clone; + }; -/** - * Return - * - * @return {String} - * @api public - */ -BinOp.prototype.toString = function() { - return this.left.toString() + ' ' + this.op + ' ' + this.right.toString(); -}; + /** + * Return + * + * @return {String} + * @api public + */ + toString() { + return this.left.toString() + ' ' + this.op + ' ' + this.right.toString(); + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -BinOp.prototype.toJSON = function(){ - var json = { - __type: 'BinOp', - left: this.left, - right: this.right, - op: this.op, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + var json = { + __type: 'BinOp', + left: this.left, + right: this.right, + op: this.op, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + if (this.val) json.val = this.val; + return json; }; - if (this.val) json.val = this.val; - return json; + }; From b606e179a0f83686fbeea613ccd7e53f7e9019dd Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:07:14 +0200 Subject: [PATCH 22/68] use class in lib/nodes/function --- lib/nodes/function.js | 205 +++++++++++++++++++++--------------------- 1 file changed, 101 insertions(+), 104 deletions(-) diff --git a/lib/nodes/function.js b/lib/nodes/function.js index 25a6586bc..667da34a0 100644 --- a/lib/nodes/function.js +++ b/lib/nodes/function.js @@ -11,118 +11,115 @@ var Node = require('./node'); -/** - * Initialize a new `Function` with `name`, `params`, and `body`. - * - * @param {String} name - * @param {Params|Function} params - * @param {Block} body - * @api public - */ - -var Function = module.exports = function Function(name, params, body){ - Node.call(this); - this.name = name; - this.params = params; - this.block = body; - if ('function' == typeof params) this.fn = params; -}; - -/** - * Check function arity. - * - * @return {Boolean} - * @api public - */ +module.exports = class Function extends Node { + /** + * Initialize a new `Function` with `name`, `params`, and `body`. + * + * @param {String} name + * @param {Params|Function} params + * @param {Block} body + * @api public + */ + + constructor(name, params, body) { + super(); + this.name = name; + this.params = params; + this.block = body; + if ('function' == typeof params) this.fn = params; + } -Function.prototype.__defineGetter__('arity', function(){ - return this.params.length; -}); + /** + * Check function arity. + * + * @return {Boolean} + * @api public + */ -/** - * Inherit from `Node.prototype`. - */ + get arity() { + return this.params.length; + }; -Function.prototype.__proto__ = Node.prototype; + /** + * Return hash. + * + * @return {String} + * @api public + */ -/** - * Return hash. - * - * @return {String} - * @api public - */ + get hash() { + return 'function ' + this.name; + }; -Function.prototype.__defineGetter__('hash', function(){ - return 'function ' + this.name; -}); + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -Function.prototype.clone = function(parent){ - if (this.fn) { - var clone = new Function( + clone(parent) { + if (this.fn) { + var clone = new Function( this.name - , this.fn); - } else { - var clone = new Function(this.name); - clone.params = this.params.clone(parent, clone); - clone.block = this.block.clone(parent, clone); - } - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; - -/** - * Return (param1, param2, ...). - * - * @return {String} - * @api public - */ - -Function.prototype.toString = function(){ - if (this.fn) { - return this.name - + '(' - + this.fn.toString() - .match(/^function *\w*\((.*?)\)/) - .slice(1) - .join(', ') - + ')'; - } else { - return this.name - + '(' - + this.params.nodes.join(', ') - + ')'; - } -}; + , this.fn); + } else { + var clone = new Function(this.name); + clone.params = this.params.clone(parent, clone); + clone.block = this.block.clone(parent, clone); + } + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return (param1, param2, ...). + * + * @return {String} + * @api public + */ + + toString() { + if (this.fn) { + return this.name + + '(' + + this.fn.toString() + .match(/^function *\w*\((.*?)\)/) + .slice(1) + .join(', ') + + ')'; + } else { + return this.name + + '(' + + this.params.nodes.join(', ') + + ')'; + } + }; -Function.prototype.toJSON = function(){ - var json = { - __type: 'Function', - name: this.name, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + var json = { + __type: 'Function', + name: this.name, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + if (this.fn) { + json.fn = this.fn; + } else { + json.params = this.params; + json.block = this.block; + } + return json; }; - if (this.fn) { - json.fn = this.fn; - } else { - json.params = this.params; - json.block = this.block; - } - return json; + }; From 0f896c06e65598a94c8a6edd1f8b9f5aeb17cc17 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:08:24 +0200 Subject: [PATCH 23/68] use class in lib/nodes/string --- lib/nodes/string.js | 237 ++++++++++++++++++++++---------------------- 1 file changed, 117 insertions(+), 120 deletions(-) diff --git a/lib/nodes/string.js b/lib/nodes/string.js index 1fd2b4504..440a6ee10 100644 --- a/lib/nodes/string.js +++ b/lib/nodes/string.js @@ -13,135 +13,132 @@ var Node = require('./node') , utils = require('../utils') , nodes = require('./'); -/** - * Initialize a new `String` with the given `val`. - * - * @param {String} val - * @param {String} quote - * @api public - */ - -var String = module.exports = function String(val, quote){ - Node.call(this); - this.val = val; - this.string = val; - this.prefixed = false; - if (typeof quote !== 'string') { - this.quote = "'"; - } else { - this.quote = quote; +module.exports = class String extends Node { + /** + * Initialize a new `String` with the given `val`. + * + * @param {String} val + * @param {String} quote + * @api public + */ + + constructor(val, quote) { + super(); + this.val = val; + this.string = val; + this.prefixed = false; + if (typeof quote !== 'string') { + this.quote = "'"; + } else { + this.quote = quote; + } } -}; - -/** - * Inherit from `Node.prototype`. - */ - -String.prototype.__proto__ = Node.prototype; - -/** - * Return quoted string. - * - * @return {String} - * @api public - */ -String.prototype.toString = function(){ - return this.quote + this.val + this.quote; -}; + /** + * Return quoted string. + * + * @return {String} + * @api public + */ -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -String.prototype.clone = function(){ - var clone = new String(this.val, this.quote); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; - -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + toString() { + return this.quote + this.val + this.quote; + }; -String.prototype.toJSON = function(){ - return { - __type: 'String', - val: this.val, - quote: this.quote, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone() { + var clone = new String(this.val, this.quote); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; }; -}; -/** - * Return Boolean based on the length of this string. - * - * @return {Boolean} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'String', + val: this.val, + quote: this.quote, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + }; -String.prototype.toBoolean = function(){ - return nodes.Boolean(this.val.length); -}; + /** + * Return Boolean based on the length of this string. + * + * @return {Boolean} + * @api public + */ -/** - * Coerce `other` to a string. - * - * @param {Node} other - * @return {String} - * @api public - */ + toBoolean() { + return nodes.Boolean(this.val.length); + }; -String.prototype.coerce = function(other){ - switch (other.nodeName) { - case 'string': - return other; - case 'expression': - return new String(other.nodes.map(function(node){ - return this.coerce(node).val; - }, this).join(' ')); - default: - return new String(other.toString()); - } -}; + /** + * Coerce `other` to a string. + * + * @param {Node} other + * @return {String} + * @api public + */ + + coerce(other) { + switch (other.nodeName) { + case 'string': + return other; + case 'expression': + return new String(other.nodes.map(function (node) { + return this.coerce(node).val; + }, this).join(' ')); + default: + return new String(other.toString()); + } + }; -/** - * Operate on `right` with the given `op`. - * - * @param {String} op - * @param {Node} right - * @return {Node} - * @api public - */ + /** + * Operate on `right` with the given `op`. + * + * @param {String} op + * @param {Node} right + * @return {Node} + * @api public + */ + + operate(op, right) { + switch (op) { + case '%': + var expr = new nodes.Expression; + expr.push(this); + + // constructargs + var args = 'expression' == right.nodeName + ? utils.unwrap(right).nodes + : [right]; + + // apply + return sprintf.apply(null, [expr].concat(args)); + case '+': + var expr = new nodes.Expression; + expr.push(new String(this.val + this.coerce(right).val)); + return expr; + default: + return Node.prototype.operate.call(this, op, right); + } + }; -String.prototype.operate = function(op, right){ - switch (op) { - case '%': - var expr = new nodes.Expression; - expr.push(this); - - // constructargs - var args = 'expression' == right.nodeName - ? utils.unwrap(right).nodes - : [right]; - - // apply - return sprintf.apply(null, [expr].concat(args)); - case '+': - var expr = new nodes.Expression; - expr.push(new String(this.val + this.coerce(right).val)); - return expr; - default: - return Node.prototype.operate.call(this, op, right); - } }; From 173791031cce12765ac5d26424d37f4bcf471f17 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:09:26 +0200 Subject: [PATCH 24/68] use class in lib/nodes/import --- lib/nodes/import.js | 93 ++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 48 deletions(-) diff --git a/lib/nodes/import.js b/lib/nodes/import.js index 7a3548a4b..3c7be3044 100644 --- a/lib/nodes/import.js +++ b/lib/nodes/import.js @@ -11,58 +11,55 @@ var Node = require('./node'); -/** - * Initialize a new `Import` with the given `expr`. - * - * @param {Expression} expr - * @api public - */ - -var Import = module.exports = function Import(expr, once){ - Node.call(this); - this.path = expr; - this.once = once || false; -}; - -/** - * Inherit from `Node.prototype`. - */ +module.exports = class Import extends Node { + /** + * Initialize a new `Import` with the given `expr`. + * + * @param {Expression} expr + * @api public + */ -Import.prototype.__proto__ = Node.prototype; + constructor(expr, once) { + super(); + this.path = expr; + this.once = once || false; + } -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Import.prototype.clone = function(parent){ - var clone = new Import(); - clone.path = this.path.nodeName ? this.path.clone(parent, clone) : this.path; - clone.once = this.once; - clone.mtime = this.mtime; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new Import(); + clone.path = this.path.nodeName ? this.path.clone(parent, clone) : this.path; + clone.once = this.once; + clone.mtime = this.mtime; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Import.prototype.toJSON = function(){ - return { - __type: 'Import', - path: this.path, - once: this.once, - mtime: this.mtime, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + return { + __type: 'Import', + path: this.path, + once: this.once, + mtime: this.mtime, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; + }; From 7a0b7804915b4d97193cdcdb3aad17de36468eca Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:13:42 +0200 Subject: [PATCH 25/68] fix expression getters written as functions --- lib/nodes/expression.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/nodes/expression.js b/lib/nodes/expression.js index af95c45a3..047331895 100644 --- a/lib/nodes/expression.js +++ b/lib/nodes/expression.js @@ -45,7 +45,7 @@ module.exports = class Expression extends Node { * @api public */ - first() { + get first() { return this.nodes[0] ? this.nodes[0].first : nodes.null; @@ -58,7 +58,7 @@ module.exports = class Expression extends Node { * @api public */ - hash() { + get hash() { return this.nodes.map(function (node) { return node.hash; }).join('::'); From dbe78d2daecfc74291350810afe9037fe1335619 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:16:04 +0200 Subject: [PATCH 26/68] use class in lib/nodes/param --- lib/nodes/params.js | 137 +++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 71 deletions(-) diff --git a/lib/nodes/params.js b/lib/nodes/params.js index d7ac6d744..446c384e3 100644 --- a/lib/nodes/params.js +++ b/lib/nodes/params.js @@ -11,80 +11,75 @@ var Node = require('./node'); -/** - * Initialize a new `Params` with `name`, `params`, and `body`. - * - * @param {String} name - * @param {Params} params - * @param {Expression} body - * @api public - */ - -var Params = module.exports = function Params(){ - Node.call(this); - this.nodes = []; -}; - -/** - * Check function arity. - * - * @return {Boolean} - * @api public - */ - -Params.prototype.__defineGetter__('length', function(){ - return this.nodes.length; -}); - -/** - * Inherit from `Node.prototype`. - */ - -Params.prototype.__proto__ = Node.prototype; - -/** - * Push the given `node`. - * - * @param {Node} node - * @api public - */ - -Params.prototype.push = function(node){ - this.nodes.push(node); -}; +module.exports = class Params extends Node { + /** + * Initialize a new `Params` with `name`, `params`, and `body`. + * + * @param {String} name + * @param {Params} params + * @param {Expression} body + * @api public + */ + + constructor() { + super(); + this.nodes = []; + } + + /** + * Check function arity. + * + * @return {Boolean} + * @api public + */ + + get length() { + return this.nodes.length; + }; -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Push the given `node`. + * + * @param {Node} node + * @api public + */ -Params.prototype.clone = function(parent){ - var clone = new Params; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - this.nodes.forEach(function(node){ - clone.push(node.clone(parent, clone)); - }); - return clone; -}; + push(node) { + this.nodes.push(node); + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone(parent) { + var clone = new Params; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + this.nodes.forEach(function (node) { + clone.push(node.clone(parent, clone)); + }); + return clone; + }; -Params.prototype.toJSON = function(){ - return { - __type: 'Params', - nodes: this.nodes, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Params', + nodes: this.nodes, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; }; - From 54f52af968651cd72caf7573a85d246dafd3f5f4 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:17:08 +0200 Subject: [PATCH 27/68] use class in lib/nodes/if --- lib/nodes/if.js | 110 +++++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 57 deletions(-) diff --git a/lib/nodes/if.js b/lib/nodes/if.js index e7d354d2b..95eb4a557 100644 --- a/lib/nodes/if.js +++ b/lib/nodes/if.js @@ -11,68 +11,64 @@ var Node = require('./node'); -/** - * Initialize a new `If` with the given `cond`. - * - * @param {Expression} cond - * @param {Boolean|Block} negate, block - * @api public - */ +module.exports = class If extends Node { + /** + * Initialize a new `If` with the given `cond`. + * + * @param {Expression} cond + * @param {Boolean|Block} negate, block + * @api public + */ -var If = module.exports = function If(cond, negate){ - Node.call(this); - this.cond = cond; - this.elses = []; - if (negate && negate.nodeName) { - this.block = negate; - } else { - this.negate = negate; + constructor(cond, negate) { + super(); + this.cond = cond; + this.elses = []; + if (negate && negate.nodeName) { + this.block = negate; + } else { + this.negate = negate; + } } -}; -/** - * Inherit from `Node.prototype`. - */ - -If.prototype.__proto__ = Node.prototype; + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -If.prototype.clone = function(parent){ - var clone = new If(); - clone.cond = this.cond.clone(parent, clone); - clone.block = this.block.clone(parent, clone); - clone.elses = this.elses.map(function(node){ return node.clone(parent, clone); }); - clone.negate = this.negate; - clone.postfix = this.postfix; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new If(); + clone.cond = this.cond.clone(parent, clone); + clone.block = this.block.clone(parent, clone); + clone.elses = this.elses.map(function (node) { return node.clone(parent, clone); }); + clone.negate = this.negate; + clone.postfix = this.postfix; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -If.prototype.toJSON = function(){ - return { - __type: 'If', - cond: this.cond, - block: this.block, - elses: this.elses, - negate: this.negate, - postfix: this.postfix, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + return { + __type: 'If', + cond: this.cond, + block: this.block, + elses: this.elses, + negate: this.negate, + postfix: this.postfix, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; }; From d70087b0836bc09592d80134eb4143ac8fecaa16 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:18:08 +0200 Subject: [PATCH 28/68] use class in lib/nodes/ternary --- lib/nodes/ternary.js | 98 +++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/lib/nodes/ternary.js b/lib/nodes/ternary.js index 2034170a4..710b518c0 100644 --- a/lib/nodes/ternary.js +++ b/lib/nodes/ternary.js @@ -11,61 +11,57 @@ var Node = require('./node'); -/** - * Initialize a new `Ternary` with `cond`, `trueExpr` and `falseExpr`. - * - * @param {Expression} cond - * @param {Expression} trueExpr - * @param {Expression} falseExpr - * @api public - */ +module.exports = class Ternary extends Node { + /** + * Initialize a new `Ternary` with `cond`, `trueExpr` and `falseExpr`. + * + * @param {Expression} cond + * @param {Expression} trueExpr + * @param {Expression} falseExpr + * @api public + */ -var Ternary = module.exports = function Ternary(cond, trueExpr, falseExpr){ - Node.call(this); - this.cond = cond; - this.trueExpr = trueExpr; - this.falseExpr = falseExpr; -}; + constructor(cond, trueExpr, falseExpr) { + super(); + this.cond = cond; + this.trueExpr = trueExpr; + this.falseExpr = falseExpr; + } -/** - * Inherit from `Node.prototype`. - */ - -Ternary.prototype.__proto__ = Node.prototype; + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -Ternary.prototype.clone = function(parent){ - var clone = new Ternary(); - clone.cond = this.cond.clone(parent, clone); - clone.trueExpr = this.trueExpr.clone(parent, clone); - clone.falseExpr = this.falseExpr.clone(parent, clone); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new Ternary(); + clone.cond = this.cond.clone(parent, clone); + clone.trueExpr = this.trueExpr.clone(parent, clone); + clone.falseExpr = this.falseExpr.clone(parent, clone); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Ternary.prototype.toJSON = function(){ - return { - __type: 'Ternary', - cond: this.cond, - trueExpr: this.trueExpr, - falseExpr: this.falseExpr, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + return { + __type: 'Ternary', + cond: this.cond, + trueExpr: this.trueExpr, + falseExpr: this.falseExpr, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; }; From aa02fab41683c4efcb7f3aa5e2a7e00d7e372237 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:20:04 +0200 Subject: [PATCH 29/68] use class in lib/nodes/each --- lib/nodes/each.js | 107 ++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 55 deletions(-) diff --git a/lib/nodes/each.js b/lib/nodes/each.js index 985bd29da..6198faf8a 100644 --- a/lib/nodes/each.js +++ b/lib/nodes/each.js @@ -12,64 +12,61 @@ var Node = require('./node') , nodes = require('./'); -/** - * Initialize a new `Each` node with the given `val` name, - * `key` name, `expr`, and `block`. - * - * @param {String} val - * @param {String} key - * @param {Expression} expr - * @param {Block} block - * @api public - */ - -var Each = module.exports = function Each(val, key, expr, block){ - Node.call(this); - this.val = val; - this.key = key; - this.expr = expr; - this.block = block; -}; - -/** - * Inherit from `Node.prototype`. - */ +module.exports = class Each extends Node { + /** + * Initialize a new `Each` node with the given `val` name, + * `key` name, `expr`, and `block`. + * + * @param {String} val + * @param {String} key + * @param {Expression} expr + * @param {Block} block + * @api public + */ -Each.prototype.__proto__ = Node.prototype; + constructor(val, key, expr, block) { + super(); + this.val = val; + this.key = key; + this.expr = expr; + this.block = block; + } -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Each.prototype.clone = function(parent){ - var clone = new Each(this.val, this.key); - clone.expr = this.expr.clone(parent, clone); - clone.block = this.block.clone(parent, clone); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new Each(this.val, this.key); + clone.expr = this.expr.clone(parent, clone); + clone.block = this.block.clone(parent, clone); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Each.prototype.toJSON = function(){ - return { - __type: 'Each', - val: this.val, - key: this.key, - expr: this.expr, - block: this.block, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + return { + __type: 'Each', + val: this.val, + key: this.key, + expr: this.expr, + block: this.block, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; -}; + +} From affb57a93a365555288d058d600a8e91be862528 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:21:54 +0200 Subject: [PATCH 30/68] use class in lib/nodes/unaryop --- lib/nodes/unaryop.js | 88 ++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/lib/nodes/unaryop.js b/lib/nodes/unaryop.js index 6ac92ab34..052dcd943 100644 --- a/lib/nodes/unaryop.js +++ b/lib/nodes/unaryop.js @@ -11,56 +11,54 @@ var Node = require('./node'); -/** - * Initialize a new `UnaryOp` with `op`, and `expr`. - * - * @param {String} op - * @param {Node} expr - * @api public - */ +module.exports = class UnaryOp extends Node { + /** + * Initialize a new `UnaryOp` with `op`, and `expr`. + * + * @param {String} op + * @param {Node} expr + * @api public + */ -var UnaryOp = module.exports = function UnaryOp(op, expr){ - Node.call(this); - this.op = op; - this.expr = expr; -}; + constructor(op, expr) { + super(); + this.op = op; + this.expr = expr; + } -/** - * Inherit from `Node.prototype`. - */ -UnaryOp.prototype.__proto__ = Node.prototype; + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -UnaryOp.prototype.clone = function(parent){ - var clone = new UnaryOp(this.op); - clone.expr = this.expr.clone(parent, clone); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new UnaryOp(this.op); + clone.expr = this.expr.clone(parent, clone); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -UnaryOp.prototype.toJSON = function(){ - return { - __type: 'UnaryOp', - op: this.op, - expr: this.expr, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + return { + __type: 'UnaryOp', + op: this.op, + expr: this.expr, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; + }; From 20a3613e60829650646ecc66b47c20918eae93ea Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:23:52 +0200 Subject: [PATCH 31/68] use class in lib/nodes/object --- lib/nodes/object.js | 396 ++++++++++++++++++++++---------------------- 1 file changed, 197 insertions(+), 199 deletions(-) diff --git a/lib/nodes/object.js b/lib/nodes/object.js index ed093d8d2..1d1176638 100644 --- a/lib/nodes/object.js +++ b/lib/nodes/object.js @@ -13,232 +13,230 @@ var Node = require('./node') , nodes = require('./') , nativeObj = {}.constructor; -/** - * Initialize a new `Object`. - * - * @api public - */ - -var Object = module.exports = function Object(){ - Node.call(this); - this.vals = {}; - this.keys = {}; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Object.prototype.__proto__ = Node.prototype; +module.exports = class Object extends Node { + /** + * Initialize a new `Object`. + * + * @api public + */ + + constructor() { + super(); + this.vals = {}; + this.keys = {}; + } -/** - * Set `key` to `val`. - * - * @param {String} key - * @param {Node} val - * @return {Object} for chaining - * @api public - */ + /** + * Set `key` to `val`. + * + * @param {String} key + * @param {Node} val + * @return {Object} for chaining + * @api public + */ + + setValue(key, val) { + this.vals[key] = val; + return this; + }; -Object.prototype.setValue = function(key, val){ - this.vals[key] = val; - return this; -}; + /** + * Alias for `setValue` for compatible API + */ -/** - * Alias for `setValue` for compatible API - */ -Object.prototype.set = Object.prototype.setValue; + set = this.setValue -/** - * Set `key` to `val`. - * - * @param {String} key - * @param {Node} val - * @return {Object} for chaining - * @api public - */ + /** + * Set `key` to `val`. + * + * @param {String} key + * @param {Node} val + * @return {Object} for chaining + * @api public + */ -Object.prototype.setKey = function(key, val){ - this.keys[key] = val; - return this; -}; - -/** - * Return length. - * - * @return {Number} - * @api public - */ + setKey(key, val) { + this.keys[key] = val; + return this; + }; -Object.prototype.__defineGetter__('length', function() { - return nativeObj.keys(this.vals).length; -}); + /** + * Return length. + * + * @return {Number} + * @api public + */ -/** - * Get `key`. - * - * @param {String} key - * @return {Node} - * @api public - */ + get length() { + return nativeObj.keys(this.vals).length; + }; -Object.prototype.get = function(key){ - return this.vals[key] || nodes.null; -}; + /** + * Get `key`. + * + * @param {String} key + * @return {Node} + * @api public + */ -/** - * Has `key`? - * - * @param {String} key - * @return {Boolean} - * @api public - */ + get(key) { + return this.vals[key] || nodes.null; + }; -Object.prototype.has = function(key){ - return key in this.vals; -}; + /** + * Has `key`? + * + * @param {String} key + * @return {Boolean} + * @api public + */ -/** - * Operate on `right` with the given `op`. - * - * @param {String} op - * @param {Node} right - * @return {Node} - * @api public - */ + has(key) { + return key in this.vals; + }; -Object.prototype.operate = function(op, right){ - switch (op) { - case '.': - case '[]': - return this.get(right.hash); - case '==': - var vals = this.vals - , a - , b; - if ('object' != right.nodeName || this.length != right.length) - return nodes.false; - for (var key in vals) { - a = vals[key]; - b = right.vals[key]; - if (a.operate(op, b).isFalse) + /** + * Operate on `right` with the given `op`. + * + * @param {String} op + * @param {Node} right + * @return {Node} + * @api public + */ + + operate(op, right) { + switch (op) { + case '.': + case '[]': + return this.get(right.hash); + case '==': + var vals = this.vals + , a + , b; + if ('object' != right.nodeName || this.length != right.length) return nodes.false; - } - return nodes.true; - case '!=': - return this.operate('==', right).negate(); - default: - return Node.prototype.operate.call(this, op, right); - } -}; - -/** - * Return Boolean based on the length of this object. - * - * @return {Boolean} - * @api public - */ + for (var key in vals) { + a = vals[key]; + b = right.vals[key]; + if (a.operate(op, b).isFalse) + return nodes.false; + } + return nodes.true; + case '!=': + return this.operate('==', right).negate(); + default: + return Node.prototype.operate.call(this, op, right); + } + }; -Object.prototype.toBoolean = function(){ - return nodes.Boolean(this.length); -}; + /** + * Return Boolean based on the length of this object. + * + * @return {Boolean} + * @api public + */ -/** - * Convert object to string with properties. - * - * @return {String} - * @api private - */ + toBoolean() { + return nodes.Boolean(this.length); + }; -Object.prototype.toBlock = function(){ - var str = '{' - , key - , val; - - for (key in this.vals) { - val = this.get(key); - if ('object' == val.first.nodeName) { - str += key + ' ' + val.first.toBlock(); - } else { - switch (key) { - case '@charset': - str += key + ' ' + val.first.toString() + ';'; - break; - default: - str += key + ':' + toString(val) + ';'; + /** + * Convert object to string with properties. + * + * @return {String} + * @api private + */ + + toBlock() { + var str = '{' + , key + , val; + + for (key in this.vals) { + val = this.get(key); + if ('object' == val.first.nodeName) { + str += key + ' ' + val.first.toBlock(); + } else { + switch (key) { + case '@charset': + str += key + ' ' + val.first.toString() + ';'; + break; + default: + str += key + ':' + toString(val) + ';'; + } } } - } - str += '}'; + str += '}'; - return str; + return str; - function toString(node) { - if (node.nodes) { - return node.nodes.map(toString).join(node.isList ? ',' : ' '); - } else if ('literal' == node.nodeName && ',' == node.val) { - return '\\,'; + function toString(node) { + if (node.nodes) { + return node.nodes.map(toString).join(node.isList ? ',' : ' '); + } else if ('literal' == node.nodeName && ',' == node.val) { + return '\\,'; + } + return node.toString(); } - return node.toString(); - } -}; - -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -Object.prototype.clone = function(parent){ - var clone = new Object; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - - var key; - for (key in this.vals) { - clone.vals[key] = this.vals[key].clone(parent, clone); - } + }; - for (key in this.keys) { - clone.keys[key] = this.keys[key].clone(parent, clone); - } + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone(parent) { + var clone = new Object; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + + var key; + for (key in this.vals) { + clone.vals[key] = this.vals[key].clone(parent, clone); + } - return clone; -}; + for (key in this.keys) { + clone.keys[key] = this.keys[key].clone(parent, clone); + } -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + return clone; + }; -Object.prototype.toJSON = function(){ - return { - __type: 'Object', - vals: this.vals, - keys: this.keys, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Object', + vals: this.vals, + keys: this.keys, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; -}; -/** - * Return "{ : }" - * - * @return {String} - * @api public - */ + /** + * Return "{ : }" + * + * @return {String} + * @api public + */ + + toString() { + var obj = {}; + for (var prop in this.vals) { + obj[prop] = this.vals[prop].toString(); + } + return JSON.stringify(obj); + }; -Object.prototype.toString = function(){ - var obj = {}; - for (var prop in this.vals) { - obj[prop] = this.vals[prop].toString(); - } - return JSON.stringify(obj); }; From 679a6c5e3307e96474670ca3f4d953f90b1706e8 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:25:36 +0200 Subject: [PATCH 32/68] use class in lib/nodes/extend --- lib/nodes/extend.js | 93 ++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 48 deletions(-) diff --git a/lib/nodes/extend.js b/lib/nodes/extend.js index 05458b612..0d6ec842b 100644 --- a/lib/nodes/extend.js +++ b/lib/nodes/extend.js @@ -11,59 +11,56 @@ var Node = require('./node'); -/** - * Initialize a new `Extend` with the given `selectors` array. - * - * @param {Array} selectors array of the selectors - * @api public - */ - -var Extend = module.exports = function Extend(selectors){ - Node.call(this); - this.selectors = selectors; -}; - -/** - * Inherit from `Node.prototype`. - */ +module.exports = class Extend extends Node { + /** + * Initialize a new `Extend` with the given `selectors` array. + * + * @param {Array} selectors array of the selectors + * @api public + */ -Extend.prototype.__proto__ = Node.prototype; + constructor(selectors) { + super(); + this.selectors = selectors; + } -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Extend.prototype.clone = function(){ - return new Extend(this.selectors); -}; + clone() { + return new Extend(this.selectors); + }; -/** - * Return `@extend selectors`. - * - * @return {String} - * @api public - */ + /** + * Return `@extend selectors`. + * + * @return {String} + * @api public + */ -Extend.prototype.toString = function(){ - return '@extend ' + this.selectors.join(', '); -}; + toString() { + return '@extend ' + this.selectors.join(', '); + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Extend.prototype.toJSON = function(){ - return { - __type: 'Extend', - selectors: this.selectors, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + return { + __type: 'Extend', + selectors: this.selectors, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; -}; + +}; \ No newline at end of file From 6254849a7f2e3e05b20dd945697331d5f42f8d5d Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:37:59 +0200 Subject: [PATCH 33/68] use classes in lib/nodes/hsla --- lib/nodes/hsla.js | 325 +++++++++++++++++++++++----------------------- 1 file changed, 161 insertions(+), 164 deletions(-) diff --git a/lib/nodes/hsla.js b/lib/nodes/hsla.js index bea1e1249..ec4ba5446 100644 --- a/lib/nodes/hsla.js +++ b/lib/nodes/hsla.js @@ -22,153 +22,176 @@ var Node = require('./node') * @api public */ -var HSLA = exports = module.exports = function HSLA(h,s,l,a){ - Node.call(this); - this.h = clampDegrees(h); - this.s = clampPercentage(s); - this.l = clampPercentage(l); - this.a = clampAlpha(a); - this.hsla = this; -}; - -/** - * Inherit from `Node.prototype`. - */ - -HSLA.prototype.__proto__ = Node.prototype; - -/** - * Return hsla(n,n,n,n). - * - * @return {String} - * @api public - */ +exports = module.exports = class HSLA extends Node { + constructor(h, s, l, a) { + super(this); + this.h = clampDegrees(h); + this.s = clampPercentage(s); + this.l = clampPercentage(l); + this.a = clampAlpha(a); + this.hsla = this; + } -HSLA.prototype.toString = function(){ - return 'hsla(' - + this.h + ',' - + this.s.toFixed(0) + '%,' - + this.l.toFixed(0) + '%,' - + this.a + ')'; -}; + /** + * Return hsla(n,n,n,n). + * + * @return {String} + * @api public + */ + + toString() { + return 'hsla(' + + this.h + ',' + + this.s.toFixed(0) + '%,' + + this.l.toFixed(0) + '%,' + + this.a + ')'; + }; -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -HSLA.prototype.clone = function(parent){ - var clone = new HSLA( + clone(parent) { + var clone = new HSLA( this.h - , this.s - , this.l - , this.a); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; - -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ - -HSLA.prototype.toJSON = function(){ - return { - __type: 'HSLA', - h: this.h, - s: this.s, - l: this.l, - a: this.a, - lineno: this.lineno, - column: this.column, - filename: this.filename + , this.s + , this.l + , this.a); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; }; -}; -/** - * Return rgba `RGBA` representation. - * - * @return {RGBA} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'HSLA', + h: this.h, + s: this.s, + l: this.l, + a: this.a, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + }; -HSLA.prototype.__defineGetter__('rgba', function(){ - return nodes.RGBA.fromHSLA(this); -}); + /** + * Return rgba `RGBA` representation. + * + * @return {RGBA} + * @api public + */ -/** - * Return hash. - * - * @return {String} - * @api public - */ + get rgba() { + return nodes.RGBA.fromHSLA(this); + }; -HSLA.prototype.__defineGetter__('hash', function(){ - return this.rgba.toString(); -}); + /** + * Return hash. + * + * @return {String} + * @api public + */ -/** - * Add h,s,l to the current component values. - * - * @param {Number} h - * @param {Number} s - * @param {Number} l - * @return {HSLA} new node - * @api public - */ + get hash() { + return this.rgba.toString(); + }; -HSLA.prototype.add = function(h,s,l){ - return new HSLA( + /** + * Add h,s,l to the current component values. + * + * @param {Number} h + * @param {Number} s + * @param {Number} l + * @return {HSLA} new node + * @api public + */ + + add(h, s, l) { + return new HSLA( this.h + h - , this.s + s - , this.l + l - , this.a); -}; + , this.s + s + , this.l + l + , this.a); + }; -/** - * Subtract h,s,l from the current component values. - * - * @param {Number} h - * @param {Number} s - * @param {Number} l - * @return {HSLA} new node - * @api public - */ + /** + * Subtract h,s,l from the current component values. + * + * @param {Number} h + * @param {Number} s + * @param {Number} l + * @return {HSLA} new node + * @api public + */ + + sub(h, s, l) { + return this.add(-h, -s, -l); + }; -HSLA.prototype.sub = function(h,s,l){ - return this.add(-h, -s, -l); -}; + /** + * Operate on `right` with the given `op`. + * + * @param {String} op + * @param {Node} right + * @return {Node} + * @api public + */ + + operate(op, right) { + switch (op) { + case '==': + case '!=': + case '<=': + case '>=': + case '<': + case '>': + case 'is a': + case '||': + case '&&': + return this.rgba.operate(op, right); + default: + return this.rgba.operate(op, right).hsla; + } + }; -/** - * Operate on `right` with the given `op`. - * - * @param {String} op - * @param {Node} right - * @return {Node} - * @api public - */ -HSLA.prototype.operate = function(op, right){ - switch (op) { - case '==': - case '!=': - case '<=': - case '>=': - case '<': - case '>': - case 'is a': - case '||': - case '&&': - return this.rgba.operate(op, right); - default: - return this.rgba.operate(op, right).hsla; - } + /** + * Adjust lightness by `percent`. + * + * @param {Number} percent + * @return {HSLA} for chaining + * @api public + */ + + adjustLightness(percent) { + this.l = clampPercentage(this.l + this.l * (percent / 100)); + return this; + }; + + /** + * Adjust hue by `deg`. + * + * @param {Number} deg + * @return {HSLA} for chaining + * @api public + */ + + adjustHue(deg) { + this.h = clampDegrees(this.h + deg); + return this; + }; }; /** @@ -179,23 +202,23 @@ HSLA.prototype.operate = function(op, right){ * @api public */ -exports.fromRGBA = function(rgba){ +exports.fromRGBA = function (rgba) { var r = rgba.r / 255 , g = rgba.g / 255 , b = rgba.b / 255 , a = rgba.a; - var min = Math.min(r,g,b) - , max = Math.max(r,g,b) + var min = Math.min(r, g, b) + , max = Math.max(r, g, b) , l = (max + min) / 2 , d = max - min , h, s; switch (max) { case min: h = 0; break; - case r: h = 60 * (g-b) / d; break; - case g: h = 60 * (b-r) / d + 120; break; - case b: h = 60 * (r-g) / d + 240; break; + case r: h = 60 * (g - b) / d; break; + case g: h = 60 * (b - r) / d + 120; break; + case b: h = 60 * (r - g) / d + 240; break; } if (max == min) { @@ -210,33 +233,7 @@ exports.fromRGBA = function(rgba){ s *= 100; l *= 100; - return new HSLA(h,s,l,a); -}; - -/** - * Adjust lightness by `percent`. - * - * @param {Number} percent - * @return {HSLA} for chaining - * @api public - */ - -HSLA.prototype.adjustLightness = function(percent){ - this.l = clampPercentage(this.l + this.l * (percent / 100)); - return this; -}; - -/** - * Adjust hue by `deg`. - * - * @param {Number} deg - * @return {HSLA} for chaining - * @api public - */ - -HSLA.prototype.adjustHue = function(deg){ - this.h = clampDegrees(this.h + deg); - return this; + return new HSLA(h, s, l, a); }; /** From 0420dd1ad2267ff67b8691af9e5d1eac8b856d9c Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:51:05 +0200 Subject: [PATCH 34/68] fix hsla/rgba fromRGBA/fromHSLA static constructors --- lib/nodes/hsla.js | 77 ++++++++++++++++++++++++----------------------- lib/nodes/rgba.js | 66 ++++++++++++++++++++-------------------- 2 files changed, 72 insertions(+), 71 deletions(-) diff --git a/lib/nodes/hsla.js b/lib/nodes/hsla.js index ec4ba5446..6be3e23c1 100644 --- a/lib/nodes/hsla.js +++ b/lib/nodes/hsla.js @@ -24,7 +24,7 @@ var Node = require('./node') exports = module.exports = class HSLA extends Node { constructor(h, s, l, a) { - super(this); + super(); this.h = clampDegrees(h); this.s = clampPercentage(s); this.l = clampPercentage(l); @@ -192,48 +192,49 @@ exports = module.exports = class HSLA extends Node { this.h = clampDegrees(this.h + deg); return this; }; -}; -/** - * Return `HSLA` representation of the given `color`. - * - * @param {RGBA} color - * @return {HSLA} - * @api public - */ -exports.fromRGBA = function (rgba) { - var r = rgba.r / 255 - , g = rgba.g / 255 - , b = rgba.b / 255 - , a = rgba.a; - - var min = Math.min(r, g, b) - , max = Math.max(r, g, b) - , l = (max + min) / 2 - , d = max - min - , h, s; - - switch (max) { - case min: h = 0; break; - case r: h = 60 * (g - b) / d; break; - case g: h = 60 * (b - r) / d + 120; break; - case b: h = 60 * (r - g) / d + 240; break; - } + /** + * Return `HSLA` representation of the given `color`. + * + * @param {RGBA} color + * @return {HSLA} + * @api public + */ - if (max == min) { - s = 0; - } else if (l < .5) { - s = d / (2 * l); - } else { - s = d / (2 - 2 * l); - } + static fromRGBA(rgba) { + var r = rgba.r / 255 + , g = rgba.g / 255 + , b = rgba.b / 255 + , a = rgba.a; + + var min = Math.min(r, g, b) + , max = Math.max(r, g, b) + , l = (max + min) / 2 + , d = max - min + , h, s; + + switch (max) { + case min: h = 0; break; + case r: h = 60 * (g - b) / d; break; + case g: h = 60 * (b - r) / d + 120; break; + case b: h = 60 * (r - g) / d + 240; break; + } - h %= 360; - s *= 100; - l *= 100; + if (max == min) { + s = 0; + } else if (l < .5) { + s = d / (2 * l); + } else { + s = d / (2 - 2 * l); + } + + h %= 360; + s *= 100; + l *= 100; - return new HSLA(h, s, l, a); + return new HSLA(h, s, l, a); + }; }; /** diff --git a/lib/nodes/rgba.js b/lib/nodes/rgba.js index d48b457db..81c804af6 100644 --- a/lib/nodes/rgba.js +++ b/lib/nodes/rgba.js @@ -117,7 +117,7 @@ exports = module.exports = class RGBA extends Node { * @api public */ - hsla() { + get hsla() { return HSLA.fromRGBA(this); }; @@ -128,7 +128,7 @@ exports = module.exports = class RGBA extends Node { * @api public */ - hash() { + get hash() { return this.toString(); }; @@ -303,40 +303,40 @@ exports = module.exports = class RGBA extends Node { + (+this.a.toFixed(3)) + ')'; } }; -}; - -/** - * Return a `RGBA` from the given `hsla`. - * - * @param {HSLA} hsla - * @return {RGBA} - * @api public - */ -exports.fromHSLA = function (hsla) { - var h = hsla.h / 360 - , s = hsla.s / 100 - , l = hsla.l / 100 - , a = hsla.a; - - var m2 = l <= .5 ? l * (s + 1) : l + s - l * s - , m1 = l * 2 - m2; - - var r = hue(h + 1 / 3) * 0xff - , g = hue(h) * 0xff - , b = hue(h - 1 / 3) * 0xff; - - function hue(h) { - if (h < 0) ++h; - if (h > 1) --h; - if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; - if (h * 2 < 1) return m2; - if (h * 3 < 2) return m1 + (m2 - m1) * (2 / 3 - h) * 6; - return m1; - } + /** + * Return a `RGBA` from the given `hsla`. + * + * @param {HSLA} hsla + * @return {RGBA} + * @api public + */ + + static fromHSLA(hsla) { + var h = hsla.h / 360 + , s = hsla.s / 100 + , l = hsla.l / 100 + , a = hsla.a; + + var m2 = l <= .5 ? l * (s + 1) : l + s - l * s + , m1 = l * 2 - m2; + + var r = hue(h + 1 / 3) * 0xff + , g = hue(h) * 0xff + , b = hue(h - 1 / 3) * 0xff; + + function hue(h) { + if (h < 0) ++h; + if (h > 1) --h; + if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; + if (h * 2 < 1) return m2; + if (h * 3 < 2) return m1 + (m2 - m1) * (2 / 3 - h) * 6; + return m1; + } - return new RGBA(r, g, b, a); + return new RGBA(r, g, b, a); + }; }; /** From 12a40a7d6b71be83e7914710db195f31d050891f Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:52:14 +0200 Subject: [PATCH 35/68] fix trying to initialize boolean without `new` --- lib/lexer.js | 2 +- lib/nodes/node.js | 14 +++++++------- lib/nodes/unit.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/lexer.js b/lib/lexer.js index a3cb658de..b9bfa8ecf 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -627,7 +627,7 @@ Lexer.prototype = { boolean: function() { var captures; if (captures = /^(true|false)\b([ \t]*)/.exec(this.str)) { - var val = nodes.Boolean('true' == captures[1]); + var val = new nodes.Boolean('true' == captures[1]); this.skip(captures); var tok = new Token('boolean', val); tok.space = captures[2]; diff --git a/lib/nodes/node.js b/lib/nodes/node.js index e3fb02000..4c293adaa 100644 --- a/lib/nodes/node.js +++ b/lib/nodes/node.js @@ -173,22 +173,22 @@ module.exports = class Node { switch (op) { case 'is a': if ('string' == right.first.nodeName) { - return nodes.Boolean(this.nodeName == right.val); + return new nodes.Boolean(this.nodeName == right.val); } else { throw new Error('"is a" expects a string, got ' + right.toString()); } case '==': - return nodes.Boolean(this.hash == right.hash); + return new nodes.Boolean(this.hash == right.hash); case '!=': - return nodes.Boolean(this.hash != right.hash); + return new nodes.Boolean(this.hash != right.hash); case '>=': - return nodes.Boolean(this.hash >= right.hash); + return new nodes.Boolean(this.hash >= right.hash); case '<=': - return nodes.Boolean(this.hash <= right.hash); + return new nodes.Boolean(this.hash <= right.hash); case '>': - return nodes.Boolean(this.hash > right.hash); + return new nodes.Boolean(this.hash > right.hash); case '<': - return nodes.Boolean(this.hash < right.hash); + return new nodes.Boolean(this.hash < right.hash); case '||': return this.toBoolean().isTrue ? this diff --git a/lib/nodes/unit.js b/lib/nodes/unit.js index 4390c93bd..422c92876 100644 --- a/lib/nodes/unit.js +++ b/lib/nodes/unit.js @@ -51,7 +51,7 @@ module.exports = class Unit extends Node { */ toBoolean() { - return nodes.Boolean(this.type + return new nodes.Boolean(this.type ? true : this.val); }; From 07fced1e230569b0f2056bf2e278e0758b29c982 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:54:08 +0200 Subject: [PATCH 36/68] use class in lib/nodes/member --- lib/nodes/member.js | 116 +++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/lib/nodes/member.js b/lib/nodes/member.js index af947388f..f60ae7e29 100644 --- a/lib/nodes/member.js +++ b/lib/nodes/member.js @@ -11,72 +11,68 @@ var Node = require('./node'); -/** - * Initialize a new `Member` with `left` and `right`. - * - * @param {Node} left - * @param {Node} right - * @api public - */ +module.exports = class Member extends Node { + /** + * Initialize a new `Member` with `left` and `right`. + * + * @param {Node} left + * @param {Node} right + * @api public + */ -var Member = module.exports = function Member(left, right){ - Node.call(this); - this.left = left; - this.right = right; -}; + constructor(left, right) { + super(); + this.left = left; + this.right = right; + } -/** - * Inherit from `Node.prototype`. - */ - -Member.prototype.__proto__ = Node.prototype; - -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Member.prototype.clone = function(parent){ - var clone = new Member; - clone.left = this.left.clone(parent, clone); - clone.right = this.right.clone(parent, clone); - if (this.val) clone.val = this.val.clone(parent, clone); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new Member; + clone.left = this.left.clone(parent, clone); + clone.right = this.right.clone(parent, clone); + if (this.val) clone.val = this.val.clone(parent, clone); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Member.prototype.toJSON = function(){ - var json = { - __type: 'Member', - left: this.left, - right: this.right, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + var json = { + __type: 'Member', + left: this.left, + right: this.right, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + if (this.val) json.val = this.val; + return json; }; - if (this.val) json.val = this.val; - return json; -}; -/** - * Return a string representation of this node. - * - * @return {String} - * @api public - */ + /** + * Return a string representation of this node. + * + * @return {String} + * @api public + */ -Member.prototype.toString = function(){ - return this.left.toString() - + '.' + this.right.toString(); + toString() { + return this.left.toString() + + '.' + this.right.toString(); + }; }; From aa054f67757e1ae67c17849fb3b188a3bd27c27c Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:55:02 +0200 Subject: [PATCH 37/68] use class in lib/nodes/comment --- lib/nodes/comment.js | 89 +++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/lib/nodes/comment.js b/lib/nodes/comment.js index 56b59b3bf..973e7f91d 100644 --- a/lib/nodes/comment.js +++ b/lib/nodes/comment.js @@ -11,54 +11,51 @@ var Node = require('./node'); -/** - * Initialize a new `Comment` with the given `str`. - * - * @param {String} str - * @param {Boolean} suppress - * @param {Boolean} inline - * @api public - */ - -var Comment = module.exports = function Comment(str, suppress, inline){ - Node.call(this); - this.str = str; - this.suppress = suppress; - this.inline = inline; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Comment.prototype.__proto__ = Node.prototype; +module.exports = class Comment extends Node { + /** + * Initialize a new `Comment` with the given `str`. + * + * @param {String} str + * @param {Boolean} suppress + * @param {Boolean} inline + * @api public + */ + + constructor(str, suppress, inline) { + super(); + this.str = str; + this.suppress = suppress; + this.inline = inline; + } + + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Comment', + str: this.str, + suppress: this.suppress, + inline: this.inline, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return comment. + * + * @return {String} + * @api public + */ -Comment.prototype.toJSON = function(){ - return { - __type: 'Comment', - str: this.str, - suppress: this.suppress, - inline: this.inline, - lineno: this.lineno, - column: this.column, - filename: this.filename + toString() { + return this.str; }; -}; - -/** - * Return comment. - * - * @return {String} - * @api public - */ -Comment.prototype.toString = function(){ - return this.str; }; From a6208d0c3f00f3c6499257ce9fbc89d23e179b4b Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:56:09 +0200 Subject: [PATCH 38/68] use class in lib/nodes/namespace --- lib/nodes/namespace.js | 86 ++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/lib/nodes/namespace.js b/lib/nodes/namespace.js index 878b474ec..0cfed4687 100644 --- a/lib/nodes/namespace.js +++ b/lib/nodes/namespace.js @@ -10,51 +10,47 @@ var Node = require('./node'); -/** - * Initialize a new `Namespace` with the given `val` and `prefix` - * - * @param {String|Call} val - * @param {String} [prefix] - * @api public - */ - -var Namespace = module.exports = function Namespace(val, prefix){ - Node.call(this); - this.val = val; - this.prefix = prefix; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Namespace.prototype.__proto__ = Node.prototype; - -/** - * Return @namespace "val". - * - * @return {String} - * @api public - */ - -Namespace.prototype.toString = function(){ - return '@namespace ' + (this.prefix ? this.prefix + ' ' : '') + this.val; -}; - -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ +module.exports = class Namespace extends Node { + /** + * Initialize a new `Namespace` with the given `val` and `prefix` + * + * @param {String|Call} val + * @param {String} [prefix] + * @api public + */ + + constructor(val, prefix) { + super(); + this.val = val; + this.prefix = prefix; + } + + /** + * Return @namespace "val". + * + * @return {String} + * @api public + */ + + toString() { + return '@namespace ' + (this.prefix ? this.prefix + ' ' : '') + this.val; + }; -Namespace.prototype.toJSON = function(){ - return { - __type: 'Namespace', - val: this.val, - prefix: this.prefix, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Namespace', + val: this.val, + prefix: this.prefix, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; }; From a6a67ba157ceff6262c48e053962733896091017 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:57:21 +0200 Subject: [PATCH 39/68] use class in lib/nodes/query-list --- lib/nodes/query-list.js | 169 ++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 86 deletions(-) diff --git a/lib/nodes/query-list.js b/lib/nodes/query-list.js index 3819640b4..f9eee6bf3 100644 --- a/lib/nodes/query-list.js +++ b/lib/nodes/query-list.js @@ -11,98 +11,95 @@ var Node = require('./node'); -/** - * Initialize a new `QueryList`. - * - * @api public - */ - -var QueryList = module.exports = function QueryList(){ - Node.call(this); - this.nodes = []; -}; - -/** - * Inherit from `Node.prototype`. - */ - -QueryList.prototype.__proto__ = Node.prototype; - -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -QueryList.prototype.clone = function(parent){ - var clone = new QueryList; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - for (var i = 0; i < this.nodes.length; ++i) { - clone.push(this.nodes[i].clone(parent, clone)); +module.exports = class QueryList extends Node { + /** + * Initialize a new `QueryList`. + * + * @api public + */ + + constructor() { + super(); + this.nodes = []; } - return clone; -}; -/** - * Push the given `node`. - * - * @param {Node} node - * @api public - */ - -QueryList.prototype.push = function(node){ - this.nodes.push(node); -}; - -/** - * Merges this query list with the `other`. - * - * @param {QueryList} other - * @return {QueryList} - * @api private - */ - -QueryList.prototype.merge = function(other){ - var list = new QueryList - , merged; - this.nodes.forEach(function(query){ - for (var i = 0, len = other.nodes.length; i < len; ++i){ - merged = query.merge(other.nodes[i]); - if (merged) list.push(merged); + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone(parent) { + var clone = new QueryList; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + for (var i = 0; i < this.nodes.length; ++i) { + clone.push(this.nodes[i].clone(parent, clone)); } - }); - return list; -}; + return clone; + }; -/** - * Return ", , " - * - * @return {String} - * @api public - */ + /** + * Push the given `node`. + * + * @param {Node} node + * @api public + */ -QueryList.prototype.toString = function(){ - return '(' + this.nodes.map(function(node){ - return node.toString(); - }).join(', ') + ')'; -}; + push(node) { + this.nodes.push(node); + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Merges this query list with the `other`. + * + * @param {QueryList} other + * @return {QueryList} + * @api private + */ + + merge(other) { + var list = new QueryList + , merged; + this.nodes.forEach(function (query) { + for (var i = 0, len = other.nodes.length; i < len; ++i) { + merged = query.merge(other.nodes[i]); + if (merged) list.push(merged); + } + }); + return list; + }; + + /** + * Return ", , " + * + * @return {String} + * @api public + */ + + toString() { + return '(' + this.nodes.map(function (node) { + return node.toString(); + }).join(', ') + ')'; + }; -QueryList.prototype.toJSON = function(){ - return { - __type: 'QueryList', - nodes: this.nodes, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'QueryList', + nodes: this.nodes, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; + }; From c695092944a6814dd1edde6ae3f9d6e6c9c25e57 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 11:59:40 +0200 Subject: [PATCH 40/68] use class in lib/nodes/query --- lib/nodes/query.js | 288 ++++++++++++++++++++++----------------------- 1 file changed, 142 insertions(+), 146 deletions(-) diff --git a/lib/nodes/query.js b/lib/nodes/query.js index 47a568b71..1184fd780 100644 --- a/lib/nodes/query.js +++ b/lib/nodes/query.js @@ -11,160 +11,156 @@ var Node = require('./node'); -/** - * Initialize a new `Query`. - * - * @api public - */ - -var Query = module.exports = function Query(){ - Node.call(this); - this.nodes = []; - this.type = ''; - this.predicate = ''; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Query.prototype.__proto__ = Node.prototype; - -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -Query.prototype.clone = function(parent){ - var clone = new Query; - clone.predicate = this.predicate; - clone.type = this.type; - for (var i = 0, len = this.nodes.length; i < len; ++i) { - clone.push(this.nodes[i].clone(parent, clone)); - } - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; - -/** - * Push the given `feature`. - * - * @param {Feature} feature - * @api public - */ - -Query.prototype.push = function(feature){ - this.nodes.push(feature); -}; - -/** - * Return resolved type of this query. - * - * @return {String} - * @api private - */ - -Query.prototype.__defineGetter__('resolvedType', function(){ - if (this.type) { - return this.type.nodeName - ? this.type.string - : this.type; +module.exports = class Query extends Node { + /** + * Initialize a new `Query`. + * + * @api public + */ + + constructor() { + super(); + this.nodes = []; + this.type = ''; + this.predicate = ''; } -}); -/** - * Return resolved predicate of this query. - * - * @return {String} - * @api private - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone(parent) { + var clone = new Query; + clone.predicate = this.predicate; + clone.type = this.type; + for (var i = 0, len = this.nodes.length; i < len; ++i) { + clone.push(this.nodes[i].clone(parent, clone)); + } + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -Query.prototype.__defineGetter__('resolvedPredicate', function(){ - if (this.predicate) { - return this.predicate.nodeName - ? this.predicate.string - : this.predicate; - } -}); + /** + * Push the given `feature`. + * + * @param {Feature} feature + * @api public + */ -/** - * Merges this query with the `other`. - * - * @param {Query} other - * @return {Query} - * @api private - */ + push(feature) { + this.nodes.push(feature); + }; -Query.prototype.merge = function(other){ - var query = new Query - , p1 = this.resolvedPredicate - , p2 = other.resolvedPredicate - , t1 = this.resolvedType - , t2 = other.resolvedType - , type, pred; - - // Stolen from Sass :D - t1 = t1 || t2; - t2 = t2 || t1; - if (('not' == p1) ^ ('not' == p2)) { - if (t1 == t2) return; - type = ('not' == p1) ? t2 : t1; - pred = ('not' == p1) ? p2 : p1; - } else if (('not' == p1) && ('not' == p2)) { - if (t1 != t2) return; - type = t1; - pred = 'not'; - } else if (t1 != t2) { - return; - } else { - type = t1; - pred = p1 || p2; - } - query.predicate = pred; - query.type = type; - query.nodes = this.nodes.concat(other.nodes); - return query; -}; + /** + * Return resolved type of this query. + * + * @return {String} + * @api private + */ + + get resolvedType() { + if (this.type) { + return this.type.nodeName + ? this.type.string + : this.type; + } + }; -/** - * Return " and and " - * - * @return {String} - * @api public - */ + /** + * Return resolved predicate of this query. + * + * @return {String} + * @api private + */ + + get resolvedPredicate() { + if (this.predicate) { + return this.predicate.nodeName + ? this.predicate.string + : this.predicate; + } + }; -Query.prototype.toString = function(){ - var pred = this.predicate ? this.predicate + ' ' : '' - , type = this.type || '' - , len = this.nodes.length - , str = pred + type; - if (len) { - str += (type && ' and ') + this.nodes.map(function(expr){ - return expr.toString(); - }).join(' and '); - } - return str; -}; + /** + * Merges this query with the `other`. + * + * @param {Query} other + * @return {Query} + * @api private + */ + + merge(other) { + var query = new Query + , p1 = this.resolvedPredicate + , p2 = other.resolvedPredicate + , t1 = this.resolvedType + , t2 = other.resolvedType + , type, pred; + + // Stolen from Sass :D + t1 = t1 || t2; + t2 = t2 || t1; + if (('not' == p1) ^ ('not' == p2)) { + if (t1 == t2) return; + type = ('not' == p1) ? t2 : t1; + pred = ('not' == p1) ? p2 : p1; + } else if (('not' == p1) && ('not' == p2)) { + if (t1 != t2) return; + type = t1; + pred = 'not'; + } else if (t1 != t2) { + return; + } else { + type = t1; + pred = p1 || p2; + } + query.predicate = pred; + query.type = type; + query.nodes = this.nodes.concat(other.nodes); + return query; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return " and and " + * + * @return {String} + * @api public + */ + + toString() { + var pred = this.predicate ? this.predicate + ' ' : '' + , type = this.type || '' + , len = this.nodes.length + , str = pred + type; + if (len) { + str += (type && ' and ') + this.nodes.map(function (expr) { + return expr.toString(); + }).join(' and '); + } + return str; + }; -Query.prototype.toJSON = function(){ - return { - __type: 'Query', - predicate: this.predicate, - type: this.type, - nodes: this.nodes, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Query', + predicate: this.predicate, + type: this.type, + nodes: this.nodes, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; }; From f0b449c4bbd523a72466baa591d3429729531ad1 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:00:54 +0200 Subject: [PATCH 41/68] use class in lib/nodes/feature --- lib/nodes/feature.js | 120 +++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 62 deletions(-) diff --git a/lib/nodes/feature.js b/lib/nodes/feature.js index f4e183282..0e81e0274 100644 --- a/lib/nodes/feature.js +++ b/lib/nodes/feature.js @@ -11,74 +11,70 @@ var Node = require('./node'); -/** - * Initialize a new `Feature` with the given `segs`. - * - * @param {Array} segs - * @api public - */ - -var Feature = module.exports = function Feature(segs){ - Node.call(this); - this.segments = segs; - this.expr = null; -}; - -/** - * Inherit from `Node.prototype`. - */ +module.exports = class Feature extends Node { + /** + * Initialize a new `Feature` with the given `segs`. + * + * @param {Array} segs + * @api public + */ -Feature.prototype.__proto__ = Node.prototype; + constructor(segs) { + super(); + this.segments = segs; + this.expr = null; + } -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ -Feature.prototype.clone = function(parent){ - var clone = new Feature; - clone.segments = this.segments.map(function(node){ return node.clone(parent, clone); }); - if (this.expr) clone.expr = this.expr.clone(parent, clone); - if (this.name) clone.name = this.name; - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; + clone(parent) { + var clone = new Feature; + clone.segments = this.segments.map(function (node) { return node.clone(parent, clone); }); + if (this.expr) clone.expr = this.expr.clone(parent, clone); + if (this.name) clone.name = this.name; + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -/** - * Return "" or "(: )" - * - * @return {String} - * @api public - */ + /** + * Return "" or "(: )" + * + * @return {String} + * @api public + */ -Feature.prototype.toString = function(){ - if (this.expr) { - return '(' + this.segments.join('') + ': ' + this.expr.toString() + ')'; - } else { - return this.segments.join(''); - } -}; + toString() { + if (this.expr) { + return '(' + this.segments.join('') + ': ' + this.expr.toString() + ')'; + } else { + return this.segments.join(''); + } + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ -Feature.prototype.toJSON = function(){ - var json = { - __type: 'Feature', - segments: this.segments, - lineno: this.lineno, - column: this.column, - filename: this.filename + toJSON() { + var json = { + __type: 'Feature', + segments: this.segments, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; + if (this.expr) json.expr = this.expr; + if (this.name) json.name = this.name; + return json; }; - if (this.expr) json.expr = this.expr; - if (this.name) json.name = this.name; - return json; }; From be7d75066bd2041fdd54303a892cf3477a297326 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:01:53 +0200 Subject: [PATCH 42/68] fix CoercionError not calling super --- lib/nodes/node.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/nodes/node.js b/lib/nodes/node.js index 4c293adaa..e501f9cfb 100644 --- a/lib/nodes/node.js +++ b/lib/nodes/node.js @@ -22,6 +22,7 @@ class CoercionError extends Error { */ constructor(msg) { + super(); this.name = 'CoercionError' this.message = msg if (Error.captureStackTrace) { @@ -201,7 +202,7 @@ module.exports = class Node { // 'prop' in obj if (1 == len && 'object' == vals[0].nodeName) { - return nodes.Boolean(vals[0].has(this.hash)); + return new nodes.Boolean(vals[0].has(this.hash)); } for (var i = 0; i < len; ++i) { From e49b52ee92489f87ecfb1fdf4843b6d8d739e811 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:02:06 +0200 Subject: [PATCH 43/68] fix trying to construct a class without `new` --- lib/nodes/object.js | 2 +- lib/nodes/string.js | 2 +- lib/visitor/evaluator.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nodes/object.js b/lib/nodes/object.js index 1d1176638..a2e4c42b6 100644 --- a/lib/nodes/object.js +++ b/lib/nodes/object.js @@ -137,7 +137,7 @@ module.exports = class Object extends Node { */ toBoolean() { - return nodes.Boolean(this.length); + return new nodes.Boolean(this.length); }; /** diff --git a/lib/nodes/string.js b/lib/nodes/string.js index 440a6ee10..0aa79ba53 100644 --- a/lib/nodes/string.js +++ b/lib/nodes/string.js @@ -86,7 +86,7 @@ module.exports = class String extends Node { */ toBoolean() { - return nodes.Boolean(this.val.length); + return new nodes.Boolean(this.val.length); }; /** diff --git a/lib/visitor/evaluator.js b/lib/visitor/evaluator.js index d60ddfcad..1afca1e07 100644 --- a/lib/visitor/evaluator.js +++ b/lib/visitor/evaluator.js @@ -1471,7 +1471,7 @@ Evaluator.prototype.lookupFunction = function(name){ Evaluator.prototype.isDefined = function(node){ if ('ident' == node.nodeName) { - return nodes.Boolean(this.lookup(node.name)); + return new nodes.Boolean(this.lookup(node.name)); } else { throw new Error('invalid "is defined" check on non-variable ' + node); } From 51ca0199a118fae0daa59285ccd8ef7a70abd03e Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:03:32 +0200 Subject: [PATCH 44/68] use class in lib/nodes/charset --- lib/nodes/charset.js | 81 +++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/lib/nodes/charset.js b/lib/nodes/charset.js index e6d520771..6d818a5ca 100644 --- a/lib/nodes/charset.js +++ b/lib/nodes/charset.js @@ -11,48 +11,45 @@ var Node = require('./node'); -/** - * Initialize a new `Charset` with the given `val` - * - * @param {String} val - * @api public - */ - -var Charset = module.exports = function Charset(val){ - Node.call(this); - this.val = val; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Charset.prototype.__proto__ = Node.prototype; - -/** - * Return @charset "val". - * - * @return {String} - * @api public - */ - -Charset.prototype.toString = function(){ - return '@charset ' + this.val; -}; - -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ +module.exports = class Charset extends Node { + /** + * Initialize a new `Charset` with the given `val` + * + * @param {String} val + * @api public + */ + + constructor(val) { + super(); + this.val = val; + } + + /** + * Return @charset "val". + * + * @return {String} + * @api public + */ + + toString() { + return '@charset ' + this.val; + }; -Charset.prototype.toJSON = function(){ - return { - __type: 'Charset', - val: this.val, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Charset', + val: this.val, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; + }; From 5d91fe5de0b900d601e49234da13d7abb2108071 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:14:26 +0200 Subject: [PATCH 45/68] use class in lib/nodes/null --- lib/nodes/null.js | 99 ++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 52 deletions(-) diff --git a/lib/nodes/null.js b/lib/nodes/null.js index 4efea99f0..4ef438088 100644 --- a/lib/nodes/null.js +++ b/lib/nodes/null.js @@ -18,71 +18,66 @@ var Node = require('./node') * @api public */ -var Null = module.exports = function Null(){}; - -/** - * Inherit from `Node.prototype`. - */ - -Null.prototype.__proto__ = Node.prototype; - -/** +module.exports = class Null extends Node { + /** * Return 'Null'. * * @return {String} * @api public */ -Null.prototype.inspect = -Null.prototype.toString = function(){ - return 'null'; -}; + toString() { + return 'null'; + }; -/** - * Return false. - * - * @return {Boolean} - * @api public - */ + inspect = this.toString -Null.prototype.toBoolean = function(){ - return nodes.false; -}; + /** + * Return false. + * + * @return {Boolean} + * @api public + */ -/** - * Check if the node is a null node. - * - * @return {Boolean} - * @api public - */ + toBoolean() { + return nodes.false; + }; -Null.prototype.__defineGetter__('isNull', function(){ - return true; -}); + /** + * Check if the node is a null node. + * + * @return {Boolean} + * @api public + */ -/** - * Return hash. - * - * @return {String} - * @api public - */ + get isNull() { + return true; + }; -Null.prototype.__defineGetter__('hash', function(){ - return null; -}); + /** + * Return hash. + * + * @return {String} + * @api public + */ -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + get hash() { + return null; + }; -Null.prototype.toJSON = function(){ - return { - __type: 'Null', - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Null', + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; }; From f2deee0841c8bdab49e23f950c07d5d6772e3607 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:16:44 +0200 Subject: [PATCH 46/68] use class in lib/nodes/return --- lib/nodes/return.js | 73 ++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/lib/nodes/return.js b/lib/nodes/return.js index 003ef3801..da626f2ad 100644 --- a/lib/nodes/return.js +++ b/lib/nodes/return.js @@ -19,45 +19,42 @@ var Node = require('./node') * @api public */ -var Return = module.exports = function Return(expr){ - this.expr = expr || nodes.null; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Return.prototype.__proto__ = Node.prototype; - -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ - -Return.prototype.clone = function(parent){ - var clone = new Return(); - clone.expr = this.expr.clone(parent, clone); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - return clone; -}; +module.exports = class Return extends Node { + constructor(expr) { + super(); + this.expr = expr || nodes.null; + }; -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone(parent) { + var clone = new Return(); + clone.expr = this.expr.clone(parent, clone); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + return clone; + }; -Return.prototype.toJSON = function(){ - return { - __type: 'Return', - expr: this.expr, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Return', + expr: this.expr, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; }; From 707ea78ffc1406f1c878aee33f382f222145e3d0 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:27:48 +0200 Subject: [PATCH 47/68] fix events not getting exported --- lib/renderer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/renderer.js b/lib/renderer.js index 37b00962a..bec93c9ae 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -229,13 +229,13 @@ class Renderer extends EventEmitter { }; /** - * Expose events explicitly. + * Expose `Renderer`. */ -module.exports.events = events; +module.exports = Renderer; /** - * Expose `Renderer`. + * Expose events explicitly. */ -module.exports = Renderer; +module.exports.events = events; From 194068e65c774937272d420113bb7595914f3652 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:28:04 +0200 Subject: [PATCH 48/68] use class in lib/nodes/root --- lib/nodes/root.js | 148 +++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 75 deletions(-) diff --git a/lib/nodes/root.js b/lib/nodes/root.js index 3bfb3c218..f76e9ed86 100644 --- a/lib/nodes/root.js +++ b/lib/nodes/root.js @@ -11,86 +11,84 @@ var Node = require('./node'); -/** - * Initialize a new `Root` node. - * - * @api public - */ - -var Root = module.exports = function Root(){ - this.nodes = []; -}; - -/** - * Inherit from `Node.prototype`. - */ - -Root.prototype.__proto__ = Node.prototype; - -/** - * Push a `node` to this block. - * - * @param {Node} node - * @api public - */ - -Root.prototype.push = function(node){ - this.nodes.push(node); -}; - -/** - * Unshift a `node` to this block. - * - * @param {Node} node - * @api public - */ - -Root.prototype.unshift = function(node){ - this.nodes.unshift(node); -}; +module.exports = class Root extends Node { + /** + * Initialize a new `Root` node. + * + * @api public + */ + + constructor() { + super(); + this.nodes = []; + } + + /** + * Push a `node` to this block. + * + * @param {Node} node + * @api public + */ + + push(node) { + this.nodes.push(node); + }; -/** - * Return a clone of this node. - * - * @return {Node} - * @api public - */ + /** + * Unshift a `node` to this block. + * + * @param {Node} node + * @api public + */ -Root.prototype.clone = function(){ - var clone = new Root(); - clone.lineno = this.lineno; - clone.column = this.column; - clone.filename = this.filename; - this.nodes.forEach(function(node){ - clone.push(node.clone(clone, clone)); - }); - return clone; -}; + unshift(node) { + this.nodes.unshift(node); + }; -/** - * Return "root". - * - * @return {String} - * @api public - */ + /** + * Return a clone of this node. + * + * @return {Node} + * @api public + */ + + clone() { + var clone = new Root(); + clone.lineno = this.lineno; + clone.column = this.column; + clone.filename = this.filename; + this.nodes.forEach(function (node) { + clone.push(node.clone(clone, clone)); + }); + return clone; + }; -Root.prototype.toString = function(){ - return '[Root]'; -}; + /** + * Return "root". + * + * @return {String} + * @api public + */ -/** - * Return a JSON representation of this node. - * - * @return {Object} - * @api public - */ + toString() { + return '[Root]'; + }; -Root.prototype.toJSON = function(){ - return { - __type: 'Root', - nodes: this.nodes, - lineno: this.lineno, - column: this.column, - filename: this.filename + /** + * Return a JSON representation of this node. + * + * @return {Object} + * @api public + */ + + toJSON() { + return { + __type: 'Root', + nodes: this.nodes, + lineno: this.lineno, + column: this.column, + filename: this.filename + }; }; + }; From d6093f9f7f347bf51490f5695929315f17b58052 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:30:19 +0200 Subject: [PATCH 49/68] use class in lib/stack --- lib/stack/index.js | 239 ++++++++++++++++++++++----------------------- 1 file changed, 118 insertions(+), 121 deletions(-) diff --git a/lib/stack/index.js b/lib/stack/index.js index 9e288c62f..7aa200607 100644 --- a/lib/stack/index.js +++ b/lib/stack/index.js @@ -5,131 +5,128 @@ * MIT Licensed */ -/** - * Initialize a new `Stack`. - * - * @api private - */ - -var Stack = module.exports = function Stack() { - Array.apply(this, arguments); -}; - -/** - * Inherit from `Array.prototype`. - */ - -Stack.prototype.__proto__ = Array.prototype; - -/** - * Push the given `frame`. - * - * @param {Frame} frame - * @api public - */ - -Stack.prototype.push = function(frame){ - frame.stack = this; - frame.parent = this.currentFrame; - return [].push.apply(this, arguments); -}; - -/** - * Return the current stack `Frame`. - * - * @return {Frame} - * @api private - */ - -Stack.prototype.__defineGetter__('currentFrame', function(){ - return this[this.length - 1]; -}); - -/** - * Lookup stack frame for the given `block`. - * - * @param {Block} block - * @return {Frame} - * @api private - */ - -Stack.prototype.getBlockFrame = function(block){ - for (var i = 0; i < this.length; ++i) { - if (block == this[i].block) { - return this[i]; - } +module.exports = class Stack extends Array { + /** + * Initialize a new `Stack`. + * + * @api private + */ + + constructor() { + super() + Array.apply(this, arguments); } -}; - -/** - * Lookup the given local variable `name`, relative - * to the lexical scope of the current frame's `Block`. - * - * When the result of a lookup is an identifier - * a recursive lookup is performed, defaulting to - * returning the identifier itself. - * - * @param {String} name - * @return {Node} - * @api private - */ -Stack.prototype.lookup = function(name){ - var block = this.currentFrame.block - , val - , ret; - - do { - var frame = this.getBlockFrame(block); - if (frame && (val = frame.lookup(name))) { - return val; + /** + * Push the given `frame`. + * + * @param {Frame} frame + * @api public + */ + + push(frame) { + frame.stack = this; + frame.parent = this.currentFrame; + return [].push.apply(this, arguments); + }; + + /** + * Return the current stack `Frame`. + * + * @return {Frame} + * @api private + */ + + get currentFrame() { + return this[this.length - 1]; + }; + + /** + * Lookup stack frame for the given `block`. + * + * @param {Block} block + * @return {Frame} + * @api private + */ + + getBlockFrame(block) { + for (var i = 0; i < this.length; ++i) { + if (block == this[i].block) { + return this[i]; + } } - } while (block = block.parent); -}; - -/** - * Custom inspect. - * - * @return {String} - * @api private - */ - -Stack.prototype.inspect = function(){ - return this.reverse().map(function(frame){ - return frame.inspect(); - }).join('\n'); -}; - -/** - * Return stack string formatted as: - * - * at (::) - * - * @return {String} - * @api private - */ - -Stack.prototype.toString = function(){ - var block - , node - , buf = [] - , location - , len = this.length; - - while (len--) { - block = this[len].block; - if (node = block.node) { - location = '(' + node.filename + ':' + (node.lineno + 1) + ':' + node.column + ')'; - switch (node.nodeName) { - case 'function': - buf.push(' at ' + node.name + '() ' + location); - break; - case 'group': - buf.push(' at "' + node.nodes[0].val + '" ' + location); - break; + }; + + /** + * Lookup the given local variable `name`, relative + * to the lexical scope of the current frame's `Block`. + * + * When the result of a lookup is an identifier + * a recursive lookup is performed, defaulting to + * returning the identifier itself. + * + * @param {String} name + * @return {Node} + * @api private + */ + + lookup(name) { + var block = this.currentFrame.block + , val + , ret; + + do { + var frame = this.getBlockFrame(block); + if (frame && (val = frame.lookup(name))) { + return val; + } + } while (block = block.parent); + }; + + /** + * Custom inspect. + * + * @return {String} + * @api private + */ + + inspect() { + return this.reverse().map(function (frame) { + return frame.inspect(); + }).join('\n'); + }; + + /** + * Return stack string formatted as: + * + * at (::) + * + * @return {String} + * @api private + */ + + toString() { + var block + , node + , buf = [] + , location + , len = this.length; + + while (len--) { + block = this[len].block; + if (node = block.node) { + location = '(' + node.filename + ':' + (node.lineno + 1) + ':' + node.column + ')'; + switch (node.nodeName) { + case 'function': + buf.push(' at ' + node.name + '() ' + location); + break; + case 'group': + buf.push(' at "' + node.nodes[0].val + '" ' + location); + break; + } } } - } - return buf.join('\n'); + return buf.join('\n'); + }; }; From 1be135338f4b9374f3416934fe149de99032cfd4 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:55:37 +0200 Subject: [PATCH 50/68] use classes in `Visitor` and it's subclasses --- lib/visitor/compiler.js | 1019 +++++++------- lib/visitor/evaluator.js | 2601 +++++++++++++++++------------------ lib/visitor/index.js | 41 +- lib/visitor/normalizer.js | 740 +++++----- lib/visitor/sourcemapper.js | 330 +++-- 5 files changed, 2357 insertions(+), 2374 deletions(-) diff --git a/lib/visitor/compiler.js b/lib/visitor/compiler.js index 9a793339e..1b00564f5 100644 --- a/lib/visitor/compiler.js +++ b/lib/visitor/compiler.js @@ -12,577 +12,574 @@ 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.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); +module.exports = class Compiler extends Visitor { + /** + * 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 + */ + + constructor(root, options) { + super(root); + options = options || {}; + this.compress = options.compress; + this.firebug = options.firebug; + this.linenos = options.linenos; + this.spaces = options['indent spaces'] || 2; + this.indents = 1; + this.stack = []; } - return this.buf; -}; -/** - * Visit Block. - */ + /** + * Compile to css, and return a string of CSS. + * + * @return {String} + * @api private + */ + + compile() { + return this.visit(this.root); + }; + + /** + * Output `str` + * + * @param {String} str + * @param {Node} node + * @return {String} + * @api private + */ + + out(str, node) { + return str; + }; + + /** + * Return indentation string. + * + * @return {String} + * @api private + */ + + get indent() { + 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 + */ + + needBrackets(node) { + return 1 == this.indents + || 'atrule' != node.nodeName + || node.hasOnlyProperties; + }; + + /** + * Visit Root. + */ + + visitRoot(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; + }; -Compiler.prototype.visitBlock = function(block){ - var node - , separator = this.compress ? '' : '\n' - , needBrackets - , lastPropertyIndex; + /** + * Visit Block. + */ - if (block.hasProperties && !block.lacksRenderedSelectors) { - needBrackets = this.needBrackets(block.node); + visitBlock(block) { + var node + , separator = this.compress ? '' : '\n' + , needBrackets + , lastPropertyIndex; - if (this.compress) { + if (block.hasProperties && !block.lacksRenderedSelectors) { + needBrackets = this.needBrackets(block.node); + + if (this.compress) { for (var i = block.nodes.length - 1; i >= 0; --i) { - if (block.nodes[i].nodeName === 'property') { - lastPropertyIndex = i; - break; - } + if (block.nodes[i].nodeName === 'property') { + lastPropertyIndex = i; + break; + } } + } + if (needBrackets) { + this.buf += this.out(this.compress ? '{' : ' {\n'); + ++this.indents; + } + for (var i = 0, len = block.nodes.length; i < len; ++i) { + this.last = lastPropertyIndex === 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); + } } - if (needBrackets) { - this.buf += this.out(this.compress ? '{' : ' {\n'); - ++this.indents; - } + + // Nesting for (var i = 0, len = block.nodes.length; i < len; ++i) { - this.last = lastPropertyIndex === i; node = block.nodes[i]; switch (node.nodeName) { - case 'null': - case 'expression': - case 'function': case 'group': case 'block': - case 'unit': - case 'media': case 'keyframes': + if (this.linenos || this.firebug) this.debugInfo(node); + this.visit(node); + break; + case 'media': + case 'import': 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); + this.visit(node); break; - case 'property': - var ret = this.visit(node) + separator; - this.buf += this.compress ? ret : this.out(ret, node); + 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; - 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.block.isEmpty) { - this.buf += this.out((this.compress ? '' : ' ') + '{}' + newline); - } else 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. - */ + /** + * Visit Keyframes. + */ -Compiler.prototype.visitSupports = function(node){ - if (!node.hasOutput) return; + visitKeyframes(node) { + if (!node.frames) 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 + var prefix = 'official' == node.prefix ? '' - : 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, time, degree or fraction - if (!(['%', 's', 'ms', 'deg', 'fr'].includes(type)) && 0 == n) return '0'; - // Omit leading '0' on floats - if (float && n < 1 && n > -1) { - return n.toString().replace('0.', '.') + type; + : '-' + 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. + */ + + visitMedia(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. + */ + + visitQueryList(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 ? '' : ' ')); } - } - - 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); + }; + + /** + * Visit Query. + */ + + visitQuery(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 '); + } + }; - // keyframe blocks (10%, 20% { ... }) - if (this.keyframe) selector = i ? selector.trim() : selector; + /** + * Visit Feature. + */ - this.buf += this.out(selector + (last ? '' : comma), group.nodes[i]); + visitFeature(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. + */ + + visitImport(imported) { + this.buf += this.out('@import ' + this.visit(imported.path) + ';\n', imported); + }; + + /** + * Visit Atrule. + */ + + visitAtrule(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.block.isEmpty) { + this.buf += this.out((this.compress ? '' : ' ') + '{}' + newline); + } else 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 { - group.block.lacksRenderedSelectors = true; + this.buf += this.out(';' + newline); } + }; + + /** + * Visit Supports. + */ + + visitSupports(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')); } - // output block - this.visit(group.block); - stack.pop(); -}; + /** + * Visit Comment. + */ + + visitComment(comment) { + return this.compress + ? comment.suppress + ? '' + : comment.str + : comment.str; + }; + + /** + * Visit Function. + */ + + visitFunction(fn) { + return fn.name; + }; + + /** + * Visit Charset. + */ + + visitCharset(charset) { + return '@charset ' + this.visit(charset.val) + ';'; + }; + + /** + * Visit Namespace. + */ + + visitNamespace(namespace) { + return '@namespace ' + + (namespace.prefix ? this.visit(namespace.prefix) + ' ' : '') + + this.visit(namespace.val) + ';'; + }; + + /** + * Visit Literal. + */ + + visitLiteral(lit) { + var val = lit.val; + if (lit.css) val = val.replace(/^ /gm, ''); + return val; + }; + + /** + * Visit Boolean. + */ + + visitBoolean(bool) { + return bool.toString(); + }; + + /** + * Visit RGBA. + */ + + visitRGBA(rgba) { + return rgba.toString(); + }; + + /** + * Visit HSLA. + */ + + visitHSLA(hsla) { + return hsla.rgba.toString(); + }; + + /** + * Visit Unit. + */ + + visitUnit(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, time, degree or fraction + if (!(['%', 's', 'ms', 'deg', 'fr'].includes(type)) && 0 == n) return '0'; + // Omit leading '0' on floats + if (float && n < 1 && n > -1) { + return n.toString().replace('0.', '.') + type; + } + } -/** - * Visit Ident. - */ + return (float ? parseFloat(n.toFixed(15)) : n).toString() + type; + }; -Compiler.prototype.visitIdent = function(ident){ - return ident.name; -}; + /** + * Visit Group. + */ -/** - * Visit String. - */ + visitGroup(group) { + var stack = this.keyframe ? [] : this.stack + , comma = this.compress ? ',' : ',\n'; -Compiler.prototype.visitString = function(string){ - return this.isURL - ? string.val - : string.toString(); -}; + stack.push(group.nodes); -/** - * Visit Null. - */ + // selectors + if (group.block.hasProperties) { + var selectors = utils.compileSelectors.call(this, stack) + , len = selectors.length; -Compiler.prototype.visitNull = function(node){ - return ''; -}; + if (len) { + if (this.keyframe) comma = this.compress ? ',' : ', '; -/** - * Visit Call. - */ + for (var i = 0; i < len; ++i) { + var selector = selectors[i] + , last = (i == len - 1); -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. - */ + // keyframe blocks (10%, 20% { ... }) + if (this.keyframe) selector = i ? selector.trim() : selector; -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; + this.buf += this.out(selector + (last ? '' : comma), group.nodes[i]); + } + } else { + group.block.lacksRenderedSelectors = true; + } + } - var space = self.isURL || (self.isCondition + // output block + this.visit(group.block); + stack.pop(); + }; + + /** + * Visit Ident. + */ + + visitIdent(ident) { + return ident.name; + }; + + /** + * Visit String. + */ + + visitString(string) { + return this.isURL + ? string.val + : string.toString(); + }; + + /** + * Visit Null. + */ + + visitNull(node) { + return ''; + }; + + /** + * Visit Call. + */ + + visitCall(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. + */ + + visitExpression(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); - }); + buf.push(expr.isList + ? (self.compress ? ',' : ', ') + : space); + }); - return buf.join(''); -}; + return buf.join(''); + }; -/** - * Visit Arguments. - */ + /** + * Visit Arguments. + */ -Compiler.prototype.visitArguments = Compiler.prototype.visitExpression; + visitArguments = this.visitExpression; -/** - * Visit Property. - */ + /** + * Visit Property. + */ -Compiler.prototype.visitProperty = function(prop){ - var val = this.visit(prop.expr).trim() - , name = (prop.name || prop.segments.join('')) - , arr = []; + visitProperty(prop) { + var val = this.visit(prop.expr).trim() + , name = (prop.name || prop.segments.join('')) + , arr = []; - if (name === '@apply') { + if (name === '@apply') { + arr.push( + this.out(this.indent), + this.out(name + ' ', prop), + this.out(val, prop.expr), + this.out(this.compress ? (this.last ? '' : ';') : ';') + ); + return arr.join(''); + } arr.push( this.out(this.indent), - this.out(name + ' ', prop), + this.out(name + (this.compress ? ':' : ': '), prop), this.out(val, prop.expr), this.out(this.compress ? (this.last ? '' : ';') : ';') ); return arr.join(''); - } - 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. - */ + /** + * Debug info. + */ -Compiler.prototype.debugInfo = function(node){ + debugInfo(node) { - var path = node.filename == 'stdin' ? 'stdin' : fs.realpathSync(node.filename) - , line = (node.nodes && node.nodes.length ? node.nodes[0].lineno : node.lineno) || 1; + 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.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'; + 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'; + } } -} + +}; diff --git a/lib/visitor/evaluator.js b/lib/visitor/evaluator.js index 1afca1e07..804ff7a75 100644 --- a/lib/visitor/evaluator.js +++ b/lib/visitor/evaluator.js @@ -103,1511 +103,1506 @@ function importFile(node, file, literal) { 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; +module.exports = class Evaluator extends Visitor { + /** + * 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 + */ + + constructor(root, options) { + super(root); + options = options || {}; + 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; + } -/** - * Proxy visit to expose node line numbers. - * - * @param {Node} node - * @return {Node} - * @api private - */ + /** + * 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(); + visit(node) { try { - err.input = fs.readFileSync(err.filename, 'utf8'); + return super.visit(node); } catch (err) { - // ignore + 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; } - throw err; - } -}; - -/** - * Perform evaluation setup: - * - * - populate global scope - * - iterate imports - * - * @api private - */ + }; -Evaluator.prototype.setup = function(){ - var root = this.root; - var imports = []; + /** + * Perform evaluation setup: + * + * - populate global scope + * - iterate imports + * + * @api private + */ + + setup() { + 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); + }; - 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); + /** + * Populate the global scope with: + * + * - css colors + * - user-defined globals + * + * @api private + */ + + populateGlobalScope() { + 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); + }); - root.nodes = imports.concat(root.nodes); -}; + // expose url function + scope.add(new nodes.Ident( + 'embedurl', + new nodes.Function('embedurl', require('../functions/url')({ + limit: false + })) + )); + + // user-defined globals + var globals = this.globals; + Object.keys(globals).forEach(function (name) { + var val = globals[name]; + if (!val.nodeName) val = new nodes.Literal(val); + scope.add(new nodes.Ident(name, val)); + }); + }; -/** - * Populate the global scope with: - * - * - css colors - * - user-defined globals - * - * @api private - */ + /** + * Evaluate the tree. + * + * @return {Node} + * @api private + */ + + evaluate() { + debug('eval %s', this.filename); + this.setup(); + return this.visit(this.root); + }; -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); - }); - - // expose url function - scope.add(new nodes.Ident( - 'embedurl', - new nodes.Function('embedurl', require('../functions/url')({ - limit: false - })) - )); - - // user-defined globals - var globals = this.globals; - Object.keys(globals).forEach(function(name){ - var val = globals[name]; - if (!val.nodeName) val = new nodes.Literal(val); - scope.add(new nodes.Ident(name, val)); - }); -}; + /** + * Visit Group. + */ -/** - * Evaluate the tree. - * - * @return {Node} - * @api private - */ + visitGroup(group) { + group.nodes = group.nodes.map(function (selector) { + selector.val = this.interpolate(selector); + debug('ruleset %s', selector.val); + return selector; + }, this); -Evaluator.prototype.evaluate = function(){ - debug('eval %s', this.filename); - this.setup(); - return this.visit(this.root); -}; + group.block = this.visit(group.block); + return group; + }; -/** - * Visit Group. - */ + /** + * Visit Return. + */ -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); + visitReturn(ret) { + ret.expr = this.visit(ret.expr); + throw ret; + }; - group.block = this.visit(group.block); - return group; -}; + /** + * Visit Media. + */ -/** - * Visit Return. - */ + visitMedia(media) { + media.block = this.visit(media.block); + media.val = this.visit(media.val); + return media; + }; -Evaluator.prototype.visitReturn = function(ret){ - ret.expr = this.visit(ret.expr); - throw ret; -}; + /** + * Visit QueryList. + */ + + visitQueryList(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 Media. - */ + /** + * Visit Query. + */ -Evaluator.prototype.visitMedia = function(media){ - media.block = this.visit(media.block); - media.val = this.visit(media.val); - return media; -}; + visitQuery(node) { + node.predicate = this.visit(node.predicate); + node.type = this.visit(node.type); + node.nodes.forEach(this.visit, this); + return node; + }; -/** - * Visit QueryList. - */ + /** + * Visit Feature. + */ -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()); + visitFeature(node) { + node.name = this.interpolate(node); + if (node.expr) { + this.return++; + node.expr = this.visit(node.expr); + this.return--; } - } - return queries; -}; + return node; + }; -/** - * Visit Query. - */ + /** + * Visit Object. + */ -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; -}; + visitObject(obj) { + for (var key in obj.vals) { + obj.vals[key] = this.visit(obj.vals[key]); + } + return obj; + }; -/** - * Visit Feature. - */ + /** + * Visit Member. + */ -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; -}; + visitMember(node) { + var left = node.left + , right = node.right + , obj = this.visit(left).first; -/** - * Visit Object. - */ + 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); + }; -Evaluator.prototype.visitObject = function(obj){ - for (var key in obj.vals) { - obj.vals[key] = this.visit(obj.vals[key]); - } - return obj; -}; + /** + * Visit Keyframes. + */ -/** - * Visit Member. - */ + visitKeyframes(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); -Evaluator.prototype.visitMember = function(node){ - var left = node.left - , right = node.right - , obj = this.visit(left).first; + if ('official' != keyframes.prefix) return keyframes; - 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); -}; + 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); -/** - * Visit Keyframes. - */ + return nodes.null; + }; -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. + */ -/** - * Visit Function. - */ + visitFunction(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'); -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'); - // 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'); - // BIF - var bif = bifs[fn.name]; - if (bif) this.warn('built-in function "' + fn.name + '" is already defined'); + return fn; + }; - return fn; -}; + /** + * Visit Each. + */ -/** - * Visit Each. - */ + visitEach(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--; -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); - } + each.block.scope = false; - // 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); + function visitBody(key, val) { + scope.add(val); + scope.add(key); + body = self.visit(each.block.clone()); + vals = vals.concat(body.nodes); } - } 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. - */ + // 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); + } + } -Evaluator.prototype.visitCall = function(call){ - debug('call %s', call); - var fn = this.lookup(call.name) - , literal - , ret; + this.mixin(vals, block); + return vals[vals.length - 1] || nodes.null; + }; - // url() - this.ignoreColors = 'url' == call.name; + /** + * Visit Call. + */ - // Variable function - if (fn && 'expression' == fn.nodeName) { - fn = fn.nodes[0]; - } + visitCall(call) { + debug('call %s', call); + var fn = this.lookup(call.name) + , literal + , ret; - // Not a function? try user-defined or built-ins - if (fn && 'function' != fn.nodeName) { - fn = this.lookupFunction(call.name); - } + // url() + this.ignoreColors = 'url' == 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); + // Variable function + if (fn && 'expression' == fn.nodeName) { + fn = fn.nodes[0]; } - 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'); - } + // Not a function? try user-defined or built-ins + if (fn && 'function' != fn.nodeName) { + fn = this.lookupFunction(call.name); + } - // First node in expression - if ('expression' == fn.nodeName) fn = fn.first; + // 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; + } - // Evaluate arguments - this.return++; - var args = this.visit(call.args); + this.calling.push(call.name); - 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); - } + // Massive stack + if (this.calling.length > 200) { + throw new RangeError('Maximum stylus call stack size exceeded'); + } - this.calling.pop(); - this.ignoreColors = false; - return ret; -}; + // First node in expression + if ('expression' == fn.nodeName) fn = fn.first; -/** - * Visit Ident. - */ + // Evaluate arguments + this.return++; + var args = this.visit(call.args); -Evaluator.prototype.visitIdent = function(ident){ - var prop; - // Property lookup - if (ident.property) { - if (prop = this.lookupProperty(ident.name)) { - return this.visit(prop.expr.clone()); + for (var key in args.map) { + args.map[key] = this.visit(args.map[key].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); + // 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.return++; - // Visit operands - var op = binop.op - , left = this.visit(binop.left) - , right = ('||' == op || '&&' == op) - ? binop.right : this.visit(binop.right); + this.calling.pop(); + this.ignoreColors = false; + return ret; + }; - // HACK: ternary - var val = binop.val - ? this.visit(binop.val) - : null; - this.return--; + /** + * Visit Ident. + */ - // 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; + visitIdent(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; } - throw err; - } -}; + }; -/** - * Visit UnaryOp. - */ + /** + * Visit BinOp. + */ -Evaluator.prototype.visitUnaryOp = function(unary){ - var op = unary.op - , node = this.visit(unary.expr); + visitBinOp(binop) { + // Special-case "is defined" pseudo binop + if ('is defined' == binop.op) return this.isDefined(binop.left); - if ('!' != op) { - node = node.first.clone(); - utils.assertType(node, 'unit'); - } + 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--; - 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(); - } + // 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; + } + }; - return node; -}; + /** + * Visit UnaryOp. + */ -/** - * Visit TernaryOp. - */ + visitUnaryOp(unary) { + var op = unary.op + , node = this.visit(unary.expr); -Evaluator.prototype.visitTernary = function(ternary){ - var ok = this.visit(ternary.cond).toBoolean(); - return ok.isTrue - ? this.visit(ternary.trueExpr) - : this.visit(ternary.falseExpr); -}; + if ('!' != op) { + node = node.first.clone(); + utils.assertType(node, 'unit'); + } -/** - * Visit Expression. - */ + 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(); + } -Evaluator.prototype.visitExpression = function(expr){ - for (var i = 0, len = expr.nodes.length; i < len; ++i) { - expr.nodes[i] = this.visit(expr.nodes[i]); - } + return node; + }; - // support (n * 5)px etc - if (this.castable(expr)) expr = this.cast(expr); + /** + * Visit TernaryOp. + */ - return expr; -}; + visitTernary(ternary) { + var ok = this.visit(ternary.cond).toBoolean(); + return ok.isTrue + ? this.visit(ternary.trueExpr) + : this.visit(ternary.falseExpr); + }; -/** - * Visit Arguments. - */ + /** + * Visit Expression. + */ -Evaluator.prototype.visitArguments = Evaluator.prototype.visitExpression; + visitExpression(expr) { + for (var i = 0, len = expr.nodes.length; i < len; ++i) { + expr.nodes[i] = this.visit(expr.nodes[i]); + } -/** - * Visit Property. - */ + // support (n * 5)px etc + if (this.castable(expr)) expr = this.cast(expr); -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; - } -}; + return expr; + }; -/** - * Visit Root. - */ + /** + * Visit Arguments. + */ + + visitArguments = this.visitExpression; + + /** + * Visit Property. + */ + + visitProperty(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; + } + }; -Evaluator.prototype.visitRoot = function(block){ - // normalize cached imports - if (block != this.root) { - block.constructor = nodes.Block; - return this.visit(block); - } + /** + * Visit Root. + */ - for (var i = 0; i < block.nodes.length; ++i) { - block.index = i; - block.nodes[i] = this.visit(block.nodes[i]); - } - return block; -}; + visitRoot(block) { + // normalize cached imports + if (block != this.root) { + block.constructor = nodes.Block; + return this.visit(block); + } -/** - * Visit Block. - */ + for (var i = 0; i < block.nodes.length; ++i) { + block.index = i; + block.nodes[i] = this.visit(block.nodes[i]); + } + return 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; + /** + * Visit Block. + */ + + visitBlock(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 { - block.nodes[block.index] = err; - break; + throw err; } - } else { - throw err; } } - } - this.stack.pop(); - return block; -}; + this.stack.pop(); + return block; + }; -/** - * Visit Atblock. - */ + /** + * Visit Atblock. + */ -Evaluator.prototype.visitAtblock = function(atblock){ - atblock.block = this.visit(atblock.block); - return atblock; -}; + visitAtblock(atblock) { + atblock.block = this.visit(atblock.block); + return atblock; + }; -/** - * Visit Atrule. - */ + /** + * Visit Atrule. + */ -Evaluator.prototype.visitAtrule = function(atrule){ - atrule.val = this.interpolate(atrule); - if (atrule.block) atrule.block = this.visit(atrule.block); - return atrule; -}; + visitAtrule(atrule) { + atrule.val = this.interpolate(atrule); + if (atrule.block) atrule.block = this.visit(atrule.block); + return atrule; + }; -/** - * Visit Supports. - */ + /** + * Visit Supports. + */ -Evaluator.prototype.visitSupports = function(node){ - var condition = node.condition - , val; + visitSupports(node) { + var condition = node.condition + , val; - this.return++; - node.condition = this.visit(condition); - this.return--; + 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; -}; + 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. - */ + /** + * Visit If. + */ -Evaluator.prototype.visitIf = function(node){ - var ret - , block = this.currentBlock - , negate = node.negate; + visitIf(node) { + var ret + , block = this.currentBlock + , negate = node.negate; - this.return++; - var ok = this.visit(node.cond).first.toBoolean(); - this.return--; + this.return++; + var ok = this.visit(node.cond).first.toBoolean(); + this.return--; - node.block.scope = node.block.hasMedia; + 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; - } + // 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 { - elses[i].scope = elses[i].hasMedia; - ret = this.visit(elses[i]); + } 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++; + // 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; + } - var path = this.visit(imported.path).first - , nodeName = imported.once ? 'require' : 'import' - , found - , literal; + return ret || nodes.null; + }; - this.return--; - debug('import %s', path); + /** + * Visit Extend. + */ + + visitExtend(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; + }; - // url() passed - if ('url' == path.name) { - if (imported.once) throw new Error('You cannot @require a url'); + /** + * Visit Import. + */ - return imported; - } + visitImport(imported) { + this.return++; - // Ensure string - if (!path.string) throw new Error('@' + nodeName + ' string expected'); + var path = this.visit(imported.path).first + , nodeName = imported.once ? 'require' : 'import' + , found + , literal; - var name = path = path.string; + this.return--; + debug('import %s', path); - // 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; - } + // url() passed + if ('url' == path.name) { + if (imported.once) throw new Error('You cannot @require a url'); - // 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'; + // Ensure string + if (!path.string) throw new Error('@' + nodeName + ' string expected'); - // 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 - */ + var name = path = path.string; -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); + // 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; + } - // mixin block - var mixinBlock = this.stack.currentFrame.block; + // Literal + if (/\.css(?:"|$)/.test(path)) { + literal = true; + if (!imported.once && !this.includeCSS) { + return imported; + } + } - // new block scope - this.stack.push(new Frame(block)); - var scope = this.currentScope; + // support optional .styl + if (!literal && !/\.styl$/i.test(path)) path += '.styl'; - // normalize arguments - if ('arguments' != args.nodeName) { - var expr = new nodes.Expression; - expr.push(args); - args = nodes.Arguments.fromExpression(expr); - } + // Lookup + found = utils.find(path, this.paths, this.filename); + if (!found) { + found = utils.lookupIndex(name, this.paths, this.filename); + } - // arguments local - scope.add(new nodes.Ident('arguments', args)); + // Throw if import failed + if (!found) throw new Error('failed to locate @' + nodeName + ' file ' + path); - // mixin scope introspection - scope.add(new nodes.Ident('mixin', this.return - ? nodes.false - : new nodes.String(mixinBlock.nodeName))); + var block = new nodes.Block; - // 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)); - } + for (var i = 0, len = found.length; i < len; ++i) { + block.push(importFile.call(this, imported, found[i], literal)); + } - // current call stack - var expr = new nodes.Expression; - for (var i = this.calling.length - 1; i-- ; ) { - expr.push(new nodes.Literal(this.calling[i])); + return block; }; - 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] = this.visit(node) : node.val = arg; - } else { - args.push(node.val); - } - // required argument not satisfied - if (node.val.isNull) { - throw new Error('argument "' + node + '" required for ' + fn); - } + /** + * Invoke `fn` with `args`. + * + * @param {Function} fn + * @param {Array} args + * @return {Node} + * @api private + */ + + invokeFunction(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); } - scope.add(node); - }, this); - - // mixin block - if (content) scope.add(new nodes.Ident('block', content, true)); - - // invoke - return this.invoke(body, true, fn.filename); -}; + // arguments local + scope.add(new nodes.Ident('arguments', args)); -/** - * Invoke built-in `fn` with `args`. - * - * @param {Function} fn - * @param {Array} args - * @return {Node} - * @api private - */ + // mixin scope introspection + scope.add(new nodes.Ident('mixin', this.return + ? nodes.false + : new nodes.String(mixinBlock.nodeName))); -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 { - if (!fn.params) { - fn.params = utils.params(fn); + // 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)); } - args = fn.params.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)); - } + + // 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] = this.visit(node) : node.val = arg; } else { - ret.push(arg.first); + args.push(node.val); } - } - 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; -}; + // required argument not satisfied + if (node.val.isNull) { + throw new Error('argument "' + node + '" required for ' + fn); + } + } -/** - * Mixin the given `nodes` to the given `block`. - * - * @param {Array} nodes - * @param {Block} block - * @api private - */ + scope.add(node); + }, this); -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 block + if (content) scope.add(new nodes.Ident('block', content, true)); -/** - * Mixin the given `items` to the `dest` array. - * - * @param {Array} items - * @param {Array} dest - * @param {Block} block - * @api private - */ + // invoke + return this.invoke(body, true, fn.filename); + }; -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'); + /** + * Invoke built-in `fn` with `args`. + * + * @param {Function} fn + * @param {Array} args + * @return {Node} + * @api private + */ + + invokeBuiltin(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 { + if (!fn.params) { + fn.params = utils.params(fn); + } + args = fn.params.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); + } } - default: - dest.push(node); + return ret; + }, []); } - } -}; -/** - * Mixin the given `node` to the current block. - * - * @param {Node} node - * @api private - */ + // Invoke the BIF + var body = utils.coerce(fn.apply(this, args)); -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; + // Always wrapping allows js functions + // to return several values with a single + // Expression node + var expr = new nodes.Expression; + expr.push(body); + body = expr; - 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; - } + // Invoke + return this.invoke(body); + }; - 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; + /** + * Invoke the given function `body`. + * + * @param {Block} body + * @return {Node} + * @api private + */ + + invoke(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; } - } -}; -/** - * Evaluate the given `vals`. - * - * @param {Array} vals - * @return {Node} - * @api private - */ + if (filename) this.paths.pop(); -Evaluator.prototype.eval = function(vals){ - if (!vals) return nodes.null; - var len = vals.length - , node = nodes.null; + return ret; + }; - try { + /** + * Mixin the given `nodes` to the given `block`. + * + * @param {Array} nodes + * @param {Block} block + * @api private + */ + + mixin(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 + */ + + _mixin(items, dest, block) { + var node + , len = items.length; 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': + switch ((node = items[i]).nodeName) { + case 'return': + return; case 'block': - node = this.visit(node); - if (node.nodes) node = this.eval(node.nodes); + 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: - node = this.visit(node); + dest.push(node); } } - } catch (err) { - if ('return' == err.nodeName) { - return err.expr; - } else { - throw err; + }; + + /** + * Mixin the given `node` to the current block. + * + * @param {Node} node + * @api private + */ + + mixinNode(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; } - } + }; - return node; -}; + /** + * Mixin the given `object` to the current block. + * + * @param {Object} object + * @api private + */ -/** - * Literal function `call`. - * - * @param {Call} call - * @return {call} - * @api private - */ + mixinObject(object) { + var Parser = require('../parser') + , root = this.root + , str = '$block ' + object.toBlock() + , parser = new Parser(str, utils.merge({ root: block }, this.options)) + , block; -Evaluator.prototype.literalCall = function(call){ - call.args = this.visit(call.args); - return call; -}; + 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; + } -/** - * Lookup property `name`. - * - * @param {String} name - * @return {Property} - * @api private - */ + 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; + } + } + }; -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(); - } + /** + * Evaluate the given `vals`. + * + * @param {Array} vals + * @return {Node} + * @api private + */ + + eval(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); } - break; + } + } catch (err) { + if ('return' == err.nodeName) { + return err.expr; + } else { + throw err; + } } - } - return nodes.null; -}; + return node; + }; -/** - * Return the closest mixin-able `Block`. - * - * @return {Block} - * @api private - */ + /** + * Literal function `call`. + * + * @param {Call} call + * @return {call} + * @api private + */ + + literalCall(call) { + call.args = this.visit(call.args); + return call; + }; -Evaluator.prototype.__defineGetter__('closestBlock', function(){ - var i = this.stack.length - , block; - while (i--) { - block = this.stack[i].block; - if (block.node) { + /** + * Lookup property `name`. + * + * @param {String} name + * @return {Property} + * @api private + */ + + lookupProperty(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 'keyframes': + case 'function': + case 'if': + case 'each': case 'atrule': - case 'atblock': case 'media': + case 'atblock': case 'call': - return block; + 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 the closest group block. - * - * @return {Block} - * @api private - */ + return nodes.null; + }; -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 closest mixin-able `Block`. + * + * @return {Block} + * @api private + */ + + get closestBlock() { + 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 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 the closest group block. + * + * @return {Block} + * @api private + */ + + get closestGroup() { + var i = this.stack.length + , block; + while (i--) { + block = this.stack[i].block; + if (block.node && 'group' == block.node.nodeName) { + return block; + } } - } - 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(/\.(?=[\w-])|^\.$/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; + /** + * Return the current selectors stack. + * + * @return {Array} + * @api private + */ + + get selectorStack() { + 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); + } } - } - - 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 - */ + return stack; + }; -Evaluator.prototype.isDefined = function(node){ - if ('ident' == node.nodeName) { - return new nodes.Boolean(this.lookup(node.name)); - } else { - throw new Error('invalid "is defined" check on non-variable ' + node); - } -}; + /** + * Lookup `name`, with support for JavaScript + * functions, and BIFs. + * + * @param {String} name + * @return {Node} + * @api private + */ + + lookup(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); + } + }; -/** - * 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 - */ + /** + * Map segments in `node` returning a string. + * + * @param {Node} node + * @return {String} + * @api private + */ + + interpolate(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(/\.(?=[\w-])|^\.$/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; + } + } -Evaluator.prototype.propertyExpression = function(prop, name){ - var expr = new nodes.Expression - , val = prop.expr.clone(); + if (node.segments) { + return node.segments.map(toString).join(''); + } else { + return toString(node); + } + }; - // name - expr.push(new nodes.String(prop.name)); + /** + * Lookup JavaScript user-defined or built-in function. + * + * @param {String} name + * @return {Function} + * @api private + */ + + lookupFunction(name) { + var fn = this.functions[name] || bifs[name]; + if (fn) return new nodes.Function(name, fn); + }; - // replace cyclic call with __CALL__ - function replace(node) { - if ('call' == node.nodeName && name == node.name) { - return new nodes.Literal('__CALL__'); + /** + * Check if the given `node` is an ident, and if it is defined. + * + * @param {Node} node + * @return {Boolean} + * @api private + */ + + isDefined(node) { + if ('ident' == node.nodeName) { + return new nodes.Boolean(this.lookup(node.name)); + } else { + throw new Error('invalid "is defined" check on non-variable ' + node); } + }; - if (node.nodes) node.nodes = node.nodes.map(replace); - return 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 + */ + + propertyExpression(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__'); + } - replace(val); - expr.push(val); - return expr; -}; + if (node.nodes) node.nodes = node.nodes.map(replace); + return node; + } -/** - * Cast `expr` to the trailing ident. - * - * @param {Expression} expr - * @return {Unit} - * @api private - */ + replace(val); + expr.push(val); + return expr; + }; -Evaluator.prototype.cast = function(expr){ - return new nodes.Unit(expr.first.val, expr.nodes[1].name); -}; + /** + * Cast `expr` to the trailing ident. + * + * @param {Expression} expr + * @return {Unit} + * @api private + */ -/** - * 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); -}; + cast(expr) { + return new nodes.Unit(expr.first.val, expr.nodes[1].name); + }; -/** - * Warn with the given `msg`. - * - * @param {String} msg - * @api private - */ + /** + * Check if `expr` is castable. + * + * @param {Expression} expr + * @return {Boolean} + * @api private + */ + + castable(expr) { + return 2 == expr.nodes.length + && 'unit' == expr.first.nodeName + && ~units.indexOf(expr.nodes[1].name); + }; -Evaluator.prototype.warn = function(msg){ - if (!this.warnings) return; - console.warn('\u001b[33mWarning:\u001b[0m ' + msg); -}; + /** + * Warn with the given `msg`. + * + * @param {String} msg + * @api private + */ -/** - * Return the current `Block`. - * - * @return {Block} - * @api private - */ + warn(msg) { + if (!this.warnings) return; + console.warn('\u001b[33mWarning:\u001b[0m ' + msg); + }; -Evaluator.prototype.__defineGetter__('currentBlock', function(){ - return this.stack.currentFrame.block; -}); + /** + * Return the current `Block`. + * + * @return {Block} + * @api private + */ -/** - * Return an array of vendor names. - * - * @return {Array} - * @api private - */ + get currentBlock() { + return this.stack.currentFrame.block; + }; -Evaluator.prototype.__defineGetter__('vendors', function(){ - return this.lookup('vendors').nodes.map(function(node){ - return node.string; - }); -}); + /** + * Return an array of vendor names. + * + * @return {Array} + * @api private + */ -/** - * Return the property name without vendor prefix. - * - * @param {String} prop - * @return {String} - * @api public - */ + get vendors() { + return this.lookup('vendors').nodes.map(function (node) { + return node.string; + }); + }; -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 the property name without vendor prefix. + * + * @param {String} prop + * @return {String} + * @api public + */ + + unvendorize(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 prop; + }; -/** - * Return the current frame `Scope`. - * - * @return {Scope} - * @api private - */ + /** + * Return the current frame `Scope`. + * + * @return {Scope} + * @api private + */ -Evaluator.prototype.__defineGetter__('currentScope', function(){ - return this.stack.currentFrame.scope; -}); + get currentScope() { + return this.stack.currentFrame.scope; + }; -/** - * Return the current `Frame`. - * - * @return {Frame} - * @api private - */ + /** + * Return the current `Frame`. + * + * @return {Frame} + * @api private + */ -Evaluator.prototype.__defineGetter__('currentFrame', function(){ - return this.stack.currentFrame; -}); + get currentFrame() { + return this.stack.currentFrame; + }; +}; diff --git a/lib/visitor/index.js b/lib/visitor/index.js index b7f3ce3f5..ebfb91417 100644 --- a/lib/visitor/index.js +++ b/lib/visitor/index.js @@ -5,27 +5,28 @@ * MIT Licensed */ -/** - * Initialize a new `Visitor` with the given `root` Node. - * - * @param {Node} root - * @api private - */ +module.exports = class Visitor { + /** + * Initialize a new `Visitor` with the given `root` Node. + * + * @param {Node} root + * @api private + */ -var Visitor = module.exports = function Visitor(root) { - this.root = root; -}; + constructor(root) { + this.root = root; + } -/** - * Visit the given `node`. - * - * @param {Node|Array} node - * @api public - */ + /** + * 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; + visit(node, fn) { + var method = 'visit' + node.constructor.name; + if (this[method]) return this[method](node); + return node; + }; }; - diff --git a/lib/visitor/normalizer.js b/lib/visitor/normalizer.js index 215e6f3c5..d6d97654f 100644 --- a/lib/visitor/normalizer.js +++ b/lib/visitor/normalizer.js @@ -13,431 +13,427 @@ 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); +module.exports = class Normalizer extends Visitor { + /** + * 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 + */ + + constructor(root, options) { + super(root); + options = options || {}; + this.hoist = options['hoist atrules']; + this.stack = []; + this.map = {}; + this.imports = []; } - return ret; -}; + /** + * Normalize the node tree. + * + * @return {Node} + * @api private + */ -/** - * Bubble up the given `node`. - * - * @param {Node} node - * @api private - */ - -Normalizer.prototype.bubble = function(node){ - var props = [] - , other = [] - , self = this; + normalize() { + var ret = this.visit(this.root); - 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); - } - }); - } + if (this.hoist) { + // hoist @import + if (this.imports.length) ret.nodes = this.imports.concat(ret.nodes); - 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; + // hoist @charset + if (this.charset) ret.nodes = [this.charset].concat(ret.nodes); + } - var block = new nodes.Block(node.block, group); - block.lineno = node.lineno; - block.column = node.column; - block.filename = node.filename; + return ret; + }; + + /** + * Bubble up the given `node`. + * + * @param {Node} node + * @api private + */ + + bubble(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); + } + }); + } - props.forEach(function(prop){ - block.push(prop); - }); + filterProps(node.block); - group.push(selector); - group.block = 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 = '&'; - node.block.nodes = []; - node.block.push(group); - other.forEach(function(n){ - node.block.push(n); - }); + var group = new nodes.Group; + group.lineno = node.lineno; + group.column = node.column; + group.filename = node.filename; - var group = this.closestGroup(node.block); - if (group) node.group = group.clone(); + var block = new nodes.Block(node.block, group); + block.lineno = node.lineno; + block.column = node.column; + block.filename = node.filename; - node.bubbled = true; - } -}; + props.forEach(function (prop) { + block.push(prop); + }); -/** - * Return group closest to the given `block`. - * - * @param {Block} block - * @return {Group} - * @api private - */ + group.push(selector); + group.block = block; -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; - } -}; + node.block.nodes = []; + node.block.push(group); + other.forEach(function (n) { + node.block.push(n); + }); -/** - * Visit Root. - */ + var group = this.closestGroup(node.block); + if (group) node.group = group.clone(); -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)); + node.bubbled = true; } - } - - 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 group closest to the given `block`. + * + * @param {Block} block + * @return {Group} + * @api private + */ + + closestGroup(block) { + var parent = block.parent + , node; + while (parent && (node = parent.node)) { + if ('group' == node.nodeName) return node; + parent = node.block && node.block.parent; } - return node; - }); - return expr; -}; + }; -/** - * Visit Block. - */ + /** + * Visit Root. + */ -Normalizer.prototype.visitBlock = function(block){ - var node; + visitRoot(block) { + var ret = new nodes.Root + , node; - if (block.hasProperties) { - for (var i = 0, len = block.nodes.length; i < len; ++i) { + for (var i = 0; i < block.nodes.length; ++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); + this.rootIndex = i; + ret.push(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; + return ret; + }; + + /** + * Visit Property. + */ + + visitProperty(prop) { + this.visit(prop.expr); + return prop; + }; + + /** + * Visit Expression. + */ + + visitExpression(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. + */ + + visitBlock(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); + } } - 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; -}; + // nesting + for (var i = 0, len = block.nodes.length; i < len; ++i) { + node = block.nodes[i]; + block.nodes[i] = this.visit(node); + } -/** - * Visit Media. - */ + return block; + }; -Normalizer.prototype.visitMedia = function(media){ - var medias = [] - , group = this.closestGroup(media.block) - , parent; + /** + * Visit Group. + */ - 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); - } - }); - } + visitGroup(group) { + var stack = this.stack + , map = this.map + , parts; - 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); + // 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; } - 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; + 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; } - }, this); - } - return media; -}; - -/** - * Visit Supports. - */ - -Normalizer.prototype.visitSupports = function(node){ - this.bubble(node); - return node; -}; + }); + stack.push(group.nodes); -/** - * Visit Atrule. - */ + var selectors = utils.compileSelectors(stack, true); -Normalizer.prototype.visitAtrule = function(node){ - if (node.block) node.block = this.visit(node.block); - return node; -}; + // map for extension lookup + selectors.forEach(function (selector) { + map[selector] = map[selector] || []; + map[selector].push(group); + }); -/** - * Visit Keyframes. - */ + // extensions + this.extend(group, selectors); + + stack.pop(); + return group; + }; + + /** + * Visit Function. + */ + + visitFunction() { + return nodes.null; + }; + + /** + * Visit Media. + */ + + visitMedia(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); + } + }); + } -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; -}; + 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 Import. - */ + /** + * Visit Supports. + */ -Normalizer.prototype.visitImport = function(node){ - this.imports.push(node); - return this.hoist ? nodes.null : node; -}; + visitSupports(node) { + this.bubble(node); + return node; + }; -/** - * Visit Charset. - */ + /** + * Visit Atrule. + */ -Normalizer.prototype.visitCharset = function(node){ - this.charset = node; - return this.hoist ? nodes.null : node; -}; + visitAtrule(node) { + if (node.block) node.block = this.visit(node.block); + return node; + }; -/** - * Apply `group` extensions. - * - * @param {Group} group - * @param {Array} selectors - * @api private - */ + /** + * Visit Keyframes. + */ -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; - groups = self._checkForPrefixedGroups(extend.selector); - if(!groups) { - var err = new Error('Failed to @extend "' + extend.selector + '"'); - err.lineno = extend.lineno; - err.column = extend.column; - throw err; + visitKeyframes(node) { + var frames = node.block.nodes.filter(function (frame) { + return frame.block && frame.block.hasProperties; + }); + node.frames = frames.length; + return node; + }; + + /** + * Visit Import. + */ + + visitImport(node) { + this.imports.push(node); + return this.hoist ? nodes.null : node; + }; + + /** + * Visit Charset. + */ + + visitCharset(node) { + this.charset = node; + return this.hoist ? nodes.null : node; + }; + + /** + * Apply `group` extensions. + * + * @param {Group} group + * @param {Array} selectors + * @api private + */ + + extend(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; + groups = self._checkForPrefixedGroups(extend.selector); + if (!groups) { + 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); + 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); + group.block = this.visit(group.block); + }; + + _checkForPrefixedGroups(selector) { + var prefix = []; + var map = this.map; + var result = null; + for (var i = 0; i < this.stack.length; i++) { + var stackElementArray = this.stack[i]; + var stackElement = stackElementArray[0]; + prefix.push(stackElement.val); + var fullSelector = prefix.join(" ") + " " + selector; + result = map[fullSelector]; + if (result) + break; + } + return result; + }; }; - -Normalizer.prototype._checkForPrefixedGroups = function (selector) { - var prefix = []; - var map = this.map; - var result = null; - for (var i = 0; i < this.stack.length; i++) { - var stackElementArray=this.stack[i]; - var stackElement = stackElementArray[0]; - prefix.push(stackElement.val); - var fullSelector = prefix.join(" ") + " " + selector; - result = map[fullSelector]; - if (result) - break; - } - return result; -}; \ No newline at end of file diff --git a/lib/visitor/sourcemapper.js b/lib/visitor/sourcemapper.js index 0e081104d..dc4dd2481 100644 --- a/lib/visitor/sourcemapper.js +++ b/lib/visitor/sourcemapper.js @@ -19,186 +19,180 @@ var Compiler = require('./compiler') , 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 (this.dest && extname(this.dest) === '.css') { - this.basename = basename(this.dest); - this.dest = dirname(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,' - + Buffer.from(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; +module.exports = class SourceMapper extends Compiler { + /** + * Initialize a new `SourceMapper` generator with the given `root` Node + * and the following `options`. + * + * @param {Node} root + * @api public + */ + + + constructor(root, options) { + super(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 (this.dest && extname(this.dest) === '.css') { + this.basename = basename(this.dest); + this.dest = dirname(this.dest); + } else { + this.basename = basename(this.filename, extname(this.filename)) + '.css'; } - } - - this.move(str); - return str; -}; - -/** - * Move current line and column position. - * - * @param {String} str - * @api private - */ + this.utf8 = false; -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, '/'); + this.map = new SourceMapGenerator({ + file: this.basename, + sourceRoot: sourcemap.sourceRoot || null + }); } - 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; + /** + * Generate and write source map. + * + * @return {String} + * @api private + */ + + compile() { + var css = super.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,' + + Buffer.from(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 + */ + + out(str, node) { + if (node && node.lineno) { + var filename = this.normalizePath(node.filename); this.map.addMapping({ original: { - line: lit.lineno + i, - column: column + line: node.lineno, + column: node.column - 1 }, generated: { - line: this.lineno + i, - column: 0 + line: this.lineno, + column: this.column - 1 }, source: filename }); - }, this); - } - return val; -}; -/** - * Visit Charset. - */ + 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 + */ + + move(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 + */ + + normalizePath(path) { + path = relative(this.dest || this.basePath, path); + if ('\\' == sep) { + path = path.replace(/^[a-z]:\\/i, '/') + .replace(/\\/g, '/'); + } + return path; + }; + + /** + * Visit Literal. + */ + + visitLiteral(lit) { + var val = super.visitLiteral.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); + visitCharset(node) { + this.utf8 = ('utf-8' == node.val.string.toLowerCase()); + return super.visitCharset(node); + }; }; From 252e6452d23f802daf18688a6f5ead8d85e9e03c Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 12:57:33 +0200 Subject: [PATCH 51/68] use classes in lib/visitor/deps-resolver --- lib/visitor/deps-resolver.js | 253 +++++++++++++++++------------------ 1 file changed, 124 insertions(+), 129 deletions(-) diff --git a/lib/visitor/deps-resolver.js b/lib/visitor/deps-resolver.js index 94827db9c..5cfc049f8 100644 --- a/lib/visitor/deps-resolver.js +++ b/lib/visitor/deps-resolver.js @@ -10,163 +10,158 @@ var Visitor = require('./') , 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 - */ +module.exports = class DepsResolver extends Visitor { + /** + * Initialize a new `DepsResolver` with the given `root` Node + * and the `options`. + * + * @param {Node} root + * @param {Object} options + * @api private + */ + + constructor(root, options) { + super(root) + this.filename = options.filename; + this.paths = options.paths || []; + this.paths.push(dirname(options.filename || '.')); + this.options = options; + this.functions = {}; + this.deps = []; + } -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`. - */ + visit(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: + super.visit(node); + } + }; -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. + */ -/** - * Visit Root. - */ + visitRoot(block) { + for (var i = 0, len = block.nodes.length; i < len; ++i) { + this.visit(block.nodes[i]); + } + }; -DepsResolver.prototype.visitRoot = function(block) { - for (var i = 0, len = block.nodes.length; i < len; ++i) { - this.visit(block.nodes[i]); - } -}; + /** + * Visit Ident. + */ -/** - * Visit Ident. - */ + visitIdent(ident) { + this.visit(ident.val); + }; -DepsResolver.prototype.visitIdent = function(ident) { - this.visit(ident.val); -}; + /** + * Visit If. + */ -/** - * Visit If. - */ + visitIf(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]); + } + }; -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. + */ -/** - * Visit Function. - */ + visitFunction(fn) { + this.functions[fn.name] = fn.block; + }; -DepsResolver.prototype.visitFunction = function(fn) { - this.functions[fn.name] = fn.block; -}; + /** + * Visit Call. + */ -/** - * Visit Call. - */ + visitCall(call) { + if (call.name in this.functions) this.visit(this.functions[call.name]); + if (call.block) this.visit(call.block); + }; -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. + */ -/** - * Visit Import. - */ + visitImport(node) { + // If it's a url() call, skip + if (node.path.first.name === 'url') return; -DepsResolver.prototype.visitImport = function(node) { - // If it's a url() call, skip - if (node.path.first.name === 'url') return; + var path = !node.path.first.val.isNull && node.path.first.val || node.path.first.name + , literal, found, oldPath; - var path = !node.path.first.val.isNull && node.path.first.val || node.path.first.name - , literal, found, oldPath; + if (!path) return; - if (!path) return; + literal = /\.css(?:"|$)/.test(path); - literal = /\.css(?:"|$)/.test(path); + // support optional .styl + if (!literal && !/\.styl$/i.test(path)) { + oldPath = path; + path += '.styl'; + } - // support optional .styl - if (!literal && !/\.styl$/i.test(path)) { - oldPath = path; - path += '.styl'; - } + // Lookup + found = utils.find(path, this.paths, this.filename); - // Lookup - found = utils.find(path, this.paths, this.filename); + // support optional index + if (!found && oldPath) found = utils.lookupIndex(oldPath, this.paths, this.filename); - // support optional index - if (!found && oldPath) found = utils.lookupIndex(oldPath, this.paths, this.filename); + if (!found) return; - if (!found) return; + this.deps = this.deps.concat(found); - this.deps = this.deps.concat(found); + if (literal) return; - 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)); - // 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); - 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; + } - 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); } + }; - this.visit(block); - } -}; - -/** - * Get dependencies. - */ + /** + * Get dependencies. + */ -DepsResolver.prototype.resolve = function() { - this.visit(this.root); - return utils.uniq(this.deps); + resolve() { + this.visit(this.root); + return utils.uniq(this.deps); + }; }; From 3590e2fd440a815d8c1a1c964334b8c76b0239b6 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:00:05 +0200 Subject: [PATCH 52/68] use classes in lib/lexer --- lib/lexer.js | 357 +++++++++++++++++++++++++-------------------------- 1 file changed, 173 insertions(+), 184 deletions(-) diff --git a/lib/lexer.js b/lib/lexer.js index b9bfa8ecf..e07f39788 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -13,18 +13,12 @@ var Token = require('./token') , nodes = require('./nodes') , errors = require('./errors'); -/** - * Expose `Lexer`. - */ - -exports = module.exports = Lexer; - /** * Operator aliases. */ var alias = { - 'and': '&&' + 'and': '&&' , 'or': '||' , 'is': '==' , 'isnt': '!=' @@ -32,70 +26,65 @@ var alias = { , ':=': '?=' }; -/** - * Initialize a new `Lexer` with the given `str` and `options`. - * - * @param {String} str - * @param {Object} options - * @api private - */ +exports = module.exports = class Lexer { + /** + * Initialize a new `Lexer` with the given `str` and `options`. + * + * @param {String} str + * @param {Object} options + * @api private + */ -function Lexer(str, options) { - options = options || {}; - this.stash = []; - this.indentStack = []; - this.indentRe = null; - this.lineno = 1; - this.column = 1; - - // HACK! - function comment(str, val, offset, s) { - var inComment = s.lastIndexOf('/*', offset) > s.lastIndexOf('*/', offset) - , commentIdx = s.lastIndexOf('//', offset) - , i = s.lastIndexOf('\n', offset) - , double = 0 - , single = 0; - - if (~commentIdx && commentIdx > i) { - while (i != offset) { - if ("'" == s[i]) single ? single-- : single++; - if ('"' == s[i]) double ? double-- : double++; - - if ('/' == s[i] && '/' == s[i + 1]) { - inComment = !single && !double; - break; + constructor(str, options) { + options = options || {}; + this.stash = []; + this.indentStack = []; + this.indentRe = null; + this.lineno = 1; + this.column = 1; + + // HACK! + function comment(str, val, offset, s) { + var inComment = s.lastIndexOf('/*', offset) > s.lastIndexOf('*/', offset) + , commentIdx = s.lastIndexOf('//', offset) + , i = s.lastIndexOf('\n', offset) + , double = 0 + , single = 0; + + if (~commentIdx && commentIdx > i) { + while (i != offset) { + if ("'" == s[i]) single ? single-- : single++; + if ('"' == s[i]) double ? double-- : double++; + + if ('/' == s[i] && '/' == s[i + 1]) { + inComment = !single && !double; + break; + } + ++i; } - ++i; } - } - - return inComment - ? str - : ((val === ',' && /^[,\t\n]+$/.test(str)) ? str.replace(/\n/, '\r') : val + '\r'); - }; - - // Remove UTF-8 BOM. - if ('\uFEFF' == str.charAt(0)) str = str.slice(1); - this.str = str - .replace(/\s+$/, '\n') - .replace(/\r\n?/g, '\n') - .replace(/\\ *\n/g, '\r') - .replace(/([,(:](?!\/\/[^ ])) *(?:\/\/[^\n]*|\/\*.*?\*\/)?\n\s*/g, comment) - .replace(/\s*\n[ \t]*([,)])/g, comment); -}; + return inComment + ? str + : ((val === ',' && /^[,\t\n]+$/.test(str)) ? str.replace(/\n/, '\r') : val + '\r'); + }; -/** - * Lexer prototype. - */ + // Remove UTF-8 BOM. + if ('\uFEFF' == str.charAt(0)) str = str.slice(1); -Lexer.prototype = { + this.str = str + .replace(/\s+$/, '\n') + .replace(/\r\n?/g, '\n') + .replace(/\\ *\n/g, '\r') + .replace(/([,(:](?!\/\/[^ ])) *(?:\/\/[^\n]*|\/\*.*?\*\/)?\n\s*/g, comment) + .replace(/\s*\n[ \t]*([,)])/g, comment); + }; /** - * Custom inspect. - */ + * Custom inspect. + */ - inspect: function(){ + inspect() { var tok , tmp = this.str , buf = []; @@ -104,7 +93,7 @@ Lexer.prototype = { } this.str = tmp; return buf.concat(tok.inspect()).join('\n'); - }, + } /** * Lookahead `n` tokens. @@ -114,11 +103,11 @@ Lexer.prototype = { * @api private */ - lookahead: function(n){ + lookahead(n) { var fetch = n - this.stash.length; while (fetch-- > 0) this.stash.push(this.advance()); return this.stash[--n]; - }, + } /** * Consume the given `len`. @@ -127,7 +116,7 @@ Lexer.prototype = { * @api private */ - skip: function(len){ + skip(len) { var chunk = len[0]; len = chunk ? chunk.length : len; this.str = this.str.substr(len); @@ -136,7 +125,7 @@ Lexer.prototype = { } else { this.column += len; } - }, + } /** * Move current line and column position. @@ -145,7 +134,7 @@ Lexer.prototype = { * @api private */ - move: function(str){ + move(str) { var lines = str.match(/\n/g) , idx = str.lastIndexOf('\n'); @@ -153,7 +142,7 @@ Lexer.prototype = { this.column = ~idx ? str.length - idx : this.column + str.length; - }, + } /** * Fetch next token including those stashed by peek. @@ -162,11 +151,11 @@ Lexer.prototype = { * @api private */ - next: function() { + next() { var tok = this.stashed() || this.advance(); this.prev = tok; return tok; - }, + } /** * Check if the current token is a part of selector. @@ -175,7 +164,7 @@ Lexer.prototype = { * @api private */ - isPartOfSelector: function() { + isPartOfSelector() { var tok = this.stash[this.stash.length - 1] || this.prev; switch (tok && tok.type) { // #for @@ -188,7 +177,7 @@ Lexer.prototype = { return true; } return false; - }, + } /** * Fetch next token. @@ -197,50 +186,50 @@ Lexer.prototype = { * @api private */ - advance: function() { + advance() { var column = this.column , line = this.lineno , tok = this.eos() - || this.null() - || this.sep() - || this.keyword() - || this.urlchars() - || this.comment() - || this.newline() - || this.escaped() - || this.important() - || this.literal() - || this.anonFunc() - || this.atrule() - || this.function() - || this.brace() - || this.paren() - || this.color() - || this.string() - || this.unit() - || this.namedop() - || this.boolean() - || this.unicode() - || this.ident() - || this.op() - || (function () { - var token = this.eol(); - - if (token) { - column = token.column; - line = token.lineno; - } - - return token; - }).call(this) - || this.space() - || this.selector(); + || this.null() + || this.sep() + || this.keyword() + || this.urlchars() + || this.comment() + || this.newline() + || this.escaped() + || this.important() + || this.literal() + || this.anonFunc() + || this.atrule() + || this.function() + || this.brace() + || this.paren() + || this.color() + || this.string() + || this.unit() + || this.namedop() + || this.boolean() + || this.unicode() + || this.ident() + || this.op() + || (function () { + var token = this.eol(); + + if (token) { + column = token.column; + line = token.lineno; + } + + return token; + }).call(this) + || this.space() + || this.selector(); tok.lineno = line; tok.column = column; return tok; - }, + } /** * Lookahead a single token. @@ -249,9 +238,9 @@ Lexer.prototype = { * @api private */ - peek: function() { + peek() { return this.lookahead(1); - }, + } /** * Return the next possibly stashed token. @@ -260,15 +249,15 @@ Lexer.prototype = { * @api private */ - stashed: function() { + stashed() { return this.stash.shift(); - }, + } /** * EOS | trailing outdents. */ - eos: function() { + eos() { if (this.str.length) return; if (this.indentStack.length) { this.indentStack.shift(); @@ -276,79 +265,79 @@ Lexer.prototype = { } else { return new Token('eos'); } - }, + } /** * url char */ - urlchars: function() { + urlchars() { var captures; if (!this.isURL) return; if (captures = /^[\/:@.;?&=*!,<>#%0-9]+/.exec(this.str)) { this.skip(captures); return new Token('literal', new nodes.Literal(captures[0])); } - }, + } /** * ';' [ \t]* */ - sep: function() { + sep() { var captures; if (captures = /^;[ \t]*/.exec(this.str)) { this.skip(captures); return new Token(';'); } - }, + } /** * '\r' */ - eol: function() { + eol() { if ('\r' == this.str[0]) { ++this.lineno; this.skip(1); this.column = 1; - while(this.space()); + while (this.space()); return this.advance(); } - }, + } /** * ' '+ */ - space: function() { + space() { var captures; if (captures = /^([ \t]+)/.exec(this.str)) { this.skip(captures); return new Token('space'); } - }, + } /** * '\\' . ' '* */ - escaped: function() { + escaped() { var captures; if (captures = /^\\(.)[ \t]*/.exec(this.str)) { var c = captures[1]; this.skip(captures); return new Token('ident', new nodes.Literal(c)); } - }, + } /** * '@css' ' '* '{' .* '}' ' '* */ - literal: function() { + literal() { // HACK attack !!! var captures; if (captures = /^@css[ \t]*\{/.exec(this.str)) { @@ -375,38 +364,38 @@ Lexer.prototype = { node.css = true; return new Token('literal', node); } - }, + } /** * '!important' ' '* */ - important: function() { + important() { var captures; if (captures = /^!important[ \t]*/.exec(this.str)) { this.skip(captures); return new Token('ident', new nodes.Literal('!important')); } - }, + } /** * '{' | '}' */ - brace: function() { + brace() { var captures; if (captures = /^([{}])/.exec(this.str)) { this.skip(1); var brace = captures[1]; return new Token(brace, brace); } - }, + } /** * '(' | ')' ' '* */ - paren: function() { + paren() { var captures; if (captures = /^([()])([ \t]*)/.exec(this.str)) { var paren = captures[1]; @@ -416,13 +405,13 @@ Lexer.prototype = { tok.space = captures[2]; return tok; } - }, + } /** * 'null' */ - null: function() { + null() { var captures , tok; if (captures = /^(null)\b[ \t]*/.exec(this.str)) { @@ -434,7 +423,7 @@ Lexer.prototype = { } return tok; } - }, + } /** * 'if' @@ -445,7 +434,7 @@ Lexer.prototype = { * | 'in' */ - keyword: function() { + keyword() { var captures , tok; if (captures = /^(return|if|else|unless|for|in)\b(?!-)[ \t]*/.exec(this.str)) { @@ -458,7 +447,7 @@ Lexer.prototype = { } return tok; } - }, + } /** * 'not' @@ -471,7 +460,7 @@ Lexer.prototype = { * | 'is defined' */ - namedop: function() { + namedop() { var captures , tok; if (captures = /^(not|and|or|is a|is defined|isnt|is not|is)(?!-)\b([ \t]*)/.exec(this.str)) { @@ -486,7 +475,7 @@ Lexer.prototype = { tok.space = captures[2]; return tok; } - }, + } /** * ',' @@ -525,7 +514,7 @@ Lexer.prototype = { * | '...' */ - op: function() { + op() { var captures; if (captures = /^([.]{1,3}|&&|\|\||[!<>=?:]=|\*\*|[-+*\/%]=?|[,=?:!~<>&\[\]])([ \t]*)/.exec(this.str)) { var op = captures[1]; @@ -536,13 +525,13 @@ Lexer.prototype = { this.isURL = false; return tok; } - }, + } /** * '@(' */ - anonFunc: function() { + anonFunc() { var tok; if ('@' == this.str[0] && '(' == this.str[1]) { this.skip(2); @@ -550,13 +539,13 @@ Lexer.prototype = { tok.anonymous = true; return tok; } - }, + } /** * '@' (-(\w+)-)?[a-zA-Z0-9-_]+ */ - atrule: function() { + atrule() { var captures; if (captures = /^@(?!apply)(?:-(\w+)-)?([a-zA-Z0-9-_]+)[ \t]*/.exec(this.str)) { this.skip(captures); @@ -585,13 +574,13 @@ Lexer.prototype = { return new Token('atrule', (vendor ? '-' + vendor + '-' + type : type)); } } - }, + } /** * '//' * */ - comment: function() { + comment() { // Single line if ('/' == this.str[0] && '/' == this.str[1]) { var end = this.str.indexOf('\n'); @@ -618,13 +607,13 @@ Lexer.prototype = { if (this.prev && ';' == this.prev.type) inline = true; return new Token('comment', new nodes.Comment(str, suppress, inline)); } - }, + } /** * 'true' | 'false' */ - boolean: function() { + boolean() { var captures; if (captures = /^(true|false)\b([ \t]*)/.exec(this.str)) { var val = new nodes.Boolean('true' == captures[1]); @@ -633,25 +622,25 @@ Lexer.prototype = { tok.space = captures[2]; return tok; } - }, + } /** * 'U+' [0-9A-Fa-f?]{1,6}(?:-[0-9A-Fa-f]{1,6})? */ - unicode: function() { + unicode() { var captures; if (captures = /^u\+[0-9a-f?]{1,6}(?:-[0-9a-f]{1,6})?/i.exec(this.str)) { this.skip(captures); return new Token('literal', new nodes.Literal(captures[0])); } - }, + } /** * -*[_a-zA-Z$] [-\w\d$]* '(' */ - function: function() { + function() { var captures; if (captures = /^(-*[_a-zA-Z$][-\w\d$]*)\(([ \t]*)/.exec(this.str)) { var name = captures[1]; @@ -661,31 +650,31 @@ Lexer.prototype = { tok.space = captures[2]; return tok; } - }, + } /** * -*[_a-zA-Z$] [-\w\d$]* */ - ident: function() { + ident() { var captures; if (captures = /^-*([_a-zA-Z$]|@apply)[-\w\d$]*/.exec(this.str)) { this.skip(captures); return new Token('ident', new nodes.Ident(captures[0])); } - }, + } /** * '\n' ' '+ */ - newline: function() { + newline() { var captures, re; // we have established the indentation regexp - if (this.indentRe){ + if (this.indentRe) { captures = this.indentRe.exec(this.str); - // figure out if we are using tabs or spaces + // figure out if we are using tabs or spaces } else { // try tabs re = /^\n([\t]*)[ \t]*/; @@ -721,24 +710,24 @@ Lexer.prototype = { this.indentStack.shift(); } tok = this.stash.pop(); - // Indent + // Indent } else if (indents && indents != this.indentStack[0]) { this.indentStack.unshift(indents); tok = new Token('indent'); - // Newline + // Newline } else { tok = new Token('newline'); } return tok; } - }, + } /** * '-'? (digit+ | digit* '.' digit+) unit */ - unit: function() { + unit() { var captures; if (captures = /^(-)?(\d+\.\d+|\d+|\.\d+)(%|[a-zA-Z]+)?[ \t]*/.exec(this.str)) { this.skip(captures); @@ -748,41 +737,41 @@ Lexer.prototype = { node.raw = captures[0]; return new Token('unit', node); } - }, + } /** * '"' [^"]+ '"' | "'"" [^']+ "'" */ - string: function() { + string() { var captures; if (captures = /^("[^"]*"|'[^']*')[ \t]*/.exec(this.str)) { var str = captures[1] , quote = captures[0][0]; this.skip(captures); - str = str.slice(1,-1).replace(/\\n/g, '\n'); + str = str.slice(1, -1).replace(/\\n/g, '\n'); return new Token('string', new nodes.String(str, quote)); } - }, + } /** * #rrggbbaa | #rrggbb | #rgba | #rgb | #nn | #n */ - color: function() { + color() { return this.rrggbbaa() || this.rrggbb() || this.rgba() || this.rgb() || this.nn() || this.n() - }, + } /** * #n */ - n: function() { + n() { var captures; if (captures = /^#([a-fA-F0-9]{1})[ \t]*/.exec(this.str)) { this.skip(captures); @@ -791,13 +780,13 @@ Lexer.prototype = { color.raw = captures[0]; return new Token('color', color); } - }, + } /** * #nn */ - nn: function() { + nn() { var captures; if (captures = /^#([a-fA-F0-9]{2})[ \t]*/.exec(this.str)) { this.skip(captures); @@ -806,13 +795,13 @@ Lexer.prototype = { color.raw = captures[0]; return new Token('color', color); } - }, + } /** * #rgb */ - rgb: function() { + rgb() { var captures; if (captures = /^#([a-fA-F0-9]{3})[ \t]*/.exec(this.str)) { this.skip(captures); @@ -824,13 +813,13 @@ Lexer.prototype = { color.raw = captures[0]; return new Token('color', color); } - }, + } /** * #rgba */ - rgba: function() { + rgba() { var captures; if (captures = /^#([a-fA-F0-9]{4})[ \t]*/.exec(this.str)) { this.skip(captures); @@ -839,17 +828,17 @@ Lexer.prototype = { , g = parseInt(rgb[1] + rgb[1], 16) , b = parseInt(rgb[2] + rgb[2], 16) , a = parseInt(rgb[3] + rgb[3], 16) - , color = new nodes.RGBA(r, g, b, a/255); + , color = new nodes.RGBA(r, g, b, a / 255); color.raw = captures[0]; return new Token('color', color); } - }, + } /** * #rrggbb */ - rrggbb: function() { + rrggbb() { var captures; if (captures = /^#([a-fA-F0-9]{6})[ \t]*/.exec(this.str)) { this.skip(captures); @@ -861,13 +850,13 @@ Lexer.prototype = { color.raw = captures[0]; return new Token('color', color); } - }, + } /** * #rrggbbaa */ - rrggbbaa: function() { + rrggbbaa() { var captures; if (captures = /^#([a-fA-F0-9]{8})[ \t]*/.exec(this.str)) { this.skip(captures); @@ -876,17 +865,17 @@ Lexer.prototype = { , g = parseInt(rgb.substr(2, 2), 16) , b = parseInt(rgb.substr(4, 2), 16) , a = parseInt(rgb.substr(6, 2), 16) - , color = new nodes.RGBA(r, g, b, a/255); + , color = new nodes.RGBA(r, g, b, a / 255); color.raw = captures[0]; return new Token('color', color); } - }, + } /** * ^|[^\n,;]+ */ - selector: function() { + selector() { var captures; if (captures = /^\^|.*?(?=\/\/(?![^\[]*\])|[,\n{])/.exec(this.str)) { var selector = captures[0]; From 3b7a165b8baa63688b4f7e4ce09a42a2f96318bc Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:01:20 +0200 Subject: [PATCH 53/68] use class in selector parser --- lib/selector-parser.js | 438 +++++++++++++++++++++-------------------- 1 file changed, 220 insertions(+), 218 deletions(-) diff --git a/lib/selector-parser.js b/lib/selector-parser.js index f5a64ff9d..6fd352416 100644 --- a/lib/selector-parser.js +++ b/lib/selector-parser.js @@ -6,253 +6,255 @@ var COMBINATORS = ['>', '+', '~']; -/** - * Initialize a new `SelectorParser` - * with the given `str` and selectors `stack`. - * - * @param {String} str - * @param {Array} stack - * @param {Array} parts - * @api private - */ - -var SelectorParser = module.exports = function SelectorParser(str, stack, parts) { - this.str = str; - this.stack = stack || []; - this.parts = parts || []; - this.pos = 0; - this.level = 2; - this.nested = true; - this.ignore = false; -}; - -/** - * Consume the given `len` and move current position. - * - * @param {Number} len - * @api private - */ - -SelectorParser.prototype.skip = function(len) { - this.str = this.str.substr(len); - this.pos += len; -}; - -/** - * Consume spaces. - */ - -SelectorParser.prototype.skipSpaces = function() { - while (' ' == this.str[0]) this.skip(1); -}; +module.exports = class SelectorParser { + /** + * Initialize a new `SelectorParser` + * with the given `str` and selectors `stack`. + * + * @param {String} str + * @param {Array} stack + * @param {Array} parts + * @api private + */ + + constructor(str, stack, parts) { + this.str = str; + this.stack = stack || []; + this.parts = parts || []; + this.pos = 0; + this.level = 2; + this.nested = true; + this.ignore = false; + } -/** - * Fetch next token. - * - * @return {String} - * @api private - */ + /** + * Consume the given `len` and move current position. + * + * @param {Number} len + * @api private + */ + + skip(len) { + this.str = this.str.substr(len); + this.pos += len; + }; + + /** + * Consume spaces. + */ + + skipSpaces() { + while (' ' == this.str[0]) this.skip(1); + }; + + /** + * Fetch next token. + * + * @return {String} + * @api private + */ + + advance() { + return this.root() + || this.relative() + || this.initial() + || this.escaped() + || this.parent() + || this.partial() + || this.char(); + }; + + /** + * '/' + */ + + root() { + if (!this.pos && '/' == this.str[0] + && 'deep' != this.str.slice(1, 5)) { + this.nested = false; + this.skip(1); + } + }; + + /** + * '../' + */ + + relative(multi) { + if ((!this.pos || multi) && '../' == this.str.slice(0, 3)) { + this.nested = false; + this.skip(3); + while (this.relative(true)) this.level++; + if (!this.raw) { + var ret = this.stack[this.stack.length - this.level]; + if (ret) { + return ret; + } else { + this.ignore = true; + } + } + } + }; -SelectorParser.prototype.advance = function() { - return this.root() - || this.relative() - || this.initial() - || this.escaped() - || this.parent() - || this.partial() - || this.char(); -}; + /** + * '~/' + */ -/** - * '/' - */ + initial() { + if (!this.pos && '~' == this.str[0] && '/' == this.str[1]) { + this.nested = false; + this.skip(2); + return this.stack[0]; + } + }; + + /** + * '\' ('&' | '^') + */ + + escaped() { + if ('\\' == this.str[0]) { + var char = this.str[1]; + if ('&' == char || '^' == char) { + this.skip(2); + return char; + } + } + }; + + /** + * '&' + */ + + parent() { + if ('&' == this.str[0]) { + this.nested = false; + + if (!this.pos && (!this.stack.length || this.raw)) { + var i = 0; + while (' ' == this.str[++i]); + if (~COMBINATORS.indexOf(this.str[i])) { + this.skip(i + 1); + return; + } + } -SelectorParser.prototype.root = function() { - if (!this.pos && '/' == this.str[0] - && 'deep' != this.str.slice(1, 5)) { - this.nested = false; - this.skip(1); - } -}; + this.skip(1); + if (!this.raw) + return this.stack[this.stack.length - 1]; + } + }; -/** - * '../' - */ + /** + * '^[' range ']' + */ -SelectorParser.prototype.relative = function(multi) { - if ((!this.pos || multi) && '../' == this.str.slice(0, 3)) { - this.nested = false; - this.skip(3); - while (this.relative(true)) this.level++; - if (!this.raw) { - var ret = this.stack[this.stack.length - this.level]; + partial() { + if ('^' == this.str[0] && '[' == this.str[1]) { + this.skip(2); + this.skipSpaces(); + var ret = this.range(); + this.skipSpaces(); + if (']' != this.str[0]) return '^['; + this.nested = false; + this.skip(1); if (ret) { return ret; } else { this.ignore = true; } } - } -}; + }; -/** - * '~/' - */ + /** + * '-'? 0-9+ + */ -SelectorParser.prototype.initial = function() { - if (!this.pos && '~' == this.str[0] && '/' == this.str[1]) { - this.nested = false; - this.skip(2); - return this.stack[0]; - } -}; + number() { + var i = 0, ret = ''; + if ('-' == this.str[i]) + ret += this.str[i++]; -/** - * '\' ('&' | '^') - */ + while (this.str.charCodeAt(i) >= 48 + && this.str.charCodeAt(i) <= 57) + ret += this.str[i++]; -SelectorParser.prototype.escaped = function() { - if ('\\' == this.str[0]) { - var char = this.str[1]; - if ('&' == char || '^' == char) { - this.skip(2); - return char; + if (ret) { + this.skip(i); + return Number(ret); } - } -}; + }; -/** - * '&' - */ + /** + * number ('..' number)? + */ -SelectorParser.prototype.parent = function() { - if ('&' == this.str[0]) { - this.nested = false; + range() { + var start = this.number() + , ret; - if (!this.pos && (!this.stack.length || this.raw)) { - var i = 0; - while (' ' == this.str[++i]) ; - if (~COMBINATORS.indexOf(this.str[i])) { - this.skip(i + 1); - return; - } - } + if ('..' == this.str.slice(0, 2)) { + this.skip(2); + var end = this.number() + , len = this.parts.length; - this.skip(1); - if (!this.raw) - return this.stack[this.stack.length - 1]; - } -}; + if (start < 0) start = len + start - 1; + if (end < 0) end = len + end - 1; -/** - * '^[' range ']' - */ + if (start > end) { + var tmp = start; + start = end; + end = tmp; + } + + if (end < len - 1) { + ret = this.parts.slice(start, end + 1).map(function (part) { + var selector = new SelectorParser(part, this.stack, this.parts); + selector.raw = true; + return selector.parse(); + }, this).map(function (selector) { + return (selector.nested ? ' ' : '') + selector.val; + }).join('').trim(); + } + } else { + ret = this.stack[ + start < 0 ? this.stack.length + start - 1 : start + ]; + } -SelectorParser.prototype.partial = function() { - if ('^' == this.str[0] && '[' == this.str[1]) { - this.skip(2); - this.skipSpaces(); - var ret = this.range(); - this.skipSpaces(); - if (']' != this.str[0]) return '^['; - this.nested = false; - this.skip(1); if (ret) { return ret; } else { this.ignore = true; } - } -}; - -/** - * '-'? 0-9+ - */ - -SelectorParser.prototype.number = function() { - var i = 0, ret = ''; - if ('-' == this.str[i]) - ret += this.str[i++]; + }; - while (this.str.charCodeAt(i) >= 48 - && this.str.charCodeAt(i) <= 57) - ret += this.str[i++]; - - if (ret) { - this.skip(i); - return Number(ret); - } -}; - -/** - * number ('..' number)? - */ + /** + * .+ + */ -SelectorParser.prototype.range = function() { - var start = this.number() - , ret; - - if ('..' == this.str.slice(0, 2)) { - this.skip(2); - var end = this.number() - , len = this.parts.length; - - if (start < 0) start = len + start - 1; - if (end < 0) end = len + end - 1; - - if (start > end) { - var tmp = start; - start = end; - end = tmp; - } - - if (end < len - 1) { - ret = this.parts.slice(start, end + 1).map(function(part) { - var selector = new SelectorParser(part, this.stack, this.parts); - selector.raw = true; - return selector.parse(); - }, this).map(function(selector) { - return (selector.nested ? ' ' : '') + selector.val; - }).join('').trim(); - } - } else { - ret = this.stack[ - start < 0 ? this.stack.length + start - 1 : start - ]; - } - - if (ret) { - return ret; - } else { - this.ignore = true; - } -}; - -/** - * .+ - */ - -SelectorParser.prototype.char = function() { - var char = this.str[0]; - this.skip(1); - return char; -}; - -/** - * Parses the selector. - * - * @return {Object} - * @api private - */ - -SelectorParser.prototype.parse = function() { - var val = ''; - while (this.str.length) { - val += this.advance() || ''; - if (this.ignore) { - val = ''; - break; + char() { + var char = this.str[0]; + this.skip(1); + return char; + }; + + /** + * Parses the selector. + * + * @return {Object} + * @api private + */ + + parse() { + var val = ''; + while (this.str.length) { + val += this.advance() || ''; + if (this.ignore) { + val = ''; + break; + } } - } - return { val: val.trimRight(), nested: this.nested }; + return { val: val.trimRight(), nested: this.nested }; + }; }; From e05efc4c70e9a9de313ec19459d00a87c2add618 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:03:05 +0200 Subject: [PATCH 54/68] use classes in Converter --- lib/convert/css.js | 603 +++++++++++++++++++++++---------------------- 1 file changed, 303 insertions(+), 300 deletions(-) diff --git a/lib/convert/css.js b/lib/convert/css.js index 20b0f9a79..414686ee7 100644 --- a/lib/convert/css.js +++ b/lib/convert/css.js @@ -12,7 +12,7 @@ * @api public */ -module.exports = function(css){ +module.exports = function (css) { return new Converter(css).stylus(); }; @@ -23,306 +23,309 @@ module.exports = function(css){ * @api private */ -function Converter(css) { - var { parse } = require('@adobe/css-tools'); - this.css = css; - this.root = parse(css, { position: false }); - this.indents = 0; -} - -/** - * Convert to Stylus. - * - * @return {String} - * @api private - */ - -Converter.prototype.stylus = function(){ - return this.visitRules(this.root.stylesheet.rules); -}; - -/** - * Return indent string. - * - * @return {String} - * @api private - */ - -Converter.prototype.__defineGetter__('indent', function(){ - return Array(this.indents + 1).join(' '); -}); - -/** - * Visit `node`. - * - * @param {*} node - * @return {String} - * @api private - */ - -Converter.prototype.visit = function(node){ - switch (node.type) { - case 'rule': - case 'comment': - case 'charset': - case 'namespace': - case 'media': - case 'import': - case 'document': - case 'keyframes': - case 'page': - case 'host': - case 'supports': - var name = node.type[0].toUpperCase() + node.type.slice(1); - return this['visit' + name](node); - case 'font-face': - return this.visitFontFace(node); +class Converter { + constructor(css) { + var { parse } = require('@adobe/css-tools'); + this.css = css; + this.root = parse(css, { position: false }); + this.indents = 0; } -}; - -/** - * Visit the rules on `node`. - * - * @param {Array} node - * @return {String} - * @api private - */ - -Converter.prototype.visitRules = function(node){ - var buf = ''; - for (var i = 0, len = node.length; i < len; ++i) { - buf += this.visit(node[i]); - } - return buf; -}; - -/** - * Visit FontFace `node`. - * - * @param {FontFace} node - * @return {String} - * @api private - */ - Converter.prototype.visitFontFace = function(node){ - var buf = this.indent + '@font-face'; - buf += '\n'; - ++this.indents; - for (var i = 0, len = node.declarations.length; i < len; ++i) { - buf += this.visitDeclaration(node.declarations[i]); - } - --this.indents; - return buf; - }; -/** - * Visit Media `node`. - * - * @param {Media} node - * @return {String} - * @api private - */ - -Converter.prototype.visitMedia = function(node){ - var buf = this.indent + '@media ' + node.media; - buf += '\n'; - ++this.indents; - buf += this.visitRules(node.rules); - --this.indents; - return buf; -}; - -/** - * Visit Declaration `node`. - * - * @param {Declaration} node - * @return {String} - * @api private - */ - -Converter.prototype.visitDeclaration = function(node){ - if ('comment' == node.type) { - return this.visitComment(node); - } else { - var buf = this.indent + node.property + ': ' + node.value + '\n'; + /** + * Convert to Stylus. + * + * @return {String} + * @api private + */ + + stylus() { + return this.visitRules(this.root.stylesheet.rules); + }; + + /** + * Return indent string. + * + * @return {String} + * @api private + */ + + get indent() { + return Array(this.indents + 1).join(' '); + }; + + /** + * Visit `node`. + * + * @param {*} node + * @return {String} + * @api private + */ + + visit(node) { + switch (node.type) { + case 'rule': + case 'comment': + case 'charset': + case 'namespace': + case 'media': + case 'import': + case 'document': + case 'keyframes': + case 'page': + case 'host': + case 'supports': + var name = node.type[0].toUpperCase() + node.type.slice(1); + return this['visit' + name](node); + case 'font-face': + return this.visitFontFace(node); + } + }; + + /** + * Visit the rules on `node`. + * + * @param {Array} node + * @return {String} + * @api private + */ + + visitRules(node) { + var buf = ''; + for (var i = 0, len = node.length; i < len; ++i) { + buf += this.visit(node[i]); + } return buf; - } -}; - -/** - * Visit Rule `node`.` - * - * @param {Rule} node - * @return {String} - * @api private - */ - -Converter.prototype.visitRule = function(node){ - var buf = this.indent + node.selectors.join(',\n' + this.indent) + '\n'; - ++this.indents; - for (var i = 0, len = node.declarations.length; i < len; ++i) { - buf += this.visitDeclaration(node.declarations[i]); - } - --this.indents; - return buf + '\n'; -}; - -/** - * Visit Comment `node`.` - * - * @param {Comment} node - * @return {String} - * @api private - */ - -Converter.prototype.visitComment = function(node){ - var buf = this.indent + '/*' + node.comment + '*/'; - return buf + '\n'; -}; - -/** - * Visit Charset `node`.` - * - * @param {Charset} node - * @return {String} - * @api private - */ - -Converter.prototype.visitCharset = function(node){ - var buf = this.indent + '@charset ' + node.charset; - return buf + '\n'; -}; - -/** - * Visit Namespace `node`.` - * - * @param {Namespace} node - * @return {String} - * @api private - */ - -Converter.prototype.visitNamespace = function(node){ - var buf = this.indent + '@namespace ' + node.namespace; - return buf + '\n'; -}; - -/** - * Visit Import `node`.` - * - * @param {Import} node - * @return {String} - * @api private - */ - -Converter.prototype.visitImport = function(node){ - var buf = this.indent + '@import ' + node.import; - return buf + '\n'; -}; - -/** - * Visit Document `node`.` - * - * @param {Document} node - * @return {String} - * @api private - */ - -Converter.prototype.visitDocument = function(node){ - var buf = this.indent + '@' + node.vendor + 'document ' + node.document; - buf += '\n'; - ++this.indents; - buf += this.visitRules(node.rules); - --this.indents; - return buf; -}; - -/** - * Visit Keyframes `node`.` - * - * @param {Keyframes} node - * @return {String} - * @api private - */ - -Converter.prototype.visitKeyframes = function(node){ - var buf = this.indent + '@keyframes ' + node.name; - buf += '\n'; - ++this.indents; - for (var i = 0, len = node.keyframes.length; i < len; ++i) { - buf += this.visitKeyframe(node.keyframes[i]); - } - --this.indents; - return buf; -}; - -/** - * Visit Keyframe `node`.` - * - * @param {Keyframe} node - * @return {String} - * @api private - */ - -Converter.prototype.visitKeyframe = function(node){ - var buf = this.indent + node.values.join(', '); - buf += '\n'; - ++this.indents; - for (var i = 0, len = node.declarations.length; i < len; ++i) { - buf += this.visitDeclaration(node.declarations[i]); - } - --this.indents; - return buf; -}; - -/** - * Visit Page `node`.` - * - * @param {Page} node - * @return {String} - * @api private - */ - -Converter.prototype.visitPage = function(node){ - var buf = this.indent + '@page' + (node.selectors.length ? ' ' + node.selectors.join(', ') : ''); - buf += '\n'; - ++this.indents; - for (var i = 0, len = node.declarations.length; i < len; ++i) { - buf += this.visitDeclaration(node.declarations[i]); - } - --this.indents; - return buf; -}; - -/** - * Visit Supports `node`.` - * - * @param {Supports} node - * @return {String} - * @api private - */ - -Converter.prototype.visitSupports = function(node){ - var buf = this.indent + '@supports ' + node.supports; - buf += '\n'; - ++this.indents; - buf += this.visitRules(node.rules); - --this.indents; - return buf; -}; - -/** - * Visit Host `node`.` - * - * @param {Host} node - * @return {String} - * @api private - */ - -Converter.prototype.visitHost = function(node){ - var buf = this.indent + '@host'; - buf += '\n'; - ++this.indents; - buf += this.visitRules(node.rules); - --this.indents; - return buf; -}; + }; + + /** + * Visit FontFace `node`. + * + * @param {FontFace} node + * @return {String} + * @api private + */ + + visitFontFace(node) { + var buf = this.indent + '@font-face'; + buf += '\n'; + ++this.indents; + for (var i = 0, len = node.declarations.length; i < len; ++i) { + buf += this.visitDeclaration(node.declarations[i]); + } + --this.indents; + return buf; + }; + + /** + * Visit Media `node`. + * + * @param {Media} node + * @return {String} + * @api private + */ + + visitMedia(node) { + var buf = this.indent + '@media ' + node.media; + buf += '\n'; + ++this.indents; + buf += this.visitRules(node.rules); + --this.indents; + return buf; + }; + + /** + * Visit Declaration `node`. + * + * @param {Declaration} node + * @return {String} + * @api private + */ + + visitDeclaration(node) { + if ('comment' == node.type) { + return this.visitComment(node); + } else { + var buf = this.indent + node.property + ': ' + node.value + '\n'; + return buf; + } + }; + + /** + * Visit Rule `node`.` + * + * @param {Rule} node + * @return {String} + * @api private + */ + + visitRule(node) { + var buf = this.indent + node.selectors.join(',\n' + this.indent) + '\n'; + ++this.indents; + for (var i = 0, len = node.declarations.length; i < len; ++i) { + buf += this.visitDeclaration(node.declarations[i]); + } + --this.indents; + return buf + '\n'; + }; + + /** + * Visit Comment `node`.` + * + * @param {Comment} node + * @return {String} + * @api private + */ + + visitComment(node) { + var buf = this.indent + '/*' + node.comment + '*/'; + return buf + '\n'; + }; + + /** + * Visit Charset `node`.` + * + * @param {Charset} node + * @return {String} + * @api private + */ + + visitCharset(node) { + var buf = this.indent + '@charset ' + node.charset; + return buf + '\n'; + }; + + /** + * Visit Namespace `node`.` + * + * @param {Namespace} node + * @return {String} + * @api private + */ + + visitNamespace(node) { + var buf = this.indent + '@namespace ' + node.namespace; + return buf + '\n'; + }; + + /** + * Visit Import `node`.` + * + * @param {Import} node + * @return {String} + * @api private + */ + + visitImport(node) { + var buf = this.indent + '@import ' + node.import; + return buf + '\n'; + }; + + /** + * Visit Document `node`.` + * + * @param {Document} node + * @return {String} + * @api private + */ + + visitDocument(node) { + var buf = this.indent + '@' + node.vendor + 'document ' + node.document; + buf += '\n'; + ++this.indents; + buf += this.visitRules(node.rules); + --this.indents; + return buf; + }; + + /** + * Visit Keyframes `node`.` + * + * @param {Keyframes} node + * @return {String} + * @api private + */ + + visitKeyframes(node) { + var buf = this.indent + '@keyframes ' + node.name; + buf += '\n'; + ++this.indents; + for (var i = 0, len = node.keyframes.length; i < len; ++i) { + buf += this.visitKeyframe(node.keyframes[i]); + } + --this.indents; + return buf; + }; + + /** + * Visit Keyframe `node`.` + * + * @param {Keyframe} node + * @return {String} + * @api private + */ + + visitKeyframe(node) { + var buf = this.indent + node.values.join(', '); + buf += '\n'; + ++this.indents; + for (var i = 0, len = node.declarations.length; i < len; ++i) { + buf += this.visitDeclaration(node.declarations[i]); + } + --this.indents; + return buf; + }; + + /** + * Visit Page `node`.` + * + * @param {Page} node + * @return {String} + * @api private + */ + + visitPage(node) { + var buf = this.indent + '@page' + (node.selectors.length ? ' ' + node.selectors.join(', ') : ''); + buf += '\n'; + ++this.indents; + for (var i = 0, len = node.declarations.length; i < len; ++i) { + buf += this.visitDeclaration(node.declarations[i]); + } + --this.indents; + return buf; + }; + + /** + * Visit Supports `node`.` + * + * @param {Supports} node + * @return {String} + * @api private + */ + + visitSupports(node) { + var buf = this.indent + '@supports ' + node.supports; + buf += '\n'; + ++this.indents; + buf += this.visitRules(node.rules); + --this.indents; + return buf; + }; + + /** + * Visit Host `node`.` + * + * @param {Host} node + * @return {String} + * @api private + */ + + visitHost(node) { + var buf = this.indent + '@host'; + buf += '\n'; + ++this.indents; + buf += this.visitRules(node.rules); + --this.indents; + return buf; + }; +} From 09c5e92a83347e7a5ab3db853dca8cc434438843 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:04:15 +0200 Subject: [PATCH 55/68] use classes in MemoryCache --- lib/cache/memory.js | 212 ++++++++++++++++++++++---------------------- 1 file changed, 107 insertions(+), 105 deletions(-) diff --git a/lib/cache/memory.js b/lib/cache/memory.js index bc80c4294..a61d36a52 100644 --- a/lib/cache/memory.js +++ b/lib/cache/memory.js @@ -5,112 +5,114 @@ var crypto = require('crypto') , nodes = require('../nodes'); -var MemoryCache = module.exports = function(options) { - options = options || {}; - this.limit = options['cache limit'] || 256; - this._cache = {}; - this.length = 0; - this.head = this.tail = null; -}; - -/** - * Set cache item with given `key` to `value`. - * - * @param {String} key - * @param {Object} value - * @api private - */ - -MemoryCache.prototype.set = function(key, value) { - var clone = value.clone() - , item; - - clone.filename = nodes.filename; - clone.lineno = nodes.lineno; - clone.column = nodes.column; - item = { key: key, value: clone }; - this._cache[key] = item; - - if (this.tail) { - this.tail.next = item; - item.prev = this.tail; - } else { - this.head = item; - } - - this.tail = item; - if (this.length++ == this.limit) this.purge(); -}; - -/** - * Get cache item with given `key`. - * - * @param {String} key - * @return {Object} - * @api private - */ - -MemoryCache.prototype.get = function(key) { - var item = this._cache[key] - , val = item.value.clone(); - - if (item == this.tail) return val; - if (item.next) { - if (item == this.head) this.head = item.next; - item.next.prev = item.prev; +module.exports = class MemoryCache { + constructor(options) { + options = options || {}; + this.limit = options['cache limit'] || 256; + this._cache = {}; + this.length = 0; + this.head = this.tail = null; } - if (item.prev) item.prev.next = item.next; - - item.next = null; - item.prev = this.tail; - - if (this.tail) this.tail.next = item; - this.tail = item; - return val; -}; - -/** - * Check if cache has given `key`. - * - * @param {String} key - * @return {Boolean} - * @api private - */ - -MemoryCache.prototype.has = function(key) { - return !!this._cache[key]; -}; - -/** - * Generate key for the source `str` with `options`. - * - * @param {String} str - * @param {Object} options - * @return {String} - * @api private - */ - -MemoryCache.prototype.key = function(str, options) { - var hash = crypto.createHash('sha1'); - hash.update(str + options.prefix); - return hash.digest('hex'); -}; - -/** - * Remove the oldest item from the cache. - * - * @api private - */ - -MemoryCache.prototype.purge = function() { - var item = this.head; - - if (this.head.next) { - this.head = this.head.next; - this.head.prev = null; - } + /** + * Set cache item with given `key` to `value`. + * + * @param {String} key + * @param {Object} value + * @api private + */ + + set(key, value) { + var clone = value.clone() + , item; + + clone.filename = nodes.filename; + clone.lineno = nodes.lineno; + clone.column = nodes.column; + item = { key: key, value: clone }; + this._cache[key] = item; + + if (this.tail) { + this.tail.next = item; + item.prev = this.tail; + } else { + this.head = item; + } + + this.tail = item; + if (this.length++ == this.limit) this.purge(); + }; + + /** + * Get cache item with given `key`. + * + * @param {String} key + * @return {Object} + * @api private + */ + + get(key) { + var item = this._cache[key] + , val = item.value.clone(); + + if (item == this.tail) return val; + if (item.next) { + if (item == this.head) this.head = item.next; + item.next.prev = item.prev; + } + if (item.prev) item.prev.next = item.next; + + item.next = null; + item.prev = this.tail; - this._cache[item.key] = item.prev = item.next = null; - this.length--; + if (this.tail) this.tail.next = item; + this.tail = item; + + return val; + }; + + /** + * Check if cache has given `key`. + * + * @param {String} key + * @return {Boolean} + * @api private + */ + + has(key) { + return !!this._cache[key]; + }; + + /** + * Generate key for the source `str` with `options`. + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api private + */ + + key(str, options) { + var hash = crypto.createHash('sha1'); + hash.update(str + options.prefix); + return hash.digest('hex'); + }; + + /** + * Remove the oldest item from the cache. + * + * @api private + */ + + purge() { + var item = this.head; + + if (this.head.next) { + this.head = this.head.next; + this.head.prev = null; + } + + this._cache[item.key] = item.prev = item.next = null; + this.length--; + }; }; From f6d594fb30e4f453045e7344b3f2750c935523dd Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:05:30 +0200 Subject: [PATCH 56/68] use classes in FSCache --- lib/cache/fs.js | 122 +++++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 59 deletions(-) diff --git a/lib/cache/fs.js b/lib/cache/fs.js index 910bf9fb5..0894a2dab 100644 --- a/lib/cache/fs.js +++ b/lib/cache/fs.js @@ -8,73 +8,77 @@ var crypto = require('crypto') , version = require('../../package').version , nodes = require('../nodes'); -var FSCache = module.exports = function(options) { - options = options || {}; - this._location = options['cache location'] || '.styl-cache'; - if (!fs.existsSync(this._location)) fs.mkdirSync(this._location); -}; +module.exports = class FSCache { + constructor(options) { + options = options || {}; + this._location = options['cache location'] || '.styl-cache'; + if (!fs.existsSync(this._location)) fs.mkdirSync(this._location); + } -/** - * Set cache item with given `key` to `value`. - * - * @param {String} key - * @param {Object} value - * @api private - */ -FSCache.prototype.set = function(key, value) { - fs.writeFileSync(join(this._location, key), JSON.stringify(value)); -}; + /** + * Set cache item with given `key` to `value`. + * + * @param {String} key + * @param {Object} value + * @api private + */ -/** - * Get cache item with given `key`. - * - * @param {String} key - * @return {Object} - * @api private - */ + set(key, value) { + fs.writeFileSync(join(this._location, key), JSON.stringify(value)); + }; -FSCache.prototype.get = function(key) { - var data = fs.readFileSync(join(this._location, key), 'utf-8'); - return JSON.parse(data, FSCache.fromJSON); -}; + /** + * Get cache item with given `key`. + * + * @param {String} key + * @return {Object} + * @api private + */ -/** - * Check if cache has given `key`. - * - * @param {String} key - * @return {Boolean} - * @api private - */ + get(key) { + var data = fs.readFileSync(join(this._location, key), 'utf-8'); + return JSON.parse(data, FSCache.fromJSON); + }; -FSCache.prototype.has = function(key) { - return fs.existsSync(join(this._location, key)); -}; + /** + * Check if cache has given `key`. + * + * @param {String} key + * @return {Boolean} + * @api private + */ -/** - * Generate key for the source `str` with `options`. - * - * @param {String} str - * @param {Object} options - * @return {String} - * @api private - */ + has(key) { + return fs.existsSync(join(this._location, key)); + }; -FSCache.prototype.key = function(str, options) { - var hash = crypto.createHash('sha1'); - hash.update(str + version + options.prefix); - return hash.digest('hex'); -}; + /** + * Generate key for the source `str` with `options`. + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api private + */ -/** - * JSON to Stylus nodes converter. - * - * @api private - */ + key(str, options) { + var hash = crypto.createHash('sha1'); + hash.update(str + version + options.prefix); + return hash.digest('hex'); + }; + + /** + * JSON to Stylus nodes converter. + * + * @api private + */ + + static fromJSON(key, val) { + if (val && val.__type) { + val.__proto__ = nodes[val.__type].prototype; + } + return val; + }; -FSCache.fromJSON = function(key, val) { - if (val && val.__type) { - val.__proto__ = nodes[val.__type].prototype; - } - return val; }; From c82b727eb79509f9c70a8573728ff5c42210517f Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:06:21 +0200 Subject: [PATCH 57/68] use classes in NullCache --- lib/cache/null.js | 93 ++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/lib/cache/null.js b/lib/cache/null.js index 07f1b0119..d3d30ad5b 100644 --- a/lib/cache/null.js +++ b/lib/cache/null.js @@ -2,49 +2,50 @@ * Module dependencies. */ -var NullCache = module.exports = function() {}; - -/** - * Set cache item with given `key` to `value`. - * - * @param {String} key - * @param {Object} value - * @api private - */ - -NullCache.prototype.set = function(key, value) {}; - -/** - * Get cache item with given `key`. - * - * @param {String} key - * @return {Object} - * @api private - */ - -NullCache.prototype.get = function(key) {}; - -/** - * Check if cache has given `key`. - * - * @param {String} key - * @return {Boolean} - * @api private - */ - -NullCache.prototype.has = function(key) { - return false; -}; - -/** - * Generate key for the source `str` with `options`. - * - * @param {String} str - * @param {Object} options - * @return {String} - * @api private - */ - -NullCache.prototype.key = function(str, options) { - return ''; -}; +module.exports = class NullCache { + + /** + * Set cache item with given `key` to `value`. + * + * @param {String} key + * @param {Object} value + * @api private + */ + + set(key, value) { }; + + /** + * Get cache item with given `key`. + * + * @param {String} key + * @return {Object} + * @api private + */ + + get(key) { }; + + /** + * Check if cache has given `key`. + * + * @param {String} key + * @return {Boolean} + * @api private + */ + + has(key) { + return false; + }; + + /** + * Generate key for the source `str` with `options`. + * + * @param {String} str + * @param {Object} options + * @return {String} + * @api private + */ + + key(str, options) { + return ''; + }; +} \ No newline at end of file From 5157af5851270d7bdf1d9330a04591a4c6d0f9e5 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:07:29 +0200 Subject: [PATCH 58/68] use classes in lib/functions/image --- lib/functions/image.js | 283 +++++++++++++++++++++-------------------- 1 file changed, 143 insertions(+), 140 deletions(-) diff --git a/lib/functions/image.js b/lib/functions/image.js index 5a1f8e117..1c3a07878 100644 --- a/lib/functions/image.js +++ b/lib/functions/image.js @@ -16,147 +16,150 @@ var utils = require('../utils') , path = require('path') , sax = require('sax'); -/** - * Initialize a new `Image` with the given `ctx` and `path. - * - * @param {Evaluator} ctx - * @param {String} path - * @api private - */ - -var Image = module.exports = function Image(ctx, path) { - this.ctx = ctx; - this.path = utils.lookup(path, ctx.paths); - if (!this.path) throw new Error('failed to locate file ' + path); -}; - -/** - * Open the image for reading. - * - * @api private - */ - -Image.prototype.open = function(){ - this.fd = fs.openSync(this.path, 'r'); - this.length = fs.fstatSync(this.fd).size; - this.extname = path.extname(this.path).slice(1); -}; - -/** - * Close the file. - * - * @api private - */ - -Image.prototype.close = function(){ - if (this.fd) fs.closeSync(this.fd); -}; - -/** - * Return the type of image, supports: - * - * - gif - * - png - * - jpeg - * - svg - * - * @return {String} - * @api private - */ - -Image.prototype.type = function(){ - var type - , buf = Buffer.alloc(4); - - fs.readSync(this.fd, buf, 0, 4, 0); - - // GIF - if (0x47 == buf[0] && 0x49 == buf[1] && 0x46 == buf[2]) type = 'gif'; - - // PNG - else if (0x50 == buf[1] && 0x4E == buf[2] && 0x47 == buf[3]) type = 'png'; - - // JPEG - else if (0xff == buf[0] && 0xd8 == buf[1]) type = 'jpeg'; - - // SVG - else if ('svg' == this.extname) type = this.extname; - - return type; -}; - -/** - * Return image dimensions `[width, height]`. - * - * @return {Array} - * @api private - */ - -Image.prototype.size = function(){ - var type = this.type() - , width - , height - , buf - , offset - , blockSize - , parser; - - function uint16(b) { return b[1] << 8 | b[0]; } - function uint32(b) { return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; } - - // Determine dimensions - switch (type) { - case 'jpeg': - buf = Buffer.alloc(this.length); - fs.readSync(this.fd, buf, 0, this.length, 0); - offset = 4; - blockSize = buf[offset] << 8 | buf[offset + 1]; - - while (offset < this.length) { - offset += blockSize; - if (offset >= this.length || 0xff != buf[offset]) break; - // SOF0 or SOF2 (progressive) - if (0xc0 == buf[offset + 1] || 0xc2 == buf[offset + 1]) { - height = buf[offset + 5] << 8 | buf[offset + 6]; - width = buf[offset + 7] << 8 | buf[offset + 8]; - } else { - offset += 2; - blockSize = buf[offset] << 8 | buf[offset + 1]; - } - } - break; - case 'png': - buf = Buffer.alloc(8); - // IHDR chunk width / height uint32_t big-endian - fs.readSync(this.fd, buf, 0, 8, 16); - width = uint32(buf); - height = uint32(buf.slice(4, 8)); - break; - case 'gif': - buf = Buffer.alloc(4); - // width / height uint16_t little-endian - fs.readSync(this.fd, buf, 0, 4, 6); - width = uint16(buf); - height = uint16(buf.slice(2, 4)); - break; - case 'svg': - offset = Math.min(this.length, 1024); - buf = Buffer.alloc(offset); - fs.readSync(this.fd, buf, 0, offset, 0); - buf = buf.toString('utf8'); - parser = sax.parser(true); - parser.onopentag = function(node) { - if ('svg' == node.name && node.attributes.width && node.attributes.height) { - width = parseInt(node.attributes.width, 10); - height = parseInt(node.attributes.height, 10); - } - }; - parser.write(buf).close(); - break; +module.exports = class Image { + /** + * Initialize a new `Image` with the given `ctx` and `path. + * + * @param {Evaluator} ctx + * @param {String} path + * @api private + */ + + constructor(ctx, path) { + this.ctx = ctx; + this.path = utils.lookup(path, ctx.paths); + if (!this.path) throw new Error('failed to locate file ' + path); } - if ('number' != typeof width) throw new Error('failed to find width of "' + this.path + '"'); - if ('number' != typeof height) throw new Error('failed to find height of "' + this.path + '"'); - return [width, height]; + /** + * Open the image for reading. + * + * @api private + */ + + open() { + this.fd = fs.openSync(this.path, 'r'); + this.length = fs.fstatSync(this.fd).size; + this.extname = path.extname(this.path).slice(1); + }; + + /** + * Close the file. + * + * @api private + */ + + close() { + if (this.fd) fs.closeSync(this.fd); + }; + + /** + * Return the type of image, supports: + * + * - gif + * - png + * - jpeg + * - svg + * + * @return {String} + * @api private + */ + + type() { + var type + , buf = Buffer.alloc(4); + + fs.readSync(this.fd, buf, 0, 4, 0); + + // GIF + if (0x47 == buf[0] && 0x49 == buf[1] && 0x46 == buf[2]) type = 'gif'; + + // PNG + else if (0x50 == buf[1] && 0x4E == buf[2] && 0x47 == buf[3]) type = 'png'; + + // JPEG + else if (0xff == buf[0] && 0xd8 == buf[1]) type = 'jpeg'; + + // SVG + else if ('svg' == this.extname) type = this.extname; + + return type; + }; + + /** + * Return image dimensions `[width, height]`. + * + * @return {Array} + * @api private + */ + + size() { + var type = this.type() + , width + , height + , buf + , offset + , blockSize + , parser; + + function uint16(b) { return b[1] << 8 | b[0]; } + function uint32(b) { return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; } + + // Determine dimensions + switch (type) { + case 'jpeg': + buf = Buffer.alloc(this.length); + fs.readSync(this.fd, buf, 0, this.length, 0); + offset = 4; + blockSize = buf[offset] << 8 | buf[offset + 1]; + + while (offset < this.length) { + offset += blockSize; + if (offset >= this.length || 0xff != buf[offset]) break; + // SOF0 or SOF2 (progressive) + if (0xc0 == buf[offset + 1] || 0xc2 == buf[offset + 1]) { + height = buf[offset + 5] << 8 | buf[offset + 6]; + width = buf[offset + 7] << 8 | buf[offset + 8]; + } else { + offset += 2; + blockSize = buf[offset] << 8 | buf[offset + 1]; + } + } + break; + case 'png': + buf = Buffer.alloc(8); + // IHDR chunk width / height uint32_t big-endian + fs.readSync(this.fd, buf, 0, 8, 16); + width = uint32(buf); + height = uint32(buf.slice(4, 8)); + break; + case 'gif': + buf = Buffer.alloc(4); + // width / height uint16_t little-endian + fs.readSync(this.fd, buf, 0, 4, 6); + width = uint16(buf); + height = uint16(buf.slice(2, 4)); + break; + case 'svg': + offset = Math.min(this.length, 1024); + buf = Buffer.alloc(offset); + fs.readSync(this.fd, buf, 0, offset, 0); + buf = buf.toString('utf8'); + parser = sax.parser(true); + parser.onopentag = function (node) { + if ('svg' == node.name && node.attributes.width && node.attributes.height) { + width = parseInt(node.attributes.width, 10); + height = parseInt(node.attributes.height, 10); + } + }; + parser.write(buf).close(); + break; + } + + if ('number' != typeof width) throw new Error('failed to find width of "' + this.path + '"'); + if ('number' != typeof height) throw new Error('failed to find height of "' + this.path + '"'); + + return [width, height]; + }; }; From cda150c9a448aa23684380cf91c38d63ebbb2497 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:08:30 +0200 Subject: [PATCH 59/68] use classes in lib/token --- lib/token.js | 80 +++++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/lib/token.js b/lib/token.js index 5b8e49bca..3a0e19fba 100644 --- a/lib/token.js +++ b/lib/token.js @@ -11,43 +11,45 @@ var inspect = require('util').inspect; -/** - * Initialize a new `Token` with the given `type` and `val`. - * - * @param {String} type - * @param {Mixed} val - * @api private - */ - -var Token = exports = module.exports = function Token(type, val) { - this.type = type; - this.val = val; -}; - -/** - * Custom inspect. - * - * @return {String} - * @api public - */ - -Token.prototype.inspect = function(){ - var val = ' ' + inspect(this.val); - return '[Token:' + this.lineno + ':' + this.column + ' ' - + '\x1b[32m' + this.type + '\x1b[0m' - + '\x1b[33m' + (this.val ? val : '') + '\x1b[0m' - + ']'; -}; - -/** - * Return type or val. - * - * @return {String} - * @api public - */ - -Token.prototype.toString = function(){ - return (undefined === this.val - ? this.type - : this.val).toString(); +exports = module.exports = class Token { + /** + * Initialize a new `Token` with the given `type` and `val`. + * + * @param {String} type + * @param {Mixed} val + * @api private + */ + + constructor(type, val) { + this.type = type; + this.val = val; + } + + /** + * Custom inspect. + * + * @return {String} + * @api public + */ + + inspect() { + var val = ' ' + inspect(this.val); + return '[Token:' + this.lineno + ':' + this.column + ' ' + + '\x1b[32m' + this.type + '\x1b[0m' + + '\x1b[33m' + (this.val ? val : '') + '\x1b[0m' + + ']'; + }; + + /** + * Return type or val. + * + * @return {String} + * @api public + */ + + toString() { + return (undefined === this.val + ? this.type + : this.val).toString(); + }; }; From a1f1fb55126c91b9bcfd285da82765cbb9ec07ff Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:09:39 +0200 Subject: [PATCH 60/68] use class in lib/stack/scope --- lib/stack/scope.js | 78 ++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/lib/stack/scope.js b/lib/stack/scope.js index 4d2dbd007..db550e86f 100644 --- a/lib/stack/scope.js +++ b/lib/stack/scope.js @@ -5,51 +5,53 @@ * MIT Licensed */ -/** - * Initialize a new `Scope`. - * - * @api private - */ +module.exports = class Scope { + /** + * Initialize a new `Scope`. + * + * @api private + */ -var Scope = module.exports = function Scope() { - this.locals = {}; -}; + constructor() { + this.locals = {}; + } -/** - * Add `ident` node to the current scope. - * - * @param {Ident} ident - * @api private - */ + /** + * Add `ident` node to the current scope. + * + * @param {Ident} ident + * @api private + */ -Scope.prototype.add = function(ident){ - this.locals[ident.name] = ident.val; -}; + add(ident) { + this.locals[ident.name] = ident.val; + }; -/** - * Lookup the given local variable `name`. - * - * @param {String} name - * @return {Node} - * @api private - */ + /** + * Lookup the given local variable `name`. + * + * @param {String} name + * @return {Node} + * @api private + */ -Scope.prototype.lookup = function(name){ - return hasOwnProperty(this.locals, name) ? this.locals[name] : undefined; -}; + lookup(name) { + return hasOwnProperty(this.locals, name) ? this.locals[name] : undefined; + }; -/** - * Custom inspect. - * - * @return {String} - * @api public - */ + /** + * Custom inspect. + * + * @return {String} + * @api public + */ -Scope.prototype.inspect = function(){ - var keys = Object.keys(this.locals).map(function(key){ return '@' + key; }); - return '[Scope' - + (keys.length ? ' ' + keys.join(', ') : '') - + ']'; + inspect() { + var keys = Object.keys(this.locals).map(function (key) { return '@' + key; }); + return '[Scope' + + (keys.length ? ' ' + keys.join(', ') : '') + + ']'; + }; }; /** From c7f74dfd270c39a141afc59cceeff93b4ca80368 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:10:51 +0200 Subject: [PATCH 61/68] use class in lib/stack/frame --- lib/stack/frame.js | 86 ++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/lib/stack/frame.js b/lib/stack/frame.js index ce3117d5c..3780a404b 100644 --- a/lib/stack/frame.js +++ b/lib/stack/frame.js @@ -11,55 +11,57 @@ var Scope = require('./scope'); -/** - * Initialize a new `Frame` with the given `block`. - * - * @param {Block} block - * @api private - */ +module.exports = class Frame { + /** + * Initialize a new `Frame` with the given `block`. + * + * @param {Block} block + * @api private + */ -var Frame = module.exports = function Frame(block) { - this._scope = false === block.scope - ? null - : new Scope; - this.block = block; -}; + constructor(block) { + this._scope = false === block.scope + ? null + : new Scope; + this.block = block; + } -/** - * Return this frame's scope or the parent scope - * for scope-less blocks. - * - * @return {Scope} - * @api public - */ + /** + * Return this frame's scope or the parent scope + * for scope-less blocks. + * + * @return {Scope} + * @api public + */ -Frame.prototype.__defineGetter__('scope', function(){ - return this._scope || this.parent.scope; -}); + get scope() { + return this._scope || this.parent.scope; + }; -/** - * Lookup the given local variable `name`. - * - * @param {String} name - * @return {Node} - * @api private - */ + /** + * Lookup the given local variable `name`. + * + * @param {String} name + * @return {Node} + * @api private + */ -Frame.prototype.lookup = function(name){ - return this.scope.lookup(name) -}; + lookup(name) { + return this.scope.lookup(name) + }; -/** - * Custom inspect. - * - * @return {String} - * @api public - */ + /** + * Custom inspect. + * + * @return {String} + * @api public + */ -Frame.prototype.inspect = function(){ - return '[Frame ' - + (false === this.block.scope + inspect() { + return '[Frame ' + + (false === this.block.scope ? 'scope-less' : this.scope.inspect()) - + ']'; + + ']'; + }; }; From 8d9845644efd5070da4e73ac29441cab562b70cd Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:13:00 +0200 Subject: [PATCH 62/68] use class in lib/parser --- lib/parser.js | 460 ++++++++++++++++++++++++-------------------------- 1 file changed, 225 insertions(+), 235 deletions(-) diff --git a/lib/parser.js b/lib/parser.js index 89e3b2654..b508a2312 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -18,7 +18,7 @@ var Lexer = require('./lexer') // debuggers var debug = { - lexer: require('debug')('stylus:lexer') + lexer: require('debug')('stylus:lexer') , selector: require('debug')('stylus:parser:selector') }; @@ -27,7 +27,7 @@ var debug = { */ var selectorTokens = [ - 'ident' + 'ident' , 'string' , 'selector' , 'function' @@ -68,7 +68,7 @@ var selectorTokens = [ var pseudoSelectors = [ // https://www.w3.org/TR/selectors-4/#logical-combination // Logical Combinations - 'is' + 'is' , 'has' , 'where' , 'not' @@ -143,59 +143,49 @@ var pseudoSelectors = [ , 'selection' ]; -/** - * Initialize a new `Parser` with the given `str` and `options`. - * - * @param {String} str - * @param {Object} options - * @api private - */ +module.exports = class Parser { + /** + * Initialize a new `Parser` with the given `str` and `options`. + * + * @param {String} str + * @param {Object} options + * @api private + */ -var Parser = module.exports = function Parser(str, options) { - var self = this; - options = options || {}; - Parser.cache = Parser.cache || Parser.getCache(options); - this.hash = Parser.cache.key(str, options); - this.lexer = {}; - if (!Parser.cache.has(this.hash)) { - this.lexer = new Lexer(str, options); - } - this.prefix = options.prefix || ''; - this.root = options.root || new nodes.Root; - this.state = ['root']; - this.stash = []; - this.parens = 0; - this.css = 0; - this.state.pop = function(){ - self.prevState = [].pop.call(this); + constructor(str, options) { + var self = this; + options = options || {}; + Parser.cache = Parser.cache || Parser.getCache(options); + this.hash = Parser.cache.key(str, options); + this.lexer = {}; + if (!Parser.cache.has(this.hash)) { + this.lexer = new Lexer(str, options); + } + this.prefix = options.prefix || ''; + this.root = options.root || new nodes.Root; + this.state = ['root']; + this.stash = []; + this.parens = 0; + this.css = 0; + this.state.pop = function () { + self.prevState = [].pop.call(this); + }; }; -}; - -/** - * Get cache instance. - * - * @param {Object} options - * @return {Object} - * @api private - */ - -Parser.getCache = function(options) { - return false === options.cache - ? cache(false) - : cache(options.cache || 'memory', options); -}; - -/** - * Parser prototype. - */ - -Parser.prototype = { /** - * Constructor. + * Get cache instance. + * + * @param {Object} options + * @return {Object} + * @api private */ - constructor: Parser, + static getCache = function (options) { + return false === options.cache + ? cache(false) + : cache(options.cache || 'memory', options); + }; + /** * Return current state. @@ -204,9 +194,9 @@ Parser.prototype = { * @api private */ - currentState: function() { + currentState() { return this.state[this.state.length - 1]; - }, + } /** * Return previous state. @@ -215,9 +205,9 @@ Parser.prototype = { * @api private */ - previousState: function() { + previousState() { return this.state[this.state.length - 2]; - }, + } /** * Parse the input, then return the root node. @@ -226,7 +216,7 @@ Parser.prototype = { * @api private */ - parse: function(){ + parse() { var block = this.parent = this.root; if (Parser.cache.has(this.hash)) { block = Parser.cache.get(this.hash); @@ -244,7 +234,7 @@ Parser.prototype = { Parser.cache.set(this.hash, block); } return block; - }, + } /** * Throw an `Error` with the given `msg`. @@ -253,14 +243,14 @@ Parser.prototype = { * @api private */ - error: function(msg){ + error(msg) { var type = this.peek().type , val = undefined == this.peek().val ? '' : ' ' + this.peek().toString(); if (val.trim() == type.trim()) val = ''; throw new errors.ParseError(msg.replace('{peek}', '"' + type + val + '"')); - }, + } /** * Accept the given token `type`, and return it, @@ -271,11 +261,11 @@ Parser.prototype = { * @api private */ - accept: function(type){ + accept(type) { if (type == this.peek().type) { return this.next(); } - }, + } /** * Expect token `type` and return it, throw otherwise. @@ -285,12 +275,12 @@ Parser.prototype = { * @api private */ - expect: function(type){ + expect(type) { if (type != this.peek().type) { this.error('expected "' + type + '", got {peek}'); } return this.next(); - }, + } /** * Get the next token. @@ -299,7 +289,7 @@ Parser.prototype = { * @api private */ - next: function() { + next() { var tok = this.stash.length ? this.stash.pop() : this.lexer.next() @@ -314,7 +304,7 @@ Parser.prototype = { nodes.column = column; debug.lexer('%s %s', tok.type, tok.val || ''); return tok; - }, + } /** * Peek with lookahead(1). @@ -323,9 +313,9 @@ Parser.prototype = { * @api private */ - peek: function() { + peek() { return this.lexer.peek(); - }, + } /** * Lookahead `n` tokens. @@ -335,9 +325,9 @@ Parser.prototype = { * @api private */ - lookahead: function(n){ + lookahead(n) { return this.lexer.lookahead(n); - }, + } /** * Check if the token at `n` is a valid selector token. @@ -347,7 +337,7 @@ Parser.prototype = { * @api private */ - isSelectorToken: function(n) { + isSelectorToken(n) { var la = this.lookahead(n).type; switch (la) { case 'for': @@ -361,7 +351,7 @@ Parser.prototype = { default: return ~selectorTokens.indexOf(la); } - }, + } /** * Check if the token at `n` is a pseudo selector. @@ -371,10 +361,10 @@ Parser.prototype = { * @api private */ - isPseudoSelector: function(n){ + isPseudoSelector(n) { var val = this.lookahead(n).val; return val && ~pseudoSelectors.indexOf(val.name); - }, + } /** * Check if the current line contains `type`. @@ -384,7 +374,7 @@ Parser.prototype = { * @api private */ - lineContains: function(type){ + lineContains(type) { var i = 1 , la; @@ -392,13 +382,13 @@ Parser.prototype = { if (~['indent', 'outdent', 'newline', 'eos'].indexOf(la.type)) return; if (type == la.type) return true; } - }, + } /** * Valid selector tokens. */ - selectorToken: function() { + selectorToken() { if (this.isSelectorToken(1)) { if ('{' == this.peek().type) { // unclosed, must be a block @@ -422,7 +412,7 @@ Parser.prototype = { } return this.next(); } - }, + } /** * Skip the given `tokens`. @@ -431,46 +421,46 @@ Parser.prototype = { * @api private */ - skip: function(tokens) { + skip(tokens) { while (~tokens.indexOf(this.peek().type)) this.next(); - }, + } /** * Consume whitespace. */ - skipWhitespace: function() { + skipWhitespace() { this.skip(['space', 'indent', 'outdent', 'newline']); - }, + } /** * Consume newlines. */ - skipNewlines: function() { + skipNewlines() { while ('newline' == this.peek().type) this.next(); - }, + } /** * Consume spaces. */ - skipSpaces: function() { + skipSpaces() { while ('space' == this.peek().type) this.next(); - }, + } /** * Consume spaces and comments. */ - skipSpacesAndComments: function() { + skipSpacesAndComments() { while ('space' == this.peek().type || 'comment' == this.peek().type) this.next(); - }, + } /** * Check if the following sequence of tokens @@ -478,10 +468,10 @@ Parser.prototype = { * `{` or indentation. */ - looksLikeFunctionDefinition: function(i) { + looksLikeFunctionDefinition(i) { return 'indent' == this.lookahead(i).type || '{' == this.lookahead(i).type; - }, + } /** * Check if the following sequence of tokens @@ -492,7 +482,7 @@ Parser.prototype = { * @api private */ - looksLikeSelector: function(fromProperty) { + looksLikeSelector(fromProperty) { var i = 1 , node , brace; @@ -506,7 +496,7 @@ Parser.prototype = { // followed by a selector while ('ident' == this.lookahead(i).type && ('newline' == this.lookahead(i + 1).type - || ',' == this.lookahead(i + 1).type)) i += 2; + || ',' == this.lookahead(i + 1).type)) i += 2; while (this.isSelectorToken(i) || ',' == this.lookahead(i).type) { @@ -627,7 +617,7 @@ Parser.prototype = { // as 'td:th-child(1)' may look like a property // and function call to the parser otherwise if (':' == this.lookahead(i++).type - && !this.lookahead(i-1).space + && !this.lookahead(i - 1).space && this.isPseudoSelector(i)) return true; @@ -655,13 +645,13 @@ Parser.prototype = { // css-style mode, false on ; } if (this.css) { if (';' == this.lookahead(i).type || - '}' == this.lookahead(i - 1).type) + '}' == this.lookahead(i - 1).type) return false; } // Trailing separators while (!~[ - 'indent' + 'indent' , 'outdent' , 'newline' , 'for' @@ -673,14 +663,14 @@ Parser.prototype = { if ('indent' == this.lookahead(i).type) return true; - }, + } /** * Check if the following sequence of tokens * forms an attribute selector. */ - looksLikeAttributeSelector: function(n) { + looksLikeAttributeSelector(n) { var type = this.lookahead(n).type; if ('=' == type && this.bracketed) return true; return ('ident' == type || 'string' == type) @@ -688,14 +678,14 @@ Parser.prototype = { && ('newline' == this.lookahead(n + 2).type || this.isSelectorToken(n + 2)) && !this.lineContains(':') && !this.lineContains('='); - }, + } /** * Check if the following sequence of tokens * forms a keyframe block. */ - looksLikeKeyframe: function() { + looksLikeKeyframe() { var i = 2 , type; switch (this.lookahead(i).type) { @@ -705,17 +695,17 @@ Parser.prototype = { return true; case 'newline': while ('unit' == this.lookahead(++i).type - || 'newline' == this.lookahead(i).type) ; + || 'newline' == this.lookahead(i).type); type = this.lookahead(i).type; return 'indent' == type || '{' == type; } - }, + } /** * Check if the current state supports selectors. */ - stateAllowsSelector: function() { + stateAllowsSelector() { switch (this.currentState()) { case 'root': case 'atblock': @@ -726,7 +716,7 @@ Parser.prototype = { case 'for': return true; } - }, + } /** * Try to assign @block to the node. @@ -735,13 +725,13 @@ Parser.prototype = { * @private */ - assignAtblock: function(expr) { + assignAtblock(expr) { try { expr.push(this.atblock(expr)); - } catch(err) { + } catch (err) { this.error('invalid right-hand side operand in assignment, got {peek}'); } - }, + } /** * statement @@ -749,7 +739,7 @@ Parser.prototype = { * | statement 'unless' expression */ - statement: function() { + statement() { var stmt = this.stmt() , state = this.prevState , block @@ -770,7 +760,7 @@ Parser.prototype = { case 'expression': case 'function arguments': while (op = - this.accept('if') + this.accept('if') || this.accept('unless') || this.accept('for')) { switch (op.type) { @@ -796,7 +786,7 @@ Parser.prototype = { } return stmt; - }, + } /** * ident @@ -819,7 +809,7 @@ Parser.prototype = { * | 'return' expression */ - stmt: function() { + stmt() { var tok = this.peek(), selector; switch (tok.type) { case 'keyframes': @@ -895,13 +885,13 @@ Parser.prototype = { if (expr.isEmpty) this.error('unexpected {peek}'); return expr; } - }, + } /** * indent (!outdent)+ outdent */ - block: function(node, scope) { + block(node, scope) { var delim , stmt , next @@ -957,23 +947,23 @@ Parser.prototype = { this.parent = block.parent; return block; - }, + } /** * comment space* */ - comment: function(){ + comment() { var node = this.next().val; this.skipSpaces(); return node; - }, + } /** * for val (',' key) in expr */ - for: function() { + for() { this.expect('for'); var key , val = this.id().name; @@ -986,25 +976,25 @@ Parser.prototype = { each.block = this.block(each, false); this.state.pop(); return each; - }, + } /** * return expression */ - return: function() { + return() { this.expect('return'); var expr = this.expression(); return expr.isEmpty ? new nodes.Return : new nodes.Return(expr); - }, + } /** * unless expression block */ - unless: function() { + unless() { this.expect('unless'); this.state.push('conditional'); this.cond = true; @@ -1013,13 +1003,13 @@ Parser.prototype = { node.block = this.block(node, false); this.state.pop(); return node; - }, + } /** * if expression block (else block)? */ - if: function() { + if() { var token = this.expect('if'); this.state.push('conditional'); @@ -1054,7 +1044,7 @@ Parser.prototype = { } this.state.pop(); return node; - }, + } /** * @block @@ -1062,20 +1052,20 @@ Parser.prototype = { * @param {Expression} [node] */ - atblock: function(node){ + atblock(node) { if (!node) this.expect('atblock'); node = new nodes.Atblock; this.state.push('atblock'); node.block = this.block(node, false); this.state.pop(); return node; - }, + } /** * atrule selector? block? */ - atrule: function(){ + atrule() { var type = this.expect('atrule').val , node = new nodes.Atrule(type) , tok; @@ -1090,33 +1080,33 @@ Parser.prototype = { this.state.pop(); } return node; - }, + } /** * scope */ - scope: function(){ + scope() { this.expect('scope'); var selector = this.selectorParts() - .map(function(selector) { return selector.val; }) + .map(function (selector) { return selector.val; }) .join(''); this.selectorScope = selector.trim(); return nodes.null; - }, + } /** * supports */ - supports: function(){ + supports() { this.expect('supports'); var node = new nodes.Supports(this.supportsCondition()); this.state.push('atrule'); node.block = this.block(node); this.state.pop(); return node; - }, + } /** * supports negation @@ -1124,7 +1114,7 @@ Parser.prototype = { * | expression */ - supportsCondition: function(){ + supportsCondition() { var node = this.supportsNegation() || this.supportsOp(); if (!node) { @@ -1133,26 +1123,26 @@ Parser.prototype = { this.cond = false; } return node; - }, + } /** * 'not' supports feature */ - supportsNegation: function(){ + supportsNegation() { if (this.accept('not')) { var node = new nodes.Expression; node.push(new nodes.Literal('not')); node.push(this.supportsFeature()); return node; } - }, + } /** * supports feature (('and' | 'or') supports feature)+ */ - supportsOp: function(){ + supportsOp() { var feature = this.supportsFeature() , op , expr; @@ -1165,14 +1155,14 @@ Parser.prototype = { } return expr; } - }, + } /** * ('(' supports condition ')') * | feature */ - supportsFeature: function(){ + supportsFeature() { this.skipSpacesAndComments(); if ('(' == this.peek().type) { var la = this.lookahead(2).type; @@ -1190,13 +1180,13 @@ Parser.prototype = { return node; } } - }, + } /** * extend */ - extend: function(){ + extend() { var tok = this.expect('extend') , selectors = [] , sel @@ -1218,32 +1208,32 @@ Parser.prototype = { this.skip(['!', 'ident']); sel.optional = true; - } while(this.accept(',')); + } while (this.accept(',')); node = new nodes.Extend(selectors); node.lineno = tok.lineno; node.column = tok.column; return node; - }, + } /** * media queries */ - media: function() { + media() { this.expect('media'); this.state.push('atrule'); var media = new nodes.Media(this.queries()); media.block = this.block(media); this.state.pop(); return media; - }, + } /** * query (',' query)* */ - queries: function() { + queries() { var queries = new nodes.QueryList , skip = ['comment', 'newline', 'space']; @@ -1253,7 +1243,7 @@ Parser.prototype = { this.skip(skip); } while (this.accept(',')); return queries; - }, + } /** * expression @@ -1261,7 +1251,7 @@ Parser.prototype = { * | feature ('and' feature)* */ - query: function() { + query() { var query = new nodes.Query , expr , pred @@ -1270,7 +1260,7 @@ Parser.prototype = { // hash values support if ('ident' == this.peek().type && ('.' == this.lookahead(2).type - || '[' == this.lookahead(2).type)) { + || '[' == this.lookahead(2).type)) { this.cond = true; expr = this.expression(); this.cond = false; @@ -1298,13 +1288,13 @@ Parser.prototype = { } while (this.accept('&&')); return query; - }, + } /** * '(' ident ( ':'? expression )? ')' */ - feature: function() { + feature() { this.skipSpacesAndComments(); this.expect('('); this.skipSpacesAndComments(); @@ -1319,13 +1309,13 @@ Parser.prototype = { this.expect(')'); this.skipSpacesAndComments(); return node; - }, + } /** * @-moz-document call (',' call)* block */ - mozdocument: function(){ + mozdocument() { this.expect('-moz-document'); var mozdocument = new nodes.Atrule('-moz-document') , calls = []; @@ -1339,44 +1329,44 @@ Parser.prototype = { mozdocument.block = this.block(mozdocument, false); this.state.pop(); return mozdocument; - }, + } /** * import expression */ - import: function() { + import() { this.expect('import'); this.allowPostfix = true; return new nodes.Import(this.expression(), false); - }, + } /** * require expression */ - require: function() { + require() { this.expect('require'); this.allowPostfix = true; return new nodes.Import(this.expression(), true); - }, + } /** * charset string */ - charset: function() { + charset() { this.expect('charset'); var str = this.expect('string').val; this.allowPostfix = true; return new nodes.Charset(str); - }, + } /** * namespace ident? (string | url) */ - namespace: function() { + namespace() { var str , prefix; this.expect('namespace'); @@ -1390,13 +1380,13 @@ Parser.prototype = { str = this.accept('string') || this.url(); this.allowPostfix = true; return new nodes.Namespace(str, prefix); - }, + } /** * keyframes name block */ - keyframes: function() { + keyframes() { var tok = this.expect('keyframes') , keyframes; @@ -1412,25 +1402,25 @@ Parser.prototype = { this.state.pop(); return keyframes; - }, + } /** * literal */ - literal: function() { + literal() { return this.expect('literal').val; - }, + } /** * ident space? */ - id: function() { + id() { var tok = this.expect('ident'); this.accept('space'); return tok.val; - }, + } /** * ident @@ -1439,7 +1429,7 @@ Parser.prototype = { * | selector */ - ident: function() { + ident() { var i = 2 , la = this.lookahead(i).type; @@ -1460,7 +1450,7 @@ Parser.prototype = { if ('space' == this.lookahead(i - 1).type) return this.selector(); if (this._ident == this.peek()) return this.id(); while ('=' != this.lookahead(++i).type - && !~['[', ',', 'newline', 'indent', 'eos'].indexOf(this.lookahead(i).type)) ; + && !~['[', ',', 'newline', 'indent', 'eos'].indexOf(this.lookahead(i).type)); if ('=' == this.lookahead(i).type) { this._ident = this.peek(); return this.expression(); @@ -1472,7 +1462,7 @@ Parser.prototype = { if (this._ident == this.peek()) return this.id(); while (']' != this.lookahead(i++).type && 'selector' != this.lookahead(i).type - && 'eos' != this.lookahead(i).type) ; + && 'eos' != this.lookahead(i).type); if ('=' == this.lookahead(i).type) { this._ident = this.peek(); return this.expression(); @@ -1545,13 +1535,13 @@ Parser.prototype = { return id; } } - }, + } /** * '*'? (ident | '{' expression '}')+ */ - interpolate: function() { + interpolate() { var node , segs = [] , star; @@ -1565,9 +1555,9 @@ Parser.prototype = { segs.push(this.expression()); this.expect('}'); this.state.pop(); - } else if (node = this.accept('-')){ + } else if (node = this.accept('-')) { segs.push(new nodes.Literal('-')); - } else if (node = this.accept('ident')){ + } else if (node = this.accept('ident')) { segs.push(node.val); } else { break; @@ -1575,14 +1565,14 @@ Parser.prototype = { } if (!segs.length) this.expect('ident'); return segs; - }, + } /** * property ':'? expression * | ident */ - property: function() { + property() { if (this.looksLikeSelector(true)) return this.selector(); // property @@ -1606,7 +1596,7 @@ Parser.prototype = { this.accept(';'); return ret; - }, + } /** * selector ',' selector @@ -1614,7 +1604,7 @@ Parser.prototype = { * | selector block */ - selector: function() { + selector() { var arr , group = new nodes.Group , scope = this.selectorScope @@ -1644,9 +1634,9 @@ Parser.prototype = { this.state.pop(); return group; - }, + } - selectorParts: function(){ + selectorParts() { var tok , arr = []; @@ -1692,13 +1682,13 @@ Parser.prototype = { } return arr; - }, + } /** * ident ('=' | '?=') expression */ - assignment: function() { + assignment() { var op, node, @@ -1706,7 +1696,7 @@ Parser.prototype = { name = ident.name; if (op = - this.accept('=') + this.accept('=') || this.accept('?=') || this.accept('+=') || this.accept('-=') @@ -1742,14 +1732,14 @@ Parser.prototype = { } return node; - }, + } /** * definition * | call */ - function: function() { + function() { var parens = 1 , i = 2 , tok; @@ -1781,26 +1771,26 @@ Parser.prototype = { ? this.functionDefinition() : this.expression(); } - }, + } /** * url '(' (expression | urlchars)+ ')' */ - url: function() { + url() { this.expect('function'); this.state.push('function arguments'); var args = this.args(); this.expect(')'); this.state.pop(); return new nodes.Call('url', args); - }, + } /** * '+'? ident '(' expression ')' block? */ - functionCall: function() { + functionCall() { var withBlock = this.accept('+'); if ('url' == this.peek().val.name) return this.url(); @@ -1824,13 +1814,13 @@ Parser.prototype = { this.state.pop(); } return call; - }, + } /** * ident '(' params ')' block */ - functionDefinition: function() { + functionDefinition() { var tok = this.expect('function'), name = tok.val.name; @@ -1853,7 +1843,7 @@ Parser.prototype = { fn.block = this.block(fn); this.state.pop(); return new nodes.Ident(name, fn); - }, + } /** * ident @@ -1862,7 +1852,7 @@ Parser.prototype = { * | ident ',' ident */ - params: function() { + params() { var tok , node , params = new nodes.Params; @@ -1879,13 +1869,13 @@ Parser.prototype = { this.skipWhitespace(); } return params; - }, + } /** * (ident ':')? expression (',' (ident ':')? expression)* */ - args: function() { + args() { var args = new nodes.Arguments , keyword; @@ -1895,20 +1885,20 @@ Parser.prototype = { keyword = this.next().val.string; this.expect(':'); args.map[keyword] = this.expression(); - // arg + // arg } else { args.push(this.expression()); } } while (this.accept(',')); return args; - }, + } /** * expression (',' expression)* */ - list: function() { + list() { var node = this.expression(); while (this.accept(',')) { @@ -1922,13 +1912,13 @@ Parser.prototype = { } } return node; - }, + } /** * negation+ */ - expression: function() { + expression() { var node , expr = new nodes.Expression; this.state.push('expression'); @@ -1942,25 +1932,25 @@ Parser.prototype = { expr.column = expr.nodes[0].column; } return expr; - }, + } /** * 'not' ternary * | ternary */ - negation: function() { + negation() { if (this.accept('not')) { return new nodes.UnaryOp('!', this.negation()); } return this.ternary(); - }, + } /** * logical ('?' expression ':' expression)? */ - ternary: function() { + ternary() { var node = this.logical(); if (this.accept('?')) { var trueExpr = this.expression(); @@ -1969,26 +1959,26 @@ Parser.prototype = { node = new nodes.Ternary(node, trueExpr, falseExpr); } return node; - }, + } /** * typecheck (('&&' | '||') typecheck)* */ - logical: function() { + logical() { var op , node = this.typecheck(); while (op = this.accept('&&') || this.accept('||')) { node = new nodes.BinOp(op.type, node, this.typecheck()); } return node; - }, + } /** * equality ('is a' equality)* */ - typecheck: function() { + typecheck() { var op , node = this.equality(); while (op = this.accept('is a')) { @@ -1998,13 +1988,13 @@ Parser.prototype = { this.operand = false; } return node; - }, + } /** * in (('==' | '!=') in)* */ - equality: function() { + equality() { var op , node = this.in(); while (op = this.accept('==') || this.accept('!=')) { @@ -2014,13 +2004,13 @@ Parser.prototype = { this.operand = false; } return node; - }, + } /** * relational ('in' relational)* */ - in: function() { + in() { var node = this.relational(); while (this.accept('in')) { this.operand = true; @@ -2029,34 +2019,34 @@ Parser.prototype = { this.operand = false; } return node; - }, + } /** * range (('>=' | '<=' | '>' | '<') range)* */ - relational: function() { + relational() { var op , node = this.range(); while (op = - this.accept('>=') + this.accept('>=') || this.accept('<=') || this.accept('<') || this.accept('>') - ) { + ) { this.operand = true; if (!node) this.error('illegal unary "' + op + '", missing left-hand operand'); node = new nodes.BinOp(op.type, node, this.range()); this.operand = false; } return node; - }, + } /** * additive (('..' | '...') additive)* */ - range: function() { + range() { var op , node = this.additive(); if (op = this.accept('...') || this.accept('..')) { @@ -2066,13 +2056,13 @@ Parser.prototype = { this.operand = false; } return node; - }, + } /** * multiplicative (('+' | '-') multiplicative)* */ - additive: function() { + additive() { var op , node = this.multiplicative(); while (op = this.accept('+') || this.accept('-')) { @@ -2081,17 +2071,17 @@ Parser.prototype = { this.operand = false; } return node; - }, + } /** * defined (('**' | '*' | '/' | '%') defined)* */ - multiplicative: function() { + multiplicative() { var op , node = this.defined(); while (op = - this.accept('**') + this.accept('**') || this.accept('*') || this.accept('/') || this.accept('%')) { @@ -2107,32 +2097,32 @@ Parser.prototype = { } } return node; - }, + } /** * unary 'is defined' * | unary */ - defined: function() { + defined() { var node = this.unary(); if (this.accept('is defined')) { if (!node) this.error('illegal unary "is defined", missing left-hand operand'); node = new nodes.BinOp('is defined', node); } return node; - }, + } /** * ('!' | '~' | '+' | '-') unary * | subscript */ - unary: function() { + unary() { var op , node; if (op = - this.accept('!') + this.accept('!') || this.accept('~') || this.accept('+') || this.accept('-')) { @@ -2144,14 +2134,14 @@ Parser.prototype = { return node; } return this.subscript(); - }, + } /** * member ('[' expression ']')+ '='? * | member */ - subscript: function() { + subscript() { var node = this.member() , id; while (this.accept('[')) { @@ -2166,14 +2156,14 @@ Parser.prototype = { if (node.val.isEmpty) this.assignAtblock(node.val); } return node; - }, + } /** * primary ('.' id)+ '='? * | primary */ - member: function() { + member() { var node = this.primary(); if (node) { while (this.accept('.')) { @@ -2188,14 +2178,14 @@ Parser.prototype = { } } return node; - }, + } /** * '{' '}' * | '{' pair (ws pair)* '}' */ - object: function(){ + object() { var obj = new nodes.Object , id, val, comma, hash; this.expect('{'); @@ -2227,7 +2217,7 @@ Parser.prototype = { } return obj; - }, + } /** * unit @@ -2243,7 +2233,7 @@ Parser.prototype = { * | '(' expression ')' '%'? */ - primary: function() { + primary() { var tok; this.skipSpaces(); From 8b9d84e7229f80172a3d0cbf34d4e1dee2f460c5 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:18:37 +0200 Subject: [PATCH 63/68] use `super.method()` instead of `Node.prototype.method.call(this, ...args)` --- lib/nodes/arguments.js | 2 +- lib/nodes/expression.js | 2 +- lib/nodes/ident.js | 4 ++-- lib/nodes/literal.js | 4 ++-- lib/nodes/object.js | 2 +- lib/nodes/rgba.js | 2 +- lib/nodes/string.js | 2 +- lib/nodes/unit.js | 6 +++--- lib/stack/scope.js | 11 +---------- 9 files changed, 13 insertions(+), 22 deletions(-) diff --git a/lib/nodes/arguments.js b/lib/nodes/arguments.js index 07304cdec..9498ce18e 100644 --- a/lib/nodes/arguments.js +++ b/lib/nodes/arguments.js @@ -52,7 +52,7 @@ module.exports = class Arguments extends nodes.Expression { */ clone(parent) { - var clone = nodes.Expression.prototype.clone.call(this, parent); + var clone = super.clone(parent); clone.map = {}; for (var key in this.map) { clone.map[key] = this.map[key].clone(parent, clone); diff --git a/lib/nodes/expression.js b/lib/nodes/expression.js index 047331895..ae8e97adb 100644 --- a/lib/nodes/expression.js +++ b/lib/nodes/expression.js @@ -145,7 +145,7 @@ module.exports = class Expression extends Node { ? this : right; case 'in': - return Node.prototype.operate.call(this, op, right); + return super.operate(op, right); case '!=': return this.operate('==', right, val).negate(); case '==': diff --git a/lib/nodes/ident.js b/lib/nodes/ident.js index 97754e3c5..ee815a287 100644 --- a/lib/nodes/ident.js +++ b/lib/nodes/ident.js @@ -119,7 +119,7 @@ module.exports = class Ident extends Node { case 'unit': return new Ident(other.toString()); default: - return Node.prototype.coerce.call(this, other); + return super.coerce(other); } }; @@ -147,6 +147,6 @@ module.exports = class Ident extends Node { case '+': return new nodes.Ident(this.string + this.coerce(val).string); } - return Node.prototype.operate.call(this, op, right); + return super.operate(op, right); }; }; diff --git a/lib/nodes/literal.js b/lib/nodes/literal.js index 68c406df9..c55bdda31 100644 --- a/lib/nodes/literal.js +++ b/lib/nodes/literal.js @@ -64,7 +64,7 @@ module.exports = class Literal extends Node { case 'literal': return new Literal(other.string); default: - return Node.prototype.coerce.call(this, other); + return super.coerce(other); } }; @@ -83,7 +83,7 @@ module.exports = class Literal extends Node { case '+': return new nodes.Literal(this.string + this.coerce(val).string); default: - return Node.prototype.operate.call(this, op, right); + return super.operate(op, right); } }; diff --git a/lib/nodes/object.js b/lib/nodes/object.js index a2e4c42b6..9be433839 100644 --- a/lib/nodes/object.js +++ b/lib/nodes/object.js @@ -125,7 +125,7 @@ module.exports = class Object extends Node { case '!=': return this.operate('==', right).negate(); default: - return Node.prototype.operate.call(this, op, right); + return super.operate.call(op, right); } }; diff --git a/lib/nodes/rgba.js b/lib/nodes/rgba.js index 81c804af6..913efb37b 100644 --- a/lib/nodes/rgba.js +++ b/lib/nodes/rgba.js @@ -263,7 +263,7 @@ exports = module.exports = class RGBA extends Node { } break; } - return Node.prototype.operate.call(this, op, right); + return super.operate(op, right); }; /** diff --git a/lib/nodes/string.js b/lib/nodes/string.js index 0aa79ba53..756d6f4d4 100644 --- a/lib/nodes/string.js +++ b/lib/nodes/string.js @@ -137,7 +137,7 @@ module.exports = class String extends Node { expr.push(new String(this.val + this.coerce(right).val)); return expr; default: - return Node.prototype.operate.call(this, op, right); + return super.operate(op, right); } }; diff --git a/lib/nodes/unit.js b/lib/nodes/unit.js index 422c92876..9ba2c2e89 100644 --- a/lib/nodes/unit.js +++ b/lib/nodes/unit.js @@ -161,7 +161,7 @@ module.exports = class Unit extends Node { } } - return Node.prototype.operate.call(this, op, right); + return super.operate(op, right); }; /** @@ -201,10 +201,10 @@ module.exports = class Unit extends Node { // keyframes interpolation if ('%' == other.val) return new nodes.Unit(0, '%'); var val = parseFloat(other.val); - if (isNaN(val)) Node.prototype.coerce.call(this, other); + if (isNaN(val)) super.coerce(other); return new nodes.Unit(val); } else { - return Node.prototype.coerce.call(this, other); + return super.coerce(other); } }; }; diff --git a/lib/stack/scope.js b/lib/stack/scope.js index db550e86f..65e9b74f0 100644 --- a/lib/stack/scope.js +++ b/lib/stack/scope.js @@ -36,7 +36,7 @@ module.exports = class Scope { */ lookup(name) { - return hasOwnProperty(this.locals, name) ? this.locals[name] : undefined; + return this.locals.hasOwnProperty(name) ? this.locals[name] : undefined; }; /** @@ -53,12 +53,3 @@ module.exports = class Scope { + ']'; }; }; - -/** - * @param {Object} obj - * @param {String} propName - * @returns {Boolean} - */ -function hasOwnProperty(obj, propName) { - return Object.prototype.hasOwnProperty.call(obj, propName); -} From b9b1ee730675cb4b323da7ced68f951819d16068 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:20:59 +0200 Subject: [PATCH 64/68] use `Object.setPrototypeOf` instead of `__proto__` --- lib/cache/fs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cache/fs.js b/lib/cache/fs.js index 0894a2dab..774d4a11d 100644 --- a/lib/cache/fs.js +++ b/lib/cache/fs.js @@ -76,7 +76,7 @@ module.exports = class FSCache { static fromJSON(key, val) { if (val && val.__type) { - val.__proto__ = nodes[val.__type].prototype; + Object.setPrototypeOf(val, nodes[val.__type].prototype); } return val; }; From b89f43552e071e59fa777314dba8a9bd3e2aeaae Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:43:56 +0200 Subject: [PATCH 65/68] use static method instead of initialisor (for node v10-12) --- lib/parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/parser.js b/lib/parser.js index b508a2312..2ef8bd87e 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -180,7 +180,7 @@ module.exports = class Parser { * @api private */ - static getCache = function (options) { + static getCache(options) { return false === options.cache ? cache(false) : cache(options.cache || 'memory', options); From 19292d65e2fd5728209a30ac1abcee93d067e705 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:44:21 +0200 Subject: [PATCH 66/68] use getters to use alias property assignment doesn't work on node v10-12 --- lib/nodes/null.js | 4 +++- lib/nodes/object.js | 4 +++- lib/visitor/compiler.js | 4 +++- lib/visitor/evaluator.js | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/nodes/null.js b/lib/nodes/null.js index 4ef438088..3fa1b1821 100644 --- a/lib/nodes/null.js +++ b/lib/nodes/null.js @@ -30,7 +30,9 @@ module.exports = class Null extends Node { return 'null'; }; - inspect = this.toString + get inspect() { + return this.toString; + } /** * Return false. diff --git a/lib/nodes/object.js b/lib/nodes/object.js index 9be433839..b696d9a7a 100644 --- a/lib/nodes/object.js +++ b/lib/nodes/object.js @@ -44,7 +44,9 @@ module.exports = class Object extends Node { * Alias for `setValue` for compatible API */ - set = this.setValue + get set() { + return this.setValue; + } /** * Set `key` to `val`. diff --git a/lib/visitor/compiler.js b/lib/visitor/compiler.js index 1b00564f5..b0187a435 100644 --- a/lib/visitor/compiler.js +++ b/lib/visitor/compiler.js @@ -528,7 +528,9 @@ module.exports = class Compiler extends Visitor { * Visit Arguments. */ - visitArguments = this.visitExpression; + get visitArguments() { + return this.visitExpression; + } /** * Visit Property. diff --git a/lib/visitor/evaluator.js b/lib/visitor/evaluator.js index 804ff7a75..4c1acead3 100644 --- a/lib/visitor/evaluator.js +++ b/lib/visitor/evaluator.js @@ -648,7 +648,9 @@ module.exports = class Evaluator extends Visitor { * Visit Arguments. */ - visitArguments = this.visitExpression; + get visitArguments() { + return this.visitExpression; + } /** * Visit Property. From 13770b5074f9e85216ab18fb3afe4b6a8a0fd340 Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 13:47:31 +0200 Subject: [PATCH 67/68] added deno tests --- .github/workflows/ci.yml | 20 ++++++++++++++++++++ deno/test.ts | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 deno/test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 092dcfe48..e11ad0699 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,3 +96,23 @@ jobs: run: npm install - name: Run nyc run: npx nyc@latest npm run test + + deno_tests: + name: 'Test stylus on ${{matrix.os}} with latest stable deno' + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + # Pull repo to test machine + - uses: actions/checkout@v3 + # Configures the deno version used on GitHub-hosted runners + - uses: denoland/setup-deno@v1 + with: + # Run with latest stable Deno + deno-version: v1.x + - name: Print deno version + # Output useful info for debugging. + run: deno --version + - name: Run Test + run: deno run -A deno/test.ts diff --git a/deno/test.ts b/deno/test.ts new file mode 100644 index 000000000..86e3fccdb --- /dev/null +++ b/deno/test.ts @@ -0,0 +1,33 @@ +import Mocha from "mocha"; + +import fs from "node:fs"; +import path from "node:path"; +import process from "node:process"; + +globalThis.process = process; + +const mocha = new Mocha({ + bail: true, + checkLeaks: true, + require: ["chai"], + reporter: "dot", +}); + +const testDirs = ["test/", "test/middleware/"]; + +testDirs.forEach((testDir) => { + fs + .readdirSync(testDir) + .filter(function (file) { + if (testDir === "test/" && file === "deno.js") return false; + + return file.slice(-3) === ".js"; + }) + .forEach(function (file) { + mocha.addFile(path.join(testDir, file)); + }); +}); + +mocha.run(function (failures) { + process.exitCode = failures ? 1 : 0; +}); \ No newline at end of file From 9610e4e63625e116c47afcc6de27ae55310f775c Mon Sep 17 00:00:00 2001 From: Angelo Verlain Date: Mon, 14 Aug 2023 14:44:49 +0200 Subject: [PATCH 68/68] downgrade `@adobe/css-tools` to 4.2.0 for node v10 compat --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index baef1a669..e39c60f5d 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "test": "mocha test/ test/middleware/ --require chai --bail --check-leaks --reporter dot" }, "dependencies": { - "@adobe/css-tools": "^4.0.1", + "@adobe/css-tools": "~4.2.0", "debug": "^4.3.2", "glob": "^7.1.6", "sax": "~1.2.4",