mirror of
https://github.com/acamarata/dayjs-hijri-plus.git
synced 2026-06-30 18:54:26 +00:00
feat: initial release of dayjs-hijri-plus v1.0.0
Day.js plugin adding Hijri calendar support via hijri-core. Adds toHijri(), fromHijri(), hijriYear/Month/Day(), isValidHijri(), and formatHijri() to all Day.js instances. Supports UAQ and FCNA calendars via ConversionOptions. Format token escaping wraps substituted values in Day.js bracket syntax to prevent re-interpretation as format tokens. 14 ESM + 8 CJS tests passing. Dual CJS/ESM build.
This commit is contained in:
commit
96dd9c5688
21 changed files with 2082 additions and 0 deletions
14
.editorconfig
Normal file
14
.editorconfig
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{ts,js,mjs,cjs,json,yaml,yml,md}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
62
.github/workflows/ci.yml
vendored
Normal file
62
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test (Node ${{ matrix.node }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: [20, 22, 24]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: pnpm
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm run build
|
||||
- run: node test.mjs
|
||||
- run: node test-cjs.cjs
|
||||
|
||||
typecheck:
|
||||
name: Typecheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm run typecheck
|
||||
|
||||
pack-check:
|
||||
name: Pack check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm run build
|
||||
- name: Verify pack contents
|
||||
run: |
|
||||
npm pack --dry-run 2>&1 | tee pack-output.txt
|
||||
grep "dist/index.cjs" pack-output.txt
|
||||
grep "dist/index.mjs" pack-output.txt
|
||||
grep "dist/index.d.ts" pack-output.txt
|
||||
grep "dist/index.d.mts" pack-output.txt
|
||||
grep "README.md" pack-output.txt
|
||||
grep "CHANGELOG.md" pack-output.txt
|
||||
grep "LICENSE" pack-output.txt
|
||||
36
.github/workflows/wiki-sync.yml
vendored
Normal file
36
.github/workflows/wiki-sync.yml
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
name: Wiki Sync
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- '.wiki/**'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync .wiki/ to GitHub Wiki
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Push wiki pages
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ github.repository }}.wiki
|
||||
path: wiki-repo
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Copy wiki files
|
||||
run: cp .wiki/*.md wiki-repo/
|
||||
|
||||
- name: Commit and push
|
||||
working-directory: wiki-repo
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add -A
|
||||
git diff --cached --quiet || git commit -m "Sync wiki from .wiki/ [skip ci]"
|
||||
git push
|
||||
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
node_modules/
|
||||
dist/
|
||||
*.tgz
|
||||
*.log
|
||||
.DS_Store
|
||||
.claude/
|
||||
.env
|
||||
.env.*
|
||||
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
package-import-method=hardlink
|
||||
1
.nvmrc
Normal file
1
.nvmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
24
|
||||
205
.wiki/API-Reference.md
Normal file
205
.wiki/API-Reference.md
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
# API Reference
|
||||
|
||||
## Setup
|
||||
|
||||
```ts
|
||||
import dayjs from 'dayjs';
|
||||
import hijriPlugin from 'dayjs-hijri-plus';
|
||||
|
||||
dayjs.extend(hijriPlugin);
|
||||
```
|
||||
|
||||
Call `dayjs.extend` once, globally. After that, every Day.js instance has the plugin methods.
|
||||
|
||||
---
|
||||
|
||||
## Instance Methods
|
||||
|
||||
### `.toHijri(opts?)`
|
||||
|
||||
Convert the Day.js date to a Hijri date.
|
||||
|
||||
**Signature:**
|
||||
```ts
|
||||
toHijri(opts?: ConversionOptions): HijriDate | null
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `opts.calendar` | `string` | `'uaq'` | Calendar engine id. Built-ins: `'uaq'`, `'fcna'` |
|
||||
|
||||
**Returns:** `{ hy: number, hm: number, hd: number }` or `null` if the date is outside the table range.
|
||||
|
||||
```ts
|
||||
dayjs('2023-03-23').toHijri();
|
||||
// => { hy: 1444, hm: 9, hd: 1 }
|
||||
|
||||
dayjs('2023-03-23').toHijri({ calendar: 'fcna' });
|
||||
// => { hy: 1444, hm: 9, hd: 2 }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `.isValidHijri(opts?)`
|
||||
|
||||
Check whether the date has a valid Hijri representation in the supported range.
|
||||
|
||||
**Signature:**
|
||||
```ts
|
||||
isValidHijri(opts?: ConversionOptions): boolean
|
||||
```
|
||||
|
||||
Returns `false` for dates outside the coverage range, `true` otherwise.
|
||||
|
||||
---
|
||||
|
||||
### `.hijriYear(opts?)`
|
||||
|
||||
**Signature:**
|
||||
```ts
|
||||
hijriYear(opts?: ConversionOptions): number | null
|
||||
```
|
||||
|
||||
Returns the Hijri year, or `null` if out of range.
|
||||
|
||||
---
|
||||
|
||||
### `.hijriMonth(opts?)`
|
||||
|
||||
**Signature:**
|
||||
```ts
|
||||
hijriMonth(opts?: ConversionOptions): number | null
|
||||
```
|
||||
|
||||
Returns the Hijri month (1-12), or `null` if out of range.
|
||||
|
||||
---
|
||||
|
||||
### `.hijriDay(opts?)`
|
||||
|
||||
**Signature:**
|
||||
```ts
|
||||
hijriDay(opts?: ConversionOptions): number | null
|
||||
```
|
||||
|
||||
Returns the Hijri day (1-30), or `null` if out of range.
|
||||
|
||||
---
|
||||
|
||||
### `.formatHijri(formatStr, opts?)`
|
||||
|
||||
Format the date using a mix of Hijri-specific tokens and standard Day.js tokens.
|
||||
|
||||
**Signature:**
|
||||
```ts
|
||||
formatHijri(formatStr: string, opts?: ConversionOptions): string
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `formatStr` | `string` | Format string containing Hijri tokens, Day.js tokens, or both |
|
||||
| `opts` | `ConversionOptions` | Optional calendar selection |
|
||||
|
||||
Returns an empty string if the date is outside the supported range.
|
||||
|
||||
**Hijri tokens:**
|
||||
|
||||
| Token | Example | Description |
|
||||
| --- | --- | --- |
|
||||
| `iYYYY` | `1444` | 4-digit Hijri year |
|
||||
| `iYY` | `44` | 2-digit Hijri year |
|
||||
| `iMMMM` | `Ramadan` | Full month name |
|
||||
| `iMMM` | `Ramadan` | Medium month name |
|
||||
| `iMM` | `09` | Zero-padded month number |
|
||||
| `iM` | `9` | Month number |
|
||||
| `iDD` | `01` | Zero-padded day |
|
||||
| `iD` | `1` | Day number |
|
||||
| `iEEEE` | `Yawm al-Khamis` | Full weekday name |
|
||||
| `iEEE` | `Kham` | Short weekday name |
|
||||
| `iE` | `5` | Weekday number (1=Sun ... 7=Sat) |
|
||||
| `ioooo` | `AH` | Era |
|
||||
| `iooo` | `AH` | Era (same as ioooo) |
|
||||
|
||||
Standard Day.js tokens pass through to `.format()` after Hijri token substitution.
|
||||
|
||||
```ts
|
||||
dayjs('2023-03-23').formatHijri('iYYYY-iMM-iDD');
|
||||
// => '1444-09-01'
|
||||
|
||||
dayjs('2023-03-23').formatHijri('iD iMMMM iYYYY [at] HH:mm');
|
||||
// => '1 Ramadan 1444 at 00:00'
|
||||
|
||||
dayjs('2023-03-23').formatHijri('iYYYY YYYY');
|
||||
// => '1444 2023'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Static Methods
|
||||
|
||||
### `dayjs.fromHijri(hy, hm, hd, opts?)`
|
||||
|
||||
Construct a Day.js instance from a Hijri date.
|
||||
|
||||
**Signature:**
|
||||
```ts
|
||||
dayjs.fromHijri(
|
||||
hy: number,
|
||||
hm: number,
|
||||
hd: number,
|
||||
opts?: ConversionOptions,
|
||||
): dayjs.Dayjs
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `hy` | `number` | Hijri year |
|
||||
| `hm` | `number` | Hijri month (1-12) |
|
||||
| `hd` | `number` | Hijri day (1-30) |
|
||||
| `opts.calendar` | `string` | Calendar engine id (default: `'uaq'`) |
|
||||
|
||||
**Throws:** `Error` if the Hijri date is invalid or outside the table range.
|
||||
|
||||
```ts
|
||||
dayjs.fromHijri(1444, 9, 1).format('YYYY-MM-DD');
|
||||
// => '2023-03-23'
|
||||
|
||||
dayjs.fromHijri(1444, 9, 1, { calendar: 'fcna' }).format('YYYY-MM-DD');
|
||||
// => '2023-03-22'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Type Exports
|
||||
|
||||
```ts
|
||||
import type {
|
||||
HijriDate, // { hy: number, hm: number, hd: number }
|
||||
ConversionOptions, // { calendar?: string }
|
||||
CalendarSystem, // string alias for calendar ids
|
||||
} from 'dayjs-hijri-plus';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Registry Exports
|
||||
|
||||
These re-export from hijri-core, so consumers can register custom calendar engines without adding hijri-core as a direct dependency:
|
||||
|
||||
```ts
|
||||
import { registerCalendar, getCalendar, listCalendars } from 'dayjs-hijri-plus';
|
||||
import type { CalendarEngine } from 'dayjs-hijri-plus';
|
||||
|
||||
registerCalendar('my-cal', myEngine);
|
||||
listCalendars(); // => ['uaq', 'fcna', 'my-cal']
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Home](Home) | [Architecture](Architecture)
|
||||
99
.wiki/Architecture.md
Normal file
99
.wiki/Architecture.md
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
# 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)
|
||||
36
.wiki/Home.md
Normal file
36
.wiki/Home.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# dayjs-hijri-plus
|
||||
|
||||
A Day.js plugin for Hijri calendar conversion and formatting. All calendar logic is delegated to [hijri-core](https://github.com/acamarata/hijri-core), making this package a thin, well-typed adapter with no calendar arithmetic of its own.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
pnpm add dayjs dayjs-hijri-plus hijri-core
|
||||
```
|
||||
|
||||
## Quick Usage
|
||||
|
||||
```ts
|
||||
import dayjs from 'dayjs';
|
||||
import hijriPlugin from 'dayjs-hijri-plus';
|
||||
|
||||
dayjs.extend(hijriPlugin);
|
||||
|
||||
dayjs('2023-03-23').toHijri();
|
||||
// => { hy: 1444, hm: 9, hd: 1 }
|
||||
|
||||
dayjs('2023-03-23').formatHijri('iD iMMMM iYYYY');
|
||||
// => '1 Ramadan 1444'
|
||||
|
||||
dayjs.fromHijri(1444, 10, 1).format('YYYY-MM-DD');
|
||||
// => '2023-04-21'
|
||||
```
|
||||
|
||||
## Contents
|
||||
|
||||
- [API Reference](API-Reference) — all methods, parameters, return types
|
||||
- [Architecture](Architecture) — design decisions, delegation model, format token resolution
|
||||
|
||||
---
|
||||
|
||||
Part of the [acamarata](https://github.com/acamarata) JavaScript library collection.
|
||||
16
CHANGELOG.md
Normal file
16
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [1.0.0] - 2026-02-25
|
||||
|
||||
### Added
|
||||
|
||||
- Day.js plugin with `.toHijri()`, `.fromHijri()`, `.hijriYear()`, `.hijriMonth()`, `.hijriDay()`, `.isValidHijri()`, and `.formatHijri()` methods
|
||||
- Umm al-Qura (UAQ) calendar support via hijri-core
|
||||
- FCNA/ISNA calendar support via hijri-core
|
||||
- Full TypeScript definitions including module augmentation for Day.js types
|
||||
- Dual CJS/ESM build with separate type declaration files
|
||||
- Re-exports of `registerCalendar`, `getCalendar`, and `listCalendars` from hijri-core for custom calendar registration
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Aric Camarata
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
190
README.md
Normal file
190
README.md
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
# dayjs-hijri-plus
|
||||
|
||||
[](https://www.npmjs.com/package/dayjs-hijri-plus)
|
||||
[](https://github.com/acamarata/dayjs-hijri-plus/actions/workflows/ci.yml)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
A Day.js plugin that adds Hijri calendar support. Converts Gregorian dates to and from Hijri, provides Hijri-aware formatting, and delegates all calendar logic to [hijri-core](https://github.com/acamarata/hijri-core) — keeping this package thin and testable.
|
||||
|
||||
Supports Umm al-Qura (UAQ) and FCNA/ISNA calendars out of the box. Custom calendar engines can be registered at runtime.
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
pnpm add dayjs dayjs-hijri-plus hijri-core
|
||||
```
|
||||
|
||||
Both `dayjs` and `hijri-core` are peer dependencies and must be installed alongside this plugin.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```ts
|
||||
import dayjs from 'dayjs';
|
||||
import hijriPlugin from 'dayjs-hijri-plus';
|
||||
|
||||
dayjs.extend(hijriPlugin);
|
||||
|
||||
// Convert a Gregorian date to Hijri
|
||||
const d = dayjs(new Date(2023, 2, 23));
|
||||
const hijri = d.toHijri();
|
||||
// => { hy: 1444, hm: 9, hd: 1 } (1 Ramadan 1444 AH)
|
||||
|
||||
// Format using Hijri tokens mixed with standard Day.js tokens
|
||||
d.formatHijri('iYYYY-iMM-iDD'); // => '1444-09-01'
|
||||
d.formatHijri('iD iMMMM iYYYY'); // => '1 Ramadan 1444'
|
||||
d.formatHijri('iD iMMMM iYYYY [at] HH:mm'); // => '1 Ramadan 1444 at 00:00'
|
||||
|
||||
// Individual Hijri components
|
||||
d.hijriYear(); // => 1444
|
||||
d.hijriMonth(); // => 9
|
||||
d.hijriDay(); // => 1
|
||||
|
||||
// Construct a Day.js instance from a Hijri date
|
||||
const eid = dayjs.fromHijri(1444, 10, 1);
|
||||
eid.format('YYYY-MM-DD'); // => '2023-04-21'
|
||||
|
||||
// FCNA/ISNA calendar variant
|
||||
d.toHijri({ calendar: 'fcna' }); // => { hy: 1444, hm: 9, hd: 2 } (varies by month)
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### dayjs.extend(hijriPlugin)
|
||||
|
||||
Register the plugin with your Day.js instance. Call once before using any plugin methods.
|
||||
|
||||
### Instance Methods
|
||||
|
||||
#### `.toHijri(opts?)`
|
||||
|
||||
Convert the Day.js date to a Hijri date object.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `opts` | `ConversionOptions` | Optional. `{ calendar: 'uaq' \| 'fcna' \| string }` |
|
||||
|
||||
Returns `HijriDate | null`. Returns `null` if the date is outside the supported range (approximately 1900-2077 CE for UAQ).
|
||||
|
||||
```ts
|
||||
dayjs('2023-03-23').toHijri();
|
||||
// => { hy: 1444, hm: 9, hd: 1 }
|
||||
```
|
||||
|
||||
#### `.isValidHijri(opts?)`
|
||||
|
||||
Check whether the date maps to a valid Hijri date in the supported range.
|
||||
|
||||
Returns `boolean`.
|
||||
|
||||
#### `.hijriYear(opts?)`
|
||||
|
||||
Returns the Hijri year as a `number`, or `null` if out of range.
|
||||
|
||||
#### `.hijriMonth(opts?)`
|
||||
|
||||
Returns the Hijri month (1-12) as a `number`, or `null` if out of range.
|
||||
|
||||
#### `.hijriDay(opts?)`
|
||||
|
||||
Returns the Hijri day (1-30) as a `number`, or `null` if out of range.
|
||||
|
||||
#### `.formatHijri(formatStr, opts?)`
|
||||
|
||||
Format the date using a mix of Hijri tokens and standard Day.js tokens.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `formatStr` | `string` | Format string — see token table below |
|
||||
| `opts` | `ConversionOptions` | Optional calendar selection |
|
||||
|
||||
Returns `string`. Returns an empty string if the date is out of range.
|
||||
|
||||
Hijri tokens are replaced first. The resulting string is then passed to Day.js `.format()`, so all standard tokens (YYYY, MM, DD, HH, mm, ss, etc.) resolve normally.
|
||||
|
||||
### Static Methods
|
||||
|
||||
#### `dayjs.fromHijri(hy, hm, hd, opts?)`
|
||||
|
||||
Construct a Day.js instance from a Hijri date.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `hy` | `number` | Hijri year |
|
||||
| `hm` | `number` | Hijri month (1-12) |
|
||||
| `hd` | `number` | Hijri day (1-30) |
|
||||
| `opts` | `ConversionOptions` | Optional calendar selection |
|
||||
|
||||
Returns a `dayjs.Dayjs` instance. Throws `Error` if the Hijri date is invalid or outside the table range.
|
||||
|
||||
## Format Tokens
|
||||
|
||||
All Hijri-specific tokens use the `i` prefix.
|
||||
|
||||
| Token | Example | Description |
|
||||
| --- | --- | --- |
|
||||
| `iYYYY` | `1444` | 4-digit Hijri year |
|
||||
| `iYY` | `44` | 2-digit Hijri year |
|
||||
| `iMMMM` | `Ramadan` | Full Hijri month name |
|
||||
| `iMMM` | `Ramadan` | Medium Hijri month name |
|
||||
| `iMM` | `09` | Zero-padded Hijri month number |
|
||||
| `iM` | `9` | Hijri month number |
|
||||
| `iDD` | `01` | Zero-padded Hijri day |
|
||||
| `iD` | `1` | Hijri day number |
|
||||
| `iEEEE` | `Yawm al-Khamis` | Full weekday name |
|
||||
| `iEEE` | `Kham` | Short weekday name |
|
||||
| `iE` | `5` | Weekday number (1=Sunday ... 7=Saturday) |
|
||||
| `ioooo` | `AH` | Era (Anno Hegirae) |
|
||||
| `iooo` | `AH` | Era (short form, same as ioooo) |
|
||||
|
||||
Standard Day.js tokens pass through untouched. Square-bracket escaping (`[literal text]`) also works as expected.
|
||||
|
||||
## Calendar Systems
|
||||
|
||||
Two calendars ship with hijri-core:
|
||||
|
||||
- **`uaq`** (default) — Umm al-Qura, the official calendar of Saudi Arabia. Table-based, covers approximately 1318-1500 AH (1900-2077 CE).
|
||||
- **`fcna`** — Fiqh Council of North America calendar. Uses an astronomical calculation with fixed criteria, independent of moon sighting.
|
||||
|
||||
Select a calendar by passing `{ calendar: 'fcna' }` to any method. The default is `'uaq'` when no option is provided.
|
||||
|
||||
Custom calendar engines can be registered:
|
||||
|
||||
```ts
|
||||
import { registerCalendar } from 'dayjs-hijri-plus';
|
||||
import type { CalendarEngine } from 'dayjs-hijri-plus';
|
||||
|
||||
const myEngine: CalendarEngine = { ... };
|
||||
registerCalendar('my-calendar', myEngine);
|
||||
|
||||
dayjs().toHijri({ calendar: 'my-calendar' });
|
||||
```
|
||||
|
||||
See the [hijri-core CalendarEngine interface](https://github.com/acamarata/hijri-core) for the full contract.
|
||||
|
||||
## TypeScript
|
||||
|
||||
Full TypeScript support is included. The plugin augments the Day.js module to add types for all instance and static methods.
|
||||
|
||||
```ts
|
||||
import type { HijriDate, ConversionOptions } from 'dayjs-hijri-plus';
|
||||
|
||||
const h: HijriDate = dayjs().toHijri()!;
|
||||
const opts: ConversionOptions = { calendar: 'fcna' };
|
||||
```
|
||||
|
||||
No `@types` package is needed.
|
||||
|
||||
## Documentation
|
||||
|
||||
Full API reference, architecture notes, and calendar system comparisons are on the [GitHub Wiki](https://github.com/acamarata/dayjs-hijri-plus/wiki).
|
||||
|
||||
## Related
|
||||
|
||||
- [hijri-core](https://github.com/acamarata/hijri-core) — the zero-dependency Hijri calendar engine this plugin wraps
|
||||
- [luxon-hijri](https://github.com/acamarata/luxon-hijri) — the same Hijri conversion for Luxon users
|
||||
- [pray-calc](https://github.com/acamarata/pray-calc) — Islamic prayer time calculation
|
||||
- [nrel-spa](https://github.com/acamarata/nrel-spa) — NREL Solar Position Algorithm in pure JavaScript
|
||||
|
||||
## License
|
||||
|
||||
MIT. Copyright (c) 2026 Aric Camarata.
|
||||
62
package.json
Normal file
62
package.json
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"name": "dayjs-hijri-plus",
|
||||
"version": "1.0.0",
|
||||
"description": "Day.js plugin for Hijri calendar conversion and formatting. Supports Umm al-Qura and FCNA calendars via hijri-core.",
|
||||
"author": "Aric Camarata",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" },
|
||||
"require": { "types": "./dist/index.d.ts", "default": "./dist/index.cjs" }
|
||||
}
|
||||
},
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"dist/index.cjs",
|
||||
"dist/index.mjs",
|
||||
"dist/index.d.ts",
|
||||
"dist/index.d.mts",
|
||||
"README.md",
|
||||
"CHANGELOG.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"engines": { "node": ">=20" },
|
||||
"packageManager": "pnpm@10.30.1",
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"pretest": "tsup",
|
||||
"test": "node test.mjs && node test-cjs.cjs",
|
||||
"prepublishOnly": "tsup"
|
||||
},
|
||||
"keywords": [
|
||||
"dayjs",
|
||||
"plugin",
|
||||
"hijri",
|
||||
"islamic",
|
||||
"calendar",
|
||||
"umm-al-qura",
|
||||
"fcna",
|
||||
"gregorian",
|
||||
"converter",
|
||||
"typescript"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"dayjs": "^1.0.0",
|
||||
"hijri-core": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"dayjs": "^1.11.0",
|
||||
"hijri-core": "file:../hijri-core",
|
||||
"tsup": "^8.0.0",
|
||||
"typescript": "^5.5.0"
|
||||
},
|
||||
"publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
|
||||
"repository": { "type": "git", "url": "git+https://github.com/acamarata/dayjs-hijri-plus.git" },
|
||||
"homepage": "https://github.com/acamarata/dayjs-hijri-plus#readme",
|
||||
"bugs": { "url": "https://github.com/acamarata/dayjs-hijri-plus/issues" }
|
||||
}
|
||||
942
pnpm-lock.yaml
Normal file
942
pnpm-lock.yaml
Normal file
|
|
@ -0,0 +1,942 @@
|
|||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^22.0.0
|
||||
version: 22.19.11
|
||||
dayjs:
|
||||
specifier: ^1.11.0
|
||||
version: 1.11.19
|
||||
hijri-core:
|
||||
specifier: file:../hijri-core
|
||||
version: file:../hijri-core
|
||||
tsup:
|
||||
specifier: ^8.0.0
|
||||
version: 8.5.1(typescript@5.9.3)
|
||||
typescript:
|
||||
specifier: ^5.5.0
|
||||
version: 5.9.3
|
||||
|
||||
packages:
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.3':
|
||||
resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.27.3':
|
||||
resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.27.3':
|
||||
resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.27.3':
|
||||
resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.3':
|
||||
resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.27.3':
|
||||
resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.3':
|
||||
resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.3':
|
||||
resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.27.3':
|
||||
resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.27.3':
|
||||
resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.27.3':
|
||||
resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.27.3':
|
||||
resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.3':
|
||||
resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.3':
|
||||
resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.3':
|
||||
resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.27.3':
|
||||
resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.27.3':
|
||||
resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.3':
|
||||
resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.3':
|
||||
resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.3':
|
||||
resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.3':
|
||||
resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.3':
|
||||
resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@esbuild/sunos-x64@0.27.3':
|
||||
resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.27.3':
|
||||
resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.27.3':
|
||||
resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.27.3':
|
||||
resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2':
|
||||
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.59.0':
|
||||
resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-android-arm64@4.59.0':
|
||||
resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.59.0':
|
||||
resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.59.0':
|
||||
resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.59.0':
|
||||
resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.59.0':
|
||||
resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.59.0':
|
||||
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
|
||||
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.59.0':
|
||||
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.59.0':
|
||||
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.59.0':
|
||||
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.59.0':
|
||||
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.59.0':
|
||||
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.59.0':
|
||||
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@rollup/rollup-openharmony-arm64@4.59.0':
|
||||
resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.59.0':
|
||||
resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.59.0':
|
||||
resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-gnu@4.59.0':
|
||||
resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.59.0':
|
||||
resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@types/estree@1.0.8':
|
||||
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||
|
||||
'@types/node@22.19.11':
|
||||
resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==}
|
||||
|
||||
acorn@8.16.0:
|
||||
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
any-promise@1.3.0:
|
||||
resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
|
||||
|
||||
bundle-require@5.1.0:
|
||||
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.18'
|
||||
|
||||
cac@6.7.14:
|
||||
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
chokidar@4.0.3:
|
||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
|
||||
commander@4.1.1:
|
||||
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
confbox@0.1.8:
|
||||
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
|
||||
|
||||
consola@3.4.2:
|
||||
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
|
||||
engines: {node: ^14.18.0 || >=16.10.0}
|
||||
|
||||
dayjs@1.11.19:
|
||||
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
|
||||
|
||||
debug@4.4.3:
|
||||
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
esbuild@0.27.3:
|
||||
resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
fdir@6.5.0:
|
||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fix-dts-default-cjs-exports@1.0.1:
|
||||
resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
hijri-core@file:../hijri-core:
|
||||
resolution: {directory: ../hijri-core, type: directory}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
joycon@3.1.1:
|
||||
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
lilconfig@3.1.3:
|
||||
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
lines-and-columns@1.2.4:
|
||||
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
|
||||
|
||||
load-tsconfig@0.2.5:
|
||||
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
mlly@1.8.0:
|
||||
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
mz@2.7.0:
|
||||
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
|
||||
|
||||
object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
picomatch@4.0.3:
|
||||
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pirates@4.0.7:
|
||||
resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
pkg-types@1.3.1:
|
||||
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
||||
|
||||
postcss-load-config@6.0.1:
|
||||
resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
jiti: '>=1.21.0'
|
||||
postcss: '>=8.0.9'
|
||||
tsx: ^4.8.1
|
||||
yaml: ^2.4.2
|
||||
peerDependenciesMeta:
|
||||
jiti:
|
||||
optional: true
|
||||
postcss:
|
||||
optional: true
|
||||
tsx:
|
||||
optional: true
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
readdirp@4.1.2:
|
||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||
engines: {node: '>= 14.18.0'}
|
||||
|
||||
resolve-from@5.0.0:
|
||||
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
rollup@4.59.0:
|
||||
resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
source-map@0.7.6:
|
||||
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
sucrase@3.35.1:
|
||||
resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
hasBin: true
|
||||
|
||||
thenify-all@1.6.0:
|
||||
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
thenify@3.3.1:
|
||||
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
|
||||
|
||||
tinyexec@0.3.2:
|
||||
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tree-kill@1.2.2:
|
||||
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
|
||||
hasBin: true
|
||||
|
||||
ts-interface-checker@0.1.13:
|
||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||
|
||||
tsup@8.5.1:
|
||||
resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@microsoft/api-extractor': ^7.36.0
|
||||
'@swc/core': ^1
|
||||
postcss: ^8.4.12
|
||||
typescript: '>=4.5.0'
|
||||
peerDependenciesMeta:
|
||||
'@microsoft/api-extractor':
|
||||
optional: true
|
||||
'@swc/core':
|
||||
optional: true
|
||||
postcss:
|
||||
optional: true
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
typescript@5.9.3:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
ufo@1.6.3:
|
||||
resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}
|
||||
|
||||
undici-types@6.21.0:
|
||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.27.3':
|
||||
optional: true
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2': {}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
dependencies:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm64@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-openharmony-arm64@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-gnu@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@types/node@22.19.11':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
acorn@8.16.0: {}
|
||||
|
||||
any-promise@1.3.0: {}
|
||||
|
||||
bundle-require@5.1.0(esbuild@0.27.3):
|
||||
dependencies:
|
||||
esbuild: 0.27.3
|
||||
load-tsconfig: 0.2.5
|
||||
|
||||
cac@6.7.14: {}
|
||||
|
||||
chokidar@4.0.3:
|
||||
dependencies:
|
||||
readdirp: 4.1.2
|
||||
|
||||
commander@4.1.1: {}
|
||||
|
||||
confbox@0.1.8: {}
|
||||
|
||||
consola@3.4.2: {}
|
||||
|
||||
dayjs@1.11.19: {}
|
||||
|
||||
debug@4.4.3:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
esbuild@0.27.3:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.27.3
|
||||
'@esbuild/android-arm': 0.27.3
|
||||
'@esbuild/android-arm64': 0.27.3
|
||||
'@esbuild/android-x64': 0.27.3
|
||||
'@esbuild/darwin-arm64': 0.27.3
|
||||
'@esbuild/darwin-x64': 0.27.3
|
||||
'@esbuild/freebsd-arm64': 0.27.3
|
||||
'@esbuild/freebsd-x64': 0.27.3
|
||||
'@esbuild/linux-arm': 0.27.3
|
||||
'@esbuild/linux-arm64': 0.27.3
|
||||
'@esbuild/linux-ia32': 0.27.3
|
||||
'@esbuild/linux-loong64': 0.27.3
|
||||
'@esbuild/linux-mips64el': 0.27.3
|
||||
'@esbuild/linux-ppc64': 0.27.3
|
||||
'@esbuild/linux-riscv64': 0.27.3
|
||||
'@esbuild/linux-s390x': 0.27.3
|
||||
'@esbuild/linux-x64': 0.27.3
|
||||
'@esbuild/netbsd-arm64': 0.27.3
|
||||
'@esbuild/netbsd-x64': 0.27.3
|
||||
'@esbuild/openbsd-arm64': 0.27.3
|
||||
'@esbuild/openbsd-x64': 0.27.3
|
||||
'@esbuild/openharmony-arm64': 0.27.3
|
||||
'@esbuild/sunos-x64': 0.27.3
|
||||
'@esbuild/win32-arm64': 0.27.3
|
||||
'@esbuild/win32-ia32': 0.27.3
|
||||
'@esbuild/win32-x64': 0.27.3
|
||||
|
||||
fdir@6.5.0(picomatch@4.0.3):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.3
|
||||
|
||||
fix-dts-default-cjs-exports@1.0.1:
|
||||
dependencies:
|
||||
magic-string: 0.30.21
|
||||
mlly: 1.8.0
|
||||
rollup: 4.59.0
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
hijri-core@file:../hijri-core: {}
|
||||
|
||||
joycon@3.1.1: {}
|
||||
|
||||
lilconfig@3.1.3: {}
|
||||
|
||||
lines-and-columns@1.2.4: {}
|
||||
|
||||
load-tsconfig@0.2.5: {}
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
mlly@1.8.0:
|
||||
dependencies:
|
||||
acorn: 8.16.0
|
||||
pathe: 2.0.3
|
||||
pkg-types: 1.3.1
|
||||
ufo: 1.6.3
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
mz@2.7.0:
|
||||
dependencies:
|
||||
any-promise: 1.3.0
|
||||
object-assign: 4.1.1
|
||||
thenify-all: 1.6.0
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@4.0.3: {}
|
||||
|
||||
pirates@4.0.7: {}
|
||||
|
||||
pkg-types@1.3.1:
|
||||
dependencies:
|
||||
confbox: 0.1.8
|
||||
mlly: 1.8.0
|
||||
pathe: 2.0.3
|
||||
|
||||
postcss-load-config@6.0.1:
|
||||
dependencies:
|
||||
lilconfig: 3.1.3
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
||||
resolve-from@5.0.0: {}
|
||||
|
||||
rollup@4.59.0:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
optionalDependencies:
|
||||
'@rollup/rollup-android-arm-eabi': 4.59.0
|
||||
'@rollup/rollup-android-arm64': 4.59.0
|
||||
'@rollup/rollup-darwin-arm64': 4.59.0
|
||||
'@rollup/rollup-darwin-x64': 4.59.0
|
||||
'@rollup/rollup-freebsd-arm64': 4.59.0
|
||||
'@rollup/rollup-freebsd-x64': 4.59.0
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.59.0
|
||||
'@rollup/rollup-linux-arm-musleabihf': 4.59.0
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.59.0
|
||||
'@rollup/rollup-linux-arm64-musl': 4.59.0
|
||||
'@rollup/rollup-linux-loong64-gnu': 4.59.0
|
||||
'@rollup/rollup-linux-loong64-musl': 4.59.0
|
||||
'@rollup/rollup-linux-ppc64-gnu': 4.59.0
|
||||
'@rollup/rollup-linux-ppc64-musl': 4.59.0
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.59.0
|
||||
'@rollup/rollup-linux-riscv64-musl': 4.59.0
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.59.0
|
||||
'@rollup/rollup-linux-x64-gnu': 4.59.0
|
||||
'@rollup/rollup-linux-x64-musl': 4.59.0
|
||||
'@rollup/rollup-openbsd-x64': 4.59.0
|
||||
'@rollup/rollup-openharmony-arm64': 4.59.0
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.59.0
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.59.0
|
||||
'@rollup/rollup-win32-x64-gnu': 4.59.0
|
||||
'@rollup/rollup-win32-x64-msvc': 4.59.0
|
||||
fsevents: 2.3.3
|
||||
|
||||
source-map@0.7.6: {}
|
||||
|
||||
sucrase@3.35.1:
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.13
|
||||
commander: 4.1.1
|
||||
lines-and-columns: 1.2.4
|
||||
mz: 2.7.0
|
||||
pirates: 4.0.7
|
||||
tinyglobby: 0.2.15
|
||||
ts-interface-checker: 0.1.13
|
||||
|
||||
thenify-all@1.6.0:
|
||||
dependencies:
|
||||
thenify: 3.3.1
|
||||
|
||||
thenify@3.3.1:
|
||||
dependencies:
|
||||
any-promise: 1.3.0
|
||||
|
||||
tinyexec@0.3.2: {}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
picomatch: 4.0.3
|
||||
|
||||
tree-kill@1.2.2: {}
|
||||
|
||||
ts-interface-checker@0.1.13: {}
|
||||
|
||||
tsup@8.5.1(typescript@5.9.3):
|
||||
dependencies:
|
||||
bundle-require: 5.1.0(esbuild@0.27.3)
|
||||
cac: 6.7.14
|
||||
chokidar: 4.0.3
|
||||
consola: 3.4.2
|
||||
debug: 4.4.3
|
||||
esbuild: 0.27.3
|
||||
fix-dts-default-cjs-exports: 1.0.1
|
||||
joycon: 3.1.1
|
||||
picocolors: 1.1.1
|
||||
postcss-load-config: 6.0.1
|
||||
resolve-from: 5.0.0
|
||||
rollup: 4.59.0
|
||||
source-map: 0.7.6
|
||||
sucrase: 3.35.1
|
||||
tinyexec: 0.3.2
|
||||
tinyglobby: 0.2.15
|
||||
tree-kill: 1.2.2
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- jiti
|
||||
- supports-color
|
||||
- tsx
|
||||
- yaml
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
ufo@1.6.3: {}
|
||||
|
||||
undici-types@6.21.0: {}
|
||||
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
160
src/index.ts
Normal file
160
src/index.ts
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import type { PluginFunc } from 'dayjs';
|
||||
import {
|
||||
toHijri,
|
||||
toGregorian,
|
||||
hmLong,
|
||||
hmMedium,
|
||||
hwLong,
|
||||
hwShort,
|
||||
hwNumeric,
|
||||
} from 'hijri-core';
|
||||
import type { ConversionOptions, HijriDate } from './types';
|
||||
|
||||
// Augment Day.js to expose plugin methods on the instance type.
|
||||
declare module 'dayjs' {
|
||||
interface Dayjs {
|
||||
/** Convert to a Hijri date. Returns null if outside the supported range. */
|
||||
toHijri(opts?: ConversionOptions): HijriDate | null;
|
||||
|
||||
/** Check whether the date maps to a valid Hijri date in the supported range. */
|
||||
isValidHijri(opts?: ConversionOptions): boolean;
|
||||
|
||||
/** Hijri year component, or null if out of range. */
|
||||
hijriYear(opts?: ConversionOptions): number | null;
|
||||
|
||||
/** Hijri month component (1-12), or null if out of range. */
|
||||
hijriMonth(opts?: ConversionOptions): number | null;
|
||||
|
||||
/** Hijri day component (1-30), or null if out of range. */
|
||||
hijriDay(opts?: ConversionOptions): number | null;
|
||||
|
||||
/**
|
||||
* Format the date using Hijri tokens (i-prefixed) and standard Day.js tokens.
|
||||
* Returns an empty string if the date is outside the supported range.
|
||||
*/
|
||||
formatHijri(formatStr: string, opts?: ConversionOptions): string;
|
||||
}
|
||||
}
|
||||
|
||||
// Augment the dayjs factory to expose the fromHijri static method.
|
||||
declare module 'dayjs' {
|
||||
interface IStatic {
|
||||
/**
|
||||
* Construct a Day.js instance from a Hijri date.
|
||||
* Throws if the Hijri date is invalid or outside the supported range.
|
||||
*/
|
||||
fromHijri(hy: number, hm: number, hd: number, opts?: ConversionOptions): import('dayjs').Dayjs;
|
||||
}
|
||||
}
|
||||
|
||||
// Hijri-specific format tokens, ordered longest-first to prevent partial matches.
|
||||
// After replacement, the remaining string is passed to Day.js .format() for
|
||||
// standard tokens (YYYY, MM, DD, HH, mm, ss, etc.).
|
||||
const HIJRI_TOKEN_RE = /iYYYY|iYY|iMMMM|iMMM|iMM|iM|iDD|iD|iEEEE|iEEE|iE|ioooo|iooo/g;
|
||||
|
||||
/**
|
||||
* Wrap a plain string value in Day.js bracket-escape syntax so that
|
||||
* `.format()` treats every character as a literal.
|
||||
*
|
||||
* Day.js uses `[...]` for literal text. A `]` inside such a section would
|
||||
* close it prematurely, so we split on `]` and re-join with `][` (which
|
||||
* closes the current literal section, outputs a raw `]` — Day.js passes
|
||||
* unrecognised characters through untouched — then opens a new one).
|
||||
*/
|
||||
function lit(value: string): string {
|
||||
return '[' + value.split(']').join('][') + ']';
|
||||
}
|
||||
|
||||
const plugin: PluginFunc = (_option, dayjsClass, dayjsFactory) => {
|
||||
// ------------------------------------------------------------------ //
|
||||
// Instance methods //
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
dayjsClass.prototype.toHijri = function (opts?: ConversionOptions): HijriDate | null {
|
||||
return toHijri(this.toDate(), opts);
|
||||
};
|
||||
|
||||
dayjsClass.prototype.isValidHijri = function (opts?: ConversionOptions): boolean {
|
||||
return this.toHijri(opts) !== null;
|
||||
};
|
||||
|
||||
dayjsClass.prototype.hijriYear = function (opts?: ConversionOptions): number | null {
|
||||
return this.toHijri(opts)?.hy ?? null;
|
||||
};
|
||||
|
||||
dayjsClass.prototype.hijriMonth = function (opts?: ConversionOptions): number | null {
|
||||
return this.toHijri(opts)?.hm ?? null;
|
||||
};
|
||||
|
||||
dayjsClass.prototype.hijriDay = function (opts?: ConversionOptions): number | null {
|
||||
return this.toHijri(opts)?.hd ?? null;
|
||||
};
|
||||
|
||||
dayjsClass.prototype.formatHijri = function (
|
||||
formatStr: string,
|
||||
opts?: ConversionOptions,
|
||||
): string {
|
||||
const hijri = this.toHijri(opts);
|
||||
if (!hijri) return '';
|
||||
|
||||
// Day.js .day() returns 0 (Sunday) ... 6 (Saturday), matching the index
|
||||
// layout of hwLong, hwShort, and hwNumeric from hijri-core.
|
||||
const dow = this.day();
|
||||
|
||||
const replaced = formatStr.replace(HIJRI_TOKEN_RE, (token) => {
|
||||
switch (token) {
|
||||
case 'iYYYY': return lit(String(hijri.hy).padStart(4, '0'));
|
||||
case 'iYY': return lit(String(hijri.hy % 100).padStart(2, '0'));
|
||||
case 'iMMMM': return lit(hmLong[hijri.hm - 1]);
|
||||
case 'iMMM': return lit(hmMedium[hijri.hm - 1]);
|
||||
case 'iMM': return lit(String(hijri.hm).padStart(2, '0'));
|
||||
case 'iM': return lit(String(hijri.hm));
|
||||
case 'iDD': return lit(String(hijri.hd).padStart(2, '0'));
|
||||
case 'iD': return lit(String(hijri.hd));
|
||||
case 'iEEEE': return lit(hwLong[dow]);
|
||||
case 'iEEE': return lit(hwShort[dow]);
|
||||
case 'iE': return lit(String(hwNumeric[dow]));
|
||||
case 'ioooo':
|
||||
case 'iooo': return lit('AH');
|
||||
default: return token;
|
||||
}
|
||||
});
|
||||
|
||||
// Pass the processed string to Day.js .format() so standard tokens
|
||||
// (YYYY, MM, DD, HH, mm, ss, etc.) resolve correctly. Hijri values are
|
||||
// already wrapped in bracket-escaped literals and pass through untouched.
|
||||
return this.format(replaced);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// Static method: dayjs.fromHijri(hy, hm, hd, opts?) //
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(dayjsFactory as any).fromHijri = (
|
||||
hy: number,
|
||||
hm: number,
|
||||
hd: number,
|
||||
opts?: ConversionOptions,
|
||||
) => {
|
||||
let greg: Date | null;
|
||||
try {
|
||||
greg = toGregorian(hy, hm, hd, opts);
|
||||
} catch {
|
||||
throw new Error(`Invalid or out-of-range Hijri date: ${hy}/${hm}/${hd}`);
|
||||
}
|
||||
if (!greg) {
|
||||
throw new Error(`Invalid or out-of-range Hijri date: ${hy}/${hm}/${hd}`);
|
||||
}
|
||||
return dayjsFactory(greg);
|
||||
};
|
||||
};
|
||||
|
||||
export default plugin;
|
||||
|
||||
// Re-export hijri-core types for consumers who import from dayjs-hijri-plus.
|
||||
export type { HijriDate, ConversionOptions, CalendarSystem } from './types';
|
||||
|
||||
// Re-export the registry API so callers can register custom calendar engines
|
||||
// without adding hijri-core as a direct dependency.
|
||||
export { registerCalendar, getCalendar, listCalendars } from 'hijri-core';
|
||||
13
src/types.ts
Normal file
13
src/types.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import type { HijriDate, ConversionOptions } from 'hijri-core';
|
||||
export type { HijriDate, ConversionOptions };
|
||||
|
||||
/** A registered calendar identifier. The built-in values are 'uaq' and 'fcna'. */
|
||||
export type CalendarSystem = string;
|
||||
|
||||
/**
|
||||
* Options passed to plugin methods. Inherits `calendar` from ConversionOptions
|
||||
* so callers can switch between 'uaq' (default) and 'fcna'.
|
||||
*/
|
||||
export interface HijriPluginOptions extends ConversionOptions {
|
||||
// calendar?: string (inherited — 'uaq' | 'fcna' | any registered calendar id)
|
||||
}
|
||||
68
test-cjs.cjs
Normal file
68
test-cjs.cjs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const dayjs = require('dayjs');
|
||||
const { default: plugin } = require('./dist/index.cjs');
|
||||
|
||||
dayjs.extend(plugin);
|
||||
|
||||
let passed = 0;
|
||||
let total = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
total++;
|
||||
try {
|
||||
fn();
|
||||
console.log(`[${name}]... PASS`);
|
||||
passed++;
|
||||
} catch (err) {
|
||||
console.error(`[${name}]... FAIL: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const D_RAMADAN_1444 = new Date(2023, 2, 23, 12);
|
||||
const D_MUHARRAM_1446 = new Date(2024, 6, 7, 12);
|
||||
|
||||
test('plugin registers (CJS)', () => {
|
||||
const d = dayjs(D_RAMADAN_1444);
|
||||
assert.equal(typeof d.toHijri, 'function');
|
||||
assert.equal(typeof d.formatHijri, 'function');
|
||||
assert.equal(typeof dayjs.fromHijri, 'function');
|
||||
});
|
||||
|
||||
test('toHijri (CJS): 2023-03-23 -> 1 Ramadan 1444', () => {
|
||||
const h = dayjs(D_RAMADAN_1444).toHijri();
|
||||
assert.deepEqual(h, { hy: 1444, hm: 9, hd: 1 });
|
||||
});
|
||||
|
||||
test('toHijri (CJS): 2024-07-07 -> 1 Muharram 1446', () => {
|
||||
const h = dayjs(D_MUHARRAM_1446).toHijri();
|
||||
assert.deepEqual(h, { hy: 1446, hm: 1, hd: 1 });
|
||||
});
|
||||
|
||||
test('fromHijri (CJS): 1444/9/1 -> 2023-03-23 (UTC)', () => {
|
||||
const d = dayjs.fromHijri(1444, 9, 1);
|
||||
const iso = d.toDate().toISOString();
|
||||
assert.ok(iso.startsWith('2023-03-23'), `Expected 2023-03-23, got ${iso}`);
|
||||
});
|
||||
|
||||
test('formatHijri (CJS): iYYYY-iMM-iDD', () => {
|
||||
const result = dayjs(D_RAMADAN_1444).formatHijri('iYYYY-iMM-iDD');
|
||||
assert.equal(result, '1444-09-01');
|
||||
});
|
||||
|
||||
test('formatHijri (CJS): iMMMM -> Ramadan', () => {
|
||||
const result = dayjs(D_RAMADAN_1444).formatHijri('iMMMM');
|
||||
assert.equal(result, 'Ramadan');
|
||||
});
|
||||
|
||||
test('isValidHijri (CJS): true for in-range date', () => {
|
||||
assert.equal(dayjs(D_RAMADAN_1444).isValidHijri(), true);
|
||||
});
|
||||
|
||||
test('fromHijri (CJS): throws for out-of-range date', () => {
|
||||
assert.throws(() => dayjs.fromHijri(1301, 1, 1), /Invalid or out-of-range/);
|
||||
});
|
||||
|
||||
console.log(`\n${passed}/${total} tests passed`);
|
||||
113
test.mjs
Normal file
113
test.mjs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import dayjs from 'dayjs';
|
||||
import plugin from './dist/index.mjs';
|
||||
|
||||
dayjs.extend(plugin);
|
||||
|
||||
let passed = 0;
|
||||
let total = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
total++;
|
||||
try {
|
||||
fn();
|
||||
console.log(`[${name}]... PASS`);
|
||||
passed++;
|
||||
} catch (err) {
|
||||
console.error(`[${name}]... FAIL: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Use noon to avoid UTC midnight boundary issues across timezones.
|
||||
const D_RAMADAN_1444 = new Date(2023, 2, 23, 12); // 1 Ramadan 1444
|
||||
const D_MUHARRAM_1446 = new Date(2024, 6, 7, 12); // 1 Muharram 1446
|
||||
|
||||
test('plugin registers on dayjs', () => {
|
||||
const d = dayjs(D_RAMADAN_1444);
|
||||
assert.equal(typeof d.toHijri, 'function');
|
||||
assert.equal(typeof d.formatHijri, 'function');
|
||||
assert.equal(typeof d.isValidHijri, 'function');
|
||||
assert.equal(typeof d.hijriYear, 'function');
|
||||
assert.equal(typeof d.hijriMonth, 'function');
|
||||
assert.equal(typeof d.hijriDay, 'function');
|
||||
assert.equal(typeof dayjs.fromHijri, 'function');
|
||||
});
|
||||
|
||||
test('toHijri: 2023-03-23 -> 1 Ramadan 1444', () => {
|
||||
const h = dayjs(D_RAMADAN_1444).toHijri();
|
||||
assert.deepEqual(h, { hy: 1444, hm: 9, hd: 1 });
|
||||
});
|
||||
|
||||
test('toHijri: 2024-07-07 -> 1 Muharram 1446', () => {
|
||||
const h = dayjs(D_MUHARRAM_1446).toHijri();
|
||||
assert.deepEqual(h, { hy: 1446, hm: 1, hd: 1 });
|
||||
});
|
||||
|
||||
test('fromHijri: 1444/9/1 -> 2023-03-23 (UTC)', () => {
|
||||
const d = dayjs.fromHijri(1444, 9, 1);
|
||||
// toGregorian returns midnight UTC; compare using UTC accessors to be timezone-safe.
|
||||
const iso = d.toDate().toISOString();
|
||||
assert.ok(iso.startsWith('2023-03-23'), `Expected 2023-03-23, got ${iso}`);
|
||||
});
|
||||
|
||||
test('fromHijri: 1446/1/1 -> 2024-07-07 (UTC)', () => {
|
||||
const d = dayjs.fromHijri(1446, 1, 1);
|
||||
const iso = d.toDate().toISOString();
|
||||
assert.ok(iso.startsWith('2024-07-07'), `Expected 2024-07-07, got ${iso}`);
|
||||
});
|
||||
|
||||
test('hijriYear/hijriMonth/hijriDay accessors on 1 Ramadan 1444', () => {
|
||||
const d = dayjs(D_RAMADAN_1444);
|
||||
assert.equal(d.hijriYear(), 1444);
|
||||
assert.equal(d.hijriMonth(), 9);
|
||||
assert.equal(d.hijriDay(), 1);
|
||||
});
|
||||
|
||||
test('formatHijri: iYYYY-iMM-iDD on 1 Ramadan 1444', () => {
|
||||
const result = dayjs(D_RAMADAN_1444).formatHijri('iYYYY-iMM-iDD');
|
||||
assert.equal(result, '1444-09-01');
|
||||
});
|
||||
|
||||
test('formatHijri: iMMMM -> Ramadan', () => {
|
||||
const result = dayjs(D_RAMADAN_1444).formatHijri('iMMMM');
|
||||
assert.equal(result, 'Ramadan');
|
||||
});
|
||||
|
||||
test('formatHijri: iEEEE on 2023-03-23 (Thursday)', () => {
|
||||
const result = dayjs(D_RAMADAN_1444).formatHijri('iEEEE');
|
||||
// 2023-03-23 is a Thursday; hwLong[4] = 'Yawm al-Khamis'
|
||||
assert.equal(result, 'Yawm al-Khamis');
|
||||
});
|
||||
|
||||
test('formatHijri: ioooo -> AH', () => {
|
||||
const result = dayjs(D_RAMADAN_1444).formatHijri('ioooo');
|
||||
assert.equal(result, 'AH');
|
||||
});
|
||||
|
||||
test('FCNA calendar: toHijri returns a valid HijriDate', () => {
|
||||
const h = dayjs(D_RAMADAN_1444).toHijri({ calendar: 'fcna' });
|
||||
assert.notEqual(h, null);
|
||||
assert.equal(typeof h.hy, 'number');
|
||||
assert.equal(typeof h.hm, 'number');
|
||||
assert.equal(typeof h.hd, 'number');
|
||||
});
|
||||
|
||||
test('isValidHijri returns true for in-range date', () => {
|
||||
const valid = dayjs(D_RAMADAN_1444).isValidHijri();
|
||||
assert.equal(valid, true);
|
||||
});
|
||||
|
||||
test('fromHijri throws for out-of-range UAQ date', () => {
|
||||
// 1301 is before the UAQ table begins (coverage starts at 1318)
|
||||
assert.throws(() => dayjs.fromHijri(1301, 1, 1), /Invalid or out-of-range/);
|
||||
});
|
||||
|
||||
test('formatHijri passthrough: iYYYY YYYY contains both Hijri and Gregorian year', () => {
|
||||
const result = dayjs(D_RAMADAN_1444).formatHijri('iYYYY YYYY');
|
||||
// Should contain '1444' (Hijri) and '2023' (Gregorian)
|
||||
assert.ok(result.includes('1444'), `Expected Hijri year 1444 in: ${result}`);
|
||||
assert.ok(result.includes('2023'), `Expected Gregorian year 2023 in: ${result}`);
|
||||
});
|
||||
|
||||
console.log(`\n${passed}/${total} tests passed`);
|
||||
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
17
tsup.config.ts
Normal file
17
tsup.config.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
format: ['cjs', 'esm'],
|
||||
dts: true,
|
||||
clean: true,
|
||||
outDir: 'dist',
|
||||
splitting: false,
|
||||
sourcemap: true,
|
||||
target: 'es2020',
|
||||
platform: 'node',
|
||||
external: ['dayjs', 'hijri-core'],
|
||||
outExtension({ format }) {
|
||||
return { js: format === 'esm' ? '.mjs' : '.cjs' };
|
||||
},
|
||||
});
|
||||
Loading…
Reference in a new issue