function transform(ast, opts) {
  if (opts !== BYPASS_OPTIONS) {
    _options.set(opts);
  }
  _shouldRemoveTrailingWs = Boolean(_options.get('whiteSpace.removeTrailing'));

  plugins.transformBefore(ast);

  _tk.eachInBetween(ast.startToken, ast.endToken, preprocessToken);
  rocambole.moonwalk(ast, transformNode);
  _tk.eachInBetween(ast.startToken, ast.endToken, postprocessToken);
  _br.limitBeforeEndOfFile(ast);

  // indent should come after all other transformations since it depends on
  // line breaks caused by "parent" nodes, otherwise it will cause conflicts.
  // it should also happen after the postprocessToken since it adds line breaks
  // before/after comments and that changes the indent logic
  indent.transform(ast);

  // plugin transformation comes after the indentation since we assume user
  // knows what he is doing (will increase flexibility and allow plugin to
  // override the indentation logic)
  // we have an alias "transform" to match v0.3 API, but favor `transformAfter`
  // moving forward. (we might deprecate "transform" in the future)
  plugins.transform(ast);
  plugins.transformAfter(ast);

  return ast;
}
	function stripAsserts(node) {
		// looking only for `libtess.assert` calls
		if (node.type !== 'CallExpression' ||
			node.callee.type !== 'MemberExpression' ||
			node.callee.property.name !== 'assert' ||
			node.callee.object.type !== 'Identifier' ||
			node.callee.object.name !== PACKAGE_NAME) {
			return;
		}

		// need to expand [startToken, endToken] to include beginning whitespace and
		// ending `;\n`
		var startToken = node.startToken;
		if (startToken.prev.type === 'WhiteSpace') {
			startToken = startToken.prev;
		}
		var endToken = node.endToken;
		if (endToken.next.value === ';' &&
			endToken.next.next.type === 'LineBreak') {
			endToken = endToken.next.next;
		}

		// if removing the assert is going to leave two blank lines, remove one
		if (startToken.prev.prev.type === 'LineBreak' &&
			endToken.next.type === 'LineBreak') {
			endToken = endToken.next;
		}

		// because our lint rules require things like always using curly braces, we
		// can safely remove libtess.assert(...) calls without replacing them with
		// `void 0` or the like.
		rocamboleToken.eachInBetween(startToken, endToken, rocamboleToken.remove);
		removedAsserts++;
	}
