import { posix as path } from 'path' ; import * as del from 'del' ; import { gte } from 'semver' ; import { main as angularCompilerCli } from '@angular/compiler-cli/src/main' ; import { AngularPackage } from '../angular-package' ; import { writeFile } from '../utilities/write-file' ; import { TypeScriptConfigurationBuilder } from './typescript-configuration-builder' ; import { getInstalledDependencyVersion } from '../utilities/get-installed-dependency-version' ; import { copyFiles } from '../utilities/copy-files' ; import { AngularPackageLogger } from '../logger/angular-package-logger' ; /**
 * Angular Package Compiler
 */ export class AngularPackageCompiler { /**
     * Angular Package
     */ private readonly angularPackage : AngularPackage ; param angularPackage constructor ( angularPackage ) { this . angularPackage = angularPackage ; } param target returns public async compile ( target ) { const tsconfig = await this . buildTypescriptConfiguration ( target ) ; const tsconfigPath = path . join ( this . angularPackage . root , this . angularPackage . outDir , 'temp' , `tsconfig.${ target }.json` ) ; await writeFile ( tsconfigPath , tsconfig ) ; const angularCompilerCliArguments = await this . getAngularCompilerCliArguments ( tsconfigPath ) ; await angularCompilerCli ( angularCompilerCliArguments , ( error ) : void => { this . handleAngularCompilerCliError ( error , tsconfig . compilerOptions . target ) ; } ) ; const buildFilesPattern = '*.+(js?(.map)|d.ts|json)' ; await copyFiles ( path . join ( this . angularPackage . root , this . angularPackage . outDir , 'temp' , 'transformed' , '**' , buildFilesPattern ) , path . join ( this . angularPackage . root , this . angularPackage . outDir , 'temp' , target ) ) ; await del ( [ path . join ( this . angularPackage . root , this . angularPackage . outDir , 'temp' , 'transformed' , '**' , buildFilesPattern ) ] ) ; } param target returns private async buildTypescriptConfiguration ( target ) { const tempDir = path . join ( this . angularPackage . root , this . angularPackage . outDir , 'temp' ) ; const entryDir = path . join ( tempDir , 'transformed' ) ; const outDir = path . join ( tempDir , 'transformed' ) ; const entryFile = path . join ( tempDir , 'transformed' , path . basename ( this . angularPackage . entryFile ) ) ; return new TypeScriptConfigurationBuilder ( ) . setEntry ( entryFile , entryDir ) . setOutDir ( outDir ) . setPackageName ( this . angularPackage . packageName ) . setCompilationTarget ( target ) . setCustomTypescriptCompilerOptions ( this . angularPackage . typescriptCompilerOptions ) . setCustomAngularCompilerOptions ( this . angularPackage . angularCompilerOptions ) . build ( ) ; } param tsconfigPath returns private async getAngularCompilerCliArguments ( tsconfigPath ) { const angularCompilerCliVersion = await getInstalledDependencyVersion ( '@angular/compiler-cli' ) ; const angularCompilerCliArguments = gte ( angularCompilerCliVersion , '5.0.0' ) ? [ '-p' , tsconfigPath ] : { p : tsconfigPath } ; return angularCompilerCliArguments ; } param error param target private handleAngularCompilerCliError ( error , target ) { let details ; switch ( true ) { case /(error|warning) TS0/ . test ( error ) : details = this . constructTypescriptErrorDetails ( error , 'tsickle' ) ; break ; case /(error|warning) TS[0-9]+/ . test ( error ) : details = this . constructTypescriptErrorDetails ( error , 'TypeScript' ) ; break ; case /compiler-cli/ . test ( error ) : details = this . constructAngularCompilerErrorDetails ( error , 'Angular Compiler' ) ; break ; default : details = error ; break ; } const errorMessage = [ `An error occured while compiling the sources to ${ target }.` , '' , details , '' , 'Tip: For known pitfalls, also see https://github.com/dominique-mueller/angular-package-builder#known-pitfalls-with-solutions' ] . join ( '\n' ) ; AngularPackageLogger . logMessage ( errorMessage , 'error' ) ; throw new Error ( errorMessage ) ; } param error param origin returns private constructTypescriptErrorDetails ( error , origin ) { const sourceFile = error . split ( ':' ) [ 0 ] . replace ( /^transformed/ , '.' ) . split ( '(' ) . join ( ' (' ) . replace ( ',' , ':' ) ; const errorCode = error . split ( ':' ) [ 1 ] . trim ( ) ; const errorMessage = error . split ( ':' ) [ 2 ] . trim ( ) . replace ( /\.$/ , '' ) ; return [ `Message:    ${ errorMessage [ 0 ] . toUpperCase ( ) }${ errorMessage . slice ( 1 ) }` , '' , `Caused by:  ${ origin }` , `Code:       ${ errorCode [ 0 ] . toUpperCase ( ) }${ errorCode . slice ( 1 ) }` , `File:       ${ sourceFile } [to be compiled]` ] . join ( '\n' ) ; } param error param origin returns private constructAngularCompilerErrorDetails ( error , origin ) { const errorLines = error . replace ( /^: Error: / , '' ) . split ( '\n' ) . filter ( ( errorLine ) : boolean => { return ! errorLine . trim ( ) . startsWith ( 'at' ) ; } ) . map ( ( errorLine ) : string => { return errorLine . trim ( ) ; } ) ; const basePath = path . join ( this . angularPackage . root , this . angularPackage . outDir , 'temp' , 'transformed' ) ; const sourceFile = errorLines . reverse ( ) . find ( ( errorLine ) : boolean => { return new RegExp ( `^${ basePath }` , 'g' ) . test ( errorLine ) ; } ) . split ( ' ' ) [ 0 ] . replace ( new RegExp ( basePath , 'g' ) , '.' ) . replace ( /:(\d+):(\d+):/ , ' ($1:$2)' ) ; const errorMessages = errorLines . reverse ( ) . map ( ( errorLine ) : string => { return new RegExp ( `^${ basePath }` , 'g' ) . test ( errorLine ) ? errorLine . split ( ' ' ) . slice ( 1 ) . join ( ' ' ) : errorLine ; } ) ; const errorMessage = errorMessages . filter ( ( errorLine ) : boolean => { return errorLine [ 0 ] !== '{' ; } ) . filter ( ( errorLine ) : boolean => { return errorLine . trim ( ) !== '' ; } ) . map ( ( errorLine , index ) : string => { return index === 0 ? `Message:    ${ errorLine }` : `            ${ errorLine }` ; } ) ; const errorDetails = JSON . stringify ( JSON . parse ( errorMessages . find ( ( errorLine ) : boolean => { return errorLine [ 0 ] === '{' ; } ) ) , null , '  ' ) . split ( '\n' ) . map ( ( objectLine , index ) : string => { return index === 0 ? `Details:    ${ objectLine }` : `            ${ objectLine }` ; } ) ; return [ ... errorMessage , '' , `Caused by:  ${ origin } [to be compiled]` , `File:       ${ sourceFile }` , ... errorDetails ] . join ( '\n' ) ; } }