node-icalendar

iCalendar (RFC 5545) parser and generator for Node.js, with a streaming API for building and reading VCALENDAR data.

Library
npm
v0.7.1
233stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
36/100Needs Attention
Development Activity0
Maintenance0
Community64
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
52/100Fair
Architecture68
Code Quality45
Innovation40
Learning Curve55

node-icalendar (published to npm as icalendar) is a Node.js library for parsing and generating iCalendar data as defined in RFC 5545. It models calendars, events, alarms, and timezones as JavaScript objects with a small property/component API, and can serialize any of them back into valid iCalendar text or a readable stream.

The library predates modern async/await Node and is built directly on top of the readable-stream polyfill, so every calendar object doubles as a Node stream you can pipe to a file or HTTP response. It supports most of RFC 5545, including recurrence rules (RRULE), but the maintainers explicitly documented gaps — HOURLY/MINUTELY/SECONDLY recurrence, BYSETPOS, WKST, BYWEEKNO, BYYEARDAY, and RDATE are unimplemented — and the project has had no commits since 2020.

What You Get

  • A parse_calendar() function that turns raw .ics text into a navigable iCalendar object with .events() and .timezone() accessors
  • Constructors for the core RFC 5545 components — iCalendar, VEvent, VAlarm, VTimezone — each with typed property getters/setters like setSummary() and setDate()
  • A CalendarObject base class every component inherits from, extending Node’s Readable stream so any calendar or event can be piped directly to a file or HTTP response
  • Built-in RFC 5545 value-type formatting/parsing (format_value/parse_value) for dates, durations, periods, and recurrence rules via the RRule class

Common Use Cases

  • Generating .ics calendar invite attachments for meeting/appointment emails
  • Parsing uploaded or fetched .ics files to extract event lists in a calendar sync tool
  • Building recurring event schedules with RRULE for reminder or booking systems
  • Round-tripping calendar data between systems that exchange RFC 5545 payloads

Under The Hood

Architecture A CalendarObject base class (lib/base.js) implements a generic property/component tree that every RFC 5545 element — iCalendar, VEvent, VAlarm, VTimezone — inherits via util.inherits, driven by a shared schema registry that maps element names to factories and enforces required properties per component type. The parser (lib/parser.js) is a hand-rolled finite-state machine — parse_record tokenizes each folded ICS line, and a chain of parse_component closures walks nested BEGIN/END blocks — that builds the exact same CalendarObject tree the public API constructs directly, so parsing and programmatic construction converge on one data structure. Serialization is symmetric: format()/_read() walk that tree to emit CRLF-terminated lines, and because CalendarObject extends Node’s Readable, any component can be piped as a stream with no separate serialization step. It’s a straightforward parse-model-format pipeline with no dependency injection, and because every derived component keys off schema[element].factory, a change to that registry’s shape would break all of them at once.

Tech Stack Plain ES5 JavaScript with no transpilation or bundler; the sole runtime dependency is readable-stream (~1.1.0), a userland Readable polyfill dating from when Node’s native streams2 API was still maturing. Dev tooling is Grunt (~0.4.1) with grunt-jasmine-node and jasmine-node driving the npm test script, and CI is a single .travis.yml entry. package.json’s main points to lib/index.js, which re-exports the public surface from lib/icalendar.js, lib/event.js, lib/alarm.js, lib/timezone.js, lib/parser.js, lib/rrule.js, and lib/types.js. There’s no database, web framework, or build step — it’s consumed purely as an npm library dependency.

Code Quality A real spec suite exists under spec/ (Jasmine, run via jasmine-node), with dedicated files for the calendar, parser, rrule, types, valarm, and vevent behavior — reasonable coverage for a project this size. Error handling is a custom ParseError thrown on malformed BEGIN/END or line syntax, but not every path is guarded: parse_calendar’s timezone-handling branch in lib/parser.js has an else clause that references tzs[i] when neither i nor tzs is defined in that scope — a latent dead-code bug that would throw if a non-string, non-iCalendar timezone argument were ever passed. There’s no linter/formatter configuration, no TypeScript or type annotations, and the code is uniformly ES5 (var, util.inherits) with no updates since 2020.

API Design The public surface is re-exported flatly from lib/index.jsparse_calendar, iCalendar, VEvent, VAlarm, VTimezone, RRule — giving a small, memorable API, and construction mirrors calendar semantics directly (new icalendar.VEvent(uid), event.setSummary(...), event.setDate(start, end)) rather than generic property-bag setters, keeping the common build-and-serialize path short. Documentation is limited to a few README snippets and one email-reply example script; the README’s “Implementation Status” section is a candid but blunt list of missing RFC 5545 features (recurrence intervals, RDATE, RECURRENCE-ID) rather than maintained docs. Nothing here is novel relative to other RFC 5545 libraries — it’s a standard object-model-plus-parser design that hasn’t picked up promises, async iteration, or TypeScript types since its last release in 2014.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search