react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / node_modules / commoner / node_modules / recast / lib / patcher.js
81169 viewsvar assert = require("assert");1var linesModule = require("./lines");2var types = require("./types");3var getFieldValue = types.getFieldValue;4var Node = types.namedTypes.Node;5var Expression = types.namedTypes.Expression;6var SourceLocation = types.namedTypes.SourceLocation;7var util = require("./util");8var comparePos = util.comparePos;9var FastPath = require("./fast-path");10var isObject = types.builtInTypes.object;11var isArray = types.builtInTypes.array;12var isString = types.builtInTypes.string;1314function Patcher(lines) {15assert.ok(this instanceof Patcher);16assert.ok(lines instanceof linesModule.Lines);1718var self = this,19replacements = [];2021self.replace = function(loc, lines) {22if (isString.check(lines))23lines = linesModule.fromString(lines);2425replacements.push({26lines: lines,27start: loc.start,28end: loc.end29});30};3132self.get = function(loc) {33// If no location is provided, return the complete Lines object.34loc = loc || {35start: { line: 1, column: 0 },36end: { line: lines.length,37column: lines.getLineLength(lines.length) }38};3940var sliceFrom = loc.start,41toConcat = [];4243function pushSlice(from, to) {44assert.ok(comparePos(from, to) <= 0);45toConcat.push(lines.slice(from, to));46}4748replacements.sort(function(a, b) {49return comparePos(a.start, b.start);50}).forEach(function(rep) {51if (comparePos(sliceFrom, rep.start) > 0) {52// Ignore nested replacement ranges.53} else {54pushSlice(sliceFrom, rep.start);55toConcat.push(rep.lines);56sliceFrom = rep.end;57}58});5960pushSlice(sliceFrom, loc.end);6162return linesModule.concat(toConcat);63};64}65exports.Patcher = Patcher;6667exports.getReprinter = function(path) {68assert.ok(path instanceof FastPath);6970// Make sure that this path refers specifically to a Node, rather than71// some non-Node subproperty of a Node.72var node = path.getValue();73if (!Node.check(node))74return;7576var orig = node.original;77var origLoc = orig && orig.loc;78var lines = origLoc && origLoc.lines;79var reprints = [];8081if (!lines || !findReprints(path, reprints))82return;8384return function(print) {85var patcher = new Patcher(lines);8687reprints.forEach(function(reprint) {88var old = reprint.oldNode;89SourceLocation.assert(old.loc, true);90patcher.replace(91old.loc,92print(reprint.newPath).indentTail(old.loc.indent)93);94});9596return patcher.get(origLoc).indentTail(-orig.loc.indent);97};98};99100function findReprints(newPath, reprints) {101var newNode = newPath.getValue();102Node.assert(newNode);103104var oldNode = newNode.original;105Node.assert(oldNode);106107assert.deepEqual(reprints, []);108109if (newNode.type !== oldNode.type) {110return false;111}112113var oldPath = new FastPath(oldNode);114var canReprint = findChildReprints(newPath, oldPath, reprints);115116if (!canReprint) {117// Make absolutely sure the calling code does not attempt to reprint118// any nodes.119reprints.length = 0;120}121122return canReprint;123}124125function findAnyReprints(newPath, oldPath, reprints) {126var newNode = newPath.getValue();127var oldNode = oldPath.getValue();128129if (newNode === oldNode)130return true;131132if (isArray.check(newNode))133return findArrayReprints(newPath, oldPath, reprints);134135if (isObject.check(newNode))136return findObjectReprints(newPath, oldPath, reprints);137138return false;139}140141function findArrayReprints(newPath, oldPath, reprints) {142var newNode = newPath.getValue();143var oldNode = oldPath.getValue();144isArray.assert(newNode);145var len = newNode.length;146147if (!(isArray.check(oldNode) &&148oldNode.length === len))149return false;150151for (var i = 0; i < len; ++i) {152newPath.stack.push(i, newNode[i]);153oldPath.stack.push(i, oldNode[i]);154var canReprint = findAnyReprints(newPath, oldPath, reprints);155newPath.stack.length -= 2;156oldPath.stack.length -= 2;157if (!canReprint) {158return false;159}160}161162return true;163}164165function findObjectReprints(newPath, oldPath, reprints) {166var newNode = newPath.getValue();167isObject.assert(newNode);168169if (newNode.original === null) {170// If newNode.original node was set to null, reprint the node.171return false;172}173174var oldNode = oldPath.getValue();175if (!isObject.check(oldNode))176return false;177178if (Node.check(newNode)) {179if (!Node.check(oldNode)) {180return false;181}182183if (!oldNode.loc) {184// If we have no .loc information for oldNode, then we won't185// be able to reprint it.186return false;187}188189// Here we need to decide whether the reprinted code for newNode190// is appropriate for patching into the location of oldNode.191192if (newNode.type === oldNode.type) {193var childReprints = [];194195if (findChildReprints(newPath, oldPath, childReprints)) {196reprints.push.apply(reprints, childReprints);197} else {198reprints.push({199oldNode: oldNode,200newPath: newPath.copy()201});202}203204return true;205}206207if (Expression.check(newNode) &&208Expression.check(oldNode)) {209210// If both nodes are subtypes of Expression, then we should be211// able to fill the location occupied by the old node with212// code printed for the new node with no ill consequences.213reprints.push({214oldNode: oldNode,215newPath: newPath.copy()216});217218return true;219}220221// The nodes have different types, and at least one of the types222// is not a subtype of the Expression type, so we cannot safely223// assume the nodes are syntactically interchangeable.224return false;225}226227return findChildReprints(newPath, oldPath, reprints);228}229230// This object is reused in hasOpeningParen and hasClosingParen to avoid231// having to allocate a temporary object.232var reusablePos = { line: 1, column: 0 };233234function hasOpeningParen(oldPath) {235var oldNode = oldPath.getValue();236var loc = oldNode.loc;237var lines = loc && loc.lines;238239if (lines) {240var pos = reusablePos;241pos.line = loc.start.line;242pos.column = loc.start.column;243244while (lines.prevPos(pos)) {245var ch = lines.charAt(pos);246247if (ch === "(") {248// If we found an opening parenthesis but it occurred before249// the start of the original subtree for this reprinting, then250// we must not return true for hasOpeningParen(oldPath).251return comparePos(oldPath.getRootValue().loc.start, pos) <= 0;252}253254if (ch !== " ") {255return false;256}257}258}259260return false;261}262263function hasClosingParen(oldPath) {264var oldNode = oldPath.getValue();265var loc = oldNode.loc;266var lines = loc && loc.lines;267268if (lines) {269var pos = reusablePos;270pos.line = loc.end.line;271pos.column = loc.end.column;272273do {274var ch = lines.charAt(pos);275276if (ch === ")") {277// If we found a closing parenthesis but it occurred after the278// end of the original subtree for this reprinting, then we279// must not return true for hasClosingParen(oldPath).280return comparePos(pos, oldPath.getRootValue().loc.end) <= 0;281}282283if (ch !== " ") {284return false;285}286287} while (lines.nextPos(pos));288}289290return false;291}292293function hasParens(oldPath) {294// This logic can technically be fooled if the node has parentheses295// but there are comments intervening between the parentheses and the296// node. In such cases the node will be harmlessly wrapped in an297// additional layer of parentheses.298return hasOpeningParen(oldPath) && hasClosingParen(oldPath);299}300301function findChildReprints(newPath, oldPath, reprints) {302var newNode = newPath.getValue();303var oldNode = oldPath.getValue();304305isObject.assert(newNode);306isObject.assert(oldNode);307308if (newNode.original === null) {309// If newNode.original node was set to null, reprint the node.310return false;311}312313// If this type of node cannot come lexically first in its enclosing314// statement (e.g. a function expression or object literal), and it315// seems to be doing so, then the only way we can ignore this problem316// and save ourselves from falling back to the pretty printer is if an317// opening parenthesis happens to precede the node. For example,318// (function(){ ... }()); does not need to be reprinted, even though319// the FunctionExpression comes lexically first in the enclosing320// ExpressionStatement and fails the hasParens test, because the321// parent CallExpression passes the hasParens test. If we relied on322// the path.needsParens() && !hasParens(oldNode) check below, the323// absence of a closing parenthesis after the FunctionExpression would324// trigger pretty-printing unnecessarily.325if (!newPath.canBeFirstInStatement() &&326newPath.firstInStatement() &&327!hasOpeningParen(oldPath))328return false;329330// If this node needs parentheses and will not be wrapped with331// parentheses when reprinted, then return false to skip reprinting332// and let it be printed generically.333if (newPath.needsParens(true) && !hasParens(oldPath)) {334return false;335}336337for (var k in util.getUnionOfKeys(newNode, oldNode)) {338if (k === "loc")339continue;340341newPath.stack.push(k, types.getFieldValue(newNode, k));342oldPath.stack.push(k, types.getFieldValue(oldNode, k));343var canReprint = findAnyReprints(newPath, oldPath, reprints);344newPath.stack.length -= 2;345oldPath.stack.length -= 2;346347if (!canReprint) {348return false;349}350}351352return true;353}354355356