@hebcal/icalendar 6.3.8 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/foldLine.js +1 -5
- package/dist/icalendar.d.ts +21 -14
- package/dist/icalendar.js +19 -100
- package/dist/pkgVersion.d.ts +1 -1
- package/dist/pkgVersion.js +1 -4
- package/package.json +20 -21
package/README.md
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
# @hebcal/icalendar
|
|
2
|
+
|
|
2
3
|
Jewish holidays and Hebrew calendar as iCalendar RFC 2445
|
|
3
4
|
|
|
4
5
|
## Installation
|
|
6
|
+
|
|
5
7
|
```bash
|
|
6
8
|
$ npm install @hebcal/icalendar
|
|
7
9
|
```
|
|
8
10
|
|
|
9
11
|
## Synopsis
|
|
12
|
+
|
|
10
13
|
```javascript
|
|
11
14
|
import {HebrewCalendar, Location} from '@hebcal/core';
|
|
12
15
|
import {eventsToIcalendar} from '@hebcal/icalendar';
|
|
@@ -24,7 +27,7 @@ const events = HebrewCalendar.calendar(options);
|
|
|
24
27
|
const str = await eventsToIcalendar(events, {
|
|
25
28
|
locale: 'he',
|
|
26
29
|
...options,
|
|
27
|
-
})
|
|
30
|
+
});
|
|
28
31
|
|
|
29
32
|
const icalStream = fs.createWriteStream('feed.ics');
|
|
30
33
|
icalStream.write(str);
|
package/dist/foldLine.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
/*! @hebcal/icalendar v6.3.8 */
|
|
2
1
|
import { Buffer } from 'node:buffer';
|
|
3
|
-
|
|
4
2
|
let foldSegmenter;
|
|
5
3
|
const char74re = /.{1,74}/g;
|
|
6
|
-
function foldLine(line) {
|
|
4
|
+
export function foldLine(line) {
|
|
7
5
|
const lineBytes = Buffer.byteLength(line);
|
|
8
6
|
if (lineBytes <= 74) {
|
|
9
7
|
return line;
|
|
@@ -47,5 +45,3 @@ function foldLine(line) {
|
|
|
47
45
|
parts.push(line.slice(chunkStart));
|
|
48
46
|
return parts.join('\r\n ');
|
|
49
47
|
}
|
|
50
|
-
|
|
51
|
-
export { foldLine };
|
package/dist/icalendar.d.ts
CHANGED
|
@@ -3,6 +3,13 @@ import { CalOptions } from '@hebcal/core/dist/esm/CalOptions';
|
|
|
3
3
|
export type ICalEventOptions = {
|
|
4
4
|
dtstamp?: string;
|
|
5
5
|
sequence?: number;
|
|
6
|
+
/**
|
|
7
|
+
* Text for the `DESCRIPTION` property, overriding `ev.memo`. This is
|
|
8
|
+
* per-event, so pass a fresh options object to each `IcalEvent` (for
|
|
9
|
+
* example `new IcalEvent(ev, {...opts, memo})`) rather than setting it on
|
|
10
|
+
* an options object shared by a whole calendar.
|
|
11
|
+
*/
|
|
12
|
+
memo?: string;
|
|
6
13
|
emoji?: boolean;
|
|
7
14
|
utmSource?: string;
|
|
8
15
|
utmMedium?: string;
|
|
@@ -22,20 +29,20 @@ export type ICalOptions = CalOptions & ICalEventOptions;
|
|
|
22
29
|
* Represents an RFC 2445 iCalendar VEVENT
|
|
23
30
|
*/
|
|
24
31
|
export declare class IcalEvent {
|
|
25
|
-
ev: Event;
|
|
26
|
-
options: ICalOptions;
|
|
27
|
-
dtstamp: string;
|
|
28
|
-
sequence?: number;
|
|
29
|
-
timed: boolean;
|
|
30
|
-
locationName: string | null | undefined;
|
|
31
|
-
startDate: string;
|
|
32
|
-
isoDateOnly: string;
|
|
33
|
-
dtargs: string;
|
|
34
|
-
transp: string;
|
|
35
|
-
busyStatus: string;
|
|
36
|
-
endDate: string;
|
|
37
|
-
subj: string;
|
|
38
|
-
category: string | null;
|
|
32
|
+
readonly ev: Event;
|
|
33
|
+
readonly options: ICalOptions;
|
|
34
|
+
readonly dtstamp: string;
|
|
35
|
+
readonly sequence?: number;
|
|
36
|
+
readonly timed: boolean;
|
|
37
|
+
readonly locationName: string | null | undefined;
|
|
38
|
+
readonly startDate: string;
|
|
39
|
+
readonly isoDateOnly: string;
|
|
40
|
+
readonly dtargs: string;
|
|
41
|
+
readonly transp: string;
|
|
42
|
+
readonly busyStatus: string;
|
|
43
|
+
readonly endDate: string;
|
|
44
|
+
readonly subj: string;
|
|
45
|
+
readonly category: string | null;
|
|
39
46
|
lines?: string[];
|
|
40
47
|
/**
|
|
41
48
|
* Builds an IcalEvent object from a Hebcal Event
|
package/dist/icalendar.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
/*! @hebcal/icalendar v6.3.8 */
|
|
2
1
|
import { flags } from '@hebcal/core/dist/esm/event';
|
|
3
2
|
import { Locale } from '@hebcal/core/dist/esm/locale';
|
|
4
3
|
import { murmur32HexSync } from 'murmurhash3';
|
|
5
|
-
import { pad2,
|
|
6
|
-
import {
|
|
7
|
-
import { getHolidayDescription } from '@hebcal/rest-api/dist/esm/holiday';
|
|
8
|
-
import { makeTorahMemoText } from '@hebcal/rest-api/dist/esm/memo';
|
|
4
|
+
import { pad2, pad4, isDate } from '@hebcal/hdate';
|
|
5
|
+
import { getCalendarTitle, getEventCategories, shouldRenderBrief, } from '@hebcal/rest-api/dist/esm/common';
|
|
9
6
|
import { appendIsraelAndTracking } from '@hebcal/rest-api/dist/esm/url';
|
|
10
7
|
import { makeAnchor } from '@hebcal/rest-api/dist/esm/makeAnchor';
|
|
11
|
-
import { promises } from 'node:fs';
|
|
8
|
+
import { promises as fs } from 'node:fs';
|
|
12
9
|
import { version } from './pkgVersion.js';
|
|
13
10
|
import { foldLine } from './foldLine.js';
|
|
14
|
-
|
|
11
|
+
const ESC_NEWLINE = String.raw `\n`;
|
|
15
12
|
const vtimezoneCache = new Map();
|
|
16
13
|
const CATEGORY = {
|
|
17
14
|
candles: 'Holiday',
|
|
@@ -65,7 +62,7 @@ const DAILY_LEARNING = flags.DAILY_LEARNING |
|
|
|
65
62
|
/**
|
|
66
63
|
* Represents an RFC 2445 iCalendar VEVENT
|
|
67
64
|
*/
|
|
68
|
-
class IcalEvent {
|
|
65
|
+
export class IcalEvent {
|
|
69
66
|
ev;
|
|
70
67
|
options;
|
|
71
68
|
dtstamp;
|
|
@@ -160,7 +157,9 @@ class IcalEvent {
|
|
|
160
157
|
}
|
|
161
158
|
}
|
|
162
159
|
this.subj = subj;
|
|
163
|
-
this.category =
|
|
160
|
+
this.category =
|
|
161
|
+
ev0.category ||
|
|
162
|
+
(CATEGORY[getEventCategories(ev)?.[0]] ?? null);
|
|
164
163
|
}
|
|
165
164
|
getAlarm() {
|
|
166
165
|
const ev = this.ev;
|
|
@@ -235,9 +234,13 @@ class IcalEvent {
|
|
|
235
234
|
arr.push('CLASS:PUBLIC');
|
|
236
235
|
}
|
|
237
236
|
const options = this.options;
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
// The caller is responsible for building the memo (holiday description,
|
|
238
|
+
// Torah reading, URL, etc), either as `options.memo` or on the event
|
|
239
|
+
// itself; we only escape newlines for RFC 5545
|
|
240
|
+
const memo = options.memo ?? ev.memo;
|
|
241
|
+
if (memo) {
|
|
242
|
+
addOptional(arr, 'DESCRIPTION', memo.replaceAll('\n', ESC_NEWLINE));
|
|
243
|
+
}
|
|
241
244
|
addOptional(arr, 'LOCATION', this.locationName);
|
|
242
245
|
const loc = options.location;
|
|
243
246
|
if (this.timed && loc) {
|
|
@@ -305,96 +308,14 @@ class IcalEvent {
|
|
|
305
308
|
* Transforms a single Event into a VEVENT string
|
|
306
309
|
* @returns multi-line result, delimited by \r\n
|
|
307
310
|
*/
|
|
308
|
-
function eventToIcal(ev, options) {
|
|
311
|
+
export function eventToIcal(ev, options) {
|
|
309
312
|
const ical = new IcalEvent(ev, options);
|
|
310
313
|
return ical.toString();
|
|
311
314
|
}
|
|
312
|
-
const torahMemoCache = new Map();
|
|
313
|
-
const HOLIDAY_IGNORE_MASK = DAILY_LEARNING |
|
|
314
|
-
flags.OMER_COUNT |
|
|
315
|
-
flags.SHABBAT_MEVARCHIM |
|
|
316
|
-
flags.MOLAD |
|
|
317
|
-
flags.USER_EVENT |
|
|
318
|
-
flags.HEBREW_DATE;
|
|
319
|
-
const ESC_NEWLINE = String.raw `\n`;
|
|
320
|
-
const DBL_NEWLINE = ESC_NEWLINE + ESC_NEWLINE;
|
|
321
|
-
/**
|
|
322
|
-
* @private
|
|
323
|
-
*/
|
|
324
|
-
function makeTorahMemo(ev, il) {
|
|
325
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
326
|
-
if (ev.getFlags() & HOLIDAY_IGNORE_MASK || ev.eventTime) {
|
|
327
|
-
return '';
|
|
328
|
-
}
|
|
329
|
-
const hd = ev.getDate();
|
|
330
|
-
const yy = hd.getFullYear();
|
|
331
|
-
const mm = hd.getMonth();
|
|
332
|
-
const dd = hd.getDate();
|
|
333
|
-
const key = [yy, mm, dd, il ? '1' : '0', ev.getDesc()].join('-');
|
|
334
|
-
let memo = torahMemoCache.get(key);
|
|
335
|
-
if (typeof memo === 'string') {
|
|
336
|
-
return memo;
|
|
337
|
-
}
|
|
338
|
-
memo = makeTorahMemoText(ev, il).replaceAll('\n', ESC_NEWLINE);
|
|
339
|
-
torahMemoCache.set(key, memo);
|
|
340
|
-
return memo;
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* @private
|
|
344
|
-
*/
|
|
345
|
-
function createMemo(ev, options) {
|
|
346
|
-
let memo = ev.memo || '';
|
|
347
|
-
if (memo.length && memo.includes('\n')) {
|
|
348
|
-
memo = memo.replaceAll('\n', ESC_NEWLINE);
|
|
349
|
-
}
|
|
350
|
-
const desc = ev.getDesc();
|
|
351
|
-
if (desc === 'Havdalah' || desc === 'Candle lighting') {
|
|
352
|
-
return memo;
|
|
353
|
-
}
|
|
354
|
-
const mask = ev.getFlags();
|
|
355
|
-
if (mask & flags.OMER_COUNT) {
|
|
356
|
-
const omerEv = ev;
|
|
357
|
-
const sefira = [
|
|
358
|
-
omerEv.sefira('en'),
|
|
359
|
-
omerEv.sefira('he'),
|
|
360
|
-
omerEv.sefira('translit'),
|
|
361
|
-
].join(ESC_NEWLINE);
|
|
362
|
-
return (omerEv.getTodayIs('en') +
|
|
363
|
-
DBL_NEWLINE +
|
|
364
|
-
omerEv.getTodayIs('he') +
|
|
365
|
-
DBL_NEWLINE +
|
|
366
|
-
sefira);
|
|
367
|
-
}
|
|
368
|
-
if (!memo) {
|
|
369
|
-
memo = getHolidayDescription(ev);
|
|
370
|
-
}
|
|
371
|
-
if (!memo) {
|
|
372
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
373
|
-
const linkEv = ev.linkedEvent;
|
|
374
|
-
if (linkEv && linkEv.getDesc() !== ev.getDesc()) {
|
|
375
|
-
memo = linkEv.render(options.locale);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
const torahMemo = makeTorahMemo(ev, options.il);
|
|
379
|
-
if (torahMemo) {
|
|
380
|
-
if (memo.length) {
|
|
381
|
-
memo += DBL_NEWLINE;
|
|
382
|
-
}
|
|
383
|
-
memo += torahMemo;
|
|
384
|
-
}
|
|
385
|
-
const url = appendTrackingToUrl(ev.url(), options);
|
|
386
|
-
if (url) {
|
|
387
|
-
if (memo.length) {
|
|
388
|
-
memo += DBL_NEWLINE;
|
|
389
|
-
}
|
|
390
|
-
memo += url;
|
|
391
|
-
}
|
|
392
|
-
return memo;
|
|
393
|
-
}
|
|
394
315
|
/**
|
|
395
316
|
* Generates an RFC 2445 iCalendar string from an array of events
|
|
396
317
|
*/
|
|
397
|
-
async function eventsToIcalendar(events, options) {
|
|
318
|
+
export async function eventsToIcalendar(events, options) {
|
|
398
319
|
if (!events.length)
|
|
399
320
|
throw new RangeError('Events can not be empty');
|
|
400
321
|
if (!options)
|
|
@@ -423,7 +344,7 @@ async function getVtimezone(tzid) {
|
|
|
423
344
|
}
|
|
424
345
|
const filename = `./zoneinfo/${tzid}.ics`;
|
|
425
346
|
try {
|
|
426
|
-
const contents = await
|
|
347
|
+
const contents = await fs.readFile(filename, 'utf-8');
|
|
427
348
|
const lines = contents.split('\r\n');
|
|
428
349
|
// ignore first 3 and last 1 lines
|
|
429
350
|
const str = lines.slice(3, lines.length - 2).join('\r\n');
|
|
@@ -472,7 +393,7 @@ function makeIcalPreamble(opts) {
|
|
|
472
393
|
/**
|
|
473
394
|
* Generates an RFC 2445 iCalendar string from an array of IcalEvents
|
|
474
395
|
*/
|
|
475
|
-
async function icalEventsToString(icals, options) {
|
|
396
|
+
export async function icalEventsToString(icals, options) {
|
|
476
397
|
if (!icals.length)
|
|
477
398
|
throw new RangeError('Events can not be empty');
|
|
478
399
|
if (!options)
|
|
@@ -503,5 +424,3 @@ async function icalEventsToString(icals, options) {
|
|
|
503
424
|
stream.push('END:VCALENDAR\r\n');
|
|
504
425
|
return stream.join('');
|
|
505
426
|
}
|
|
506
|
-
|
|
507
|
-
export { IcalEvent, eventToIcal, eventsToIcalendar, icalEventsToString };
|
package/dist/pkgVersion.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "
|
|
1
|
+
export declare const version = "7.0.0";
|
package/dist/pkgVersion.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hebcal/icalendar",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"author": "Michael J. Radwin (https://github.com/mjradwin)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ical",
|
|
@@ -30,33 +30,32 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://hebcal.github.io/api/icalendar/",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@hebcal/core": "^6.
|
|
34
|
-
"@hebcal/hdate": "^0.22.
|
|
35
|
-
"@hebcal/rest-api": "^
|
|
36
|
-
"murmurhash3": "^0.5.0"
|
|
37
|
-
"tslib": "^2.8.1"
|
|
33
|
+
"@hebcal/core": "^6.8.2",
|
|
34
|
+
"@hebcal/hdate": "^0.22.5",
|
|
35
|
+
"@hebcal/rest-api": "^7.0.0",
|
|
36
|
+
"murmurhash3": "^0.5.0"
|
|
38
37
|
},
|
|
39
38
|
"scripts": {
|
|
40
|
-
"build:
|
|
41
|
-
"build
|
|
42
|
-
"build": "npm run build:version && npm run build:rollup",
|
|
39
|
+
"build:version": "node ./version.js package.json src/pkgVersion.ts",
|
|
40
|
+
"build": "npm run build:version && tsc",
|
|
43
41
|
"pretest": "npm run build",
|
|
44
|
-
"docs": "typedoc",
|
|
42
|
+
"docs": "npx -y typedoc",
|
|
45
43
|
"test": "vitest",
|
|
46
|
-
"lint": "
|
|
47
|
-
"clean": "
|
|
44
|
+
"lint": "oxlint && prettier --check .",
|
|
45
|
+
"clean": "rm -rf dist",
|
|
48
46
|
"compile": "tsc",
|
|
49
|
-
"fix": "
|
|
47
|
+
"fix": "oxlint --fix && prettier --write ."
|
|
50
48
|
},
|
|
51
49
|
"license": "BSD-2-Clause",
|
|
52
50
|
"devDependencies": {
|
|
53
|
-
"@hebcal/learning": "^6.
|
|
54
|
-
"@
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
51
|
+
"@hebcal/learning": "^6.9.5",
|
|
52
|
+
"@types/node": "26.1.1",
|
|
53
|
+
"oxlint": "^1.76.0",
|
|
54
|
+
"prettier": "^3.9.6",
|
|
55
|
+
"typescript": "^7.0.2",
|
|
56
|
+
"vitest": "^4.1.10"
|
|
57
|
+
},
|
|
58
|
+
"allowScripts": {
|
|
59
|
+
"murmurhash3": true
|
|
61
60
|
}
|
|
62
61
|
}
|