import * as path from 'path' ; import * as webpack from 'webpack' ; import * as mime from 'mime' ; import * as AWS from 'aws-sdk' ; import { promisify } from 'bluebird' ; import MemoryFileSystem = require ( 'memory-fs' ) ; import * as zlib from 'zlib' ; import createWebpackConfig from './webpack.config' ; const s3 = new AWS . S3 ( ) ; const cloudfront = new AWS . CloudFront ( ) ; const gzip = promisify ( zlib . gzip ) ; interface File { name : string ; type : string ; contents : Buffer ; } export default function handler ( event , context , callback ) { run ( ) . then ( ( result ) => callback ( null , result ) ) . catch ( ( err ) => callback ( err ) ) ; } async function run ( ) { const config = await createWebpackConfig ( ) ; const files = await compile ( config ) ; await Promise . all ( files . map ( upload ) ) ; const invalidation = await invalidate ( ) ; return invalidation ; } async function compile ( config ) { const compiler = webpack ( config ) ; const fs = new MemoryFileSystem ( ) ; compiler . outputFileSystem = fs ; const compile = promisify ( compiler . run , { context : compiler } ) ; const stats = await compile ( ) ; if ( stats . hasErrors ( ) ) throw new Error ( `Compiler error: ${ stats . toJson ( ) . errors [ 0 ] }` ) ; const files = fs . readdirSync ( config . output . path ) . map ( ( name ) => ( { name , type : mime . lookup ( name ) , contents : fs . readFileSync ( path . resolve ( config . output . path , name ) ) } ) ) ; return files ; } async function upload ( file ) { const contents = await gzip ( file . contents ) ; const expiresIn = file . type === 'text/html' ? 900 : 7200 ; const params = { Bucket : process . env . S3_BUCKET , ACL : 'public-read' , Key : file . name , ContentType : `${ file . type };charset=utf-8` , ContentEncoding : 'gzip' , CacheControl : `max-age=${ expiresIn }` , Body : contents } ; return s3 . putObject ( params ) . promise ( ) ; } async function invalidate ( ) { const params = { DistributionId : process . env . CF_DISTRIBUTION , InvalidationBatch : { CallerReference : `${ + ( new Date ( ) ) }` , Paths : { Quantity : 1 , Items : [ '/*' ] } } } ; return cloudfront . createInvalidation ( params ) . promise ( ) ; }