import Path from 'path' ; import { pipeline } from 'stream' ; import { promisify } from 'util' ; import fs from 'fs' ; import gulpBrotli from 'gulp-brotli' ; import gulpGzip from 'gulp-gzip' ; import gulpPostCSS from 'gulp-postcss' ; import gulpTerser from 'gulp-terser' ; import { ToolingLog } from '@kbn/dev-utils' ; import terser from 'terser' ; import vfs from 'vinyl-fs' ; import globby from 'globby' ; import del from 'del' ; import { Task , write } from '../lib' ; const EUI_THEME_RE = /\.v\d\.(light|dark)\.css$/ ; const ASYNC_CHUNK_RE = /\.chunk\.\d+\.js$/ ; const asyncPipeline = promisify ( pipeline ) ; const getSize = ( paths ) => paths . reduce ( ( acc , path ) => acc + fs . statSync ( path ) . size , 0 ) ; async function optimizeAssets ( log , assetDir ) { log . info ( 'Creating optimized assets for' , assetDir ) ; log . indent ( 4 ) ; try { log . debug ( 'Remove Pre Minify Sourcemaps' ) ; await del ( [ '**/*.map' ] , { cwd : assetDir } ) ; log . debug ( 'Minify CSS' ) ; await asyncPipeline ( vfs . src ( [ '**/*.css' ] , { cwd : assetDir } ) , gulpPostCSS ( [ require ( 'cssnano' ) ( { preset : [ 'default' , { discardComments : false } ] , } ) , ] ) , vfs . dest ( assetDir ) ) ; log . debug ( 'Minify JS' ) ; await asyncPipeline ( vfs . src ( [ '**/*.js' ] , { cwd : assetDir } ) , gulpTerser ( { compress : true , mangle : true } , terser . minify ) , vfs . dest ( assetDir ) ) ; log . debug ( 'Brotli compress' ) ; await asyncPipeline ( vfs . src ( [ '**/*.{js,css}' ] , { cwd : assetDir } ) , gulpBrotli ( ) , vfs . dest ( assetDir ) ) ; log . debug ( 'GZip compress' ) ; await asyncPipeline ( vfs . src ( [ '**/*.{js,css}' ] , { cwd : assetDir } ) , gulpGzip ( ) , vfs . dest ( assetDir ) ) ; } finally { log . indent ( - 4 ) ; } } type Category = ReturnType < typeof getCategory > ; const getCategory = ( relative ) => { if ( EUI_THEME_RE . test ( relative ) ) { return 'euiTheme' ; } if ( relative . endsWith ( '.css' ) ) { return 'css' ; } if ( relative . endsWith ( '.ttf' ) ) { return 'font' ; } if ( ASYNC_CHUNK_RE . test ( relative ) ) { return 'asyncChunk' ; } if ( relative . includes ( 'kbn-ui-shared-deps-npm' ) ) { return 'npm' ; } if ( relative . includes ( 'kbn-ui-shared-deps-src' ) ) { return 'src' ; } throw new Error ( `unable to categorize file [${ relative }]` ) ; } ; function categorizeAssets ( assetDirs ) { const assets = assetDirs . flatMap ( ( assetDir ) => globby . sync ( [ '**/*' ] , { cwd : assetDir , ignore : [ '*-manifest.json' , '*.gz' , '*.br' ] , absolute : true , } ) . map ( ( path ) : { path : string ; category : Category ; } => ( { path , category : getCategory ( Path . relative ( assetDir , path ) ) , } ) ) ) ; const groups = new Map < Category , string [ ] > ( ) ; const add = ( cat , path ) => { const group = groups . get ( cat ) ?? [ ] ; group . push ( path ) ; groups . set ( cat , group ) ; } ; for ( const { path , category } of assets ) { if ( category === 'euiTheme' ) { if ( path . includes ( 'v8.light' ) ) { add ( 'css' , path ) ; } continue ; } add ( category , path ) ; } return groups ; } export const GeneratePackagesOptimizedAssets = { description : 'Generates Optimized Assets for Packages' , async run ( config , log , build ) { const npmAssetDir = build . resolvePath ( `node_modules/@kbn/ui-shared-deps-npm/shared_built_assets` ) ; const srcAssetDir = build . resolvePath ( `node_modules/@kbn/ui-shared-deps-src/shared_built_assets` ) ; const assetDirs = [ npmAssetDir , srcAssetDir ] ; for ( const assetDir of assetDirs ) { await optimizeAssets ( log , assetDir ) ; } const groups = categorizeAssets ( assetDirs ) ; log . verbose ( 'categorized assets' , groups ) ; const metrics = [ { group : 'page load bundle size' , id : 'kbnUiSharedDeps-npmDll' , value : getSize ( groups . get ( 'npm' ) ?? [ ] ) , } , { group : 'page load bundle size' , id : 'kbnUiSharedDeps-srcJs' , value : getSize ( groups . get ( 'src' ) ?? [ ] ) , } , { group : 'page load bundle size' , id : 'kbnUiSharedDeps-css' , value : getSize ( groups . get ( 'css' ) ?? [ ] ) , } , { group : 'page load bundle size' , id : 'kbnUiSharedDeps-fonts' , value : getSize ( groups . get ( 'font' ) ?? [ ] ) , } , ] ; log . verbose ( 'metrics:' , metrics ) ; log . debug ( 'Create metrics.json' ) ; await write ( Path . resolve ( srcAssetDir , 'metrics.json' ) , JSON . stringify ( metrics , null , 2 ) ) ; } , } ;