Parser.js 45 KB

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