dayjs-hijri-plus/.github/wiki/Architecture.md

99 lines
4.1 KiB
Markdown

# Architecture
## Design Philosophy
dayjs-hijri-plus contains no Hijri calendar arithmetic. Every conversion delegates to [hijri-core](https://github.com/acamarata/hijri-core), which provides a pluggable engine registry with UAQ and FCNA built in.
This separation is deliberate. Calendar algorithms are complex, have known edge cases, and require dedicated testing. Keeping them in hijri-core means both this plugin and future adapters (for Temporal, date-fns, etc.) share a single, well-tested core.
## Plugin Structure
```
src/
index.ts Plugin entry: registers methods on dayjsClass and dayjsFactory
types.ts Type definitions and module augmentation for dayjs
```
The plugin follows the standard Day.js `PluginFunc` signature:
```ts
const plugin: PluginFunc = (_option, dayjsClass, dayjsFactory) => { ... };
```
- `dayjsClass.prototype.*`: instance methods (`.toHijri`, `.formatHijri`, etc.)
- `(dayjsFactory as any).fromHijri`: static method added to the factory function
## Peer Dependencies
Both `dayjs` and `hijri-core` are peer dependencies. This means:
1. The host application controls which version of `dayjs` is used. No version conflict possible.
2. The host application controls which version of `hijri-core` is used. If hijri-core ships updated tables covering new years, the plugin benefits automatically.
3. The plugin itself has zero runtime dependencies in `node_modules`, only peer resolutions.
## Format Token Resolution
`formatHijri` works in two passes:
**Pass 1:** Replace Hijri tokens using a single regex sweep over the format string.
```ts
const HIJRI_TOKEN_RE = /iYYYY|iYY|iMMMM|iMMM|iMM|iM|iDD|iD|iEEEE|iEEE|iE|ioooo|iooo/g;
```
Tokens are listed longest-first in the alternation. This prevents `iYY` from matching before `iYYYY`, and `iMM` from matching before `iMMMM`. The regex engine tries alternatives left-to-right at each position, so ordering is the only safeguard needed.
**Pass 2:** The modified string is passed to `this.format(result)`. Day.js resolves all remaining tokens (YYYY, MM, DD, HH, mm, ss, etc.) and square-bracket escapes (`[literal]`).
This means Hijri tokens and Gregorian tokens can coexist in the same format string. For example, `'iYYYY YYYY'` produces `'1444 2023'`.
## Weekday Alignment
Day.js `.day()` returns `0` for Sunday through `6` for Saturday, the same convention as `Date.prototype.getDay()`.
The weekday arrays exported by hijri-core (`hwLong`, `hwShort`, `hwNumeric`) use the same index layout: index `0` = Sunday, index `6` = Saturday. So `hwLong[this.day()]` always yields the correct weekday name with no offset arithmetic.
## fromHijri Error Handling
`dayjs.fromHijri` calls `toGregorian` from hijri-core. If the Hijri date is invalid or outside the table range, `toGregorian` returns `null`. The plugin converts that into a thrown `Error` with the specific Hijri components included in the message, so callers get a useful diagnostic rather than a null-dereference downstream.
## Calendar Extension
The registry is global within a process. Registering a custom calendar once makes it available to all plugin method calls:
```ts
import { registerCalendar } from 'dayjs-hijri-plus';
registerCalendar('tabular', tabularEngine);
dayjs('2023-03-23').toHijri({ calendar: 'tabular' });
```
Custom engines must implement the `CalendarEngine` interface from hijri-core:
```ts
interface CalendarEngine {
readonly id: string;
toHijri(date: Date): HijriDate | null;
toGregorian(hy: number, hm: number, hd: number): Date | null;
isValid(hy: number, hm: number, hd: number): boolean;
daysInMonth(hy: number, hm: number): number;
}
```
## Build
The package ships a dual CJS/ESM build via tsup. Both `dayjs` and `hijri-core` are marked as `external`, so they are never bundled. Consumers provide them via peer dependency resolution.
Output:
| File | Format |
| ------------------ | ------------------------------- |
| `dist/index.cjs` | CommonJS (Node `require`) |
| `dist/index.mjs` | ESM (`import`) |
| `dist/index.d.ts` | TypeScript declarations for CJS |
| `dist/index.d.mts` | TypeScript declarations for ESM |
---
[Home](Home) | [API Reference](API-Reference)