diff --git a/src/deploy-builder.ts b/src/deploy-builder.ts new file mode 100644 index 00000000..b99003da --- /dev/null +++ b/src/deploy-builder.ts @@ -0,0 +1,42 @@ +import * as path from 'path' +import * as fs from 'fs-p' + +export class DeployBuilder { + build(buildFolder: string) { + const buildPath = path.resolve(buildFolder) + + if (this.isNodeModulesAbsent(buildPath)) { + this.createNodeModulesFolder(buildPath) + } + } + + private isNodeModulesAbsent(buildPath: string): boolean { + return !fs.existsSync(path.join(buildPath, 'node_modules')) + } + + private createNodeModulesFolder(buildPath: string) { + try { + fs.symlinkSync(path.resolve('node_modules'), path.join(buildPath, 'node_modules')) + } catch (error) { + this.copyIfMissingSymlinkPermission(buildPath, error) + } + } + + private copyIfMissingSymlinkPermission(buildPath: string, error: SymlinkException) { + if (this.isMissingSymlinkPermission(error)) { + fs.copySync(path.resolve('node_modules'), path.join(buildPath, 'node_modules')) + } else { + throw error + } + } + + private isMissingSymlinkPermission(error: SymlinkException): boolean { + // Generally happens when no admin rights with UAC enabled on Windows. + return error.code === 'EPERM' && error.errno === -4048 + } +} + +export interface SymlinkException { + code: string + errno: number +} diff --git a/src/index.ts b/src/index.ts index 0025e213..418e862b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import * as fs from 'fs-p' import * as _ from 'lodash' import * as globby from 'globby' +import { DeployBuilder } from './deploy-builder' import { ServerlessOptions, ServerlessInstance, ServerlessFunction } from './types' import * as typescript from './typescript' @@ -11,7 +12,6 @@ const serverlessFolder = '.serverless' const buildFolder = '.build' class ServerlessPlugin { - private originalServicePath: string private originalFunctions: { [key: string]: ServerlessFunction } | {} @@ -20,6 +20,8 @@ class ServerlessPlugin { commands: { [key: string]: any } hooks: { [key: string]: Function } + private deployBuilder: DeployBuilder + constructor(serverless: ServerlessInstance, options: ServerlessOptions) { this.serverless = serverless this.options = options @@ -56,6 +58,8 @@ class ServerlessPlugin { }, }, } + + this.deployBuilder = new DeployBuilder() } async beforeCreateDeploymentArtifacts(type: string): Promise { @@ -86,10 +90,7 @@ class ServerlessPlugin { await typescript.run(tsFileNames, tsconfig) - // include node_modules into build - if (!fs.existsSync(path.resolve(path.join(buildFolder, 'node_modules')))) { - fs.symlinkSync(path.resolve('node_modules'), path.resolve(path.join(buildFolder, 'node_modules'))) - } + this.deployBuilder.build(buildFolder) // include any "extras" from the "include" section if (this.serverless.service.package.include && this.serverless.service.package.include.length > 0) {