Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: ignore npm v7+ err ELSPROBLEMS #289

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 15 additions & 18 deletions src/packagers/npm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { any, isEmpty, reduce, replace, split, startsWith } from 'ramda';
import { any, isEmpty, replace, split, startsWith, takeWhile } from 'ramda';
import * as path from 'path';

import { DependenciesResult, DependencyMap, JSONObject } from '../types';
Expand Down Expand Up @@ -125,6 +125,7 @@ export class NPM implements Packager {
{ npmError: 'extraneous', log: false },
{ npmError: 'missing', log: false },
{ npmError: 'peer dep missing', log: true },
{ npmError: 'code ELSPROBLEMS', log: false },
];

let parsedDeps: NpmV6Deps | NpmV7Deps;
Expand All @@ -134,25 +135,21 @@ export class NPM implements Packager {
} catch (err) {
if (err instanceof SpawnError) {
// Only exit with an error if we have critical npm errors for 2nd level inside
const errors = split('\n', err.stderr);
const failed = reduce(
(f, error) => {
if (f) {
return true;
}
return (
!isEmpty(error) &&
!any(
(ignoredError) => startsWith(`npm ERR! ${ignoredError.npmError}`, error),
ignoredNpmErrors
)
);
},
false,
errors
// Split the stderr by \n character to get the npm ERR! plaintext lines, ignore additonal JSON blob (emitted by npm >=7)
// see https://github.com/serverless-heaven/serverless-webpack/pull/782 and https://github.com/floydspace/serverless-esbuild/issues/288
const lines = split('\n', err.stderr);
const npmErrors = takeWhile((line) => line !== '{', lines);

const hasThrowableErrors = npmErrors.every(
(error) =>
!isEmpty(error) &&
!any(
(ignoredError) => startsWith(`npm ERR! ${ignoredError.npmError}`, error),
ignoredNpmErrors
)
);

if (!failed && !isEmpty(err.stdout)) {
if (!hasThrowableErrors && !isEmpty(err.stdout)) {
return { stdout: err.stdout };
}
}
Expand Down
119 changes: 119 additions & 0 deletions src/tests/packagers/npm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1003,4 +1003,123 @@ describe('NPM Packager', () => {
expect(v6dependencies).toStrictEqual(expectedResult);
expect(v7dependencies).toStrictEqual(expectedResult);
});

test.each([
{ a: 1, b: 1, expected: 2 },
{ a: 1, b: 2, expected: 3 },
{ a: 2, b: 1, expected: 3 },
])('.add($a, $b)', ({ a, b, expected }) => {
expect(a + b).toBe(expected);
});

it.each([
'npm ERR! code ELSPROBLEMS',
'npm ERR! extraneous: [email protected] ./bar/node_modules/foo',
'npm ERR! missing: [email protected], required by [email protected]',
'npm ERR! peer dep missing: [email protected]',
])('should ignore npm error "%s"', async (ignoredNpmError) => {
const v7depsList: NpmV7Deps = {
samchungy marked this conversation as resolved.
Show resolved Hide resolved
version: '1.0.0',
name: 'serverless-example',
description: 'Packaged externals for serverless-example',
private: true,
scripts: {},
_id: '[email protected]',
extraneous: false,
path: '/workdir/.esbuild/.build',
_dependencies: {
'samchungy-a': '2.0.0',
'samchungy-b': '2.0.0',
},
devDependencies: {},
peerDependencies: {},
dependencies: {
'samchungy-a': {
version: '2.0.0',
resolved: 'https://registry.npmjs.org/samchungy-a/-/samchungy-a-2.0.0.tgz',
name: 'samchungy-a',
integrity:
'sha512-gUv/cvd9AFYvvGep0e9m1wSAf3dfnb71eri5TjtgC6N7qvJALXFaFVOkLNBHEYGEm2ZJdosXvGqr3ISZ7Yh46Q==',
_id: '[email protected]',
extraneous: false,
path: '/workdir/.esbuild/.build/node_modules/samchungy-a',
_dependencies: {
'samchungy-dep-a': '1.0.0',
},
devDependencies: {},
peerDependencies: {},
dependencies: {
'samchungy-dep-a': {
version: '1.0.0',
resolved: 'https://registry.npmjs.org/samchungy-dep-a/-/samchungy-dep-a-1.0.0.tgz',
name: 'samchungy-dep-a',
integrity:
'sha512-NVac5aAU+p7bsIrUTQO438vAO8MHyNILbeckhzxhadIUqGx3L9kEZ5HTqZ+XqDIRARmOU6UmFtus6Bc7q5+mWA==',
_id: '[email protected]',
extraneous: false,
path: '/workdir/.esbuild/.build/node_modules/samchungy-dep-a',
_dependencies: {},
devDependencies: {},
peerDependencies: {},
},
},
},
'samchungy-b': {
version: '2.0.0',
resolved: 'https://registry.npmjs.org/samchungy-b/-/samchungy-b-2.0.0.tgz',
name: 'samchungy-b',
integrity:
'sha512-i42OG9FC2Py3RfbI8bBFZi3VoN7+MxM0OUvFcWrsIgqvZMUDVI4hNKHqpE6GTt07gDDqQnxlMNehbrsQLtHRVA==',
_id: '[email protected]',
extraneous: false,
path: '/workdir/.esbuild/.build/node_modules/samchungy-b',
_dependencies: {
'samchungy-dep-a': '2.0.0',
},
devDependencies: {},
peerDependencies: {},
dependencies: {
'samchungy-dep-a': {
version: '2.0.0',
resolved: 'https://registry.npmjs.org/samchungy-dep-a/-/samchungy-dep-a-2.0.0.tgz',
name: 'samchungy-dep-a',
integrity:
'sha512-Yp30ASjwmyLWCGhlLTqWZa8MlBeBiaaHsmxXMwwQxK/o044vhCsPeugHqhtsZq7Xiq68/TcBux/LKId6eyPNjA==',
_id: '[email protected]',
extraneous: false,
path: '/workdir/.esbuild/.build/node_modules/samchungy-b/node_modules/samchungy-dep-a',
_dependencies: {},
devDependencies: {},
peerDependencies: {},
},
},
},
},
};

const npm7stderr: string = ''.concat(
ignoredNpmError,
'\n',
JSON.stringify(
{
error: {
code: 'TEST_ERROR_CODE',
summary: 'test summary',
detail: 'test detail',
},
},
null,
1
)
);

spawnSpy
.mockResolvedValueOnce({ stderr: '', stdout: '7.0.0' })
.mockRejectedValueOnce(
new utils.SpawnError('a spawn error', JSON.stringify(v7depsList), npm7stderr)
samchungy marked this conversation as resolved.
Show resolved Hide resolved
);

const result = await npm.getProdDependencies(path);
expect(result).toStrictEqual({ stdout: JSON.stringify(v7depsList) });
});
});