Skip to content

Commit

Permalink
feat(types): add types #193
Browse files Browse the repository at this point in the history
  • Loading branch information
TobiTenno authored Jun 17, 2020
2 parents 0324606 + 6179b80 commit 43c37e0
Show file tree
Hide file tree
Showing 11 changed files with 499 additions and 329 deletions.
412 changes: 98 additions & 314 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
"version": "3.0.0",
"description": "Super RSS News Feed aggregator written in Node.js and ES6",
"main": "src/FeedEmitter.js",
"types": "types/FeedEmitter.d.ts",
"directories": {
"test": "test/**/*.spec.js"
},
"scripts": {
"test": "npx nyc npx mocha \"test/**/*.spec.js\" --exit",
"coverage": "npm test && npx nyc report --reporter=text-lcov | npx coveralls",
"lint": "npx eslint src/ test/",
"lint:fix": "npx eslint src/ test/ --fix"
"lint:fix": "npx eslint src/ test/ --fix",
"prepublish": "npm run build:types",
"build:types": "npx -p typescript tsc -p tsconfig.declaration.json"
},
"repository": {
"type": "git",
Expand All @@ -31,15 +34,15 @@
},
"homepage": "https://github.com/filipedeschamps/rss-feed-emitter#readme",
"devDependencies": {
"@types/chai": "^4.2.7",
"@types/chai": "^4.2.11",
"@types/feedparser": "^2.2.3",
"@types/request": "^2.48.4",
"chai": "3.4.0",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-import": "^2.20.0",
"nock": "^11.7.2"
"eslint-plugin-import": "^2.20.2",
"nock": "11.7.2"
},
"dependencies": {
"feedparser": "1.1.4",
Expand Down
54 changes: 51 additions & 3 deletions src/Feed.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
const FeedParser = require('feedparser');
const request = require('request');
const FeedError = require('./FeedError');
const FeedItem = require('./FeedItem'); // eslint-disable-line no-unused-vars

/**
* Map of specially handled error codes
* @type {Object}
*/
const RESPONSE_CODES = {
OK: 200,
NOT_FOUND: 404,
Expand All @@ -27,6 +32,21 @@ const historyLengthMultiplier = 3;
*/
const DEFAULT_UA = 'Node/RssFeedEmitter (https://github.com/filipedeschamps/rss-feed-emitter)';

/**
* Allowed mime types to allow fetching
* @type {Array<string>}
*/
const ALLOWED_MIMES = ['text/html', 'application/xhtml+xml', 'application/xml', 'text/xml'];

/**
* Storage object for properties of a feed
* @typedef {Object} Feed
* @property {string} url Feed url
* @property {FeedItem[]} items items currently retrieved from the feed
* @property {number} refresh timeout between refreshes
* @property {string} userAgent User Agent string to fetch the feed with
* @property {string} eventName event name to use when emitting this feed
*/
class Feed {
constructor(data) {
({
Expand Down Expand Up @@ -60,6 +80,10 @@ class Feed {
}

if (!this.eventName) {
/**
* event name for this feed to emit when a new item becomes available
* @type {String}
*/
this.eventName = 'new-item';
}
}
Expand All @@ -70,8 +94,8 @@ class Feed {
* this to see if there's already an item inside
* the feed item list. If there is, we know it's
* not a new item.
* @param {Object} item item specitics
* @returns {Object} the matched element
* @param {FeedItem} item item specitics
* @returns {FeedItem} the matched element
*/
findItem(item) {
return this.items.find((entry) => {
Expand All @@ -84,15 +108,28 @@ class Feed {
});
}

/**
* Update the maximum history length based on the length of a feed retrieval
* @param {FeedItem[]} newItems new list of items to base the history length on
* @mutator
*/
updateHxLength(newItems) {
this.maxHistoryLength = newItems.length * historyLengthMultiplier;
}

/**
* Add an item to the feed
* @param {FeedItem} item Feed item. Indeterminant structure.
*/
addItem(item) {
this.items.push(item);
this.items = this.items.slice(this.items.length - this.maxHistoryLength, this.items.length);
}

/**
* Fetch the data for this feed
* @returns {Promise} array of new feed items
*/
fetchData() {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
Expand All @@ -114,13 +151,17 @@ class Feed {
});
}

/**
* Perform the feed parsing
* @param {FeedParser} feedparser feedparser instance to use for parsing a retrieved feed
*/
get(feedparser) {
request
.get({
url: this.url,
headers: {
'user-agent': this.userAgent,
accept: 'text/html,application/xhtml+xml,application/xml,text/xml',
accept: ALLOWED_MIMES.join(','),
},
})
.on('response', (res) => {
Expand All @@ -135,6 +176,10 @@ class Feed {
.on('end', () => {});
}

/**
* Private: handle errors inside the feed retrieval process
* @param {Error} error error to be handled
*/
handleError(error) {
if (this.handler) {
this.handler.handle(error);
Expand All @@ -143,6 +188,9 @@ class Feed {
}
}

/**
* Destroy feed
*/
destroy() {
clearInterval(this.interval);
delete this.interval;
Expand Down
20 changes: 13 additions & 7 deletions src/FeedEmitter.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,42 @@
'use strict';

const EventEmitter = require('events');
const { EventEmitter } = require('events');

const FeedError = require('./FeedError');
const FeedManager = require('./FeedManager');
const Feed = require('./Feed');

/**
* A feed list entry
* @type {Object} Feed
* @property {Interval} setInterval
*/

/**
* Default UserAgent string
* Since static stuff doesn't work in older versions, keep using global const
* @type {String}
*/
const DEFAULT_UA = 'Node/RssFeedEmitter (https://github.com/filipedeschamps/rss-feed-emitter)';

/**
* Validate if the feed exists
* @param {Feed} feed feed configuration
*/
const checkFeed = (feed) => {
if (!feed) {
throw new FeedError('You must call #add method with a feed configuration object.', 'type_error');
}
};

/**
* Validate feed url is a string or array of strings
* @param {Feed} feed feed to validate url(s) for
*/
const checkUrl = (feed) => {
if (!feed.url || !(typeof feed.url === 'string' || Array.isArray(feed.url))) {
throw new FeedError('Your configuration object should have an "url" key with a string or array value', 'type_error');
}
};

/**
* Validate that the feed refresh is valid
* @param {Feed} feed feed to validate refresh timeout for
*/
const checkRefresh = (feed) => {
if (feed.refresh && typeof feed.refresh !== 'number') {
throw new FeedError('Your configuration object should have a "refresh" key with a number value', 'type_error');
Expand Down
23 changes: 23 additions & 0 deletions src/FeedItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

/**
* @typedef {Object} FeedItem
* @property {string} title Title of the feed item
* @property {string} description description of the feed item
* @property {string} summary summary on the feed item
* @property {Date|null} date date the item "occurred"
* @property {Date|null} pubdate Published date of the item
* @property {string} link link to item relating to the feed
* @property {string} origlink original link
* @property {string} author author string
* @property {string} guid globally unique identifying string
* @property {string} comments comment string(s)
* @property {Object} image Image, indeterminant format
* @property {string} categories categories of the feed
* @property {Object} enclosures
* @property {Object} meta
* @property {any} [x] String-keyed object. Various more are allowed than are representable
*/
class FeedItem {}

module.exports = FeedItem;
17 changes: 17 additions & 0 deletions tsconfig.declaration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"inlcude": [
"src/**/*.js"
],
"exclude": [
"test/**/*.js",
"types/**/*.*",
"node_modules/**/*.*"
],
"compilerOptions": {
"outDir": "./types",
"declaration": true,
"noEmit": false,
"allowJs": true,
"emitDeclarationOnly": true
}
}
90 changes: 90 additions & 0 deletions types/Feed.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
export = Feed;
/**
* Storage object for properties of a feed
* @typedef {Object} Feed
* @property {string} url Feed url
* @property {FeedItem[]} items items currently retrieved from the feed
* @property {number} refresh timeout between refreshes
* @property {string} userAgent User Agent string to fetch the feed with
* @property {string} eventName event name to use when emitting this feed
*/
declare class Feed {
constructor(data: any);
items: any[];
refresh: number;
userAgent: string;
/**
* event name for this feed to emit when a new item becomes available
* @type {String}
*/
eventName: string;
/**
* Given a feed and item, try to find
* it inside the feed item list. We will use
* this to see if there's already an item inside
* the feed item list. If there is, we know it's
* not a new item.
* @param {FeedItem} item item specitics
* @returns {FeedItem} the matched element
*/
findItem(item: import("./FeedItem")): import("./FeedItem");
/**
* Update the maximum history length based on the length of a feed retrieval
* @param {FeedItem[]} newItems new list of items to base the history length on
* @mutator
*/
updateHxLength(newItems: import("./FeedItem")[]): void;
maxHistoryLength: number;
/**
* Add an item to the feed
* @param {FeedItem} item Feed item. Indeterminant structure.
*/
addItem(item: import("./FeedItem")): void;
/**
* Fetch the data for this feed
* @returns {Promise} array of new feed items
*/
fetchData(): Promise<any>;
/**
* Perform the feed parsing
* @param {FeedParser} feedparser feedparser instance to use for parsing a retrieved feed
*/
get(feedparser: import("feedparser")): void;
/**
* Private: handle errors inside the feed retrieval process
* @param {Error} error error to be handled
*/
handleError(error: Error): void;
/**
* Destroy feed
*/
destroy(): void;
}
declare namespace Feed {
export { Feed };
}
/**
* Storage object for properties of a feed
*/
type Feed = {
/**
* Feed url
*/
url: string;
/**
* items currently retrieved from the feed
*/
items: import("./FeedItem")[];
/**
* timeout between refreshes
*/
refresh: number;
/**
* User Agent string to fetch the feed with
*/
userAgent: string;
/**
* event name to use when emitting this feed
*/
eventName: string;
};
Loading

0 comments on commit 43c37e0

Please sign in to comment.