mirror of
https://github.com/acamarata/dayjs-hijri-plus.git
synced 2026-07-01 19:24:26 +00:00
99 lines
4.1 KiB
Markdown
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)
|