ci: fix eslint config, add missing ts-eslint devDeps, format src

- Add @typescript-eslint/parser and @typescript-eslint/eslint-plugin to
  devDependencies (required by eslint.config.mjs direct imports and by
  @acamarata/eslint-config peerDependencies)
- Fix eslint.config.mjs: scope files to src/**/*.ts, add parserOptions.project
  for type-aware rules, expand ignores to cover coverage/ and docs/
- Run prettier --write src/ to fix format:check failures
This commit is contained in:
Aric Camarata 2026-05-31 08:47:31 -04:00
parent 680bc72c19
commit 6caa9eed2c
10 changed files with 95 additions and 80 deletions

View file

@ -5,12 +5,19 @@ import { typescript } from '@acamarata/eslint-config';
export default [ export default [
{ {
plugins: { '@typescript-eslint': tsPlugin }, ignores: ['dist/**', 'node_modules/**', 'coverage/**', 'docs/**'],
languageOptions: { parser: tsParser },
}, },
...typescript,
eslintConfigPrettier,
{ {
ignores: ['dist/', 'node_modules/', '*.cjs', '*.mjs'], files: ['src/**/*.ts'],
plugins: { '@typescript-eslint': tsPlugin },
languageOptions: {
parser: tsParser,
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: import.meta.dirname,
},
},
}, },
...typescript.map((cfg) => ({ ...cfg, files: ['src/**/*.ts'] })),
eslintConfigPrettier,
]; ];

View file

@ -60,6 +60,8 @@
"@acamarata/tsconfig": "^0.1.0", "@acamarata/tsconfig": "^0.1.0",
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@types/node": "^22.15.0", "@types/node": "^22.15.0",
"@typescript-eslint/eslint-plugin": "^8.56.1",
"@typescript-eslint/parser": "^8.56.1",
"c8": "^10.1.0", "c8": "^10.1.0",
"eslint": "^10.0.3", "eslint": "^10.0.3",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",

View file

@ -23,6 +23,12 @@ importers:
'@types/node': '@types/node':
specifier: ^22.15.0 specifier: ^22.15.0
version: 22.15.0 version: 22.15.0
'@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: c8:
specifier: ^10.1.0 specifier: ^10.1.0
version: 10.1.3 version: 10.1.3

View file

@ -1,4 +1,4 @@
import type { HijriYearRecord } from '../types'; import type { HijriYearRecord } from "../types";
// Umm al-Qura reference table: Hijri years 1318-1501. // Umm al-Qura reference table: Hijri years 1318-1501.
// Each entry records the 1 Muharram Gregorian date and a 12-bit days-per-month // Each entry records the 1 Muharram Gregorian date and a 12-bit days-per-month

View file

