npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ymchun/ics

v0.0.1-alpha.18

Published

A ics file parser and generator based on RFC-5545

Downloads

7

Readme

@ymchun/ics

Build Status Coverage Status Known Vulnerabilities npm version

UNDER ACTIVE DEVELOPMENT, MAY HAVE BREAKING CHANGES

Install

$ npm i @ymchun/ics

Basic Usage

import { Parser, ParserOptions } from '@ymchun/ics';

const icsStr = '......'; // your ics file content
const opts: ParserOptions = {
	// whether the parser should emit warning message
	quiet: true,
};

// 'calendars' is array of v-calendar components
const calendars = new Parser(opts).parse(icsStr);
// convert Vcalendar back to ics file
const ics = convertToIcs(calendar[0].getICSTokens());

Extending components and properties

More components and properties can be supported by extending Component and Property classes.

Also, your custom components and properties should implements ComponentImpl and PropertyImpl<T>.

For PropertyImpl<T>, the generic type refer to the type of your property value.

Define your custom property with parameters

class MyCustomPropertyA extends Property implements PropertyImpl<TextValue> {
	// type should match your property key when feeding into the parser options
	public type = 'MyCustomPropertyA'; // <-- property key, mandatory
	public value!: TextValue; // <-- property value, mandatory
	public parameters = {
		// property parameters
		Encoding: null as TextValue | null,
		MyCustomParam: null as MyCustomParam | null,
	};

	// set property value
	public setValue(value: string): this {
		// your property should use the 'token' property from parent class to set the value
		// since properties may have different value type, so this method is where you parse your own value
		// 'token' will become null after this method is called
		this.value = new BinaryValue().setValue(this.token.value);
		return this;
	}

	// set property parameters
	public setParameter(type: string, value: string): this {
		switch (param.name) {
			case PARAMETER.Encoding:
				this.parameters.Encoding = new TextValue().setValue(param.value);
				break;
			case PARAMETER.MyCustomParam:
				this.parameters.MyCustomParam = new MyCustomParam().setValue(param.value);
				break;
		}
		return this;
	}

	// toString convert property into ics string
	public toString(): string {
		const paramStr = propertyParameterToString(this.parameters);
		return foldLine(`${this.type}${paramStr}:${this.value.toString()}`);
	}
}

Define your custom property without parameters

class MyCustomPropertyB extends Property implements PropertyImpl<DateValue> {
	// type should match your property key when feeding into the parser options
	public type = 'MyCustomPropertyB'; // <-- property key, mandatory
	public value!: DateValue; // <-- property value, mandatory

	// this method should be implemented
	public setValue(value: string): this {
		// or if your property value is a Date object
		this.value = new DateValue().setValue(this.token.value);
		return this;
	}

	// toString convert property into ics string
	public toString(): string {
		return foldLine(`${this.type}:${this.value.toString()}`);
	}
}

Define your custom component

class MyCustomComponent extends Component implements ComponentImpl {
	// type should match your component key when feeding into the parser options
	public type = 'MyCustomComponent'; // <-- component key, mandatory

	public myCustomPropertyA: MyCustomPropertyA; // <-- use 'MyCustomPropertyA' here
	public myCustomPropertyB: MyCustomPropertyB; // <-- use 'MyCustomPropertyB' here

	public childComponentTypeA: ComponentTypeA;
	public childComponentTypeB: ComponentTypeB;

	public propertyTypeA: PropertyTypeA;
	public propertyTypeB: PropertyTypeB;
	public propertyTypeC: PropertyTypeC;

	// this method should be implemented when your component contains other components
	public setComponent(component: Component): this {
		switch (component.type) {
			case 'TypeA':
				this.daylight = component as ComponentTypeA;
				break;
			case 'TypeB':
				this.daylight = component as ComponentTypeB;
				break;
		}
		return this;
	}

	// this method should be implemented when your component has properties
	public setProperty(property: Property): this {
		switch (property.type) {
			case 'TypeA':
				this.propertyTypeA = property as PropertyTypeA;
				break;
			case 'TypeB':
				this.propertyTypeB = property as PropertyTypeB;
				break;
			case 'TypeC':
				this.propertyTypeC = property as PropertyTypeC;
				break;
			// property key --v
			case 'MyCustomPropertyA':
				this.myCustomPropertyA = property as MyCustomPropertyA;
				break;
			case 'MyCustomPropertyB':
				this.myCustomPropertyB = property as MyCustomPropertyB;
				break;
		}
		return this;
	}

	// toString convert component into ics string
	public getICSTokens(): ConvertToICS {
		// result
		const payload: ConvertToICS = {
			children: [],
			type: this.type,
		};

		// push properties
		if (this.myCustomPropertyA) {
			payload.children.push(this.myCustomPropertyA.toString());
		}
		if (this.myCustomPropertyB) {
			payload.children.push(this.myCustomPropertyB.toString());
		}
		if (this.propertyTypeA) {
			payload.children.push(this.propertyTypeA.toString());
		}
		if (this.propertyTypeB) {
			payload.children.push(this.propertyTypeB.toString());
		}
		if (this.propertyTypeC) {
			payload.children.push(this.propertyTypeC.toString());
		}

		// push components
		if (this.childComponentTypeA) {
			payload.children.push(this.childComponentTypeA.getICSTokens());
		}
		if (this.childComponentTypeB) {
			payload.children.push(this.childComponentTypeB.getICSTokens());
		}

		return payload;
	}
}

Extending existing component

class MyExtendedVCalendar extends VCalendar implements ComponentImpl {
	public myCustomProperty: MyCustomProperty; // <-- use 'MyCustomProperty' here

	// this method should be implemented when your component has properties
	public setProperty(property: Property): this {
		switch (property.type) {
			case 'MyCustomProperty':
				this.myCustomProperty = property as MyCustomProperty;
				break;
			default:
				// you should call parent class 'setProperty' method to avoid missing parent properties
				super.setProperty(property);
		}
		return this;
	}

	// toString convert component into ics string
	public getICSTokens(): ConvertToICS {
		// you should call the parent get tokens first
		const payload = super.getICSTokens();

		// then append your custom properties
		if (this.myCustomProperty) {
			payload.children.push(this.myCustomProperty.toString());
		}

		return payload;
	}
}

Register your custom components and properties via options

const icsStr = '......'; // your ics file content
const opts: ParserOptions = {
	components: {
		// the key should match the ics 'BEGIN:xxx' and 'END:xxx'
		// i.e. when parser meet 'BEGIN:MyCustomComponent', it will use this component.
		// v-- component key
		MyCustomComponent: MyCustomComponent,
		// overriding the default vCalendar component
		[COMPONENT.Calendar]: MyExtendedVCalendar,
	},
	properties: {
		// the key should match the ics 'MyCustomPropertyA:xxx' component property
		// v-- property key
		MyCustomPropertyA: MyCustomPropertyA,
		MyCustomPropertyB: MyCustomPropertyB,
	},
};

// parse icsStr into VCalendar object
const calendars = new Parser(opts).parse(icsStr);