Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 3 additions & 1 deletion src/utils/win-releases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ export async function parseRELEASES(
content: string,
): Promise<SquirrelRelease[]> {
const stripped = stripBom(content);
const normalizedEOL = stripped.replace("\r\n", "\n");
// String#replace with a string pattern would only replace the first
// CRLF; replaceAll normalizes every CRLF occurrence
const normalizedEOL = stripped.replaceAll("\r\n", "\n");
const lines = normalizedEOL.split("\n");
const goodlines = lines.filter((line) => !!releaseRe.exec(line));

Expand Down
36 changes: 36 additions & 0 deletions test/unit/win-releases.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,42 @@ describe("Windows RELEASES", function () {
expect(releases.length).toBe(5);
});

it("should normalize every CRLF, not just the first one", async function () {
// the line regex tolerates a trailing \r ([\r]*$), so parsed output
// alone can't distinguish full normalization from first-only
// String#replace; capture the lines actually handed to the regex and
// assert none still carries a \r
const seenLines: string[] = [];
const originalExec = RegExp.prototype.exec;
const spy = vi
.spyOn(RegExp.prototype, "exec")
.mockImplementation(function (this: RegExp, str: string) {
seenLines.push(str);
return originalExec.call(this, str);
});
let crlfReleases;
try {
crlfReleases = await parseRELEASES(
"62E8BF432F29E8E08240910B85EDBF2D1A41EDF2 atom-0.178.0-full.nupkg 81272434\r\n" +
"5D754139E89802E88984185D2276B54DB730CD5E atom-0.178.1-delta.nupkg 8938535\r\n" +
"DD48D16EE177DD278F0A82CDDB72EBD043C767D2 atom-0.178.1-full.nupkg 81293415",
);
} finally {
// restore even if parseRELEASES throws, or the global spy would
// leak into subsequent tests
spy.mockRestore();
}

expect(crlfReleases.length).toBe(3);
expect(crlfReleases[2].filename).toBe("atom-0.178.1-full.nupkg");
expect(crlfReleases[2].size).toBe(81293415);
// with first-only replacement the second line (any CRLF-terminated
// line after the first) would still end in \r when it reaches the
// regex; the last line has no CRLF terminator
expect(seenLines.length).toBeGreaterThan(0);
expect(seenLines.some((line) => line.includes("\r"))).toBe(false);
});

it("should parse a one-line file (with utf-8 BOM)", async function () {
const oneRelease = await parseRELEASES(
"\uFEFF24182FAD211FB9EB72610B1C086810FE37F70AE3 gitbook-editor-4.0.0-full.nupkg 46687158",
Expand Down