• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

serverless-heaven / serverless-webpack / 23967479888

04 Apr 2026 12:39AM UTC coverage: 91.641%. First build
23967479888

Pull #2392

github

web-flow
Merge c0c175c90 into f433736e2
Pull Request #2392: fix: Copy node_modules when noInstall option is true

1011 of 1130 branches covered (89.47%)

Branch coverage included in aggregate %.

18 of 61 new or added lines in 1 file covered. (29.51%)

2552 of 2758 relevant lines covered (92.53%)

70.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.32
/lib/packExternalModules.js
1
const BbPromise = require('bluebird');
2✔
2
const _ = require('lodash');
2✔
3
const path = require('node:path');
2✔
4
const fse = require('fs-extra');
2✔
5
const findWorkspaceRoot = require('find-yarn-workspace-root');
2✔
6

2✔
7
const Packagers = require('./packagers');
2✔
8
const { isProviderGoogle } = require('./utils');
2✔
9

2✔
10
function rebaseFileReferences(pathToPackageRoot, moduleVersion) {
270✔
11
  if (/^(?:file:[^/]{2}|\.\/|\.\.\/)/.test(moduleVersion)) {
270✔
12
    const filePath = _.replace(moduleVersion, /^file:/, '');
4✔
13
    return _.replace(
4✔
14
      `${_.startsWith(moduleVersion, 'file:') ? 'file:' : ''}${pathToPackageRoot}/${filePath}`,
4!
15
      /\\/g,
4✔
16
      '/'
4✔
17
    );
4✔
18
  }
4✔
19

266✔
20
  return moduleVersion;
266✔
21
}
266✔
22

2✔
23
/**
2✔
24
 * Add the given modules to a package json's dependencies.
2✔
25
 */
2✔
26
function addModulesToPackageJson(externalModules, packageJson, pathToPackageRoot) {
90✔
27
  _.forEach(externalModules.sort(), externalModule => {
90✔
28
    const splitModule = _.split(externalModule, '@');
270✔
29
    // If we have a scoped module we have to re-add the @
270✔
30
    if (_.startsWith(externalModule, '@')) {
270✔
31
      splitModule.splice(0, 1);
62✔
32
      splitModule[0] = `@${splitModule[0]}`;
62✔
33
    }
62✔
34
    let moduleVersion = _.join(_.tail(splitModule), '@');
270✔
35
    // We have to rebase file references to the target package.json
270✔
36
    moduleVersion = rebaseFileReferences(pathToPackageRoot, moduleVersion);
270✔
37
    packageJson.dependencies = packageJson.dependencies || {};
270✔
38
    packageJson.dependencies[_.first(splitModule)] = moduleVersion;
270✔
39
  });
90✔
40
}
90✔
41

2✔
42
/**
2✔
43
 * Remove a given list of excluded modules from a module list
2✔
44
 * @this - The active plugin instance
2✔
45
 */
2✔
46
function removeExcludedModules(modules, packageForceExcludes, log) {
92✔
47
  const excludedModules = _.remove(modules, externalModule => {
92✔
48
    const splitModule = _.split(externalModule, '@');
274✔
49
    // If we have a scoped module we have to re-add the @
274✔
50
    if (_.startsWith(externalModule, '@')) {
274✔
51
      splitModule.splice(0, 1);
62✔
52
      splitModule[0] = `@${splitModule[0]}`;
62✔
53
    }
62✔
54
    const moduleName = _.first(splitModule);
274✔
55
    return _.includes(packageForceExcludes, moduleName);
274✔
56
  });
92✔
57

92✔
58
  if (log && !_.isEmpty(excludedModules)) {
92✔
59
    if (this.log) {
2!
60
      this.log(`Excluding external modules: ${_.join(excludedModules, ', ')}`);
×
61
    } else {
2✔
62
      this.serverless.cli.log(`Excluding external modules: ${_.join(excludedModules, ', ')}`);
2✔
63
    }
2✔
64
  }
2✔
65
}
92✔
66

2✔
67
/**
2✔
68
 * Resolve the needed versions of production dependencies for external modules.
2✔
69
 * @this - The active plugin instance
2✔
70
 */
