transform.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.transformSpan = exports.transform = void 0;
  4. const utils_1 = require("./utils");
  5. const transform = (node, context, isInParentParens = false) => {
  6. const type = utils_1.getNgType(node);
  7. switch (type) {
  8. case 'Unary': {
  9. // @ts-ignore: there is no `Unary` in `@angular/compiler@<10.1.0`
  10. const { operator, expr } = node;
  11. const tArgument = _t(expr);
  12. return _c('UnaryExpression', {
  13. prefix: true,
  14. argument: tArgument,
  15. operator: operator,
  16. }, node.span, { hasParentParens: isInParentParens });
  17. }
  18. case 'Binary': {
  19. const { left, operation, right } = node;
  20. const isPrefixAdd = right.span.start === right.span.end; // +1 === 1 - 0
  21. const isPrefixMinus = left.span.start === left.span.end; // -1 === 0 - 1
  22. // `@angular/compiler` changed this to `Unary` since `v10.1.0`
  23. // istanbul ignore next
  24. if (isPrefixAdd || isPrefixMinus) {
  25. const tArgument = left.span.start === left.span.end
  26. ? _t(right)
  27. : _t(left);
  28. return _c('UnaryExpression', {
  29. prefix: true,
  30. argument: tArgument,
  31. operator: isPrefixAdd ? '+' : '-',
  32. }, {
  33. start: node.span.start,
  34. end: _getOuterEnd(tArgument),
  35. }, { hasParentParens: isInParentParens });
  36. }
  37. const tLeft = _t(left);
  38. const tRight = _t(right);
  39. return _c(operation === '&&' || operation === '||'
  40. ? 'LogicalExpression'
  41. : 'BinaryExpression', {
  42. left: tLeft,
  43. right: tRight,
  44. // @ts-ignore
  45. operator: operation,
  46. }, { start: _getOuterStart(tLeft), end: _getOuterEnd(tRight) }, { hasParentParens: isInParentParens });
  47. }
  48. case 'BindingPipe': {
  49. const { exp, name, args } = node;
  50. const tExp = _t(exp);
  51. const nameStart = _findBackChar(/\S/, _findBackChar(/\|/, _getOuterEnd(tExp)) + 1);
  52. const tName = _c('Identifier', { name }, { start: nameStart, end: nameStart + name.length });
  53. const tArgs = args.map(_t);
  54. return _c('NGPipeExpression', {
  55. left: tExp,
  56. right: tName,
  57. arguments: tArgs,
  58. }, {
  59. start: _getOuterStart(tExp),
  60. end: _getOuterEnd(tArgs.length === 0 ? tName : utils_1.getLast(tArgs)),
  61. }, { hasParentParens: isInParentParens });
  62. }
  63. case 'Chain': {
  64. const { expressions } = node;
  65. return _c('NGChainedExpression', { expressions: expressions.map(_t) }, node.span, { hasParentParens: isInParentParens });
  66. }
  67. case 'Comment': {
  68. const { value } = node;
  69. return _c('CommentLine', { value }, node.span, {
  70. processSpan: false,
  71. });
  72. }
  73. case 'Conditional': {
  74. const { condition, trueExp, falseExp } = node;
  75. const tCondition = _t(condition);
  76. const tTrueExp = _t(trueExp);
  77. const tFalseExp = _t(falseExp);
  78. return _c('ConditionalExpression', {
  79. test: tCondition,
  80. consequent: tTrueExp,
  81. alternate: tFalseExp,
  82. }, { start: _getOuterStart(tCondition), end: _getOuterEnd(tFalseExp) }, { hasParentParens: isInParentParens });
  83. }
  84. case 'EmptyExpr':
  85. return _c('NGEmptyExpression', {}, node.span, {
  86. hasParentParens: isInParentParens,
  87. });
  88. case 'FunctionCall': {
  89. const { target, args } = node;
  90. const tArgs = args.length === 1
  91. ? [_transformHasParentParens(args[0])]
  92. : args.map(_t);
  93. const tTarget = _t(target);
  94. return _c('CallExpression', {
  95. callee: tTarget,
  96. arguments: tArgs,
  97. }, {
  98. start: _getOuterStart(tTarget),
  99. end: node.span.end,
  100. }, { hasParentParens: isInParentParens });
  101. }
  102. case 'ImplicitReceiver': {
  103. return _c('ThisExpression', {}, node.span, {
  104. hasParentParens: isInParentParens,
  105. });
  106. }
  107. case 'KeyedRead': {
  108. const { obj, key } = node;
  109. const tKey = _t(key);
  110. return _transformReceiverAndName(obj, tKey, {
  111. computed: true,
  112. optional: false,
  113. }, {
  114. end: node.span.end,
  115. hasParentParens: isInParentParens,
  116. });
  117. }
  118. case 'LiteralArray': {
  119. const { expressions } = node;
  120. return _c('ArrayExpression', { elements: expressions.map(_t) }, node.span, { hasParentParens: isInParentParens });
  121. }
  122. case 'LiteralMap': {
  123. const { keys, values } = node;
  124. const tValues = values.map(value => _t(value));
  125. const tProperties = keys.map(({ key, quoted }, index) => {
  126. const tValue = tValues[index];
  127. const keyStart = _findBackChar(/\S/, index === 0
  128. ? node.span.start + 1 // {
  129. : _findBackChar(/,/, _getOuterEnd(tValues[index - 1])) + 1);
  130. const keyEnd = _findFrontChar(/\S/, _findFrontChar(/:/, _getOuterStart(tValue) - 1) - 1) + 1;
  131. const keySpan = { start: keyStart, end: keyEnd };
  132. const tKey = quoted
  133. ? _c('StringLiteral', { value: key }, keySpan)
  134. : _c('Identifier', { name: key }, keySpan);
  135. return _c('ObjectProperty', {
  136. key: tKey,
  137. value: tValue,
  138. method: false,
  139. shorthand: false,
  140. computed: false,
  141. }, { start: _getOuterStart(tKey), end: _getOuterEnd(tValue) });
  142. });
  143. return _c('ObjectExpression', { properties: tProperties }, node.span, { hasParentParens: isInParentParens });
  144. }
  145. case 'LiteralPrimitive': {
  146. const { value } = node;
  147. switch (typeof value) {
  148. case 'boolean':
  149. return _c('BooleanLiteral', { value }, node.span, {
  150. hasParentParens: isInParentParens,
  151. });
  152. case 'number':
  153. return _c('NumericLiteral', { value }, node.span, {
  154. hasParentParens: isInParentParens,
  155. });
  156. case 'object':
  157. return _c('NullLiteral', {}, node.span, {
  158. hasParentParens: isInParentParens,
  159. });
  160. case 'string':
  161. return _c('StringLiteral', { value }, node.span, {
  162. hasParentParens: isInParentParens,
  163. });
  164. case 'undefined':
  165. return _c('Identifier', { name: 'undefined' }, node.span, { hasParentParens: isInParentParens });
  166. // istanbul ignore next
  167. default:
  168. throw new Error(`Unexpected LiteralPrimitive value type ${typeof value}`);
  169. }
  170. }
  171. case 'MethodCall':
  172. case 'SafeMethodCall': {
  173. const isOptionalType = type === 'SafeMethodCall';
  174. const { receiver, name, args } = node;
  175. const tArgs = args.length === 1
  176. ? [_transformHasParentParens(args[0])]
  177. : args.map(_t);
  178. const nameEnd = _findFrontChar(/\S/, _findFrontChar(/\(/, (tArgs.length === 0
  179. ? _findFrontChar(/\)/, node.span.end - 1)
  180. : _getOuterStart(tArgs[0])) - 1) - 1) + 1;
  181. const tName = _c('Identifier', { name }, { start: nameEnd - name.length, end: nameEnd });
  182. const tReceiverAndName = _transformReceiverAndName(receiver, tName, {
  183. computed: false,
  184. optional: isOptionalType,
  185. });
  186. const isOptionalReceiver = _isOptionalReceiver(tReceiverAndName);
  187. return _c(isOptionalType || isOptionalReceiver
  188. ? 'OptionalCallExpression'
  189. : 'CallExpression', {
  190. callee: tReceiverAndName,
  191. arguments: tArgs,
  192. }, {
  193. start: _getOuterStart(tReceiverAndName),
  194. end: node.span.end,
  195. }, { hasParentParens: isInParentParens });
  196. }
  197. case 'NonNullAssert': {
  198. const { expression } = node;
  199. const tExpression = _t(expression);
  200. return _c('TSNonNullExpression', { expression: tExpression }, {
  201. start: _getOuterStart(tExpression),
  202. end: node.span.end,
  203. }, { hasParentParens: isInParentParens });
  204. }
  205. case 'PrefixNot': {
  206. const { expression } = node;
  207. const tExpression = _t(expression);
  208. return _c('UnaryExpression', {
  209. prefix: true,
  210. operator: '!',
  211. argument: tExpression,
  212. }, {
  213. start: node.span.start,
  214. end: _getOuterEnd(tExpression),
  215. }, { hasParentParens: isInParentParens });
  216. }
  217. case 'PropertyRead':
  218. case 'SafePropertyRead': {
  219. const isOptionalType = type === 'SafePropertyRead';
  220. const { receiver, name } = node;
  221. const nameEnd = _findFrontChar(/\S/, node.span.end - 1) + 1;
  222. const tName = _c('Identifier', { name }, { start: nameEnd - name.length, end: nameEnd }, _isImplicitThis(receiver) ? { hasParentParens: isInParentParens } : {});
  223. return _transformReceiverAndName(receiver, tName, {
  224. computed: false,
  225. optional: isOptionalType,
  226. }, { hasParentParens: isInParentParens });
  227. }
  228. case 'KeyedWrite': {
  229. const { obj, key, value } = node;
  230. const tKey = _t(key);
  231. const tValue = _t(value);
  232. const tReceiverAndName = _transformReceiverAndName(obj, tKey, {
  233. computed: true,
  234. optional: false,
  235. }, { end: _findBackChar(/\]/, _getOuterEnd(tKey)) + 1 });
  236. return _c('AssignmentExpression', {
  237. left: tReceiverAndName,
  238. operator: '=',
  239. right: tValue,
  240. }, { start: _getOuterStart(tReceiverAndName), end: _getOuterEnd(tValue) }, { hasParentParens: isInParentParens });
  241. }
  242. case 'PropertyWrite': {
  243. const { receiver, name, value } = node;
  244. const tValue = _t(value);
  245. const nameEnd = _findFrontChar(/\S/, _findFrontChar(/=/, _getOuterStart(tValue) - 1) - 1) + 1;
  246. const tName = _c('Identifier', { name }, { start: nameEnd - name.length, end: nameEnd });
  247. const tReceiverAndName = _transformReceiverAndName(receiver, tName, {
  248. computed: false,
  249. optional: false,
  250. });
  251. return _c('AssignmentExpression', {
  252. left: tReceiverAndName,
  253. operator: '=',
  254. right: tValue,
  255. }, { start: _getOuterStart(tReceiverAndName), end: _getOuterEnd(tValue) }, { hasParentParens: isInParentParens });
  256. }
  257. case 'Quote': {
  258. const { prefix, uninterpretedExpression } = node;
  259. return _c('NGQuotedExpression', {
  260. prefix,
  261. value: uninterpretedExpression,
  262. }, node.span, { hasParentParens: isInParentParens });
  263. }
  264. // istanbul ignore next
  265. default:
  266. throw new Error(`Unexpected node ${type}`);
  267. }
  268. function _t(n) {
  269. return exports.transform(n, context);
  270. }
  271. function _transformHasParentParens(n) {
  272. return exports.transform(n, context, true);
  273. }
  274. function _c(t, n, span, { processSpan = true, hasParentParens = false } = {}) {
  275. const newNode = Object.assign(Object.assign({ type: t }, transformSpan(span, context, processSpan, hasParentParens)), n);
  276. switch (t) {
  277. case 'Identifier': {
  278. const identifier = newNode;
  279. identifier.loc.identifierName = identifier.name;
  280. break;
  281. }
  282. case 'NumericLiteral': {
  283. const numericLiteral = newNode;
  284. numericLiteral.extra = Object.assign(Object.assign({}, numericLiteral.extra), { raw: context.text.slice(numericLiteral.start, numericLiteral.end), rawValue: numericLiteral.value });
  285. break;
  286. }
  287. case 'StringLiteral': {
  288. const stringLiteral = newNode;
  289. stringLiteral.extra = Object.assign(Object.assign({}, stringLiteral.extra), { raw: context.text.slice(stringLiteral.start, stringLiteral.end), rawValue: stringLiteral.value });
  290. break;
  291. }
  292. }
  293. return newNode;
  294. }
  295. function _transformReceiverAndName(receiver, tName, props, { end = _getOuterEnd(tName), hasParentParens = false } = {}) {
  296. if (_isImplicitThis(receiver)) {
  297. return tName;
  298. }
  299. const tReceiver = _t(receiver);
  300. const isOptionalReceiver = _isOptionalReceiver(tReceiver);
  301. return _c(props.optional || isOptionalReceiver
  302. ? 'OptionalMemberExpression'
  303. : 'MemberExpression', Object.assign({ object: tReceiver, property: tName, computed: props.computed }, (props.optional
  304. ? { optional: true }
  305. : isOptionalReceiver
  306. ? { optional: false }
  307. : null)), { start: _getOuterStart(tReceiver), end }, { hasParentParens });
  308. }
  309. function _findFrontChar(regex, index) {
  310. return utils_1.findFrontChar(regex, index, context.text);
  311. }
  312. function _findBackChar(regex, index) {
  313. return utils_1.findBackChar(regex, index, context.text);
  314. }
  315. function _isImplicitThis(n) {
  316. return (n.span.start >= n.span.end ||
  317. /^\s+$/.test(context.text.slice(n.span.start, n.span.end)));
  318. }
  319. function _isOptionalReceiver(n) {
  320. return ((n.type === 'OptionalCallExpression' ||
  321. n.type === 'OptionalMemberExpression') &&
  322. !_isParenthesized(n));
  323. }
  324. function _isParenthesized(n) {
  325. // @ts-ignore
  326. return n.extra && n.extra.parenthesized;
  327. }
  328. function _getOuterStart(n) {
  329. // @ts-ignore
  330. return _isParenthesized(n) ? n.extra.parenStart : n.start;
  331. }
  332. function _getOuterEnd(n) {
  333. // @ts-ignore
  334. return _isParenthesized(n) ? n.extra.parenEnd : n.end;
  335. }
  336. };
  337. exports.transform = transform;
  338. function transformSpan(span, context, processSpan = false, hasParentParens = false) {
  339. if (!processSpan) {
  340. const { start, end } = span;
  341. return {
  342. start,
  343. end,
  344. loc: {
  345. start: context.locator.locationForIndex(start),
  346. end: context.locator.locationForIndex(end),
  347. },
  348. };
  349. }
  350. const { outerSpan, innerSpan, hasParens } = utils_1.fitSpans(span, context.text, hasParentParens);
  351. return Object.assign({ start: innerSpan.start, end: innerSpan.end, loc: {
  352. start: context.locator.locationForIndex(innerSpan.start),
  353. end: context.locator.locationForIndex(innerSpan.end),
  354. } }, (hasParens && {
  355. extra: {
  356. parenthesized: true,
  357. parenStart: outerSpan.start,
  358. parenEnd: outerSpan.end,
  359. },
  360. }));
  361. }
  362. exports.transformSpan = transformSpan;