Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b33be2f

Browse files
fix(packager): introduce unit tests for packagers, fix bugs found on testing
1 parent 23854b7 commit b33be2f

File tree

8 files changed

+600
-61
lines changed

8 files changed

+600
-61
lines changed

‎examples/complete/src/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import validateIsin from 'isin-validator';
22

3-
export function handler(event: any) {
3+
export function handler(event: string) {
44
const isInvalid = validateIsin(event);
55

66
return {

‎src/packagers/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function get(cwd: string, packagerId?: keyof typeof registeredPackagers):
3535
const pkger = findPackager(cwd, packagerId);
3636

3737
if (!(pkger in registeredPackagers)) {
38-
const message = `Could not find packager '${packagerId}'`;
38+
const message = `Could not find packager '${pkger}'`;
3939
console.log(`ERROR: ${message}`);
4040
throw new Error(message);
4141
}

‎src/packagers/npm.ts‎

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { any, isEmpty,replace } from 'ramda';
1+
import { any, isEmpty } from 'ramda';
22

33
import { JSONObject } from '../types';
44
import { SpawnError, spawnProcess } from '../utils';
@@ -32,7 +32,7 @@ export class NPM implements Packager {
3232
}
3333
}
3434

35-
getProdDependencies(cwd: string, depth: number) {
35+
getProdDependencies(cwd: string, depth?: number) {
3636
// Get first level dependency graph
3737
const command = /^win/.test(process.platform) ? 'npm.cmd' : 'npm';
3838
const args = [
@@ -48,32 +48,34 @@ export class NPM implements Packager {
4848
{ npmError: 'peer dep missing', log: true }
4949
];
5050

51+
let processOutput;
5152
try {
52-
const processOutput = spawnProcess(command, args, { cwd });
53-
const depJson = processOutput.stdout;
54-
55-
return JSON.parse(depJson);
53+
processOutput = spawnProcess(command, args, { cwd });
5654
} catch (err) {
57-
if (err instanceof SpawnError) {
58-
// Only exit with an error if we have critical npm errors for 2nd level inside
59-
const errors = err.stderr?.split('\n') ?? [];
60-
const failed = errors.reduce((f, error) => {
61-
if (f) {
62-
return true;
63-
}
64-
return (
65-
!isEmpty(error) &&
66-
!any(ignoredError => error.startsWith(`npm ERR! ${ignoredError.npmError}`), ignoredNpmErrors)
67-
);
68-
}, false);
69-
70-
if (!failed && !isEmpty(err.stdout)) {
71-
return { stdout: err.stdout };
55+
if (!(err instanceof SpawnError)) {
56+
throw err;
57+
}
58+
59+
// Only exit with an error if we have critical npm errors for 2nd level inside
60+
const errors = err.stderr?.split('\n') ?? [];
61+
const failed = errors.reduce((f, error) => {
62+
if (f) {
63+
return true;
7264
}
65+
return (
66+
!isEmpty(error) &&
67+
!any(ignoredError => error.startsWith(`npm ERR! ${ignoredError.npmError}`), ignoredNpmErrors)
68+
);
69+
}, false);
70+
71+
if (failed || isEmpty(err.stdout)) {
72+
throw err;
7373
}
7474

75-
throwerr;
75+
processOutput={stdout: err.stdout};
7676
}
77+
78+
return JSON.parse(processOutput.stdout);
7779
}
7880

7981
/**
@@ -90,7 +92,7 @@ export class NPM implements Packager {
9092

9193
if (lockfile.dependencies) {
9294
for (const lockedDependency in lockfile.dependencies) {
93-
this.rebaseLockfile(pathToPackageRoot, lockedDependency);
95+
this.rebaseLockfile(pathToPackageRoot, lockfile.dependencies[lockedDependency]);
9496
}
9597
}
9698

@@ -114,17 +116,13 @@ export class NPM implements Packager {
114116
runScripts(cwd, scriptNames) {
115117
const command = /^win/.test(process.platform) ? 'npm.cmd' : 'npm';
116118

117-
scriptNames.forEach(scriptName => {
118-
const args = ['run', scriptName];
119-
120-
spawnProcess(command, args, { cwd });
121-
});
119+
scriptNames.forEach(scriptName => spawnProcess(command, ['run', scriptName], { cwd }));
122120
}
123121

124122
private rebaseFileReferences(pathToPackageRoot: string, moduleVersion: string) {
125123
if (/^file:[^/]{2}/.test(moduleVersion)) {
126-
const filePath = replace(/^file:/, '',moduleVersion);
127-
return replace(/\\/g,'/',`file:${pathToPackageRoot}/${filePath}`);
124+
const filePath = moduleVersion.replace(/^file:/, '');
125+
return `file:${pathToPackageRoot}/${filePath}`.replace(/\\/g,'/');
128126
}
129127

130128
return moduleVersion;

‎src/packagers/yarn.ts‎

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { any, head, isEmpty, join, pathOr, reduce,replace,split,startsWith, tail } from 'ramda';
1+
import { any, head, isEmpty, join, pathOr, split, tail } from 'ramda';
22

33
import { JSONObject } from '../types';
4-
import { SpawnError, spawnProcess } from '../utils';
4+
import { safeJsonParse,SpawnError, spawnProcess,splitLines } from '../utils';
55
import { Packager } from './packager';
66

77
/**
@@ -36,7 +36,7 @@ export class Yarn implements Packager {
3636
}
3737
}
3838

39-
getProdDependencies(cwd, depth) {
39+
getProdDependencies(cwd: string, depth?: number) {
4040
const command = /^win/.test(process.platform) ? 'yarn.cmd' : 'yarn';
4141
const args = ['list', `--depth=${depth || 1}`, '--json', '--production'];
4242

@@ -47,33 +47,36 @@ export class Yarn implements Packager {
4747
try {
4848
processOutput = spawnProcess(command, args, { cwd });
4949
} catch (err) {
50-
if (err instanceof SpawnError) {
51-
// Only exit with an error if we have critical npm errors for 2nd level inside
52-
const errors = err.stderr?.split('\n') ?? [];
53-
const failed = errors.reduce((f, error) => {
54-
if (f) {
55-
return true;
56-
}
57-
return (
58-
!isEmpty(error) &&
59-
!any(ignoredError => error.startsWith(`npm ERR! ${ignoredError.npmError}`), ignoredYarnErrors)
60-
);
61-
}, false);
62-
63-
if (!failed && !isEmpty(err.stdout)) {
64-
return { stdout: err.stdout };
50+
if (!(err instanceof SpawnError)) {
51+
throw err;
52+
}
53+
54+
// Only exit with an error if we have critical npm errors for 2nd level inside
55+
const errors = err.stderr?.split('\n') ?? [];
56+
const failed = errors.reduce((f, error) => {
57+
if (f) {
58+
return true;
6559
}
60+
return (
61+
!isEmpty(error) &&
62+
!any(ignoredError => error.startsWith(`npm ERR! ${ignoredError.npmError}`), ignoredYarnErrors)
63+
);
64+
}, false);
65+
66+
if (failed || isEmpty(err.stdout)) {
67+
throw err;
6668
}
6769

68-
throwerr;
70+
processOutput={stdout: err.stdout};
6971
}
7072

71-
const depJson = processOutput.stdout;
72-
const parsedTree = JSON.parse(depJson);
73-
const convertTrees = reduce((__, tree: JSONObject) => {
73+
const lines = splitLines(processOutput.stdout);
74+
const parsedLines = lines.map(safeJsonParse);
75+
const parsedTree = parsedLines.find(line => line && line.type === 'tree');
76+
const convertTrees = ts => ts.reduce((__, tree: JSONObject) => {
7477
const splitModule = split('@', tree.name);
7578
// If we have a scoped module we have to re-add the @
76-
if (startsWith('@',tree.name)) {
79+
if (tree.name.startsWith('@')) {
7780
splitModule.splice(0, 1);
7881
splitModule[0] = '@' + splitModule[0];
7982
}
@@ -101,28 +104,34 @@ export class Yarn implements Packager {
101104
while ((match = fileVersionMatcher.exec(lockfile)) !== null) {
102105
replacements.push({
103106
oldRef: match[1],
104-
newRef: replace(/\\/g,'/',`${pathToPackageRoot}/${match[1]}`)
107+
newRef: `${pathToPackageRoot}/${match[1]}`.replace(/\\/g,'/')
105108
});
106109
}
107110

108111
// Replace all lines in lockfile
109-
return reduce((__, replacement) => replace(__,replacement.oldRef, replacement.newRef), lockfile,replacements);
112+
return replacements.reduce((__, replacement) => __.replace(replacement.oldRef, replacement.newRef), lockfile);
110113
}
111114

112-
install(cwd) {
115+
install(cwd: string,packagerOptions?) {
113116
const command = /^win/.test(process.platform) ? 'yarn.cmd' : 'yarn';
114-
const args = ['install', '--frozen-lockfile', '--non-interactive'];
117+
const args = [ 'install', '--frozen-lockfile', '--non-interactive' ];
118+
119+
// Convert supported packagerOptions
120+
if (packagerOptions.ignoreScripts) {
121+
args.push('--ignore-scripts');
122+
}
115123

116124
spawnProcess(command, args, { cwd });
117125
}
118126

119127
// "Yarn install" prunes automatically
120-
prune(cwd) {
121-
return this.install(cwd);
128+
prune(cwd: string,packagerOptions?) {
129+
return this.install(cwd,packagerOptions);
122130
}
123131

124132
runScripts(cwd, scriptNames: string[]) {
125133
const command = /^win/.test(process.platform) ? 'yarn.cmd' : 'yarn';
134+
126135
scriptNames.forEach(scriptName => spawnProcess(command, ['run', scriptName], { cwd }));
127136
}
128137
}

‎src/utils.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ export function spawnProcess(command: string, args: string[], options: childProc
3333
return { stdout, stderr };
3434
}
3535

36+
export function safeJsonParse(str: string) {
37+
try {
38+
return JSON.parse(str);
39+
} catch (e) {
40+
return null;
41+
}
42+
}
43+
44+
export function splitLines(str: string) {
45+
return str.split(/\r?\n/);
46+
}
47+
3648
/**
3749
* Extracts the file name from handler string.
3850
*/

‎tests/packagers/index.test.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Unit tests for packagers/index
3+
*/
4+
5+
import { get } from '../../src/packagers';
6+
import { NPM } from '../../src/packagers/npm';
7+
import * as Utils from '../../src/utils';
8+
9+
const getCurrentPackager = jest.spyOn(Utils, 'getCurrentPackager');
10+
11+
describe('packagers factory', () => {
12+
it('should throw on unknown packagers', () => {
13+
getCurrentPackager.mockReset().mockReturnValue('unknown' as never);
14+
expect(() => get('.')).toThrowError(/Couldnotfindpackager'unknown'/);
15+
});
16+
17+
it('should return npm packager', () => {
18+
const npm = get('.', 'npm');
19+
expect(npm).toBeInstanceOf(NPM);
20+
});
21+
});

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /