Skip to content

Commit ffefe00

Browse files
authored
Merge pull request #1252 from jagonzalr/detect-yarn-2-or-above
Detect yarn version to dynamically not add deprecated flags
2 parents 035f40f + eb892ec commit ffefe00

File tree

7 files changed

+151
-30
lines changed

7 files changed

+151
-30
lines changed

lib/packExternalModules.js

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -397,17 +397,20 @@ module.exports = {
397397
} else {
398398
this.serverless.cli.log('Packing external modules: ' + compositeModules.join(', '));
399399
}
400-
return packager
401-
.install(compositeModulePath, this.configuration.packagerOptions)
402-
.then(() => {
403-
if (this.log) {
404-
this.log.verbose(`Package took [${_.now() - start} ms]`);
405-
} else {
406-
this.options.verbose && this.serverless.cli.log(`Package took [${_.now() - start} ms]`);
407-
}
408-
return null;
409-
})
410-
.return(stats.stats);
400+
401+
return packager.getPackagerVersion(compositeModulePath).then(version => {
402+
return packager
403+
.install(compositeModulePath, this.configuration.packagerOptions, version)
404+
.then(() => {
405+
if (this.log) {
406+
this.log.verbose(`Package took [${_.now() - start} ms]`);
407+
} else {
408+
this.options.verbose && this.serverless.cli.log(`Package took [${_.now() - start} ms]`);
409+
}
410+
return null;
411+
})
412+
.return(stats.stats);
413+
});
411414
})
412415
.mapSeries(compileStats => {
413416
const modulePath = compileStats.outputPath;
@@ -485,13 +488,15 @@ module.exports = {
485488
.then(() => {
486489
// Prune extraneous packages - removes not needed ones
487490
const startPrune = _.now();
488-
return packager.prune(modulePath, this.configuration.packagerOptions).tap(() => {
489-
if (this.log) {
490-
this.log.verbose(`Prune: ${modulePath} [${_.now() - startPrune} ms]`);
491-
} else {
492-
this.options.verbose &&
493-
this.serverless.cli.log(`Prune: ${modulePath} [${_.now() - startPrune} ms]`);
494-
}
491+
return packager.getPackagerVersion(modulePath).then(version => {
492+
return packager.prune(modulePath, this.configuration.packagerOptions, version).tap(() => {
493+
if (this.log) {
494+
this.log.verbose(`Prune: ${modulePath} [${_.now() - startPrune} ms]`);
495+
} else {
496+
this.options.verbose &&
497+
this.serverless.cli.log(`Prune: ${modulePath} [${_.now() - startPrune} ms]`);
498+
}
499+
});
495500
});
496501
})
497502
.then(() => {

lib/packagers/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* static get lockfileName(): string;
1010
* static get copyPackageSectionNames(): Array<string>;
1111
* static get mustCopyModules(): boolean;
12+
* static getPackagerVersion(cwd: string): BbPromise<Object>
1213
* static getProdDependencies(cwd: string, depth: number = 1): BbPromise<Object>;
1314
* static rebaseLockfile(pathToPackageRoot: string, lockfile: Object): void;
1415
* static install(cwd: string): BbPromise<void>;

lib/packagers/npm.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ class NPM {
2525
return true;
2626
}
2727

28+
static getPackagerVersion(cwd) {
29+
const command = /^win/.test(process.platform) ? 'npm.cmd' : 'npm';
30+
const args = ['-v'];
31+
32+
return Utils.spawnProcess(command, args, { cwd })
33+
.catch(err => {
34+
return BbPromise.resolve({ stdout: err.stdout });
35+
})
36+
.then(processOutput => processOutput.stdout);
37+
}
38+
2839
static getProdDependencies(cwd, depth, packagerOptions) {
2940
// Try to use NPM lockfile v2 when possible
3041
const options = packagerOptions || {};

lib/packagers/yarn.js

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@ class Yarn {
2929
return false;
3030
}
3131

32+
static isBerryVersion(version) {
33+
const versionNumber = version.charAt(0);
34+
const mainVersion = parseInt(versionNumber);
35+
return mainVersion > 1;
36+
}
37+
38+
static getPackagerVersion(cwd) {
39+
const command = /^win/.test(process.platform) ? 'yarn.cmd' : 'yarn';
40+
const args = ['-v'];
41+
42+
return Utils.spawnProcess(command, args, { cwd })
43+
.catch(err => {
44+
return BbPromise.resolve({ stdout: err.stdout });
45+
})
46+
.then(processOutput => processOutput.stdout);
47+
}
48+
3249
static getProdDependencies(cwd, depth) {
3350
const command = /^win/.test(process.platform) ? 'yarn.cmd' : 'yarn';
3451
const args = ['list', `--depth=${depth || 1}`, '--json', '--production'];
@@ -118,20 +135,24 @@ class Yarn {
118135
return _.reduce(replacements, (__, replacement) => _.replace(__, replacement.oldRef, replacement.newRef), lockfile);
119136
}
120137

121-
static install(cwd, packagerOptions) {
138+
static install(cwd, packagerOptions, version) {
122139
if (packagerOptions.noInstall) {
123140
return BbPromise.resolve();
124141
}
142+
const isBerry = Yarn.isBerryVersion(version);
125143

126144
const command = /^win/.test(process.platform) ? 'yarn.cmd' : 'yarn';
127145
const args = ['install'];
128-
129146
// Convert supported packagerOptions
130-
if (!packagerOptions.noNonInteractive) {
147+
if (!packagerOptions.noNonInteractive && !isBerry) {
131148
args.push('--non-interactive');
132149
}
133150
if (!packagerOptions.noFrozenLockfile) {
134-
args.push('--frozen-lockfile');
151+
if (isBerry) {
152+
args.push('--immutable');
153+
} else {
154+
args.push('--frozen-lockfile');
155+
}
135156
}
136157
if (packagerOptions.ignoreScripts) {
137158
args.push('--ignore-scripts');
@@ -144,8 +165,8 @@ class Yarn {
144165
}
145166

146167
// "Yarn install" prunes automatically
147-
static prune(cwd, packagerOptions) {
148-
return Yarn.install(cwd, packagerOptions);
168+
static prune(cwd, packagerOptions, version) {
169+
return Yarn.install(cwd, packagerOptions, version);
149170
}
150171

151172
static runScripts(cwd, scriptNames) {

tests/packExternalModules.test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ jest.mock('../lib/packagers/index', () => {
2121
copyPackageSectionNames: ['section1', 'section2'],
2222
mustCopyModules: true,
2323
rebaseLockfile: jest.fn(),
24+
getPackagerVersion: jest.fn(),
2425
getProdDependencies: jest.fn(),
2526
install: jest.fn(),
2627
prune: jest.fn(),
@@ -203,6 +204,7 @@ describe('packExternalModules', () => {
203204
fsExtraMock.pathExists.mockImplementation((p, cb) => cb(null, false));
204205
fsExtraMock.copy.mockImplementation((from, to, cb) => cb());
205206
packagerFactoryMock.get('npm').getProdDependencies.mockReturnValue(BbPromise.resolve({}));
207+
packagerFactoryMock.get('npm').getPackagerVersion.mockReturnValue(BbPromise.resolve());
206208
packagerFactoryMock.get('npm').install.mockReturnValue(BbPromise.resolve());
207209
packagerFactoryMock.get('npm').prune.mockReturnValue(BbPromise.resolve());
208210
packagerFactoryMock.get('npm').runScripts.mockReturnValue(BbPromise.resolve());
@@ -219,6 +221,7 @@ describe('packExternalModules', () => {
219221
expect(fsExtraMock.copy).toHaveBeenCalledTimes(1),
220222
// npm ls and npm prune should have been called
221223
expect(packagerFactoryMock.get('npm').getProdDependencies).toHaveBeenCalledTimes(1),
224+
expect(packagerFactoryMock.get('npm').getPackagerVersion).toHaveBeenCalledTimes(2),
222225
expect(packagerFactoryMock.get('npm').install).toHaveBeenCalledTimes(1),
223226
expect(packagerFactoryMock.get('npm').prune).toHaveBeenCalledTimes(1),
224227
expect(packagerFactoryMock.get('npm').runScripts).toHaveBeenCalledTimes(1)
@@ -256,6 +259,7 @@ describe('packExternalModules', () => {
256259
fsExtraMock.pathExists.mockImplementation((p, cb) => cb(null, false));
257260
fsExtraMock.copy.mockImplementation((from, to, cb) => cb());
258261
packagerFactoryMock.get('npm').getProdDependencies.mockReturnValue(BbPromise.resolve({}));
262+
packagerFactoryMock.get('npm').getPackagerVersion.mockReturnValue(BbPromise.resolve());
259263
packagerFactoryMock.get('npm').install.mockReturnValue(BbPromise.resolve());
260264
packagerFactoryMock.get('npm').prune.mockReturnValue(BbPromise.resolve());
261265
packagerFactoryMock.get('npm').runScripts.mockReturnValue(BbPromise.resolve());
@@ -272,6 +276,7 @@ describe('packExternalModules', () => {
272276
expect(fsExtraMock.copy).toHaveBeenCalledTimes(1),
273277
// npm ls and npm prune should have been called
274278
expect(packagerFactoryMock.get('npm').getProdDependencies).toHaveBeenCalledTimes(1),
279+
expect(packagerFactoryMock.get('npm').getPackagerVersion).toHaveBeenCalledTimes(2),
275280
expect(packagerFactoryMock.get('npm').install).toHaveBeenCalledTimes(1),
276281
expect(packagerFactoryMock.get('npm').prune).toHaveBeenCalledTimes(1),
277282
expect(packagerFactoryMock.get('npm').runScripts).toHaveBeenCalledTimes(1)
@@ -341,6 +346,7 @@ describe('packExternalModules', () => {
341346
fsExtraMock.copy.mockImplementation((from, to, cb) => cb());
342347
packagerFactoryMock.get('npm').getProdDependencies.mockReturnValue(BbPromise.resolve({}));
343348
packagerFactoryMock.get('npm').rebaseLockfile.mockImplementation((pathToPackageRoot, lockfile) => lockfile);
349+
packagerFactoryMock.get('npm').getPackagerVersion.mockReturnValue(BbPromise.resolve());
344350
packagerFactoryMock.get('npm').install.mockReturnValue(BbPromise.resolve());
345351
packagerFactoryMock.get('npm').prune.mockReturnValue(BbPromise.resolve());
346352
packagerFactoryMock.get('npm').runScripts.mockReturnValue(BbPromise.resolve());
@@ -371,6 +377,7 @@ describe('packExternalModules', () => {
371377
),
372378
// npm ls and npm prune should have been called
373379
expect(packagerFactoryMock.get('npm').getProdDependencies).toHaveBeenCalledTimes(1),
380+
expect(packagerFactoryMock.get('npm').getPackagerVersion).toHaveBeenCalledTimes(2),
374381
expect(packagerFactoryMock.get('npm').install).toHaveBeenCalledTimes(1),
375382
expect(packagerFactoryMock.get('npm').prune).toHaveBeenCalledTimes(1),
376383
expect(packagerFactoryMock.get('npm').runScripts).toHaveBeenCalledTimes(1)
@@ -412,6 +419,7 @@ describe('packExternalModules', () => {
412419
fsExtraMock.pathExists.mockImplementation((p, cb) => cb(null, false));
413420
fsExtraMock.copy.mockImplementation((from, to, cb) => cb());
414421
packagerFactoryMock.get('npm').getProdDependencies.mockReturnValue(BbPromise.resolve({}));
422+
packagerFactoryMock.get('npm').getPackagerVersion.mockReturnValue(BbPromise.resolve());
415423
packagerFactoryMock.get('npm').install.mockReturnValue(BbPromise.resolve());
416424
packagerFactoryMock.get('npm').prune.mockReturnValue(BbPromise.resolve());
417425
packagerFactoryMock.get('npm').runScripts.mockReturnValue(BbPromise.resolve());
@@ -428,6 +436,7 @@ describe('packExternalModules', () => {
428436
expect(fsExtraMock.copy).toHaveBeenCalledTimes(0),
429437
// npm ls and npm prune should have been called
430438
expect(packagerFactoryMock.get('npm').getProdDependencies).toHaveBeenCalledTimes(1),
439+
expect(packagerFactoryMock.get('npm').getPackagerVersion).toHaveBeenCalledTimes(1),
431440
expect(packagerFactoryMock.get('npm').install).toHaveBeenCalledTimes(1),
432441
expect(packagerFactoryMock.get('npm').prune).toHaveBeenCalledTimes(0),
433442
expect(packagerFactoryMock.get('npm').runScripts).toHaveBeenCalledTimes(0)
@@ -440,6 +449,7 @@ describe('packExternalModules', () => {
440449
fsExtraMock.pathExists.mockImplementation((p, cb) => cb(null, false));
441450
fsExtraMock.copy.mockImplementation((from, to, cb) => cb());
442451
packagerFactoryMock.get('npm').getProdDependencies.mockReturnValue(BbPromise.resolve({}));
452+
packagerFactoryMock.get('npm').getPackagerVersion.mockReturnValue(BbPromise.resolve());
443453
packagerFactoryMock
444454
.get('npm')
445455
.install.mockImplementation(() => BbPromise.reject(new Error('npm install failed')));
@@ -452,6 +462,7 @@ describe('packExternalModules', () => {
452462
BbPromise.all([
453463
// npm ls and npm install should have been called
454464
expect(packagerFactoryMock.get('npm').getProdDependencies).toHaveBeenCalledTimes(1),
465+
expect(packagerFactoryMock.get('npm').getPackagerVersion).toHaveBeenCalledTimes(1),
455466
expect(packagerFactoryMock.get('npm').install).toHaveBeenCalledTimes(1),
456467
expect(packagerFactoryMock.get('npm').prune).toHaveBeenCalledTimes(0),
457468
expect(packagerFactoryMock.get('npm').runScripts).toHaveBeenCalledTimes(0)

tests/packagers/npm.test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,36 @@ describe('npm', () => {
3838
expect(npmModule.mustCopyModules).toBe(true);
3939
});
4040

41+
describe('getPackagerVersion', () => {
42+
it('should use npm version 6.14.17', () => {
43+
const npmVersion = '6.14.17';
44+
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: npmVersion, stderr: '' }));
45+
return expect(npmModule.getPackagerVersion('myPath', 1))
46+
.resolves.toEqual(npmVersion)
47+
.then(() => {
48+
expect(Utils.spawnProcess).toHaveBeenCalledTimes(1);
49+
expect(Utils.spawnProcess).toHaveBeenNthCalledWith(1, expect.stringMatching(/^npm/), ['-v'], {
50+
cwd: 'myPath'
51+
});
52+
return null;
53+
});
54+
});
55+
56+
it('should use npm version 8.19.2', () => {
57+
const npmVersion = '8.19.2';
58+
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: npmVersion, stderr: '' }));
59+
return expect(npmModule.getPackagerVersion('myPath', 1))
60+
.resolves.toEqual(npmVersion)
61+
.then(() => {
62+
expect(Utils.spawnProcess).toHaveBeenCalledTimes(1);
63+
expect(Utils.spawnProcess).toHaveBeenNthCalledWith(1, expect.stringMatching(/^npm/), ['-v'], {
64+
cwd: 'myPath'
65+
});
66+
return null;
67+
});
68+
});
69+
});
70+
4171
describe('install', () => {
4272
it('should use npm install', () => {
4373
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: 'installed successfully', stderr: '' }));

tests/packagers/yarn.test.js

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,48 @@ describe('yarn', () => {
2828
expect(yarnModule.mustCopyModules).toBe(false);
2929
});
3030

31+
describe('isBerryVersion', () => {
32+
it('Yarn version 1.22.19', () => {
33+
const yarnVersion = '1.22.19';
34+
expect(yarnModule.isBerryVersion(yarnVersion)).toBe(false);
35+
});
36+
37+
it('Yarn version 3.2.3', () => {
38+
const yarnVersion = '3.2.3';
39+
expect(yarnModule.isBerryVersion(yarnVersion)).toBe(true);
40+
});
41+
});
42+
43+
describe('getPackagerVersion', () => {
44+
it('should use yarn version 1.22.19', () => {
45+
const yarnVersion = '1.22.19';
46+
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: yarnVersion, stderr: '' }));
47+
return expect(yarnModule.getPackagerVersion('myPath', 1))
48+
.resolves.toEqual(yarnVersion)
49+
.then(() => {
50+
expect(Utils.spawnProcess).toHaveBeenCalledTimes(1);
51+
expect(Utils.spawnProcess).toHaveBeenNthCalledWith(1, expect.stringMatching(/^yarn/), ['-v'], {
52+
cwd: 'myPath'
53+
});
54+
return null;
55+
});
56+
});
57+
58+
it('should use yarn version 3.2.3', () => {
59+
const yarnVersion = '3.2.3';
60+
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: yarnVersion, stderr: '' }));
61+
return expect(yarnModule.getPackagerVersion('myPath', 1))
62+
.resolves.toEqual(yarnVersion)
63+
.then(() => {
64+
expect(Utils.spawnProcess).toHaveBeenCalledTimes(1);
65+
expect(Utils.spawnProcess).toHaveBeenNthCalledWith(1, expect.stringMatching(/^yarn/), ['-v'], {
66+
cwd: 'myPath'
67+
});
68+
return null;
69+
});
70+
});
71+
});
72+
3173
describe('getProdDependencies', () => {
3274
it('should use yarn list', () => {
3375
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: '{}', stderr: '' }));
@@ -198,7 +240,7 @@ describe('yarn', () => {
198240
describe('install', () => {
199241
it('should use yarn install', () => {
200242
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: 'installed successfully', stderr: '' }));
201-
return expect(yarnModule.install('myPath', {}))
243+
return expect(yarnModule.install('myPath', {}, '1.22.19'))
202244
.resolves.toBeUndefined()
203245
.then(() => {
204246
expect(Utils.spawnProcess).toHaveBeenCalledTimes(1);
@@ -215,7 +257,7 @@ describe('yarn', () => {
215257

216258
it('should use noNonInteractive option', () => {
217259
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: 'installed successfully', stderr: '' }));
218-
return expect(yarnModule.install('myPath', { noNonInteractive: true }))
260+
return expect(yarnModule.install('myPath', { noNonInteractive: true }, '1.22.19'))
219261
.resolves.toBeUndefined()
220262
.then(() => {
221263
expect(Utils.spawnProcess).toHaveBeenCalledTimes(1);
@@ -232,7 +274,7 @@ describe('yarn', () => {
232274

233275
it('should use ignoreScripts option', () => {
234276
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: 'installed successfully', stderr: '' }));
235-
return expect(yarnModule.install('myPath', { ignoreScripts: true }))
277+
return expect(yarnModule.install('myPath', { ignoreScripts: true }, '1.22.19'))
236278
.resolves.toBeUndefined()
237279
.then(() => {
238280
expect(Utils.spawnProcess).toHaveBeenCalledTimes(1);
@@ -249,7 +291,7 @@ describe('yarn', () => {
249291

250292
it('should use noFrozenLockfile option', () => {
251293
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: 'installed successfully', stderr: '' }));
252-
return expect(yarnModule.install('myPath', { noFrozenLockfile: true }))
294+
return expect(yarnModule.install('myPath', { noFrozenLockfile: true }, '1.22.19'))
253295
.resolves.toBeUndefined()
254296
.then(() => {
255297
expect(Utils.spawnProcess).toHaveBeenCalledTimes(1);
@@ -266,7 +308,7 @@ describe('yarn', () => {
266308

267309
it('should use networkConcurrency option', () => {
268310
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: 'installed successfully', stderr: '' }));
269-
return expect(yarnModule.install('myPath', { networkConcurrency: 1 }))
311+
return expect(yarnModule.install('myPath', { networkConcurrency: 1 }, '1.22.19'))
270312
.resolves.toBeUndefined()
271313
.then(() => {
272314
expect(Utils.spawnProcess).toHaveBeenCalledTimes(1);
@@ -291,7 +333,7 @@ describe('yarn', () => {
291333
describe('prune', () => {
292334
it('should call install', () => {
293335
Utils.spawnProcess.mockReturnValue(BbPromise.resolve({ stdout: 'success', stderr: '' }));
294-
return expect(yarnModule.prune('myPath', {}))
336+
return expect(yarnModule.prune('myPath', {}, '1.22.19'))
295337
.resolves.toBeUndefined()
296338
.then(() => {
297339
expect(Utils.spawnProcess).toHaveBeenCalledTimes(1);

0 commit comments

Comments
 (0)