"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.transformSpan = exports.transform = void 0; const utils_1 = require("./utils"); const transform = (node, context, isInParentParens = false) => { const type = utils_1.getNgType(node); switch (type) { case 'Unary': { // @ts-ignore: there is no `Unary` in `@angular/compiler@<10.1.0` const { operator, expr } = node; const tArgument = _t(expr); return _c('UnaryExpression', { prefix: true, argument: tArgument, operator: operator, }, node.span, { hasParentParens: isInParentParens }); } case 'Binary': { const { left, operation, right } = node; const isPrefixAdd = right.span.start === right.span.end; // +1 === 1 - 0 const isPrefixMinus = left.span.start === left.span.end; // -1 === 0 - 1 // `@angular/compiler` changed this to `Unary` since `v10.1.0` // istanbul ignore next if (isPrefixAdd || isPrefixMinus) { const tArgument = left.span.start === left.span.end ? _t(right) : _t(left); return _c('UnaryExpression', { prefix: true, argument: tArgument, operator: isPrefixAdd ? '+' : '-', }, { start: node.span.start, end: _getOuterEnd(tArgument), }, { hasParentParens: isInParentParens }); } const tLeft = _t(left); const tRight = _t(right); return _c(operation === '&&' || operation === '||' ? 'LogicalExpression' : 'BinaryExpression', { left: tLeft, right: tRight, // @ts-ignore operator: operation, }, { start: _getOuterStart(tLeft), end: _getOuterEnd(tRight) }, { hasParentParens: isInParentParens }); } case 'BindingPipe': { const { exp, name, args } = node; const tExp = _t(exp); const nameStart = _findBackChar(/\S/, _findBackChar(/\|/, _getOuterEnd(tExp)) + 1); const tName = _c('Identifier', { name }, { start: nameStart, end: nameStart + name.length }); const tArgs = args.map(_t); return _c('NGPipeExpression', { left: tExp, right: tName, arguments: tArgs, }, { start: _getOuterStart(tExp), end: _getOuterEnd(tArgs.length === 0 ? tName : utils_1.getLast(tArgs)), }, { hasParentParens: isInParentParens }); } case 'Chain': { const { expressions } = node; return _c('NGChainedExpression', { expressions: expressions.map(_t) }, node.span, { hasParentParens: isInParentParens }); } case 'Comment': { const { value } = node; return _c('CommentLine', { value }, node.span, { processSpan: false, }); } case 'Conditional': { const { condition, trueExp, falseExp } = node; const tCondition = _t(condition); const tTrueExp = _t(trueExp); const tFalseExp = _t(falseExp); return _c('ConditionalExpression', { test: tCondition, consequent: tTrueExp, alternate: tFalseExp, }, { start: _getOuterStart(tCondition), end: _getOuterEnd(tFalseExp) }, { hasParentParens: isInParentParens }); } case 'EmptyExpr': return _c('NGEmptyExpression', {}, node.span, { hasParentParens: isInParentParens, }); case 'FunctionCall': { const { target, args } = node; const tArgs = args.length === 1 ? [_transformHasParentParens(args[0])] : args.map(_t); const tTarget = _t(target); return _c('CallExpression', { callee: tTarget, arguments: tArgs, }, { start: _getOuterStart(tTarget), end: node.span.end, }, { hasParentParens: isInParentParens }); } case 'ImplicitReceiver': { return _c('ThisExpression', {}, node.span, { hasParentParens: isInParentParens, }); } case 'KeyedRead': { const { obj, key } = node; const tKey = _t(key); return _transformReceiverAndName(obj, tKey, { computed: true, optional: false, }, { end: node.span.end, hasParentParens: isInParentParens, }); } case 'LiteralArray': { const { expressions } = node; return _c('ArrayExpression', { elements: expressions.map(_t) }, node.span, { hasParentParens: isInParentParens }); } case 'LiteralMap': { const { keys, values } = node; const tValues = values.map(value => _t(value)); const tProperties = keys.map(({ key, quoted }, index) => { const tValue = tValues[index]; const keyStart = _findBackChar(/\S/, index === 0 ? node.span.start + 1 // { : _findBackChar(/,/, _getOuterEnd(tValues[index - 1])) + 1); const keyEnd = _findFrontChar(/\S/, _findFrontChar(/:/, _getOuterStart(tValue) - 1) - 1) + 1; const keySpan = { start: keyStart, end: keyEnd }; const tKey = quoted ? _c('StringLiteral', { value: key }, keySpan) : _c('Identifier', { name: key }, keySpan); return _c('ObjectProperty', { key: tKey, value: tValue, method: false, shorthand: false, computed: false, }, { start: _getOuterStart(tKey), end: _getOuterEnd(tValue) }); }); return _c('ObjectExpression', { properties: tProperties }, node.span, { hasParentParens: isInParentParens }); } case 'LiteralPrimitive': { const { value } = node; switch (typeof value) { case 'boolean': return _c('BooleanLiteral', { value }, node.span, { hasParentParens: isInParentParens, }); case 'number': return _c('NumericLiteral', { value }, node.span, { hasParentParens: isInParentParens, }); case 'object': return _c('NullLiteral', {}, node.span, { hasParentParens: isInParentParens, }); case 'string': return _c('StringLiteral', { value }, node.span, { hasParentParens: isInParentParens, }); case 'undefined': return _c('Identifier', { name: 'undefined' }, node.span, { hasParentParens: isInParentParens }); // istanbul ignore next default: throw new Error(`Unexpected LiteralPrimitive value type ${typeof value}`); } } case 'MethodCall': case 'SafeMethodCall': { const isOptionalType = type === 'SafeMethodCall'; const { receiver, name, args } = node; const tArgs = args.length === 1 ? [_transformHasParentParens(args[0])] : args.map(_t); const nameEnd = _findFrontChar(/\S/, _findFrontChar(/\(/, (tArgs.length === 0 ? _findFrontChar(/\)/, node.span.end - 1) : _getOuterStart(tArgs[0])) - 1) - 1) + 1; const tName = _c('Identifier', { name }, { start: nameEnd - name.length, end: nameEnd }); const tReceiverAndName = _transformReceiverAndName(receiver, tName, { computed: false, optional: isOptionalType, }); const isOptionalReceiver = _isOptionalReceiver(tReceiverAndName); return _c(isOptionalType || isOptionalReceiver ? 'OptionalCallExpression' : 'CallExpression', { callee: tReceiverAndName, arguments: tArgs, }, { start: _getOuterStart(tReceiverAndName), end: node.span.end, }, { hasParentParens: isInParentParens }); } case 'NonNullAssert': { const { expression } = node; const tExpression = _t(expression); return _c('TSNonNullExpression', { expression: tExpression }, { start: _getOuterStart(tExpression), end: node.span.end, }, { hasParentParens: isInParentParens }); } case 'PrefixNot': { const { expression } = node; const tExpression = _t(expression); return _c('UnaryExpression', { prefix: true, operator: '!', argument: tExpression, }, { start: node.span.start, end: _getOuterEnd(tExpression), }, { hasParentParens: isInParentParens }); } case 'PropertyRead': case 'SafePropertyRead': { const isOptionalType = type === 'SafePropertyRead'; const { receiver, name } = node; const nameEnd = _findFrontChar(/\S/, node.span.end - 1) + 1; const tName = _c('Identifier', { name }, { start: nameEnd - name.length, end: nameEnd }, _isImplicitThis(receiver) ? { hasParentParens: isInParentParens } : {}); return _transformReceiverAndName(receiver, tName, { computed: false, optional: isOptionalType, }, { hasParentParens: isInParentParens }); } case 'KeyedWrite': { const { obj, key, value } = node; const tKey = _t(key); const tValue = _t(value); const tReceiverAndName = _transformReceiverAndName(obj, tKey, { computed: true, optional: false, }, { end: _findBackChar(/\]/, _getOuterEnd(tKey)) + 1 }); return _c('AssignmentExpression', { left: tReceiverAndName, operator: '=', right: tValue, }, { start: _getOuterStart(tReceiverAndName), end: _getOuterEnd(tValue) }, { hasParentParens: isInParentParens }); } case 'PropertyWrite': { const { receiver, name, value } = node; const tValue = _t(value); const nameEnd = _findFrontChar(/\S/, _findFrontChar(/=/, _getOuterStart(tValue) - 1) - 1) + 1; const tName = _c('Identifier', { name }, { start: nameEnd - name.length, end: nameEnd }); const tReceiverAndName = _transformReceiverAndName(receiver, tName, { computed: false, optional: false, }); return _c('AssignmentExpression', { left: tReceiverAndName, operator: '=', right: tValue, }, { start: _getOuterStart(tReceiverAndName), end: _getOuterEnd(tValue) }, { hasParentParens: isInParentParens }); } case 'Quote': { const { prefix, uninterpretedExpression } = node; return _c('NGQuotedExpression', { prefix, value: uninterpretedExpression, }, node.span, { hasParentParens: isInParentParens }); } // istanbul ignore next default: throw new Error(`Unexpected node ${type}`); } function _t(n) { return exports.transform(n, context); } function _transformHasParentParens(n) { return exports.transform(n, context, true); } function _c(t, n, span, { processSpan = true, hasParentParens = false } = {}) { const newNode = Object.assign(Object.assign({ type: t }, transformSpan(span, context, processSpan, hasParentParens)), n); switch (t) { case 'Identifier': { const identifier = newNode; identifier.loc.identifierName = identifier.name; break; } case 'NumericLiteral': { const numericLiteral = newNode; numericLiteral.extra = Object.assign(Object.assign({}, numericLiteral.extra), { raw: context.text.slice(numericLiteral.start, numericLiteral.end), rawValue: numericLiteral.value }); break; } case 'StringLiteral': { const stringLiteral = newNode; stringLiteral.extra = Object.assign(Object.assign({}, stringLiteral.extra), { raw: context.text.slice(stringLiteral.start, stringLiteral.end), rawValue: stringLiteral.value }); break; } } return newNode; } function _transformReceiverAndName(receiver, tName, props, { end = _getOuterEnd(tName), hasParentParens = false } = {}) { if (_isImplicitThis(receiver)) { return tName; } const tReceiver = _t(receiver); const isOptionalReceiver = _isOptionalReceiver(tReceiver); return _c(props.optional || isOptionalReceiver ? 'OptionalMemberExpression' : 'MemberExpression', Object.assign({ object: tReceiver, property: tName, computed: props.computed }, (props.optional ? { optional: true } : isOptionalReceiver ? { optional: false } : null)), { start: _getOuterStart(tReceiver), end }, { hasParentParens }); } function _findFrontChar(regex, index) { return utils_1.findFrontChar(regex, index, context.text); } function _findBackChar(regex, index) { return utils_1.findBackChar(regex, index, context.text); } function _isImplicitThis(n) { return (n.span.start >= n.span.end || /^\s+$/.test(context.text.slice(n.span.start, n.span.end))); } function _isOptionalReceiver(n) { return ((n.type === 'OptionalCallExpression' || n.type === 'OptionalMemberExpression') && !_isParenthesized(n)); } function _isParenthesized(n) { // @ts-ignore return n.extra && n.extra.parenthesized; } function _getOuterStart(n) { // @ts-ignore return _isParenthesized(n) ? n.extra.parenStart : n.start; } function _getOuterEnd(n) { // @ts-ignore return _isParenthesized(n) ? n.extra.parenEnd : n.end; } }; exports.transform = transform; function transformSpan(span, context, processSpan = false, hasParentParens = false) { if (!processSpan) { const { start, end } = span; return { start, end, loc: { start: context.locator.locationForIndex(start), end: context.locator.locationForIndex(end), }, }; } const { outerSpan, innerSpan, hasParens } = utils_1.fitSpans(span, context.text, hasParentParens); return Object.assign({ start: innerSpan.start, end: innerSpan.end, loc: { start: context.locator.locationForIndex(innerSpan.start), end: context.locator.locationForIndex(innerSpan.end), } }, (hasParens && { extra: { parenthesized: true, parenStart: outerSpan.start, parenEnd: outerSpan.end, }, })); } exports.transformSpan = transformSpan;