license import { ɵParsedTranslation , ɵisMissingTranslationError , ɵmakeTemplateObject , ɵtranslate , } from '@angular/localize' ; import { NodePath } from '@babel/traverse' ; import * as t from '@babel/types' ; import { Diagnostics } from '../common/diagnostics' ; param expression export function isNamedIdentifier ( expression , name ) { return expression . isIdentifier ( ) && expression . node . name === name ; } param identifier export function isGlobalIdentifier ( identifier ) { return ( ! identifier . scope || ! identifier . scope . hasBinding ( identifier . node . name ) ) ; } param messageParts param substitutions export function buildLocalizeReplacement ( messageParts , substitutions ) { let mappedString = t . stringLiteral ( messageParts [ 0 ] ) ; for ( let i = 1 ; i < messageParts . length ; i ++ ) { mappedString = t . binaryExpression ( '+' , mappedString , wrapInParensIfNecessary ( substitutions [ i - 1 ] ) ) ; mappedString = t . binaryExpression ( '+' , mappedString , t . stringLiteral ( messageParts [ i ] ) ) ; } return mappedString ; } param call export function unwrapMessagePartsFromLocalizeCall ( call ) { let cooked = call . get ( 'arguments' ) [ 0 ] as any ; if ( cooked === undefined ) { throw new BabelParseError ( call . node , '`$localize` called without any arguments.' ) ; } if ( ! cooked . isExpression ( ) ) { throw new BabelParseError ( cooked . node , 'Unexpected argument to `$localize` (expected an array).' ) ; } let raw = cooked ; if ( cooked . isLogicalExpression ( ) && cooked . node . operator === '||' && cooked . get ( 'left' ) . isIdentifier ( ) ) { const right = cooked . get ( 'right' ) ; if ( right . isAssignmentExpression ( ) ) { cooked = right . get ( 'right' ) ; if ( ! cooked . isExpression ( ) ) { throw new BabelParseError ( cooked . node , 'Unexpected "makeTemplateObject()" function (expected an expression).' ) ; } } } if ( cooked . isCallExpression ( ) ) { let cookedCall = cooked ; if ( cookedCall . get ( 'arguments' ) . length === 0 ) { cookedCall = unwrapLazyLoadHelperCall ( cookedCall ) ; } cooked = cookedCall . get ( 'arguments' ) [ 0 ] ; if ( ! cooked . isExpression ( ) ) { throw new BabelParseError ( cooked . node , 'Unexpected `cooked` argument to the "makeTemplateObject()" function (expected an expression).' ) ; } const arg2 = cookedCall . get ( 'arguments' ) [ 1 ] ; if ( arg2 && ! arg2 . isExpression ( ) ) { throw new BabelParseError ( arg2 . node , 'Unexpected `raw` argument to the "makeTemplateObject()" function (expected an expression).' ) ; } raw = arg2 !== undefined ? arg2 : cooked ; } const cookedStrings = unwrapStringLiteralArray ( cooked . node ) ; const rawStrings = unwrapStringLiteralArray ( raw . node ) ; return ɵmakeTemplateObject ( cookedStrings , rawStrings ) ; } export function unwrapSubstitutionsFromLocalizeCall ( call ) { const expressions = call . arguments . splice ( 1 ) ; if ( ! isArrayOfExpressions ( expressions ) ) { const badExpression = expressions . find ( ( expression ) => ! t . isExpression ( expression ) ) ! ; throw new BabelParseError ( badExpression , 'Invalid substitutions for `$localize` (expected all substitution arguments to be expressions).' ) ; } return expressions ; } export function unwrapMessagePartsFromTemplateLiteral ( elements ) { const cooked = elements . map ( ( q ) => { if ( q . value . cooked === undefined ) { throw new BabelParseError ( q , `Unexpected undefined message part in "${ elements . map ( ( eq ) => eq . value . cooked ) }"` ) ; } return q . value . cooked ; } ) ; const raw = elements . map ( ( q ) => q . value . raw ) ; return ɵmakeTemplateObject ( cooked , raw ) ; } param expression export function wrapInParensIfNecessary ( expression ) { if ( t . isBinaryExpression ( expression ) ) { return t . parenthesizedExpression ( expression ) ; } else { return expression ; } } param array export function unwrapStringLiteralArray ( array ) { if ( ! isStringLiteralArray ( array ) ) { throw new BabelParseError ( array , 'Unexpected messageParts for `$localize` (expected an array of strings).' ) ; } return array . elements . map ( ( str ) => str . value ) ; } param call returns export function unwrapLazyLoadHelperCall ( call ) { const callee = call . get ( 'callee' ) as any ; if ( ! callee . isIdentifier ( ) ) { throw new BabelParseError ( callee . node , 'Unexpected lazy-load helper call (expected a call of the form `_templateObject()`).' ) ; } const lazyLoadBinding = call . scope . getBinding ( callee . node . name ) ; if ( ! lazyLoadBinding ) { throw new BabelParseError ( callee . node , 'Missing declaration for lazy-load helper function' ) ; } const lazyLoadFn = lazyLoadBinding . path as NodePath < t . FunctionDeclaration > ; if ( ! lazyLoadFn . isFunctionDeclaration ( ) ) { throw new BabelParseError ( ( lazyLoadFn as any ) . node , 'Unexpected expression (expected a function declaration' ) ; } const returnedNode = getReturnedExpression ( lazyLoadFn ) ; if ( returnedNode . isCallExpression ( ) ) { return returnedNode ; } if ( returnedNode . isIdentifier ( ) ) { const identifierName = returnedNode . node . name ; const declaration = returnedNode . scope . getBinding ( identifierName ) ; if ( declaration === undefined ) { throw new BabelParseError ( returnedNode . node , 'Missing declaration for return value from helper.' ) ; } if ( ! declaration . path . isVariableDeclarator ( ) ) { throw new BabelParseError ( ( declaration as any ) . path . node , 'Unexpected helper return value declaration (expected a variable declaration).' ) ; } const initializer = declaration . path . get ( 'init' ) as NodePath < t . CallExpression > ; if ( ! initializer . isCallExpression ( ) ) { throw new BabelParseError ( declaration . path . node , 'Unexpected return value from helper (expected a call expression).' ) ; } if ( lazyLoadBinding . references === 1 ) { lazyLoadFn . remove ( ) ; } return initializer ; } return call ; } function getReturnedExpression ( fn ) { const bodyStatements = ( fn . get ( 'body' ) as any ) . get ( 'body' ) ; for ( const statement of bodyStatements ) { if ( statement . isReturnStatement ( ) ) { const argument = statement . get ( 'argument' ) ; if ( argument . isSequenceExpression ( ) ) { const expressions = argument . get ( 'expressions' ) ; return Array . isArray ( expressions ) ? expressions [ expressions . length - 1 ] : expressions ; } else if ( argument . isExpression ( ) ) { return argument ; } else { throw new BabelParseError ( statement . node , 'Invalid return argument in helper function (expected an expression).' ) ; } } } throw new BabelParseError ( fn . node , 'Missing return statement in helper function.' ) ; } param node export function isStringLiteralArray ( node ) { return ( t . isArrayExpression ( node ) && node . elements . every ( ( element ) => t . isStringLiteral ( element ) ) ) ; } param nodes export function isArrayOfExpressions ( nodes ) { return nodes . every ( ( element ) => t . isExpression ( element ) ) ; } /** Options that affect how the `makeEsXXXTranslatePlugin()` functions work. */ export interface TranslatePluginOptions { missingTranslation ? : MissingTranslationStrategy ; localizeName ? : string ; } /**
 * How to handle missing translations.
 */ export type MissingTranslationStrategy = 'error' | 'warning' | 'ignore' ; /**
 * Translate the text of the given message, using the given translations.
 *
 * Logs as warning if the translation is not available
 */ export function translate ( diagnostics , translations , messageParts , substitutions , missingTranslation ) { try { return ɵtranslate ( translations , messageParts , substitutions ) ; } catch ( e ) { if ( ɵisMissingTranslationError ( e ) ) { if ( missingTranslation === 'error' ) { diagnostics . error ( e . message ) ; } else if ( missingTranslation === 'warning' ) { diagnostics . warn ( e . message ) ; } return [ ɵmakeTemplateObject ( e . parsedMessage . messageParts , e . parsedMessage . messageParts ) , substitutions , ] ; } else { diagnostics . error ( e . message ) ; return [ messageParts , substitutions ] ; } } } export class BabelParseError extends Error { private readonly type = 'BabelParseError' ; constructor ( public node , message ) { super ( message ) ; } } export function isBabelParseError ( e ) { return e . type === 'BabelParseError' ; }