diff --git a/package.json b/package.json index 0e40721..7de3bb4 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "dependencies": { "archiver": "3.0.0", "lodash": "^4.17.15", - "make-dir": "1.3.0" + "make-dir": "1.3.0", + "mz": "^2.7.0" }, "devDependencies": { "@babel/cli": "^7.4.4", @@ -22,6 +23,7 @@ "@types/archiver": "^2.1.2", "@types/lodash": "^4.14.118", "@types/make-dir": "^1.0.3", + "@types/mz": "^0.0.32", "@types/node": "^10.12.27", "@types/serverless": "^1.18.0", "@zeit/ncc": "^0.15.2", diff --git a/src/index.ts b/src/index.ts index d81da2c..cb509eb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,11 +2,12 @@ import { CompiledOutput } from '@zeit/ncc'; import _ from 'lodash'; import path from 'path'; import makeDir from 'make-dir'; +import { readFile, stat } from 'mz/fs'; import Serverless from 'serverless'; import compiler from './compiler'; import parseServiceConfig, { IPackagingConfig } from './parse-service-config'; -import { IFileNameAndPath } from './types'; +import { IFileNameAndPath, CompiledAsset } from './types'; import zipper, { ZipContent } from './zipper'; export default class ServerlessPlugin { @@ -36,16 +37,32 @@ export default class ServerlessPlugin { await makeDir(dotServerlessPath); const packageFilesConfig = await parseServiceConfig(this.serverless); - const packagingPromises = packageFilesConfig.filter(Boolean).map(async (pkg) => { + const packagingPromises = packageFilesConfig.filter(Boolean).map(async pkg => { const { zip, files, perFunctionNccConfig = {} } = pkg; const nccConfig = Object.assign({}, globalNccConfig, perFunctionNccConfig); + const { includeAssets = [] } = nccConfig; + // For now pass all ncc options directly to ncc. This has the benefit of testing out new // ncc releases and changes quickly. Later it would be nice to add a validation step in between. const codeCompilePromises = files.map(({ absPath }) => compiler({ inputFilePath: absPath, ...nccConfig }), ); + const compiledCodes = await Promise.all(codeCompilePromises); - const zipperFiles = createZipperFiles(files, compiledCodes); + const compiledAssets: CompiledAsset[] = await Promise.all( + includeAssets.map( + async (name: string): Promise => { + const fullpath = path.join(servicePath, name); + return { + name, + source: await readFile(fullpath), + permissions: (await stat(fullpath)).mode, + }; + }, + ), + ); + + const zipperFiles = createZipperFiles(files, compiledCodes, compiledAssets.filter(Boolean)); await zipper({ zipPath: zip.absPath, zipContents: zipperFiles }); }); setArtifacts(this.serverless, packageFilesConfig); @@ -57,6 +74,7 @@ export default class ServerlessPlugin { function createZipperFiles( files: IFileNameAndPath[], compiledCodes: CompiledOutput[], + compiledAssets: CompiledAsset[] = [], ): ZipContent[] { if (files.length !== compiledCodes.length) { throw new Error('Expecting NCC output for all files.'); @@ -64,6 +82,10 @@ function createZipperFiles( const content: ZipContent[] = []; + compiledAssets.forEach(({ name, source, permissions }) => { + return content.push({ name, mode: permissions, data: source }); + }); + files.forEach((file, index) => { const compilerOutput = compiledCodes[index]; diff --git a/src/types.ts b/src/types.ts index 695a426..96670bc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,3 +10,9 @@ export interface IFileNameAndPath { name: string; absPath: string; } + +export type CompiledAsset = { + source: Buffer; + permissions: number; + name: string; +};