Skip to content

Commit 85ae175

Browse files
refactor: ESM migration
1 parent 55a6735 commit 85ae175

43 files changed

Lines changed: 2296 additions & 1575 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/nodejs.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,14 @@ jobs:
5252
run: npm ci
5353

5454
- name: Generate windowsZones (strict)
55-
run: node build/update-windows-zones.mjs
55+
run: node build/update-windows-zones.js
5656
env:
5757
CI: true
5858

59+
- name: Build CommonJS entry
60+
run: npm run build:cjs
61+
5962
- name: Unit Tests
60-
run: npx mocha --timeout 8000
63+
run: npx mocha --extension js --timeout 8000
6164
env:
6265
CI: true

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
node_modules
22
.idea
33
.DS_Store
4+
5+
# Generated CommonJS entry (built from node-ical.js via `npm run build:cjs`)
6+
node-ical.cjs

README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ node-ical is available on npm:
1515
npm install node-ical
1616
```
1717

18+
## Module formats
19+
node-ical provides an ESM-first entry point while keeping CommonJS compatibility.
20+
21+
```javascript
22+
// ESM
23+
import ical from 'node-ical';
24+
```
25+
26+
```javascript
27+
// CommonJS
28+
const ical = require('node-ical');
29+
```
30+
1831
## API
1932
The API has now been broken into three sections:
2033
- [sync](#sync)
@@ -29,8 +42,8 @@ All functions will either return a promise for `async/await` or use a callback i
2942

3043
`autodetect` provides a mix of both for backwards compatibility with older node-ical applications.
3144

32-
All API functions are documented using JSDoc in the [node-ical.js](node-ical.js) file.
33-
This allows for IDE hinting!
45+
All API functions are documented in the runtime entry files and exposed through the bundled typings in [node-ical.d.ts](node-ical.d.ts).
46+
This allows for IDE hinting in both CommonJS and ESM projects.
3447

3548
### sync
3649
```javascript
@@ -197,9 +210,9 @@ Fetch the specified URL using the native fetch API (```options``` are passed to
197210

198211
#### Example: Print list of upcoming node conferences
199212

200-
See [`examples/example.mjs`](./examples/example.mjs) for a full example script.
213+
See [`examples/example.js`](./examples/example.js) for a full example script.
201214

202-
> **Note:** This snippet uses `import` and top-level `await` (ESM). Save it as a `.mjs` file, or add `"type": "module"` to your `package.json`.
215+
> **Note:** This snippet uses `import` and top-level `await` (ESM). Save it as a `.mjs` file, or add `"type": "module"` to your `package.json`. If you prefer CommonJS in your own project, keep using `require('node-ical')` from a `.cjs` file.
203216
204217
```javascript
205218
import ical from 'node-ical';

build/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This folder contains data and scripts used to generate `windowsZones.json`, the
44

55
## Files
66

7-
- `update-windows-zones.mjs`: Node-only updater that fetches the upstream CLDR `windowsZones.xml` and regenerates `windowsZones.json` using fast-xml-parser.
7+
- `update-windows-zones.js`: Node-only updater that fetches the upstream CLDR `windowsZones.xml` and regenerates `windowsZones.json` using fast-xml-parser.
88
- `windowsZonesOld.json`: A curated map of legacy Windows display-name labels (the human-readable aliases used by various Outlook/Exchange/ICS exporters) to the canonical Windows time zone IDs (e.g., `"(UTC+03:00) Tbilisi" -> "Georgian Standard Time"`).
99

1010
## Why `windowsZonesOld.json` exists
@@ -19,7 +19,7 @@ To keep `node-ical` resilient, we preserve a set of these legacy labels and map
1919

2020
## How generation works
2121

22-
1. `update-windows-zones.mjs` downloads CLDR `windowsZones.xml` and parses it directly.
22+
1. `update-windows-zones.js` downloads CLDR `windowsZones.xml` and parses it directly.
2323
2. It builds a `zoneTable` from CLDR, mapping Windows IDs to `{ iana: [primaryIana] }`.
2424
3. It then loads `build/windowsZonesOld.json` and, for each legacy label (top-level key), looks up the canonical Windows ID (value) and injects a mapping entry into the final `zoneTable` so that legacy labels resolve to the same IANA zone as the canonical ID.
2525
4. The script writes `windowsZones.json` in a one-entry-per-line format with sorted keys for stable diffs.
@@ -38,7 +38,7 @@ npm run build:strict
3838
# or
3939
CI=true npm run build
4040
# or (low-level)
41-
node build/update-windows-zones.mjs --strict
41+
node build/update-windows-zones.js --strict
4242
```
4343

4444
Note: CI runs the generator in strict mode to catch unresolved aliases early in pull requests.

build/build-cjs.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Generate the CommonJS entry point (node-ical.cjs) from the ESM sources.
2+
//
3+
// ESM (*.js, type: module) is the single source of truth. This bundles
4+
// node-ical.js into a single self-contained CommonJS file so that
5+
// `require('node-ical')` keeps working, while runtime dependencies stay
6+
// external `require()` calls.
7+
import {build} from 'esbuild';
8+
9+
await build({
10+
entryPoints: ['node-ical.js'],
11+
outfile: 'node-ical.cjs',
12+
bundle: true,
13+
platform: 'node',
14+
format: 'cjs',
15+
target: 'node20',
16+
// Keep node_modules dependencies as require() calls; only bundle our own code
17+
// (and inline windowsZones.json, which is imported relatively).
18+
packages: 'external',
19+
logLevel: 'info',
20+
});

examples/example-rrule-basic.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
* RRULE within a fixed window, and print the resulting instances.
66
*/
77

8-
const path = require('node:path');
9-
const ical = require('../node-ical.js');
8+
import path from 'node:path';
9+
import {fileURLToPath} from 'node:url';
10+
import ical from 'node-ical';
11+
12+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
1013

1114
// Load the basic example calendar that contains one recurring event.
1215
const data = ical.parseFile(path.join(__dirname, 'example-rrule-basic.ics'));

examples/example-rrule-datefns.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,12 @@
1515
* fixed window (here: calendar year 2017) keeps expansion finite and practical.
1616
*/
1717

18-
const path = require('node:path');
19-
const {
20-
format,
21-
differenceInMilliseconds,
22-
parseISO,
23-
} = require('date-fns');
24-
const ical = require('../node-ical.js');
18+
import path from 'node:path';
19+
import {fileURLToPath} from 'node:url';
20+
import {differenceInMilliseconds, format, parseISO} from 'date-fns';
21+
import ical from 'node-ical';
22+
23+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
2524

2625
// Load an example iCal file with various recurring events.
2726
const data = ical.parseFile(path.join(__dirname, 'example-rrule.ics'));

examples/example-rrule-dayjs.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
* fixed window (here: calendar year 2017) keeps expansion finite and practical.
1818
*/
1919

20-
const path = require('node:path');
21-
const dayjs = require('dayjs');
22-
const utc = require('dayjs/plugin/utc');
23-
const duration = require('dayjs/plugin/duration');
24-
const localizedFormat = require('dayjs/plugin/localizedFormat');
25-
const ical = require('../node-ical.js');
20+
import path from 'node:path';
21+
import {fileURLToPath} from 'node:url';
22+
import dayjs from 'dayjs';
23+
import duration from 'dayjs/plugin/duration.js';
24+
import localizedFormat from 'dayjs/plugin/localizedFormat.js';
25+
import utc from 'dayjs/plugin/utc.js';
26+
import ical from 'node-ical';
27+
28+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
2629

2730
// Extend Day.js with plugins for timezone and duration support
2831
dayjs.extend(utc);

examples/example-rrule-luxon.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
* fixed window (here: calendar year 2017) keeps expansion finite and practical.
1717
*/
1818

19-
const path = require('node:path');
20-
const {DateTime} = require('luxon');
21-
const ical = require('../node-ical.js');
19+
import path from 'node:path';
20+
import {fileURLToPath} from 'node:url';
21+
import {DateTime} from 'luxon';
22+
import ical from 'node-ical';
23+
24+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
2225

2326
// Load an example iCal file with various recurring events.
2427
const data = ical.parseFile(path.join(__dirname, 'example-rrule.ics'));

0 commit comments

Comments
 (0)