2✔
71
function getProdModules(externalModules, packagePath, nodeModulesRelativeDir, dependencyGraph, forceExcludes) {
106✔
72
  const packageJsonPath = path.join(process.cwd(), packagePath);
106✔
73
  const packageJson = require(packageJsonPath);
106✔
74
  const prodModules = [];
106✔
75

106✔
76
  // only process the module stated in dependencies section
106✔
77
  if (!packageJson.dependencies) {
106!
78
    return [];
×
79
  }
×
80

106✔
81
  // Get versions of all transient modules
106✔
82
  _.forEach(externalModules, module => {
106✔
83
    let moduleVersion = packageJson.dependencies[module.external];
288✔
84

288✔
85
    if (moduleVersion) {
288✔
86
      prodModules.push(`${module.external}@${moduleVersion}`);
258✔
87

258✔
88
      let nodeModulesBase = path.join(path.dirname(path.join(process.cwd(), packagePath)), 'node_modules');
258✔
89

258✔
90
      if (nodeModulesRelativeDir) {
258✔
91
        const customNodeModulesDir = path.join(process.cwd(), nodeModulesRelativeDir, 'node_modules');
12✔
92

12✔
93
        if (fse.pathExistsSync(customNodeModulesDir)) {
12✔
94
          nodeModulesBase = customNodeModulesDir;
12✔
95
        } else {
12!
96
          if (this.log) {
×
97
            this.log.warning(`${customNodeModulesDir} dose not exist. Please check nodeModulesRelativeDir setting`);
×
98
          } else {
×
99
            this.serverless.cli.log(
×
100
              `WARNING: ${customNodeModulesDir} dose not exist. Please check nodeModulesRelativeDir setting`
×
101
            );
×
102
          }
×
103
        }
×
104
      }
12✔
105

258✔
106
      // Check if the module has any peer dependencies and include them too
258✔
107
      try {
258✔
108
        const modulePackagePath = path.join(nodeModulesBase, module.external, 'package.json');
258✔
109

258✔
110
        const peerDependencies = require(modulePackagePath).peerDependencies;
258✔
111
        if (!_.isEmpty(peerDependencies)) {
258✔
112
          if (this.log) {
12!
113
            this.log.verbose(`Adding explicit peers for dependency ${module.external}`);
×
114
          } else {
12✔
115
            this.options.verbose && this.serverless.cli.log(`Adding explicit peers for dependency ${module.external}`);
12✔
116
          }
12✔
117

12✔
118
          const peerDependenciesMeta = require(modulePackagePath).peerDependenciesMeta;
12✔
119

12✔
120
          if (!_.isEmpty(peerDependenciesMeta)) {
12✔
121
            _.forEach(peerDependencies, (_value, key) => {
8✔
122
              if (peerDependenciesMeta[key] && peerDependenciesMeta[key].optional === true) {
12✔
123
                if (this.log) {
4!
124
                  this.log.verbose(
×
125
                    `Skipping peers dependency ${key} for dependency ${module.external} because it's optional`
×
126
                  );
×
127
                } else {
4✔
128
                  this.options.verbose &&
4✔
129
                    this.serverless.cli.log(
4✔
130
                      `Skipping peers dependency ${key} for dependency ${module.external} because it's optional`
4✔
131
                    );
4✔
132
                }
4✔
133
                _.unset(peerDependencies, key);
4✔
134
              }
4✔
135
            });
8✔
136
          }
8✔
137

12✔
138
          if (!_.isEmpty(peerDependencies)) {
12✔
139
            const peerModules = getProdModules.call(
12✔
140
              this,
12✔
141
              _.map(peerDependencies, (_value, key) => ({ external: key })),
12✔
142
              packagePath,
12✔
143
              nodeModulesRelativeDir,
12✔
144
              dependencyGraph,
12✔
145
              forceExcludes
12✔
146
            );
12✔
147
            Array.prototype.push.apply(prodModules, peerModules);
12✔
148
          }
12✔
149
        }
12✔
150
      } catch (_e) {
258✔
151
        if (this.log) {
110✔
152
          this.log.warning(
4✔
153
            `Could not check for peer dependencies of ${module.external}. Set nodeModulesRelativeDir if node_modules is in different directory.`
4✔
154
          );
4✔
155
        } else {
110✔
156
          this.serverless.cli.log(
106✔
157
            `WARNING: Could not check for peer dependencies of ${module.external}. Set nodeModulesRelativeDir if node_modules is in different directory.`
106✔
158
          );
106✔
159
        }
106✔
160
      }
110✔
161
    } else {
288✔
162
      if (!packageJson.devDependencies || !packageJson.devDependencies[module.external]) {
30✔
163
        // Add transient dependencies if they appear not in the service's dev dependencies
16✔
164
        const originInfo = _.get(dependencyGraph, 'dependencies', {})[module.origin] || {};
16✔
165
        moduleVersion = _.get(_.get(originInfo, 'dependencies', {})[module.external], 'version');
16✔
166
        if (!moduleVersion) {
16✔
167
          moduleVersion = _.get(dependencyGraph, ['dependencies', module.external, 'version']);
16✔
168
        }
16✔
169
        if (!moduleVersion) {
16✔
170
          if (this.log) {
12!
171
            this.log.warning(`Could not determine version of module ${module.external}`);
×
172
          } else {
12✔
173
            this.serverless.cli.log(`WARNING: Could not determine version of module ${module.external}`);
12✔
174
          }
12✔
175
        }
12✔
176
        prodModules.push(moduleVersion ? `${module.external}@${moduleVersion}` : module.external);
16✔
177
      } else if (packageJson.devDependencies?.[module.external] && !_.includes(forceExcludes, module.external)) {
30✔
178
        // To minimize the chance of breaking setups we whitelist packages available on AWS here. These are due to the previously missing check
10✔
179
        // most likely set in devDependencies and should not lead to an error now.
10✔
180
        const ignoredDevDependencies = ['aws-sdk'];
10✔
181

10✔
182
        if (!_.includes(ignoredDevDependencies, module.external)) {
10✔
183
          // Runtime dependency found in devDependencies but not forcefully excluded
2✔
184
          if (this.log) {
2!
185
            this.log.error(
×
186
              `Runtime dependency '${module.external}' found in devDependencies. Move it to dependencies or use forceExclude to explicitly exclude it.`
×
187
            );
×
188
          } else {
2✔
189
            this.serverless.cli.log(
2✔
190
              `ERROR: Runtime dependency '${module.external}' found in devDependencies. Move it to dependencies or use forceExclude to explicitly exclude it.`
2✔
191
            );
2✔
192
          }
2✔
193
          throw new this.serverless.classes.Error(`Serverless-webpack dependency error: ${module.external}.`);
2✔
194
        }
2✔
195
        if (this.log) {
10✔
196
          this.log.verbose(
4✔
197
            `Runtime dependency '${module.external}' found in devDependencies. It has been excluded automatically.`
4✔
198
          );
4✔
199
        } else {
4✔
200
          this.options.verbose &&
4✔
201
            this.serverless.cli.log(
4✔
202
              `INFO: Runtime dependency '${module.external}' found in devDependencies. It has been excluded automatically.`
4✔
203
            );
4✔
204
        }
4✔
205
      }
10✔
206
    }
30✔
207
  });
106✔
208

106✔
209
  return prodModules;
106✔
210
}
106✔
211