@ -7,9 +7,9 @@
// New moon times come from Jean Meeus, Astronomical Algorithms (2nd ed.), // New moon times come from Jean Meeus, Astronomical Algorithms (2nd ed.),
// Chapter 49, accurate to within a few minutes for 1000-3000 CE. // Chapter 49, accurate to within a few minutes for 1000-3000 CE.
import { hDatesTable } from '../data/hDates'; import { hDatesTable } from "../data/hDates";
import { MS_PER_DAY, MONTHS_PER_YEAR } from '../constants'; import { MS_PER_DAY, MONTHS_PER_YEAR } from "../constants";
import type { CalendarEngine, HijriDate } from '../types'; import type { CalendarEngine, HijriDate } from "../types";
// ─── Constants ─────────────────────────────────────────────────────────────── // ─── Constants ───────────────────────────────────────────────────────────────
@ -211,7 +211,7 @@ function fcnaDaysInMonth(hy: number, hm: number): number {
function fcnaToHijri(gregorianDate: Date): HijriDate | null { function fcnaToHijri(gregorianDate: Date): HijriDate | null {
if (!(gregorianDate instanceof Date) || isNaN(gregorianDate.getTime())) { if (!(gregorianDate instanceof Date) || isNaN(gregorianDate.getTime())) {
throw new Error('Invalid Gregorian date'); throw new Error("Invalid Gregorian date");
} }
// FCNA criterion is UTC-based, so UTC date components ensure correct round-trips. // FCNA criterion is UTC-based, so UTC date components ensure correct round-trips.
@ -271,7 +271,7 @@ function fcnaIsValid(hy: number, hm: number, hd: number): boolean {
// ─── Engine export ──────────────────────────────────────────────────────────── // ─── Engine export ────────────────────────────────────────────────────────────
export const fcnaEngine: CalendarEngine = { export const fcnaEngine: CalendarEngine = {
id: 'fcna', id: "fcna",
toHijri: fcnaToHijri, toHijri: fcnaToHijri,
toGregorian: fcnaToGregorian, toGregorian: fcnaToGregorian,
isValid: fcnaIsValid, isValid: fcnaIsValid,

View file

@ -4,9 +4,9 @@
// (Gregorian 1900-2076). Each entry records the Gregorian date of 1 Muharram and // (Gregorian 1900-2076). Each entry records the Gregorian date of 1 Muharram and
// a 12-bit days-per-month bitmask. Dates outside that window return null. // a 12-bit days-per-month bitmask. Dates outside that window return null.
import { hDatesTable } from '../data/hDates'; import { hDatesTable } from "../data/hDates";
import { MS_PER_DAY, MONTHS_PER_YEAR } from '../constants'; import { MS_PER_DAY, MONTHS_PER_YEAR } from "../constants";
import type { CalendarEngine, HijriDate, HijriYearRecord } from '../types'; import type { CalendarEngine, HijriDate, HijriYearRecord } from "../types";
/** /**
* Binary search for a Hijri year entry in the UAQ table. * Binary search for a Hijri year entry in the UAQ table.
@ -38,7 +38,7 @@ function findYearEntry(hy: number): HijriYearRecord | null {
// the calendar-date lookup is timezone-safe regardless of the host environment. // the calendar-date lookup is timezone-safe regardless of the host environment.
function uaqToHijri(date: Date): HijriDate | null { function uaqToHijri(date: Date): HijriDate | null {
if (!(date instanceof Date) || isNaN(date.getTime())) { if (!(date instanceof Date) || isNaN(date.getTime())) {
throw new Error('Invalid Gregorian date'); throw new Error("Invalid Gregorian date");
} }
const inputUtc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()); const inputUtc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());
@ -126,7 +126,7 @@ function uaqDaysInMonth(hy: number, hm: number): number {
} }
export const uaqEngine: CalendarEngine = { export const uaqEngine: CalendarEngine = {
id: 'uaq', id: "uaq",
toHijri: uaqToHijri, toHijri: uaqToHijri,
toGregorian: uaqToGregorian, toGregorian: uaqToGregorian,
isValid: uaqIsValid, isValid: uaqIsValid,

View file

@ -1,32 +1,32 @@
// Built-in engines are registered at module load so that 'uaq' and 'fcna' are // Built-in engines are registered at module load so that 'uaq' and 'fcna' are
// available immediately on import. This module-level side effect is intentional // available immediately on import. This module-level side effect is intentional
// and documented in the sideEffects field of package.json. // and documented in the sideEffects field of package.json.
import { uaqEngine } from './engines/uaq'; import { uaqEngine } from "./engines/uaq";
import { fcnaEngine } from './engines/fcna'; import { fcnaEngine } from "./engines/fcna";
import { registerCalendar } from './registry'; import { registerCalendar } from "./registry";
registerCalendar('uaq', uaqEngine); registerCalendar("uaq", uaqEngine);
registerCalendar('fcna', fcnaEngine); registerCalendar("fcna", fcnaEngine);
// Registry // Registry
export { registerCalendar, getCalendar, listCalendars } from './registry'; export { registerCalendar, getCalendar, listCalendars } from "./registry";
// Constants // Constants
export { MS_PER_DAY, MONTHS_PER_YEAR } from './constants'; export { MS_PER_DAY, MONTHS_PER_YEAR } from "./constants";
// Types // Types
export type { HijriDate, HijriYearRecord, CalendarEngine, ConversionOptions } from './types'; export type { HijriDate, HijriYearRecord, CalendarEngine, ConversionOptions } from "./types";
// Data // Data
export { hDatesTable } from './data/hDates'; export { hDatesTable } from "./data/hDates";
// Names // Names
export { hmLong, hmMedium, hmShort } from './names/months'; export { hmLong, hmMedium, hmShort } from "./names/months";
export { hwLong, hwShort, hwNumeric } from './names/weekdays'; export { hwLong, hwShort, hwNumeric } from "./names/weekdays";
// Convenience wrappers // Convenience wrappers
import { getCalendar } from './registry'; import { getCalendar } from "./registry";
import type { HijriDate, ConversionOptions } from './types'; import type { HijriDate, ConversionOptions } from "./types";
/** /**
* Convert a Gregorian date to a Hijri date. * Convert a Gregorian date to a Hijri date.
@ -41,9 +41,9 @@ import type { HijriDate, ConversionOptions } from './types';
*/ */
export function toHijri(date: Date, options?: ConversionOptions): HijriDate | null { export function toHijri(date: Date, options?: ConversionOptions): HijriDate | null {
if (!(date instanceof Date) || isNaN(date.getTime())) { if (!(date instanceof Date) || isNaN(date.getTime())) {
throw new Error('Invalid Gregorian date'); throw new Error("Invalid Gregorian date");
} }
return getCalendar(options?.calendar ?? 'uaq').toHijri(date); return getCalendar(options?.calendar ?? "uaq").toHijri(date);
} }
/** /**
@ -63,7 +63,7 @@ export function toGregorian(
hd: number, hd: number,
options?: ConversionOptions, options?: ConversionOptions,
): Date | null { ): Date | null {
return getCalendar(options?.calendar ?? 'uaq').toGregorian(hy, hm, hd); return getCalendar(options?.calendar ?? "uaq").toGregorian(hy, hm, hd);
} }
/** /**
@ -81,7 +81,7 @@ export function isValidHijriDate(
hd: number, hd: number,
options?: ConversionOptions, options?: ConversionOptions,
): boolean { ): boolean {
return getCalendar(options?.calendar ?? 'uaq').isValid(hy, hm, hd); return getCalendar(options?.calendar ?? "uaq").isValid(hy, hm, hd);
} }
/** /**
@ -94,5 +94,5 @@ export function isValidHijriDate(
* @throws {RangeError} if the month or year is out of range * @throws {RangeError} if the month or year is out of range
*/ */
export function daysInHijriMonth(hy: number, hm: number, options?: ConversionOptions): number { export function daysInHijriMonth(hy: number, hm: number, options?: ConversionOptions): number {
return getCalendar(options?.calendar ?? 'uaq').daysInMonth(hy, hm); return getCalendar(options?.calendar ?? "uaq").daysInMonth(hy, hm);
} }

View file

@ -11,18 +11,18 @@
* const month = hmLong[hijriDate.hm - 1]; // "Ramadan" * const month = hmLong[hijriDate.hm - 1]; // "Ramadan"
*/ */
export const hmLong = [ export const hmLong = [
'Muharram', // 1 "Muharram", // 1
'Safar', // 2 "Safar", // 2
"Rabi'l Awwal", // 3 "Rabi'l Awwal", // 3
"Rabi'l Thani", // 4 "Rabi'l Thani", // 4
'Jumadal Awwal', // 5 "Jumadal Awwal", // 5
'Jumadal Thani', // 6 "Jumadal Thani", // 6
'Rajab', // 7 "Rajab", // 7
"Sha'ban", // 8 "Sha'ban", // 8
'Ramadan', // 9 "Ramadan", // 9
'Shawwal', // 10 "Shawwal", // 10
"Dhul Qi'dah", // 11 "Dhul Qi'dah", // 11
'Dhul Hijjah', // 12 "Dhul Hijjah", // 12
]; ];
/** /**
@ -35,18 +35,18 @@ export const hmLong = [
* const label = hmMedium[hijriDate.hm - 1]; // "Ramadan" * const label = hmMedium[hijriDate.hm - 1]; // "Ramadan"
*/ */
export const hmMedium = [ export const hmMedium = [
'Muharram', "Muharram",
'Safar', "Safar",
'Rabi1', "Rabi1",
'Rabi2', "Rabi2",
'Jumada1', "Jumada1",
'Jumada2', "Jumada2",
'Rajab', "Rajab",
'Shaban', "Shaban",
'Ramadan', "Ramadan",
'Shawwal', "Shawwal",
'Dhul-Qidah', "Dhul-Qidah",
'Dhul-Hijjah', "Dhul-Hijjah",
]; ];
/** /**
@ -59,16 +59,16 @@ export const hmMedium = [
* const abbr = hmShort[hijriDate.hm - 1]; // "Ram" * const abbr = hmShort[hijriDate.hm - 1]; // "Ram"
*/ */
export const hmShort = [ export const hmShort = [
'Muh', "Muh",
'Saf', "Saf",
'Ra1', "Ra1",
'Ra2', "Ra2",
'Ju1', "Ju1",
'Ju2', "Ju2",
'Raj', "Raj",
'Shb', "Shb",
'Ram', "Ram",
'Shw', "Shw",
'DhQ', "DhQ",
'DhH', "DhH",
]; ];

View file

@ -11,13 +11,13 @@
* const dayName = hwLong[gregorianDate.getDay()]; // "Yawm al-Jum`a" * const dayName = hwLong[gregorianDate.getDay()]; // "Yawm al-Jum`a"
*/ */
export const hwLong = [ export const hwLong = [
'Yawm al-Ahad', // Sunday "Yawm al-Ahad", // Sunday
'Yawm al-Ithnayn', // Monday "Yawm al-Ithnayn", // Monday
"Yawm ath-Thulatha'", // Tuesday "Yawm ath-Thulatha'", // Tuesday
"Yawm al-Arba`a'", // Wednesday "Yawm al-Arba`a'", // Wednesday
'Yawm al-Khamis', // Thursday "Yawm al-Khamis", // Thursday
'Yawm al-Jum`a', // Friday "Yawm al-Jum`a", // Friday
'Yawm as-Sabt', // Saturday "Yawm as-Sabt", // Saturday
]; ];
/** /**
@ -30,13 +30,13 @@ export const hwLong = [
* const abbr = hwShort[gregorianDate.getDay()]; // "Jum`a" * const abbr = hwShort[gregorianDate.getDay()]; // "Jum`a"
*/ */
export const hwShort = [ export const hwShort = [
'Ahad', // Sunday "Ahad", // Sunday
'Ithn', // Monday "Ithn", // Monday
'Thul', // Tuesday "Thul", // Tuesday
'Arba', // Wednesday "Arba", // Wednesday
'Kham', // Thursday "Kham", // Thursday
'Jum`a', // Friday "Jum`a", // Friday
'Sabt', // Saturday "Sabt", // Saturday
]; ];
/** /**

View file

@ -1,4 +1,4 @@
import type { CalendarEngine } from './types'; import type { CalendarEngine } from "./types";
const _engines = new Map<string, CalendarEngine>(); const _engines = new Map<string, CalendarEngine>();
@ -25,7 +25,7 @@ export function registerCalendar(name: string, engine: CalendarEngine): void {
export function getCalendar(name: string): CalendarEngine { export function getCalendar(name: string): CalendarEngine {
const engine = _engines.get(name); const engine = _engines.get(name);
if (!engine) { if (!engine) {
const available = listCalendars().join(', '); const available = listCalendars().join(", ");
throw new Error( throw new Error(
`Unknown Hijri calendar: "${name}". Available: ${available}. Register custom calendars with registerCalendar().`, `Unknown Hijri calendar: "${name}". Available: ${available}. Register custom calendars with registerCalendar().`,
); );