mirror of
https://github.com/acamarata/moon-cycle.git
synced 2026-06-30 18:54:29 +00:00
ci: fix eslint config and add missing @typescript-eslint devDeps
This commit is contained in:
parent
2d18b68794
commit
49d880b751
11 changed files with 169 additions and 144 deletions
|
|
@ -1,16 +1,33 @@
|
|||
import tsParser from '@typescript-eslint/parser';
|
||||
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
import { typescript } from '@acamarata/eslint-config';
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import tsPlugin from "@typescript-eslint/eslint-plugin";
|
||||
import eslintConfigPrettier from "eslint-config-prettier";
|
||||
import { typescript } from "@acamarata/eslint-config";
|
||||
|
||||
export default [
|
||||
{
|
||||
plugins: { '@typescript-eslint': tsPlugin },
|
||||
languageOptions: { parser: tsParser },
|
||||
files: ["**/*.ts", "**/*.tsx"],
|
||||
plugins: { "@typescript-eslint": tsPlugin },
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
parserOptions: {
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
},
|
||||
},
|
||||
...typescript,
|
||||
...typescript.map((c) => ({
|
||||
...c,
|
||||
files: ["**/*.ts", "**/*.tsx"],
|
||||
languageOptions: {
|
||||
...(c.languageOptions ?? {}),
|
||||
parser: tsParser,
|
||||
parserOptions: {
|
||||
...((c.languageOptions ?? {}).parserOptions ?? {}),
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
},
|
||||
})),
|
||||
eslintConfigPrettier,
|
||||
{
|
||||
ignores: ['dist/', 'node_modules/', 'test.mjs', 'test-cjs.cjs'],
|
||||
ignores: ["dist/", "node_modules/", "test.mjs", "test-cjs.cjs"],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@
|
|||
"@acamarata/tsconfig": "^0.1.0",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@types/node": "^22.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
||||
"@typescript-eslint/parser": "^8.56.1",
|
||||
"c8": "^10.1.3",
|
||||
"eslint": "^10.0.3",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ importers:
|
|||
'@types/node':
|
||||
specifier: ^22.0.0
|
||||
version: 22.19.11
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^8.56.1
|
||||
version: 8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(typescript@5.9.3)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^8.56.1
|
||||
version: 8.56.1(eslint@10.0.3)(typescript@5.9.3)
|
||||
c8:
|
||||
specifier: ^10.1.3
|
||||
version: 10.1.3
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { SYNODIC_MONTH, MONTH_IMAGES, MONTH_ANCHOR } from './types.js';
|
||||
import { SYNODIC_MONTH, MONTH_IMAGES, MONTH_ANCHOR } from "./types.js";
|
||||
|
||||
const SYNODIC_SECONDS = SYNODIC_MONTH * 24 * 60 * 60;
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ const SYNODIC_SECONDS = SYNODIC_MONTH * 24 * 60 * 60;
|
|||
*/
|
||||
export function cycleMonth(date: Date = new Date()): string {
|
||||
if (!(date instanceof Date) || isNaN(date.getTime())) {
|
||||
throw new TypeError('date must be a valid Date instance');
|
||||
throw new TypeError("date must be a valid Date instance");
|
||||
}
|
||||
|
||||
// Seconds elapsed since the known new moon anchor
|
||||
|
|
@ -30,5 +30,5 @@ export function cycleMonth(date: Date = new Date()): string {
|
|||
// Map to 1-indexed image number: 1 to MONTH_IMAGES
|
||||
const index = Math.floor(fraction * MONTH_IMAGES) + 1;
|
||||
|
||||
return index.toString().padStart(3, '0') + '.webp';
|
||||
return index.toString().padStart(3, "0") + ".webp";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { YEAR_IMAGES, YEAR_ANCHOR } from './types.js';
|
||||
import { YEAR_IMAGES, YEAR_ANCHOR } from "./types.js";
|
||||
|
||||
/**
|
||||
* Maps a date to the corresponding NASA moon image for the yearly cycle.
|
||||
|
|
@ -16,7 +16,7 @@ import { YEAR_IMAGES, YEAR_ANCHOR } from './types.js';
|
|||
*/
|
||||
export function cycleYear(date: Date = new Date()): string {
|
||||
if (!(date instanceof Date) || isNaN(date.getTime())) {
|
||||
throw new TypeError('date must be a valid Date instance');
|
||||
throw new TypeError("date must be a valid Date instance");
|
||||
}
|
||||
|
||||
// Hours elapsed since 2023-01-01T00:00:00Z
|
||||
|
|
@ -31,5 +31,5 @@ export function cycleYear(date: Date = new Date()): string {
|
|||
// Map to 1-indexed image number: 1 to YEAR_IMAGES
|
||||
const index = Math.floor(fraction * YEAR_IMAGES) + 1;
|
||||
|
||||
return index.toString().padStart(4, '0') + '.webp';
|
||||
return index.toString().padStart(4, "0") + ".webp";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ImageSet, ImageSize, ImageQuality } from './types.js';
|
||||
import { ImageSet, ImageSize, ImageQuality } from "./types.js";
|
||||
|
||||
/**
|
||||
* Returns the folder name for a given image set, size, and quality.
|
||||
|
|
@ -38,7 +38,7 @@ export function cdnUrl(
|
|||
set: ImageSet,
|
||||
size: ImageSize,
|
||||
quality: ImageQuality,
|
||||
ref: string = 'main',
|
||||
ref: string = "main",
|
||||
): string {
|
||||
const folder = imageFolder(set, size, quality);
|
||||
return `https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@${ref}/${folder}/${filename}`;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
export { cycleMonth } from './cycleMonth.js';
|
||||
export { cycleYear } from './cycleYear.js';
|
||||
export { cycleMonth } from "./cycleMonth.js";
|
||||
export { cycleYear } from "./cycleYear.js";
|
||||
export {
|
||||
ImageSet,
|
||||
ImageSize,
|
||||
|
|
@ -9,5 +9,5 @@ export {
|
|||
YEAR_IMAGES,
|
||||
MONTH_ANCHOR,
|
||||
YEAR_ANCHOR,
|
||||
} from './types.js';
|
||||
export { imageFolder, cdnUrl } from './helpers.js';
|
||||
} from "./types.js";
|
||||
export { imageFolder, cdnUrl } from "./helpers.js";
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* import type { ImageSet } from 'moon-cycle';
|
||||
* const set: ImageSet = 'mm'; // monthly synodic cycle
|
||||
*/
|
||||
export type ImageSet = 'mm' | 'my';
|
||||
export type ImageSet = "mm" | "my";
|
||||
|
||||
/**
|
||||
* Image dimension in pixels (square).
|
||||
|
|
@ -86,7 +86,7 @@ export const YEAR_IMAGES = 8760;
|
|||
* import { MONTH_ANCHOR } from 'moon-cycle';
|
||||
* const ageMs = Date.now() - MONTH_ANCHOR.getTime();
|
||||
*/
|
||||
export const MONTH_ANCHOR = new Date('2023-11-13T09:27:00Z');
|
||||
export const MONTH_ANCHOR = new Date("2023-11-13T09:27:00Z");
|
||||
|
||||
/**
|
||||
* Anchor date for the yearly cycle: start of the 2023 NASA image collection.
|
||||
|
|
@ -98,4 +98,4 @@ export const MONTH_ANCHOR = new Date('2023-11-13T09:27:00Z');
|
|||
* import { YEAR_ANCHOR } from 'moon-cycle';
|
||||
* const hours = (Date.now() - YEAR_ANCHOR.getTime()) / 3600000;
|
||||
*/
|
||||
export const YEAR_ANCHOR = new Date('2023-01-01T00:00:00Z');
|
||||
export const YEAR_ANCHOR = new Date("2023-01-01T00:00:00Z");
|
||||
|
|
|
|||
64
test-cjs.cjs
64
test-cjs.cjs
|
|
@ -3,10 +3,10 @@
|
|||
* Verifies CommonJS compatibility of the built package.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { describe, it } = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const {
|
||||
cycleMonth,
|
||||
cycleYear,
|
||||
|
|
@ -17,59 +17,59 @@ const {
|
|||
YEAR_IMAGES,
|
||||
MONTH_ANCHOR,
|
||||
YEAR_ANCHOR,
|
||||
} = require('./dist/index.cjs');
|
||||
} = require("./dist/index.cjs");
|
||||
|
||||
describe('CJS exports', () => {
|
||||
it('all exports are available via require()', () => {
|
||||
assert.strictEqual(typeof cycleMonth, 'function');
|
||||
assert.strictEqual(typeof cycleYear, 'function');
|
||||
assert.strictEqual(typeof imageFolder, 'function');
|
||||
assert.strictEqual(typeof cdnUrl, 'function');
|
||||
assert.strictEqual(typeof SYNODIC_MONTH, 'number');
|
||||
assert.strictEqual(typeof MONTH_IMAGES, 'number');
|
||||
assert.strictEqual(typeof YEAR_IMAGES, 'number');
|
||||
describe("CJS exports", () => {
|
||||
it("all exports are available via require()", () => {
|
||||
assert.strictEqual(typeof cycleMonth, "function");
|
||||
assert.strictEqual(typeof cycleYear, "function");
|
||||
assert.strictEqual(typeof imageFolder, "function");
|
||||
assert.strictEqual(typeof cdnUrl, "function");
|
||||
assert.strictEqual(typeof SYNODIC_MONTH, "number");
|
||||
assert.strictEqual(typeof MONTH_IMAGES, "number");
|
||||
assert.strictEqual(typeof YEAR_IMAGES, "number");
|
||||
assert(MONTH_ANCHOR instanceof Date);
|
||||
assert(YEAR_ANCHOR instanceof Date);
|
||||
});
|
||||
|
||||
it('cycleMonth at anchor returns 001.webp', () => {
|
||||
assert.strictEqual(cycleMonth(MONTH_ANCHOR), '001.webp');
|
||||
it("cycleMonth at anchor returns 001.webp", () => {
|
||||
assert.strictEqual(cycleMonth(MONTH_ANCHOR), "001.webp");
|
||||
});
|
||||
|
||||
it('cycleYear at anchor returns 0001.webp', () => {
|
||||
assert.strictEqual(cycleYear(YEAR_ANCHOR), '0001.webp');
|
||||
it("cycleYear at anchor returns 0001.webp", () => {
|
||||
assert.strictEqual(cycleYear(YEAR_ANCHOR), "0001.webp");
|
||||
});
|
||||
|
||||
it('cycleMonth result format is correct', () => {
|
||||
it("cycleMonth result format is correct", () => {
|
||||
assert.match(cycleMonth(), /^\d{3}\.webp$/);
|
||||
});
|
||||
|
||||
it('cycleYear result format is correct', () => {
|
||||
it("cycleYear result format is correct", () => {
|
||||
assert.match(cycleYear(), /^\d{4}\.webp$/);
|
||||
});
|
||||
|
||||
it('imageFolder constructs correct path', () => {
|
||||
assert.strictEqual(imageFolder('mm', 256, 75), 'mm-256-75');
|
||||
it("imageFolder constructs correct path", () => {
|
||||
assert.strictEqual(imageFolder("mm", 256, 75), "mm-256-75");
|
||||
});
|
||||
|
||||
it('cdnUrl returns expected jsDelivr URL', () => {
|
||||
it("cdnUrl returns expected jsDelivr URL", () => {
|
||||
assert.strictEqual(
|
||||
cdnUrl('001.webp', 'mm', 256, 75),
|
||||
'https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@main/mm-256-75/001.webp',
|
||||
cdnUrl("001.webp", "mm", 256, 75),
|
||||
"https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@main/mm-256-75/001.webp",
|
||||
);
|
||||
});
|
||||
|
||||
it('cycleMonth throws TypeError for invalid Date', () => {
|
||||
assert.throws(() => cycleMonth(new Date('invalid')), {
|
||||
name: 'TypeError',
|
||||
message: 'date must be a valid Date instance',
|
||||
it("cycleMonth throws TypeError for invalid Date", () => {
|
||||
assert.throws(() => cycleMonth(new Date("invalid")), {
|
||||
name: "TypeError",
|
||||
message: "date must be a valid Date instance",
|
||||
});
|
||||
});
|
||||
|
||||
it('cycleYear throws TypeError for invalid Date', () => {
|
||||
assert.throws(() => cycleYear(new Date('invalid')), {
|
||||
name: 'TypeError',
|
||||
message: 'date must be a valid Date instance',
|
||||
it("cycleYear throws TypeError for invalid Date", () => {
|
||||
assert.throws(() => cycleYear(new Date("invalid")), {
|
||||
name: "TypeError",
|
||||
message: "date must be a valid Date instance",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
164
test.mjs
164
test.mjs
|
|
@ -3,8 +3,8 @@
|
|||
* Uses Node.js built-in test runner.
|
||||
*/
|
||||
|
||||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
cycleMonth,
|
||||
cycleYear,
|
||||
|
|
@ -15,213 +15,213 @@ import {
|
|||
YEAR_IMAGES,
|
||||
MONTH_ANCHOR,
|
||||
YEAR_ANCHOR,
|
||||
} from './dist/index.mjs';
|
||||
} from "./dist/index.mjs";
|
||||
|
||||
// ─── Constants ───────────────────────────────────────────────────────────────
|
||||
|
||||
describe('constants', () => {
|
||||
it('SYNODIC_MONTH is correct IAU value', () => {
|
||||
describe("constants", () => {
|
||||
it("SYNODIC_MONTH is correct IAU value", () => {
|
||||
assert.strictEqual(SYNODIC_MONTH, 29.53058821398858);
|
||||
});
|
||||
|
||||
it('MONTH_IMAGES is 708', () => {
|
||||
it("MONTH_IMAGES is 708", () => {
|
||||
assert.strictEqual(MONTH_IMAGES, 708);
|
||||
});
|
||||
|
||||
it('YEAR_IMAGES is 8760', () => {
|
||||
it("YEAR_IMAGES is 8760", () => {
|
||||
assert.strictEqual(YEAR_IMAGES, 8760);
|
||||
});
|
||||
|
||||
it('MONTH_ANCHOR is 2023-11-13T09:27:00Z', () => {
|
||||
assert.strictEqual(MONTH_ANCHOR.toISOString(), '2023-11-13T09:27:00.000Z');
|
||||
it("MONTH_ANCHOR is 2023-11-13T09:27:00Z", () => {
|
||||
assert.strictEqual(MONTH_ANCHOR.toISOString(), "2023-11-13T09:27:00.000Z");
|
||||
});
|
||||
|
||||
it('YEAR_ANCHOR is 2023-01-01T00:00:00Z', () => {
|
||||
assert.strictEqual(YEAR_ANCHOR.toISOString(), '2023-01-01T00:00:00.000Z');
|
||||
it("YEAR_ANCHOR is 2023-01-01T00:00:00Z", () => {
|
||||
assert.strictEqual(YEAR_ANCHOR.toISOString(), "2023-01-01T00:00:00.000Z");
|
||||
});
|
||||
});
|
||||
|
||||
// ─── cycleMonth ──────────────────────────────────────────────────────────────
|
||||
|
||||
describe('cycleMonth', () => {
|
||||
it('returns a string', () => {
|
||||
assert.strictEqual(typeof cycleMonth(), 'string');
|
||||
describe("cycleMonth", () => {
|
||||
it("returns a string", () => {
|
||||
assert.strictEqual(typeof cycleMonth(), "string");
|
||||
});
|
||||
|
||||
it('result matches /^\\d{3}\\.webp$/', () => {
|
||||
it("result matches /^\\d{3}\\.webp$/", () => {
|
||||
assert.match(cycleMonth(), /^\d{3}\.webp$/);
|
||||
});
|
||||
|
||||
it('result is never 000.webp (images are 1-indexed)', () => {
|
||||
it("result is never 000.webp (images are 1-indexed)", () => {
|
||||
const anchor = MONTH_ANCHOR.getTime();
|
||||
const synodic_ms = SYNODIC_MONTH * 24 * 60 * 60 * 1000;
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const date = new Date(anchor + i * (synodic_ms / 100));
|
||||
assert.notStrictEqual(cycleMonth(date), '000.webp', `Got 000.webp for offset ${i}`);
|
||||
assert.notStrictEqual(cycleMonth(date), "000.webp", `Got 000.webp for offset ${i}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('at anchor date returns 001.webp (start of synodic cycle)', () => {
|
||||
assert.strictEqual(cycleMonth(MONTH_ANCHOR), '001.webp');
|
||||
it("at anchor date returns 001.webp (start of synodic cycle)", () => {
|
||||
assert.strictEqual(cycleMonth(MONTH_ANCHOR), "001.webp");
|
||||
});
|
||||
|
||||
it('one synodic month + 1 min after anchor returns near start of next cycle', () => {
|
||||
it("one synodic month + 1 min after anchor returns near start of next cycle", () => {
|
||||
const oneMonthPlus = new Date(
|
||||
MONTH_ANCHOR.getTime() + SYNODIC_MONTH * 24 * 60 * 60 * 1000 + 60_000,
|
||||
);
|
||||
const index = parseInt(cycleMonth(oneMonthPlus).replace('.webp', ''), 10);
|
||||
const index = parseInt(cycleMonth(oneMonthPlus).replace(".webp", ""), 10);
|
||||
assert(index <= 3, `Expected near start of next cycle (index <= 3), got ${index}`);
|
||||
});
|
||||
|
||||
it('at halfway through synodic month returns ~354.webp', () => {
|
||||
it("at halfway through synodic month returns ~354.webp", () => {
|
||||
const half = new Date(MONTH_ANCHOR.getTime() + (SYNODIC_MONTH / 2) * 24 * 60 * 60 * 1000);
|
||||
const index = parseInt(cycleMonth(half).replace('.webp', ''), 10);
|
||||
const index = parseInt(cycleMonth(half).replace(".webp", ""), 10);
|
||||
assert(index >= 353 && index <= 355, `Expected ~354, got ${index}`);
|
||||
});
|
||||
|
||||
it('result is always in range [001, 708]', () => {
|
||||
const start = new Date('2020-01-01T00:00:00Z');
|
||||
it("result is always in range [001, 708]", () => {
|
||||
const start = new Date("2020-01-01T00:00:00Z");
|
||||
const step = 24 * 60 * 60 * 1000 * 7;
|
||||
for (let i = 0; i < 260; i++) {
|
||||
const date = new Date(start.getTime() + i * step);
|
||||
const index = parseInt(cycleMonth(date).replace('.webp', ''), 10);
|
||||
const index = parseInt(cycleMonth(date).replace(".webp", ""), 10);
|
||||
assert(index >= 1, `Index ${index} below minimum (date: ${date.toISOString()})`);
|
||||
assert(index <= 708, `Index ${index} above maximum (date: ${date.toISOString()})`);
|
||||
}
|
||||
});
|
||||
|
||||
it('handles dates before the anchor (pre-2023)', () => {
|
||||
const past = new Date('2020-06-15T00:00:00Z');
|
||||
it("handles dates before the anchor (pre-2023)", () => {
|
||||
const past = new Date("2020-06-15T00:00:00Z");
|
||||
const result = cycleMonth(past);
|
||||
assert.match(result, /^\d{3}\.webp$/);
|
||||
const index = parseInt(result.replace('.webp', ''), 10);
|
||||
const index = parseInt(result.replace(".webp", ""), 10);
|
||||
assert(index >= 1 && index <= 708);
|
||||
});
|
||||
|
||||
it('with no args returns a valid result', () => {
|
||||
it("with no args returns a valid result", () => {
|
||||
assert.match(cycleMonth(), /^\d{3}\.webp$/);
|
||||
});
|
||||
|
||||
it('throws TypeError for invalid Date', () => {
|
||||
assert.throws(() => cycleMonth(new Date('invalid')), {
|
||||
name: 'TypeError',
|
||||
message: 'date must be a valid Date instance',
|
||||
it("throws TypeError for invalid Date", () => {
|
||||
assert.throws(() => cycleMonth(new Date("invalid")), {
|
||||
name: "TypeError",
|
||||
message: "date must be a valid Date instance",
|
||||
});
|
||||
});
|
||||
|
||||
it('throws TypeError for non-Date input', () => {
|
||||
assert.throws(() => cycleMonth('2023-01-01'), {
|
||||
name: 'TypeError',
|
||||
message: 'date must be a valid Date instance',
|
||||
it("throws TypeError for non-Date input", () => {
|
||||
assert.throws(() => cycleMonth("2023-01-01"), {
|
||||
name: "TypeError",
|
||||
message: "date must be a valid Date instance",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ─── cycleYear ───────────────────────────────────────────────────────────────
|
||||
|
||||
describe('cycleYear', () => {
|
||||
it('returns a string', () => {
|
||||
assert.strictEqual(typeof cycleYear(), 'string');
|
||||
describe("cycleYear", () => {
|
||||
it("returns a string", () => {
|
||||
assert.strictEqual(typeof cycleYear(), "string");
|
||||
});
|
||||
|
||||
it('result matches /^\\d{4}\\.webp$/', () => {
|
||||
it("result matches /^\\d{4}\\.webp$/", () => {
|
||||
assert.match(cycleYear(), /^\d{4}\.webp$/);
|
||||
});
|
||||
|
||||
it('result is never 0000.webp (images are 1-indexed)', () => {
|
||||
it("result is never 0000.webp (images are 1-indexed)", () => {
|
||||
const anchor = YEAR_ANCHOR.getTime();
|
||||
const year_ms = YEAR_IMAGES * 60 * 60 * 1000;
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const date = new Date(anchor + i * (year_ms / 100));
|
||||
assert.notStrictEqual(cycleYear(date), '0000.webp', `Got 0000.webp for offset ${i}`);
|
||||
assert.notStrictEqual(cycleYear(date), "0000.webp", `Got 0000.webp for offset ${i}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('at anchor date returns 0001.webp (start of year)', () => {
|
||||
assert.strictEqual(cycleYear(YEAR_ANCHOR), '0001.webp');
|
||||
it("at anchor date returns 0001.webp (start of year)", () => {
|
||||
assert.strictEqual(cycleYear(YEAR_ANCHOR), "0001.webp");
|
||||
});
|
||||
|
||||
it('at exactly one year after anchor returns 0001.webp', () => {
|
||||
it("at exactly one year after anchor returns 0001.webp", () => {
|
||||
const oneYear = new Date(YEAR_ANCHOR.getTime() + YEAR_IMAGES * 60 * 60 * 1000);
|
||||
assert.strictEqual(cycleYear(oneYear), '0001.webp');
|
||||
assert.strictEqual(cycleYear(oneYear), "0001.webp");
|
||||
});
|
||||
|
||||
it('at halfway through year returns ~4380.webp', () => {
|
||||
it("at halfway through year returns ~4380.webp", () => {
|
||||
const half = new Date(YEAR_ANCHOR.getTime() + (YEAR_IMAGES / 2) * 60 * 60 * 1000);
|
||||
const index = parseInt(cycleYear(half).replace('.webp', ''), 10);
|
||||
const index = parseInt(cycleYear(half).replace(".webp", ""), 10);
|
||||
assert(index >= 4379 && index <= 4381, `Expected ~4380, got ${index}`);
|
||||
});
|
||||
|
||||
it('result is always in range [0001, 8760]', () => {
|
||||
const start = new Date('2020-01-01T00:00:00Z');
|
||||
it("result is always in range [0001, 8760]", () => {
|
||||
const start = new Date("2020-01-01T00:00:00Z");
|
||||
const step = 24 * 60 * 60 * 1000 * 7;
|
||||
for (let i = 0; i < 260; i++) {
|
||||
const date = new Date(start.getTime() + i * step);
|
||||
const index = parseInt(cycleYear(date).replace('.webp', ''), 10);
|
||||
const index = parseInt(cycleYear(date).replace(".webp", ""), 10);
|
||||
assert(index >= 1, `Index ${index} below minimum`);
|
||||
assert(index <= 8760, `Index ${index} above maximum`);
|
||||
}
|
||||
});
|
||||
|
||||
it('handles dates before 2023', () => {
|
||||
const past = new Date('2021-06-15T00:00:00Z');
|
||||
it("handles dates before 2023", () => {
|
||||
const past = new Date("2021-06-15T00:00:00Z");
|
||||
const result = cycleYear(past);
|
||||
assert.match(result, /^\d{4}\.webp$/);
|
||||
const index = parseInt(result.replace('.webp', ''), 10);
|
||||
const index = parseInt(result.replace(".webp", ""), 10);
|
||||
assert(index >= 1 && index <= 8760);
|
||||
});
|
||||
|
||||
it('with no args returns a valid result', () => {
|
||||
it("with no args returns a valid result", () => {
|
||||
assert.match(cycleYear(), /^\d{4}\.webp$/);
|
||||
});
|
||||
|
||||
it('throws TypeError for invalid Date', () => {
|
||||
assert.throws(() => cycleYear(new Date('invalid')), {
|
||||
name: 'TypeError',
|
||||
message: 'date must be a valid Date instance',
|
||||
it("throws TypeError for invalid Date", () => {
|
||||
assert.throws(() => cycleYear(new Date("invalid")), {
|
||||
name: "TypeError",
|
||||
message: "date must be a valid Date instance",
|
||||
});
|
||||
});
|
||||
|
||||
it('throws TypeError for non-Date input', () => {
|
||||
assert.throws(() => cycleYear('2023-01-01'), {
|
||||
name: 'TypeError',
|
||||
message: 'date must be a valid Date instance',
|
||||
it("throws TypeError for non-Date input", () => {
|
||||
assert.throws(() => cycleYear("2023-01-01"), {
|
||||
name: "TypeError",
|
||||
message: "date must be a valid Date instance",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ─── imageFolder ─────────────────────────────────────────────────────────────
|
||||
|
||||
describe('imageFolder', () => {
|
||||
it('returns correct folder name', () => {
|
||||
assert.strictEqual(imageFolder('mm', 256, 75), 'mm-256-75');
|
||||
assert.strictEqual(imageFolder('mm', 512, 85), 'mm-512-85');
|
||||
assert.strictEqual(imageFolder('my', 256, 75), 'my-256-75');
|
||||
assert.strictEqual(imageFolder('my', 512, 85), 'my-512-85');
|
||||
describe("imageFolder", () => {
|
||||
it("returns correct folder name", () => {
|
||||
assert.strictEqual(imageFolder("mm", 256, 75), "mm-256-75");
|
||||
assert.strictEqual(imageFolder("mm", 512, 85), "mm-512-85");
|
||||
assert.strictEqual(imageFolder("my", 256, 75), "my-256-75");
|
||||
assert.strictEqual(imageFolder("my", 512, 85), "my-512-85");
|
||||
});
|
||||
});
|
||||
|
||||
// ─── cdnUrl ──────────────────────────────────────────────────────────────────
|
||||
|
||||
describe('cdnUrl', () => {
|
||||
it('returns a valid jsDelivr URL', () => {
|
||||
describe("cdnUrl", () => {
|
||||
it("returns a valid jsDelivr URL", () => {
|
||||
assert.strictEqual(
|
||||
cdnUrl('354.webp', 'mm', 256, 75),
|
||||
'https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@main/mm-256-75/354.webp',
|
||||
cdnUrl("354.webp", "mm", 256, 75),
|
||||
"https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@main/mm-256-75/354.webp",
|
||||
);
|
||||
});
|
||||
|
||||
it('respects custom ref parameter', () => {
|
||||
it("respects custom ref parameter", () => {
|
||||
assert.strictEqual(
|
||||
cdnUrl('001.webp', 'my', 512, 85, 'v2.0.0'),
|
||||
'https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@v2.0.0/my-512-85/001.webp',
|
||||
cdnUrl("001.webp", "my", 512, 85, "v2.0.0"),
|
||||
"https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@v2.0.0/my-512-85/001.webp",
|
||||
);
|
||||
});
|
||||
|
||||
it('integrates with cycleMonth output', () => {
|
||||
it("integrates with cycleMonth output", () => {
|
||||
const filename = cycleMonth(MONTH_ANCHOR);
|
||||
const url = cdnUrl(filename, 'mm', 256, 75);
|
||||
assert(url.startsWith('https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@main/mm-256-75/'));
|
||||
assert(url.endsWith('.webp'));
|
||||
const url = cdnUrl(filename, "mm", 256, 75);
|
||||
assert(url.startsWith("https://cdn.jsdelivr.net/gh/acamarata/moon-cycle@main/mm-256-75/"));
|
||||
assert(url.endsWith(".webp"));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { defineConfig } from 'tsup';
|
||||
import { defineConfig } from "tsup";
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
format: ['cjs', 'esm'],
|
||||
entry: ["src/index.ts"],
|
||||
format: ["cjs", "esm"],
|
||||
dts: true,
|
||||
clean: true,
|
||||
outDir: 'dist',
|
||||
outDir: "dist",
|
||||
splitting: false,
|
||||
sourcemap: true,
|
||||
target: 'es2020',
|
||||
platform: 'neutral',
|
||||
outExtension: ({ format }) => ({ js: format === 'cjs' ? '.cjs' : '.mjs' }),
|
||||
target: "es2020",
|
||||
platform: "neutral",
|
||||
outExtension: ({ format }) => ({ js: format === "cjs" ? ".cjs" : ".mjs" }),
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue