split('||'), fix up tests

This commit is contained in:
Dylan Conway
2023-11-06 21:17:27 -08:00
parent 434f5bde95
commit 5fd09153cd
3 changed files with 176 additions and 114 deletions

View File

@@ -757,7 +757,7 @@ pub fn parseWithTag(
input = input[1..];
}
const version = Semver.Query.parse(
const version = if (input.len > 0) Semver.Query.parse(
allocator,
input,
sliced.sub(input),
@@ -773,6 +773,9 @@ pub fn parseWithTag(
},
) catch unreachable;
return null;
} else Semver.Query.Group{
.allocator = allocator,
.input = input,
};
const result = Version{

View File

@@ -1858,7 +1858,6 @@ pub const Query = struct {
input: string,
sliced: SlicedString,
) !Group {
var i: usize = 0;
var list = Group{
.allocator = allocator,
.input = input,
@@ -1870,86 +1869,96 @@ pub const Query = struct {
var count: u8 = 0;
var skip_round = false;
var is_or = false;
var only_tagged_versions: ?bool = null;
while (i < input.len) {
skip_round = false;
var itr = strings.split(input, "||");
while (itr.next()) |part| {
var i: usize = 0;
while (i < part.len and strings.containsChar(&std.ascii.whitespace, part[i])) : (i += 1) {}
if (i >= part.len) return Group{
.allocator = allocator,
.input = input,
};
if (i < part.len) {
skip_round = false;
switch (part[i]) {
'>' => {
if (part.len > i + 1 and part[i + 1] == '=') {
token.tag = .gte;
i += 1;
} else {
token.tag = .gt;
}
switch (input[i]) {
'>' => {
if (input.len > i + 1 and input[i + 1] == '=') {
token.tag = .gte;
i += 1;
} else {
token.tag = .gt;
}
while (i < part.len and part[i] == ' ') : (i += 1) {}
},
'<' => {
if (part.len > i + 1 and part[i + 1] == '=') {
token.tag = .lte;
i += 1;
} else {
token.tag = .lt;
}
i += 1;
while (i < input.len and input[i] == ' ') : (i += 1) {}
},
'<' => {
if (input.len > i + 1 and input[i + 1] == '=') {
token.tag = .lte;
i += 1;
} else {
token.tag = .lt;
}
while (i < part.len and part[i] == ' ') : (i += 1) {}
},
'=', 'v' => {
token.tag = .version;
is_or = true;
i += 1;
while (i < part.len and part[i] == ' ') : (i += 1) {}
},
'~' => {
token.tag = .tilda;
i += 1;
i += 1;
while (i < input.len and input[i] == ' ') : (i += 1) {}
},
'=', 'v' => {
token.tag = .version;
is_or = true;
i += 1;
while (i < input.len and input[i] == ' ') : (i += 1) {}
},
'~' => {
token.tag = .tilda;
i += 1;
if (i < part.len and part[i] == '>') i += 1;
if (i < input.len and input[i] == '>') i += 1;
while (i < part.len and part[i] == ' ') : (i += 1) {}
},
'^' => {
token.tag = .caret;
i += 1;
while (i < part.len and part[i] == ' ') : (i += 1) {}
},
'0'...'9', 'X', 'x', '*' => {
token.tag = .version;
is_or = true;
},
'|' => {
i += 1;
while (i < input.len and input[i] == ' ') : (i += 1) {}
},
'^' => {
token.tag = .caret;
i += 1;
while (i < input.len and input[i] == ' ') : (i += 1) {}
},
'0'...'9', 'X', 'x', '*' => {
token.tag = .version;
is_or = true;
},
'|' => {
i += 1;
while (i < part.len and part[i] == '|') : (i += 1) {}
while (i < part.len and part[i] == ' ') : (i += 1) {}
is_or = true;
token.tag = Token.Tag.none;
continue;
},
'-' => {
i += 1;
while (i < part.len and part[i] == ' ') : (i += 1) {}
},
' ' => {
i += 1;
while (i < part.len and part[i] == ' ') : (i += 1) {}
continue;
},
else => {
i += 1;
token.tag = Token.Tag.none;
while (i < input.len and input[i] == '|') : (i += 1) {}
while (i < input.len and input[i] == ' ') : (i += 1) {}
is_or = true;
token.tag = Token.Tag.none;
skip_round = true;
},
'-' => {
i += 1;
while (i < input.len and input[i] == ' ') : (i += 1) {}
},
' ' => {
i += 1;
while (i < input.len and input[i] == ' ') : (i += 1) {}
skip_round = true;
},
else => {
i += 1;
token.tag = Token.Tag.none;
// skip tagged versions
while (i < part.len and part[i] != ' ' and part[i] != '|') : (i += 1) {}
if (only_tagged_versions == null) only_tagged_versions = true;
continue;
},
}
// skip tagged versions
while (i < input.len and input[i] != ' ' and input[i] != '|') : (i += 1) {}
skip_round = true;
},
}
if (!skip_round) {
const parse_result = Version.parse(sliced.sub(input[i..]));
const parse_result = Version.parse(sliced.sub(part[i..]));
const version = parse_result.version.fill();
if (version.tag.hasBuild()) list.flags.setValue(Group.Flags.build, true);
if (version.tag.hasPre()) list.flags.setValue(Group.Flags.pre, true);
@@ -1959,21 +1968,21 @@ pub const Query = struct {
i += parse_result.stopped_at;
const rollback = i;
const had_space = i < input.len and input[i] == ' ';
const had_space = i < part.len and part[i] == ' ';
// TODO: can we do this without rolling back?
const hyphenate: bool = had_space and possibly_hyphenate: {
i += 1;
while (i < input.len and input[i] == ' ') : (i += 1) {}
if (!(i < input.len and input[i] == '-')) break :possibly_hyphenate false;
while (i < part.len and part[i] == ' ') : (i += 1) {}
if (!(i < part.len and part[i] == '-')) break :possibly_hyphenate false;
i += 1;
if (!(i < input.len and input[i] == ' ')) break :possibly_hyphenate false;
if (!(i < part.len and part[i] == ' ')) break :possibly_hyphenate false;
i += 1;
while (i < input.len and switch (input[i]) {
while (i < part.len and switch (part[i]) {
' ', 'v', '=' => true,
else => false,
}) : (i += 1) {}
if (!(i < input.len and switch (input[i]) {
if (!(i < part.len and switch (part[i]) {
'0'...'9', 'X', 'x', '*' => true,
else => false,
})) break :possibly_hyphenate false;
@@ -1985,7 +1994,7 @@ pub const Query = struct {
i += @as(usize, @intFromBool(!hyphenate));
if (hyphenate) {
const second_parsed = Version.parse(sliced.sub(input[i..]));
const second_parsed = Version.parse(sliced.sub(part[i..]));
var second_version = second_parsed.version.fill();
if (second_version.tag.hasBuild()) list.flags.setValue(Group.Flags.build, true);
if (second_version.tag.hasPre()) list.flags.setValue(Group.Flags.pre, true);
@@ -2060,12 +2069,17 @@ pub const Query = struct {
}
is_or = false;
only_tagged_versions = false;
count += 1;
token.wildcard = .none;
prev_token.tag = token.tag;
}
}
if (count == 0 and only_tagged_versions != null and only_tagged_versions.?) {
return error.InvalidDependencyVersion;
}
return list;
}
};

View File

@@ -70,7 +70,6 @@ test("basic 1", async () => {
const err = await new Response(stderr).text();
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -83,6 +82,7 @@ test("basic 1", async () => {
name: "basic-1",
version: "1.0.0",
} as any);
expect(await exited).toBe(0);
});
test("dependency from root satisfies range from dependency", async () => {
@@ -111,7 +111,6 @@ test("dependency from root satisfies range from dependency", async () => {
const err = await new Response(stderr).text();
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -125,6 +124,7 @@ test("dependency from root satisfies range from dependency", async () => {
name: "no-deps",
version: "1.0.0",
} as any);
expect(await exited).toBe(0);
});
test("package added after install", async () => {
@@ -152,7 +152,6 @@ test("package added after install", async () => {
var err = await new Response(stderr).text();
expect(stdout).toBeDefined();
var out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -165,6 +164,7 @@ test("package added after install", async () => {
name: "no-deps",
version: "1.1.0",
} as any);
expect(await exited).toBe(0);
// add `no-deps` to root package.json with a smaller but still compatible
// version for `one-range-dep`.
@@ -193,7 +193,6 @@ test("package added after install", async () => {
err = await new Response(stderr).text();
expect(stdout).toBeDefined();
out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -212,6 +211,7 @@ test("package added after install", async () => {
name: "no-deps",
version: "1.1.0",
} as any);
expect(await exited).toBe(0);
});
describe("semver", () => {
@@ -239,12 +239,12 @@ describe("semver", () => {
{
title: "start with ||",
depVersion: "|| 1",
expected: "1.0.1",
expected: "3.0.0",
},
{
title: "start with || no space",
depVersion: "||2",
expected: "2.0.1",
expected: "3.0.0",
},
{
title: "|| with no space on both sides",
@@ -266,6 +266,51 @@ describe("semver", () => {
depVersion: "pre-3",
expected: "3.0.1",
},
{
title: "'||'",
depVersion: "||",
expected: "3.0.0",
},
{
title: "'|'",
depVersion: "|",
expected: "3.0.0",
},
{
title: "'|||'",
depVersion: "|||",
expected: "3.0.0",
},
{
title: "'|| ||'",
depVersion: "|| ||",
expected: "3.0.0",
},
{
title: "'|| 1 ||'",
depVersion: "|| 1 ||",
expected: "3.0.0",
},
{
title: "'| | |'",
depVersion: "| | |",
expected: "3.0.0",
},
{
title: "'|||||||||||||||||||||||||'",
depVersion: "|||||||||||||||||||||||||",
expected: "3.0.0",
},
{
title: "'2 ||| 1'",
depVersion: "2 ||| 1",
expected: "2.0.1",
},
{
title: "'2 |||| 1'",
depVersion: "2 |||| 1",
expected: "3.0.0",
},
];
for (const { title, depVersion, expected } of taggedVersionTests) {
@@ -294,7 +339,6 @@ describe("semver", () => {
var err = await new Response(stderr).text();
expect(stdout).toBeDefined();
var out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -303,6 +347,7 @@ describe("semver", () => {
"",
" 1 package installed",
]);
expect(await exited).toBe(0);
});
}
@@ -331,8 +376,8 @@ describe("semver", () => {
var err = await new Response(stderr).text();
expect(stdout).toBeDefined();
var out = await new Response(stdout).text();
expect(err).toContain('InvalidDependencyVersion parsing version "pre-1 || pre-2"');
expect(await exited).toBe(1);
expect(err).toContain('InvalidDependencyVersion parsing dependency "dep-with-tags" with version "pre-1 || pre-2"');
expect(out).toBeEmpty();
});
});
@@ -386,7 +431,6 @@ describe("prereleases", () => {
const err = await new Response(stderr).text();
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -399,6 +443,7 @@ describe("prereleases", () => {
name: depName,
version: expected,
} as any);
expect(await exited).toBe(0);
});
}
});
@@ -432,7 +477,6 @@ describe("yarn tests", () => {
const err = await new Response(stderr).text();
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -468,6 +512,7 @@ describe("yarn tests", () => {
"dragon-test-1-a": "1.0.0",
},
} as any);
expect(await exited).toBe(0);
});
test("dragon test 2", async () => {
@@ -522,7 +567,6 @@ describe("yarn tests", () => {
const err = await new Response(stderr).text();
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -547,6 +591,7 @@ describe("yarn tests", () => {
name: "no-deps",
version: "1.0.0",
} as any);
expect(await exited).toBe(0);
});
test("dragon test 3", async () => {
@@ -574,7 +619,6 @@ describe("yarn tests", () => {
const err = await new Response(stderr).text();
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -599,6 +643,7 @@ describe("yarn tests", () => {
"no-deps": "*",
},
} as any);
expect(await exited).toBe(0);
});
test("dragon test 4", async () => {
@@ -641,7 +686,6 @@ describe("yarn tests", () => {
const err = await new Response(stderr).text();
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -667,6 +711,7 @@ describe("yarn tests", () => {
"no-deps": "*",
},
} as any);
expect(await exited).toBe(0);
});
test("dragon test 5", async () => {
@@ -720,7 +765,6 @@ describe("yarn tests", () => {
const err = await new Response(stderr).text();
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -753,6 +797,7 @@ describe("yarn tests", () => {
name: "various-requires",
version: "1.0.0",
} as any);
expect(await exited).toBe(0);
});
test.todo("dragon test 6", async () => {
@@ -854,7 +899,6 @@ describe("yarn tests", () => {
const err = await new Response(stderr).text();
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -869,6 +913,7 @@ describe("yarn tests", () => {
"",
" 7 packages installed",
]);
expect(await exited).toBe(0);
});
test.todo("dragon test 7", async () => {
@@ -899,7 +944,6 @@ describe("yarn tests", () => {
var err = await new Response(stderr).text();
expect(stdout).toBeDefined();
var out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -911,6 +955,7 @@ describe("yarn tests", () => {
"",
" 7 packages installed",
]);
expect(await exited).toBe(0);
await writeFile(
join(packageDir, "test.js"),
@@ -930,7 +975,6 @@ describe("yarn tests", () => {
err = await new Response(stderr).text();
expect(stdout).toBeDefined();
out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toBeEmpty();
expect(out).toBe("1.0.0 1.0.0\n");
@@ -952,6 +996,7 @@ describe("yarn tests", () => {
join(packageDir, "node_modules", "dragon-test-7-d", "node_modules", "dragon-test-7-b", "node_modules"),
),
).toBeFalse();
expect(await exited).toBe(0);
});
test("dragon test 8", async () => {
@@ -982,7 +1027,6 @@ describe("yarn tests", () => {
const err = await new Response(stderr).text();
expect(stdout).toBeDefined();
const out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -994,6 +1038,7 @@ describe("yarn tests", () => {
"",
" 4 packages installed",
]);
expect(await exited).toBe(0);
});
test("dragon test 9", async () => {
@@ -1022,7 +1067,6 @@ describe("yarn tests", () => {
var err = await new Response(stderr).text();
expect(stdout).toBeDefined();
var out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("not found");
expect(err).not.toContain("error:");
@@ -1036,6 +1080,7 @@ describe("yarn tests", () => {
expect(await file(join(packageDir, "node_modules", "first", "package.json")).json()).toEqual(
await file(join(packageDir, "node_modules", "second", "package.json")).json(),
);
expect(await exited).toBe(0);
});
test.todo("dragon test 10", async () => {
@@ -1099,7 +1144,6 @@ describe("yarn tests", () => {
const out = await new Response(stdout).text();
expect(stderr).toBeDefined();
const err = await new Response(stderr).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("error:");
expect(err).not.toContain("not found");
@@ -1110,6 +1154,7 @@ describe("yarn tests", () => {
"",
" 3 packages installed",
]);
expect(await exited).toBe(0);
});
test("dragon test 12", async () => {
@@ -1161,7 +1206,6 @@ describe("yarn tests", () => {
const out = await new Response(stdout).text();
expect(stderr).toBeDefined();
const err = await new Response(stderr).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("error:");
expect(err).not.toContain("not found");
@@ -1185,6 +1229,7 @@ describe("yarn tests", () => {
"no-deps": "*",
},
} as any);
expect(await exited).toBe(0);
});
test("it should not warn when the peer dependency resolution is compatible", async () => {
@@ -1213,7 +1258,6 @@ describe("yarn tests", () => {
const out = await new Response(stdout).text();
expect(stderr).toBeDefined();
const err = await new Response(stderr).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("error:");
expect(err).not.toContain("not found");
@@ -1225,6 +1269,7 @@ describe("yarn tests", () => {
" 2 packages installed",
]);
expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".cache", "no-deps", "peer-deps-fixed"]);
expect(await exited).toBe(0);
});
test("it should warn when the peer dependency resolution is incompatible", async () => {
@@ -1253,7 +1298,6 @@ describe("yarn tests", () => {
const out = await new Response(stdout).text();
expect(stderr).toBeDefined();
const err = await new Response(stderr).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("error:");
expect(err).not.toContain("not found");
@@ -1265,6 +1309,7 @@ describe("yarn tests", () => {
" 2 packages installed",
]);
expect(await readdirSorted(join(packageDir, "node_modules"))).toEqual([".cache", "no-deps", "peer-deps-fixed"]);
expect(await exited).toBe(0);
});
test.todo(
@@ -1293,7 +1338,6 @@ describe("yarn tests", () => {
var err = await new Response(stderr).text();
var out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("error:");
expect(err).not.toContain("not found");
@@ -1304,6 +1348,7 @@ describe("yarn tests", () => {
"",
" 5 packages installed",
]);
expect(await exited).toBe(0);
await writeFile(
join(packageDir, "test.js"),
@@ -1368,9 +1413,9 @@ describe("yarn tests", () => {
err = await new Response(stderr).text();
out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toBeEmpty();
expect(out).toBe("true\ntrue\ntrue");
expect(err).toBeEmpty();
expect(await exited).toBe(0);
},
);
@@ -1398,7 +1443,6 @@ describe("yarn tests", () => {
var err = await new Response(stderr).text();
var out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("error:");
expect(err).not.toContain("not found");
@@ -1409,6 +1453,7 @@ describe("yarn tests", () => {
"",
" 4 packages installed",
]);
expect(await exited).toBe(0);
await writeFile(
join(packageDir, "test.js"),
@@ -1429,9 +1474,9 @@ describe("yarn tests", () => {
err = await new Response(stderr).text();
out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toBeEmpty();
expect(out).toBe("true\n");
expect(err).toBeEmpty();
expect(await exited).toBe(0);
});
test("it should install in such a way that two identical packages with the same peer dependencies are the same instances (complex)", async () => {
await writeFile(
@@ -1458,7 +1503,6 @@ describe("yarn tests", () => {
var err = await new Response(stderr).text();
var out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("error:");
expect(err).not.toContain("not found");
@@ -1470,6 +1514,7 @@ describe("yarn tests", () => {
"",
" 4 packages installed",
]);
expect(await exited).toBe(0);
await writeFile(
join(packageDir, "test.js"),
@@ -1490,9 +1535,9 @@ describe("yarn tests", () => {
err = await new Response(stderr).text();
out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toBeEmpty();
expect(out).toBe("true\n");
expect(err).toBeEmpty();
expect(await exited).toBe(0);
});
test("it shouldn't deduplicate two packages with similar peer dependencies but different names", async () => {
@@ -1520,7 +1565,6 @@ describe("yarn tests", () => {
var err = await new Response(stderr).text();
var out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toContain("Saved lockfile");
expect(err).not.toContain("error:");
expect(err).not.toContain("not found");
@@ -1532,6 +1576,7 @@ describe("yarn tests", () => {
"",
" 3 packages installed",
]);
expect(await exited).toBe(0);
await writeFile(join(packageDir, "test.js"), `console.log(require('peer-deps') === require('peer-deps-too'));`);
@@ -1546,8 +1591,8 @@ describe("yarn tests", () => {
err = await new Response(stderr).text();
out = await new Response(stdout).text();
expect(await exited).toBe(0);
expect(err).toBeEmpty();
expect(out).toBe("false\n");
expect(err).toBeEmpty();
expect(await exited).toBe(0);
});
});