text: const defaultRaw = {
      colon:        ': ',
      indent:       '  ',
      commentLeft:  ' ',
      commentRight: ' '
  };
  
  export default class Stringifier {
  
      constructor(builder) {
          this.builder = builder;
      }
  
      stringify(node, semicolon) {
          this[node.type](node, semicolon);
      }
  
      root(node) {
          this.body(node);
          if ( node.raws.after ) this.builder(node.raws.after);
      }
  
      comment(node) {
          let left  = defaultRaw.commentLeft;
          let right = defaultRaw.commentRight;
          if ( this.has(node.raws.left) ) left = node.raws.left;
  
          if ( node.raws.inline ) {
              if ( this.has(node.raws.inlineRight) ) {
                  right = node.raws.inlineRight;
              } else {
                  right = '';
              }
              if ( node.raws.extraIndent ) {
                  this.builder(node.raws.extraIndent);
              }
              this.builder('//' + left + node.text + right, node);
          } else {
              if ( this.has(node.raws.right) ) right = node.raws.right;
              this.builder('/*' + left + node.text + right + '*/', node);
          }
      }
  
      decl(node) {
          let between = node.raws.between || defaultRaw.colon;
          let string  = node.prop + between + this.rawValue(node, 'value');
  
          if ( node.important ) {
              string += node.raws.important || ' !important';
          }
  
          this.builder(string, node);
      }
  
      rule(node) {
          this.block(node, this.rawValue(node, 'selector'));
      }
  
      atrule(node) {
          let name   = '@' + node.name;
          let params = node.params ? this.rawValue(node, 'params') : '';
  
          if ( this.has(node.raws.afterName) ) {
              name += node.raws.afterName;
          } else if ( params ) {
              name += ' ';
          }
  
          this.block(node, name + params);
      }
  
      body(node) {
          let indent = node.root().raws.indent || defaultRaw.indent;
  
          for ( let i = 0; i < node.nodes.length; i++ ) {
              let child  = node.nodes[i];
              let before = child.raws.before.replace(/[^\n]*$/, '') +
                           this.indent(node, indent);
              if ( child.type === 'comment' &&
                   child.raws.before.indexOf('\n') === -1 ) {
                  before = child.raws.before;
              }
              if ( before ) this.builder(before);
              this.stringify(child);
          }
      }
  
      block(node, start) {
          let between = node.raws.sssBetween || '';
          this.builder(start + between, node, 'start');
          if ( this.has(node.nodes) ) this.body(node);
      }
  
      indent(node, step) {
          let result = '';
          while ( node.parent ) {
              result += step;
              node = node.parent;
          }
          return result;
      }
  
      has(value) {
          return typeof value !== 'undefined';
      }
  
      rawValue(node, prop) {
          let value = node[prop];
          let raw   = node.raws[prop];
          if ( raw && raw.value === value ) {
              return raw.sss || raw.raw;
          } else {
              return value;
          }
      }
  
  }
  