2✔
212
module.exports = {
2✔
213
  /**
2✔
214
   * We need a performant algorithm to install the packages for each single
2✔
215
   * function (in case we package individually).
2✔
216
   * (1) We fetch ALL packages needed by ALL functions in a first step
2✔
217
   * and use this as a base npm checkout. The checkout will be done to a
2✔
218
   * separate temporary directory with a package.json that contains everything.
2✔
219
   * (2) For each single compile we copy the whole node_modules to the compile
2✔
220
   * directory and create a (function) compile specific package.json and store
2✔
221
   * it in the compile directory. Now we start npm again there, and npm will just
2✔
222
   * remove the superfluous packages and optimize the remaining dependencies.
2✔
223
   * This will utilize the npm cache at its best and give us the needed results
2✔
224
   * and performance.
2✔
225
   */
2✔
226
  packExternalModules() {
2✔
227
    if (this.skipCompile) {
56✔
228
      return BbPromise.resolve();
2✔
229
    }
2✔
230

54✔
231
    const stats = this.compileStats;
54✔
232

54✔
233
    const includes = this.configuration.includeModules;
54✔
234

54✔
235
    if (!includes) {
56✔
236
      return BbPromise.resolve();
2✔
237
    }
2✔
238
    if (this.log) {
56✔
239
      this.log.verbose('Packing external modules');
6✔
240
      this.progress.get('webpack').notice('[Webpack] Packing external modules');
6✔
241
    }
6✔
242

52✔
243
    // Read plugin configuration
52✔
244
    const packageForceIncludes = _.get(includes, 'forceInclude', []);
52✔
245
    const packageForceExcludes = _.get(includes, 'forceExclude', []);
52✔
246
    const packagePath = includes.packagePath || './package.json';
56✔
247
    const nodeModulesRelativeDir = includes.nodeModulesRelativeDir;
56✔
248
    const packageJsonPath = path.join(process.cwd(), packagePath);
56✔
249
    const packageScripts = _.reduce(
56✔
250
      this.configuration.packagerOptions.scripts || [],
56✔
251
      (__, script, index) => {
56✔
252
        __[`script${index}`] = script;
×
253
        return __;
×
254
      },
56✔
255
      {}
56✔
256
    );
56✔
257

56✔
258
    // Determine and create packager
56✔
259
    return BbPromise.try(() => Packagers.get.call(this, this.configuration.packager)).then(packager => {
56✔
260
      // Fetch needed original package.json sections
52✔
261
      const sectionNames = packager.copyPackageSectionNames(this.configuration.packagerOptions);
52✔
262
      const packageJson = this.serverless.utils.readFileSync(packageJsonPath);
52✔
263
      const packageSections = _.pick(packageJson, sectionNames);
52✔
264
      if (!_.isEmpty(packageSections)) {
52✔
265
        if (this.log) {
4!
266
          this.log.verbose(`Using package.json sections ${_.join(_.keys(packageSections), ', ')}`);
×
267
        } else {
4✔
268
          this.options.verbose &&
4✔
269
            this.serverless.cli.log(`Using package.json sections ${_.join(_.keys(packageSections), ', ')}`);
4✔
270
        }
4✔
271
      }
4✔
272

52✔
273
      // Get first level dependency graph
52✔
274
      if (this.log) {
52✔
275
        this.log.verbose(`Fetch dependency graph from ${packageJsonPath}`);
6✔
276
      } else {
52✔
277
        this.options.verbose && this.serverless.cli.log(`Fetch dependency graph from ${packageJsonPath}`);
46✔
278
      }
46✔
279

52✔
280
      return packager
52✔
281
        .getProdDependencies(path.dirname(packageJsonPath), 1, this.configuration.packagerOptions)
52✔
282
        .then(dependencyGraph => {
52✔
283
          const problems = _.get(dependencyGraph, 'problems', []);
50✔
284
          if (this.options.verbose && !_.isEmpty(problems)) {
50✔
285
            if (this.log) {
2!
286
              this.log.verbose(`Ignoring ${_.size(problems)} NPM errors:`);
×
287
            } else {
2✔
288
              this.serverless.cli.log(`Ignoring ${_.size(problems)} NPM errors:`);
2✔
289
            }
2✔
290
            _.forEach(problems, problem => {
2✔
291
              if (this.log) {
4!
292
                this.log.verbose(`=> ${problem}`);
×
293
              } else {
4✔
294
                this.serverless.cli.log(`=> ${problem}`);
4✔
295
              }
4✔
296
            });
2✔
297
          }
2✔
298

50✔
299
          // (1) Generate dependency composition
50✔
300
          const compositeModules = _.uniq(
50✔
301
            _.flatMap(stats.stats, compileStats => {
50✔
302
              const externalModules = _.concat(
50✔
303
                compileStats.externalModules,
50✔
304
                _.map(packageForceIncludes, whitelistedPackage => ({
50✔
305
                  external: whitelistedPackage
8✔
306
                }))
50✔
307
              );
50✔
308
              return getProdModules.call(
50✔
309
                this,
50✔
310
                externalModules,
50✔
311
                packagePath,
50✔
312
                nodeModulesRelativeDir,
50✔
313
                dependencyGraph,
50✔
314
                packageForceExcludes
50✔
315
              );
50✔
316
            })
50✔
317
          );
50✔
318
          removeExcludedModules.call(this, compositeModules, packageForceExcludes, true);
50✔
319

50✔
320
          if (_.isEmpty(compositeModules)) {
50✔
321
            // The compiled code does not reference any external modules at all
2✔
322
            if (this.log) {
2!
323
              this.log('No external modules needed');
×
324
            } else {
2✔
325
              this.serverless.cli.log('No external modules needed');
2✔
326
            }
2✔
327
            return BbPromise.resolve();
2✔
328
          }
2✔
329

46✔
330
          // (1.a) Install all needed modules
46✔
331
          const compositeModulePath = path.join(this.webpackOutputPath, 'dependencies');
46✔
332
          const compositePackageJson = path.join(compositeModulePath, 'package.json');
46✔
333

46✔
334
          // (1.a.1) Create a package.json
46✔
335
          const compositePackage = _.defaults(
46✔
336
            {
46✔
337
              name: this.serverless.service.service,
46✔
338
              version: '1.0.0',
46✔
339
              description: `Packaged externals for ${this.serverless.service.service}`,
46✔
340
              private: true,
46✔
341
              scripts: packageScripts
46✔
342
            },
46✔
343
            packageSections
46✔
344
          );
46✔
345
          const relPath = path.relative(compositeModulePath, path.dirname(packageJsonPath));
46✔
346
          addModulesToPackageJson(compositeModules, compositePackage, relPath);
46✔
347
          this.serverless.utils.writeFileSync(compositePackageJson, JSON.stringify(compositePackage, null, 2));
46✔
348

46✔
349
          // (1.a.2) Copy package-lock.json if it exists, to prevent unwanted upgrades
46✔
350
          const packagerOptions = this.configuration.packagerOptions || {};
50!
351
          const packageLockPath = path.join(
50✔
352
            findWorkspaceRoot(path.dirname(packageJsonPath)) || path.dirname(packageJsonPath),
50✔
353
            packagerOptions.lockFile || packager.lockfileName
50✔
354
          );
50✔
355
          let hasPackageLock = false;
50✔
356
          return BbPromise.fromCallback(cb => fse.pathExists(packageLockPath, cb))
50✔
357
            .then(exists => {
50✔
358
              if (exists) {
46✔
359
                if (this.log) {
16✔
360
                  this.log('Package lock found - Using locked versions');
6✔
361
                } else {
16✔
362
                  this.serverless.cli.log('Package lock found - Using locked versions');
10✔
363
                }
10✔
364
                try {
16✔
365
                  let packageLockFile = this.serverless.utils.readFileSync(packageLockPath);
16✔
366
                  packageLockFile = packager.rebaseLockfile(relPath, packageLockFile);
16✔
367
                  if (_.isObject(packageLockFile)) {
16✔
368
                    packageLockFile = JSON.stringify(packageLockFile, null, 2);
8✔
369
                  }
8✔
370

12✔
371
                  this.serverless.utils.writeFileSync(
12✔
372
                    path.join(compositeModulePath, packager.lockfileName),
12✔
373
                    packageLockFile
12✔
374
                  );
12✔
375
                  hasPackageLock = true;
12✔
376
                } catch (err) {
16✔
377
                  if (this.log) {
4!
378
                    this.log.warning(`Could not read lock file: ${err.message}`);
×
379
                  } else {
4✔
380
                    this.serverless.cli.log(`Warning: Could not read lock file: ${err.message}`);
4✔
381
                  }
4✔
382
                }
4✔
383
              }
16✔
384
              return BbPromise.resolve();
46✔
385
            })
50✔
386
            .then(() => {
50✔
387
              const start = _.now();
46✔
388
              if (this.log) {
46✔
389
                this.log(`Packing external modules: ${compositeModules.join(', ')}`);
6✔
390
              } else {
46✔
391
                this.serverless.cli.log(`Packing external modules: ${compositeModules.join(', ')}`);
40✔
392
              }
40✔
393

46✔
394
              const installOrCopy = () => {
46✔
395
                if (packagerOptions.noInstall) {
46!
NEW
396
                  if (this.log) {
×
NEW
397
                    this.log.verbose('noInstall: true, copying existing node_modules');
×
NEW
398
                  } else {
×
NEW
399
                    this.options.verbose &&
×
NEW
400
                      this.serverless.cli.log('INFO: noInstall: true, copying existing node_modules');
×
NEW
401
                  }
×
NEW
402

×
NEW
403
                  let sourceNodeModules;
×
NEW
404
                  if (nodeModulesRelativeDir) {
×
NEW
405
                    const customNodeModulesDir = path.join(process.cwd(), nodeModulesRelativeDir, 'node_modules');
×
NEW
406
                    if (fse.pathExistsSync(customNodeModulesDir)) {
×
NEW
407
                      sourceNodeModules = customNodeModulesDir;
×
408
                    } else {
×
NEW
409
                      if (this.log) {
×
NEW
410
                        this.log.warning(
×
NEW
411
                          `${customNodeModulesDir} does not exist. Please check nodeModulesRelativeDir setting`
×
NEW
412
                        );
×
NEW
413
                      } else {
×
NEW
414
                        this.serverless.cli.log(
×
NEW
415
                          `WARNING: ${customNodeModulesDir} does not exist. Please check nodeModulesRelativeDir setting`
×
NEW
416
                        );
×
NEW
417
                      }
×
418
                    }
×
NEW
419
                  }
×
NEW
420

×
NEW
421
                  if (!sourceNodeModules) {
×
NEW
422
                    sourceNodeModules = path.join(
×
NEW
423
                      findWorkspaceRoot(path.dirname(packageJsonPath)) || path.dirname(packageJsonPath),
×
NEW
424
                      'node_modules'
×
NEW
425
                    );
×
NEW
426
                  }
×
NEW
427

×
NEW
428
                  const targetNodeModules = path.join(compositeModulePath, 'node_modules');
×
NEW
429

×
NEW
430
                  if (this.log) {
×
NEW
431
                    this.log.verbose(`Copying node_modules from ${sourceNodeModules} to ${targetNodeModules}`);
×
NEW
432
                  } else {
×
NEW
433
                    this.options.verbose &&
×
NEW
434
                      this.serverless.cli.log(
×
NEW
435
                        `INFO: Copying node_modules from ${sourceNodeModules} to ${targetNodeModules}`
×
NEW
436
                      );
×
NEW
437
                  }
×
NEW
438

×
NEW
439
                  return BbPromise.fromCallback(cb => fse.copy(sourceNodeModules, targetNodeModules, cb));
×
NEW
440
                }
×
441

46✔
442
                return packager.getPackagerVersion(compositeModulePath).then(version => {
46✔
443
                  return packager.install(compositeModulePath, this.configuration.packagerOptions, version);
46✔
444
                });
46✔
445
              };
46✔
446

46✔
447
              return installOrCopy()
46✔
448
                .then(() => {
46✔
449
                  if (this.log) {
44✔
450
                    this.log.verbose(`Package took [${_.now() - start} ms]`);
6✔
451
                  } else {
44✔
452
                    this.options.verbose && this.serverless.cli.log(`Package took [${_.now() - start} ms]`);
38✔
453
                  }
38✔
454
                  return null;
44✔
455
                })
46✔
456
                .return(stats.stats);
46✔
457
            })
50✔
458
            .mapSeries(compileStats => {
50✔
459
              const modulePath = compileStats.outputPath;
44✔
460

44✔
461
              // Create package.json
44✔
462
              const modulePackageJson = path.join(modulePath, 'package.json');
44✔
463
              const modulePackage = _.defaults(
44✔
464
                {
44✔
465
                  name: this.serverless.service.service,
44✔
466
                  version: '1.0.0',
44✔
467
                  description: `Packaged externals for ${this.serverless.service.service}`,
44✔
468
                  private: true,
44✔
469
                  scripts: packageScripts,
44✔
470
                  dependencies: {}
44✔
471
                },
44✔
472
                packageSections
44✔
473
              );
44✔
474
              const prodModules = getProdModules.call(
44✔
475
                this,
44✔
476
                _.concat(
44✔
477
                  compileStats.externalModules,
44✔
478
                  _.map(packageForceIncludes, whitelistedPackage => ({
44✔
479
                    external: whitelistedPackage
8✔
480
                  }))
44✔
481
                ),
44✔
482
                packagePath,
44✔
483
                nodeModulesRelativeDir,
44✔
484
                dependencyGraph,
44✔
485
                packageForceExcludes
44✔
486
              );
44✔
487
              removeExcludedModules.call(this, prodModules, packageForceExcludes);
44✔
488
              const relPath = path.relative(modulePath, path.dirname(packageJsonPath));
44✔
489
              addModulesToPackageJson(prodModules, modulePackage, relPath);
44✔
490
              this.serverless.utils.writeFileSync(modulePackageJson, JSON.stringify(modulePackage, null, 2));
44✔
491

44✔
492
              // GOOGLE: Copy modules only if not google-cloud-functions
44✔
493
              //         GCF Auto installs the package json
44✔
494
              if (isProviderGoogle(this.serverless)) {
44✔
495
                return BbPromise.resolve();
2✔
496
              }
2✔
497

42✔
498
              const startCopy = _.now();
42✔
499
              return BbPromise.try(() => {
42✔
500
                // Only copy dependency modules if demanded by packager
42✔
501
                if (packager.mustCopyModules) {
42✔
502
                  return BbPromise.fromCallback(callback =>
36✔
503
                    fse.copy(
36✔
504
                      path.join(compositeModulePath, 'node_modules'),
36✔
505
                      path.join(modulePath, 'node_modules'),
36✔
506
                      callback
36✔
507
                    )
36✔
508
                  );
36✔
509
                }
36✔
510
                return BbPromise.resolve();
6✔
511
              })
42✔
512
                .then(() =>
42✔
513
                  hasPackageLock
42✔
514
                    ? BbPromise.fromCallback(callback =>
12✔
515
                        fse.copy(
12✔
516
                          path.join(compositeModulePath, packager.lockfileName),
12✔
517
                          path.join(modulePath, packager.lockfileName),
12✔
518
                          callback
12✔
519
                        )
12✔
520
                      )
12✔
521
                    : BbPromise.resolve()
30✔
522
                )
42✔
523
                .tap(() => {
42✔
524
                  if (this.log) {
42✔
525
                    this.log.verbose(`Copy modules: ${modulePath} [${_.now() - startCopy} ms]`);
6✔
526
                  } else {
42✔
527
                    this.options.verbose &&
36✔
528
                      this.serverless.cli.log(`Copy modules: ${modulePath} [${_.now() - startCopy} ms]`);
36✔
529
                  }
36✔
530
                })
42✔
531
                .then(() => {
42✔
532
                  // Prune extraneous packages - removes not needed ones
42✔
533
                  const startPrune = _.now();
42✔
534
                  return packager.getPackagerVersion(modulePath).then(version => {
42✔
535
                    return packager.prune(modulePath, this.configuration.packagerOptions, version).tap(() => {
42✔
536
                      if (this.log) {
42✔
537
                        this.log.verbose(`Prune: ${modulePath} [${_.now() - startPrune} ms]`);
6✔
538
                      } else {
42✔
539
                        this.options.verbose &&
36✔
540
                          this.serverless.cli.log(`Prune: ${modulePath} [${_.now() - startPrune} ms]`);
36✔
541
                      }
36✔
542
                    });
42✔
543
                  });
42✔
544
                })
42✔
545
                .then(() => {
42✔
546
                  // Prune extraneous packages - removes not needed ones
42✔
547
                  const startRunScripts = _.now();
42✔
548
                  return packager.runScripts(modulePath, _.keys(packageScripts)).tap(() => {
42✔
549
                    if (this.log) {
42✔
550
                      this.log.verbose(`Run scripts: ${modulePath} [${_.now() - startRunScripts} ms]`);
6✔
551
                    } else {
42✔
552
                      this.options.verbose &&
36✔
553
                        this.serverless.cli.log(`Run scripts: ${modulePath} [${_.now() - startRunScripts} ms]`);
36✔
554
                    }
36✔
555
                  });
42✔
556
                });
42✔
557
            })
50✔
558
            .return();
50✔
559
        });
52✔
560
    });
56✔
561
  }
56✔
562
};
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc