util.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.isTrailingCommaEnabled = exports.getParentExportDeclaration = exports.isExportDeclaration = exports.fixFaultyLocations = exports.getTrueLoc = exports.composeSourceMaps = exports.copyPos = exports.comparePos = exports.getUnionOfKeys = exports.getOption = exports.isBrowser = exports.getLineTerminator = void 0;
  4. var tslib_1 = require("tslib");
  5. var assert_1 = tslib_1.__importDefault(require("assert"));
  6. var types = tslib_1.__importStar(require("ast-types"));
  7. var n = types.namedTypes;
  8. var source_map_1 = tslib_1.__importDefault(require("source-map"));
  9. var SourceMapConsumer = source_map_1.default.SourceMapConsumer;
  10. var SourceMapGenerator = source_map_1.default.SourceMapGenerator;
  11. var hasOwn = Object.prototype.hasOwnProperty;
  12. function getLineTerminator() {
  13. return isBrowser() ? "\n" : require("os").EOL || "\n";
  14. }
  15. exports.getLineTerminator = getLineTerminator;
  16. function isBrowser() {
  17. return (typeof window !== "undefined" && typeof window.document !== "undefined");
  18. }
  19. exports.isBrowser = isBrowser;
  20. function getOption(options, key, defaultValue) {
  21. if (options && hasOwn.call(options, key)) {
  22. return options[key];
  23. }
  24. return defaultValue;
  25. }
  26. exports.getOption = getOption;
  27. function getUnionOfKeys() {
  28. var args = [];
  29. for (var _i = 0; _i < arguments.length; _i++) {
  30. args[_i] = arguments[_i];
  31. }
  32. var result = {};
  33. var argc = args.length;
  34. for (var i = 0; i < argc; ++i) {
  35. var keys = Object.keys(args[i]);
  36. var keyCount = keys.length;
  37. for (var j = 0; j < keyCount; ++j) {
  38. result[keys[j]] = true;
  39. }
  40. }
  41. return result;
  42. }
  43. exports.getUnionOfKeys = getUnionOfKeys;
  44. function comparePos(pos1, pos2) {
  45. return pos1.line - pos2.line || pos1.column - pos2.column;
  46. }
  47. exports.comparePos = comparePos;
  48. function copyPos(pos) {
  49. return {
  50. line: pos.line,
  51. column: pos.column,
  52. };
  53. }
  54. exports.copyPos = copyPos;
  55. function composeSourceMaps(formerMap, latterMap) {
  56. if (formerMap) {
  57. if (!latterMap) {
  58. return formerMap;
  59. }
  60. }
  61. else {
  62. return latterMap || null;
  63. }
  64. var smcFormer = new SourceMapConsumer(formerMap);
  65. var smcLatter = new SourceMapConsumer(latterMap);
  66. var smg = new SourceMapGenerator({
  67. file: latterMap.file,
  68. sourceRoot: latterMap.sourceRoot,
  69. });
  70. var sourcesToContents = {};
  71. smcLatter.eachMapping(function (mapping) {
  72. var origPos = smcFormer.originalPositionFor({
  73. line: mapping.originalLine,
  74. column: mapping.originalColumn,
  75. });
  76. var sourceName = origPos.source;
  77. if (sourceName === null) {
  78. return;
  79. }
  80. smg.addMapping({
  81. source: sourceName,
  82. original: copyPos(origPos),
  83. generated: {
  84. line: mapping.generatedLine,
  85. column: mapping.generatedColumn,
  86. },
  87. name: mapping.name,
  88. });
  89. var sourceContent = smcFormer.sourceContentFor(sourceName);
  90. if (sourceContent && !hasOwn.call(sourcesToContents, sourceName)) {
  91. sourcesToContents[sourceName] = sourceContent;
  92. smg.setSourceContent(sourceName, sourceContent);
  93. }
  94. });
  95. return smg.toJSON();
  96. }
  97. exports.composeSourceMaps = composeSourceMaps;
  98. function getTrueLoc(node, lines) {
  99. // It's possible that node is newly-created (not parsed by Esprima),
  100. // in which case it probably won't have a .loc property (or an
  101. // .original property for that matter). That's fine; we'll just
  102. // pretty-print it as usual.
  103. if (!node.loc) {
  104. return null;
  105. }
  106. var result = {
  107. start: node.loc.start,
  108. end: node.loc.end,
  109. };
  110. function include(node) {
  111. expandLoc(result, node.loc);
  112. }
  113. // If the node is an export declaration and its .declaration has any
  114. // decorators, their locations might contribute to the true start/end
  115. // positions of the export declaration node.
  116. if (node.declaration &&
  117. node.declaration.decorators &&
  118. isExportDeclaration(node)) {
  119. node.declaration.decorators.forEach(include);
  120. }
  121. if (comparePos(result.start, result.end) < 0) {
  122. // Trim leading whitespace.
  123. result.start = copyPos(result.start);
  124. lines.skipSpaces(result.start, false, true);
  125. if (comparePos(result.start, result.end) < 0) {
  126. // Trim trailing whitespace, if the end location is not already the
  127. // same as the start location.
  128. result.end = copyPos(result.end);
  129. lines.skipSpaces(result.end, true, true);
  130. }
  131. }
  132. // If the node has any comments, their locations might contribute to
  133. // the true start/end positions of the node.
  134. if (node.comments) {
  135. node.comments.forEach(include);
  136. }
  137. return result;
  138. }
  139. exports.getTrueLoc = getTrueLoc;
  140. function expandLoc(parentLoc, childLoc) {
  141. if (parentLoc && childLoc) {
  142. if (comparePos(childLoc.start, parentLoc.start) < 0) {
  143. parentLoc.start = childLoc.start;
  144. }
  145. if (comparePos(parentLoc.end, childLoc.end) < 0) {
  146. parentLoc.end = childLoc.end;
  147. }
  148. }
  149. }
  150. function fixFaultyLocations(node, lines) {
  151. var loc = node.loc;
  152. if (loc) {
  153. if (loc.start.line < 1) {
  154. loc.start.line = 1;
  155. }
  156. if (loc.end.line < 1) {
  157. loc.end.line = 1;
  158. }
  159. }
  160. if (node.type === "File") {
  161. // Babylon returns File nodes whose .loc.{start,end} do not include
  162. // leading or trailing whitespace.
  163. loc.start = lines.firstPos();
  164. loc.end = lines.lastPos();
  165. }
  166. fixForLoopHead(node, lines);
  167. fixTemplateLiteral(node, lines);
  168. if (loc && node.decorators) {
  169. // Expand the .loc of the node responsible for printing the decorators
  170. // (here, the decorated node) so that it includes node.decorators.
  171. node.decorators.forEach(function (decorator) {
  172. expandLoc(loc, decorator.loc);
  173. });
  174. }
  175. else if (node.declaration && isExportDeclaration(node)) {
  176. // Nullify .loc information for the child declaration so that we never
  177. // try to reprint it without also reprinting the export declaration.
  178. node.declaration.loc = null;
  179. // Expand the .loc of the node responsible for printing the decorators
  180. // (here, the export declaration) so that it includes node.decorators.
  181. var decorators = node.declaration.decorators;
  182. if (decorators) {
  183. decorators.forEach(function (decorator) {
  184. expandLoc(loc, decorator.loc);
  185. });
  186. }
  187. }
  188. else if ((n.MethodDefinition && n.MethodDefinition.check(node)) ||
  189. (n.Property.check(node) && (node.method || node.shorthand))) {
  190. // If the node is a MethodDefinition or a .method or .shorthand
  191. // Property, then the location information stored in
  192. // node.value.loc is very likely untrustworthy (just the {body}
  193. // part of a method, or nothing in the case of shorthand
  194. // properties), so we null out that information to prevent
  195. // accidental reuse of bogus source code during reprinting.
  196. node.value.loc = null;
  197. if (n.FunctionExpression.check(node.value)) {
  198. // FunctionExpression method values should be anonymous,
  199. // because their .id fields are ignored anyway.
  200. node.value.id = null;
  201. }
  202. }
  203. else if (node.type === "ObjectTypeProperty") {
  204. var loc_1 = node.loc;
  205. var end = loc_1 && loc_1.end;
  206. if (end) {
  207. end = copyPos(end);
  208. if (lines.prevPos(end) && lines.charAt(end) === ",") {
  209. // Some parsers accidentally include trailing commas in the
  210. // .loc.end information for ObjectTypeProperty nodes.
  211. if ((end = lines.skipSpaces(end, true, true))) {
  212. loc_1.end = end;
  213. }
  214. }
  215. }
  216. }
  217. }
  218. exports.fixFaultyLocations = fixFaultyLocations;
  219. function fixForLoopHead(node, lines) {
  220. if (node.type !== "ForStatement") {
  221. return;
  222. }
  223. function fix(child) {
  224. var loc = child && child.loc;
  225. var start = loc && loc.start;
  226. var end = loc && copyPos(loc.end);
  227. while (start && end && comparePos(start, end) < 0) {
  228. lines.prevPos(end);
  229. if (lines.charAt(end) === ";") {
  230. // Update child.loc.end to *exclude* the ';' character.
  231. loc.end.line = end.line;
  232. loc.end.column = end.column;
  233. }
  234. else {
  235. break;
  236. }
  237. }
  238. }
  239. fix(node.init);
  240. fix(node.test);
  241. fix(node.update);
  242. }
  243. function fixTemplateLiteral(node, lines) {
  244. if (node.type !== "TemplateLiteral") {
  245. return;
  246. }
  247. if (node.quasis.length === 0) {
  248. // If there are no quasi elements, then there is nothing to fix.
  249. return;
  250. }
  251. // node.loc is not present when using export default with a template literal
  252. if (node.loc) {
  253. // First we need to exclude the opening ` from the .loc of the first
  254. // quasi element, in case the parser accidentally decided to include it.
  255. var afterLeftBackTickPos = copyPos(node.loc.start);
  256. assert_1.default.strictEqual(lines.charAt(afterLeftBackTickPos), "`");
  257. assert_1.default.ok(lines.nextPos(afterLeftBackTickPos));
  258. var firstQuasi = node.quasis[0];
  259. if (comparePos(firstQuasi.loc.start, afterLeftBackTickPos) < 0) {
  260. firstQuasi.loc.start = afterLeftBackTickPos;
  261. }
  262. // Next we need to exclude the closing ` from the .loc of the last quasi
  263. // element, in case the parser accidentally decided to include it.
  264. var rightBackTickPos = copyPos(node.loc.end);
  265. assert_1.default.ok(lines.prevPos(rightBackTickPos));
  266. assert_1.default.strictEqual(lines.charAt(rightBackTickPos), "`");
  267. var lastQuasi = node.quasis[node.quasis.length - 1];
  268. if (comparePos(rightBackTickPos, lastQuasi.loc.end) < 0) {
  269. lastQuasi.loc.end = rightBackTickPos;
  270. }
  271. }
  272. // Now we need to exclude ${ and } characters from the .loc's of all
  273. // quasi elements, since some parsers accidentally include them.
  274. node.expressions.forEach(function (expr, i) {
  275. // Rewind from expr.loc.start over any whitespace and the ${ that
  276. // precedes the expression. The position of the $ should be the same
  277. // as the .loc.end of the preceding quasi element, but some parsers
  278. // accidentally include the ${ in the .loc of the quasi element.
  279. var dollarCurlyPos = lines.skipSpaces(expr.loc.start, true, false);
  280. if (lines.prevPos(dollarCurlyPos) &&
  281. lines.charAt(dollarCurlyPos) === "{" &&
  282. lines.prevPos(dollarCurlyPos) &&
  283. lines.charAt(dollarCurlyPos) === "$") {
  284. var quasiBefore = node.quasis[i];
  285. if (comparePos(dollarCurlyPos, quasiBefore.loc.end) < 0) {
  286. quasiBefore.loc.end = dollarCurlyPos;
  287. }
  288. }
  289. // Likewise, some parsers accidentally include the } that follows
  290. // the expression in the .loc of the following quasi element.
  291. var rightCurlyPos = lines.skipSpaces(expr.loc.end, false, false);
  292. if (lines.charAt(rightCurlyPos) === "}") {
  293. assert_1.default.ok(lines.nextPos(rightCurlyPos));
  294. // Now rightCurlyPos is technically the position just after the }.
  295. var quasiAfter = node.quasis[i + 1];
  296. if (comparePos(quasiAfter.loc.start, rightCurlyPos) < 0) {
  297. quasiAfter.loc.start = rightCurlyPos;
  298. }
  299. }
  300. });
  301. }
  302. function isExportDeclaration(node) {
  303. if (node)
  304. switch (node.type) {
  305. case "ExportDeclaration":
  306. case "ExportDefaultDeclaration":
  307. case "ExportDefaultSpecifier":
  308. case "DeclareExportDeclaration":
  309. case "ExportNamedDeclaration":
  310. case "ExportAllDeclaration":
  311. return true;
  312. }
  313. return false;
  314. }
  315. exports.isExportDeclaration = isExportDeclaration;
  316. function getParentExportDeclaration(path) {
  317. var parentNode = path.getParentNode();
  318. if (path.getName() === "declaration" && isExportDeclaration(parentNode)) {
  319. return parentNode;
  320. }
  321. return null;
  322. }
  323. exports.getParentExportDeclaration = getParentExportDeclaration;
  324. function isTrailingCommaEnabled(options, context) {
  325. var trailingComma = options.trailingComma;
  326. if (typeof trailingComma === "object") {
  327. return !!trailingComma[context];
  328. }
  329. return !!trailingComma;
  330. }
  331. exports.isTrailingCommaEnabled = isTrailingCommaEnabled;