Skip to content

Commit 1f0ece8

Browse files
author
Sebastian McKenzie
committed
collapse duplicate objects in lockfile to single line
1 parent 1246f74 commit 1f0ece8

File tree

4 files changed

+101
-38
lines changed

4 files changed

+101
-38
lines changed

src/lockfile/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ export default class Lockfile {
114114
[packagePattern: string]: Manifest
115115
}): Object {
116116
let lockfile = {};
117-
let seen: Map<string, string> = new Map;
117+
let seen: Map<string, Object> = new Map;
118+
118119
// order by name so that lockfile manifest is assigned to the first dependency with this manifest
119120
// the others that have the same remote.resovled will just refer to the first
120121
// oredring allows for consistency in lockfile when it is serialized
@@ -138,7 +139,7 @@ export default class Lockfile {
138139

139140
invariant(remote, "Package is missing a remote");
140141

141-
lockfile[pattern] = {
142+
let obj = {
142143
name: pkg.name,
143144
version: pkg.version,
144145
uid: pkg.uid === pkg.version ? undefined : pkg.uid,
@@ -148,9 +149,10 @@ export default class Lockfile {
148149
optionalDependencies: _.isEmpty(pkg.optionalDependencies) ? undefined : pkg.optionalDependencies,
149150
permissions: _.isEmpty(ref.permissions) ? undefined : ref.permissions
150151
};
152+
lockfile[pattern] = obj;
151153

152154
if (remote.resolved) {
153-
seen.set(remote.resolved, pattern);
155+
seen.set(remote.resolved, obj);
154156
}
155157
}
156158

src/lockfile/parse.js

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,16 @@ const TOKEN_TYPES = {
2424
newline: "NEWLINE",
2525
indent: "INDENT",
2626
invalid: "INVALID",
27-
number: "NUMBER"
27+
number: "NUMBER",
28+
comma: "COMMA"
2829
};
2930

3031
const VALID_PROP_VALUE_TOKENS = [TOKEN_TYPES.boolean, TOKEN_TYPES.string, TOKEN_TYPES.number];
3132

33+
function isValidPropValueToken(token): boolean {
34+
return VALID_PROP_VALUE_TOKENS.indexOf(token.type) >= 0;
35+
}
36+
3237
type Token = {
3338
line: number,
3439
col: number,
@@ -111,6 +116,9 @@ export function* tokenise(input: string): Iterator<Token> {
111116
} else if (input[0] === ":") {
112117
yield buildToken(TOKEN_TYPES.colon);
113118
chop++;
119+
} else if (input[0] === ",") {
120+
yield buildToken(TOKEN_TYPES.comma);
121+
chop++;
114122
} else if (/^[a-zA-Z]/g.test(input)) {
115123
let name = "";
116124
for (let i = 0; i < input.length; i++) {
@@ -172,6 +180,15 @@ export class Parser {
172180
}
173181
}
174182

183+
eat(tokType: string): boolean {
184+
if (this.token.type === tokType) {
185+
this.next();
186+
return true;
187+
} else {
188+
return false;
189+
}
190+
}
191+
175192
parse(indent: number = 0): Object {
176193
let obj = map();
177194

@@ -210,16 +227,37 @@ export class Parser {
210227
let key = propToken.value;
211228
invariant(key, "Expected a key");
212229

230+
let keys = [key];
231+
232+
// support multiple keys
233+
while (this.eat(TOKEN_TYPES.comma)) {
234+
let keyToken = this.token;
235+
this.expect(TOKEN_TYPES.string);
236+
237+
let key = keyToken.value;
238+
invariant(key, "Expected a key");
239+
keys.push(key);
240+
}
241+
213242
let valToken = this.next();
214-
if (valToken.type === TOKEN_TYPES.colon) {
243+
if (valToken.type === TOKEN_TYPES.colon) { // object
215244
this.next();
216-
obj[key] = this.parse(indent + 1);
245+
246+
// parse object
247+
let val = this.parse(indent + 1);
248+
249+
for (let key of keys) {
250+
obj[key] = val;
251+
}
217252

218253
if (indent && this.token.type !== TOKEN_TYPES.indent) {
219254
break;
220255
}
221-
} else if (VALID_PROP_VALUE_TOKENS.indexOf(valToken.type) >= 0) {
222-
obj[key] = valToken.value;
256+
} else if (isValidPropValueToken(valToken)) { // plain value
257+
for (let key of keys) {
258+
obj[key] = valToken.value;
259+
}
260+
223261
this.next();
224262
} else {
225263
this.unexpected("Invalid value type");

src/lockfile/stringify.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ let _ = require("lodash");
1313

1414
function shouldWrapKey(str: string): boolean {
1515
return str.indexOf("true") === 0 || str.indexOf("false") === 0 ||
16-
/[:\s\n\\"]/g.test(str) || /^[0-9]/g.test(str) || !/^[a-zA-Z]/g.test(str);
16+
/[:\s\n\\",]/g.test(str) || /^[0-9]/g.test(str) || !/^[a-zA-Z]/g.test(str);
1717
}
1818

1919
function maybeWrap(str: string): string {
@@ -56,19 +56,39 @@ export default function stringify(obj: any, indent: string = ""): string {
5656
// stable sort, V8 Array.prototype.sort is not stable and we don't want to shuffle things randomly
5757
keys = _.sortBy(keys, getKeyPriority);
5858

59-
for (let key of keys) {
59+
let addedKeys = [];
60+
61+
for (let i = 0; i < keys.length; i++) {
62+
let key = keys[i];
6063
let val = obj[key];
6164
if (val == null) continue;
65+
if (addedKeys.indexOf(key) >= 0) continue;
66+
67+
//
68+
let valKeys = [key];
6269

63-
key = maybeWrap(key);
70+
// get all keys that have the same value equality, we only want this for objects
71+
if (typeof val === "object") {
72+
for (let j = i + 1; j < keys.length; j++) {
73+
let key = keys[j];
74+
if (val === obj[key]) {
75+
valKeys.push(key);
76+
}
77+
}
78+
}
79+
80+
//
81+
let keyLine = valKeys.map(maybeWrap).join(", ");
6482

6583
if (typeof val === "string" || typeof val === "boolean" || typeof val === "number") {
66-
lines.push(`${key} ${maybeWrap(val)}`);
84+
lines.push(`${keyLine} ${maybeWrap(val)}`);
6785
} else if (typeof val === "object") {
68-
lines.push(`${key}:\n${stringify(val, indent + " ")}`);
86+
lines.push(`${keyLine}:\n${stringify(val, indent + " ")}`);
6987
} else {
7088
throw new TypeError;
7189
}
90+
91+
addedKeys = addedKeys.concat(valKeys);
7292
}
7393

7494
return indent + lines.join(`\n${indent}`);

test/lockfile.js

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ test("parse", (t) => {
5151
});
5252

5353
test("stringify", (t) => {
54-
stringify;
55-
t;
54+
let obj = { foo: "bar" };
55+
t.deepEqual(stringify({ a: obj, b: obj }), "a, b:\n foo bar");
5656
});
5757

5858
test("Lockfile.isStrict", (t) => {
@@ -147,17 +147,19 @@ test("Lockfile.getLockfile", (t) => {
147147

148148
let actual = new Lockfile().getLockfile(patterns);
149149

150+
let expectedFoobar = {
151+
name: "foobar",
152+
version: "0.0.0",
153+
uid: undefined,
154+
resolved: "http://example.com/foobar",
155+
registry: undefined,
156+
dependencies: undefined,
157+
optionalDependencies: undefined,
158+
permissions: undefined
159+
};
160+
150161
let expected = {
151-
foobar: {
152-
name: "foobar",
153-
version: "0.0.0",
154-
uid: undefined,
155-
resolved: "http://example.com/foobar",
156-
registry: undefined,
157-
dependencies: undefined,
158-
optionalDependencies: undefined,
159-
permissions: undefined
160-
},
162+
foobar: expectedFoobar,
161163

162164
barfoo: {
163165
name: "barfoo",
@@ -170,7 +172,7 @@ test("Lockfile.getLockfile", (t) => {
170172
permissions: { foo: "bar" }
171173
},
172174

173-
foobar2: "foobar"
175+
foobar2: expectedFoobar
174176
};
175177

176178
t.deepEqual(actual, expected);
@@ -198,19 +200,20 @@ test("Lockfile.getLockfile (sorting)", (t) => {
198200

199201
let actual = new Lockfile().getLockfile(patterns);
200202

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-
},
203+
let expectedFoobar = {
204+
name: "foobar",
205+
version: "0.0.0",
206+
uid: undefined,
207+
resolved: "http://example.com/foobar",
208+
registry: undefined,
209+
dependencies: undefined,
210+
optionalDependencies: undefined,
211+
permissions: undefined
212+
};
212213

213-
foobar2: "foobar1"
214+
let expected = {
215+
foobar1: expectedFoobar,
216+
foobar2: expectedFoobar
214217
};
215218

216219
t.deepEqual(actual, expected);

0 commit comments

Comments
 (0)