Skip to content

Commit 73bffc0

Browse files
authored
refactor compareSemver (#787)
1 parent a6da6ac commit 73bffc0

File tree

7 files changed

+128
-47
lines changed

7 files changed

+128
-47
lines changed

.changeset/gentle-points-live.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@opennextjs/aws": patch
3+
---
4+
5+
refactor `compareSemver`.
6+
7+
It now takes a comparison operator and return a boolean, i.e. `compareSemver("1.0", "<=", "1.2.3")`

packages/open-next/src/build/compileCache.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ export function compileCache(
1717
const ext = format === "cjs" ? "cjs" : "mjs";
1818
const outFile = path.join(options.buildDir, `cache.${ext}`);
1919

20-
const isAfter15 =
21-
buildHelper.compareSemver(options.nextVersion, "15.0.0") >= 0;
20+
const isAfter15 = buildHelper.compareSemver(
21+
options.nextVersion,
22+
">=",
23+
"15.0.0",
24+
);
2225

2326
buildHelper.esbuildSync(
2427
{

packages/open-next/src/build/createImageOptimizationBundle.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export async function createImageOptimizationBundle(
3636
}),
3737
];
3838

39-
if (buildHelper.compareSemver(options.nextVersion, "14.1.1") >= 0) {
39+
if (buildHelper.compareSemver(options.nextVersion, ">=", "14.1.1")) {
4040
plugins.push(
4141
openNextReplacementPlugin({
4242
name: "opennext-14.1.1-image-optimization",

packages/open-next/src/build/createServerBundle.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,15 +190,21 @@ async function generateBundle(
190190
// Next.js app.
191191

192192
const disableNextPrebundledReact =
193-
buildHelper.compareSemver(options.nextVersion, "13.5.1") >= 0 ||
194-
buildHelper.compareSemver(options.nextVersion, "13.4.1") <= 0;
193+
buildHelper.compareSemver(options.nextVersion, ">=", "13.5.1") ||
194+
buildHelper.compareSemver(options.nextVersion, "<=", "13.4.1");
195195

196196
const overrides = fnOptions.override ?? {};
197197

198-
const isBefore13413 =
199-
buildHelper.compareSemver(options.nextVersion, "13.4.13") <= 0;
200-
const isAfter141 =
201-
buildHelper.compareSemver(options.nextVersion, "14.1") >= 0;
198+
const isBefore13413 = buildHelper.compareSemver(
199+
options.nextVersion,
200+
"<=",
201+
"13.4.13",
202+
);
203+
const isAfter141 = buildHelper.compareSemver(
204+
options.nextVersion,
205+
">=",
206+
"14.1",
207+
);
202208

203209
const disableRouting = isBefore13413 || config.middleware?.external;
204210

packages/open-next/src/build/helper.ts

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -288,33 +288,65 @@ export function getNextVersion(appPath: string): string {
288288
return version.split("-")[0];
289289
}
290290

291+
export type SemverOp = "=" | ">=" | "<=" | ">" | "<";
292+
291293
/**
292294
* Compare two semver versions.
293295
*
294296
* @param v1 - First version. Can be "latest", otherwise it should be a valid semver version in the format of `major.minor.patch`. Usually is the next version from the package.json without canary suffix. If minor or patch are missing, they are considered 0.
295297
* @param v2 - Second version. Should not be "latest", it should be a valid semver version in the format of `major.minor.patch`. If minor or patch are missing, they are considered 0.
296298
* @example
297-
* compareSemver("1.0.0", "1.0.0") // 0
299+
* compareSemver("2.0.0", ">=", "1.0.0") === true
298300
*/
299-
export function compareSemver(v1: string, v2: string): number {
300-
if (v1 === "latest") return 1;
301-
if (/^[^\d]/.test(v1)) {
302-
// biome-ignore lint/style/noParameterAssign:
303-
v1 = v1.substring(1);
304-
}
305-
if (/^[^\d]/.test(v2)) {
306-
// biome-ignore lint/style/noParameterAssign:
307-
v2 = v2.substring(1);
308-
}
309-
const [major1, minor1 = 0, patch1 = 0] = v1.split(".").map(Number);
310-
const [major2, minor2 = 0, patch2 = 0] = v2.split(".").map(Number);
311-
if (Number.isNaN(major1) || Number.isNaN(major2)) {
312-
throw new Error("The major version is required.");
301+
export function compareSemver(
302+
v1: string,
303+
operator: SemverOp,
304+
v2: string,
305+
): boolean {
306+
// - = 0 when versions are equal
307+
// - > 0 if v1 > v2
308+
// - < 0 if v2 > v1
309+
let versionDiff = 0;
310+
if (v1 === "latest") {
311+
versionDiff = 1;
312+
} else {
313+
if (/^[^\d]/.test(v1)) {
314+
// biome-ignore lint/style/noParameterAssign:
315+
v1 = v1.substring(1);
316+
}
317+
if (/^[^\d]/.test(v2)) {
318+
// biome-ignore lint/style/noParameterAssign:
319+
v2 = v2.substring(1);
320+
}
321+
const [major1, minor1 = 0, patch1 = 0] = v1.split(".").map(Number);
322+
const [major2, minor2 = 0, patch2 = 0] = v2.split(".").map(Number);
323+
if (Number.isNaN(major1) || Number.isNaN(major2)) {
324+
throw new Error("The major version is required.");
325+
}
326+
327+
if (major1 !== major2) {
328+
versionDiff = major1 - major2;
329+
} else if (minor1 !== minor2) {
330+
versionDiff = minor1 - minor2;
331+
} else if (patch1 !== patch2) {
332+
versionDiff = patch1 - patch2;
333+
}
313334
}
314335

315-
if (major1 !== major2) return major1 - major2;
316-
if (minor1 !== minor2) return minor1 - minor2;
317-
return patch1 - patch2;
336+
switch (operator) {
337+
case "=":
338+
return versionDiff === 0;
339+
case ">=":
340+
return versionDiff >= 0;
341+
case "<=":
342+
return versionDiff <= 0;
343+
case ">":
344+
return versionDiff > 0;
345+
case "<":
346+
return versionDiff < 0;
347+
default:
348+
throw new Error(`Unsupported operator: ${operator}`);
349+
}
318350
}
319351

320352
export function copyOpenNextConfig(

packages/open-next/src/build/patch/codePatcher.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,20 @@ export function extractVersionedField<T>(
103103
if (
104104
before &&
105105
after &&
106-
buildHelper.compareSemver(version, before) <= 0 &&
107-
buildHelper.compareSemver(version, after) >= 0
106+
buildHelper.compareSemver(version, "<=", before) &&
107+
buildHelper.compareSemver(version, ">=", after)
108108
) {
109109
result.push(field.field);
110110
} else if (
111111
before &&
112112
!after &&
113-
buildHelper.compareSemver(version, before) <= 0
113+
buildHelper.compareSemver(version, "<=", before)
114114
) {
115115
result.push(field.field);
116116
} else if (
117117
after &&
118118
!before &&
119-
buildHelper.compareSemver(version, after) >= 0
119+
buildHelper.compareSemver(version, ">=", after)
120120
) {
121121
result.push(field.field);
122122
}

packages/tests-unit/tests/build/helper.test.ts

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,66 @@ import { compareSemver } from "@opennextjs/aws/build/helper.js";
22

33
// We don't need to test canary versions, they are stripped out
44
describe("compareSemver", () => {
5-
it("should return 0 when versions are equal", () => {
6-
expect(compareSemver("1.0.0", "1.0.0")).toBe(0);
5+
test("=", () => {
6+
expect(compareSemver("1.0.0", "=", "1.0.0")).toBe(true);
7+
expect(compareSemver("1.1.0", "=", "1.0.0")).toBe(false);
8+
expect(compareSemver("1.0.1", "=", "1.0.0")).toBe(false);
79
});
810

9-
it("should return 1 when first version is greater", () => {
10-
expect(compareSemver("1.0.1", "1.0.0")).toBe(1);
11+
it(">", () => {
12+
expect(compareSemver("1.0.1", ">", "1.0.0")).toBe(true);
13+
expect(compareSemver("1.0.0", ">", "1.0.0")).toBe(false);
14+
expect(compareSemver("1.0.0", ">", "1.0.1")).toBe(false);
1115
});
1216

13-
it("should return -1 when first version is smaller", () => {
14-
expect(compareSemver("1.0.0", "1.0.1")).toBe(-1);
17+
it(">=", () => {
18+
expect(compareSemver("1.0.1", ">=", "1.0.0")).toBe(true);
19+
expect(compareSemver("1.0.0", ">=", "1.0.0")).toBe(true);
20+
expect(compareSemver("1.0.0", ">=", "1.0.1")).toBe(false);
1521
});
1622

17-
it("should work with latest", () => {
18-
expect(compareSemver("latest", "1.0.0")).toBe(1);
23+
it("<", () => {
24+
expect(compareSemver("1.0.0", "<", "1.0.1")).toBe(true);
25+
expect(compareSemver("1.0.0", "<", "1.0.0")).toBe(false);
26+
expect(compareSemver("1.0.0", "<", "0.0.1")).toBe(false);
1927
});
2028

21-
it("should work with incomplete version for patch", () => {
22-
expect(compareSemver("14.1.0", "14.1")).toBe(0);
23-
expect(compareSemver("14.1", "14.1.0")).toBe(0);
29+
it("<=", () => {
30+
expect(compareSemver("1.0.0", "<=", "1.0.1")).toBe(true);
31+
expect(compareSemver("1.0.0", "<=", "1.0.0")).toBe(true);
32+
expect(compareSemver("1.0.0", "<=", "0.0.1")).toBe(false);
2433
});
2534

26-
it("should work with incomplete version for minor", () => {
27-
expect(compareSemver("14.0.0", "14")).toBe(0);
35+
test("latest", () => {
36+
expect(compareSemver("latest", "=", "1.0.0")).toBe(false);
37+
expect(compareSemver("latest", ">=", "1.0.0")).toBe(true);
38+
expect(compareSemver("latest", ">", "1.0.0")).toBe(true);
39+
expect(compareSemver("latest", "<=", "1.0.0")).toBe(false);
40+
expect(compareSemver("latest", "<", "1.0.0")).toBe(false);
2841
});
2942

30-
it("should throw if the major version is missing", () => {
31-
expect(() => compareSemver("incorrect", "14.0.0")).toThrow();
32-
expect(() => compareSemver("14.0.0", "latest")).toThrow();
43+
test("incomplete version for patch", () => {
44+
expect(compareSemver("14.1.0", "=", "14.1")).toBe(true);
45+
expect(compareSemver("14.1", "=", "14.1.0")).toBe(true);
46+
});
47+
48+
test("incomplete version for minor", () => {
49+
expect(compareSemver("14.0.0", "=", "14")).toBe(true);
50+
expect(compareSemver("14", "=", "14.0.0")).toBe(true);
51+
});
52+
53+
test("throw if the major version is missing", () => {
54+
expect(() => compareSemver("incorrect", "=", "14.0.0")).toThrow();
55+
expect(() => compareSemver("14.0.0", "=", "latest")).toThrow();
56+
});
57+
58+
test("throw if the major version is missing", () => {
59+
expect(() => compareSemver("incorrect", "=", "14.0.0")).toThrow();
60+
expect(() => compareSemver("14.0.0", "=", "latest")).toThrow();
61+
});
62+
63+
test("throw if the operator is not supported", () => {
64+
expect(() => compareSemver("14.0.0", "==" as any, "14.0.0")).toThrow();
65+
expect(() => compareSemver("14.0.0", "!=" as any, "14.0.0")).toThrow();
3366
});
3467
});

0 commit comments

Comments
 (0)