import { spawn } from 'cross-spawn' ; import { NODE_MODULES_ROOT , SRC_ROOT } from './constants' ; import { dest , src } from 'gulp' ; import { dirname , join , resolve } from 'path' ; import { ensureDirSync , readFile , readFileSync , readdirSync , statSync , writeFile , writeFileSync } from 'fs-extra' ; import { rollup } from 'rollup' ; import { Replacer } from 'strip-function' ; import * as commonjs from 'rollup-plugin-commonjs' ; import * as multiEntry from 'rollup-plugin-multi-entry' ; import * as nodeResolve from 'rollup-plugin-node-resolve' ; import * as through from 'through2' ; import * as uglifyPlugin from 'rollup-plugin-uglify' ; import { argv } from 'yargs' ; import { runWorker } from './utils/app-scripts-worker-client' ; const resolveBin = require ( 'resolve-bin' ) ; export function mergeObjects ( obj1 , obj2 ) { if ( ! obj1 ) { obj1 = { } ; } if ( ! obj2 ) { obj2 = { } ; } var obj3 = { } ; for ( let attrname in obj1 ) { ( < any > obj3 ) [ attrname ] = obj1 [ attrname ] ; } for ( let attrname in obj2 ) { ( < any > obj3 ) [ attrname ] = obj2 [ attrname ] ; } return obj3 ; } function getRootTsConfig ( pathToReadFile ) { const json = readFileSync ( pathToReadFile ) ; let tsConfig = JSON . parse ( json . toString ( ) ) ; return tsConfig ; } export function createTempTsConfig ( includeGlob , target , moduleType , pathToReadFile , pathToWriteFile , overrideCompileOptions = null ) { let config = getRootTsConfig ( pathToReadFile ) ; if ( ! config . compilerOptions ) { config . compilerOptions = { } ; } if ( config . compilerOptions && config . compilerOptions . outDir ) { delete config . compilerOptions . outDir ; } if ( config . compilerOptions . noUnusedLocals ) { delete config . compilerOptions . noUnusedLocals ; } if ( config . compilerOptions . noUnusedParameters ) { delete config . compilerOptions . noUnusedParameters ; } if ( config . compilerOptions ) { config . compilerOptions . module = moduleType ; config . compilerOptions . target = target ; } config . include = includeGlob ; if ( overrideCompileOptions ) { config . compilerOptions = Object . assign ( config . compilerOptions , overrideCompileOptions ) ; } let json = JSON . stringify ( config , null , 2 ) ; json = json . replace ( /\\\\/g , '/' ) ; const dirToCreate = dirname ( pathToWriteFile ) ; ensureDirSync ( dirToCreate ) ; writeFileSync ( pathToWriteFile , json ) ; } function removeDebugStatements ( ) { let replacer = new Replacer ( [ 'console.debug' , 'console.time' , 'console.timeEnd' , 'assert' , 'runInDev' ] ) ; return through . obj ( function ( file , encoding , callback ) { const content = file . contents . toString ( ) ; const cleanedJs = replacer . replace ( content ) ; file . contents = new Buffer ( cleanedJs , 'utf8' ) ; callback ( null , file ) ; } ) ; } export function copySourceToDest ( destinationPath , excludeSpecs , excludeE2e , stripDebug ) { let glob = [ `${ SRC_ROOT }/**/*.ts` ] ; if ( excludeSpecs ) { glob . push ( `!${ SRC_ROOT }/**/*.spec.ts` ) ; } else { glob . push ( `${ SRC_ROOT }/**/*.spec.ts` ) ; } if ( excludeE2e ) { glob . push ( `!${ SRC_ROOT }/components/*/test/*/**/*.ts` ) ; } let stream = src ( glob ) ; if ( stripDebug ) { console . log ( 'Removing debug statements:' , destinationPath ) ; stream = stream . pipe ( removeDebugStatements ( ) ) ; } return stream . pipe ( dest ( destinationPath ) ) ; } export function copyGlobToDest ( sourceGlob , destPath ) { return src ( sourceGlob ) . pipe ( dest ( destPath ) ) ; } export function copyFonts ( destinationPath ) { return src ( [ 'src/fonts/*.+(ttf|woff|woff2)' , 'node_modules/ionicons/dist/fonts/*.+(ttf|woff|woff2)' ] ) . pipe ( dest ( destinationPath ) ) ; } export function compileSass ( destinationPath ) { let sass = require ( 'gulp-sass' ) ; let autoprefixer = require ( 'gulp-autoprefixer' ) ; let cleanCSS = require ( 'gulp-clean-css' ) ; let rename = require ( 'gulp-rename' ) ; let buildConfig = require ( '../build/config' ) ; let ioniconsPath = join ( NODE_MODULES_ROOT , 'ionicons/dist/scss/' ) ; return src ( [ join ( SRC_ROOT , 'themes/ionic.build.default.scss' ) , join ( SRC_ROOT , 'themes/ionic.build.dark.scss' ) ] ) . pipe ( sass ( { includePaths : [ ioniconsPath ] } ) . on ( 'error' , sass . logError ) ) . pipe ( autoprefixer ( buildConfig . autoprefixer ) ) . pipe ( rename ( function ( path ) { path . basename = path . basename . replace ( '.default' , '' ) ; path . basename = path . basename . replace ( '.build' , '' ) ; } ) ) . pipe ( dest ( destinationPath ) ) . pipe ( cleanCSS ( ) ) . pipe ( rename ( { extname : '.min.css' } ) ) . pipe ( dest ( destinationPath ) ) ; } export function setSassIonicVersion ( version ) { writeFileSync ( join ( SRC_ROOT , 'themes/version.scss' ) , `$ionic-version: "${ version }";` ) ; } export function copyFile ( srcPath , destPath ) { const sourceData = readFileSync ( srcPath ) ; writeFileSync ( destPath , sourceData ) ; } export function runNgc ( pathToConfigFile , done ) { let exec = require ( 'child_process' ) . exec ; let ngcPath = getBinaryPath ( '@angular/compiler-cli' , 'ngc' ) ; let shellCommand = `node --max_old_space_size=8096 ${ ngcPath } -p ${ pathToConfigFile }` ; exec ( shellCommand , function ( err , stdout , stderr ) { process . stdout . write ( stdout ) ; process . stderr . write ( stderr ) ; done ( err ) ; } ) ; } export function runTsc ( pathToConfigFile , done ) { let exec = require ( 'child_process' ) . exec ; let tscPath = getBinaryPath ( 'typescript' , 'tsc' ) ; let shellCommand = `node --max_old_space_size=8096 ${ tscPath } -p ${ pathToConfigFile }` ; exec ( shellCommand , function ( err , stdout , stderr ) { process . stdout . write ( stdout ) ; process . stderr . write ( stderr ) ; done ( err ) ; } ) ; } export function runWebpack ( pathToWebpackConfig , done ) { let exec = require ( 'child_process' ) . exec ; let webpackPath = getBinaryPath ( 'webpack' ) ; let shellCommand = `node --max_old_space_size=8096 ${ webpackPath } --config ${ pathToWebpackConfig } --display-error-details` ; exec ( shellCommand , function ( err , stdout , stderr ) { process . stdout . write ( stdout ) ; process . stderr . write ( stderr ) ; done ( err ) ; } ) ; } export function runAppScriptsServe ( testOrDemoName , appEntryPoint , appNgModulePath , srcDir , distDir , tsConfig , ionicAngularDir , sassConfigPath , copyConfigPath , watchConfigPath , devApp ) { console . log ( 'Running ionic-app-scripts serve with' , testOrDemoName ) ; const deepLinksDir = dirname ( dirname ( appNgModulePath ) ) ; let scriptArgs = [ 'serve' , '--appEntryPoint' , appEntryPoint , '--appNgModulePath' , appNgModulePath , '--deepLinksDir' , deepLinksDir , '--srcDir' , srcDir , '--wwwDir' , distDir , '--tsconfig' , tsConfig , '--readConfigJson' , 'false' , '--ionicAngularDir' , ionicAngularDir , '--sass' , sassConfigPath , '--copy' , copyConfigPath , '--enableLint' , 'false' , '--skipIonicAngularVersion' , 'true' ] ; if ( devApp ) { scriptArgs . push ( '--bonjour' ) ; } if ( watchConfigPath ) { scriptArgs . push ( '--watch' ) ; scriptArgs . push ( watchConfigPath ) ; } const debug = argv . debug ; if ( debug ) { scriptArgs . push ( '--debug' ) ; } return new Promise ( ( resolve , reject ) => { let pathToAppScripts = join ( NODE_MODULES_ROOT , '.bin' , 'ionic-app-scripts' ) ; pathToAppScripts = process . platform === 'win32' ? pathToAppScripts + '.cmd' : pathToAppScripts ; const spawnedCommand = spawn ( pathToAppScripts , scriptArgs , { stdio : 'inherit' } ) ; console . log ( `${ pathToAppScripts } ${ scriptArgs . join ( ' ' ) }` ) ; spawnedCommand . on ( 'close' , ( code ) => { if ( code === 0 ) { return resolve ( ) ; } reject ( new Error ( 'App-scripts failed with non-zero status code' ) ) ; } ) ; } ) ; } export function runAppScriptsBuild ( appEntryPoint , appNgModulePath , srcDir , distDir , tsConfig , ionicAngularDir , sassConfigPath , copyConfigPath , isDev = false , minifyCss = true , minifyJs = true , optimizeJs = true ) { const pathToAppScripts = join ( NODE_MODULES_ROOT , '.bin' , 'ionic-app-scripts' ) ; const debug = argv . debug ; return runWorker ( pathToAppScripts , debug , appEntryPoint , appNgModulePath , srcDir , distDir , tsConfig , ionicAngularDir , sassConfigPath , copyConfigPath , isDev , minifyCss , minifyJs , optimizeJs ) ; } /** Resolves the path for a node package executable. */ export function getBinaryPath ( packageName , executable = packageName ) { return resolveBin . sync ( packageName , { executable } ) ; } export function deleteFiles ( glob , done ) { let del = require ( 'del' ) ; del . sync ( glob ) ; done ( ) ; } export function createTimestamp ( ) { var d = new Date ( ) ; return d . getUTCFullYear ( ) + ( '0' + ( d . getUTCMonth ( ) + 1 ) ) . slice ( - 2 ) + ( '0' + ( d . getUTCDate ( ) ) ) . slice ( - 2 ) + ( '0' + ( d . getUTCHours ( ) ) ) . slice ( - 2 ) + ( '0' + ( d . getUTCMinutes ( ) ) ) . slice ( - 2 ) ; } export function writePolyfills ( outputDirectory ) { const MODERN_ENTRIES = [ 'node_modules/core-js/es6/array.js' , 'node_modules/core-js/es6/date.js' , 'node_modules/core-js/es6/function.js' , 'node_modules/core-js/es6/map.js' , 'node_modules/core-js/es6/number.js' , 'node_modules/core-js/es6/object.js' , 'node_modules/core-js/es6/parse-float.js' , 'node_modules/core-js/es6/parse-int.js' , 'node_modules/core-js/es6/set.js' , 'node_modules/core-js/es6/string.js' , 'node_modules/core-js/es7/reflect.js' , 'node_modules/core-js/es6/reflect.js' , 'node_modules/zone.js/dist/zone.js' , 'scripts/polyfill/polyfill.dom.js' ] ; const ALL_ENTRIES = [ 'node_modules/core-js/es6/array.js' , 'node_modules/core-js/es6/date.js' , 'node_modules/core-js/es6/function.js' , 'node_modules/core-js/es6/map.js' , 'node_modules/core-js/es6/math.js' , 'node_modules/core-js/es6/number.js' , 'node_modules/core-js/es6/object.js' , 'node_modules/core-js/es6/parse-float.js' , 'node_modules/core-js/es6/parse-int.js' , 'node_modules/core-js/es6/reflect.js' , 'node_modules/core-js/es6/regexp.js' , 'node_modules/core-js/es6/set.js' , 'node_modules/core-js/es6/string.js' , 'node_modules/core-js/es6/symbol.js' , 'node_modules/core-js/es6/typed.js' , 'node_modules/core-js/es6/weak-map.js' , 'node_modules/core-js/es6/weak-set.js' , 'node_modules/core-js/es7/reflect.js' , 'node_modules/zone.js/dist/zone.js' , 'scripts/polyfill/polyfill.dom.js' ] ; const NG_ENTRIES = [ 'node_modules/core-js/es7/reflect.js' , 'node_modules/zone.js/dist/zone.js' , ] ; let promises = [ ] ; promises . push ( bundlePolyfill ( MODERN_ENTRIES , join ( outputDirectory , 'polyfills.modern.js' ) ) ) ; promises . push ( bundlePolyfill ( ALL_ENTRIES , join ( outputDirectory , 'polyfills.js' ) ) ) ; promises . push ( bundlePolyfill ( NG_ENTRIES , join ( outputDirectory , 'polyfills.ng.js' ) ) ) ; return Promise . all ( promises ) ; } function bundlePolyfill ( pathsToIncludeInPolyfill , outputPath ) { return rollup ( { entry : pathsToIncludeInPolyfill , plugins : [ multiEntry ( ) , nodeResolve ( { module : true , jsnext : true , main : true } ) , commonjs ( ) , uglifyPlugin ( ) ] , onwarn : ( ) => { return ( ) => { } ; } } ) . then ( ( bundle ) => { return bundle . write ( { format : 'iife' , moduleName : 'MyBundle' , dest : outputPath } ) ; } ) . catch ( err => { console . log ( 'caught rollup error: ' , err ) ; } ) ; } export function getFolderInfo ( ) { let componentName = null ; let componentTest = null ; const folder = argv . folder || argv . f ; if ( folder && folder . length ) { const folderSplit = folder . split ( '/' ) ; componentName = folderSplit [ 0 ] ; componentTest = ( folderSplit . length > 1 ? folderSplit [ 1 ] : 'basic' ) ; } const devApp = argv . devapp !== undefined ; return { componentName : componentName , componentTest : componentTest , devApp : devApp } ; } export function getFolders ( dir ) { return readdirSync ( dir ) . filter ( function ( file ) { return statSync ( join ( dir , file ) ) . isDirectory ( ) ; } ) ; } export function readFileAsync ( filePath ) { return new Promise ( ( resolve , reject ) => { readFile ( filePath , ( err , buffer ) => { if ( err ) { return reject ( err ) ; } return resolve ( buffer . toString ( ) ) ; } ) ; } ) ; } export function writeFileAsync ( filePath , fileContent ) { return new Promise ( ( resolve , reject ) => { writeFile ( filePath , fileContent , ( err ) => { if ( err ) { return reject ( err ) ; } return resolve ( ) ; } ) ; } ) ; }