Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Palette's Journal

## 2024-05-22 - DX is UX for Libraries
**Learning:** For a utility library, "UX" often means Developer Experience (DX). Clear, rich JSDoc comments with `@example` and `@see` links make the library much easier to adopt and debug without leaving the IDE.
**Action:** Always check if public exports have sufficient documentation, including usage examples and external references where applicable.
2 changes: 2 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ const v = getVersion();
| `isLTS` | `boolean` | `true` if the current version is an [LTS release](https://github.com/nodejs/release#release-schedule). |
| `ltsName` | `string` | The LTS codename (e.g., 'Iron') or `undefined`. |
| `isEOL` | `boolean` | `true` if the current version is past its [End-of-Life date](https://github.com/nodejs/release#end-of-life-releases). |
| `eolDate` | `Date` | The date when this major version becomes End-of-Life. |
| `daysUntilEOL` | `number` | The number of days until the EOL date (negative if passed). |

## Compare Versions

Expand Down
54 changes: 50 additions & 4 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* MIT Licensed
*/

import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { versions as realVersions } from "node:process";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import { EOL_DATES, getVersion, version } from "./index.js";

const { mockVersion, mockRelease } = vi.hoisted(() => ({
Expand Down Expand Up @@ -104,9 +104,9 @@ describe("node-version", () => {
expect(typeof v.build).toBe("string");
});

test("object should have exactly 16 properties", () => {
expect(Object.keys(version)).toHaveLength(16);
expect(Object.keys(getVersion())).toHaveLength(16);
test("object should have exactly 17 properties", () => {
expect(Object.keys(version)).toHaveLength(17);
expect(Object.keys(getVersion())).toHaveLength(17);
});

test("original property should start with v", () => {
Expand Down Expand Up @@ -329,5 +329,51 @@ describe("node-version", () => {
const v = getVersion();
expect(v.eolDate).toBeUndefined();
});

test("should return a number for a known EOL date", () => {
// Mock current date to be before EOL
vi.useFakeTimers();
const mockDate = new Date("2024-01-01T00:00:00Z");
vi.setSystemTime(mockDate);
mockVersion.node = "20.10.0";

const version = getVersion();

// Node 20 EOL is 2026-04-30
const eolDate = new Date("2026-04-30");
const diffTime = eolDate.getTime() - mockDate.getTime();
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

expect(version.daysUntilEOL).toBe(diffDays);
expect(version.daysUntilEOL).toBeGreaterThan(0);

vi.useRealTimers();
});

test("should return negative number for passed EOL date", () => {
// Mock current date to be after EOL
vi.useFakeTimers();
const mockDate = new Date("2027-01-01T00:00:00Z");
vi.setSystemTime(mockDate);
mockVersion.node = "20.10.0";

const version = getVersion();

// Node 20 EOL is 2026-04-30
const eolDate = new Date("2026-04-30");
const diffTime = eolDate.getTime() - mockDate.getTime();
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

expect(version.daysUntilEOL).toBe(diffDays);
expect(version.daysUntilEOL).toBeLessThan(0);

vi.useRealTimers();
});

test("should have undefined daysUntilEOL for unknown version", () => {
mockVersion.node = "99.0.0";
const v = getVersion();
expect(v.daysUntilEOL).toBeUndefined();
});
});
});
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export const getVersion = (): NodeVersion => {
const nodeVersionParts = split.map((s) => Number(s) || 0);
const major = split[0] || "0";
const eolString = EOL_DATES[major];
const eolDate = eolString ? new Date(eolString) : undefined;
const daysUntilEOL = eolDate
? Math.ceil((eolDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24))
: undefined;

/**
* Compare the current node version with a target version string.
Expand Down Expand Up @@ -107,7 +111,8 @@ export const getVersion = (): NodeVersion => {
isLTS: !!release.lts,
ltsName: String(release.lts || "") || undefined,
isEOL: checkEOL(major),
eolDate: eolString ? new Date(eolString) : undefined,
eolDate,
daysUntilEOL,
toString: () => `v${nodeVersion}`,
};
};
Expand Down
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ export interface NodeVersion {
* @see https://github.com/nodejs/release#release-schedule
*/
eolDate: Date | undefined;
/**
* The number of days until the EOL date.
* Positive if EOL is in the future, negative if in the past.
* Undefined if the EOL date is not known.
*/
daysUntilEOL: number | undefined;
/**
* Returns the original version string.
*/
Expand Down