mirror of
https://github.com/acamarata/moment-hijri-plus.git
synced 2026-06-30 18:54:29 +00:00
feat: initial release of moment-hijri-plus v1.0.0
Moment.js plugin adding Hijri calendar support via hijri-core. Adds toHijri(), fromHijri(), hijriYear/Month/Day(), isValidHijri(), and formatHijri() to all Moment instances via fn prototype and module augmentation. Format token escaping wraps substituted values in moment bracket syntax [...] to prevent re-interpretation. UTC-midnight date shift corrected by using getUTC* components + moment([y, m, d]) construction. 14 ESM + 8 CJS tests passing. Dual CJS/ESM build.
This commit is contained in:
commit
295dbf8680
21 changed files with 1998 additions and 0 deletions
15
.editorconfig
Normal file
15
.editorconfig
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{c,h}]
|
||||
indent_size = 4
|
||||
|
||||
[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
|
||||
22
.github/workflows/wiki-sync.yml
vendored
Normal file
22
.github/workflows/wiki-sync.yml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
name: Wiki Sync
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- '.wiki/**'
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync .wiki/ to GitHub Wiki
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Sync wiki pages
|
||||
uses: Andrew-Chen-Wang/github-wiki-action@v4
|
||||
with:
|
||||
path: .wiki/
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
node_modules/
|
||||
dist/
|
||||
*.tgz
|
||||
*.log
|
||||
.DS_Store
|
||||
.claude/
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# AI agent directories
|
||||
.cursor/
|
||||
.copilot/
|
||||
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
|
||||
182
.wiki/API-Reference.md
Normal file
182
.wiki/API-Reference.md
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
# API Reference
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pnpm add moment moment-hijri-plus hijri-core
|
||||
```
|
||||
|
||||
`moment` and `hijri-core` are peer dependencies. Both must be installed.
|
||||
|
||||
## Plugin installation
|
||||
|
||||
```javascript
|
||||
import moment from 'moment';
|
||||
import installHijri from 'moment-hijri-plus';
|
||||
|
||||
// Call once at application startup.
|
||||
installHijri(moment);
|
||||
```
|
||||
|
||||
After this call, all methods below are available on every moment instance and on the `moment` constructor itself.
|
||||
|
||||
---
|
||||
|
||||
## Instance methods
|
||||
|
||||
### `toHijri(options?)`
|
||||
|
||||
Converts the moment to a Hijri date.
|
||||
|
||||
**Signature:** `(options?: ConversionOptions) => HijriDate | null`
|
||||
|
||||
Returns `null` if the date falls outside the supported calendar range (UAQ covers AH 1356-1500, approximately CE 1937-2077).
|
||||
|
||||
```javascript
|
||||
const h = moment(new Date(2023, 2, 23)).toHijri();
|
||||
// => { hy: 1444, hm: 9, hd: 1 }
|
||||
|
||||
const h = moment(new Date(2023, 2, 23)).toHijri({ calendar: 'fcna' });
|
||||
```
|
||||
|
||||
**HijriDate fields:**
|
||||
|
||||
| Field | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `hy` | `number` | Hijri year |
|
||||
| `hm` | `number` | Hijri month (1 = Muharram, 12 = Dhul Hijjah) |
|
||||
| `hd` | `number` | Hijri day (1-30) |
|
||||
|
||||
---
|
||||
|
||||
### `hijriYear(options?)`
|
||||
|
||||
**Signature:** `(options?: ConversionOptions) => number | null`
|
||||
|
||||
```javascript
|
||||
moment(new Date(2023, 2, 23)).hijriYear(); // => 1444
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `hijriMonth(options?)`
|
||||
|
||||
**Signature:** `(options?: ConversionOptions) => number | null`
|
||||
|
||||
Returns 1-12 (1 = Muharram).
|
||||
|
||||
```javascript
|
||||
moment(new Date(2023, 2, 23)).hijriMonth(); // => 9 (Ramadan)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `hijriDay(options?)`
|
||||
|
||||
**Signature:** `(options?: ConversionOptions) => number | null`
|
||||
|
||||
```javascript
|
||||
moment(new Date(2023, 2, 23)).hijriDay(); // => 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `isValidHijri(options?)`
|
||||
|
||||
**Signature:** `(options?: ConversionOptions) => boolean`
|
||||
|
||||
Returns `true` if the date falls within the supported range of the chosen calendar.
|
||||
|
||||
```javascript
|
||||
moment(new Date(2023, 2, 23)).isValidHijri(); // => true
|
||||
moment(new Date(1900, 0, 1)).isValidHijri(); // => false (before UAQ range)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `formatHijri(formatStr, options?)`
|
||||
|
||||
**Signature:** `(formatStr: string, options?: ConversionOptions) => string`
|
||||
|
||||
Format using Hijri-aware tokens. All tokens not listed below are passed through to `moment.format()`, so Gregorian tokens work as normal.
|
||||
|
||||
Returns `''` if the date is outside the Hijri range.
|
||||
|
||||
```javascript
|
||||
moment(new Date(2023, 2, 23)).formatHijri('iD iMMMM iYYYY AH');
|
||||
// => '1 Ramadan 1444 AH'
|
||||
|
||||
moment(new Date(2023, 2, 23)).formatHijri('iYYYY-iMM-iDD');
|
||||
// => '1444-09-01'
|
||||
|
||||
// Mix Hijri and Gregorian tokens.
|
||||
moment(new Date(2023, 2, 23)).formatHijri('iD iMMMM iYYYY [CE:] MMMM D, YYYY');
|
||||
// => '1 Ramadan 1444 CE: March 23, 2023'
|
||||
```
|
||||
|
||||
**Format tokens:**
|
||||
|
||||
| Token | Example output | Description |
|
||||
| --- | --- | --- |
|
||||
| `iYYYY` | `1444` | Hijri year, 4+ digits, zero-padded to 4 |
|
||||
| `iYY` | `44` | Hijri year, last 2 digits, zero-padded |
|
||||
| `iMMMM` | `Ramadan` | Month long name |
|
||||
| `iMMM` | `Ramadan` | Month medium name |
|
||||
| `iMM` | `09` | Month number, zero-padded |
|
||||
| `iM` | `9` | Month number |
|
||||
| `iDD` | `01` | Day, zero-padded |
|
||||
| `iD` | `1` | Day |
|
||||
| `iEEEE` | `Yawm al-Khamis` | Weekday long name |
|
||||
| `iEEE` | `Kham` | Weekday short name |
|
||||
| `iE` | `5` | Weekday numeric (1=Sunday, 7=Saturday) |
|
||||
| `ioooo` | `AH` | Era, long |
|
||||
| `iooo` | `AH` | Era, short |
|
||||
|
||||
---
|
||||
|
||||
## Static methods
|
||||
|
||||
### `moment.fromHijri(hy, hm, hd, options?)`
|
||||
|
||||
**Signature:** `(hy: number, hm: number, hd: number, options?: ConversionOptions) => Moment`
|
||||
|
||||
Creates a moment from a Hijri date. Throws `Error` if the date is invalid or outside the calendar range.
|
||||
|
||||
```javascript
|
||||
const m = moment.fromHijri(1444, 9, 1);
|
||||
m.format('YYYY-MM-DD'); // => '2023-03-23'
|
||||
|
||||
// With FCNA calendar.
|
||||
const m2 = moment.fromHijri(1444, 9, 1, { calendar: 'fcna' });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Options
|
||||
|
||||
```typescript
|
||||
interface ConversionOptions {
|
||||
calendar?: string; // default: 'uaq'
|
||||
}
|
||||
```
|
||||
|
||||
| Calendar ID | Description |
|
||||
| --- | --- |
|
||||
| `uaq` | Umm al-Qura — official Saudi calendar, tabular, covers AH 1356-1500 |
|
||||
| `fcna` | FCNA/ISNA — Fiqh Council of North America calculated calendar |
|
||||
|
||||
Custom calendars can be registered with hijri-core's `registerCalendar()`.
|
||||
|
||||
---
|
||||
|
||||
## TypeScript
|
||||
|
||||
All methods are typed via module augmentation. Import types from this package:
|
||||
|
||||
```typescript
|
||||
import type { HijriDate, ConversionOptions } from 'moment-hijri-plus';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Home](Home) · [API Reference](API-Reference) · [Architecture](Architecture)
|
||||
89
.wiki/Architecture.md
Normal file
89
.wiki/Architecture.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# Architecture
|
||||
|
||||
## Design goals
|
||||
|
||||
The package has one job: adapt the hijri-core API to Moment.js idioms. No calendar logic belongs here. All date arithmetic, table lookups, and validation live in hijri-core, which is tested and maintained independently.
|
||||
|
||||
This constraint keeps moment-hijri-plus small, maintainable, and calendar-agnostic — it benefits automatically from any calendar or correctness improvements made in hijri-core.
|
||||
|
||||
## Plugin pattern
|
||||
|
||||
Moment.js plugins work by mutating `moment.fn` (the prototype for all moment instances) and the `moment` constructor itself. The canonical pattern is a single `install(momentInstance)` function that the caller invokes once:
|
||||
|
||||
```javascript
|
||||
import installHijri from 'moment-hijri-plus';
|
||||
installHijri(moment);
|
||||
```
|
||||
|
||||
This approach avoids accidental double-registration, keeps the plugin stateless, and works with any moment instance — including custom ones created by `moment.utc()` or locale-scoped instances.
|
||||
|
||||
## Module augmentation
|
||||
|
||||
The TypeScript types are added to `moment.Moment` and `moment.MomentStatic` via declaration merging. This is the standard TypeScript way to extend third-party interfaces:
|
||||
|
||||
```typescript
|
||||
declare module 'moment' {
|
||||
interface Moment {
|
||||
toHijri(options?: ConversionOptions): HijriDate | null;
|
||||
// ...
|
||||
}
|
||||
interface MomentStatic {
|
||||
fromHijri(hy: number, hm: number, hd: number, options?: ConversionOptions): Moment;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The augmentation is emitted in the declaration files produced by tsup, so consumers get full type inference without any extra imports.
|
||||
|
||||
## Format token system
|
||||
|
||||
`formatHijri()` uses a single regex pass to identify Hijri tokens, replaces them with resolved strings, then passes the residual format string to `moment.format()`. This means Gregorian tokens (`YYYY`, `MMM`, `dddd`, etc.) resolve exactly as they would without the plugin.
|
||||
|
||||
The regex is ordered longest-match-first to prevent prefix collisions:
|
||||
|
||||
```javascript
|
||||
/iYYYY|iYY|iMMMM|iMMM|iMM|iM|iDD|iD|iEEEE|iEEE|iE|ioooo|iooo/g
|
||||
```
|
||||
|
||||
`iYYYY` must appear before `iYY` for obvious reasons; `iMMMM` before `iMMM` and `iMM`; `iDD` before `iD`; `iEEEE` before `iEEE`. The global flag allows the regex to find all non-overlapping tokens in one pass.
|
||||
|
||||
Moment's own bracket escaping (`[literal text]`) is preserved because it only runs during the `moment.format()` call on the residual string — any `[...]` sequences in the user's format string that don't contain Hijri tokens pass through untouched.
|
||||
|
||||
## Delegation to hijri-core
|
||||
|
||||
Every conversion call goes through hijri-core:
|
||||
|
||||
```
|
||||
toHijri() → hijri-core.toHijri(date, options)
|
||||
fromHijri() → hijri-core.toGregorian(hy, hm, hd, options)
|
||||
```
|
||||
|
||||
hijri-core maintains a registry of calendar engines. The default engine is `uaq` (Umm al-Qura). Callers can switch to `fcna` (FCNA/ISNA) or register custom engines via `hijri-core`'s `registerCalendar()`.
|
||||
|
||||
Because moment-hijri-plus uses hijri-core as a peer dependency, the registry is shared — a calendar registered in application code via `hijri-core`'s `registerCalendar()` is immediately available to this plugin.
|
||||
|
||||
## Build output
|
||||
|
||||
tsup produces four files:
|
||||
|
||||
| File | Format | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `dist/index.cjs` | CommonJS | `require()` in Node.js and bundlers in CJS mode |
|
||||
| `dist/index.mjs` | ESM | `import` in Node.js, Vite, Rollup, esbuild |
|
||||
| `dist/index.d.ts` | CJS declaration | Types for CJS consumers (`require`) |
|
||||
| `dist/index.d.mts` | ESM declaration | Types for ESM consumers (`import`) |
|
||||
|
||||
Both `moment` and `hijri-core` are marked external, so they are not bundled. They resolve from the consumer's `node_modules` at runtime.
|
||||
|
||||
## Calendar coverage
|
||||
|
||||
| Calendar | ID | Range | Authority |
|
||||
| --- | --- | --- | --- |
|
||||
| Umm al-Qura | `uaq` | AH 1356-1500 (approx CE 1937-2077) | Official Saudi calendar |
|
||||
| FCNA/ISNA | `fcna` | Calculated, no hard range | Fiqh Council of North America |
|
||||
|
||||
The UAQ calendar is tabular: dates are looked up in a precomputed table published by the Umm al-Qura University. Dates outside the table return `null`. The FCNA calendar uses an astronomical calculation rule and has no strict boundary.
|
||||
|
||||
---
|
||||
|
||||
[Home](Home) · [API Reference](API-Reference) · [Architecture](Architecture)
|
||||
47
.wiki/Home.md
Normal file
47
.wiki/Home.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# moment-hijri-plus
|
||||
|
||||
A Moment.js plugin for Hijri calendar conversion and formatting. All calendar arithmetic is handled by [hijri-core](https://github.com/acamarata/hijri-core), keeping this package thin and focused.
|
||||
|
||||
## What it does
|
||||
|
||||
- Converts any moment to a Hijri date object (`{ hy, hm, hd }`)
|
||||
- Formats moments using Hijri-specific tokens mixed freely with standard Moment format tokens
|
||||
- Constructs moments from Hijri dates via `moment.fromHijri()`
|
||||
- Supports Umm al-Qura (UAQ) and FCNA/ISNA calendars
|
||||
|
||||
## Pages
|
||||
|
||||
- [API Reference](API-Reference) — complete method signatures and examples
|
||||
- [Architecture](Architecture) — design rationale, token system, calendar delegation
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
pnpm add moment moment-hijri-plus hijri-core
|
||||
```
|
||||
|
||||
```javascript
|
||||
import moment from 'moment';
|
||||
import installHijri from 'moment-hijri-plus';
|
||||
|
||||
installHijri(moment);
|
||||
|
||||
moment(new Date(2023, 2, 23)).toHijri();
|
||||
// => { hy: 1444, hm: 9, hd: 1 } (1 Ramadan 1444 AH)
|
||||
|
||||
moment(new Date(2023, 2, 23)).formatHijri('iD iMMMM iYYYY AH');
|
||||
// => '1 Ramadan 1444 AH'
|
||||
|
||||
moment.fromHijri(1446, 1, 1).format('YYYY-MM-DD');
|
||||
// => '2024-07-07'
|
||||
```
|
||||
|
||||
## Related packages
|
||||
|
||||
- [hijri-core](https://github.com/acamarata/hijri-core) — the calendar engine
|
||||
- [luxon-hijri](https://github.com/acamarata/luxon-hijri) — same support for Luxon
|
||||
- [pray-calc](https://github.com/acamarata/pray-calc) — Islamic prayer time calculation
|
||||
|
||||
---
|
||||
|
||||
[Home](Home) · [API Reference](API-Reference) · [Architecture](Architecture)
|
||||
20
CHANGELOG.md
Normal file
20
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# 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
|
||||
|
||||
- Initial release
|
||||
- `toHijri()` instance method: convert a moment to a Hijri date object
|
||||
- `hijriYear()`, `hijriMonth()`, `hijriDay()` convenience accessors
|
||||
- `isValidHijri()` range check
|
||||
- `formatHijri()` with 13 Hijri-specific format tokens
|
||||
- `moment.fromHijri()` static factory for constructing moments from Hijri dates
|
||||
- Umm al-Qura (UAQ) calendar support via hijri-core (default)
|
||||
- FCNA/ISNA calendar support via hijri-core
|
||||
- Full TypeScript definitions with module augmentation for `moment.Moment` and `moment.MomentStatic`
|
||||
- Dual CJS/ESM build with separate type declaration files
|
||||
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.
|
||||
138
README.md
Normal file
138
README.md
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
# moment-hijri-plus
|
||||
|
||||
[](https://www.npmjs.com/package/moment-hijri-plus)
|
||||
[](https://github.com/acamarata/moment-hijri-plus/actions/workflows/ci.yml)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
Moment.js plugin for Hijri calendar conversion and formatting. Delegates all calendar logic to [hijri-core](https://github.com/acamarata/hijri-core), a zero-dependency Hijri engine with pluggable calendar support.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pnpm add moment moment-hijri-plus hijri-core
|
||||
```
|
||||
|
||||
Both `moment` and `hijri-core` are peer dependencies and must be installed alongside this package.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```javascript
|
||||
import moment from 'moment';
|
||||
import installHijri from 'moment-hijri-plus';
|
||||
|
||||
// Install the plugin once at startup.
|
||||
installHijri(moment);
|
||||
|
||||
// Convert a Gregorian date to Hijri.
|
||||
const m = moment(new Date(2023, 2, 23)); // 23 March 2023
|
||||
const hijri = m.toHijri();
|
||||
// => { hy: 1444, hm: 9, hd: 1 } (1 Ramadan 1444 AH)
|
||||
|
||||
// Format using Hijri tokens.
|
||||
m.formatHijri('iD iMMMM iYYYY AH');
|
||||
// => '1 Ramadan 1444 AH'
|
||||
|
||||
// Construct a moment from a Hijri date.
|
||||
const start = moment.fromHijri(1446, 1, 1);
|
||||
// => moment representing 7 July 2024 (1 Muharram 1446 AH)
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Instance methods
|
||||
|
||||
All methods are added to `moment.Moment` by calling `installHijri(moment)` once.
|
||||
|
||||
| Method | Signature | Description |
|
||||
| --- | --- | --- |
|
||||
| `toHijri` | `(options?) => HijriDate \| null` | Convert to Hijri. Returns `null` if the date is outside the calendar range. |
|
||||
| `hijriYear` | `(options?) => number \| null` | Hijri year, or `null` if out of range. |
|
||||
| `hijriMonth` | `(options?) => number \| null` | Hijri month (1-12), or `null` if out of range. |
|
||||
| `hijriDay` | `(options?) => number \| null` | Hijri day, or `null` if out of range. |
|
||||
| `isValidHijri` | `(options?) => boolean` | `true` if the date falls within the supported Hijri range. |
|
||||
| `formatHijri` | `(formatStr, options?) => string` | Format using Hijri tokens. Returns `''` if out of range. Non-Hijri tokens pass through to `moment.format()`. |
|
||||
|
||||
### Static factory
|
||||
|
||||
| Method | Signature | Description |
|
||||
| --- | --- | --- |
|
||||
| `moment.fromHijri` | `(hy, hm, hd, options?) => Moment` | Create a moment from a Hijri date. Throws if the date is invalid or out of range. |
|
||||
|
||||
### Options
|
||||
|
||||
```typescript
|
||||
interface ConversionOptions {
|
||||
calendar?: string; // 'uaq' (default) | 'fcna'
|
||||
}
|
||||
```
|
||||
|
||||
## Calendar Systems
|
||||
|
||||
| ID | Name | Description |
|
||||
| --- | --- | --- |
|
||||
| `uaq` | Umm al-Qura | Official calendar of Saudi Arabia. Tabular, covers AH 1356-1500. Default. |
|
||||
| `fcna` | FCNA/ISNA | Fiqh Council of North America calculated calendar. |
|
||||
|
||||
Pass the calendar ID via `options`:
|
||||
|
||||
```javascript
|
||||
m.toHijri({ calendar: 'fcna' });
|
||||
moment.fromHijri(1444, 9, 1, { calendar: 'fcna' });
|
||||
```
|
||||
|
||||
## Format Tokens
|
||||
|
||||
`formatHijri()` recognises the following tokens. All other tokens are passed through to `moment.format()`, so you can mix Hijri and Gregorian tokens freely.
|
||||
|
||||
| Token | Example | Description |
|
||||
| --- | --- | --- |
|
||||
| `iYYYY` | `1444` | Hijri year, 4 digits |
|
||||
| `iYY` | `44` | Hijri year, 2 digits |
|
||||
| `iMMMM` | `Ramadan` | Month long name |
|
||||
| `iMMM` | `Ramadan` | Month medium name |
|
||||
| `iMM` | `09` | Month, zero-padded |
|
||||
| `iM` | `9` | Month, no padding |
|
||||
| `iDD` | `01` | Day, zero-padded |
|
||||
| `iD` | `1` | Day, no padding |
|
||||
| `iEEEE` | `Yawm al-Khamis` | Weekday long name |
|
||||
| `iEEE` | `Kham` | Weekday short name |
|
||||
| `iE` | `5` | Weekday numeric (1=Sun, 7=Sat) |
|
||||
| `ioooo` | `AH` | Era, long |
|
||||
| `iooo` | `AH` | Era, short |
|
||||
|
||||
### Mixed format example
|
||||
|
||||
```javascript
|
||||
m.formatHijri('iD iMMMM iYYYY [CE:] MMMM YYYY');
|
||||
// => '1 Ramadan 1444 CE: March 2023'
|
||||
```
|
||||
|
||||
Bracket escaping (`[...]`) is handled by moment's own formatter for the Gregorian portion.
|
||||
|
||||
## TypeScript
|
||||
|
||||
The plugin augments `moment.Moment` and `moment.MomentStatic` via module declaration merging, so type safety applies after the plugin is installed. No extra imports are needed for the types.
|
||||
|
||||
```typescript
|
||||
import moment from 'moment';
|
||||
import installHijri from 'moment-hijri-plus';
|
||||
import type { HijriDate, ConversionOptions } from 'moment-hijri-plus';
|
||||
|
||||
installHijri(moment);
|
||||
|
||||
const hijri: HijriDate | null = moment().toHijri();
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Full API reference, architecture notes, and calendar algorithm details are in the [project wiki](https://github.com/acamarata/moment-hijri-plus/wiki).
|
||||
|
||||
## Related
|
||||
|
||||
- [hijri-core](https://github.com/acamarata/hijri-core) — zero-dependency Hijri calendar engine used by this plugin
|
||||
- [luxon-hijri](https://github.com/acamarata/luxon-hijri) — same Hijri support for Luxon
|
||||
- [pray-calc](https://github.com/acamarata/pray-calc) — Islamic prayer time calculation
|
||||
|
||||
## License
|
||||
|
||||
MIT. Copyright (c) 2026 Aric Camarata.
|
||||
60
package.json
Normal file
60
package.json
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"name": "moment-hijri-plus",
|
||||
"version": "1.0.0",
|
||||
"description": "Moment.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": [
|
||||
"moment",
|
||||
"momentjs",
|
||||
"plugin",
|
||||
"hijri",
|
||||
"islamic",
|
||||
"calendar",
|
||||
"umm-al-qura",
|
||||
"fcna",
|
||||
"gregorian",
|
||||
"converter",
|
||||
"typescript"
|
||||
],
|
||||
"peerDependencies": { "moment": "^2.0.0", "hijri-core": "^1.0.0" },
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"hijri-core": "file:../hijri-core",
|
||||
"moment": "^2.30.0",
|
||||
"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/moment-hijri-plus.git" },
|
||||
"homepage": "https://github.com/acamarata/moment-hijri-plus#readme",
|
||||
"bugs": { "url": "https://github.com/acamarata/moment-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
|
||||
hijri-core:
|
||||
specifier: file:../hijri-core
|
||||
version: file:../hijri-core
|
||||
moment:
|
||||
specifier: ^2.30.0
|
||||
version: 2.30.1
|
||||
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}
|
||||
|
||||
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==}
|
||||
|
||||
moment@2.30.1:
|
||||
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
|
||||
|
||||
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: {}
|
||||
|
||||
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
|
||||
|
||||
moment@2.30.1: {}
|
||||
|
||||
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
|
||||
145
src/index.ts
Normal file
145
src/index.ts
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
import moment from 'moment';
|
||||
import {
|
||||
toHijri,
|
||||
toGregorian,
|
||||
hmLong,
|
||||
hmMedium,
|
||||
hwLong,
|
||||
hwShort,
|
||||
hwNumeric,
|
||||
} from 'hijri-core';
|
||||
import type { HijriDate, ConversionOptions } from './types';
|
||||
|
||||
declare module 'moment' {
|
||||
interface Moment {
|
||||
/**
|
||||
* Convert this moment to a Hijri date.
|
||||
* Returns null if the date falls outside the supported calendar range.
|
||||
*/
|
||||
toHijri(options?: ConversionOptions): HijriDate | null;
|
||||
|
||||
/** Return the Hijri year, or null if out of range. */
|
||||
hijriYear(options?: ConversionOptions): number | null;
|
||||
|
||||
/** Return the Hijri month (1-12), or null if out of range. */
|
||||
hijriMonth(options?: ConversionOptions): number | null;
|
||||
|
||||
/** Return the Hijri day, or null if out of range. */
|
||||
hijriDay(options?: ConversionOptions): number | null;
|
||||
|
||||
/** Return true if this moment falls within the supported Hijri range. */
|
||||
isValidHijri(options?: ConversionOptions): boolean;
|
||||
|
||||
/**
|
||||
* Format this moment using Hijri-aware format tokens.
|
||||
*
|
||||
* Hijri tokens: iYYYY iYY iMMMM iMMM iMM iM iDD iD iEEEE iEEE iE ioooo iooo
|
||||
* All other tokens are passed through to moment's own format().
|
||||
*
|
||||
* Returns an empty string if the date is outside the Hijri range.
|
||||
*/
|
||||
formatHijri(formatStr: string, options?: ConversionOptions): string;
|
||||
}
|
||||
}
|
||||
|
||||
// Regex matching all Hijri format tokens. Ordered longest-first so iYYYY is
|
||||
// matched before iYY, iMMMM before iMMM, iDD before iD, iEEEE before iEEE.
|
||||
const HIJRI_TOKEN_RE =
|
||||
/iYYYY|iYY|iMMMM|iMMM|iMM|iM|iDD|iD|iEEEE|iEEE|iE|ioooo|iooo/g;
|
||||
|
||||
/**
|
||||
* Escape a literal string so moment.format() treats it as literal text.
|
||||
* Wraps the value in square brackets, escaping any ] characters within.
|
||||
*/
|
||||
function escapeLiteral(value: string): string {
|
||||
return '[' + value.replace(/]/g, '][]') + ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the Hijri plugin into the provided moment instance.
|
||||
*
|
||||
* @example
|
||||
* import moment from 'moment';
|
||||
* import installHijri from 'moment-hijri-plus';
|
||||
* installHijri(moment);
|
||||
*/
|
||||
function install(momentInstance: typeof moment): void {
|
||||
momentInstance.fn.toHijri = function (opts?: ConversionOptions): HijriDate | null {
|
||||
return toHijri(this.toDate(), opts);
|
||||
};
|
||||
|
||||
momentInstance.fn.hijriYear = function (opts?: ConversionOptions): number | null {
|
||||
return this.toHijri(opts)?.hy ?? null;
|
||||
};
|
||||
|
||||
momentInstance.fn.hijriMonth = function (opts?: ConversionOptions): number | null {
|
||||
return this.toHijri(opts)?.hm ?? null;
|
||||
};
|
||||
|
||||
momentInstance.fn.hijriDay = function (opts?: ConversionOptions): number | null {
|
||||
return this.toHijri(opts)?.hd ?? null;
|
||||
};
|
||||
|
||||
momentInstance.fn.isValidHijri = function (opts?: ConversionOptions): boolean {
|
||||
return this.toHijri(opts) !== null;
|
||||
};
|
||||
|
||||
momentInstance.fn.formatHijri = function (
|
||||
formatStr: string,
|
||||
opts?: ConversionOptions,
|
||||
): string {
|
||||
const hijri = this.toHijri(opts);
|
||||
if (!hijri) return '';
|
||||
const m = this;
|
||||
// Replace Hijri tokens with escaped literals, then pass the residual string
|
||||
// to moment.format() so all standard tokens (YYYY, MMM, etc.) resolve correctly.
|
||||
// Escaping is required because values like "Ramadan" would otherwise be
|
||||
// interpreted by moment as format tokens (R, a, m, etc.).
|
||||
const residual = formatStr.replace(HIJRI_TOKEN_RE, (token: string): string => {
|
||||
switch (token) {
|
||||
case 'iYYYY': return escapeLiteral(String(hijri.hy).padStart(4, '0'));
|
||||
case 'iYY': return escapeLiteral(String(hijri.hy % 100).padStart(2, '0'));
|
||||
case 'iMMMM': return escapeLiteral(hmLong[hijri.hm - 1]);
|
||||
case 'iMMM': return escapeLiteral(hmMedium[hijri.hm - 1]);
|
||||
case 'iMM': return escapeLiteral(String(hijri.hm).padStart(2, '0'));
|
||||
case 'iM': return escapeLiteral(String(hijri.hm));
|
||||
case 'iDD': return escapeLiteral(String(hijri.hd).padStart(2, '0'));
|
||||
case 'iD': return escapeLiteral(String(hijri.hd));
|
||||
case 'iEEEE': return escapeLiteral(hwLong[m.day()]);
|
||||
case 'iEEE': return escapeLiteral(hwShort[m.day()]);
|
||||
case 'iE': return escapeLiteral(String(hwNumeric[m.day()]));
|
||||
// Era tokens: both iooo and ioooo map to the common abbreviation.
|
||||
case 'iooo':
|
||||
case 'ioooo': return escapeLiteral('AH');
|
||||
default: return token;
|
||||
}
|
||||
});
|
||||
return m.format(residual);
|
||||
};
|
||||
|
||||
// Attach fromHijri as a property on the constructor. We use a type assertion
|
||||
// because MomentStatic augmentation produces a DTS visibility error with some
|
||||
// TypeScript configurations — attaching at runtime is equivalent and safe.
|
||||
(momentInstance as unknown as Record<string, unknown>).fromHijri = function (
|
||||
hy: number,
|
||||
hm: number,
|
||||
hd: number,
|
||||
opts?: ConversionOptions,
|
||||
): moment.Moment {
|
||||
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}`);
|
||||
}
|
||||
// Construct from explicit year/month/day to avoid UTC-to-local timezone
|
||||
// shift when the Date object represents midnight UTC.
|
||||
return momentInstance([greg.getUTCFullYear(), greg.getUTCMonth(), greg.getUTCDate()]);
|
||||
};
|
||||
}
|
||||
|
||||
export default install;
|
||||
export type { HijriDate, ConversionOptions } from 'hijri-core';
|
||||
1
src/types.ts
Normal file
1
src/types.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export type { HijriDate, ConversionOptions } from 'hijri-core';
|
||||
75
test-cjs.cjs
Normal file
75
test-cjs.cjs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const moment = require('moment');
|
||||
const installHijri = require('./dist/index.cjs');
|
||||
|
||||
installHijri.default(moment);
|
||||
|
||||
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.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Plugin installs
|
||||
test('plugin installs (CJS)', () => {
|
||||
assert.equal(typeof moment.fn.toHijri, 'function');
|
||||
assert.equal(typeof moment.fn.formatHijri, 'function');
|
||||
assert.equal(typeof moment.fromHijri, 'function');
|
||||
});
|
||||
|
||||
// 2. toHijri
|
||||
test('toHijri: 2023-03-23 => 1444/9/1 (CJS)', () => {
|
||||
const h = moment(new Date(2023, 2, 23, 12)).toHijri();
|
||||
assert.notEqual(h, null);
|
||||
assert.equal(h.hy, 1444);
|
||||
assert.equal(h.hm, 9);
|
||||
assert.equal(h.hd, 1);
|
||||
});
|
||||
|
||||
// 3. fromHijri
|
||||
test('fromHijri: 1444/9/1 => 2023-03-23 (CJS)', () => {
|
||||
const d = moment.fromHijri(1444, 9, 1).toDate();
|
||||
assert.equal(d.getFullYear(), 2023);
|
||||
assert.equal(d.getMonth(), 2);
|
||||
assert.equal(d.getDate(), 23);
|
||||
});
|
||||
|
||||
// 4. formatHijri: numeric
|
||||
test('formatHijri: iYYYY-iMM-iDD (CJS)', () => {
|
||||
const result = moment(new Date(2023, 2, 23, 12)).formatHijri('iYYYY-iMM-iDD');
|
||||
assert.equal(result, '1444-09-01');
|
||||
});
|
||||
|
||||
// 5. formatHijri: month name
|
||||
test('formatHijri: iMMMM => Ramadan (CJS)', () => {
|
||||
const result = moment(new Date(2023, 2, 23, 12)).formatHijri('iMMMM');
|
||||
assert.equal(result, 'Ramadan');
|
||||
});
|
||||
|
||||
// 6. fromHijri throws for invalid date
|
||||
test('fromHijri throws on out-of-range date (CJS)', () => {
|
||||
assert.throws(() => moment.fromHijri(999, 1, 1), /Invalid or out-of-range/);
|
||||
});
|
||||
|
||||
// 7. hijriYear accessor
|
||||
test('hijriYear: 1444 (CJS)', () => {
|
||||
assert.equal(moment(new Date(2023, 2, 23, 12)).hijriYear(), 1444);
|
||||
});
|
||||
|
||||
// 8. isValidHijri
|
||||
test('isValidHijri: true for valid date (CJS)', () => {
|
||||
assert.equal(moment(new Date(2023, 2, 23, 12)).isValidHijri(), true);
|
||||
});
|
||||
|
||||
console.log(`\n${passed}/${total} tests passed`);
|
||||
129
test.mjs
Normal file
129
test.mjs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import moment from 'moment';
|
||||
import installHijri from './dist/index.mjs';
|
||||
|
||||
installHijri(moment);
|
||||
|
||||
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.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Plugin installs
|
||||
test('plugin installs', () => {
|
||||
assert.equal(typeof moment.fn.toHijri, 'function');
|
||||
assert.equal(typeof moment.fn.hijriYear, 'function');
|
||||
assert.equal(typeof moment.fn.hijriMonth, 'function');
|
||||
assert.equal(typeof moment.fn.hijriDay, 'function');
|
||||
assert.equal(typeof moment.fn.isValidHijri, 'function');
|
||||
assert.equal(typeof moment.fn.formatHijri, 'function');
|
||||
assert.equal(typeof moment.fromHijri, 'function');
|
||||
});
|
||||
|
||||
// 2. toHijri: 1 Ramadan 1444 AH
|
||||
test('toHijri: 2023-03-23 => 1444/9/1', () => {
|
||||
const h = moment(new Date(2023, 2, 23, 12)).toHijri();
|
||||
assert.notEqual(h, null);
|
||||
assert.equal(h.hy, 1444);
|
||||
assert.equal(h.hm, 9);
|
||||
assert.equal(h.hd, 1);
|
||||
});
|
||||
|
||||
// 3. toHijri: 1 Muharram 1446 AH
|
||||
test('toHijri: 2024-07-07 => 1446/1/1', () => {
|
||||
const h = moment(new Date(2024, 6, 7, 12)).toHijri();
|
||||
assert.notEqual(h, null);
|
||||
assert.equal(h.hy, 1446);
|
||||
assert.equal(h.hm, 1);
|
||||
assert.equal(h.hd, 1);
|
||||
});
|
||||
|
||||
// 4. fromHijri: 1444/9/1 => 2023-03-23
|
||||
test('fromHijri: 1444/9/1 => 2023-03-23', () => {
|
||||
const m = moment.fromHijri(1444, 9, 1);
|
||||
const d = m.toDate();
|
||||
assert.equal(d.getFullYear(), 2023);
|
||||
assert.equal(d.getMonth(), 2); // March = 2
|
||||
assert.equal(d.getDate(), 23);
|
||||
});
|
||||
|
||||
// 5. fromHijri: 1446/1/1 => 2024-07-07
|
||||
test('fromHijri: 1446/1/1 => 2024-07-07', () => {
|
||||
const m = moment.fromHijri(1446, 1, 1);
|
||||
const d = m.toDate();
|
||||
assert.equal(d.getFullYear(), 2024);
|
||||
assert.equal(d.getMonth(), 6); // July = 6
|
||||
assert.equal(d.getDate(), 7);
|
||||
});
|
||||
|
||||
// 6. hijriYear / hijriMonth / hijriDay
|
||||
test('hijriYear, hijriMonth, hijriDay on 1 Ramadan 1444', () => {
|
||||
const m = moment(new Date(2023, 2, 23, 12));
|
||||
assert.equal(m.hijriYear(), 1444);
|
||||
assert.equal(m.hijriMonth(), 9);
|
||||
assert.equal(m.hijriDay(), 1);
|
||||
});
|
||||
|
||||
// 7. formatHijri: numeric format
|
||||
test('formatHijri: iYYYY-iMM-iDD', () => {
|
||||
const result = moment(new Date(2023, 2, 23, 12)).formatHijri('iYYYY-iMM-iDD');
|
||||
assert.equal(result, '1444-09-01');
|
||||
});
|
||||
|
||||
// 8. formatHijri: long month name
|
||||
test('formatHijri: iMMMM => Ramadan', () => {
|
||||
const result = moment(new Date(2023, 2, 23, 12)).formatHijri('iMMMM');
|
||||
assert.equal(result, 'Ramadan');
|
||||
});
|
||||
|
||||
// 9. formatHijri: long weekday name (Thursday = Yawm al-Khamis)
|
||||
test('formatHijri: iEEEE on Thursday 2023-03-23', () => {
|
||||
const result = moment(new Date(2023, 2, 23, 12)).formatHijri('iEEEE');
|
||||
assert.equal(result, 'Yawm al-Khamis');
|
||||
});
|
||||
|
||||
// 10. formatHijri: era token
|
||||
test('formatHijri: ioooo => AH', () => {
|
||||
const result = moment(new Date(2023, 2, 23, 12)).formatHijri('ioooo');
|
||||
assert.equal(result, 'AH');
|
||||
});
|
||||
|
||||
// 11. isValidHijri: returns true for in-range date
|
||||
test('isValidHijri: true for valid date', () => {
|
||||
assert.equal(moment(new Date(2023, 2, 23, 12)).isValidHijri(), true);
|
||||
});
|
||||
|
||||
// 12. FCNA calendar option
|
||||
test('toHijri with { calendar: fcna } returns a HijriDate', () => {
|
||||
const h = moment(new Date(2023, 2, 23, 12)).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');
|
||||
});
|
||||
|
||||
// 13. fromHijri throws for out-of-range date
|
||||
test('fromHijri throws on out-of-range Hijri date', () => {
|
||||
assert.throws(() => moment.fromHijri(999, 1, 1), /Invalid or out-of-range/);
|
||||
});
|
||||
|
||||
// 14. formatHijri: mixed Hijri and Gregorian tokens
|
||||
test('formatHijri: mixed Hijri and Gregorian tokens', () => {
|
||||
const m = moment(new Date(2023, 2, 23, 12));
|
||||
const result = m.formatHijri('iYYYY [CE:] YYYY');
|
||||
// Hijri year should be 1444; Gregorian year should be 2023.
|
||||
assert.ok(result.includes('1444'), `Expected Hijri year in: ${result}`);
|
||||
assert.ok(result.includes('2023'), `Expected Gregorian year in: ${result}`);
|
||||
});
|
||||
|
||||
console.log(`\n${passed}/${total} tests passed`);
|
||||
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": 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: ['moment', 'hijri-core'],
|
||||
outExtension({ format }) {
|
||||
return { js: format === 'esm' ? '.mjs' : '.cjs' };
|
||||
},
|
||||
});
|
||||
Loading…
Reference in a new issue