parser.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. var assert = require("assert");
  2. var types = require("./types");
  3. var n = types.namedTypes;
  4. var b = types.builders;
  5. var isObject = types.builtInTypes.object;
  6. var isArray = types.builtInTypes.array;
  7. var isFunction = types.builtInTypes.function;
  8. var Patcher = require("./patcher").Patcher;
  9. var normalizeOptions = require("./options").normalize;
  10. var fromString = require("./lines").fromString;
  11. var attachComments = require("./comments").attach;
  12. var util = require("./util");
  13. exports.parse = function parse(source, options) {
  14. options = normalizeOptions(options);
  15. var lines = fromString(source, options);
  16. var sourceWithoutTabs = lines.toString({
  17. tabWidth: options.tabWidth,
  18. reuseWhitespace: false,
  19. useTabs: false
  20. });
  21. var comments = [];
  22. var program = options.parser.parse(sourceWithoutTabs, {
  23. jsx: true,
  24. loc: true,
  25. locations: true,
  26. range: options.range,
  27. comment: true,
  28. onComment: comments,
  29. tolerant: options.tolerant,
  30. ecmaVersion: 6,
  31. sourceType: 'module'
  32. });
  33. // If the source was empty, some parsers give loc.{start,end}.line
  34. // values of 0, instead of the minimum of 1.
  35. util.fixFaultyLocations(program, lines);
  36. program.loc = program.loc || {
  37. start: lines.firstPos(),
  38. end: lines.lastPos()
  39. };
  40. program.loc.lines = lines;
  41. program.loc.indent = 0;
  42. // Expand the Program node's .loc to include all comments, since
  43. // typically its .loc.start and .loc.end will coincide with those of the
  44. // first and last statements, respectively, excluding any comments that
  45. // fall outside that region.
  46. var trueProgramLoc = util.getTrueLoc(program, lines);
  47. program.loc.start = trueProgramLoc.start;
  48. program.loc.end = trueProgramLoc.end;
  49. if (program.comments) {
  50. comments = program.comments;
  51. delete program.comments;
  52. }
  53. // In order to ensure we reprint leading and trailing program comments,
  54. // wrap the original Program node with a File node.
  55. var file = program;
  56. if (file.type === "Program") {
  57. var file = b.file(program, options.sourceFileName || null);
  58. file.loc = {
  59. lines: lines,
  60. indent: 0,
  61. start: lines.firstPos(),
  62. end: lines.lastPos()
  63. };
  64. } else if (file.type === "File") {
  65. program = file.program;
  66. }
  67. // Passing file.program here instead of just file means that initial
  68. // comments will be attached to program.body[0] instead of program.
  69. attachComments(
  70. comments,
  71. program.body.length ? file.program : file,
  72. lines
  73. );
  74. // Return a copy of the original AST so that any changes made may be
  75. // compared to the original.
  76. return new TreeCopier(lines).copy(file);
  77. };
  78. function TreeCopier(lines) {
  79. assert.ok(this instanceof TreeCopier);
  80. this.lines = lines;
  81. this.indent = 0;
  82. }
  83. var TCp = TreeCopier.prototype;
  84. TCp.copy = function(node) {
  85. if (isArray.check(node)) {
  86. return node.map(this.copy, this);
  87. }
  88. if (!isObject.check(node)) {
  89. return node;
  90. }
  91. util.fixFaultyLocations(node, this.lines);
  92. var copy = Object.create(Object.getPrototypeOf(node), {
  93. original: { // Provide a link from the copy to the original.
  94. value: node,
  95. configurable: false,
  96. enumerable: false,
  97. writable: true
  98. }
  99. });
  100. var loc = node.loc;
  101. var oldIndent = this.indent;
  102. var newIndent = oldIndent;
  103. if (loc) {
  104. // When node is a comment, we set node.loc.indent to
  105. // node.loc.start.column so that, when/if we print the comment by
  106. // itself, we can strip that much whitespace from the left margin of
  107. // the comment. This only really matters for multiline Block comments,
  108. // but it doesn't hurt for Line comments.
  109. if (node.type === "Block" || node.type === "Line" ||
  110. node.type === "CommentBlock" || node.type === "CommentLine" ||
  111. this.lines.isPrecededOnlyByWhitespace(loc.start)) {
  112. newIndent = this.indent = loc.start.column;
  113. }
  114. loc.lines = this.lines;
  115. loc.indent = newIndent;
  116. }
  117. var keys = Object.keys(node);
  118. var keyCount = keys.length;
  119. for (var i = 0; i < keyCount; ++i) {
  120. var key = keys[i];
  121. if (key === "loc") {
  122. copy[key] = node[key];
  123. } else if (key === "tokens" &&
  124. node.type === "File") {
  125. // Preserve file.tokens (uncopied) in case client code cares about
  126. // it, even though Recast ignores it when reprinting.
  127. copy[key] = node[key];
  128. } else {
  129. copy[key] = this.copy(node[key]);
  130. }
  131. }
  132. this.indent = oldIndent;
  133. return copy;
  134. };