mirror of
https://github.com/vercel-labs/json-render.git
synced 2026-10-02 03:54:41 +08:00
fix(directives): handle future dates in $format and warn on $math NaN coercion
$format relative dates now support future timestamps ("2h from now")
and return "just now" for zero diff. $math emits a console.warn in
dev mode when a non-numeric value is silently coerced to 0.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import {
|
||||
resolvePropValue,
|
||||
createDirectiveRegistry,
|
||||
@@ -87,6 +87,36 @@ describe("$format", () => {
|
||||
);
|
||||
expect(result).toBe("3h ago");
|
||||
});
|
||||
|
||||
it("formats a future relative date", () => {
|
||||
const ctx = makeCtx();
|
||||
const baseDate = new Date("2024-06-15T12:00:00Z").getTime();
|
||||
const result = resolvePropValue(
|
||||
{
|
||||
$format: "date",
|
||||
value: "2024-06-15T12:00:00Z",
|
||||
style: "relative",
|
||||
now: baseDate - 2 * 60 * 60 * 1000,
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
expect(result).toBe("2h from now");
|
||||
});
|
||||
|
||||
it("returns 'just now' when date equals now", () => {
|
||||
const ctx = makeCtx();
|
||||
const ts = new Date("2024-06-15T12:00:00Z").getTime();
|
||||
const result = resolvePropValue(
|
||||
{
|
||||
$format: "date",
|
||||
value: "2024-06-15T12:00:00Z",
|
||||
style: "relative",
|
||||
now: ts,
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
expect(result).toBe("just now");
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
@@ -168,6 +198,18 @@ describe("$math", () => {
|
||||
const ctx = makeCtx();
|
||||
expect(resolvePropValue({ $math: "add", b: 3 }, ctx)).toBe(3);
|
||||
});
|
||||
|
||||
it("warns when a non-numeric value is coerced to 0", () => {
|
||||
const ctx = makeCtx();
|
||||
const spy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||
const result = resolvePropValue({ $math: "add", a: "foo", b: 3 }, ctx);
|
||||
expect(result).toBe(3);
|
||||
expect(spy).toHaveBeenCalledWith(
|
||||
"$math: non-numeric value coerced to 0:",
|
||||
"foo",
|
||||
);
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -25,14 +25,17 @@ export const formatDirective = defineDirective({
|
||||
if (raw.style === "relative") {
|
||||
const now = raw.now ?? Date.now();
|
||||
const diff = now - date.getTime();
|
||||
const seconds = Math.floor(diff / 1000);
|
||||
if (diff === 0) return "just now";
|
||||
const absDiff = Math.abs(diff);
|
||||
const suffix = diff > 0 ? "ago" : "from now";
|
||||
const seconds = Math.floor(absDiff / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
if (days > 0) return `${days}d ago`;
|
||||
if (hours > 0) return `${hours}h ago`;
|
||||
if (minutes > 0) return `${minutes}m ago`;
|
||||
return `${seconds}s ago`;
|
||||
if (days > 0) return `${days}d ${suffix}`;
|
||||
if (hours > 0) return `${hours}h ${suffix}`;
|
||||
if (minutes > 0) return `${minutes}m ${suffix}`;
|
||||
return `${seconds}s ${suffix}`;
|
||||
}
|
||||
return new Intl.DateTimeFormat(
|
||||
locale,
|
||||
|
||||
@@ -4,7 +4,13 @@ import { defineDirective, resolvePropValue } from "@json-render/core";
|
||||
function toNum(v: unknown): number {
|
||||
if (v == null) return 0;
|
||||
const n = Number(v);
|
||||
return Number.isNaN(n) ? 0 : n;
|
||||
if (Number.isNaN(n)) {
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
console.warn(`$math: non-numeric value coerced to 0:`, v);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
export const mathDirective = defineDirective({
|
||||
|
||||
Reference in New Issue
Block a user