aboutsummaryrefslogtreecommitdiff
path: root/node_modules/stylus/lib/visitor/normalizer.js
diff options
context:
space:
mode:
authorruki <waruqi@gmail.com>2018-11-08 00:38:48 +0800
committerruki <waruqi@gmail.com>2018-11-07 21:53:09 +0800
commit26105034da4fcce7ac883c899d781f016559310d (patch)
treec459a5dc4e3aa0972d9919033ece511ce76dd129 /node_modules/stylus/lib/visitor/normalizer.js
parent2c77f00f1a7ecb6c8192f9c16d3b2001b254a107 (diff)
downloadxmake-docs-26105034da4fcce7ac883c899d781f016559310d.tar.gz
xmake-docs-26105034da4fcce7ac883c899d781f016559310d.zip
switch to vuepress
Diffstat (limited to 'node_modules/stylus/lib/visitor/normalizer.js')
-rw-r--r--node_modules/stylus/lib/visitor/normalizer.js424
1 files changed, 424 insertions, 0 deletions
diff --git a/node_modules/stylus/lib/visitor/normalizer.js b/node_modules/stylus/lib/visitor/normalizer.js
new file mode 100644
index 00000000..2d67ddfb
--- /dev/null
+++ b/node_modules/stylus/lib/visitor/normalizer.js
@@ -0,0 +1,424 @@
+
+/*!
+ * Stylus - Normalizer
+ * Copyright (c) Automattic <developer.wordpress.com>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var Visitor = require('./')
+ , nodes = require('../nodes')
+ , utils = require('../utils');
+
+/**
+ * Initialize a new `Normalizer` with the given `root` Node.
+ *
+ * This visitor implements the first stage of the duel-stage
+ * compiler, tasked with stripping the "garbage" from
+ * the evaluated nodes, ditching null rules, resolving
+ * ruleset selectors etc. This step performs the logic
+ * necessary to facilitate the "@extend" functionality,
+ * as these must be resolved _before_ buffering output.
+ *
+ * @param {Node} root
+ * @api public
+ */
+
+var Normalizer = module.exports = function Normalizer(root, options) {
+ options = options || {};
+ Visitor.call(this, root);
+ this.hoist = options['hoist atrules'];
+ this.stack = [];
+ this.map = {};
+ this.imports = [];
+};
+
+/**
+ * Inherit from `Visitor.prototype`.
+ */
+
+Normalizer.prototype.__proto__ = Visitor.prototype;
+
+/**
+ * Normalize the node tree.
+ *
+ * @return {Node}
+ * @api private
+ */
+
+Normalizer.prototype.normalize = function(){
+ var ret = this.visit(this.root);
+
+ if (this.hoist) {
+ // hoist @import
+ if (this.imports.length) ret.nodes = this.imports.concat(ret.nodes);
+
+ // hoist @charset
+ if (this.charset) ret.nodes = [this.charset].concat(ret.nodes);
+ }
+
+ return ret;
+};
+
+/**
+ * Bubble up the given `node`.
+ *
+ * @param {Node} node
+ * @api private
+ */
+
+Normalizer.prototype.bubble = function(node){
+ var props = []
+ , other = []
+ , self = this;
+
+ function filterProps(block) {
+ block.nodes.forEach(function(node) {
+ node = self.visit(node);
+
+ switch (node.nodeName) {
+ case 'property':
+ props.push(node);
+ break;
+ case 'block':
+ filterProps(node);
+ break;
+ default:
+ other.push(node);
+ }
+ });
+ }
+
+ filterProps(node.block);
+
+ if (props.length) {
+ var selector = new nodes.Selector([new nodes.Literal('&')]);
+ selector.lineno = node.lineno;
+ selector.column = node.column;
+ selector.filename = node.filename;
+ selector.val = '&';
+
+ var group = new nodes.Group;
+ group.lineno = node.lineno;
+ group.column = node.column;
+ group.filename = node.filename;
+
+ var block = new nodes.Block(node.block, group);
+ block.lineno = node.lineno;
+ block.column = node.column;
+ block.filename = node.filename;
+
+ props.forEach(function(prop){
+ block.push(prop);
+ });
+
+ group.push(selector);
+ group.block = block;
+
+ node.block.nodes = [];
+ node.block.push(group);
+ other.forEach(function(n){
+ node.block.push(n);
+ });
+
+ var group = this.closestGroup(node.block);
+ if (group) node.group = group.clone();
+
+ node.bubbled = true;
+ }
+};
+
+/**
+ * Return group closest to the given `block`.
+ *
+ * @param {Block} block
+ * @return {Group}
+ * @api private
+ */
+
+Normalizer.prototype.closestGroup = function(block){
+ var parent = block.parent
+ , node;
+ while (parent && (node = parent.node)) {
+ if ('group' == node.nodeName) return node;
+ parent = node.block && node.block.parent;
+ }
+};
+
+/**
+ * Visit Root.
+ */
+
+Normalizer.prototype.visitRoot = function(block){
+ var ret = new nodes.Root
+ , node;
+
+ for (var i = 0; i < block.nodes.length; ++i) {
+ node = block.nodes[i];
+ switch (node.nodeName) {
+ case 'null':
+ case 'expression':
+ case 'function':
+ case 'unit':
+ case 'atblock':
+ continue;
+ default:
+ this.rootIndex = i;
+ ret.push(this.visit(node));
+ }
+ }
+
+ return ret;
+};
+
+/**
+ * Visit Property.
+ */
+
+Normalizer.prototype.visitProperty = function(prop){
+ this.visit(prop.expr);
+ return prop;
+};
+
+/**
+ * Visit Expression.
+ */
+
+Normalizer.prototype.visitExpression = function(expr){
+ expr.nodes = expr.nodes.map(function(node){
+ // returns `block` literal if mixin's block
+ // is used as part of a property value
+ if ('block' == node.nodeName) {
+ var literal = new nodes.Literal('block');
+ literal.lineno = expr.lineno;
+ literal.column = expr.column;
+ return literal;
+ }
+ return node;
+ });
+ return expr;
+};
+
+/**
+ * Visit Block.
+ */
+
+Normalizer.prototype.visitBlock = function(block){
+ var node;
+
+ if (block.hasProperties) {
+ for (var i = 0, len = block.nodes.length; i < len; ++i) {
+ node = block.nodes[i];
+ switch (node.nodeName) {
+ case 'null':
+ case 'expression':
+ case 'function':
+ case 'group':
+ case 'unit':
+ case 'atblock':
+ continue;
+ default:
+ block.nodes[i] = this.visit(node);
+ }
+ }
+ }
+
+ // nesting
+ for (var i = 0, len = block.nodes.length; i < len; ++i) {
+ node = block.nodes[i];
+ block.nodes[i] = this.visit(node);
+ }
+
+ return block;
+};
+
+/**
+ * Visit Group.
+ */
+
+Normalizer.prototype.visitGroup = function(group){
+ var stack = this.stack
+ , map = this.map
+ , parts;
+
+ // normalize interpolated selectors with comma
+ group.nodes.forEach(function(selector, i){
+ if (!~selector.val.indexOf(',')) return;
+ if (~selector.val.indexOf('\\,')) {
+ selector.val = selector.val.replace(/\\,/g, ',');
+ return;
+ }
+ parts = selector.val.split(',');
+ var root = '/' == selector.val.charAt(0)
+ , part, s;
+ for (var k = 0, len = parts.length; k < len; ++k){
+ part = parts[k].trim();
+ if (root && k > 0 && !~part.indexOf('&')) {
+ part = '/' + part;
+ }
+ s = new nodes.Selector([new nodes.Literal(part)]);
+ s.val = part;
+ s.block = group.block;
+ group.nodes[i++] = s;
+ }
+ });
+ stack.push(group.nodes);
+
+ var selectors = utils.compileSelectors(stack, true);
+
+ // map for extension lookup
+ selectors.forEach(function(selector){
+ map[selector] = map[selector] || [];
+ map[selector].push(group);
+ });
+
+ // extensions
+ this.extend(group, selectors);
+
+ stack.pop();
+ return group;
+};
+
+/**
+ * Visit Function.
+ */
+
+Normalizer.prototype.visitFunction = function(){
+ return nodes.null;
+};
+
+/**
+ * Visit Media.
+ */
+
+Normalizer.prototype.visitMedia = function(media){
+ var medias = []
+ , group = this.closestGroup(media.block)
+ , parent;
+
+ function mergeQueries(block) {
+ block.nodes.forEach(function(node, i){
+ switch (node.nodeName) {
+ case 'media':
+ node.val = media.val.merge(node.val);
+ medias.push(node);
+ block.nodes[i] = nodes.null;
+ break;
+ case 'block':
+ mergeQueries(node);
+ break;
+ default:
+ if (node.block && node.block.nodes)
+ mergeQueries(node.block);
+ }
+ });
+ }
+
+ mergeQueries(media.block);
+ this.bubble(media);
+
+ if (medias.length) {
+ medias.forEach(function(node){
+ if (group) {
+ group.block.push(node);
+ } else {
+ this.root.nodes.splice(++this.rootIndex, 0, node);
+ }
+ node = this.visit(node);
+ parent = node.block.parent;
+ if (node.bubbled && (!group || 'group' == parent.node.nodeName)) {
+ node.group.block = node.block.nodes[0].block;
+ node.block.nodes[0] = node.group;
+ }
+ }, this);
+ }
+ return media;
+};
+
+/**
+ * Visit Supports.
+ */
+
+Normalizer.prototype.visitSupports = function(node){
+ this.bubble(node);
+ return node;
+};
+
+/**
+ * Visit Atrule.
+ */
+
+Normalizer.prototype.visitAtrule = function(node){
+ if (node.block) node.block = this.visit(node.block);
+ return node;
+};
+
+/**
+ * Visit Keyframes.
+ */
+
+Normalizer.prototype.visitKeyframes = function(node){
+ var frames = node.block.nodes.filter(function(frame){
+ return frame.block && frame.block.hasProperties;
+ });
+ node.frames = frames.length;
+ return node;
+};
+
+/**
+ * Visit Import.
+ */
+
+Normalizer.prototype.visitImport = function(node){
+ this.imports.push(node);
+ return this.hoist ? nodes.null : node;
+};
+
+/**
+ * Visit Charset.
+ */
+
+Normalizer.prototype.visitCharset = function(node){
+ this.charset = node;
+ return this.hoist ? nodes.null : node;
+};
+
+/**
+ * Apply `group` extensions.
+ *
+ * @param {Group} group
+ * @param {Array} selectors
+ * @api private
+ */
+
+Normalizer.prototype.extend = function(group, selectors){
+ var map = this.map
+ , self = this
+ , parent = this.closestGroup(group.block);
+
+ group.extends.forEach(function(extend){
+ var groups = map[extend.selector];
+ if (!groups) {
+ if (extend.optional) return;
+ var err = new Error('Failed to @extend "' + extend.selector + '"');
+ err.lineno = extend.lineno;
+ err.column = extend.column;
+ throw err;
+ }
+ selectors.forEach(function(selector){
+ var node = new nodes.Selector;
+ node.val = selector;
+ node.inherits = false;
+ groups.forEach(function(group){
+ // prevent recursive extend
+ if (!parent || (parent != group)) self.extend(group, selectors);
+ group.push(node);
+ });
+ });
+ });
+
+ group.block = this.visit(group.block);
+};