node-lambda 0.18.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,13 +9,14 @@ jobs:
9
9
  strategy:
10
10
  fail-fast: false
11
11
  matrix:
12
- node-version: [10.x, 12.x, 14.x]
12
+ node-version: [12.x, 14.x, 16.x, 17.x]
13
13
  os: [ubuntu-latest, windows-latest, macos-latest]
14
14
  steps:
15
- - uses: actions/checkout@master
16
- - uses: actions/setup-node@master
15
+ - uses: actions/checkout@v2
16
+ - uses: actions/setup-node@v2
17
17
  with:
18
18
  node-version: ${{ matrix.node-version }}
19
+ cache: 'npm'
19
20
  - run: npm i -g npm
20
21
  - run: node -v
21
22
  - run: npm -v
package/.travis.yml CHANGED
@@ -4,6 +4,8 @@ node_js:
4
4
  - "lts/*"
5
5
  - "node"
6
6
 
7
+ install: npm i -g npm
8
+
7
9
  cache:
8
10
  directories:
9
11
  - node_modules
package/CHANGELOG.md CHANGED
@@ -421,3 +421,26 @@ This project adheres to [Semantic Versioning](http://semver.org/).
421
421
  - Upgrade packages [#538](https://github.com/motdotla/node-lambda/pull/538)
422
422
  - Bump lodash from 4.17.15 to 4.17.19 [#536](https://github.com/motdotla/node-lambda/pull/536)
423
423
  - Add build badge in README [#534](https://github.com/motdotla/node-lambda/pull/534)
424
+
425
+ ## [0.19.0] - 2021-03-30
426
+ ### Features
427
+ - feat: support `--no-optional` option in npm install [#557](https://github.com/motdotla/node-lambda/pull/557)
428
+
429
+ ## [0.19.1] - 2021-04-24
430
+ ### Bugfixes
431
+ - Fix errors caused by old proxy-agent [#564](https://github.com/motdotla/node-lambda/pull/564)
432
+
433
+ ## [0.20.0] - 2021-09-25
434
+ ### Features
435
+ - updated proxy-agent to 5.0.0 [#574](https://github.com/motdotla/node-lambda/pull/574)
436
+ - show suggestions after an error for an unknown command or option [#572](https://github.com/motdotla/node-lambda/pull/572)
437
+ - feat: drop nodejs10x from lambda runtime [#571](https://github.com/motdotla/node-lambda/pull/571)
438
+
439
+ ### Bugfixes
440
+ - fix(_uploadExisting): fix function update errors [#575](https://github.com/motdotla/node-lambda/pull/575)
441
+ - test: fix npm install test failing in some cases [#569](https://github.com/motdotla/node-lambda/pull/569)
442
+ - Clean the tmp dir during `_archivePrebuilt` to match `_buildAndArchive` behavior [#518](https://github.com/motdotla/node-lambda/pull/518)
443
+
444
+ ## [0.21.0] - 2021-11-10
445
+ ### Features
446
+ - feat: support for yarn [#581](https://github.com/motdotla/node-lambda/pull/581)
package/README.md CHANGED
@@ -64,6 +64,7 @@ $ echo -e ".env\ndeploy.env\nevent.json\n.lambda" >> .gitignore
64
64
  ##### Deploy env variables
65
65
 
66
66
  ```
67
+ PACKAGE_MANAGER // (default: 'npm')
67
68
  AWS_ENVIRONMENT // (default: '')
68
69
  AWS_ENDPOINT // (default: '')
69
70
  CONFIG_FILE // (default: '')
@@ -109,23 +110,21 @@ Runs your Amazon Lambda index.js file locally. Passes `event.json` data to the A
109
110
 
110
111
  ```
111
112
  $ node-lambda run --help
112
-
113
- Usage: run|execute [options]
113
+ Usage: node-lambda run|execute [options]
114
114
 
115
115
  Run your Amazon Lambda application locally
116
116
 
117
-
118
117
  Options:
119
- -h, --help output usage information
120
- -H, --handler [index.handler] Lambda Handler {index.handler}
121
- -j, --eventFile [event.json] Event JSON File
122
- -u, --runtime [nodejs14.x] Lambda Runtime
123
- -t, --timeout [3] Lambda Timeout
124
- -f, --configFile [] Path to file holding secret environment variables (e.g. "deploy.env")
125
- -x, --contextFile [context.json] Context JSON File
126
- -M, --enableRunMultipleEvents [true] Enable run multiple events
127
- -y, --proxy [] Proxy server
128
- --apiGateway Convert to API Gateway events
118
+ -H, --handler [AWS_HANDLER] Lambda Handler {index.handler} (default: "index.handler")
119
+ -j, --eventFile [EVENT_FILE] Event JSON File (default: "event.json")
120
+ -u, --runtime [AWS_RUNTIME] Lambda Runtime (default: "nodejs14.x")
121
+ -t, --timeout [AWS_RUN_TIMEOUT] Lambda Timeout (default: 3)
122
+ -f, --configFile [CONFIG_FILE] Path to file holding secret environment variables (e.g. "deploy.env") (default: "")
123
+ -x, --contextFile [CONTEXT_FILE] Context JSON File (default: "context.json")
124
+ -M, --enableRunMultipleEvents [ENABLE_RUN_MULTIPLE_EVENTS] Enable run multiple events (default: true)
125
+ -y, --proxy [PROXY] Proxy server (default: "")
126
+ --apiGateway Convert to API Gateway events (default: false)
127
+ -h, --help display help for command
129
128
  ```
130
129
 
131
130
  #### package
@@ -134,23 +133,26 @@ Bundles your application into a local zip file.
134
133
 
135
134
  ```
136
135
  $ node-lambda package --help
137
-
138
- Usage: package|zip [options]
136
+ Usage: node-lambda package|zip [options]
139
137
 
140
138
  Create zipped package for Amazon Lambda deployment
141
139
 
142
-
143
140
  Options:
144
- -h, --help output usage information
145
- -A, --packageDirectory [build] Local Package Directory
146
- -I, --dockerImage [] Docker image for npm install
147
- -n, --functionName [node-lambda] Lambda FunctionName
148
- -H, --handler [index.handler] Lambda Handler {index.handler}
149
- -e, --environment [development] Choose environment {dev, staging, production}
150
- -x, --excludeGlobs [event.json] Space-separated glob pattern(s) for additional exclude files (e.g. "event.json dotenv.sample")
151
- -D, --prebuiltDirectory [] Prebuilt directory
152
- -m, --keepNodeModules [false] Keep the current node_modules and skip the npm install step
153
- -v, --dockerVolumes [] Additional docker volumes to mount. Each volume definition has to be separated by a space (e.g. "$HOME/.gitconfig:/etc/gitconfig $HOME/.ssh:/root/.ssh"
141
+ --packageManager [PACKAGE_MANAGER] Package manager used to install dependencies (default: "npm", options: "npm", "yarn")
142
+ -A, --packageDirectory [PACKAGE_DIRECTORY] Local Package Directory
143
+ -I, --dockerImage [DOCKER_IMAGE] Docker image for npm ci (default: "")
144
+ -n, --functionName [AWS_FUNCTION_NAME] Lambda FunctionName (default: "node-lambda")
145
+ -H, --handler [AWS_HANDLER] Lambda Handler {index.handler} (default: "index.handler")
146
+ -e, --environment [AWS_ENVIRONMENT] Choose environment {dev, staging, production} (default: "")
147
+ -x, --excludeGlobs [EXCLUDE_GLOBS] Space-separated glob pattern(s) for additional exclude files (e.g.
148
+ "event.json dotenv.sample") (default: "")
149
+ -D, --prebuiltDirectory [PREBUILT_DIRECTORY] Prebuilt directory (default: "")
150
+ -m, --keepNodeModules [KEEP_NODE_MODULES] Keep the current node_modules directory. (default: false)
151
+ -v, --dockerVolumes [DOCKER_VOLUMES] Additional docker volumes to mount. Each volume definition has to be
152
+ separated by a space (e.g. "$HOME/.gitconfig:/etc/gitconfig
153
+ $HOME/.ssh:/root/.ssh") (default: "")
154
+ --no-optionalDependencies Run `npm install` with `--no-optional`
155
+ -h, --help display help for command
154
156
  ```
155
157
 
156
158
  #### deploy
@@ -159,50 +161,50 @@ Bundles and deploys your application up to Amazon Lambda.
159
161
 
160
162
  ```
161
163
  $ node-lambda deploy --help
162
-
163
- Usage: deploy [options]
164
+ Usage: node-lambda deploy [options]
164
165
 
165
166
  Deploy your application to Amazon Lambda
166
167
 
167
-
168
168
  Options:
169
- -h, --help output usage information
170
- -e, --environment [development] Choose environment {dev, staging, production}
171
- -E, --endpoint [AWS_ENDPOINT] Choose endpoint (e.g. localstack, "http://127.0.0.1:4574") (default: "")
172
- -a, --accessKey [your_key] AWS Access Key
173
- -s, --secretKey [your_secret] AWS Secret Key
174
- -P, --profile [] AWS Profile
175
- -k, --sessionToken [] AWS Session Token
176
- -r, --region [us-east-1] AWS Region
177
- -n, --functionName [node-lambda] Lambda FunctionName
178
- -H, --handler [index.handler] Lambda Handler {index.handler}
179
- -o, --role [your_amazon_role] Amazon Role ARN
180
- -m, --memorySize [128] Lambda Memory Size
181
- -t, --timeout [3] Lambda Timeout
182
- -d, --description [missing] Lambda Description
183
- -u, --runtime [nodejs14.x] Lambda Runtime
184
- -p, --publish [false] Lambda Publish
185
- -L, --lambdaVersion [] Lambda Function Version
186
- -b, --vpcSubnets [] Lambda Function VPC Subnet IDs (comma delimited)
187
- -g, --vpcSecurityGroups [] Lambda VPC Security Group IDs (comma delimited)
188
- -K, --kmsKeyArn [] Lambda KMS Key ARN
189
- -Q, --deadLetterConfigTargetArn [] Lambda DLQ resource
190
- -c, --tracingConfig [] Lambda tracing settings
191
- -l, --layers [] Lambda Layers settings (e.g. "ARN1,ARN2[,..])" (default: "")
192
- -R, --retentionInDays [] CloudWatchLogs retentionInDays settings
193
- -G, --sourceDirectory [] Path to lambda source Directory (e.g. "./some-lambda")
194
- -I, --dockerImage [] Docker image for npm install
195
- -f, --configFile [] Path to file holding secret environment variables (e.g. "deploy.env")
196
- -S, --eventSourceFile [] Path to file holding event source mapping variables (e.g. "event_sources.json")
197
- -x, --excludeGlobs [event.json] Space-separated glob pattern(s) for additional exclude files (e.g. "event.json dotenv.sample")
198
- -D, --prebuiltDirectory [] Prebuilt directory
199
- -T, --deployTimeout [120000] Deploy Timeout
200
- -z, --deployZipfile [] Deploy zipfile
201
- -B, --deployUseS3 [] Use S3 to deploy.
202
- -i, --imageUri [] URI of a container image in the Amazon ECR registry. (default: "")
203
- -y, --proxy [] Proxy server
204
- -A, --tags [] Tags as key value pairs (e.g. "tagname1=tagvalue1,tagname2=tagvalue2") (default: "")
205
- --silent Silent or quiet mode (default: false)
169
+ --packageManager [PACKAGE_MANAGER] Package manager used to install dependencies (default: "npm", options: "npm", "yarn")
170
+ -e, --environment [AWS_ENVIRONMENT] Choose environment {dev, staging, production} (default: "")
171
+ -E, --endpoint [AWS_ENDPOINT] Choose endpoint (e.g. localstack, "http://127.0.0.1:4574") (default: "")
172
+ -a, --accessKey [AWS_ACCESS_KEY_ID] AWS Access Key
173
+ -s, --secretKey [AWS_SECRET_ACCESS_KEY] AWS Secret Key
174
+ -P, --profile [AWS_PROFILE] AWS Profile (default: "")
175
+ -k, --sessionToken [AWS_SESSION_TOKEN] AWS Session Token (default: "")
176
+ -r, --region [AWS_REGION] AWS Region (default: "us-east-1,us-west-2,eu-west-1")
177
+ -n, --functionName [AWS_FUNCTION_NAME] Lambda FunctionName (default: "node-lambda")
178
+ -H, --handler [AWS_HANDLER] Lambda Handler {index.handler} (default: "index.handler")
179
+ -o, --role [AWS_ROLE] Amazon Role ARN (default: "missing")
180
+ -m, --memorySize [AWS_MEMORY_SIZE] Lambda Memory Size (default: 128)
181
+ -t, --timeout [AWS_TIMEOUT] Lambda Timeout (default: 60)
182
+ -d, --description [AWS_DESCRIPTION] Lambda Description (default: "Command line tool for locally running and remotely deploying your node.js applications to Amazon Lambda.")
183
+ -u, --runtime [AWS_RUNTIME] Lambda Runtime (default: "nodejs14.x")
184
+ -p, --publish [AWS_PUBLISH] Lambda Publish (default: false)
185
+ -L, --lambdaVersion [AWS_FUNCTION_VERSION] Lambda Function Version (default: "")
186
+ -b, --vpcSubnets [AWS_VPC_SUBNETS] Lambda Function VPC Subnet IDs (comma delimited) (default: "")
187
+ -g, --vpcSecurityGroups [AWS_VPC_SECURITY_GROUPS] Lambda VPC Security Group IDs (comma delimited) (default: "")
188
+ -K, --kmsKeyArn [AWS_KMS_KEY_ARN] Lambda KMS Key ARN (default: "")
189
+ -Q, --deadLetterConfigTargetArn [AWS_DLQ_TARGET_ARN] Lambda DLQ resource
190
+ -c, --tracingConfig [AWS_TRACING_CONFIG] Lambda tracing settings (default: "")
191
+ -l, --layers [AWS_LAYERS] Lambda Layers settings (e.g. "ARN1,ARN2[,..])" (default: "")
192
+ -R, --retentionInDays [AWS_LOGS_RETENTION_IN_DAYS] CloudWatchLogs retentionInDays settings (default: "")
193
+ -G, --sourceDirectory [SRC_DIRECTORY] Path to lambda source Directory (e.g. "./some-lambda") (default: "")
194
+ -I, --dockerImage [DOCKER_IMAGE] Docker image for npm ci (default: "")
195
+ -f, --configFile [CONFIG_FILE] Path to file holding secret environment variables (e.g. "deploy.env") (default: "")
196
+ -S, --eventSourceFile [EVENT_SOURCE_FILE] Path to file holding event source mapping variables (e.g. "event_sources.json") (default: "")
197
+ -x, --excludeGlobs [EXCLUDE_GLOBS] Space-separated glob pattern(s) for additional exclude files (e.g. "event.json dotenv.sample") (default: "")
198
+ -D, --prebuiltDirectory [PREBUILT_DIRECTORY] Prebuilt directory (default: "")
199
+ -T, --deployTimeout [DEPLOY_TIMEOUT] Deploy Timeout (default: 120000)
200
+ -z, --deployZipfile [DEPLOY_ZIPFILE] Deploy zipfile (default: "")
201
+ -B, --deployUseS3 [DEPLOY_USE_S3] Use S3 to deploy. (default: false)
202
+ -i, --imageUri [IMAGE_URI] URI of a container image in the Amazon ECR registry. (default: "")
203
+ -y, --proxy [PROXY] Proxy server (default: "")
204
+ -A, --tags [AWS_TAGS] Tags as key value pairs (e.g. "tagname1=tagvalue1,tagname2=tagvalue2)" (default: "")
205
+ --silent Silent or quiet mode (default: false)
206
+ --no-optionalDependencies Run `npm install` with `--no-optional`
207
+ -h, --help display help for command
206
208
  ```
207
209
 
208
210
  If you are deploying to a custom endpoint you may also need to pass in an access key/secret. For localstack these can be anything, but cannot be blank:
@@ -217,7 +219,7 @@ AWS Lambda will let you set environment variables for your function. Use the sam
217
219
 
218
220
  ## Node.js Runtime Configuration
219
221
 
220
- AWS Lambda now supports Node.js14, Node.js 12 and Node.js 10. Please also check the [Building Lambda functions with Node.js](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html) page.
222
+ AWS Lambda now supports Node.js14 and Node.js 12. Please also check the [Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) page.
221
223
 
222
224
  ## To deploy a container image to Lambda
223
225
 
package/bin/node-lambda CHANGED
@@ -11,6 +11,7 @@ const fs = require('fs')
11
11
  const packageJson = fs.existsSync(path.join(process.cwd(), 'package.json')) ? require(path.join(process.cwd(), 'package.json')) : {}
12
12
  const packageJsonName = packageJson.name || 'UnnamedFunction'
13
13
 
14
+ const PACKAGE_MANAGER = process.env.PACKAGE_MANAGER || 'npm'
14
15
  const AWS_ENVIRONMENT = process.env.AWS_ENVIRONMENT || ''
15
16
  const AWS_ENDPOINT = process.env.AWS_ENDPOINT || ''
16
17
  const CONFIG_FILE = process.env.CONFIG_FILE || ''
@@ -61,9 +62,12 @@ const DOCKER_VOLUMES = process.env.DOCKER_VOLUMES || ''
61
62
  const AWS_TAGS = process.env.AWS_TAGS || ''
62
63
  const IMAGE_URI = process.env.IMAGE_URI || ''
63
64
 
65
+ program.showSuggestionAfterError()
66
+
64
67
  program
65
68
  .command('deploy')
66
69
  .description('Deploy your application to Amazon Lambda')
70
+ .option('--packageManager [PACKAGE_MANAGER]', 'Package manager used to install dependencies', PACKAGE_MANAGER)
67
71
  .option('-e, --environment [AWS_ENVIRONMENT]', 'Choose environment {dev, staging, production}',
68
72
  AWS_ENVIRONMENT)
69
73
  .option('-E, --endpoint [AWS_ENDPOINT]', 'Choose endpoint (e.g. localstack, "http://127.0.0.1:4574")',
@@ -109,6 +113,7 @@ program
109
113
  .option('-y, --proxy [PROXY]', 'Proxy server', PROXY)
110
114
  .option('-A, --tags [AWS_TAGS]', 'Tags as key value pairs (e.g. "tagname1=tagvalue1,tagname2=tagvalue2)"', AWS_TAGS)
111
115
  .option('--silent', 'Silent or quiet mode', false)
116
+ .option('--no-optionalDependencies', 'Run `npm install` with `--no-optional`')
112
117
  .action((prg) => {
113
118
  if (prg.silent) {
114
119
  console.log = () => {}
@@ -120,6 +125,7 @@ program
120
125
  .command('package')
121
126
  .alias('zip')
122
127
  .description('Create zipped package for Amazon Lambda deployment')
128
+ .option('--packageManager [PACKAGE_MANAGER]', 'Package manager used to install dependencies', PACKAGE_MANAGER)
123
129
  .option('-A, --packageDirectory [PACKAGE_DIRECTORY]', 'Local Package Directory', PACKAGE_DIRECTORY)
124
130
  .option('-I, --dockerImage [DOCKER_IMAGE]', 'Docker image for npm ci', DOCKER_IMAGE)
125
131
  .option('-n, --functionName [AWS_FUNCTION_NAME]', 'Lambda FunctionName', AWS_FUNCTION_NAME)
@@ -131,6 +137,7 @@ program
131
137
  .option('-D, --prebuiltDirectory [PREBUILT_DIRECTORY]', 'Prebuilt directory', PREBUILT_DIRECTORY)
132
138
  .option('-m, --keepNodeModules [KEEP_NODE_MODULES]', 'Keep the current node_modules directory.', KEEP_NODE_MODULES)
133
139
  .option('-v, --dockerVolumes [DOCKER_VOLUMES]', 'Additional docker volumes to mount. Each volume definition has to be separated by a space (e.g. "$HOME/.gitconfig:/etc/gitconfig $HOME/.ssh:/root/.ssh")', DOCKER_VOLUMES)
140
+ .option('--no-optionalDependencies', 'Run `npm install` with `--no-optional`')
134
141
  .action((prg) => lambda.package(prg))
135
142
 
136
143
  program
@@ -157,12 +164,6 @@ program
157
164
  .option('-x, --contextFile [CONTEXT_FILE]', 'Context JSON File', CONTEXT_FILE)
158
165
  .action((prg) => lambda.setup(prg))
159
166
 
160
- program.on('command:*', (cmd) => {
161
- console.log(`Unknown command ${cmd}`)
162
- console.log()
163
- program.help()
164
- })
165
-
166
167
  program
167
168
  .version(lambda.version)
168
169
  .parse(process.argv)
package/lib/main.js CHANGED
@@ -51,7 +51,7 @@ event_sources.json and ${program.eventFile} files as needed.`)
51
51
  }
52
52
 
53
53
  run (program) {
54
- if (!['nodejs10.x', 'nodejs12.x', 'nodejs14.x'].includes(program.runtime)) {
54
+ if (!['nodejs12.x', 'nodejs14.x'].includes(program.runtime)) {
55
55
  console.error(`Runtime [${program.runtime}] is not supported.`)
56
56
  process.exit(254)
57
57
  }
@@ -385,73 +385,108 @@ Emulate only the body of the API Gateway event.
385
385
  return fs.existsSync(path.join(codeDirectory, 'package-lock.json'))
386
386
  }
387
387
 
388
- _npmInstall (program, codeDirectory) {
389
- const installCommand = this._shouldUseNpmCi(codeDirectory)
390
- ? 'ci'
391
- : 'install'
392
-
393
- const dockerBaseOptions = [
394
- 'run', '--rm',
395
- '-v', `${fs.realpathSync(codeDirectory)}:/var/task`,
396
- '-w', '/var/task'
388
+ _getNpmInstallCommand (program, codeDirectory) {
389
+ const installOptions = [
390
+ '-s',
391
+ this._shouldUseNpmCi(codeDirectory) ? 'ci' : 'install',
392
+ '--production',
393
+ '--no-audit'
397
394
  ]
398
395
 
399
- let dockerVolumesOptions = []
400
- program.dockerVolumes && program.dockerVolumes.split(' ').forEach((volume) => {
401
- dockerVolumesOptions = dockerVolumesOptions.concat(['-v', volume])
402
- })
396
+ if (program.optionalDependencies === false) {
397
+ installOptions.push('--no-optional')
398
+ }
403
399
 
404
- const dockerCommand = [program.dockerImage, 'npm', '-s', installCommand, '--production', '--no-audit']
400
+ if (!program.dockerImage) {
401
+ installOptions.push('--prefix', codeDirectory)
402
+ }
405
403
 
406
- const dockerOptions = dockerBaseOptions.concat(dockerVolumesOptions).concat(dockerCommand)
404
+ return {
405
+ packageManager: 'npm',
406
+ installOptions
407
+ }
408
+ }
407
409
 
408
- const npmInstallBaseOptions = [
410
+ _getYarnInstallCommand (program, codeDirectory) {
411
+ const installOptions = [
409
412
  '-s',
410
- installCommand,
411
- '--production',
412
- '--no-audit',
413
- '--prefix', codeDirectory
413
+ 'install',
414
+ '--production'
414
415
  ]
415
416
 
416
- const params = (() => {
417
- // reference: https://nodejs.org/api/child_process.html#child_process_spawning_bat_and_cmd_files_on_windows
417
+ if (program.optionalDependencies === false) {
418
+ installOptions.push('--ignore-optional')
419
+ }
420
+
421
+ if (!program.dockerImage) {
422
+ installOptions.push('--cwd', codeDirectory)
423
+ }
424
+
425
+ return {
426
+ packageManager: 'yarn',
427
+ installOptions
428
+ }
429
+ }
430
+
431
+ _packageInstall (program, codeDirectory) {
432
+ // Run on windows:
433
+ // https://nodejs.org/api/child_process.html#child_process_spawning_bat_and_cmd_files_on_windows
418
434
 
435
+ const { packageManager, installOptions } = (() => {
436
+ // default npm
437
+ if (program.packageManager === 'yarn') {
438
+ return this._getYarnInstallCommand(program, codeDirectory)
439
+ }
440
+ return this._getNpmInstallCommand(program, codeDirectory)
441
+ })()
442
+
443
+ const paramsOnContainer = (() => {
419
444
  // with docker
420
- if (program.dockerImage) {
421
- if (process.platform === 'win32') {
422
- return {
423
- command: 'cmd.exe',
424
- options: ['/c', 'docker'].concat(dockerOptions)
425
- }
426
- }
445
+ let dockerVolumesOptions = []
446
+ program.dockerVolumes && program.dockerVolumes.split(' ').forEach((volume) => {
447
+ dockerVolumesOptions = dockerVolumesOptions.concat(['-v', volume])
448
+ })
449
+ const dockerCommand = [program.dockerImage, packageManager].concat(installOptions)
450
+ const dockerBaseOptions = [
451
+ 'run', '--rm',
452
+ '-v', `${fs.realpathSync(codeDirectory)}:/var/task`,
453
+ '-w', '/var/task'
454
+ ]
455
+ const dockerOptions = dockerBaseOptions.concat(dockerVolumesOptions).concat(dockerCommand)
456
+ if (process.platform === 'win32') {
427
457
  return {
428
- command: 'docker',
429
- options: dockerOptions
458
+ command: 'cmd.exe',
459
+ options: ['/c', 'docker'].concat(dockerOptions)
430
460
  }
431
461
  }
462
+ return {
463
+ command: 'docker',
464
+ options: dockerOptions
465
+ }
466
+ })()
432
467
 
433
- // simple npm install
468
+ const paramsOnHost = (() => {
469
+ // simple install
434
470
  if (process.platform === 'win32') {
435
471
  return {
436
472
  command: 'cmd.exe',
437
- options: ['/c', 'npm']
438
- .concat(npmInstallBaseOptions)
439
- .concat(['--cwd', codeDirectory])
473
+ options: ['/c', packageManager].concat(installOptions)
440
474
  }
441
475
  }
442
476
  return {
443
- command: 'npm',
444
- options: npmInstallBaseOptions
477
+ command: packageManager,
478
+ options: installOptions
445
479
  }
446
480
  })()
447
481
 
482
+ const params = program.dockerImage ? paramsOnContainer : paramsOnHost
448
483
  return new Promise((resolve, reject) => {
449
484
  execFile(params.command, params.options, {
450
485
  maxBuffer: maxBufferSize,
451
486
  env: process.env
452
487
  }, (err) => {
453
488
  if (err) return reject(err)
454
- resolve()
489
+ resolve(packageManager)
455
490
  })
456
491
  })
457
492
  }
@@ -519,7 +554,7 @@ Emulate only the body of the API Gateway event.
519
554
 
520
555
  _codeDirectory () {
521
556
  // Why realpathSync?:
522
- // If tmpdir is symbolic link and npm>=7, `this._npmInstall()` may not work properly.
557
+ // If tmpdir is symbolic link and npm>=7, `this._packageInstall()` may not work properly.
523
558
  return path.join(fs.realpathSync(os.tmpdir()), `${path.basename(path.resolve('.'))}-lambda`)
524
559
  }
525
560
 
@@ -531,28 +566,27 @@ Emulate only the body of the API Gateway event.
531
566
  resolve()
532
567
  })
533
568
  })
534
- } else {
535
- return new Promise((resolve, reject) => {
536
- fs.readdir(codeDirectory, (err, files) => {
537
- if (err) return reject(err)
569
+ }
570
+ return new Promise((resolve, reject) => {
571
+ fs.readdir(codeDirectory, (err, files) => {
572
+ if (err) return reject(err)
538
573
 
539
- Promise.all(files.map(file => {
540
- return new Promise((resolve, reject) => {
541
- if (keepNodeModules && file === 'node_modules') {
574
+ Promise.all(files.map(file => {
575
+ return new Promise((resolve, reject) => {
576
+ if (keepNodeModules && file === 'node_modules') {
577
+ resolve()
578
+ } else {
579
+ fs.remove(path.join(codeDirectory, file), err => {
580
+ if (err) return reject(err)
542
581
  resolve()
543
- } else {
544
- fs.remove(path.join(codeDirectory, file), err => {
545
- if (err) return reject(err)
546
- resolve()
547
- })
548
- }
549
- })
550
- })).then(() => {
551
- resolve()
582
+ })
583
+ }
552
584
  })
585
+ })).then(() => {
586
+ resolve()
553
587
  })
554
588
  })
555
- }
589
+ })
556
590
  }
557
591
 
558
592
  _setRunTimeEnvironmentVars (program) {
@@ -568,7 +602,7 @@ Emulate only the body of the API Gateway event.
568
602
  }
569
603
  }
570
604
 
571
- _uploadExisting (lambda, params) {
605
+ async _uploadExisting (lambda, params) {
572
606
  const functionCodeParams = Object.assign({
573
607
  FunctionName: params.FunctionName,
574
608
  Publish: params.Publish
@@ -596,29 +630,30 @@ Emulate only the body of the API Gateway event.
596
630
  delete functionConfigParams.Layers
597
631
  }
598
632
 
599
- return new Promise((resolve, reject) => {
600
- const updateConfigRequest = lambda.updateFunctionConfiguration(
601
- functionConfigParams,
602
- (err, configResponse) => {
603
- if (err) return reject(err)
604
-
605
- const updateCodeRequest = lambda.updateFunctionCode(functionCodeParams, (err) => {
606
- if (err) return reject(err)
607
- resolve(configResponse)
608
- })
633
+ const updateConfigRequest = lambda.updateFunctionConfiguration(functionConfigParams)
634
+ updateConfigRequest.on('retry', (response) => {
635
+ console.log(response.error.message)
636
+ console.log('=> Retrying')
637
+ })
638
+ const updateConfigResponse = await updateConfigRequest.promise()
609
639
 
610
- updateCodeRequest.on('retry', (response) => {
611
- console.log(response.error.message)
612
- console.log('=> Retrying')
613
- })
614
- }
615
- )
640
+ // Wait for the `Configuration.LastUpdateStatus` to change from `InProgress` to `Successful`.
641
+ for (let i = 0; i < 10; i++) {
642
+ const data = await lambda.getFunction({ FunctionName: params.FunctionName }).promise()
643
+ if (data.Configuration.LastUpdateStatus === 'Successful') {
644
+ break
645
+ }
646
+ await new Promise((resolve) => setTimeout(resolve, 3000))
647
+ }
616
648
 
617
- updateConfigRequest.on('retry', (response) => {
618
- console.log(response.error.message)
619
- console.log('=> Retrying')
620
- })
649
+ const updateCodeRequest = lambda.updateFunctionCode(functionCodeParams)
650
+ updateCodeRequest.on('retry', (response) => {
651
+ console.log(response.error.message)
652
+ console.log('=> Retrying')
621
653
  })
654
+ await updateCodeRequest.promise()
655
+
656
+ return updateConfigResponse
622
657
  }
623
658
 
624
659
  _uploadNew (lambda, params) {
@@ -659,13 +694,17 @@ Emulate only the body of the API Gateway event.
659
694
  _archivePrebuilt (program) {
660
695
  const codeDirectory = this._codeDirectory()
661
696
 
662
- return this._fileCopy(program, program.prebuiltDirectory, codeDirectory, false).then(() => {
663
- console.log('=> Zipping deployment package')
664
- return this._zip(program, codeDirectory)
697
+ return Promise.resolve().then(() => {
698
+ return this._cleanDirectory(codeDirectory, program.keepNodeModules)
699
+ }).then(() => {
700
+ return this._fileCopy(program, program.prebuiltDirectory, codeDirectory, false).then(() => {
701
+ console.log('=> Zipping deployment package')
702
+ return this._zip(program, codeDirectory)
703
+ })
665
704
  })
666
705
  }
667
706
 
668
- _buildAndArchive (program) {
707
+ async _buildAndArchive (program) {
669
708
  if (!fs.existsSync('.env')) {
670
709
  console.warn('[Warning] `.env` file does not exist.')
671
710
  console.info('Execute `node-lambda setup` as necessary and set it up.')
@@ -684,24 +723,17 @@ they may not work as expected in the Lambda environment.
684
723
  const codeDirectory = this._codeDirectory()
685
724
  const lambdaSrcDirectory = program.sourceDirectory ? program.sourceDirectory.replace(/\/$/, '') : '.'
686
725
 
687
- return Promise.resolve().then(() => {
688
- return this._cleanDirectory(codeDirectory, program.keepNodeModules)
689
- }).then(() => {
690
- console.log('=> Moving files to temporary directory')
691
- return this._fileCopy(program, lambdaSrcDirectory, codeDirectory, true)
692
- }).then(() => {
693
- if (program.keepNodeModules) {
694
- return Promise.resolve()
695
- } else {
696
- console.log(`=> Running npm ${this._shouldUseNpmCi(codeDirectory) ? 'ci' : 'install'} --production`)
697
- return this._npmInstall(program, codeDirectory)
698
- }
699
- }).then(() => {
700
- return this._postInstallScript(program, codeDirectory)
701
- }).then(() => {
702
- console.log('=> Zipping deployment package')
703
- return this._zip(program, codeDirectory)
704
- })
726
+ await this._cleanDirectory(codeDirectory, program.keepNodeModules)
727
+ console.log('=> Moving files to temporary directory')
728
+ await this._fileCopy(program, lambdaSrcDirectory, codeDirectory, true)
729
+ if (!program.keepNodeModules) {
730
+ console.log('=> Running package install')
731
+ const usedPackageManager = await this._packageInstall(program, codeDirectory)
732
+ console.log(`(Package manager used was '${usedPackageManager}'.)`)
733
+ }
734
+ await this._postInstallScript(program, codeDirectory)
735
+ console.log('=> Zipping deployment package')
736
+ return this._zip(program, codeDirectory)
705
737
  }
706
738
 
707
739
  _listEventSourceMappings (lambda, params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-lambda",
3
- "version": "0.18.0",
3
+ "version": "0.21.0",
4
4
  "description": "Command line tool for locally running and remotely deploying your node.js applications to Amazon Lambda.",
5
5
  "main": "lib/main.js",
6
6
  "directories": {
@@ -32,25 +32,25 @@
32
32
  "author": "motdotla",
33
33
  "license": "BSD-2-Clause",
34
34
  "devDependencies": {
35
- "aws-sdk-mock": "^5.1.0",
36
- "chai": "^4.2.0",
37
- "mocha": "^8.2.1",
35
+ "aws-sdk-mock": "^5.2.1",
36
+ "chai": "^4.3.4",
37
+ "mocha": "^9.0.1",
38
38
  "node-zip": "^1.1.1",
39
- "standard": "^16.0.1"
39
+ "standard": "^16.0.3"
40
+ },
41
+ "engines": {
42
+ "node": ">= 10.0.0"
40
43
  },
41
44
  "dependencies": {
42
- "archiver": "^5.0.0",
43
- "aws-sdk": "^2.831.0",
44
- "aws-xray-sdk-core": "^3.1.0",
45
- "commander": "^7.0.0",
45
+ "archiver": "^5.3.0",
46
+ "aws-sdk": "^2.936.0",
47
+ "aws-xray-sdk-core": "^3.3.3",
48
+ "commander": "^8.2.0",
46
49
  "continuation-local-storage": "^3.2.1",
47
- "dotenv": "^8.2.0",
48
- "fs-extra": "^9.0.1",
50
+ "dotenv": "^10.0.0",
51
+ "fs-extra": "^10.0.0",
49
52
  "klaw": "^3.0.0",
50
53
  "minimatch": "^3.0.4",
51
- "proxy-agent": "^3.1.1"
52
- },
53
- "engines": {
54
- "node": ">= 10.0.0"
54
+ "proxy-agent": "^5.0.0"
55
55
  }
56
56
  }
package/test/main.js CHANGED
@@ -10,6 +10,7 @@ const awsMock = require('aws-sdk-mock')
10
10
  awsMock.setSDK(path.resolve('node_modules/aws-sdk'))
11
11
 
12
12
  const originalProgram = {
13
+ packageManager: 'npm',
13
14
  environment: 'development',
14
15
  accessKey: 'key',
15
16
  secretKey: 'secret',
@@ -31,17 +32,18 @@ const originalProgram = {
31
32
  contextFile: 'context.json',
32
33
  deployTimeout: 120000,
33
34
  prebuiltDirectory: '',
34
- proxy: ''
35
+ proxy: '',
36
+ optionalDependencies: true
35
37
  }
36
38
 
37
39
  let program = {}
38
40
  let codeDirectory = lambda._codeDirectory()
39
41
 
40
42
  const _timeout = function (params) {
41
- // Even if timeout is set for the whole test for Windows,
43
+ // Even if timeout is set for the whole test for Windows and Mac,
42
44
  // if it is set in local it will be valid.
43
- // For Windows, do not set it with local.
44
- if (process.platform !== 'win32') {
45
+ // For Windows and Mac, do not set it with local.
46
+ if (!['win32', 'darwin'].includes(process.platform)) {
45
47
  params.this.timeout(params.sec * 1000)
46
48
  }
47
49
  }
@@ -51,7 +53,7 @@ const lambdaMockSettings = {
51
53
  addPermission: {},
52
54
  getFunction: {
53
55
  Code: {},
54
- Configuration: {},
56
+ Configuration: { LastUpdateStatus: 'Successful' },
55
57
  FunctionArn: 'Lambda.getFunction.mock.FunctionArn'
56
58
  },
57
59
  createFunction: {
@@ -129,8 +131,8 @@ const _awsRestore = () => {
129
131
 
130
132
  /* global before, after, beforeEach, afterEach, describe, it */
131
133
  describe('lib/main', function () {
132
- if (process.platform === 'win32') {
133
- // It seems that it takes time for file operation in Windows.
134
+ if (['win32', 'darwin'].includes(process.platform)) {
135
+ // It seems that it takes time for file operation in Windows and Mac.
134
136
  // So set `timeout(60000)` for the whole test.
135
137
  this.timeout(60000)
136
138
  }
@@ -148,7 +150,7 @@ describe('lib/main', function () {
148
150
  })
149
151
 
150
152
  it('version should be set', () => {
151
- assert.equal(lambda.version, '0.18.0')
153
+ assert.equal(lambda.version, '0.21.0')
152
154
  })
153
155
 
154
156
  describe('_codeDirectory', () => {
@@ -589,65 +591,251 @@ describe('lib/main', function () {
589
591
  })
590
592
  })
591
593
 
592
- describe('_npmInstall', function () {
593
- _timeout({ this: this, sec: 30 }) // ci should be faster than install
594
+ describe('_getNpmInstallCommand', () => {
595
+ describe('when package-lock.json exists', () => {
596
+ const codeDirectory = '.'
597
+
598
+ it('npm ci', () => {
599
+ const { packageManager, installOptions } = lambda._getNpmInstallCommand(program, codeDirectory)
600
+ assert.equal(packageManager, 'npm')
601
+ assert.deepEqual(installOptions, ['-s', 'ci', '--production', '--no-audit', '--prefix', codeDirectory])
602
+ })
603
+
604
+ it('npm ci with "--no-optional"', () => {
605
+ const { packageManager, installOptions } = lambda._getNpmInstallCommand(
606
+ {
607
+ ...program,
608
+ optionalDependencies: false
609
+ },
610
+ codeDirectory
611
+ )
612
+ assert.equal(packageManager, 'npm')
613
+ assert.deepEqual(
614
+ installOptions,
615
+ ['-s', 'ci', '--production', '--no-audit', '--no-optional', '--prefix', codeDirectory]
616
+ )
617
+ })
618
+
619
+ it('npm ci on docker', () => {
620
+ const { packageManager, installOptions } = lambda._getNpmInstallCommand(
621
+ {
622
+ ...program,
623
+ dockerImage: 'test'
624
+ },
625
+ codeDirectory
626
+ )
627
+ assert.equal(packageManager, 'npm')
628
+ assert.deepEqual(installOptions, ['-s', 'ci', '--production', '--no-audit'])
629
+ })
630
+ })
631
+
632
+ describe('when package-lock.json does not exist', () => {
633
+ const codeDirectory = './test'
634
+
635
+ it('npm install', () => {
636
+ const { packageManager, installOptions } = lambda._getNpmInstallCommand(program, './test')
637
+ assert.equal(packageManager, 'npm')
638
+ assert.deepEqual(installOptions, ['-s', 'install', '--production', '--no-audit', '--prefix', './test'])
639
+ })
640
+
641
+ it('npm install with "--no-optional"', () => {
642
+ const { packageManager, installOptions } = lambda._getNpmInstallCommand(
643
+ {
644
+ ...program,
645
+ optionalDependencies: false
646
+ },
647
+ codeDirectory
648
+ )
649
+ assert.equal(packageManager, 'npm')
650
+ assert.deepEqual(
651
+ installOptions,
652
+ ['-s', 'install', '--production', '--no-audit', '--no-optional', '--prefix', codeDirectory]
653
+ )
654
+ })
655
+
656
+ it('npm install on docker', () => {
657
+ const { packageManager, installOptions } = lambda._getNpmInstallCommand(
658
+ {
659
+ ...program,
660
+ dockerImage: 'test'
661
+ },
662
+ codeDirectory
663
+ )
664
+ assert.equal(packageManager, 'npm')
665
+ assert.deepEqual(installOptions, ['-s', 'install', '--production', '--no-audit'])
666
+ })
667
+ })
668
+ })
669
+
670
+ describe('_getYarnInstallCommand', () => {
671
+ const codeDirectory = '.'
672
+
673
+ it('yarn install', () => {
674
+ const { packageManager, installOptions } = lambda._getYarnInstallCommand(program, codeDirectory)
675
+ assert.equal(packageManager, 'yarn')
676
+ assert.deepEqual(installOptions, ['-s', 'install', '--production', '--cwd', codeDirectory])
677
+ })
678
+
679
+ it('yarn install with "--no-optional"', () => {
680
+ const { packageManager, installOptions } = lambda._getYarnInstallCommand(
681
+ {
682
+ ...program,
683
+ optionalDependencies: false
684
+ },
685
+ codeDirectory
686
+ )
687
+ assert.equal(packageManager, 'yarn')
688
+ assert.deepEqual(
689
+ installOptions,
690
+ ['-s', 'install', '--production', '--ignore-optional', '--cwd', codeDirectory]
691
+ )
692
+ })
693
+
694
+ it('yarn install on docker', () => {
695
+ const { packageManager, installOptions } = lambda._getYarnInstallCommand(
696
+ {
697
+ ...program,
698
+ dockerImage: 'test'
699
+ },
700
+ codeDirectory
701
+ )
702
+ assert.equal(packageManager, 'yarn')
703
+ assert.deepEqual(installOptions, ['-s', 'install', '--production'])
704
+ })
705
+ })
706
+
707
+ describe('_packageInstall', function () {
708
+ _timeout({ this: this, sec: 60 }) // ci should be faster than install
594
709
 
595
710
  // npm treats files as packages when installing, and so removes them.
596
711
  // Test with `devDependencies` packages that are not installed with the `--production` option.
597
712
  const nodeModulesMocha = path.join(codeDirectory, 'node_modules', 'mocha')
598
713
 
599
- beforeEach(() => {
600
- return lambda._cleanDirectory(codeDirectory).then(() => {
601
- fs.copySync(
602
- path.join('node_modules', 'aws-sdk'),
603
- path.join(codeDirectory, 'node_modules', 'aws-sdk')
604
- )
605
- return lambda._fileCopy(program, '.', codeDirectory, true)
606
- })
714
+ const testCleanAndInstall = async (packageManager) => {
715
+ const beforeAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
716
+
717
+ const usedPackageManager = await lambda._packageInstall(
718
+ {
719
+ ...program,
720
+ packageManager
721
+ },
722
+ codeDirectory
723
+ )
724
+ assert.equal(usedPackageManager, packageManager)
725
+
726
+ const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
727
+ assert.include(contents, 'dotenv')
728
+
729
+ // To remove and then install.
730
+ // beforeAwsSdkStat.ctimeMs < afterAwsSdkStat.ctimeMs
731
+ const afterAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
732
+ assert.isBelow(beforeAwsSdkStat.ctimeMs, afterAwsSdkStat.ctimeMs)
733
+
734
+ // Not installed with the `--production` option.
735
+ assert.isFalse(fs.existsSync(nodeModulesMocha))
736
+ }
737
+
738
+ const beforeEachOptionalDependencies = () => {
739
+ const packageJsonPath = path.join(codeDirectory, 'package.json')
740
+ const packageJson = require(packageJsonPath)
741
+ packageJson.optionalDependencies = { npm: '*' }
742
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson))
743
+ }
744
+
745
+ const testOptionalDependenciesIsInstalled = async (packageManager) => {
746
+ const usedPackageManager = await lambda._packageInstall(
747
+ {
748
+ ...program,
749
+ packageManager
750
+ },
751
+ codeDirectory
752
+ )
753
+ assert.equal(usedPackageManager, packageManager)
754
+
755
+ const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
756
+ assert.include(contents, 'npm')
757
+ }
758
+
759
+ const testOptionalDependenciesIsNotInstalled = async (packageManager) => {
760
+ const params = {
761
+ ...program,
762
+ packageManager,
763
+ optionalDependencies: false
764
+ }
765
+ const usedPackageManager = await lambda._packageInstall(params, codeDirectory)
766
+ assert.equal(usedPackageManager, packageManager)
767
+
768
+ const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
769
+ assert.notInclude(contents, 'npm')
770
+ }
771
+
772
+ beforeEach(async () => {
773
+ await lambda._cleanDirectory(codeDirectory)
774
+ await lambda._fileCopy(program, '.', codeDirectory, false)
607
775
  })
608
776
 
609
- describe('when package-lock.json does exist', () => {
610
- it('should use "npm ci"', () => {
611
- const beforeAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
612
- return lambda._npmInstall(program, codeDirectory).then(() => {
613
- const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
614
- assert.include(contents, 'dotenv')
777
+ describe('Use npm', () => {
778
+ describe('when package-lock.json does exist', () => {
779
+ it('should use "npm ci"', () => testCleanAndInstall('npm'))
780
+ })
781
+
782
+ describe('when package-lock.json does not exist', () => {
783
+ beforeEach(() => {
784
+ return fs.removeSync(path.join(codeDirectory, 'package-lock.json'))
785
+ })
786
+
787
+ it('should use "npm install"', () => {
788
+ const beforeAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
789
+ return lambda._packageInstall(program, codeDirectory).then((usedPackageManager) => {
790
+ assert.equal(usedPackageManager, 'npm')
615
791
 
616
- // To remove and then install.
617
- // beforeAwsSdkStat.ctimeMs < afterAwsSdkStat.ctimeMs
618
- const afterAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
619
- assert.isBelow(beforeAwsSdkStat.ctimeMs, afterAwsSdkStat.ctimeMs)
792
+ const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
793
+ assert.include(contents, 'dotenv')
620
794
 
621
- // Not installed with the `--production` option.
622
- assert.isFalse(fs.existsSync(nodeModulesMocha))
795
+ // Installed packages will remain intact.
796
+ // beforeAwsSdkStat.ctimeMs === afterAwsSdkStat.ctimeMs
797
+ const afterAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
798
+ assert.equal(beforeAwsSdkStat.ctimeMs, afterAwsSdkStat.ctimeMs)
799
+
800
+ // Not installed with the `--production` option.
801
+ assert.isFalse(fs.existsSync(nodeModulesMocha))
802
+ })
623
803
  })
624
804
  })
625
- })
626
805
 
627
- describe('when package-lock.json does not exist', () => {
628
- beforeEach(() => {
629
- return fs.removeSync(path.join(codeDirectory, 'package-lock.json'))
806
+ describe('optionalDependencies', () => {
807
+ beforeEach(beforeEachOptionalDependencies)
808
+
809
+ describe('No `--no-optionalDependencies`', () => {
810
+ it('optionalDependencies is installed', () => testOptionalDependenciesIsInstalled('npm'))
811
+ })
812
+
813
+ describe('With `--no-optionalDependencies`', () => {
814
+ it('optionalDependency is NOT installed', () => testOptionalDependenciesIsNotInstalled('npm'))
815
+ })
630
816
  })
817
+ })
631
818
 
632
- it('should use "npm install"', () => {
633
- const beforeAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
634
- return lambda._npmInstall(program, codeDirectory).then(() => {
635
- const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
636
- assert.include(contents, 'dotenv')
819
+ describe('Use yarn', () => {
820
+ it('should use "yarn install"', () => testCleanAndInstall('yarn'))
637
821
 
638
- // Installed packages will remain intact.
639
- // beforeAwsSdkStat.ctimeMs === afterAwsSdkStat.ctimeMs
640
- const afterAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
641
- assert.equal(beforeAwsSdkStat.ctimeMs, afterAwsSdkStat.ctimeMs)
822
+ describe('optionalDependencies', () => {
823
+ beforeEach(beforeEachOptionalDependencies)
824
+
825
+ describe('No `--ignore-optionalDependencies`', () => {
826
+ it('optionalDependencies is installed', () => testOptionalDependenciesIsInstalled('yarn'))
827
+ })
642
828
 
643
- // Not installed with the `--production` option.
644
- assert.isFalse(fs.existsSync(nodeModulesMocha))
829
+ describe('With `--ignore-optionalDependencies`', () => {
830
+ it('optionalDependency is NOT installed', () => testOptionalDependenciesIsNotInstalled('yarn'))
645
831
  })
646
832
  })
647
833
  })
648
834
  })
649
835
 
650
- describe('_npmInstall (When codeDirectory contains characters to be escaped)', () => {
836
+ describe('_packageInstall (When codeDirectory contains characters to be escaped)', function () {
837
+ _timeout({ this: this, sec: 30 }) // give it time to build the node modules
838
+
651
839
  beforeEach(() => {
652
840
  // Since '\' can not be included in the file or directory name in Windows
653
841
  const directoryName = process.platform === 'win32'
@@ -664,14 +852,21 @@ describe('lib/main', function () {
664
852
  codeDirectory = lambda._codeDirectory()
665
853
  })
666
854
 
667
- it('_npm adds node_modules', function () {
668
- _timeout({ this: this, sec: 30 }) // give it time to build the node modules
855
+ const testFunc = async (packageManager) => {
856
+ const usedPackageManager = await lambda._packageInstall(
857
+ {
858
+ ...program,
859
+ packageManager
860
+ },
861
+ codeDirectory
862
+ )
863
+ assert.equal(usedPackageManager, packageManager)
864
+ const contents = fs.readdirSync(codeDirectory)
865
+ assert.include(contents, 'node_modules')
866
+ }
669
867
 
670
- return lambda._npmInstall(program, codeDirectory).then(() => {
671
- const contents = fs.readdirSync(codeDirectory)
672
- assert.include(contents, 'node_modules')
673
- })
674
- })
868
+ it('npm adds node_modules', () => testFunc('npm'))
869
+ it('yarn adds node_modules', () => testFunc('yarn'))
675
870
  })
676
871
 
677
872
  describe('_postInstallScript', () => {
@@ -741,53 +936,70 @@ describe('lib/main', function () {
741
936
  })
742
937
  })
743
938
 
744
- describe('_zip', () => {
745
- beforeEach(function () {
746
- _timeout({ this: this, sec: 30 }) // give it time to build the node modules
747
- return Promise.resolve().then(() => {
748
- return lambda._cleanDirectory(codeDirectory)
749
- }).then(() => {
750
- return lambda._fileCopy(program, '.', codeDirectory, true)
751
- }).then(() => {
752
- return lambda._npmInstall(program, codeDirectory)
753
- }).then(() => {
754
- if (process.platform !== 'win32') {
755
- fs.symlinkSync(
756
- path.join(__dirname, '..', 'bin', 'node-lambda'),
757
- path.join(codeDirectory, 'node-lambda-link')
758
- )
759
- }
760
- })
761
- })
939
+ describe('_zip', function () {
940
+ _timeout({ this: this, sec: 60 }) // give it time to zip
762
941
 
763
- it('Compress the file. `index.js` and `bin/node-lambda` are included and the permission is also preserved.', function () {
764
- _timeout({ this: this, sec: 30 }) // give it time to zip
942
+ const beforeTask = async (packageManager) => {
943
+ await lambda._cleanDirectory(codeDirectory)
944
+ await lambda._fileCopy(program, '.', codeDirectory, true)
945
+ const usedPackageManager = await lambda._packageInstall(
946
+ {
947
+ ...program,
948
+ packageManager
949
+ },
950
+ codeDirectory
951
+ )
952
+ assert.equal(usedPackageManager, packageManager)
953
+ if (process.platform !== 'win32') {
954
+ fs.symlinkSync(
955
+ path.join(__dirname, '..', 'bin', 'node-lambda'),
956
+ path.join(codeDirectory, 'node-lambda-link')
957
+ )
958
+ }
959
+ }
765
960
 
766
- return lambda._zip(program, codeDirectory).then((data) => {
767
- const archive = new Zip(data)
768
- assert.include(archive.files['index.js'].name, 'index.js')
769
- assert.include(archive.files['bin/node-lambda'].name, 'bin/node-lambda')
770
-
771
- if (process.platform !== 'win32') {
772
- const indexJsStat = fs.lstatSync('index.js')
773
- const binNodeLambdaStat = fs.lstatSync(path.join('bin', 'node-lambda'))
774
- assert.equal(
775
- archive.files['index.js'].unixPermissions,
776
- indexJsStat.mode
777
- )
778
- assert.equal(
779
- archive.files['bin/node-lambda'].unixPermissions,
780
- binNodeLambdaStat.mode
781
- )
782
-
783
- // isSymbolicLink
784
- assert.include(archive.files['node-lambda-link'].name, 'node-lambda-link')
785
- assert.equal(
786
- archive.files['node-lambda-link'].unixPermissions & fs.constants.S_IFMT,
787
- fs.constants.S_IFLNK
788
- )
789
- }
790
- })
961
+ const testFunc = async (packageManager) => {
962
+ // setup
963
+ await beforeTask(packageManager)
964
+
965
+ // tests
966
+ const data = await lambda._zip(program, codeDirectory)
967
+ const archive = new Zip(data)
968
+ assert.include(archive.files['index.js'].name, 'index.js')
969
+ assert.include(archive.files['bin/node-lambda'].name, 'bin/node-lambda')
970
+
971
+ if (process.platform !== 'win32') {
972
+ const indexJsStat = fs.lstatSync('index.js')
973
+ const binNodeLambdaStat = fs.lstatSync(path.join('bin', 'node-lambda'))
974
+ assert.equal(
975
+ archive.files['index.js'].unixPermissions,
976
+ indexJsStat.mode
977
+ )
978
+ assert.equal(
979
+ archive.files['bin/node-lambda'].unixPermissions,
980
+ binNodeLambdaStat.mode
981
+ )
982
+
983
+ // isSymbolicLink
984
+ assert.include(archive.files['node-lambda-link'].name, 'node-lambda-link')
985
+ assert.equal(
986
+ archive.files['node-lambda-link'].unixPermissions & fs.constants.S_IFMT,
987
+ fs.constants.S_IFLNK
988
+ )
989
+ }
990
+ }
991
+
992
+ describe('Use npm', () => {
993
+ it(
994
+ 'Compress the file. `index.js` and `bin/node-lambda` are included and the permission is also preserved.',
995
+ () => testFunc('npm')
996
+ )
997
+ })
998
+ describe('Use yarn', () => {
999
+ it(
1000
+ 'Compress the file. `index.js` and `bin/node-lambda` are included and the permission is also preserved.',
1001
+ () => testFunc('yarn')
1002
+ )
791
1003
  })
792
1004
  })
793
1005
 
@@ -833,6 +1045,24 @@ describe('lib/main', function () {
833
1045
  })
834
1046
  })
835
1047
  })
1048
+
1049
+ it('cleans the temporary directory before running `_archivePrebuilt`', function () {
1050
+ _timeout({ this: this, sec: 30 }) // give it time to zip
1051
+ const buildDir = '.build_' + Date.now()
1052
+ const codeDir = lambda._codeDirectory()
1053
+ const tmpFile = path.join(codeDir, 'deleteme')
1054
+ after(() => fs.removeSync(buildDir))
1055
+
1056
+ fs.mkdirSync(codeDir, { recursive: true })
1057
+ fs.writeFileSync(tmpFile, '...')
1058
+ fs.mkdirSync(buildDir)
1059
+ fs.writeFileSync(path.join(buildDir, 'test'), '...')
1060
+
1061
+ program.prebuiltDirectory = buildDir
1062
+ return lambda._archive(program).then((_data) => {
1063
+ assert.isNotTrue(fs.existsSync(tmpFile))
1064
+ })
1065
+ })
836
1066
  })
837
1067
 
838
1068
  describe('_readArchive', () => {