Skip to content

Commit c0c12d2

Browse files
franciscocpgHyperBrain
authored andcommitted
Fix npm file references package lock (#304)
* Rebase package-lock * Fix rebasePackageLock call * Adding tests for package-lock file rewrite * Fix comment
1 parent 697be1e commit c0c12d2

File tree

2 files changed

+88
-20
lines changed

2 files changed

+88
-20
lines changed

lib/packExternalModules.js

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,27 @@ const childProcess = require('child_process');
77
const fse = require('fs-extra');
88
const isBuiltinModule = require('is-builtin-module');
99

10+
function rebaseFileReferences(pathToPackageRoot, moduleVersion) {
11+
if (/^file:[^/]{2}/.test(moduleVersion)) {
12+
const filePath = _.replace(moduleVersion, /^file:/, '');
13+
return _.replace(`file:${pathToPackageRoot}/${filePath}`, /\\/g, '/');
14+
}
15+
16+
return moduleVersion;
17+
}
18+
19+
function rebasePackageLock(pathToPackageRoot, module) {
20+
if (module.version) {
21+
module.version = rebaseFileReferences(pathToPackageRoot, module.version);
22+
}
23+
24+
if (module.dependencies) {
25+
_.forIn(module.dependencies, moduleDependency => {
26+
rebasePackageLock(pathToPackageRoot, moduleDependency);
27+
});
28+
}
29+
}
30+
1031
/**
1132
* Add the given modules to a package json's dependencies.
1233
*/
@@ -20,10 +41,7 @@ function addModulesToPackageJson(externalModules, packageJson, pathToPackageRoot
2041
}
2142
let moduleVersion = _.join(_.tail(splitModule), '@');
2243
// We have to rebase file references to the target package.json
23-
if (/^file:[^/]{2}/.test(moduleVersion)) {
24-
const filePath = _.replace(moduleVersion, /^file:/, '');
25-
moduleVersion = _.replace(`file:${pathToPackageRoot}/${filePath}`, /\\/g, '/');
26-
}
44+
moduleVersion = rebaseFileReferences(pathToPackageRoot, moduleVersion);
2745
packageJson.dependencies = packageJson.dependencies || {};
2846
packageJson.dependencies[_.first(splitModule)] = moduleVersion;
2947
});
@@ -262,8 +280,21 @@ module.exports = {
262280
.then(exists => {
263281
if (exists) {
264282
this.serverless.cli.log('Package lock found - Using locked versions');
265-
return BbPromise.fromCallback(cb => fse.copy(packageLockPath, path.join(compositeModulePath, 'package-lock.json'), cb))
266-
.catch(err => this.serverless.cli.log(`Warning: Could not copy lock file: ${err.message}`));
283+
try {
284+
const packageLockJson = this.serverless.utils.readFileSync(packageLockPath);
285+
/**
286+
* We should not be modifying 'package-lock.json'
287+
* because this file should be treat as internal to npm.
288+
*
289+
* Rebase package-lock is a temporary workaround and must be
290+
* removed as soon as https://github.com/npm/npm/issues/19183 gets fixed.
291+
*/
292+
rebasePackageLock(relPath, packageLockJson);
293+
294+
this.serverless.utils.writeFileSync(path.join(compositeModulePath, 'package-lock.json'), JSON.stringify(packageLockJson, null, 2));
295+
} catch(err) {
296+
this.serverless.cli.log(`Warning: Could not read lock file: ${err.message}`);
297+
}
267298
}
268299
return BbPromise.resolve();
269300
})

tests/packExternalModules.test.js

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ describe('packExternalModules', () => {
3030
let fsExtraMock;
3131
// Serverless stubs
3232
let writeFileSyncStub;
33+
let readFileSyncStub;
3334

3435
before(() => {
3536
sandbox = sinon.sandbox.create();
@@ -60,6 +61,7 @@ describe('packExternalModules', () => {
6061
_.set(serverless, 'service.service', 'test-service');
6162

6263
writeFileSyncStub = sandbox.stub(serverless.utils, 'writeFileSync');
64+
readFileSyncStub = sandbox.stub(serverless.utils, 'readFileSync');
6365
_.set(serverless, 'service.custom.webpackIncludeModules', true);
6466

6567
module = _.assign({
@@ -73,6 +75,7 @@ describe('packExternalModules', () => {
7375
afterEach(() => {
7476
// Reset all counters and restore all stubbed functions
7577
writeFileSyncStub.reset();
78+
readFileSyncStub.reset();
7679
childProcessMock.exec.reset();
7780
fsExtraMock.pathExists.reset();
7881
fsExtraMock.copy.reset();
@@ -258,6 +261,7 @@ describe('packExternalModules', () => {
258261
});
259262

260263
it('should rebase file references', () => {
264+
const expectedLocalModule = 'file:../../locals/../../mymodule';
261265
const expectedCompositePackageJSON = {
262266
name: 'test-service',
263267
version: '1.0.0',
@@ -274,14 +278,52 @@ describe('packExternalModules', () => {
274278
dependencies: {
275279
'@scoped/vendor': '1.0.0',
276280
uuid: '^5.4.1',
277-
localmodule: 'file:../../locals/../../mymodule',
281+
localmodule: expectedLocalModule,
278282
bluebird: '^3.4.0'
279283
}
280284
};
281285

286+
const fakePackageLockJSON = {
287+
name: 'test-service',
288+
version: '1.0.0',
289+
description: 'Packaged externals for test-service',
290+
private: true,
291+
dependencies: {
292+
'@scoped/vendor': '1.0.0',
293+
uuid: {
294+
version: '^5.4.1'
295+
},
296+
bluebird: {
297+
version: '^3.4.0'
298+
},
299+
localmodule: {
300+
version: 'file:../../mymodule'
301+
}
302+
}
303+
};
304+
const expectedPackageLockJSON = {
305+
name: 'test-service',
306+
version: '1.0.0',
307+
description: 'Packaged externals for test-service',
308+
private: true,
309+
dependencies: {
310+
'@scoped/vendor': '1.0.0',
311+
uuid: {
312+
version: '^5.4.1'
313+
},
314+
bluebird: {
315+
version: '^3.4.0'
316+
},
317+
localmodule: {
318+
version: expectedLocalModule
319+
}
320+
}
321+
};
322+
282323
_.set(serverless, 'service.custom.webpackIncludeModules.packagePath', path.join('locals', 'package.json'));
283324
module.webpackOutputPath = 'outputPath';
284-
fsExtraMock.pathExists.yields(null, false);
325+
readFileSyncStub.returns(fakePackageLockJSON);
326+
fsExtraMock.pathExists.yields(null, true);
285327
fsExtraMock.copy.yields();
286328
childProcessMock.exec.onFirstCall().yields(null, '{}', '');
287329
childProcessMock.exec.onSecondCall().yields(null, '', '');
@@ -294,9 +336,10 @@ describe('packExternalModules', () => {
294336
return expect(module.packExternalModules()).to.be.fulfilled
295337
.then(() => BbPromise.all([
296338
// The module package JSON and the composite one should have been stored
297-
expect(writeFileSyncStub).to.have.been.calledTwice,
339+
expect(writeFileSyncStub).to.have.been.calledThrice,
298340
expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)),
299-
expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)),
341+
expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageLockJSON, null, 2)),
342+
expect(writeFileSyncStub.thirdCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)),
300343
// The modules should have been copied
301344
expect(fsExtraMock.copy).to.have.been.calledOnce,
302345
// npm ls and npm prune should have been called
@@ -702,10 +745,7 @@ describe('packExternalModules', () => {
702745
expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)),
703746
expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)),
704747
// The modules should have been copied
705-
expect(fsExtraMock.copy).to.have.been.calledTwice,
706-
expect(fsExtraMock.copy.firstCall).to.have.been.calledWith(
707-
sinon.match(/package-lock.json$/)
708-
),
748+
expect(fsExtraMock.copy).to.have.been.calledOnce,
709749
// npm ls and npm prune should have been called
710750
expect(childProcessMock.exec).to.have.been.calledThrice,
711751
expect(childProcessMock.exec.firstCall).to.have.been.calledWith(
@@ -741,9 +781,9 @@ describe('packExternalModules', () => {
741781
};
742782

743783
module.webpackOutputPath = 'outputPath';
784+
readFileSyncStub.throws(new Error('Failed to read package-lock.json'));
744785
fsExtraMock.pathExists.yields(null, true);
745-
fsExtraMock.copy.onFirstCall().yields(new Error('Failed to read package-lock.json'));
746-
fsExtraMock.copy.onSecondCall().yields();
786+
fsExtraMock.copy.onFirstCall().yields();
747787
childProcessMock.exec.onFirstCall().yields(null, '{}', '');
748788
childProcessMock.exec.onSecondCall().yields(null, '', '');
749789
childProcessMock.exec.onThirdCall().yields();
@@ -755,10 +795,7 @@ describe('packExternalModules', () => {
755795
expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)),
756796
expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)),
757797
// The modules should have been copied
758-
expect(fsExtraMock.copy).to.have.been.calledTwice,
759-
expect(fsExtraMock.copy.firstCall).to.have.been.calledWith(
760-
sinon.match(/package-lock.json$/)
761-
),
798+
expect(fsExtraMock.copy).to.have.been.calledOnce,
762799
// npm ls and npm prune should have been called
763800
expect(childProcessMock.exec).to.have.been.calledThrice,
764801
expect(childProcessMock.exec.firstCall).to.have.been.calledWith(

0 commit comments

Comments
 (0)