Skip to content

Commit 572c01d

Browse files
authored
fixes lockfile inconsistency between runs (#109)
* fixes lockfile inconsistency between runs * nits and comments for Shayne
1 parent 482eb4f commit 572c01d

File tree

3 files changed

+71
-17
lines changed

3 files changed

+71
-17
lines changed

src/lockfile/index.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,19 @@ export default class Lockfile {
114114
[packagePattern: string]: Manifest
115115
}): Object {
116116
let lockfile = {};
117-
let seen: Map<Manifest, string> = new Map;
118-
119-
for (let pattern in patterns) {
117+
let seen: Map<string, string> = new Map;
118+
// order by name so that lockfile manifest is assigned to the first dependency with this manifest
119+
// the others that have the same remote.resovled will just refer to the first
120+
// oredring allows for consistency in lockfile when it is serialized
121+
let sortedPatternsKeys: string[] = Object.keys(patterns).sort((a, b) => {
122+
return a.toLowerCase().localeCompare(b.toLowerCase());
123+
});
124+
125+
for (let pattern of sortedPatternsKeys) {
120126
let pkg = patterns[pattern];
127+
let remote = pkg.remote;
121128

122-
let seenPattern = seen.get(pkg);
129+
let seenPattern = remote && remote.resolved && seen.get(remote.resolved);
123130
if (seenPattern) {
124131
// no point in duplicating it
125132
lockfile[pattern] = seenPattern;
@@ -129,7 +136,6 @@ export default class Lockfile {
129136
let ref = pkg.reference;
130137
invariant(ref, "Package is missing a reference");
131138

132-
let remote = pkg.remote;
133139
invariant(remote, "Package is missing a remote");
134140

135141
lockfile[pattern] = {
@@ -143,7 +149,9 @@ export default class Lockfile {
143149
permissions: _.isEmpty(ref.permissions) ? undefined : ref.permissions
144150
};
145151

146-
seen.set(pkg, pattern);
152+
if (remote.resolved) {
153+
seen.set(remote.resolved, pattern);
154+
}
147155
}
148156

149157
return lockfile;

src/lockfile/stringify.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
* @flow
1010
*/
1111

12+
let _ = require("lodash");
13+
1214
function shouldWrapKey(str: string): boolean {
1315
return str.indexOf("true") === 0 || str.indexOf("false") === 0 ||
1416
/[:\s\n\\"]/g.test(str) || /^[0-9]/g.test(str) || !/^[a-zA-Z]/g.test(str);
@@ -42,13 +44,17 @@ export default function stringify(obj: any, indent: string = ""): string {
4244

4345
let lines = [];
4446

45-
let keys = Object.keys(obj).sort(function (a, b) {
46-
// sort alphabetically
47-
return a.toLowerCase().localeCompare(b.toLowerCase());
48-
}).sort(function (a, b) {
49-
// prioritise certain fields
50-
return +(getKeyPriority(a) > getKeyPriority(b));
51-
});
47+
// Sorting order needs to be consistent between runs, we run native sort by name because there are no
48+
// problems with it being unstable because there are no to keys the same
49+
// However priorities can be duplicated and native sort can shuffle things from run to run
50+
let keys = Object.keys(obj)
51+
.sort(function (a, b) {
52+
// sort alphabetically
53+
return a.toLowerCase().localeCompare(b.toLowerCase());
54+
});
55+
56+
// stable sort, V8 Array.prototype.sort is not stable and we don't want to shuffle things randomly
57+
keys = _.sortBy(keys, getKeyPriority);
5258

5359
for (let key of keys) {
5460
let val = obj[key];

test/lockfile.js

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ test("Lockfile.getLockfile", (t) => {
116116
permissions: {}
117117
},
118118
remote: {
119-
resolved: "http://example.com",
119+
resolved: "http://example.com/foobar",
120120
registry: "npm"
121121
}
122122
},
@@ -137,7 +137,7 @@ test("Lockfile.getLockfile", (t) => {
137137
}
138138
},
139139
remote: {
140-
resolved: "http://example.com",
140+
resolved: "http://example.com/barfoo",
141141
registry: "bower"
142142
}
143143
}
@@ -152,7 +152,7 @@ test("Lockfile.getLockfile", (t) => {
152152
name: "foobar",
153153
version: "0.0.0",
154154
uid: undefined,
155-
resolved: "http://example.com",
155+
resolved: "http://example.com/foobar",
156156
registry: undefined,
157157
dependencies: undefined,
158158
optionalDependencies: undefined,
@@ -163,7 +163,7 @@ test("Lockfile.getLockfile", (t) => {
163163
name: "barfoo",
164164
version: "0.0.1",
165165
uid: "0.1.0",
166-
resolved: "http://example.com",
166+
resolved: "http://example.com/barfoo",
167167
registry: "bower",
168168
dependencies: { yes: "no" },
169169
optionalDependencies: { no: "yes" },
@@ -175,3 +175,43 @@ test("Lockfile.getLockfile", (t) => {
175175

176176
t.deepEqual(actual, expected);
177177
});
178+
179+
test("Lockfile.getLockfile (sorting)", (t) => {
180+
let patterns = {
181+
foobar2: {
182+
name: "foobar",
183+
version: "0.0.0",
184+
uid: "0.0.0",
185+
dependencies: {},
186+
optionalDependencies: {},
187+
reference: {
188+
permissions: {}
189+
},
190+
remote: {
191+
resolved: "http://example.com/foobar",
192+
registry: "npm"
193+
}
194+
},
195+
};
196+
197+
patterns.foobar1 = patterns.foobar2;
198+
199+
let actual = new Lockfile().getLockfile(patterns);
200+
201+
let expected = {
202+
foobar1: {
203+
name: "foobar",
204+
version: "0.0.0",
205+
uid: undefined,
206+
resolved: "http://example.com/foobar",
207+
registry: undefined,
208+
dependencies: undefined,
209+
optionalDependencies: undefined,
210+
permissions: undefined
211+
},
212+
213+
foobar2: "foobar1"
214+
};
215+
216+
t.deepEqual(actual, expected);
217+
});

0 commit comments

Comments
 (0)