Skip to content

Commit

Permalink
feat: Replace external types with stub types
Browse files Browse the repository at this point in the history
Previously, Typescript users had to install the optional dependencies for the types to be available. These have now been replaced with stub types so that the optional dependencies no longer need to be installed.

close #405
  • Loading branch information
sebbo2002 committed Jul 25, 2022
1 parent 75f90a0 commit 56cffc7
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 51 deletions.
12 changes: 0 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,6 @@ npm run browser-test
It's [here](https://github.com/sebbo2002/ical-generator/blob/develop/CHANGELOG.md). If you need the changelog for
`ical-generator` 1.x.x and older, you'll find it [here](https://github.com/sebbo2002/ical-generator/blob/25338b8bf98f9afd3c88849e735fa33fa45fb766/CHANGELOG.md).

### I use Typescript and get `TS2307: Cannot find module` errors
`ical-generator` supports some third-party libraries such as moment.js or Day.js. To enable Typescript to do something
with these types, they must of course also be installed. Unfortunately, npm does not install optional `peerDependencies`.
Because these modules are not necessary for JavaScript users, I have marked these modules as optional. So if you use
Typescript, you need the following modules to build the code that uses `ical-calendar`:

```bash
npm i -D @types/node rrule moment-timezone moment dayjs @types/luxon
```

For JavaScript users they are not necessary.