function countBrInBetween(start, end) {
  var count = 0;
  _tk.eachInBetween(start, end, function(token) {
    if (_tk.isBr(token)) count++;
  });
  return count;
}
function expectedLength(node) {
  var length = 0;

  var startOfTheLine = _tk.findPrev(node.startToken, 'LineBreak');

  // No linebreak indicates first line of the file, find first token instead
  if (!startOfTheLine) {
    startOfTheLine = _tk.findPrev(node.startToken, function(token) {
      return !token.prev;
    });
  }
  var firstChar = _tk.findNextNonEmpty(startOfTheLine);

  // Need to take into consideration the indent
  _tk.eachInBetween(startOfTheLine, firstChar, function(token) {
    length += String(token.raw || token.value).length;
  });

  var prev;
  _tk.eachInBetween(firstChar, node.endToken, function(token) {
    if (_tk.isEmpty(token)) {

      // Empty tokens are "collapsed" (multiple linebreaks/whitespace becomes
      // a single whitespace)
      length += _tk.isEmpty(prev) ? 0 : 1;
      prev = token;
      return;
    }

    // Don't collapse objects with line comments; block comments should be okay
    if (token.type === 'LineComment') {
      length += 1000;
    }
    length += String(token.raw || token.value).length;
    prev = token;
  });

  if (length === 0) {
    throw new Error('Failed to measure length of object expression: ' + node.toString());
  }

  return length;
}
Example #5
0
function transform(ast, opts) {
  _options.set(opts);
  _shouldRemoveTrailingWs = Boolean(_options.get('whiteSpace.removeTrailing'));

  _tk.eachInBetween(ast.startToken, ast.endToken, preprocessToken);
  rocambole.moonwalk(ast, transformNode);
  _tk.eachInBetween(ast.startToken, ast.endToken, postprocessToken);

  // indent should come after all other transformations since it depends on
  // line breaks caused by "parent" nodes, otherwise it will cause conflicts.
  // it should also happen after the postprocessToken since it adds line breaks
  // before/after comments and that changes the indent logic
  indent.transform(ast);

  if (process.env.LOG_TOKENS) {
    _ast.logTokens(ast);
  }

  return ast;
}
// Below from https://gist.github.com/jzaefferer/23bef744ffea751b2668
// Copyright Jörn Zaefferer; licensed MIT
function collapse(node) {
  // This one seems short
  _tk.eachInBetween(node.startToken, node.endToken, function(token) {
    if (_tk.isBr(token)) {

      // Insert one blank to replace the line break
      _tk.before(token, {
        type: 'WhiteSpace',
        value: ' '
      });

      // Remove all whitespace/indent after the line break
      var next = token.next;
      while (_tk.isEmpty(next)) {
        _tk.remove(next);
        next = next.next;
      }

      // Remove the line break itself
      _tk.remove(token);
    }
  });
}
	removeTask: function(task) {
		var taskObj = task ? this.tasks[task] : null;

		if ( !taskObj ) {
			return this;
		}

		// We also need to remove extra whitespace and comma
		var next = taskObj.node.endToken.next;
		while ( next.type === 'Punctuator' || next.type === 'LineBreak' || next.type === 'WhiteSpace' ) {
			next = next.next;
		}
		var end = next.prev;

		// Remove everything
		tk.eachInBetween(taskObj.node.startToken, end, tk.remove);

		if ( this.autosave ) {
			this.save();
		}

		this.reparse();
		return this;
	},
