espree.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. /**
  2. * @fileoverview Main Espree file that converts Acorn into Esprima output.
  3. *
  4. * This file contains code from the following MIT-licensed projects:
  5. * 1. Acorn
  6. * 2. Babylon
  7. * 3. Babel-ESLint
  8. *
  9. * This file also contains code from Esprima, which is BSD licensed.
  10. *
  11. * Acorn is Copyright 2012-2015 Acorn Contributors (https://github.com/marijnh/acorn/blob/master/AUTHORS)
  12. * Babylon is Copyright 2014-2015 various contributors (https://github.com/babel/babel/blob/master/packages/babylon/AUTHORS)
  13. * Babel-ESLint is Copyright 2014-2015 Sebastian McKenzie <sebmck@gmail.com>
  14. *
  15. * Redistribution and use in source and binary forms, with or without
  16. * modification, are permitted provided that the following conditions are met:
  17. *
  18. * * Redistributions of source code must retain the above copyright
  19. * notice, this list of conditions and the following disclaimer.
  20. * * Redistributions in binary form must reproduce the above copyright
  21. * notice, this list of conditions and the following disclaimer in the
  22. * documentation and/or other materials provided with the distribution.
  23. *
  24. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  25. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27. * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  28. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  29. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  30. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  31. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  32. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  33. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  34. *
  35. * Esprima is Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved.
  36. *
  37. * Redistribution and use in source and binary forms, with or without
  38. * modification, are permitted provided that the following conditions are met:
  39. *
  40. * * Redistributions of source code must retain the above copyright
  41. * notice, this list of conditions and the following disclaimer.
  42. * * Redistributions in binary form must reproduce the above copyright
  43. * notice, this list of conditions and the following disclaimer in the
  44. * documentation and/or other materials provided with the distribution.
  45. *
  46. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  47. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  48. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  49. * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  50. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  51. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  52. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  53. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  54. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  55. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  56. */
  57. /* eslint no-undefined:0, no-use-before-define: 0 */
  58. "use strict";
  59. var astNodeTypes = require("./lib/ast-node-types"),
  60. commentAttachment = require("./lib/comment-attachment"),
  61. TokenTranslator = require("./lib/token-translator"),
  62. acornJSX = require("acorn-jsx/inject"),
  63. rawAcorn = require("acorn");
  64. var acorn = acornJSX(rawAcorn);
  65. var DEFAULT_ECMA_VERSION = 5;
  66. var lookahead,
  67. extra,
  68. lastToken;
  69. /**
  70. * Object.assign polyfill for Node < 4
  71. * @param {Object} target The target object
  72. * @param {...Object} sources Sources for the object
  73. * @returns {Object} `target` after being mutated
  74. */
  75. var assign = Object.assign || function assign(target) {
  76. for (var argIndex = 1; argIndex < arguments.length; argIndex++) {
  77. if (arguments[argIndex] !== null && typeof arguments[argIndex] === "object") {
  78. var keys = Object.keys(arguments[argIndex]);
  79. for (var keyIndex = 0; keyIndex < keys.length; keyIndex++) {
  80. target[keys[keyIndex]] = arguments[argIndex][keys[keyIndex]];
  81. }
  82. }
  83. }
  84. return target;
  85. };
  86. /**
  87. * Resets the extra object to its default.
  88. * @returns {void}
  89. * @private
  90. */
  91. function resetExtra() {
  92. extra = {
  93. tokens: null,
  94. range: false,
  95. loc: false,
  96. comment: false,
  97. comments: [],
  98. tolerant: false,
  99. errors: [],
  100. strict: false,
  101. ecmaFeatures: {},
  102. ecmaVersion: DEFAULT_ECMA_VERSION,
  103. isModule: false
  104. };
  105. }
  106. var tt = acorn.tokTypes,
  107. getLineInfo = acorn.getLineInfo;
  108. // custom type for JSX attribute values
  109. tt.jsxAttrValueToken = {};
  110. /**
  111. * Normalize ECMAScript version from the initial config
  112. * @param {number} ecmaVersion ECMAScript version from the initial config
  113. * @returns {number} normalized ECMAScript version
  114. */
  115. function normalizeEcmaVersion(ecmaVersion) {
  116. if (typeof ecmaVersion === "number") {
  117. var version = ecmaVersion;
  118. // Calculate ECMAScript edition number from official year version starting with
  119. // ES2015, which corresponds with ES6 (or a difference of 2009).
  120. if (version >= 2015) {
  121. version -= 2009;
  122. }
  123. switch (version) {
  124. case 3:
  125. case 5:
  126. case 6:
  127. case 7:
  128. case 8:
  129. case 9:
  130. return version;
  131. default:
  132. throw new Error("Invalid ecmaVersion.");
  133. }
  134. } else {
  135. return DEFAULT_ECMA_VERSION;
  136. }
  137. }
  138. /**
  139. * Determines if a node is valid given the set of ecmaFeatures.
  140. * @param {ASTNode} node The node to check.
  141. * @returns {boolean} True if the node is allowed, false if not.
  142. * @private
  143. */
  144. function isValidNode(node) {
  145. var ecma = extra.ecmaFeatures;
  146. switch (node.type) {
  147. case "ExperimentalSpreadProperty":
  148. case "ExperimentalRestProperty":
  149. return ecma.experimentalObjectRestSpread;
  150. case "ImportDeclaration":
  151. case "ExportNamedDeclaration":
  152. case "ExportDefaultDeclaration":
  153. case "ExportAllDeclaration":
  154. return extra.isModule;
  155. default:
  156. return true;
  157. }
  158. }
  159. /**
  160. * Performs last-minute Esprima-specific compatibility checks and fixes.
  161. * @param {ASTNode} result The node to check.
  162. * @returns {ASTNode} The finished node.
  163. * @private
  164. * @this acorn.Parser
  165. */
  166. function esprimaFinishNode(result) {
  167. // ensure that parsed node was allowed through ecmaFeatures
  168. if (!isValidNode(result)) {
  169. this.unexpected(result.start);
  170. }
  171. // https://github.com/marijnh/acorn/issues/323
  172. if (result.type === "TryStatement") {
  173. delete result.guardedHandlers;
  174. } else if (result.type === "CatchClause") {
  175. delete result.guard;
  176. }
  177. // Acorn doesn't count the opening and closing backticks as part of templates
  178. // so we have to adjust ranges/locations appropriately.
  179. if (result.type === "TemplateElement") {
  180. // additional adjustment needed if ${ is the last token
  181. var terminalDollarBraceL = this.input.slice(result.end, result.end + 2) === "${";
  182. if (result.range) {
  183. result.range[0]--;
  184. result.range[1] += (terminalDollarBraceL ? 2 : 1);
  185. }
  186. if (result.loc) {
  187. result.loc.start.column--;
  188. result.loc.end.column += (terminalDollarBraceL ? 2 : 1);
  189. }
  190. }
  191. // Acorn uses undefined instead of null, which affects serialization
  192. if (result.type === "Literal" && result.value === undefined) {
  193. result.value = null;
  194. }
  195. if (extra.attachComment) {
  196. commentAttachment.processComment(result);
  197. }
  198. if (result.type.indexOf("Function") > -1 && !result.generator) {
  199. result.generator = false;
  200. }
  201. return result;
  202. }
  203. /**
  204. * Determines if a token is valid given the set of ecmaFeatures.
  205. * @param {acorn.Parser} parser The parser to check.
  206. * @returns {boolean} True if the token is allowed, false if not.
  207. * @private
  208. */
  209. function isValidToken(parser) {
  210. var ecma = extra.ecmaFeatures;
  211. var type = parser.type;
  212. switch (type) {
  213. case tt.jsxName:
  214. case tt.jsxText:
  215. case tt.jsxTagStart:
  216. case tt.jsxTagEnd:
  217. return ecma.jsx;
  218. // https://github.com/ternjs/acorn/issues/363
  219. case tt.regexp:
  220. if (extra.ecmaVersion < 6 && parser.value.flags && parser.value.flags.indexOf("y") > -1) {
  221. return false;
  222. }
  223. return true;
  224. default:
  225. return true;
  226. }
  227. }
  228. /**
  229. * Injects esprimaFinishNode into the finishNode process.
  230. * @param {Function} finishNode Original finishNode function.
  231. * @returns {ASTNode} The finished node.
  232. * @private
  233. */
  234. function wrapFinishNode(finishNode) {
  235. return /** @this acorn.Parser */ function(node, type, pos, loc) {
  236. var result = finishNode.call(this, node, type, pos, loc);
  237. return esprimaFinishNode.call(this, result);
  238. };
  239. }
  240. acorn.plugins.espree = function(instance) {
  241. instance.extend("finishNode", wrapFinishNode);
  242. instance.extend("finishNodeAt", wrapFinishNode);
  243. instance.extend("next", function(next) {
  244. return /** @this acorn.Parser */ function() {
  245. if (!isValidToken(this)) {
  246. this.unexpected();
  247. }
  248. return next.call(this);
  249. };
  250. });
  251. // needed for experimental object rest/spread
  252. instance.extend("checkLVal", function(checkLVal) {
  253. return /** @this acorn.Parser */ function(expr, isBinding, checkClashes) {
  254. if (extra.ecmaFeatures.experimentalObjectRestSpread && expr.type === "ObjectPattern") {
  255. for (var i = 0; i < expr.properties.length; i++) {
  256. if (expr.properties[i].type.indexOf("Experimental") === -1) {
  257. this.checkLVal(expr.properties[i].value, isBinding, checkClashes);
  258. }
  259. }
  260. return undefined;
  261. }
  262. return checkLVal.call(this, expr, isBinding, checkClashes);
  263. };
  264. });
  265. instance.extend("parseTopLevel", function(parseTopLevel) {
  266. return /** @this acorn.Parser */ function(node) {
  267. if (extra.ecmaFeatures.impliedStrict && this.options.ecmaVersion >= 5) {
  268. this.strict = true;
  269. }
  270. return parseTopLevel.call(this, node);
  271. };
  272. });
  273. instance.extend("toAssignable", function(toAssignable) {
  274. return /** @this acorn.Parser */ function(node, isBinding, refDestructuringErrors) {
  275. if (extra.ecmaFeatures.experimentalObjectRestSpread &&
  276. node.type === "ObjectExpression"
  277. ) {
  278. node.type = "ObjectPattern";
  279. for (var i = 0; i < node.properties.length; i++) {
  280. var prop = node.properties[i];
  281. if (prop.type === "ExperimentalSpreadProperty") {
  282. prop.type = "ExperimentalRestProperty";
  283. } else if (prop.kind !== "init") {
  284. this.raise(prop.key.start, "Object pattern can't contain getter or setter");
  285. } else {
  286. this.toAssignable(prop.value, isBinding);
  287. }
  288. }
  289. return node;
  290. } else {
  291. return toAssignable.call(this, node, isBinding, refDestructuringErrors);
  292. }
  293. };
  294. });
  295. /**
  296. * Method to parse an object rest or object spread.
  297. * @returns {ASTNode} The node representing object rest or object spread.
  298. * @this acorn.Parser
  299. */
  300. instance.parseObjectRest = function() {
  301. var node = this.startNode();
  302. this.next();
  303. node.argument = this.parseIdent();
  304. if (this.type === tt.comma) {
  305. this.raise(this.start, "Unexpected trailing comma after rest property");
  306. }
  307. return this.finishNode(node, "ExperimentalRestProperty");
  308. };
  309. instance.extend("parseProperty", function(parseProperty) {
  310. /**
  311. * Override `parseProperty` method to parse rest/spread properties.
  312. * @param {boolean} isPattern True if the object is a destructuring pattern.
  313. * @param {Object} refDestructuringErrors ?
  314. * @returns {ASTNode} The node representing a rest/spread property.
  315. * @this acorn.Parser
  316. */
  317. return function(isPattern, refDestructuringErrors) {
  318. if (extra.ecmaFeatures.experimentalObjectRestSpread && this.type === tt.ellipsis) {
  319. var prop;
  320. if (isPattern) {
  321. prop = this.parseObjectRest();
  322. } else {
  323. prop = this.parseSpread();
  324. prop.type = "ExperimentalSpreadProperty";
  325. }
  326. return prop;
  327. }
  328. return parseProperty.call(this, isPattern, refDestructuringErrors);
  329. };
  330. });
  331. instance.extend("checkPropClash", function(checkPropClash) {
  332. /**
  333. * Override `checkPropClash` method to avoid clash on rest/spread properties.
  334. * @param {ASTNode} prop A property node to check.
  335. * @param {Object} propHash Names map.
  336. * @param {Object} refDestructuringErrors Destructuring error information.
  337. * @returns {void}
  338. * @this acorn.Parser
  339. */
  340. return function(prop, propHash, refDestructuringErrors) {
  341. if (prop.type === "ExperimentalRestProperty" || prop.type === "ExperimentalSpreadProperty") {
  342. return;
  343. }
  344. checkPropClash.call(this, prop, propHash, refDestructuringErrors);
  345. };
  346. });
  347. /**
  348. * Overwrites the default raise method to throw Esprima-style errors.
  349. * @param {int} pos The position of the error.
  350. * @param {string} message The error message.
  351. * @throws {SyntaxError} A syntax error.
  352. * @returns {void}
  353. */
  354. instance.raise = instance.raiseRecoverable = function(pos, message) {
  355. var loc = getLineInfo(this.input, pos);
  356. var err = new SyntaxError(message);
  357. err.index = pos;
  358. err.lineNumber = loc.line;
  359. err.column = loc.column + 1; // acorn uses 0-based columns
  360. throw err;
  361. };
  362. /**
  363. * Overwrites the default unexpected method to throw Esprima-style errors.
  364. * @param {int} pos The position of the error.
  365. * @throws {SyntaxError} A syntax error.
  366. * @returns {void}
  367. */
  368. instance.unexpected = function(pos) {
  369. var message = "Unexpected token";
  370. if (pos !== null && pos !== undefined) {
  371. this.pos = pos;
  372. if (this.options.locations) {
  373. while (this.pos < this.lineStart) {
  374. this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1;
  375. --this.curLine;
  376. }
  377. }
  378. this.nextToken();
  379. }
  380. if (this.end > this.start) {
  381. message += " " + this.input.slice(this.start, this.end);
  382. }
  383. this.raise(this.start, message);
  384. };
  385. /*
  386. * Esprima-FB represents JSX strings as tokens called "JSXText", but Acorn-JSX
  387. * uses regular tt.string without any distinction between this and regular JS
  388. * strings. As such, we intercept an attempt to read a JSX string and set a flag
  389. * on extra so that when tokens are converted, the next token will be switched
  390. * to JSXText via onToken.
  391. */
  392. instance.extend("jsx_readString", function(jsxReadString) {
  393. return /** @this acorn.Parser */ function(quote) {
  394. var result = jsxReadString.call(this, quote);
  395. if (this.type === tt.string) {
  396. extra.jsxAttrValueToken = true;
  397. }
  398. return result;
  399. };
  400. });
  401. };
  402. //------------------------------------------------------------------------------
  403. // Tokenizer
  404. //------------------------------------------------------------------------------
  405. /**
  406. * Tokenizes the given code.
  407. * @param {string} code The code to tokenize.
  408. * @param {Object} options Options defining how to tokenize.
  409. * @returns {Token[]} An array of tokens.
  410. * @throws {SyntaxError} If the input code is invalid.
  411. * @private
  412. */
  413. function tokenize(code, options) {
  414. var toString,
  415. tokens,
  416. impliedStrict,
  417. translator = new TokenTranslator(tt, code);
  418. toString = String;
  419. if (typeof code !== "string" && !(code instanceof String)) {
  420. code = toString(code);
  421. }
  422. lookahead = null;
  423. // Options matching.
  424. options = assign({}, options);
  425. var acornOptions = {
  426. ecmaVersion: DEFAULT_ECMA_VERSION,
  427. plugins: {
  428. espree: true
  429. }
  430. };
  431. resetExtra();
  432. // Of course we collect tokens here.
  433. options.tokens = true;
  434. extra.tokens = [];
  435. extra.range = (typeof options.range === "boolean") && options.range;
  436. acornOptions.ranges = extra.range;
  437. extra.loc = (typeof options.loc === "boolean") && options.loc;
  438. acornOptions.locations = extra.loc;
  439. extra.comment = typeof options.comment === "boolean" && options.comment;
  440. if (extra.comment) {
  441. acornOptions.onComment = function() {
  442. var comment = convertAcornCommentToEsprimaComment.apply(this, arguments);
  443. extra.comments.push(comment);
  444. };
  445. }
  446. extra.tolerant = typeof options.tolerant === "boolean" && options.tolerant;
  447. acornOptions.ecmaVersion = extra.ecmaVersion = normalizeEcmaVersion(options.ecmaVersion);
  448. // apply parsing flags
  449. if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {
  450. extra.ecmaFeatures = assign({}, options.ecmaFeatures);
  451. impliedStrict = extra.ecmaFeatures.impliedStrict;
  452. extra.ecmaFeatures.impliedStrict = typeof impliedStrict === "boolean" && impliedStrict;
  453. }
  454. try {
  455. var tokenizer = acorn.tokenizer(code, acornOptions);
  456. while ((lookahead = tokenizer.getToken()).type !== tt.eof) {
  457. translator.onToken(lookahead, extra);
  458. }
  459. // filterTokenLocation();
  460. tokens = extra.tokens;
  461. if (extra.comment) {
  462. tokens.comments = extra.comments;
  463. }
  464. if (extra.tolerant) {
  465. tokens.errors = extra.errors;
  466. }
  467. } catch (e) {
  468. throw e;
  469. }
  470. return tokens;
  471. }
  472. //------------------------------------------------------------------------------
  473. // Parser
  474. //------------------------------------------------------------------------------
  475. /**
  476. * Converts an Acorn comment to a Esprima comment.
  477. * @param {boolean} block True if it's a block comment, false if not.
  478. * @param {string} text The text of the comment.
  479. * @param {int} start The index at which the comment starts.
  480. * @param {int} end The index at which the comment ends.
  481. * @param {Location} startLoc The location at which the comment starts.
  482. * @param {Location} endLoc The location at which the comment ends.
  483. * @returns {Object} The comment object.
  484. * @private
  485. */
  486. function convertAcornCommentToEsprimaComment(block, text, start, end, startLoc, endLoc) {
  487. var comment = {
  488. type: block ? "Block" : "Line",
  489. value: text
  490. };
  491. if (typeof start === "number") {
  492. comment.start = start;
  493. comment.end = end;
  494. comment.range = [start, end];
  495. }
  496. if (typeof startLoc === "object") {
  497. comment.loc = {
  498. start: startLoc,
  499. end: endLoc
  500. };
  501. }
  502. return comment;
  503. }
  504. /**
  505. * Parses the given code.
  506. * @param {string} code The code to tokenize.
  507. * @param {Object} options Options defining how to tokenize.
  508. * @returns {ASTNode} The "Program" AST node.
  509. * @throws {SyntaxError} If the input code is invalid.
  510. * @private
  511. */
  512. function parse(code, options) {
  513. var program,
  514. toString = String,
  515. translator,
  516. impliedStrict,
  517. acornOptions = {
  518. ecmaVersion: DEFAULT_ECMA_VERSION,
  519. plugins: {
  520. espree: true
  521. }
  522. };
  523. lastToken = null;
  524. if (typeof code !== "string" && !(code instanceof String)) {
  525. code = toString(code);
  526. }
  527. resetExtra();
  528. commentAttachment.reset();
  529. if (typeof options !== "undefined") {
  530. extra.range = (typeof options.range === "boolean") && options.range;
  531. extra.loc = (typeof options.loc === "boolean") && options.loc;
  532. extra.attachComment = (typeof options.attachComment === "boolean") && options.attachComment;
  533. if (extra.loc && options.source !== null && options.source !== undefined) {
  534. extra.source = toString(options.source);
  535. }
  536. if (typeof options.tokens === "boolean" && options.tokens) {
  537. extra.tokens = [];
  538. translator = new TokenTranslator(tt, code);
  539. }
  540. if (typeof options.comment === "boolean" && options.comment) {
  541. extra.comment = true;
  542. extra.comments = [];
  543. }
  544. if (typeof options.tolerant === "boolean" && options.tolerant) {
  545. extra.errors = [];
  546. }
  547. if (extra.attachComment) {
  548. extra.range = true;
  549. extra.comments = [];
  550. commentAttachment.reset();
  551. }
  552. acornOptions.ecmaVersion = extra.ecmaVersion = normalizeEcmaVersion(options.ecmaVersion);
  553. if (options.sourceType === "module") {
  554. extra.isModule = true;
  555. // modules must be in 6 at least
  556. if (acornOptions.ecmaVersion < 6) {
  557. acornOptions.ecmaVersion = 6;
  558. extra.ecmaVersion = 6;
  559. }
  560. acornOptions.sourceType = "module";
  561. }
  562. // apply parsing flags after sourceType to allow overriding
  563. if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {
  564. extra.ecmaFeatures = assign({}, options.ecmaFeatures);
  565. impliedStrict = extra.ecmaFeatures.impliedStrict;
  566. extra.ecmaFeatures.impliedStrict = typeof impliedStrict === "boolean" && impliedStrict;
  567. if (options.ecmaFeatures.globalReturn) {
  568. acornOptions.allowReturnOutsideFunction = true;
  569. }
  570. }
  571. acornOptions.onToken = function(token) {
  572. if (extra.tokens) {
  573. translator.onToken(token, extra);
  574. }
  575. if (token.type !== tt.eof) {
  576. lastToken = token;
  577. }
  578. };
  579. if (extra.attachComment || extra.comment) {
  580. acornOptions.onComment = function() {
  581. var comment = convertAcornCommentToEsprimaComment.apply(this, arguments);
  582. extra.comments.push(comment);
  583. if (extra.attachComment) {
  584. commentAttachment.addComment(comment);
  585. }
  586. };
  587. }
  588. if (extra.range) {
  589. acornOptions.ranges = true;
  590. }
  591. if (extra.loc) {
  592. acornOptions.locations = true;
  593. }
  594. if (extra.ecmaFeatures.jsx) {
  595. // Should process jsx plugin before espree plugin.
  596. acornOptions.plugins = {
  597. jsx: true,
  598. espree: true
  599. };
  600. }
  601. }
  602. program = acorn.parse(code, acornOptions);
  603. program.sourceType = extra.isModule ? "module" : "script";
  604. if (extra.comment || extra.attachComment) {
  605. program.comments = extra.comments;
  606. }
  607. if (extra.tokens) {
  608. program.tokens = extra.tokens;
  609. }
  610. /*
  611. * Adjust opening and closing position of program to match Esprima.
  612. * Acorn always starts programs at range 0 whereas Esprima starts at the
  613. * first AST node's start (the only real difference is when there's leading
  614. * whitespace or leading comments). Acorn also counts trailing whitespace
  615. * as part of the program whereas Esprima only counts up to the last token.
  616. */
  617. if (program.range) {
  618. program.range[0] = program.body.length ? program.body[0].range[0] : program.range[0];
  619. program.range[1] = lastToken ? lastToken.range[1] : program.range[1];
  620. }
  621. if (program.loc) {
  622. program.loc.start = program.body.length ? program.body[0].loc.start : program.loc.start;
  623. program.loc.end = lastToken ? lastToken.loc.end : program.loc.end;
  624. }
  625. return program;
  626. }
  627. //------------------------------------------------------------------------------
  628. // Public
  629. //------------------------------------------------------------------------------
  630. exports.version = require("./package.json").version;
  631. exports.tokenize = tokenize;
  632. exports.parse = parse;
  633. // Deep copy.
  634. /* istanbul ignore next */
  635. exports.Syntax = (function() {
  636. var name, types = {};
  637. if (typeof Object.create === "function") {
  638. types = Object.create(null);
  639. }
  640. for (name in astNodeTypes) {
  641. if (astNodeTypes.hasOwnProperty(name)) {
  642. types[name] = astNodeTypes[name];
  643. }
  644. }
  645. if (typeof Object.freeze === "function") {
  646. Object.freeze(types);
  647. }
  648. return types;
  649. }());
  650. /* istanbul ignore next */
  651. exports.VisitorKeys = (function() {
  652. var visitorKeys = require("./lib/visitor-keys");
  653. var name,
  654. keys = {};
  655. if (typeof Object.create === "function") {
  656. keys = Object.create(null);
  657. }
  658. for (name in visitorKeys) {
  659. if (visitorKeys.hasOwnProperty(name)) {
  660. keys[name] = visitorKeys[name];
  661. }
  662. }
  663. if (typeof Object.freeze === "function") {
  664. Object.freeze(keys);
  665. }
  666. return keys;
  667. }());