### I get a `ReferenceError: TextEncoder is not defined` error (in some browsers)
This library uses [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder), which
is available in node.js ≥ 11.0.0 and [all modern browsers](https://caniuse.com/?search=textencoder).
Expand Down
12 changes: 5 additions & 7 deletions src/calendar.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict';

import type {Duration} from 'moment-timezone';
import {
addOrGetCustomAttributes,
checkEnum,
Expand All @@ -10,10 +9,9 @@ import {
toDurationString
} from './tools';
import ICalEvent, {ICalEventData, ICalEventJSONData} from './event';
import {writeFile, writeFileSync} from 'fs';
import {promises as fsPromises} from 'fs';
import {writeFile, writeFileSync, promises as fsPromises} from 'fs';
import {ServerResponse} from 'http';
import {ICalTimezone} from './types';
import { ICalMomentDurationStub, ICalTimezone } from './types';


export interface ICalCalendarData {
Expand All @@ -25,7 +23,7 @@ export interface ICalCalendarData {
source?: string | null;
url?: string | null;
scale?: string | null;
ttl?: number | Duration | null;
ttl?: number | ICalMomentDurationStub | null;
events?: (ICalEvent | ICalEventData)[];
x?: {key: string, value: string}[] | [string, string][] | Record<string, string>;
}
Expand Down Expand Up @@ -472,8 +470,8 @@ export default class ICalCalendar {
*
* @since 0.2.5
*/
ttl(ttl: number | Duration | null): this;
ttl(ttl?: number | Duration | null): this | number | null {
ttl(ttl: number | ICalMomentDurationStub | null): this;
ttl(ttl?: number | ICalMomentDurationStub | null): this | number | null {
if (ttl === undefined) {
return this.data.ttl;
}
Expand Down
17 changes: 9 additions & 8 deletions src/event.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use strict';

import type {RRule} from 'rrule';
import uuid from 'uuid-random';
import {
addOrGetCustomAttributes,
Expand All @@ -10,7 +9,8 @@ import {
escape,
formatDate,
formatDateTZ,
generateCustomAttributes, isRRule,
generateCustomAttributes,
isRRule,
toDate,
toJSON
} from './tools';
Expand All @@ -25,6 +25,7 @@ import {
ICalLocation,
ICalOrganizer,
ICalRepeatingOptions,
ICalRRuleStub,
ICalWeekday
} from './types';

Expand Down Expand Up @@ -63,7 +64,7 @@ export interface ICalEventData {
stamp?: ICalDateTimeValue,
allDay?: boolean,
floating?: boolean,
repeating?: ICalRepeatingOptions | RRule | string | null,
repeating?: ICalRepeatingOptions | ICalRRuleStub | string | null,
summary?: string,
location?: ICalLocation | string | null,
description?: ICalDescription | string | null,
Expand Down Expand Up @@ -93,7 +94,7 @@ interface ICalEventInternalData {
stamp: ICalDateTimeValue,
allDay: boolean,
floating: boolean,
repeating: ICalEventInternalRepeatingData | RRule | string | null,
repeating: ICalEventInternalRepeatingData | ICalRRuleStub | string | null,
summary: string,
location: ICalLocation | null,
description: ICalDescription | null,
Expand Down Expand Up @@ -555,7 +556,7 @@ export default class ICalEvent {
* Get the event's repeating options
* @since 0.2.0
*/
repeating(): ICalEventInternalRepeatingData | RRule | string | null;
repeating(): ICalEventInternalRepeatingData | ICalRRuleStub | string | null;

/**
* Set the event's repeating options by passing an [[`ICalRepeatingOptions`]] object.
Expand Down Expand Up @@ -584,7 +585,7 @@ export default class ICalEvent {
* Set the event's repeating options by passing an [RRule object](https://github.com/jakubroztocil/rrule).
* @since 2.0.0-develop.5
*/
repeating(repeating: RRule | null): this;
repeating(repeating: ICalRRuleStub | null): this;

/**
* Set the events repeating options by passing a string which is inserted in the ical file.
Expand All @@ -595,8 +596,8 @@ export default class ICalEvent {
/**
* @internal
*/
repeating(repeating: ICalRepeatingOptions | RRule | string | null): this;
repeating(repeating?: ICalRepeatingOptions | RRule | string | null): this | ICalEventInternalRepeatingData | RRule | string | null {
repeating(repeating: ICalRepeatingOptions | ICalRRuleStub | string | null): this;
repeating(repeating?: ICalRepeatingOptions | ICalRRuleStub | string | null): this | ICalEventInternalRepeatingData | ICalRRuleStub | string | null {
if (repeating === undefined) {
return this.data.repeating;
}
Expand Down
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,13 @@ export {
ICalDescription,
ICalEventRepeatingFreq,
ICalWeekday,
ICalTimezone
ICalTimezone,
ICalMomentStub,
ICalMomentTimezoneStub,
ICalMomentDurationStub,
ICalLuxonDateTimeStub,
ICalDayJsStub,
ICalRRuleStub
} from './types';

export {
Expand Down
31 changes: 15 additions & 16 deletions src/tools.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
'use strict';


import type {Moment, Duration} from 'moment';
import type {Moment as MomentTZ} from 'moment-timezone';
import type {Dayjs} from 'dayjs';
import type {DateTime as LuxonDateTime} from 'luxon';
import type { RRule } from 'rrule';

import {ICalDateTimeValue, ICalOrganizer} from './types';
import {
ICalDateTimeValue, ICalDayJsStub, ICalLuxonDateTimeStub,
ICalMomentDurationStub,
ICalMomentStub,
ICalMomentTimezoneStub,
ICalOrganizer, ICalRRuleStub
} from './types';

/**
* Converts a valid date/time object supported by this library to a string.
Expand Down Expand Up @@ -315,32 +314,32 @@ export function toDate(value: ICalDateTimeValue): Date {
return value.toDate();
}

export function isMoment(value: ICalDateTimeValue): value is Moment {
export function isMoment(value: ICalDateTimeValue): value is ICalMomentStub {

// @ts-ignore
return value != null && value._isAMomentObject != null;
}
export function isMomentTZ(value: ICalDateTimeValue): value is MomentTZ {
return isMoment(value) && typeof value.tz === 'function';
export function isMomentTZ(value: ICalDateTimeValue): value is ICalMomentTimezoneStub {
return isMoment(value) && 'tz' in value && typeof value.tz === 'function';
}
export function isDayjs(value: ICalDateTimeValue): value is Dayjs {
export function isDayjs(value: ICalDateTimeValue): value is ICalDayJsStub {
return typeof value === 'object' &&
value !== null &&
!(value instanceof Date) &&
!isMoment(value) &&
!isLuxonDate(value);
}
export function isLuxonDate(value: ICalDateTimeValue): value is LuxonDateTime {
return typeof value === 'object' && value !== null && typeof (value as LuxonDateTime).toJSDate === 'function';
export function isLuxonDate(value: ICalDateTimeValue): value is ICalLuxonDateTimeStub {
return typeof value === 'object' && value !== null && 'toJSDate' in value && typeof value.toJSDate === 'function';
}

export function isMomentDuration(value: unknown): value is Duration {
export function isMomentDuration(value: unknown): value is ICalMomentDurationStub {

// @ts-ignore
return value !== null && typeof value === 'object' && typeof value.asSeconds === 'function';
}

export function isRRule(value: unknown): value is RRule {
export function isRRule(value: unknown): value is ICalRRuleStub {

// @ts-ignore
return value !== null && typeof value === 'object' && typeof value.between === 'function' && typeof value.toString === 'function';
Expand Down
51 changes: 44 additions & 7 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import type {Moment} from 'moment';
import type {Moment as MomentTZ} from 'moment-timezone';
import type {Dayjs} from 'dayjs';
import type {DateTime as LuxonDateTime} from 'luxon';


/**
* ical-generator supports [native Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date),
* [moment.js](https://momentjs.com/) (and [moment-timezone](https://momentjs.com/timezone/), [Day.js](https://day.js.org/en/) and
* [Luxon](https://moment.github.io/luxon/)'s [DateTime](https://moment.github.io/luxon/docs/class/src/datetime.js~DateTime.html)
* objects. You can also pass a string which is then passed to javascript's Date internally.
*/
export type ICalDateTimeValue = Date | Moment | MomentTZ | Dayjs | LuxonDateTime | string;
export type ICalDateTimeValue = Date | ICalMomentStub | ICalMomentTimezoneStub | ICalLuxonDateTimeStub | ICalDayJsStub | string;

export interface ICalRepeatingOptions {
freq: ICalEventRepeatingFreq;
Expand Down Expand Up @@ -54,6 +48,49 @@ export interface ICalTimezone {
generator?: (timezone: string) => string|null;
}

export interface ICalMomentStub {
_isAMomentObject: true;
format(format?: string): string;
clone(): ICalMomentStub;
utc(): ICalMomentStub;
toDate(): Date;
isValid(): boolean;
toJSON(): string;
}

export interface ICalMomentTimezoneStub extends ICalMomentStub {
clone(): ICalMomentTimezoneStub;
utc(): ICalMomentTimezoneStub;
tz(): string | undefined;
tz(timezone: string): ICalMomentTimezoneStub;
}

export interface ICalMomentDurationStub {
asSeconds(): number;
}

export interface ICalLuxonDateTimeStub {
setZone(zone?: string): ICalLuxonDateTimeStub;
toFormat(fmt: string): string;
toJSDate(): Date;
get isValid(): boolean;

This comment has been minimized.

Copy link
@icaroGS

icaroGS Aug 2, 2022

I'm started to have a problem when installed a new version of adonis/mail, because yarn updated ical-generator to the last version and I could not avoid this. The error points to this line when I try to build the project, where if I remove this "get" the error is solved and the project seems to work correctly.. is this correct?

This comment has been minimized.

Copy link
@sebbo2002

sebbo2002 Aug 3, 2022

Author Owner

Hmm, get isValid(): boolean; is exactly the type used in @types/luxon. Can you somehow send me an example so I can reproduce the error? A JSFiddle or something?

This comment has been minimized.

Copy link
@icaroGS

icaroGS Aug 3, 2022

I managed to solve the problem by updating the devDependencies versions of my package.json file. Looks like there was some incompatibility.

However, I appreciate the attention, I was able to solve the problem just by trying to reproduce the error and generating a new adonis application to send you, that's why I noticed the difference between versions in the package file, so thanks!

This comment has been minimized.

Copy link
@sebbo2002

sebbo2002 Aug 3, 2022

Author Owner

Glad to hear 🥰

This comment has been minimized.

Copy link
@safal07

safal07 May 6, 2024

I stumbled on the same error, just in case anyone else is facing this issue, upgrade your typescript version.

toJSON(): string;
}

export interface ICalDayJsStub {
tz(zone?: string): ICalDayJsStub;
utc(): ICalDayJsStub;
format(format?: string): string;
toDate(): Date;
isValid(): boolean;
toJSON(): string;
}

export interface ICalRRuleStub {
between(after: Date, before: Date, inc?: boolean, iterator?: (d: Date, len: number) => boolean): Date[];
toString(): string;
}

export enum ICalEventRepeatingFreq {
SECONDLY = 'SECONDLY',
MINUTELY = 'MINUTELY',
Expand Down

0 comments on commit 56cffc7

Please sign in to comment.