exports._transformNode = function (node) {
  // If the token is not a variable declaration (e.g. `var`, `let`), exit early
  // https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API
  // interface VariableDeclaration <: Declaration {
  //     type: "VariableDeclaration";
  //     declarations: [ VariableDeclarator ];
  //     kind: "var" | "let" | "const";
  // }
  // interface VariableDeclarator <: Node {
  //   type: "VariableDeclarator";
  //   id: Pattern;
  //   init: Expression | null;
  // }
  if (node.type !== 'VariableDeclaration') {
    return node;
  }

  // If we are inside of a loop, do nothing (e.g. `for`, `while`, `do ... while`)
  // DEV: Technically, a while/dowhile can't have a `var` but this is for good measure
  var parentType = node.parent ? node.parent.type : '';
  if (parentType.match(/WhileStatement|DoWhileStatement|ForStatement|ForInStatement/)) {
    return node;
  }

  // Determine the terminating character
  // Example: `var foo = bar;`
  //   varDeclaration = {type: VariableDeclaration, declarations: [...], kind: 'var'}
  //   declarators[*] = {type: VariableDeclarator, id: {type: Identifier, name: 'foo'}, init: {type: Literal, value: 'bar'}}
  var varDeclaration = node;
  var declarators = varDeclaration.declarations;

  // Find the head and tail of the var declaration for reuse among its declaration clones
  // e.g. `var hello = 'world', goodbye = 'moon';` -> ['var', ' '] = starting tokens; ['hello = world'] = declaration; ...; [';'] = endToken
  var startingTokens = [];
  rocamboleToken.eachInBetween(varDeclaration.startToken, declarators[0].startToken.prev, function saveToken (token) {
    startingTokens.push(token);
  });
  // Determine whether we use automatic semicolon insertion or not
  var endingSemicolonToken = rocamboleToken.findNext(varDeclaration.endToken.prev, function findStatementTerminator (token) {
    return rocamboleToken.isSemiColon(token) || rocamboleToken.isBr(token);
  });
  if (rocamboleToken.isBr(endingSemicolonToken)) {
    endingSemicolonToken = null;
  }

  // Additionally, find the whitespace tokens before our `var` started (e.g. all indents/whitespace)
  var preStartingTokens = [];
  var token = varDeclaration.startToken.prev;
  while (token) {
    // If the token is whitespace or an indent, save it
    // https://github.com/millermedeiros/rocambole-token/blob/fc03674b38f288dc545db0a5b2bdfd2d96cab170/is.js#L19-L25
    if (token.type === 'WhiteSpace' || token.type === 'Indent') {
      preStartingTokens.unshift(token);
      token = token.prev;
    // Otherwise, stop
    // DEV: We ignore line breaks because this could be the start of a program
    //   Also, line breaks can lead to weird edge cases so we keep it consistent/predictable with a single one
    } else {
      break;
    }
  }

  // Copy over the preStartingTokens as betweenDeclarationTokens and add in `;` (if applicable) and `\n`
  // DEV: We add from the left of the queue so `\n` then `;` to get `[';', '\n', ' ']`
  var betweenDeclarationTokens = preStartingTokens.slice();
  betweenDeclarationTokens.unshift(exports.createToken({
    type: 'LineBreak',
    value: options.lineBreak.value,
    root: varDeclaration.startToken.root
  }));
  if (endingSemicolonToken) {
    betweenDeclarationTokens.unshift(exports.cloneToken(endingSemicolonToken));
  }

  // Generate a `var` for each of the declarators
  // e.g. `var hello = 'world', goodbye = 'moon';` -> `var hello = 'world'; var goodbye = 'moon';`
  var declarations = declarators.map(function generateDeclaration (declarator, index) {
    // DEV: A brief refresher on nodes and tokens
    //   Nodes are the AST representation of parts of a program (e.g. Identifier, VariableDeclaration)
    //   Tokens are the actual chunks of code these represent (e.g. Keyword, WhiteSpace)
    //   Tokens can be present without there being a node related to them
    //   Nodes have a prev (previous node on the same level), next (next node on the same level),
    //     parent (node containing our node), and sometimes something like a `body` key where they declare child nodes
    //     `body` varies from node type to node type
    //   Tokens don't have levels but are one giant chain
    //   Tokens have next (next token to render), prev (previous token to render),
    //     root (root node of the entire token chain -- i.e. a Program node)
    //   Nodes also have startToken and endToken which are the tokens that a node will start/end on
    //     (e.g. `var` is the start token for a VariableDeclaration)
    //   The only attachment from tokens to nodes is via `range` but this is brittle in rocambole so avoid it

    // Generate a new declaration similar to the original
    // Example: `var hello = 'world', goodbye = 'moon';` should use `var` and have a trailing semicolon `;`
    // https://github.com/millermedeiros/rocambole/blob/a3d0d63d58b769d13bad288aca32c6e2f7766542/rocambole.js#L69-L74
    var declaration = {
      type: varDeclaration.type, // should always be `VariableDeclaration`
      declarations: [declarator],
      kind: varDeclaration.kind, // (e.g. `var`, `let`)
      toString: varDeclaration.toString
      // prev: bound later
      // next: bound later
      // startToken: bound later
      // endToken: bound later
    };
    return declaration;
  });

  // Set up linkages for nodes
  // DEV: None of these changes will affect the token chain
  //   However, each `node.toString()` is more/less impractical as there are no tokens bound to declarations
  declarations.forEach(function connectNodes (declaration, index) {
    // Attach declaration as the declarator's parent node
    var declarator = declaration.declarations[0];
    declarator.parent = declaration;

    // If this is the first node, connect to var declaration's previous node
    if (index === 0) {
      var varDeclarationPrevNode = varDeclaration.prev;
      if (varDeclarationPrevNode) {
        declaration.prev = varDeclarationPrevNode;
        varDeclarationPrevNode.next = declaration;
      }
    // Otherwise, connect to the last declaration
    } else {
      var lastDeclarationNode = declarations[index - 1];
      declaration.prev = lastDeclarationNode;
      lastDeclarationNode.next = declaration;
    }

    // If this is the last node, connect it to var declaration's next node
    if (index === declarations.length - 1) {
      var varDeclarationNextNode = varDeclaration.next;
      if (varDeclarationNextNode) {
        declaration.next = varDeclarationNextNode;
        varDeclarationNextNode.prev = declaration;
      }
    // Otherwise, do nothing as we will connect to the next node via the previous if/else
    } else {
      // Do nothing
    }

    // In all cases, save this var declaration's parent node as this declaration node's parent
    declaration.parent = varDeclaration.parent;
  });

  // Swap the declarations in the `body` of the parent block statement
  // e.g. `BlockStatement.body = [{orig VariableDeclaration}, some other expressions]`
  //     -> `BlockStatement.body = [{new VariableDeclaration}, {another new VariableDeclaration}, some other expressions]`
  var varDeclarationParentNode = varDeclaration.parent;
  // DEV: Our `body` can be `body` in `Program` or `BlockStatement` but is `consequent` for `SwitchCase's`
  //   https://github.com/estree/estree/blob/9a35a3d091af6ff9cf7fadfe27c49e22533b2bce/spec.md#blockstatement
  //   https://github.com/estree/estree/blob/9a35a3d091af6ff9cf7fadfe27c49e22533b2bce/spec.md#switchcase
  var varDeclarationParentBody = varDeclarationParentNode.body || varDeclarationParentNode.consequent;
  var varDeclarationParentBodyIndex = varDeclarationParentBody.indexOf(varDeclaration);
  var spliceArgs = [varDeclarationParentBodyIndex, 1].concat(declarations);
  varDeclarationParentBody.splice.apply(varDeclarationParentBody, spliceArgs);

  // Handle token bindings (aka the annoying/hard part)
  var queue = [];
  declarations.forEach(function defineAndAttachTokens (declaration, index) {
    // DEV: We have a few linkages to perform:
    //   Example: HEAD; var a = 1, b = 2; TAIL
    //     VariableDeclaration tokens = ['var', ' ', 'a', ' ', '=', ..., ';']
    //     VariableDeclarator tokens = ['a', ' ', '=', ..., '1']
    var declarator = declaration.declarations[0];

    // If this is the first VariableDeclaration
    if (index === 0) {
      // Define STARTING tokens for each VariableDeclaration (e.g. `var ` for `var a = 1`)
      // DEV: `varDeclaration.startToken` is already linked with all previous tokens in the application, making this transition easy
      // DEV: `varDeclaration.startToken` will be the `var` of `var ` (i.e. `['var', ' ']`, it's the `'var'`)
      var firstStartingToken = varDeclaration.startToken;
      var lastStartingToken = null;

      // Save ORIGINAL VariableDeclaration token (which links to HEAD) AS FIRST VariableDeclaration token (e.g. reuse the existing `var ` token chain)
      declaration.startToken = firstStartingToken;
    // Otherwise, (we are a non-first VariableDeclaration)
    } else {
      // Insert leading content for each non-first VariableDeclaration between VariableDeclaration's
      // Create `var ` tokens
      var newStartingTokens = exports.cloneTokenChain(startingTokens);
      // DEV: This is always defined as we always need a `var` keyword
      var firstStartingToken = newStartingTokens[0];
      var lastStartingToken = newStartingTokens[newStartingTokens.length - 1];

      // Save firstStartingToken and lastStartingToken for later
      declaration.firstStartingToken = firstStartingToken;
      declaration.lastStartingToken = lastStartingToken;

      // Attach FIRST `var ` token TO last VariableDeclaration END token
      //   and attach FIRST `var ` token AS current VariableDeclaration START token
      var lastDeclaration = declarations[index - 1];
      lastDeclaration.endToken.next = firstStartingToken;
      firstStartingToken.prev = lastDeclaration.endToken;
      declaration.startToken = firstStartingToken;

      // Attach LAST `var ` token TO FIRST VariableDeclarator token AS PREVIOUS token
      declarator.startToken.prev = lastStartingToken;
      lastStartingToken.next = declarator.startToken;
    }

    // If this is a non-last VariableDeclaration
    if (index < declarations.length - 1) {
      // Define ENDING tokens for each VariableDeclaration (e.g. `;\n  ` for `var a = 1;\n  `)
      // Insert terminating content for each VariableDeclaration between VariableDeclaration's (e.g. `;\n  `)
      // Create `;\n  ` tokens
      var newBetweenTokens = exports.cloneTokenChain(betweenDeclarationTokens);
      var firstBetweenToken = newBetweenTokens[0];
      var lastBetweenToken = newBetweenTokens[newBetweenTokens.length - 1];

      // Attach LAST `;\n  ` token TO current VariableDeclaration AS END token
      //   and attach LAST `;\n  ` token to next VariableDeclaration AS PREVIOUS token
      declaration.endToken = lastBetweenToken;
      var nextDeclaration = declarations[index + 1];
      // DEV: We need to queue these actions since `nextDeclaration.startToken` doesn't exist yet =/
      // QUEUE: nextDeclaration.startToken.prev = lastBetweenToken;
      // QUEUE: lastBetweenToken.next = nextDeclaration.startToken;
      queue.push({
        srcNode: nextDeclaration,
        srcTokenKey: 'startToken',
        srcDirectionKey: 'prev',
        targetToken: lastBetweenToken,
        targetDirectionKey: 'next'
      });

      // Attach FIRST `;\n ` token TO LAST VariableDeclarator token AS NEXT token
      declarator.endToken.next = firstBetweenToken;
      firstBetweenToken.prev = declarator.endToken;
    // Otherwise, (this is the last VariableDeclaration)
    } else {
      // DEV: `lastDeclarator.endToken.next` is already linked with all previous tokens in the application, making this transition easy
      // DEV: `lastDeclarator.endToken.next` will be the `;` of `;\n  ` (i.e. `[';', '\n', '  ']`, it's the `';'`)
      var lastDeclarator = varDeclaration.declarations[index];
      var firstEndingToken = lastDeclarator.endToken.next;
      var lastEndingToken = null;

      // If there is no first ending token, then we are at the end of the program (e.g. `var a = 1EOF`)
      if (!firstEndingToken) {
        // Save the same `lastDeclarator.endToken` as our `declaration.endToken` for consistency
        // DEV: `lastDeclarator.endToken` is already bound to `Program.endToken` as this was the original setup
        declarator.endToken = lastDeclarator.endToken;
      // Save ORIGINAL VariableDeclaration token (which links to TAIL) AS START token for  VariableDeclaration END token (e.g. reuse the existing `var ` token chain)
      } else {
        declaration.endToken = firstEndingToken;
      }
    }
  });

  // Walk over our queued actions and bind them
  queue.forEach(function saveQueueAction (action) {
    var srcNode = action.srcNode;
    var srcToken = srcNode[action.srcTokenKey];
    var targetToken = action.targetToken;
    srcToken[action.srcDirectionKey] = targetToken;
    targetToken[action.targetDirectionKey] = srcToken;
  });

  // Return the updated node
  return node;
};
exports.stringBefore = function(str) {
  rocambole.parseFn = espree.parse.bind(espree);
  var ast = rocambole.parse(str, parseOptions);
  tk.eachInBetween(ast.startToken, ast.endToken, processToken);
  return ast.toString();
};
Example #10
0
function removeNode(node) {
	token.eachInBetween(node.startToken, node.endToken, token.remove);
}