@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 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 };
@@ -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, isDate, pad4 } from '@hebcal/hdate';
6
- import { shouldRenderBrief, getEventCategories, getCalendarTitle } from '@hebcal/rest-api/dist/esm/common';
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 = ev0.category || CATEGORY[getEventCategories(ev)?.[0]];
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
- // create memo (holiday descr, Torah, etc)
239
- const memo = createMemo(ev, options);
240
- addOptional(arr, 'DESCRIPTION', memo);
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 promises.readFile(filename, 'utf-8');
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 };
@@ -1 +1 @@
1
- export declare const version = "6.3.8";
1
+ export declare const version = "7.0.0";
@@ -1,5 +1,2 @@
1
- /*! @hebcal/icalendar v6.3.8 */
2
1
  // DO NOT EDIT THIS AUTO-GENERATED FILE!
3
- const version = '6.3.8';
4
-
5
- export { version };
2
+ export const version = '7.0.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hebcal/icalendar",
3
- "version": "6.3.8",
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.3.3",
34
- "@hebcal/hdate": "^0.22.2",
35
- "@hebcal/rest-api": "^6.4.7",
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:rollup": "rollup -c",
41
- "build:version": "node ./version.cjs package.json src/pkgVersion.ts",
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": "gts lint",
47
- "clean": "gts clean",
44
+ "lint": "oxlint && prettier --check .",
45
+ "clean": "rm -rf dist",
48
46
  "compile": "tsc",
49
- "fix": "gts fix"
47
+ "fix": "oxlint --fix && prettier --write ."
50
48
  },
51
49
  "license": "BSD-2-Clause",
52
50
  "devDependencies": {
53
- "@hebcal/learning": "^6.8.0",
54
- "@rollup/plugin-typescript": "^12.3.0",
55
- "@types/node": "25.6.0",
56
- "gts": "^7.0.0",
57
- "rollup": "^4.60.2",
58
- "typedoc": "^0.28.19",
59
- "typescript": "^6.0.3",
60
- "vitest": "^4.1.5"
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
  }