Parser.js 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. // Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
  7. const acorn = require("acorn-dynamic-import").default;
  8. const Tapable = require("tapable");
  9. const json5 = require("json5");
  10. const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
  11. function joinRanges(startRange, endRange) {
  12. if(!endRange) return startRange;
  13. if(!startRange) return endRange;
  14. return [startRange[0], endRange[1]];
  15. }
  16. const POSSIBLE_AST_OPTIONS = [{
  17. ranges: true,
  18. locations: true,
  19. ecmaVersion: 2017,
  20. sourceType: "module",
  21. plugins: {
  22. dynamicImport: true
  23. }
  24. }, {
  25. ranges: true,
  26. locations: true,
  27. ecmaVersion: 2017,
  28. sourceType: "script",
  29. plugins: {
  30. dynamicImport: true
  31. }
  32. }];
  33. class Parser extends Tapable {
  34. constructor(options) {
  35. super();
  36. this.options = options;
  37. this.scope = undefined;
  38. this.state = undefined;
  39. this.comments = undefined;
  40. this.initializeEvaluating();
  41. }
  42. initializeEvaluating() {
  43. this.plugin("evaluate Literal", expr => {
  44. switch(typeof expr.value) {
  45. case "number":
  46. return new BasicEvaluatedExpression().setNumber(expr.value).setRange(expr.range);
  47. case "string":
  48. return new BasicEvaluatedExpression().setString(expr.value).setRange(expr.range);
  49. case "boolean":
  50. return new BasicEvaluatedExpression().setBoolean(expr.value).setRange(expr.range);
  51. }
  52. if(expr.value === null)
  53. return new BasicEvaluatedExpression().setNull().setRange(expr.range);
  54. if(expr.value instanceof RegExp)
  55. return new BasicEvaluatedExpression().setRegExp(expr.value).setRange(expr.range);
  56. });
  57. this.plugin("evaluate LogicalExpression", function(expr) {
  58. let left;
  59. let leftAsBool;
  60. let right;
  61. if(expr.operator === "&&") {
  62. left = this.evaluateExpression(expr.left);
  63. leftAsBool = left && left.asBool();
  64. if(leftAsBool === false) return left.setRange(expr.range);
  65. if(leftAsBool !== true) return;
  66. right = this.evaluateExpression(expr.right);
  67. return right.setRange(expr.range);
  68. } else if(expr.operator === "||") {
  69. left = this.evaluateExpression(expr.left);
  70. leftAsBool = left && left.asBool();
  71. if(leftAsBool === true) return left.setRange(expr.range);
  72. if(leftAsBool !== false) return;
  73. right = this.evaluateExpression(expr.right);
  74. return right.setRange(expr.range);
  75. }
  76. });
  77. this.plugin("evaluate BinaryExpression", function(expr) {
  78. let left;
  79. let right;
  80. let res;
  81. if(expr.operator === "+") {
  82. left = this.evaluateExpression(expr.left);
  83. right = this.evaluateExpression(expr.right);
  84. if(!left || !right) return;
  85. res = new BasicEvaluatedExpression();
  86. if(left.isString()) {
  87. if(right.isString()) {
  88. res.setString(left.string + right.string);
  89. } else if(right.isNumber()) {
  90. res.setString(left.string + right.number);
  91. } else if(right.isWrapped() && right.prefix && right.prefix.isString()) {
  92. res.setWrapped(
  93. new BasicEvaluatedExpression()
  94. .setString(left.string + right.prefix.string)
  95. .setRange(joinRanges(left.range, right.prefix.range)),
  96. right.postfix);
  97. } else if(right.isWrapped()) {
  98. res.setWrapped(
  99. new BasicEvaluatedExpression()
  100. .setString(left.string)
  101. .setRange(left.range),
  102. right.postfix);
  103. } else {
  104. res.setWrapped(left, null);
  105. }
  106. } else if(left.isNumber()) {
  107. if(right.isString()) {
  108. res.setString(left.number + right.string);
  109. } else if(right.isNumber()) {
  110. res.setNumber(left.number + right.number);
  111. }
  112. } else if(left.isWrapped()) {
  113. if(left.postfix && left.postfix.isString() && right.isString()) {
  114. res.setWrapped(left.prefix,
  115. new BasicEvaluatedExpression()
  116. .setString(left.postfix.string + right.string)
  117. .setRange(joinRanges(left.postfix.range, right.range))
  118. );
  119. } else if(left.postfix && left.postfix.isString() && right.isNumber()) {
  120. res.setWrapped(left.prefix,
  121. new BasicEvaluatedExpression()
  122. .setString(left.postfix.string + right.number)
  123. .setRange(joinRanges(left.postfix.range, right.range))
  124. );
  125. } else if(right.isString()) {
  126. res.setWrapped(left.prefix, right);
  127. } else if(right.isNumber()) {
  128. res.setWrapped(left.prefix,
  129. new BasicEvaluatedExpression()
  130. .setString(right.number + "")
  131. .setRange(right.range));
  132. } else {
  133. res.setWrapped(left.prefix, new BasicEvaluatedExpression());
  134. }
  135. } else {
  136. if(right.isString()) {
  137. res.setWrapped(null, right);
  138. }
  139. }
  140. res.setRange(expr.range);
  141. return res;
  142. } else if(expr.operator === "-") {
  143. left = this.evaluateExpression(expr.left);
  144. right = this.evaluateExpression(expr.right);
  145. if(!left || !right) return;
  146. if(!left.isNumber() || !right.isNumber()) return;
  147. res = new BasicEvaluatedExpression();
  148. res.setNumber(left.number - right.number);
  149. res.setRange(expr.range);
  150. return res;
  151. } else if(expr.operator === "*") {
  152. left = this.evaluateExpression(expr.left);
  153. right = this.evaluateExpression(expr.right);
  154. if(!left || !right) return;
  155. if(!left.isNumber() || !right.isNumber()) return;
  156. res = new BasicEvaluatedExpression();
  157. res.setNumber(left.number * right.number);
  158. res.setRange(expr.range);
  159. return res;
  160. } else if(expr.operator === "/") {
  161. left = this.evaluateExpression(expr.left);
  162. right = this.evaluateExpression(expr.right);
  163. if(!left || !right) return;
  164. if(!left.isNumber() || !right.isNumber()) return;
  165. res = new BasicEvaluatedExpression();
  166. res.setNumber(left.number / right.number);
  167. res.setRange(expr.range);
  168. return res;
  169. } else if(expr.operator === "==" || expr.operator === "===") {
  170. left = this.evaluateExpression(expr.left);
  171. right = this.evaluateExpression(expr.right);
  172. if(!left || !right) return;
  173. res = new BasicEvaluatedExpression();
  174. res.setRange(expr.range);
  175. if(left.isString() && right.isString()) {
  176. return res.setBoolean(left.string === right.string);
  177. } else if(left.isNumber() && right.isNumber()) {
  178. return res.setBoolean(left.number === right.number);
  179. } else if(left.isBoolean() && right.isBoolean()) {
  180. return res.setBoolean(left.bool === right.bool);
  181. }
  182. } else if(expr.operator === "!=" || expr.operator === "!==") {
  183. left = this.evaluateExpression(expr.left);
  184. right = this.evaluateExpression(expr.right);
  185. if(!left || !right) return;
  186. res = new BasicEvaluatedExpression();
  187. res.setRange(expr.range);
  188. if(left.isString() && right.isString()) {
  189. return res.setBoolean(left.string !== right.string);
  190. } else if(left.isNumber() && right.isNumber()) {
  191. return res.setBoolean(left.number !== right.number);
  192. } else if(left.isBoolean() && right.isBoolean()) {
  193. return res.setBoolean(left.bool !== right.bool);
  194. }
  195. }
  196. });
  197. this.plugin("evaluate UnaryExpression", function(expr) {
  198. if(expr.operator === "typeof") {
  199. let res;
  200. let name;
  201. if(expr.argument.type === "Identifier") {
  202. name = this.scope.renames["$" + expr.argument.name] || expr.argument.name;
  203. if(this.scope.definitions.indexOf(name) === -1) {
  204. res = this.applyPluginsBailResult1("evaluate typeof " + name, expr);
  205. if(res !== undefined) return res;
  206. }
  207. }
  208. if(expr.argument.type === "MemberExpression") {
  209. let expression = expr.argument;
  210. let exprName = [];
  211. while(expression.type === "MemberExpression" && !expression.computed) {
  212. exprName.unshift(this.scope.renames["$" + expression.property.name] || expression.property.name);
  213. expression = expression.object;
  214. }
  215. if(expression.type === "Identifier") {
  216. exprName.unshift(this.scope.renames["$" + expression.name] || expression.name);
  217. if(this.scope.definitions.indexOf(name) === -1) {
  218. exprName = exprName.join(".");
  219. res = this.applyPluginsBailResult1("evaluate typeof " + exprName, expr);
  220. if(res !== undefined) return res;
  221. }
  222. }
  223. }
  224. if(expr.argument.type === "FunctionExpression") {
  225. return new BasicEvaluatedExpression().setString("function").setRange(expr.range);
  226. }
  227. const arg = this.evaluateExpression(expr.argument);
  228. if(arg.isString() || arg.isWrapped()) return new BasicEvaluatedExpression().setString("string").setRange(expr.range);
  229. else if(arg.isNumber()) return new BasicEvaluatedExpression().setString("number").setRange(expr.range);
  230. else if(arg.isBoolean()) return new BasicEvaluatedExpression().setString("boolean").setRange(expr.range);
  231. else if(arg.isArray() || arg.isConstArray() || arg.isRegExp()) return new BasicEvaluatedExpression().setString("object").setRange(expr.range);
  232. } else if(expr.operator === "!") {
  233. const argument = this.evaluateExpression(expr.argument);
  234. if(!argument) return;
  235. if(argument.isBoolean()) {
  236. return new BasicEvaluatedExpression().setBoolean(!argument.bool).setRange(expr.range);
  237. } else if(argument.isString()) {
  238. return new BasicEvaluatedExpression().setBoolean(!argument.string).setRange(expr.range);
  239. } else if(argument.isNumber()) {
  240. return new BasicEvaluatedExpression().setBoolean(!argument.number).setRange(expr.range);
  241. }
  242. }
  243. });
  244. this.plugin("evaluate typeof undefined", function(expr) {
  245. return new BasicEvaluatedExpression().setString("undefined").setRange(expr.range);
  246. });
  247. this.plugin("evaluate Identifier", function(expr) {
  248. const name = this.scope.renames["$" + expr.name] || expr.name;
  249. if(this.scope.definitions.indexOf(expr.name) === -1) {
  250. const result = this.applyPluginsBailResult1("evaluate Identifier " + name, expr);
  251. if(result) return result;
  252. return new BasicEvaluatedExpression().setIdentifier(name).setRange(expr.range);
  253. } else {
  254. return this.applyPluginsBailResult1("evaluate defined Identifier " + name, expr);
  255. }
  256. });
  257. this.plugin("evaluate MemberExpression", function(expression) {
  258. let expr = expression;
  259. let exprName = [];
  260. while(expr.type === "MemberExpression" && expr.property.type === (expr.computed ? "Literal" : "Identifier")) {
  261. exprName.unshift(expr.property.name || expr.property.value);
  262. expr = expr.object;
  263. }
  264. if(expr.type === "Identifier") {
  265. const name = this.scope.renames["$" + expr.name] || expr.name;
  266. if(this.scope.definitions.indexOf(name) === -1) {
  267. exprName.unshift(name);
  268. exprName = exprName.join(".");
  269. if(this.scope.definitions.indexOf(expr.name) === -1) {
  270. const result = this.applyPluginsBailResult1("evaluate Identifier " + exprName, expression);
  271. if(result) return result;
  272. return new BasicEvaluatedExpression().setIdentifier(exprName).setRange(expression.range);
  273. } else {
  274. return this.applyPluginsBailResult1("evaluate defined Identifier " + exprName, expression);
  275. }
  276. }
  277. }
  278. });
  279. this.plugin("evaluate CallExpression", function(expr) {
  280. if(expr.callee.type !== "MemberExpression") return;
  281. if(expr.callee.property.type !== (expr.callee.computed ? "Literal" : "Identifier")) return;
  282. const param = this.evaluateExpression(expr.callee.object);
  283. if(!param) return;
  284. const property = expr.callee.property.name || expr.callee.property.value;
  285. return this.applyPluginsBailResult("evaluate CallExpression ." + property, expr, param);
  286. });
  287. this.plugin("evaluate CallExpression .replace", function(expr, param) {
  288. if(!param.isString()) return;
  289. if(expr.arguments.length !== 2) return;
  290. let arg1 = this.evaluateExpression(expr.arguments[0]);
  291. let arg2 = this.evaluateExpression(expr.arguments[1]);
  292. if(!arg1.isString() && !arg1.isRegExp()) return;
  293. arg1 = arg1.regExp || arg1.string;
  294. if(!arg2.isString()) return;
  295. arg2 = arg2.string;
  296. return new BasicEvaluatedExpression().setString(param.string.replace(arg1, arg2)).setRange(expr.range);
  297. });
  298. ["substr", "substring"].forEach(fn => {
  299. this.plugin("evaluate CallExpression ." + fn, function(expr, param) {
  300. if(!param.isString()) return;
  301. let arg1;
  302. let result, str = param.string;
  303. switch(expr.arguments.length) {
  304. case 1:
  305. arg1 = this.evaluateExpression(expr.arguments[0]);
  306. if(!arg1.isNumber()) return;
  307. result = str[fn](arg1.number);
  308. break;
  309. case 2:
  310. {
  311. arg1 = this.evaluateExpression(expr.arguments[0]);
  312. const arg2 = this.evaluateExpression(expr.arguments[1]);
  313. if(!arg1.isNumber()) return;
  314. if(!arg2.isNumber()) return;
  315. result = str[fn](arg1.number, arg2.number);
  316. break;
  317. }
  318. default:
  319. return;
  320. }
  321. return new BasicEvaluatedExpression().setString(result).setRange(expr.range);
  322. });
  323. /**
  324. * @param {string} kind "cooked" | "raw"
  325. * @param {any[]} quasis quasis
  326. * @param {any[]} expressions expressions
  327. * @return {BasicEvaluatedExpression[]} Simplified template
  328. */
  329. function getSimplifiedTemplateResult(kind, quasis, expressions) {
  330. const parts = [];
  331. for(let i = 0; i < quasis.length; i++) {
  332. parts.push(new BasicEvaluatedExpression().setString(quasis[i].value[kind]).setRange(quasis[i].range));
  333. if(i > 0) {
  334. const prevExpr = parts[parts.length - 2],
  335. lastExpr = parts[parts.length - 1];
  336. const expr = this.evaluateExpression(expressions[i - 1]);
  337. if(!(expr.isString() || expr.isNumber())) continue;
  338. prevExpr.setString(prevExpr.string + (expr.isString() ? expr.string : expr.number) + lastExpr.string);
  339. prevExpr.setRange([prevExpr.range[0], lastExpr.range[1]]);
  340. parts.pop();
  341. }
  342. }
  343. return parts;
  344. }
  345. this.plugin("evaluate TemplateLiteral", function(node) {
  346. const parts = getSimplifiedTemplateResult.call(this, "cooked", node.quasis, node.expressions);
  347. if(parts.length === 1) {
  348. return parts[0].setRange(node.range);
  349. }
  350. return new BasicEvaluatedExpression().setTemplateString(parts).setRange(node.range);
  351. });
  352. this.plugin("evaluate TaggedTemplateExpression", function(node) {
  353. if(this.evaluateExpression(node.tag).identifier !== "String.raw") return;
  354. const parts = getSimplifiedTemplateResult.call(this, "raw", node.quasi.quasis, node.quasi.expressions);
  355. return new BasicEvaluatedExpression().setTemplateString(parts).setRange(node.range);
  356. });
  357. });
  358. this.plugin("evaluate CallExpression .split", function(expr, param) {
  359. if(!param.isString()) return;
  360. if(expr.arguments.length !== 1) return;
  361. let result;
  362. const arg = this.evaluateExpression(expr.arguments[0]);
  363. if(arg.isString()) {
  364. result = param.string.split(arg.string);
  365. } else if(arg.isRegExp()) {
  366. result = param.string.split(arg.regExp);
  367. } else return;
  368. return new BasicEvaluatedExpression().setArray(result).setRange(expr.range);
  369. });
  370. this.plugin("evaluate ConditionalExpression", function(expr) {
  371. const condition = this.evaluateExpression(expr.test);
  372. const conditionValue = condition.asBool();
  373. let res;
  374. if(conditionValue === undefined) {
  375. const consequent = this.evaluateExpression(expr.consequent);
  376. const alternate = this.evaluateExpression(expr.alternate);
  377. if(!consequent || !alternate) return;
  378. res = new BasicEvaluatedExpression();
  379. if(consequent.isConditional())
  380. res.setOptions(consequent.options);
  381. else
  382. res.setOptions([consequent]);
  383. if(alternate.isConditional())
  384. res.addOptions(alternate.options);
  385. else
  386. res.addOptions([alternate]);
  387. } else {
  388. res = this.evaluateExpression(conditionValue ? expr.consequent : expr.alternate);
  389. }
  390. res.setRange(expr.range);
  391. return res;
  392. });
  393. this.plugin("evaluate ArrayExpression", function(expr) {
  394. const items = expr.elements.map(function(element) {
  395. return element !== null && this.evaluateExpression(element);
  396. }, this);
  397. if(!items.every(Boolean)) return;
  398. return new BasicEvaluatedExpression().setItems(items).setRange(expr.range);
  399. });
  400. }
  401. getRenameIdentifier(expr) {
  402. const result = this.evaluateExpression(expr);
  403. if(!result) return;
  404. if(result.isIdentifier()) return result.identifier;
  405. return;
  406. }
  407. walkClass(classy) {
  408. if(classy.superClass)
  409. this.walkExpression(classy.superClass);
  410. if(classy.body && classy.body.type === "ClassBody") {
  411. classy.body.body.forEach(methodDefinition => {
  412. if(methodDefinition.type === "MethodDefinition")
  413. this.walkMethodDefinition(methodDefinition);
  414. });
  415. }
  416. }
  417. walkMethodDefinition(methodDefinition) {
  418. if(methodDefinition.computed && methodDefinition.key)
  419. this.walkExpression(methodDefinition.key);
  420. if(methodDefinition.value)
  421. this.walkExpression(methodDefinition.value);
  422. }
  423. // Prewalking iterates the scope for variable declarations
  424. prewalkStatements(statements) {
  425. for(let index = 0, len = statements.length; index < len; index++) {
  426. const statement = statements[index];
  427. this.prewalkStatement(statement);
  428. }
  429. }
  430. // Walking iterates the statements and expressions and processes them
  431. walkStatements(statements) {
  432. for(let index = 0, len = statements.length; index < len; index++) {
  433. const statement = statements[index];
  434. this.walkStatement(statement);
  435. }
  436. }
  437. prewalkStatement(statement) {
  438. const handler = this["prewalk" + statement.type];
  439. if(handler)
  440. handler.call(this, statement);
  441. }
  442. walkStatement(statement) {
  443. if(this.applyPluginsBailResult1("statement", statement) !== undefined) return;
  444. const handler = this["walk" + statement.type];
  445. if(handler)
  446. handler.call(this, statement);
  447. }
  448. // Real Statements
  449. prewalkBlockStatement(statement) {
  450. this.prewalkStatements(statement.body);
  451. }
  452. walkBlockStatement(statement) {
  453. this.walkStatements(statement.body);
  454. }
  455. walkExpressionStatement(statement) {
  456. this.walkExpression(statement.expression);
  457. }
  458. prewalkIfStatement(statement) {
  459. this.prewalkStatement(statement.consequent);
  460. if(statement.alternate)
  461. this.prewalkStatement(statement.alternate);
  462. }
  463. walkIfStatement(statement) {
  464. const result = this.applyPluginsBailResult1("statement if", statement);
  465. if(result === undefined) {
  466. this.walkExpression(statement.test);
  467. this.walkStatement(statement.consequent);
  468. if(statement.alternate)
  469. this.walkStatement(statement.alternate);
  470. } else {
  471. if(result)
  472. this.walkStatement(statement.consequent);
  473. else if(statement.alternate)
  474. this.walkStatement(statement.alternate);
  475. }
  476. }
  477. prewalkLabeledStatement(statement) {
  478. this.prewalkStatement(statement.body);
  479. }
  480. walkLabeledStatement(statement) {
  481. const result = this.applyPluginsBailResult1("label " + statement.label.name, statement);
  482. if(result !== true)
  483. this.walkStatement(statement.body);
  484. }
  485. prewalkWithStatement(statement) {
  486. this.prewalkStatement(statement.body);
  487. }
  488. walkWithStatement(statement) {
  489. this.walkExpression(statement.object);
  490. this.walkStatement(statement.body);
  491. }
  492. prewalkSwitchStatement(statement) {
  493. this.prewalkSwitchCases(statement.cases);
  494. }
  495. walkSwitchStatement(statement) {
  496. this.walkExpression(statement.discriminant);
  497. this.walkSwitchCases(statement.cases);
  498. }
  499. walkTerminatingStatement(statement) {
  500. if(statement.argument)
  501. this.walkExpression(statement.argument);
  502. }
  503. walkReturnStatement(statement) {
  504. this.walkTerminatingStatement(statement);
  505. }
  506. walkThrowStatement(statement) {
  507. this.walkTerminatingStatement(statement);
  508. }
  509. prewalkTryStatement(statement) {
  510. this.prewalkStatement(statement.block);
  511. }
  512. walkTryStatement(statement) {
  513. if(this.scope.inTry) {
  514. this.walkStatement(statement.block);
  515. } else {
  516. this.scope.inTry = true;
  517. this.walkStatement(statement.block);
  518. this.scope.inTry = false;
  519. }
  520. if(statement.handler)
  521. this.walkCatchClause(statement.handler);
  522. if(statement.finalizer)
  523. this.walkStatement(statement.finalizer);
  524. }
  525. prewalkWhileStatement(statement) {
  526. this.prewalkStatement(statement.body);
  527. }
  528. walkWhileStatement(statement) {
  529. this.walkExpression(statement.test);
  530. this.walkStatement(statement.body);
  531. }
  532. prewalkDoWhileStatement(statement) {
  533. this.prewalkStatement(statement.body);
  534. }
  535. walkDoWhileStatement(statement) {
  536. this.walkStatement(statement.body);
  537. this.walkExpression(statement.test);
  538. }
  539. prewalkForStatement(statement) {
  540. if(statement.init) {
  541. if(statement.init.type === "VariableDeclaration")
  542. this.prewalkStatement(statement.init);
  543. }
  544. this.prewalkStatement(statement.body);
  545. }
  546. walkForStatement(statement) {
  547. if(statement.init) {
  548. if(statement.init.type === "VariableDeclaration")
  549. this.walkStatement(statement.init);
  550. else
  551. this.walkExpression(statement.init);
  552. }
  553. if(statement.test)
  554. this.walkExpression(statement.test);
  555. if(statement.update)
  556. this.walkExpression(statement.update);
  557. this.walkStatement(statement.body);
  558. }
  559. prewalkForInStatement(statement) {
  560. if(statement.left.type === "VariableDeclaration")
  561. this.prewalkStatement(statement.left);
  562. this.prewalkStatement(statement.body);
  563. }
  564. walkForInStatement(statement) {
  565. if(statement.left.type === "VariableDeclaration")
  566. this.walkStatement(statement.left);
  567. else
  568. this.walkExpression(statement.left);
  569. this.walkExpression(statement.right);
  570. this.walkStatement(statement.body);
  571. }
  572. prewalkForOfStatement(statement) {
  573. if(statement.left.type === "VariableDeclaration")
  574. this.prewalkStatement(statement.left);
  575. this.prewalkStatement(statement.body);
  576. }
  577. walkForOfStatement(statement) {
  578. if(statement.left.type === "VariableDeclaration")
  579. this.walkStatement(statement.left);
  580. else
  581. this.walkExpression(statement.left);
  582. this.walkExpression(statement.right);
  583. this.walkStatement(statement.body);
  584. }
  585. // Declarations
  586. prewalkFunctionDeclaration(statement) {
  587. if(statement.id) {
  588. this.scope.renames["$" + statement.id.name] = undefined;
  589. this.scope.definitions.push(statement.id.name);
  590. }
  591. }
  592. walkFunctionDeclaration(statement) {
  593. statement.params.forEach(param => {
  594. this.walkPattern(param);
  595. });
  596. this.inScope(statement.params, function() {
  597. if(statement.body.type === "BlockStatement") {
  598. this.prewalkStatement(statement.body);
  599. this.walkStatement(statement.body);
  600. } else {
  601. this.walkExpression(statement.body);
  602. }
  603. }.bind(this));
  604. }
  605. prewalkImportDeclaration(statement) {
  606. const source = statement.source.value;
  607. this.applyPluginsBailResult("import", statement, source);
  608. statement.specifiers.forEach(function(specifier) {
  609. const name = specifier.local.name;
  610. this.scope.renames["$" + name] = undefined;
  611. this.scope.definitions.push(name);
  612. switch(specifier.type) {
  613. case "ImportDefaultSpecifier":
  614. this.applyPluginsBailResult("import specifier", statement, source, "default", name);
  615. break;
  616. case "ImportSpecifier":
  617. this.applyPluginsBailResult("import specifier", statement, source, specifier.imported.name, name);
  618. break;
  619. case "ImportNamespaceSpecifier":
  620. this.applyPluginsBailResult("import specifier", statement, source, null, name);
  621. break;
  622. }
  623. }, this);
  624. }
  625. prewalkExportNamedDeclaration(statement) {
  626. let source;
  627. if(statement.source) {
  628. source = statement.source.value;
  629. this.applyPluginsBailResult("export import", statement, source);
  630. } else {
  631. this.applyPluginsBailResult1("export", statement);
  632. }
  633. if(statement.declaration) {
  634. if(/Expression$/.test(statement.declaration.type)) {
  635. throw new Error("Doesn't occur?");
  636. } else {
  637. if(!this.applyPluginsBailResult("export declaration", statement, statement.declaration)) {
  638. const pos = this.scope.definitions.length;
  639. this.prewalkStatement(statement.declaration);
  640. const newDefs = this.scope.definitions.slice(pos);
  641. for(let index = newDefs.length - 1; index >= 0; index--) {
  642. const def = newDefs[index];
  643. this.applyPluginsBailResult("export specifier", statement, def, def, index);
  644. }
  645. }
  646. }
  647. }
  648. if(statement.specifiers) {
  649. for(let specifierIndex = 0; specifierIndex < statement.specifiers.length; specifierIndex++) {
  650. const specifier = statement.specifiers[specifierIndex];
  651. switch(specifier.type) {
  652. case "ExportSpecifier":
  653. {
  654. const name = specifier.exported.name;
  655. if(source)
  656. this.applyPluginsBailResult("export import specifier", statement, source, specifier.local.name, name, specifierIndex);
  657. else
  658. this.applyPluginsBailResult("export specifier", statement, specifier.local.name, name, specifierIndex);
  659. break;
  660. }
  661. }
  662. }
  663. }
  664. }
  665. walkExportNamedDeclaration(statement) {
  666. if(statement.declaration) {
  667. this.walkStatement(statement.declaration);
  668. }
  669. }
  670. prewalkExportDefaultDeclaration(statement) {
  671. if(/Declaration$/.test(statement.declaration.type)) {
  672. const pos = this.scope.definitions.length;
  673. this.prewalkStatement(statement.declaration);
  674. const newDefs = this.scope.definitions.slice(pos);
  675. for(let index = 0, len = newDefs.length; index < len; index++) {
  676. const def = newDefs[index];
  677. this.applyPluginsBailResult("export specifier", statement, def, "default");
  678. }
  679. }
  680. }
  681. walkExportDefaultDeclaration(statement) {
  682. this.applyPluginsBailResult1("export", statement);
  683. if(/Declaration$/.test(statement.declaration.type)) {
  684. if(!this.applyPluginsBailResult("export declaration", statement, statement.declaration)) {
  685. this.walkStatement(statement.declaration);
  686. }
  687. } else {
  688. this.walkExpression(statement.declaration);
  689. if(!this.applyPluginsBailResult("export expression", statement, statement.declaration)) {
  690. this.applyPluginsBailResult("export specifier", statement, statement.declaration, "default");
  691. }
  692. }
  693. }
  694. prewalkExportAllDeclaration(statement) {
  695. const source = statement.source.value;
  696. this.applyPluginsBailResult("export import", statement, source);
  697. this.applyPluginsBailResult("export import specifier", statement, source, null, null, 0);
  698. }
  699. prewalkVariableDeclaration(statement) {
  700. if(statement.declarations)
  701. this.prewalkVariableDeclarators(statement.declarations);
  702. }
  703. walkVariableDeclaration(statement) {
  704. if(statement.declarations)
  705. this.walkVariableDeclarators(statement.declarations);
  706. }
  707. prewalkClassDeclaration(statement) {
  708. if(statement.id) {
  709. this.scope.renames["$" + statement.id.name] = undefined;
  710. this.scope.definitions.push(statement.id.name);
  711. }
  712. }
  713. walkClassDeclaration(statement) {
  714. this.walkClass(statement);
  715. }
  716. prewalkSwitchCases(switchCases) {
  717. for(let index = 0, len = switchCases.length; index < len; index++) {
  718. const switchCase = switchCases[index];
  719. this.prewalkStatements(switchCase.consequent);
  720. }
  721. }
  722. walkSwitchCases(switchCases) {
  723. for(let index = 0, len = switchCases.length; index < len; index++) {
  724. const switchCase = switchCases[index];
  725. if(switchCase.test) {
  726. this.walkExpression(switchCase.test);
  727. }
  728. this.walkStatements(switchCase.consequent);
  729. }
  730. }
  731. walkCatchClause(catchClause) {
  732. this.inScope([catchClause.param], function() {
  733. this.prewalkStatement(catchClause.body);
  734. this.walkStatement(catchClause.body);
  735. }.bind(this));
  736. }
  737. prewalkVariableDeclarators(declarators) {
  738. declarators.forEach(declarator => {
  739. switch(declarator.type) {
  740. case "VariableDeclarator":
  741. {
  742. this.enterPattern(declarator.id, (name, decl) => {
  743. if(!this.applyPluginsBailResult1("var-" + declarator.kind + " " + name, decl)) {
  744. if(!this.applyPluginsBailResult1("var " + name, decl)) {
  745. this.scope.renames["$" + name] = undefined;
  746. if(this.scope.definitions.indexOf(name) < 0)
  747. this.scope.definitions.push(name);
  748. }
  749. }
  750. });
  751. break;
  752. }
  753. }
  754. });
  755. }
  756. walkVariableDeclarators(declarators) {
  757. declarators.forEach(declarator => {
  758. switch(declarator.type) {
  759. case "VariableDeclarator":
  760. {
  761. const renameIdentifier = declarator.init && this.getRenameIdentifier(declarator.init);
  762. if(renameIdentifier && declarator.id.type === "Identifier" && this.applyPluginsBailResult1("can-rename " + renameIdentifier, declarator.init)) {
  763. // renaming with "var a = b;"
  764. if(!this.applyPluginsBailResult1("rename " + renameIdentifier, declarator.init)) {
  765. this.scope.renames["$" + declarator.id.name] = this.scope.renames["$" + renameIdentifier] || renameIdentifier;
  766. const idx = this.scope.definitions.indexOf(declarator.id.name);
  767. if(idx >= 0) this.scope.definitions.splice(idx, 1);
  768. }
  769. } else {
  770. this.walkPattern(declarator.id);
  771. if(declarator.init)
  772. this.walkExpression(declarator.init);
  773. }
  774. break;
  775. }
  776. }
  777. });
  778. }
  779. walkPattern(pattern) {
  780. if(pattern.type === "Identifier")
  781. return;
  782. if(this["walk" + pattern.type])
  783. this["walk" + pattern.type](pattern);
  784. }
  785. walkAssignmentPattern(pattern) {
  786. this.walkExpression(pattern.right);
  787. this.walkPattern(pattern.left);
  788. }
  789. walkObjectPattern(pattern) {
  790. for(let i = 0, len = pattern.properties.length; i < len; i++) {
  791. const prop = pattern.properties[i];
  792. if(prop) {
  793. if(prop.computed)
  794. this.walkExpression(prop.key);
  795. if(prop.value)
  796. this.walkPattern(prop.value);
  797. }
  798. }
  799. }
  800. walkArrayPattern(pattern) {
  801. for(let i = 0, len = pattern.elements.length; i < len; i++) {
  802. const element = pattern.elements[i];
  803. if(element)
  804. this.walkPattern(element);
  805. }
  806. }
  807. walkRestElement(pattern) {
  808. this.walkPattern(pattern.argument);
  809. }
  810. walkExpressions(expressions) {
  811. for(let expressionsIndex = 0, len = expressions.length; expressionsIndex < len; expressionsIndex++) {
  812. const expression = expressions[expressionsIndex];
  813. if(expression)
  814. this.walkExpression(expression);
  815. }
  816. }
  817. walkExpression(expression) {
  818. if(this["walk" + expression.type])
  819. return this["walk" + expression.type](expression);
  820. }
  821. walkAwaitExpression(expression) {
  822. const argument = expression.argument;
  823. if(this["walk" + argument.type])
  824. return this["walk" + argument.type](argument);
  825. }
  826. walkArrayExpression(expression) {
  827. if(expression.elements)
  828. this.walkExpressions(expression.elements);
  829. }
  830. walkSpreadElement(expression) {
  831. if(expression.argument)
  832. this.walkExpression(expression.argument);
  833. }
  834. walkObjectExpression(expression) {
  835. for(let propIndex = 0, len = expression.properties.length; propIndex < len; propIndex++) {
  836. const prop = expression.properties[propIndex];
  837. if(prop.computed)
  838. this.walkExpression(prop.key);
  839. if(prop.shorthand)
  840. this.scope.inShorthand = true;
  841. this.walkExpression(prop.value);
  842. if(prop.shorthand)
  843. this.scope.inShorthand = false;
  844. }
  845. }
  846. walkFunctionExpression(expression) {
  847. expression.params.forEach(param => {
  848. this.walkPattern(param);
  849. });
  850. this.inScope(expression.params, function() {
  851. if(expression.body.type === "BlockStatement") {
  852. this.prewalkStatement(expression.body);
  853. this.walkStatement(expression.body);
  854. } else {
  855. this.walkExpression(expression.body);
  856. }
  857. }.bind(this));
  858. }
  859. walkArrowFunctionExpression(expression) {
  860. expression.params.forEach(param => {
  861. this.walkPattern(param);
  862. });
  863. this.inScope(expression.params, function() {
  864. if(expression.body.type === "BlockStatement") {
  865. this.prewalkStatement(expression.body);
  866. this.walkStatement(expression.body);
  867. } else {
  868. this.walkExpression(expression.body);
  869. }
  870. }.bind(this));
  871. }
  872. walkSequenceExpression(expression) {
  873. if(expression.expressions)
  874. this.walkExpressions(expression.expressions);
  875. }
  876. walkUpdateExpression(expression) {
  877. this.walkExpression(expression.argument);
  878. }
  879. walkUnaryExpression(expression) {
  880. if(expression.operator === "typeof") {
  881. let expr = expression.argument;
  882. let exprName = [];
  883. while(expr.type === "MemberExpression" && expr.property.type === (expr.computed ? "Literal" : "Identifier")) {
  884. exprName.unshift(expr.property.name || expr.property.value);
  885. expr = expr.object;
  886. }
  887. if(expr.type === "Identifier" && this.scope.definitions.indexOf(expr.name) === -1) {
  888. exprName.unshift(this.scope.renames["$" + expr.name] || expr.name);
  889. exprName = exprName.join(".");
  890. const result = this.applyPluginsBailResult1("typeof " + exprName, expression);
  891. if(result === true)
  892. return;
  893. }
  894. }
  895. this.walkExpression(expression.argument);
  896. }
  897. walkLeftRightExpression(expression) {
  898. this.walkExpression(expression.left);
  899. this.walkExpression(expression.right);
  900. }
  901. walkBinaryExpression(expression) {
  902. this.walkLeftRightExpression(expression);
  903. }
  904. walkLogicalExpression(expression) {
  905. this.walkLeftRightExpression(expression);
  906. }
  907. walkAssignmentExpression(expression) {
  908. const renameIdentifier = this.getRenameIdentifier(expression.right);
  909. if(expression.left.type === "Identifier" && renameIdentifier && this.applyPluginsBailResult1("can-rename " + renameIdentifier, expression.right)) {
  910. // renaming "a = b;"
  911. if(!this.applyPluginsBailResult1("rename " + renameIdentifier, expression.right)) {
  912. this.scope.renames["$" + expression.left.name] = renameIdentifier;
  913. const idx = this.scope.definitions.indexOf(expression.left.name);
  914. if(idx >= 0) this.scope.definitions.splice(idx, 1);
  915. }
  916. } else if(expression.left.type === "Identifier") {
  917. if(!this.applyPluginsBailResult1("assigned " + expression.left.name, expression)) {
  918. this.walkExpression(expression.right);
  919. }
  920. this.scope.renames["$" + expression.left.name] = undefined;
  921. if(!this.applyPluginsBailResult1("assign " + expression.left.name, expression)) {
  922. this.walkExpression(expression.left);
  923. }
  924. } else {
  925. this.walkExpression(expression.right);
  926. this.walkPattern(expression.left);
  927. this.enterPattern(expression.left, (name, decl) => {
  928. this.scope.renames["$" + name] = undefined;
  929. });
  930. }
  931. }
  932. walkConditionalExpression(expression) {
  933. const result = this.applyPluginsBailResult1("expression ?:", expression);
  934. if(result === undefined) {
  935. this.walkExpression(expression.test);
  936. this.walkExpression(expression.consequent);
  937. if(expression.alternate)
  938. this.walkExpression(expression.alternate);
  939. } else {
  940. if(result)
  941. this.walkExpression(expression.consequent);
  942. else if(expression.alternate)
  943. this.walkExpression(expression.alternate);
  944. }
  945. }
  946. walkNewExpression(expression) {
  947. this.walkExpression(expression.callee);
  948. if(expression.arguments)
  949. this.walkExpressions(expression.arguments);
  950. }
  951. walkYieldExpression(expression) {
  952. if(expression.argument)
  953. this.walkExpression(expression.argument);
  954. }
  955. walkTemplateLiteral(expression) {
  956. if(expression.expressions)
  957. this.walkExpressions(expression.expressions);
  958. }
  959. walkTaggedTemplateExpression(expression) {
  960. if(expression.tag)
  961. this.walkExpression(expression.tag);
  962. if(expression.quasi && expression.quasi.expressions)
  963. this.walkExpressions(expression.quasi.expressions);
  964. }
  965. walkClassExpression(expression) {
  966. this.walkClass(expression);
  967. }
  968. walkCallExpression(expression) {
  969. let result;
  970. function walkIIFE(functionExpression, options) {
  971. const params = functionExpression.params;
  972. const args = options.map(function(arg) {
  973. const renameIdentifier = this.getRenameIdentifier(arg);
  974. if(renameIdentifier && this.applyPluginsBailResult1("can-rename " + renameIdentifier, arg)) {
  975. if(!this.applyPluginsBailResult1("rename " + renameIdentifier, arg))
  976. return renameIdentifier;
  977. }
  978. this.walkExpression(arg);
  979. }, this);
  980. this.inScope(params.filter(function(identifier, idx) {
  981. return !args[idx];
  982. }), function() {
  983. for(let i = 0; i < args.length; i++) {
  984. const param = args[i];
  985. if(!param) continue;
  986. if(!params[i] || params[i].type !== "Identifier") continue;
  987. this.scope.renames["$" + params[i].name] = param;
  988. }
  989. if(functionExpression.body.type === "BlockStatement") {
  990. this.prewalkStatement(functionExpression.body);
  991. this.walkStatement(functionExpression.body);
  992. } else
  993. this.walkExpression(functionExpression.body);
  994. }.bind(this));
  995. }
  996. if(expression.callee.type === "MemberExpression" &&
  997. expression.callee.object.type === "FunctionExpression" &&
  998. !expression.callee.computed &&
  999. (["call", "bind"]).indexOf(expression.callee.property.name) >= 0 &&
  1000. expression.arguments &&
  1001. expression.arguments.length > 1
  1002. ) {
  1003. // (function(...) { }.call/bind(?, ...))
  1004. walkIIFE.call(this, expression.callee.object, expression.arguments.slice(1));
  1005. this.walkExpression(expression.arguments[0]);
  1006. } else if(expression.callee.type === "FunctionExpression" && expression.arguments) {
  1007. // (function(...) { }(...))
  1008. walkIIFE.call(this, expression.callee, expression.arguments);
  1009. } else if(expression.callee.type === "Import") {
  1010. result = this.applyPluginsBailResult1("import-call", expression);
  1011. if(result === true)
  1012. return;
  1013. if(expression.arguments)
  1014. this.walkExpressions(expression.arguments);
  1015. } else {
  1016. const callee = this.evaluateExpression(expression.callee);
  1017. if(callee.isIdentifier()) {
  1018. result = this.applyPluginsBailResult1("call " + callee.identifier, expression);
  1019. if(result === true)
  1020. return;
  1021. }
  1022. if(expression.callee)
  1023. this.walkExpression(expression.callee);
  1024. if(expression.arguments)
  1025. this.walkExpressions(expression.arguments);
  1026. }
  1027. }
  1028. walkMemberExpression(expression) {
  1029. let expr = expression;
  1030. let exprName = [];
  1031. while(expr.type === "MemberExpression" && expr.property.type === (expr.computed ? "Literal" : "Identifier")) {
  1032. exprName.unshift(expr.property.name || expr.property.value);
  1033. expr = expr.object;
  1034. }
  1035. if(expr.type === "Identifier" && this.scope.definitions.indexOf(expr.name) === -1) {
  1036. exprName.unshift(this.scope.renames["$" + expr.name] || expr.name);
  1037. let result = this.applyPluginsBailResult1("expression " + exprName.join("."), expression);
  1038. if(result === true)
  1039. return;
  1040. exprName[exprName.length - 1] = "*";
  1041. result = this.applyPluginsBailResult1("expression " + exprName.join("."), expression);
  1042. if(result === true)
  1043. return;
  1044. }
  1045. this.walkExpression(expression.object);
  1046. if(expression.computed === true)
  1047. this.walkExpression(expression.property);
  1048. }
  1049. walkIdentifier(expression) {
  1050. if(this.scope.definitions.indexOf(expression.name) === -1) {
  1051. const result = this.applyPluginsBailResult1("expression " + (this.scope.renames["$" + expression.name] || expression.name), expression);
  1052. if(result === true)
  1053. return;
  1054. }
  1055. }
  1056. inScope(params, fn) {
  1057. const oldScope = this.scope;
  1058. this.scope = {
  1059. inTry: false,
  1060. inShorthand: false,
  1061. definitions: oldScope.definitions.slice(),
  1062. renames: Object.create(oldScope.renames)
  1063. };
  1064. for(let paramIndex = 0, len = params.length; paramIndex < len; paramIndex++) {
  1065. const param = params[paramIndex];
  1066. if(typeof param !== "string") {
  1067. this.enterPattern(param, param => {
  1068. this.scope.renames["$" + param] = undefined;
  1069. this.scope.definitions.push(param);
  1070. });
  1071. } else {
  1072. this.scope.renames["$" + param] = undefined;
  1073. this.scope.definitions.push(param);
  1074. }
  1075. }
  1076. fn();
  1077. this.scope = oldScope;
  1078. }
  1079. enterPattern(pattern, onIdent) {
  1080. if(pattern && this["enter" + pattern.type])
  1081. this["enter" + pattern.type](pattern, onIdent);
  1082. }
  1083. enterIdentifier(pattern, onIdent) {
  1084. onIdent(pattern.name, pattern);
  1085. }
  1086. enterObjectPattern(pattern, onIdent) {
  1087. for(let propIndex = 0, len = pattern.properties.length; propIndex < len; propIndex++) {
  1088. const prop = pattern.properties[propIndex];
  1089. this.enterPattern(prop.value, onIdent);
  1090. }
  1091. }
  1092. enterArrayPattern(pattern, onIdent) {
  1093. for(let elementIndex = 0, len = pattern.elements.length; elementIndex < len; elementIndex++) {
  1094. const element = pattern.elements[elementIndex];
  1095. this.enterPattern(element, onIdent);
  1096. }
  1097. }
  1098. enterRestElement(pattern, onIdent) {
  1099. this.enterPattern(pattern.argument, onIdent);
  1100. }
  1101. enterAssignmentPattern(pattern, onIdent) {
  1102. this.enterPattern(pattern.left, onIdent);
  1103. }
  1104. evaluateExpression(expression) {
  1105. try {
  1106. const result = this.applyPluginsBailResult1("evaluate " + expression.type, expression);
  1107. if(result !== undefined)
  1108. return result;
  1109. } catch(e) {
  1110. console.warn(e);
  1111. // ignore error
  1112. }
  1113. return new BasicEvaluatedExpression().setRange(expression.range);
  1114. }
  1115. parseString(expression) {
  1116. switch(expression.type) {
  1117. case "BinaryExpression":
  1118. if(expression.operator === "+")
  1119. return this.parseString(expression.left) + this.parseString(expression.right);
  1120. break;
  1121. case "Literal":
  1122. return expression.value + "";
  1123. }
  1124. throw new Error(expression.type + " is not supported as parameter for require");
  1125. }
  1126. parseCalculatedString(expression) {
  1127. switch(expression.type) {
  1128. case "BinaryExpression":
  1129. if(expression.operator === "+") {
  1130. const left = this.parseCalculatedString(expression.left);
  1131. const right = this.parseCalculatedString(expression.right);
  1132. if(left.code) {
  1133. return {
  1134. range: left.range,
  1135. value: left.value,
  1136. code: true
  1137. };
  1138. } else if(right.code) {
  1139. return {
  1140. range: [left.range[0], right.range ? right.range[1] : left.range[1]],
  1141. value: left.value + right.value,
  1142. code: true
  1143. };
  1144. } else {
  1145. return {
  1146. range: [left.range[0], right.range[1]],
  1147. value: left.value + right.value
  1148. };
  1149. }
  1150. }
  1151. break;
  1152. case "ConditionalExpression":
  1153. {
  1154. const consequent = this.parseCalculatedString(expression.consequent);
  1155. const alternate = this.parseCalculatedString(expression.alternate);
  1156. const items = [];
  1157. if(consequent.conditional)
  1158. Array.prototype.push.apply(items, consequent.conditional);
  1159. else if(!consequent.code)
  1160. items.push(consequent);
  1161. else break;
  1162. if(alternate.conditional)
  1163. Array.prototype.push.apply(items, alternate.conditional);
  1164. else if(!alternate.code)
  1165. items.push(alternate);
  1166. else break;
  1167. return {
  1168. value: "",
  1169. code: true,
  1170. conditional: items
  1171. };
  1172. }
  1173. case "Literal":
  1174. return {
  1175. range: expression.range,
  1176. value: expression.value + ""
  1177. };
  1178. }
  1179. return {
  1180. value: "",
  1181. code: true
  1182. };
  1183. }
  1184. parseStringArray(expression) {
  1185. if(expression.type !== "ArrayExpression") {
  1186. return [this.parseString(expression)];
  1187. }
  1188. const arr = [];
  1189. if(expression.elements)
  1190. expression.elements.forEach(function(expr) {
  1191. arr.push(this.parseString(expr));
  1192. }, this);
  1193. return arr;
  1194. }
  1195. parseCalculatedStringArray(expression) {
  1196. if(expression.type !== "ArrayExpression") {
  1197. return [this.parseCalculatedString(expression)];
  1198. }
  1199. const arr = [];
  1200. if(expression.elements)
  1201. expression.elements.forEach(function(expr) {
  1202. arr.push(this.parseCalculatedString(expr));
  1203. }, this);
  1204. return arr;
  1205. }
  1206. parse(source, initialState) {
  1207. let ast;
  1208. const comments = [];
  1209. for(let i = 0, len = POSSIBLE_AST_OPTIONS.length; i < len; i++) {
  1210. if(!ast) {
  1211. try {
  1212. comments.length = 0;
  1213. POSSIBLE_AST_OPTIONS[i].onComment = comments;
  1214. ast = acorn.parse(source, POSSIBLE_AST_OPTIONS[i]);
  1215. } catch(e) {
  1216. // ignore the error
  1217. }
  1218. }
  1219. }
  1220. if(!ast) {
  1221. // for the error
  1222. ast = acorn.parse(source, {
  1223. ranges: true,
  1224. locations: true,
  1225. ecmaVersion: 2017,
  1226. sourceType: "module",
  1227. plugins: {
  1228. dynamicImport: true
  1229. },
  1230. onComment: comments
  1231. });
  1232. }
  1233. if(!ast || typeof ast !== "object")
  1234. throw new Error("Source couldn't be parsed");
  1235. const oldScope = this.scope;
  1236. const oldState = this.state;
  1237. const oldComments = this.comments;
  1238. this.scope = {
  1239. inTry: false,
  1240. definitions: [],
  1241. renames: {}
  1242. };
  1243. const state = this.state = initialState || {};
  1244. this.comments = comments;
  1245. if(this.applyPluginsBailResult("program", ast, comments) === undefined) {
  1246. this.prewalkStatements(ast.body);
  1247. this.walkStatements(ast.body);
  1248. }
  1249. this.scope = oldScope;
  1250. this.state = oldState;
  1251. this.comments = oldComments;
  1252. return state;
  1253. }
  1254. evaluate(source) {
  1255. const ast = acorn.parse("(" + source + ")", {
  1256. ranges: true,
  1257. locations: true,
  1258. ecmaVersion: 2017,
  1259. sourceType: "module",
  1260. plugins: {
  1261. dynamicImport: true
  1262. }
  1263. });
  1264. if(!ast || typeof ast !== "object" || ast.type !== "Program")
  1265. throw new Error("evaluate: Source couldn't be parsed");
  1266. if(ast.body.length !== 1 || ast.body[0].type !== "ExpressionStatement")
  1267. throw new Error("evaluate: Source is not a expression");
  1268. return this.evaluateExpression(ast.body[0].expression);
  1269. }
  1270. getComments(range) {
  1271. return this.comments.filter(comment => comment.range[0] >= range[0] && comment.range[1] <= range[1]);
  1272. }
  1273. getCommentOptions(range) {
  1274. const comments = this.getComments(range);
  1275. if(comments.length === 0) return null;
  1276. const options = comments.map(comment => {
  1277. try {
  1278. return json5.parse(`{${comment.value}}`);
  1279. } catch(e) {
  1280. return {};
  1281. }
  1282. });
  1283. return options.reduce((o, i) => Object.assign(o, i), {});
  1284. }
  1285. }
  1286. module.exports = Parser;