node-lambda 0.20.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,7 +9,7 @@ jobs:
9
9
  strategy:
10
10
  fail-fast: false
11
11
  matrix:
12
- node-version: [10.x, 12.x, 14.x, 16.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
15
  - uses: actions/checkout@v2
package/CHANGELOG.md CHANGED
@@ -440,3 +440,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
440
440
  - fix(_uploadExisting): fix function update errors [#575](https://github.com/motdotla/node-lambda/pull/575)
441
441
  - test: fix npm install test failing in some cases [#569](https://github.com/motdotla/node-lambda/pull/569)
442
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,24 +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"
154
- --no-optionalDependencies Run `npm install` with `--no-optional`
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
155
156
  ```
156
157
 
157
158
  #### deploy
@@ -160,51 +161,50 @@ Bundles and deploys your application up to Amazon Lambda.
160
161
 
161
162
  ```
162
163
  $ node-lambda deploy --help
163
-
164
- Usage: deploy [options]
164
+ Usage: node-lambda deploy [options]
165
165
 
166
166
  Deploy your application to Amazon Lambda
167
167
 
168
-
169
168
  Options:
170
- -h, --help output usage information
171
- -e, --environment [development] Choose environment {dev, staging, production}
172
- -E, --endpoint [AWS_ENDPOINT] Choose endpoint (e.g. localstack, "http://127.0.0.1:4574") (default: "")
173
- -a, --accessKey [your_key] AWS Access Key
174
- -s, --secretKey [your_secret] AWS Secret Key
175
- -P, --profile [] AWS Profile
176
- -k, --sessionToken [] AWS Session Token
177
- -r, --region [us-east-1] AWS Region
178
- -n, --functionName [node-lambda] Lambda FunctionName
179
- -H, --handler [index.handler] Lambda Handler {index.handler}
180
- -o, --role [your_amazon_role] Amazon Role ARN
181
- -m, --memorySize [128] Lambda Memory Size
182
- -t, --timeout [3] Lambda Timeout
183
- -d, --description [missing] Lambda Description
184
- -u, --runtime [nodejs14.x] Lambda Runtime
185
- -p, --publish [false] Lambda Publish
186
- -L, --lambdaVersion [] Lambda Function Version
187
- -b, --vpcSubnets [] Lambda Function VPC Subnet IDs (comma delimited)
188
- -g, --vpcSecurityGroups [] Lambda VPC Security Group IDs (comma delimited)
189
- -K, --kmsKeyArn [] Lambda KMS Key ARN
190
- -Q, --deadLetterConfigTargetArn [] Lambda DLQ resource
191
- -c, --tracingConfig [] Lambda tracing settings
192
- -l, --layers [] Lambda Layers settings (e.g. "ARN1,ARN2[,..])" (default: "")
193
- -R, --retentionInDays [] CloudWatchLogs retentionInDays settings
194
- -G, --sourceDirectory [] Path to lambda source Directory (e.g. "./some-lambda")
195
- -I, --dockerImage [] Docker image for npm install
196
- -f, --configFile [] Path to file holding secret environment variables (e.g. "deploy.env")
197
- -S, --eventSourceFile [] Path to file holding event source mapping variables (e.g. "event_sources.json")
198
- -x, --excludeGlobs [event.json] Space-separated glob pattern(s) for additional exclude files (e.g. "event.json dotenv.sample")
199
- -D, --prebuiltDirectory [] Prebuilt directory
200
- -T, --deployTimeout [120000] Deploy Timeout
201
- -z, --deployZipfile [] Deploy zipfile
202
- -B, --deployUseS3 [] Use S3 to deploy.
203
- -i, --imageUri [] URI of a container image in the Amazon ECR registry. (default: "")
204
- -y, --proxy [] Proxy server
205
- -A, --tags [] Tags as key value pairs (e.g. "tagname1=tagvalue1,tagname2=tagvalue2") (default: "")
206
- --silent Silent or quiet mode (default: false)
207
- --no-optionalDependencies Run `npm install` with `--no-optional`
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
208
208
  ```
209
209
 
210
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:
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 || ''
@@ -66,6 +67,7 @@ program.showSuggestionAfterError()
66
67
  program
67
68
  .command('deploy')
68
69
  .description('Deploy your application to Amazon Lambda')
70
+ .option('--packageManager [PACKAGE_MANAGER]', 'Package manager used to install dependencies', PACKAGE_MANAGER)
69
71
  .option('-e, --environment [AWS_ENVIRONMENT]', 'Choose environment {dev, staging, production}',
70
72
  AWS_ENVIRONMENT)
71
73
  .option('-E, --endpoint [AWS_ENDPOINT]', 'Choose endpoint (e.g. localstack, "http://127.0.0.1:4574")',
@@ -123,6 +125,7 @@ program
123
125
  .command('package')
124
126
  .alias('zip')
125
127
  .description('Create zipped package for Amazon Lambda deployment')
128
+ .option('--packageManager [PACKAGE_MANAGER]', 'Package manager used to install dependencies', PACKAGE_MANAGER)
126
129
  .option('-A, --packageDirectory [PACKAGE_DIRECTORY]', 'Local Package Directory', PACKAGE_DIRECTORY)
127
130
  .option('-I, --dockerImage [DOCKER_IMAGE]', 'Docker image for npm ci', DOCKER_IMAGE)
128
131
  .option('-n, --functionName [AWS_FUNCTION_NAME]', 'Lambda FunctionName', AWS_FUNCTION_NAME)
package/lib/main.js CHANGED
@@ -385,31 +385,68 @@ 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
- // Run on windows:
390
- // https://nodejs.org/api/child_process.html#child_process_spawning_bat_and_cmd_files_on_windows
391
-
392
- const installCommand = this._shouldUseNpmCi(codeDirectory)
393
- ? 'ci'
394
- : 'install'
395
- const npmInstallBaseOptions = [
388
+ _getNpmInstallCommand (program, codeDirectory) {
389
+ const installOptions = [
396
390
  '-s',
397
- installCommand,
391
+ this._shouldUseNpmCi(codeDirectory) ? 'ci' : 'install',
398
392
  '--production',
399
393
  '--no-audit'
400
394
  ]
401
395
 
402
396
  if (program.optionalDependencies === false) {
403
- npmInstallBaseOptions.push('--no-optional')
397
+ installOptions.push('--no-optional')
398
+ }
399
+
400
+ if (!program.dockerImage) {
401
+ installOptions.push('--prefix', codeDirectory)
402
+ }
403
+
404
+ return {
405
+ packageManager: 'npm',
406
+ installOptions
407
+ }
408
+ }
409
+
410
+ _getYarnInstallCommand (program, codeDirectory) {
411
+ const installOptions = [
412
+ '-s',
413
+ 'install',
414
+ '--production'
415
+ ]
416
+
417
+ if (program.optionalDependencies === false) {
418
+ installOptions.push('--ignore-optional')
419
+ }
420
+
421
+ if (!program.dockerImage) {
422
+ installOptions.push('--cwd', codeDirectory)
404
423
  }
405
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
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
+
406
443
  const paramsOnContainer = (() => {
407
444
  // with docker
408
445
  let dockerVolumesOptions = []
409
446
  program.dockerVolumes && program.dockerVolumes.split(' ').forEach((volume) => {
410
447
  dockerVolumesOptions = dockerVolumesOptions.concat(['-v', volume])
411
448
  })
412
- const dockerCommand = [program.dockerImage, 'npm'].concat(npmInstallBaseOptions)
449
+ const dockerCommand = [program.dockerImage, packageManager].concat(installOptions)
413
450
  const dockerBaseOptions = [
414
451
  'run', '--rm',
415
452
  '-v', `${fs.realpathSync(codeDirectory)}:/var/task`,
@@ -429,17 +466,16 @@ Emulate only the body of the API Gateway event.
429
466
  })()
430
467
 
431
468
  const paramsOnHost = (() => {
432
- // simple npm install
433
- const options = npmInstallBaseOptions.concat(['--prefix', codeDirectory])
469
+ // simple install
434
470
  if (process.platform === 'win32') {
435
471
  return {
436
472
  command: 'cmd.exe',
437
- options: ['/c', 'npm'].concat(options)
473
+ options: ['/c', packageManager].concat(installOptions)
438
474
  }
439
475
  }
440
476
  return {
441
- command: 'npm',
442
- options
477
+ command: packageManager,
478
+ options: installOptions
443
479
  }
444
480
  })()
445
481
 
@@ -450,7 +486,7 @@ Emulate only the body of the API Gateway event.
450
486
  env: process.env
451
487
  }, (err) => {
452
488
  if (err) return reject(err)
453
- resolve()
489
+ resolve(packageManager)
454
490
  })
455
491
  })
456
492
  }
@@ -518,7 +554,7 @@ Emulate only the body of the API Gateway event.
518
554
 
519
555
  _codeDirectory () {
520
556
  // Why realpathSync?:
521
- // 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.
522
558
  return path.join(fs.realpathSync(os.tmpdir()), `${path.basename(path.resolve('.'))}-lambda`)
523
559
  }
524
560
 
@@ -691,8 +727,9 @@ they may not work as expected in the Lambda environment.
691
727
  console.log('=> Moving files to temporary directory')
692
728
  await this._fileCopy(program, lambdaSrcDirectory, codeDirectory, true)
693
729
  if (!program.keepNodeModules) {
694
- console.log(`=> Running npm ${this._shouldUseNpmCi(codeDirectory) ? 'ci' : 'install'} --production`)
695
- await this._npmInstall(program, codeDirectory)
730
+ console.log('=> Running package install')
731
+ const usedPackageManager = await this._packageInstall(program, codeDirectory)
732
+ console.log(`(Package manager used was '${usedPackageManager}'.)`)
696
733
  }
697
734
  await this._postInstallScript(program, codeDirectory)
698
735
  console.log('=> Zipping deployment package')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-lambda",
3
- "version": "0.20.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": {
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',
@@ -149,7 +150,7 @@ describe('lib/main', function () {
149
150
  })
150
151
 
151
152
  it('version should be set', () => {
152
- assert.equal(lambda.version, '0.20.0')
153
+ assert.equal(lambda.version, '0.21.0')
153
154
  })
154
155
 
155
156
  describe('_codeDirectory', () => {
@@ -590,91 +591,251 @@ describe('lib/main', function () {
590
591
  })
591
592
  })
592
593
 
593
- describe('_npmInstall', function () {
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 () {
594
708
  _timeout({ this: this, sec: 60 }) // ci should be faster than install
595
709
 
596
710
  // npm treats files as packages when installing, and so removes them.
597
711
  // Test with `devDependencies` packages that are not installed with the `--production` option.
598
712
  const nodeModulesMocha = path.join(codeDirectory, 'node_modules', 'mocha')
599
713
 
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
+
600
772
  beforeEach(async () => {
601
773
  await lambda._cleanDirectory(codeDirectory)
602
774
  await lambda._fileCopy(program, '.', codeDirectory, false)
603
775
  })
604
776
 
605
- describe('when package-lock.json does exist', () => {
606
- it('should use "npm ci"', () => {
607
- const beforeAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
608
- return lambda._npmInstall(program, codeDirectory).then(() => {
609
- const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
610
- assert.include(contents, 'dotenv')
611
-
612
- // To remove and then install.
613
- // beforeAwsSdkStat.ctimeMs < afterAwsSdkStat.ctimeMs
614
- const afterAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
615
- assert.isBelow(beforeAwsSdkStat.ctimeMs, afterAwsSdkStat.ctimeMs)
777
+ describe('Use npm', () => {
778
+ describe('when package-lock.json does exist', () => {
779
+ it('should use "npm ci"', () => testCleanAndInstall('npm'))
780
+ })
616
781
 
617
- // Not installed with the `--production` option.
618
- assert.isFalse(fs.existsSync(nodeModulesMocha))
782
+ describe('when package-lock.json does not exist', () => {
783
+ beforeEach(() => {
784
+ return fs.removeSync(path.join(codeDirectory, 'package-lock.json'))
619
785
  })
620
- })
621
- })
622
786
 
623
- describe('when package-lock.json does not exist', () => {
624
- beforeEach(() => {
625
- return fs.removeSync(path.join(codeDirectory, 'package-lock.json'))
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')
791
+
792
+ const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
793
+ assert.include(contents, 'dotenv')
794
+
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
+ })
803
+ })
626
804
  })
627
805
 
628
- it('should use "npm install"', () => {
629
- const beforeAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
630
- return lambda._npmInstall(program, codeDirectory).then(() => {
631
- const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
632
- assert.include(contents, 'dotenv')
806
+ describe('optionalDependencies', () => {
807
+ beforeEach(beforeEachOptionalDependencies)
633
808
 
634
- // Installed packages will remain intact.
635
- // beforeAwsSdkStat.ctimeMs === afterAwsSdkStat.ctimeMs
636
- const afterAwsSdkStat = fs.statSync(path.join(codeDirectory, 'node_modules', 'aws-sdk'))
637
- assert.equal(beforeAwsSdkStat.ctimeMs, afterAwsSdkStat.ctimeMs)
809
+ describe('No `--no-optionalDependencies`', () => {
810
+ it('optionalDependencies is installed', () => testOptionalDependenciesIsInstalled('npm'))
811
+ })
638
812
 
639
- // Not installed with the `--production` option.
640
- assert.isFalse(fs.existsSync(nodeModulesMocha))
813
+ describe('With `--no-optionalDependencies`', () => {
814
+ it('optionalDependency is NOT installed', () => testOptionalDependenciesIsNotInstalled('npm'))
641
815
  })
642
816
  })
643
817
  })
644
818
 
645
- describe('optionalDependencies', () => {
646
- beforeEach(() => {
647
- const packageJsonPath = path.join(codeDirectory, 'package.json')
648
- const packageJson = require(packageJsonPath)
649
- packageJson.optionalDependencies = { npm: '*' }
650
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson))
651
- })
819
+ describe('Use yarn', () => {
820
+ it('should use "yarn install"', () => testCleanAndInstall('yarn'))
652
821
 
653
- describe('No `--no-optionalDependencies`', () => {
654
- it('optionalDependencies is installed', () => {
655
- return lambda._npmInstall(program, codeDirectory).then(() => {
656
- const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
657
- assert.include(contents, 'npm')
658
- })
822
+ describe('optionalDependencies', () => {
823
+ beforeEach(beforeEachOptionalDependencies)
824
+
825
+ describe('No `--ignore-optionalDependencies`', () => {
826
+ it('optionalDependencies is installed', () => testOptionalDependenciesIsInstalled('yarn'))
659
827
  })
660
- })
661
828
 
662
- describe('With `--no-optionalDependencies`', () => {
663
- it('optionalDependency is NOT installed', () => {
664
- const params = {
665
- ...program,
666
- optionalDependencies: false
667
- }
668
- return lambda._npmInstall(params, codeDirectory).then(() => {
669
- const contents = fs.readdirSync(path.join(codeDirectory, 'node_modules'))
670
- assert.notInclude(contents, 'npm')
671
- })
829
+ describe('With `--ignore-optionalDependencies`', () => {
830
+ it('optionalDependency is NOT installed', () => testOptionalDependenciesIsNotInstalled('yarn'))
672
831
  })
673
832
  })
674
833
  })
675
834
  })
676
835
 
677
- 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
+
678
839
  beforeEach(() => {
679
840
  // Since '\' can not be included in the file or directory name in Windows
680
841
  const directoryName = process.platform === 'win32'
@@ -691,14 +852,21 @@ describe('lib/main', function () {
691
852
  codeDirectory = lambda._codeDirectory()
692
853
  })
693
854
 
694
- it('_npm adds node_modules', function () {
695
- _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
+ }
696
867
 
697
- return lambda._npmInstall(program, codeDirectory).then(() => {
698
- const contents = fs.readdirSync(codeDirectory)
699
- assert.include(contents, 'node_modules')
700
- })
701
- })
868
+ it('npm adds node_modules', () => testFunc('npm'))
869
+ it('yarn adds node_modules', () => testFunc('yarn'))
702
870
  })
703
871
 
704
872
  describe('_postInstallScript', () => {
@@ -768,53 +936,70 @@ describe('lib/main', function () {
768
936
  })
769
937
  })
770
938
 
771
- describe('_zip', () => {
772
- beforeEach(function () {
773
- _timeout({ this: this, sec: 30 }) // give it time to build the node modules
774
- return Promise.resolve().then(() => {
775
- return lambda._cleanDirectory(codeDirectory)
776
- }).then(() => {
777
- return lambda._fileCopy(program, '.', codeDirectory, true)
778
- }).then(() => {
779
- return lambda._npmInstall(program, codeDirectory)
780
- }).then(() => {
781
- if (process.platform !== 'win32') {
782
- fs.symlinkSync(
783
- path.join(__dirname, '..', 'bin', 'node-lambda'),
784
- path.join(codeDirectory, 'node-lambda-link')
785
- )
786
- }
787
- })
788
- })
939
+ describe('_zip', function () {
940
+ _timeout({ this: this, sec: 60 }) // give it time to zip
789
941
 
790
- it('Compress the file. `index.js` and `bin/node-lambda` are included and the permission is also preserved.', function () {
791
- _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
+ }
792
960
 
793
- return lambda._zip(program, codeDirectory).then((data) => {
794
- const archive = new Zip(data)
795
- assert.include(archive.files['index.js'].name, 'index.js')
796
- assert.include(archive.files['bin/node-lambda'].name, 'bin/node-lambda')
797
-
798
- if (process.platform !== 'win32') {
799
- const indexJsStat = fs.lstatSync('index.js')
800
- const binNodeLambdaStat = fs.lstatSync(path.join('bin', 'node-lambda'))
801
- assert.equal(
802
- archive.files['index.js'].unixPermissions,
803
- indexJsStat.mode
804
- )
805
- assert.equal(
806
- archive.files['bin/node-lambda'].unixPermissions,
807
- binNodeLambdaStat.mode
808
- )
809
-
810
- // isSymbolicLink
811
- assert.include(archive.files['node-lambda-link'].name, 'node-lambda-link')
812
- assert.equal(
813
- archive.files['node-lambda-link'].unixPermissions & fs.constants.S_IFMT,
814
- fs.constants.S_IFLNK
815
- )
816
- }
817
- })
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
+ )
818
1003
  })
819
1004
  })
820
1005