mirror of
https://github.com/acamarata/moment-hijri-plus.git
synced 2026-06-30 18:54:29 +00:00
chore: CR/QA polish for v1.0.0 release
Fix documentation style (no em dashes). Update hijri-core devDep from file: path to ^1.0.0. Add wiki-sync workflow permissions. Correct UAQ range to 1318-1500 AH / 1900-2076 CE throughout.
This commit is contained in:
parent
60690bd8c9
commit
a872c5b5ed
10 changed files with 46 additions and 32 deletions
3
.github/workflows/wiki-sync.yml
vendored
3
.github/workflows/wiki-sync.yml
vendored
|
|
@ -6,6 +6,9 @@ on:
|
|||
paths:
|
||||
- '.wiki/**'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync .wiki/ to GitHub Wiki
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ 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).
|
||||
Returns `null` if the date falls outside the supported calendar range (UAQ covers AH 1318-1500, approximately CE 1900-2076).
|
||||
|
||||
```javascript
|
||||
const h = moment(new Date(2023, 2, 23)).toHijri();
|
||||
|
|
@ -162,8 +162,8 @@ interface ConversionOptions {
|
|||
|
||||
| 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 |
|
||||
| `uaq` | Umm al-Qura: official Saudi calendar, tabular, covers AH 1318-1500 |
|
||||
| `fcna` | FCNA/ISNA: Fiqh Council of North America calculated calendar |
|
||||
|
||||
Custom calendars can be registered with hijri-core's `registerCalendar()`.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
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.
|
||||
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
|
||||
|
||||
|
|
@ -15,25 +15,25 @@ 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.
|
||||
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:
|
||||
The TypeScript types are added to `moment.Moment` and `moment.MomentStatic` via declaration merging:
|
||||
|
||||
```typescript
|
||||
declare module 'moment' {
|
||||
interface MomentStatic {
|
||||
fromHijri(hy: number, hm: number, hd: number, options?: ConversionOptions): 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.
|
||||
The augmentation is emitted in the declaration files produced by tsup, so consumers get full type inference without extra imports.
|
||||
|
||||
## Format token system
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ The regex is ordered longest-match-first to prevent prefix collisions:
|
|||
|
||||
`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.
|
||||
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
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ 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.
|
||||
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
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ Both `moment` and `hijri-core` are marked external, so they are not bundled. The
|
|||
|
||||
| Calendar | ID | Range | Authority |
|
||||
| --- | --- | --- | --- |
|
||||
| Umm al-Qura | `uaq` | AH 1356-1500 (approx CE 1937-2077) | Official Saudi calendar |
|
||||
| Umm al-Qura | `uaq` | AH 1318-1500 (approx CE 1900-2076) | 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.
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ A Moment.js plugin for Hijri calendar conversion and formatting. All calendar ar
|
|||
|
||||
## Pages
|
||||
|
||||
- [API Reference](API-Reference) — complete method signatures and examples
|
||||
- [Architecture](Architecture) — design rationale, token system, calendar delegation
|
||||
- [API Reference](API-Reference): complete method signatures and examples
|
||||
- [Architecture](Architecture): design rationale, token system, calendar delegation
|
||||
|
||||
## Quick start
|
||||
|
||||
|
|
@ -38,9 +38,9 @@ moment.fromHijri(1446, 1, 1).format('YYYY-MM-DD');
|
|||
|
||||
## 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
|
||||
- [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
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ interface ConversionOptions {
|
|||
|
||||
| ID | Name | Description |
|
||||
| --- | --- | --- |
|
||||
| `uaq` | Umm al-Qura | Official calendar of Saudi Arabia. Tabular, covers AH 1356-1500. Default. |
|
||||
| `uaq` | Umm al-Qura | Official calendar of Saudi Arabia. Tabular, covers AH 1318-1500 (1900-2076 CE). Default. |
|
||||
| `fcna` | FCNA/ISNA | Fiqh Council of North America calculated calendar. |
|
||||
|
||||
Pass the calendar ID via `options`:
|
||||
|
|
@ -129,9 +129,9 @@ Full API reference, architecture notes, and calendar algorithm details are in th
|
|||
|
||||
## 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
|
||||
- [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
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
"peerDependencies": { "moment": "^2.0.0", "hijri-core": "^1.0.0" },
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"hijri-core": "file:../hijri-core",
|
||||
"hijri-core": "^1.0.0",
|
||||
"moment": "^2.30.0",
|
||||
"tsup": "^8.0.0",
|
||||
"typescript": "^5.5.0"
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ importers:
|
|||
specifier: ^22.0.0
|
||||
version: 22.19.11
|
||||
hijri-core:
|
||||
specifier: file:../hijri-core
|
||||
version: file:../hijri-core
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
moment:
|
||||
specifier: ^2.30.0
|
||||
version: 2.30.1
|
||||
|
|
@ -403,8 +403,8 @@ packages:
|
|||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
hijri-core@file:../hijri-core:
|
||||
resolution: {directory: ../hijri-core, type: directory}
|
||||
hijri-core@1.0.0:
|
||||
resolution: {integrity: sha512-wImBZLBKbEWEEUE1nrc1CFY/uvx4XjGNWYChImJZlswXIVhrBCzSVaj6DP1AU2gUMJ6KDh2ygXo/u/Qx232CXA==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
joycon@3.1.1:
|
||||
|
|
@ -791,7 +791,7 @@ snapshots:
|
|||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
hijri-core@file:../hijri-core: {}
|
||||
hijri-core@1.0.0: {}
|
||||
|
||||
joycon@3.1.1: {}
|
||||
|
||||
|
|
|
|||
15
src/index.ts
15
src/index.ts
|
|
@ -1,4 +1,5 @@
|
|||
import moment from 'moment';
|
||||
import type { Moment as MomentInstance } from 'moment';
|
||||
import {
|
||||
toHijri,
|
||||
toGregorian,
|
||||
|
|
@ -11,6 +12,15 @@ import {
|
|||
import type { HijriDate, ConversionOptions } from './types';
|
||||
|
||||
declare module 'moment' {
|
||||
interface MomentStatic {
|
||||
/**
|
||||
* Construct a moment from a Hijri date.
|
||||
* Throws if the date is invalid or outside the supported range.
|
||||
* Call installHijri(moment) before use.
|
||||
*/
|
||||
fromHijri(hy: number, hm: number, hd: number, options?: ConversionOptions): MomentInstance;
|
||||
}
|
||||
|
||||
interface Moment {
|
||||
/**
|
||||
* Convert this moment to a Hijri date.
|
||||
|
|
@ -119,7 +129,7 @@ function install(momentInstance: typeof moment): void {
|
|||
|
||||
// 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.
|
||||
// TypeScript configurations; attaching at runtime is equivalent and safe.
|
||||
(momentInstance as unknown as Record<string, unknown>).fromHijri = function (
|
||||
hy: number,
|
||||
hm: number,
|
||||
|
|
@ -142,4 +152,5 @@ function install(momentInstance: typeof moment): void {
|
|||
}
|
||||
|
||||
export default install;
|
||||
export type { HijriDate, ConversionOptions } from 'hijri-core';
|
||||
export type { HijriDate, ConversionOptions, CalendarEngine } from 'hijri-core';
|
||||
export { registerCalendar, getCalendar, listCalendars } from 'hijri-core';
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ function test(name, fn) {
|
|||
passed++;
|
||||
} catch (err) {
|
||||
console.error(`[${name}]... FAIL: ${err.message}`);
|
||||
process.exitCode = 1;
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
2
test.mjs
2
test.mjs
|
|
@ -15,7 +15,7 @@ function test(name, fn) {
|
|||
passed++;
|
||||
} catch (err) {
|
||||
console.error(`[${name}]... FAIL: ${err.message}`);
|
||||
process.exitCode = 1;
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue