diff --git a/src/category.ts b/src/category.ts index cf8b663..6d3ec47 100644 --- a/src/category.ts +++ b/src/category.ts @@ -8,23 +8,47 @@ type ApiPageInfo = { }; export interface MwnCategoryStatic { - new (title: MwnTitle | string): MwnCategory; + /** + * @param {string} name - name of the category + */ + new (name: MwnTitle | string): MwnCategory; } export interface MwnCategory extends MwnPage { + /** + * Get all members in the category - this includes subcategories, pages and files + * @param {Object} options - additional API parameters + * @returns {Promise} - Resolved with array of objects of form + * { pageid: 324234, ns: 0, title: 'Main Page' } + */ members(options?: ApiQueryCategoryMembersParams): Promise; membersGen(options?: ApiQueryCategoryMembersParams): AsyncGenerator; + /** + * Get all pages in the category - does not include subcategories or files + * @param {Object} options - additional API parameters + * @returns {Promise} - Resolved with array of objects of form + * { pageid: 324234, ns: 0, title: 'Main Page' } + */ pages(options?: ApiQueryCategoryMembersParams): Promise; + /** + * Get all subcategories of the category + * @param {Object} options - additional API parameters + * @returns {Promise} - Resolved with array of objects of form + * { pageid: 324234, ns: 14, title: 'Category:Living people' } + */ subcats(options?: ApiQueryCategoryMembersParams): Promise; + /** + * Get all files in the category + * @param {Object} options - additional API parameters + * @returns {Promise} - Resolved with array of objects of form + * { pageid: 324234, ns: 6, title: 'File:Image.jpg' } + */ files(options?: ApiQueryCategoryMembersParams): Promise; } export default function (bot: mwn) { class Category extends bot.page implements MwnCategory { - /** - * @constructor - * @param {string} name - name of the category - */ + /** @inheritDoc */ constructor(name: MwnTitle | string) { super(name, 14); if (this.namespace !== 14) { @@ -34,12 +58,7 @@ export default function (bot: mwn) { // TODO: Add recursive modes - /** - * Get all members in the category - this includes subcategories, pages and files - * @param {Object} options - additional API parameters - * @returns {Promise} - Resolved with array of objects of form - * { pageid: 324234, ns: 0, title: 'Main Page' } - */ + /** @inheritDoc */ members(options?: ApiQueryCategoryMembersParams): Promise { return bot .request({ @@ -67,32 +86,17 @@ export default function (bot: mwn) { } } - /** - * Get all pages in the category - does not include subcategories or files - * @param {Object} options - additional API parameters - * @returns {Promise} - Resolved with array of objects of form - * { pageid: 324234, ns: 0, title: 'Main Page' } - */ + /** @inheritDoc */ pages(options?: ApiQueryCategoryMembersParams): Promise { return this.members({ cmtype: ['page'], ...options }); } - /** - * Get all subcategories of the category - * @param {Object} options - additional API parameters - * @returns {Promise} - Resolved with array of objects of form - * { pageid: 324234, ns: 14, title: 'Category:Living people' } - */ + /** @inheritDoc */ subcats(options?: ApiQueryCategoryMembersParams): Promise { return this.members({ cmtype: ['subcat'], ...options }); } - /** - * Get all files in the category - * @param {Object} options - additional API parameters - * @returns {Promise} - Resolved with array of objects of form - * { pageid: 324234, ns: 6, title: 'File:Image.jpg' } - */ + /** @inheritDoc */ files(options?: ApiQueryCategoryMembersParams): Promise { return this.members({ cmtype: ['file'], ...options }); } diff --git a/src/date.ts b/src/date.ts index 73d403e..e5a6f89 100644 --- a/src/date.ts +++ b/src/date.ts @@ -6,15 +6,6 @@ import { Unbinder } from './wikitext'; * handling dates, as well as a constructor that * can parse MediaWiki dating formats. */ - -export interface MwnDateStatic { - new (...args: any[]): MwnDate; - getMonthName(monthNum: number): string; - getMonthNameAbbrev(monthNum: number): string; - getDayName(dayNum: number): string; - getDayNameAbbrev(dayNum: number): string; -} - export interface MwnDate extends Date { isValid(): boolean; isBefore(date: Date | MwnDate): boolean; @@ -27,24 +18,72 @@ export interface MwnDate extends Date { getUTCDayNameAbbrev(): string; getDayName(): string; getDayNameAbbrev(): string; + + /** + * Add a given number of minutes, hours, days, months or years to the date. + * This is done in-place. The modified date object is also returned, allowing chaining. + * @param {number} number - should be an integer + * @param {string} unit + * @throws {Error} if invalid or unsupported unit is given + * @returns {XDate} + */ add(number: number, unit: timeUnit): MwnDate; + + /** + * Subtracts a given number of minutes, hours, days, months or years to the date. + * This is done in-place. The modified date object is also returned, allowing chaining. + * @param {number} number - should be an integer + * @param {string} unit + * @throws {Error} if invalid or unsupported unit is given + * @returns {XDate} + */ subtract(number: number, unit: timeUnit): MwnDate; + + /** + * Formats the date into a string per the given format string. + * Replacement syntax is a subset of that in moment.js. + * @param {string} formatstr + * @param {(string|number)} [zone=utc] - 'system' (for system-default time zone), + * 'utc' (for UTC), or specify a time zone as number of minutes past UTC. + * @returns {string} + */ format(formatstr: string, zone?: number | 'utc' | 'system'): string; + + /** + * Gives a readable relative time string such as "Yesterday at 6:43 PM" or "Last Thursday at 11:45 AM". + * Similar to calendar in moment.js, but with time zone support. + * @param {(string|number)} [zone=system] - 'system' (for browser-default time zone), + * 'utc' (for UTC), or specify a time zone as number of minutes past UTC + * @returns {string} + */ calendar(zone?: number | 'utc' | 'system'): string; } - -/** - * Wrapper around the native JS Date() for ease of - * handling dates, as well as a constructor that - * can parse MediaWiki dating formats. - */ +export interface MwnDateStatic { + /** + * Create a date object. MediaWiki timestamp format is also acceptable, + * in addition to everything that JS Date() accepts. + */ + new (...args: any[]): MwnDate; + /** + * Get month name from month number (1-indexed) + */ + getMonthName(monthNum: number): string; + /** + * Get abbreviated month name from month number (1-indexed) + */ + getMonthNameAbbrev(monthNum: number): string; + /** + * Get day name from day number (1-indexed, starting from Sunday) + */ + getDayName(dayNum: number): string; + /** + * Get abbreviated day name from day number (1-indexed, starting from Sunday) + */ + getDayNameAbbrev(dayNum: number): string; +} export default function (bot: mwn) { class XDate extends Date implements MwnDate { - /** - * Create a date object. MediaWiki timestamp format is also acceptable, - * in addition to everything that JS Date() accepts. - */ constructor(...args: any[]) { if (args.length === 1 && typeof args[0] === 'string') { // parse MediaWiki format: YYYYMMDDHHmmss @@ -123,14 +162,7 @@ export default function (bot: mwn) { return XDate.localeData.daysShort[this.getDay()]; } - /** - * Add a given number of minutes, hours, days, months or years to the date. - * This is done in-place. The modified date object is also returned, allowing chaining. - * @param {number} number - should be an integer - * @param {string} unit - * @throws {Error} if invalid or unsupported unit is given - * @returns {XDate} - */ + /** @inheritDoc */ add(number: number, unit: timeUnit): XDate { // @ts-ignore let unitNorm = unitMap[unit] || unitMap[unit + 's']; // so that both singular and plural forms work @@ -142,26 +174,12 @@ export default function (bot: mwn) { throw new Error('Invalid unit "' + unit + '": Only ' + Object.keys(unitMap).join(', ') + ' are allowed.'); } - /** - * Subtracts a given number of minutes, hours, days, months or years to the date. - * This is done in-place. The modified date object is also returned, allowing chaining. - * @param {number} number - should be an integer - * @param {string} unit - * @throws {Error} if invalid or unsupported unit is given - * @returns {XDate} - */ + /** @inheritDoc */ subtract(number: number, unit: timeUnit): XDate { return this.add(-number, unit); } - /** - * Formats the date into a string per the given format string. - * Replacement syntax is a subset of that in moment.js. - * @param {string} formatstr - * @param {(string|number)} [zone=utc] - 'system' (for system-default time zone), - * 'utc' (for UTC), or specify a time zone as number of minutes past UTC. - * @returns {string} - */ + /** @inheritDoc */ format(formatstr: string, zone: number | 'utc' | 'system' = 'utc'): string { if (!this.isValid()) { return ''; // avoid bogus NaNs in output @@ -227,13 +245,7 @@ export default function (bot: mwn) { return unbinder.rebind().replace(/\[(.*?)\]/g, '$1'); } - /** - * Gives a readable relative time string such as "Yesterday at 6:43 PM" or "Last Thursday at 11:45 AM". - * Similar to calendar in moment.js, but with time zone support. - * @param {(string|number)} [zone=system] - 'system' (for browser-default time zone), - * 'utc' (for UTC), or specify a time zone as number of minutes past UTC - * @returns {string} - */ + /** @inheritDoc */ calendar(zone: number | 'utc' | 'system' = 'utc'): string { // Zero out the hours, minutes, seconds and milliseconds - keeping only the date; // find the difference. Note that setHours() returns the same thing as getTime(). @@ -282,30 +294,22 @@ export default function (bot: mwn) { }, }; - /** - * Get month name from month number (1-indexed) - */ + /** @inheritDoc */ static getMonthName(monthNum: number): string { return XDate.localeData.months[monthNum - 1]; } - /** - * Get abbreviated month name from month number (1-indexed) - */ + /** @inheritDoc */ static getMonthNameAbbrev(monthNum: number): string { return XDate.localeData.monthsShort[monthNum - 1]; } - /** - * Get day name from day number (1-indexed, starting from Sunday) - */ + /** @inheritDoc */ static getDayName(dayNum: number): string { return XDate.localeData.days[dayNum - 1]; } - /** - * Get abbreviated day name from day number (1-indexed, starting from Sunday) - */ + /** @inheritDoc */ static getDayNameAbbrev(dayNum: number): string { return XDate.localeData.daysShort[dayNum - 1]; } diff --git a/src/eventstream.ts b/src/eventstream.ts index 52236bf..7ce5069 100644 --- a/src/eventstream.ts +++ b/src/eventstream.ts @@ -20,9 +20,17 @@ export interface MwnStreamStatic { } export interface MwnStream { + /** + * Register a function to trigger for every message data from the source. + * @param {Function | Object} [filter={}] + * @param {Function} action + */ addListener(filter: ((data: any) => boolean) | any, action: (data: any) => void): void; } +/** + * Represents an event in the recentchange stream. + */ export type RecentChangeStreamEvent = { $schema: string; meta: { @@ -117,11 +125,7 @@ export default function (bot: mwn) { }; } - /** - * Register a function to trigger for every message data from the source. - * @param {Function | Object} [filter={}] - * @param {Function} action - */ + /** @inheritDoc */ addListener(filter: ((data: any) => boolean) | any = {}, action: (data: any) => void): void { let filterer = typeof filter === 'function' diff --git a/src/file.ts b/src/file.ts index b567236..2058f86 100644 --- a/src/file.ts +++ b/src/file.ts @@ -2,11 +2,35 @@ import type { mwn, MwnPage, MwnTitle } from './bot'; import { ApiQueryBacklinkspropParams } from './api_params'; export interface MwnFileStatic { + /** + * @constructor + * @param {string} name - name of the file + */ new (title: MwnTitle | string): MwnFile; } export interface MwnFile extends MwnPage { + /** + * Get the name of the file without extension or namespace prefix. + * + * @return {string} File name without file extension, in the canonical form with + * underscores instead of spaces. For example, the title "File:Example_image.svg" + * will be returned as "Example_image". + */ getName(): string; + /** + * Get the name of the file without extension or namespace prefix. + * + * @return {string} File name without file extension, formatted with spaces instead + * of underscores. For example, the title "File:Example_image.svg" will be returned + * as "Example image". + */ getNameText(): string; + /** + * Get file usages + * @param {Object} options - additional API options + * @returns {Promise} - resolved with array of { pageid: 32434, + * ns: 0, title: 'Main Page', redirect: false } like objects. + */ usages( options?: ApiQueryBacklinkspropParams, ): Promise< @@ -16,15 +40,18 @@ export interface MwnFile extends MwnPage { redirect: boolean; }[] >; + + /** + * Download an image from the wiki to the local file system + * @param localname - local path (with file name) to download to, + * defaults to current directory with same file name as on the wiki. + */ download(localname: string): void; } export default function (bot: mwn) { class File extends bot.page implements MwnFile { - /** - * @constructor - * @param {string} name - name of the file - */ + /** @inheritDoc */ constructor(name: MwnTitle | string) { super(name, 6); if (this.namespace !== 6) { @@ -32,13 +59,7 @@ export default function (bot: mwn) { } } - /** - * Get the name of the file without extension or namespace prefix. - * - * @return {string} File name without file extension, in the canonical form with - * underscores instead of spaces. For example, the title "File:Example_image.svg" - * will be returned as "Example_image". - */ + /** @inheritDoc */ getName(): string { let ext = this.getExtension(); if (ext === null) { @@ -47,23 +68,12 @@ export default function (bot: mwn) { return this.getMain().slice(0, -ext.length - 1); } - /** - * Get the name of the file without extension or namespace prefix. - * - * @return {string} File name without file extension, formatted with spaces instead - * of underscores. For example, the title "File:Example_image.svg" will be returned - * as "Example image". - */ + /** @inheritDoc */ getNameText(): string { return this.getName().replace(/_/g, ' '); } - /** - * Get file usages - * @param {Object} options - additional API options - * @returns {Promise} - resolved with array of { pageid: 32434, - * ns: 0, title: 'Main Page', redirect: false } like objects. - */ + /** @inheritDoc */ usages(options?: ApiQueryBacklinkspropParams): Promise<{ pageid: number; title: string; redirect: boolean }[]> { return bot .request({ @@ -80,6 +90,7 @@ export default function (bot: mwn) { // TODO: Add wrapper for prop=imageinfo + /** @inheritDoc */ download(localname: string) { return bot.download(this.toString(), localname); } diff --git a/src/page.ts b/src/page.ts index bfd3e0a..3aa2a94 100644 --- a/src/page.ts +++ b/src/page.ts @@ -22,20 +22,91 @@ export interface MwnPage extends MwnTitle { data: any; getTalkPage(): MwnPage; getSubjectPage(): MwnPage; + /** + * Get page wikitext + */ text(): Promise; + /** + * Get page categories + * @returns {Promise} Resolved with array of objects like + * { sortkey: '...', category: '...', hidden: true } + */ categories(): Promise; + /** + * Get templates transcluded on the page + * @returns {Promise} Resolved with array of objects like + * { ns: 10, title: 'Template:Cite web', exists: true } + */ templates(): Promise; + /** + * Get links on the page + * @returns {Promise} Resolved with array of objects like + * { ns: 0, title: 'Main Page', exists: true } + */ links(): Promise; + /** + * Get list of pages linking to this page + * @returns {Promise} + */ backlinks(): Promise; + /** + * Get list of pages transcluding this page + * @returns {Promise} + */ transclusions(): Promise; + /** + * Returns list of images on the page + * @returns {Promise} - array elements don't include File: prefix + */ images(): Promise; + /** + * Returns list of external links on the page + * @returns {Promise} + */ externallinks(): Promise; + /** + * Returns list of subpages of the page + * @returns {Promise} + */ subpages(options?: ApiQueryAllPagesParams): Promise; + /** + * Check if page is redirect or not + * @returns {Promise} + */ isRedirect(): Promise; + /** + * Get redirect target. + * Returns the same page name if the page is not a redirect. + * @returns {Promise} + */ getRedirectTarget(): Promise; + /** + * Get username of the page creator + * @returns {Promise} + */ getCreator(): Promise; + /** + * Get username of the last deleting admin (or null) + * @returns {Promise} + */ getDeletingAdmin(): Promise; + /** + * Get short description, either the local one (for English Wikipedia) + * or the one from wikidata. + * @param {Object} customOptions + * @returns {Promise} + */ getDescription(customOptions?: any): Promise; + /** + * Get the edit history of the page + * @param {string|string[]} props - revision properties to fetch, by default content is + * excluded + * @param {number} [limit=50] - number of revisions to fetch data about + * @param {Object} customOptions - custom API options + * @returns {Promise} - resolved with array of objects representing + * revisions, eg. { revid: 951809097, parentid: 951809097, timestamp: + * "2020-04-19T00:45:35Z", comment: "Edit summary" } + */ history( props: ApiQueryRevisionsParams['rvprop'], limit: number, @@ -45,6 +116,17 @@ export interface MwnPage extends MwnTitle { props: ApiQueryRevisionsParams['rvprop'], customOptions?: ApiQueryRevisionsParams, ): AsyncGenerator; + /** + * Get the page logs. + * @param {string|string[]} props - data about log entries to fetch + * @param {number} limit - max number of log entries to fetch + * @param {string} type - type of log to fetch, can either be an letype or leaction + * Leave undefined (or null) to fetch all log types + * @param {Object} customOptions + * @returns {Promise} - resolved with array of objects representing + * log entries, eg. { ns: '0', title: 'Main Page', type: 'delete', user: 'Example', + * action: 'revision', timestamp: '2020-05-05T17:13:34Z', comment: 'edit summary' } + */ logs( props: ApiQueryLogEventsParams['leprop'], limit?: number, @@ -56,7 +138,18 @@ export interface MwnPage extends MwnTitle { type?: string, customOptions?: ApiQueryLogEventsParams, ): AsyncGenerator; + /** + * Get page views data (only for Wikimedia wikis) + * @see https://wikitech.wikimedia.org/wiki/Analytics/AQS/Pageviews + * @param options + */ pageViews(options?: PageViewOptions): Promise; + /** + * Query the top contributors to the article using the WikiWho API. + * This API has a throttling of 2000 requests a day. + * Supported for EN, DE, ES, EU, TR Wikipedias only + * @see https://api.wikiwho.net/ + */ queryAuthors(): Promise; edit(transform: (rev: { content: string; timestamp: string }) => string | ApiEditPageParams): Promise; save(text: string, summary?: string, options?: ApiEditPageParams): Promise; @@ -97,9 +190,7 @@ export default function (bot: mwn): MwnPageStatic { /**** Get operations *****/ - /** - * Get page wikitext - */ + /** @inheritDoc */ text(): Promise { return bot .request({ @@ -113,11 +204,7 @@ export default function (bot: mwn): MwnPageStatic { }); } - /** - * Get page categories - * @returns {Promise} Resolved with array of objects like - * { sortkey: '...', category: '...', hidden: true } - */ + /** @inheritDoc */ categories(): Promise { return bot .request({ @@ -128,11 +215,7 @@ export default function (bot: mwn): MwnPageStatic { .then((data) => data.parse.categories); } - /** - * Get templates transcluded on the page - * @returns {Promise} Resolved with array of objects like - * { ns: 10, title: 'Template:Cite web', exists: true } - */ + /** @inheritDoc */ templates(): Promise { return bot .request({ @@ -143,11 +226,7 @@ export default function (bot: mwn): MwnPageStatic { .then((data) => data.parse.templates); } - /** - * Get links on the page - * @returns {Promise} Resolved with array of objects like - * { ns: 0, title: 'Main Page', exists: true } - */ + /** @inheritDoc */ links(): Promise { return bot .request({ @@ -158,10 +237,7 @@ export default function (bot: mwn): MwnPageStatic { .then((data) => data.parse.links); } - /** - * Get list of pages linking to this page - * @returns {Promise} - */ + /** @inheritDoc */ backlinks(): Promise { return bot .continuedQuery({ @@ -181,10 +257,7 @@ export default function (bot: mwn): MwnPageStatic { }); } - /** - * Get list of pages transcluding this page - * @returns {Promise} - */ + /** @inheritDoc */ transclusions(): Promise { return bot .continuedQuery({ @@ -204,10 +277,7 @@ export default function (bot: mwn): MwnPageStatic { }); } - /** - * Returns list of images on the page - * @returns {Promise} - array elements don't include File: prefix - */ + /** @inheritDoc */ images(): Promise { return bot .request({ @@ -218,10 +288,7 @@ export default function (bot: mwn): MwnPageStatic { .then((data) => data.parse.images); } - /** - * Returns list of external links on the page - * @returns {Promise} - */ + /** @inheritDoc */ externallinks(): Promise { return bot .request({ @@ -232,10 +299,7 @@ export default function (bot: mwn): MwnPageStatic { .then((data) => data.parse.externallinks); } - /** - * Returns list of subpages of the page - * @returns {Promise} - */ + /** @inheritDoc */ subpages(options?: ApiQueryAllPagesParams): Promise { return bot .request({ @@ -251,21 +315,14 @@ export default function (bot: mwn): MwnPageStatic { }); } - /** - * Check if page is redirect or not - * @returns {Promise} - */ + /** @inheritDoc */ isRedirect(): Promise { return this.getRedirectTarget().then((target) => { return this.toText() !== target; }); } - /** - * Get redirect target. - * Returns the same page name if the page is not a redirect. - * @returns {Promise} - */ + /** @inheritDoc */ getRedirectTarget(): Promise { if (this.data.text) { let target = /^\s*#redirect \[\[(.*?)\]\]/.exec(this.data.text); @@ -289,10 +346,7 @@ export default function (bot: mwn): MwnPageStatic { }); } - /** - * Get username of the page creator - * @returns {Promise} - */ + /** @inheritDoc */ getCreator(): Promise { return bot .request({ @@ -312,10 +366,7 @@ export default function (bot: mwn): MwnPageStatic { }); } - /** - * Get username of the last deleting admin (or null) - * @returns {Promise} - */ + /** @inheritDoc */ getDeletingAdmin(): Promise { return bot .request({ @@ -334,12 +385,7 @@ export default function (bot: mwn): MwnPageStatic { }); } - /** - * Get short description, either the local one (for English Wikipedia) - * or the one from wikidata. - * @param {Object} customOptions - * @returns {Promise} - */ + /** @inheritDoc */ getDescription(customOptions: WikibaseClientApiDescriptionParams) { // ApiParams return bot @@ -358,16 +404,7 @@ export default function (bot: mwn): MwnPageStatic { }); } - /** - * Get the edit history of the page - * @param {string|string[]} props - revision properties to fetch, by default content is - * excluded - * @param {number} [limit=50] - number of revisions to fetch data about - * @param {Object} customOptions - custom API options - * @returns {Promise} - resolved with array of objects representing - * revisions, eg. { revid: 951809097, parentid: 951809097, timestamp: - * "2020-04-19T00:45:35Z", comment: "Edit summary" } - */ + /** @inheritDoc */ history( props: ApiQueryRevisionsParams['rvprop'], limit = 50, @@ -410,17 +447,7 @@ export default function (bot: mwn): MwnPageStatic { } } - /** - * Get the page logs. - * @param {string|string[]} props - data about log entries to fetch - * @param {number} limit - max number of log entries to fetch - * @param {string} type - type of log to fetch, can either be an letype or leaction - * Leave undefined (or null) to fetch all log types - * @param {Object} customOptions - * @returns {Promise} - resolved with array of objects representing - * log entries, eg. { ns: '0', title: 'Main Page', type: 'delete', user: 'Example', - * action: 'revision', timestamp: '2020-05-05T17:13:34Z', comment: 'edit summary' } - */ + /** @inheritDoc */ logs( props: ApiQueryLogEventsParams['leprop'], limit?: number, @@ -471,11 +498,7 @@ export default function (bot: mwn): MwnPageStatic { } } - /** - * Get page views data (only for Wikimedia wikis) - * @see https://wikitech.wikimedia.org/wiki/Analytics/AQS/Pageviews - * @param options - */ + /** @inheritDoc */ async pageViews(options: PageViewOptions = {}): Promise { let project = bot.options.apiUrl.match(/.*\/(.*?)\.(?:org|com|net)/)?.[1]; if (!project) { @@ -517,12 +540,7 @@ export default function (bot: mwn): MwnPageStatic { }); } - /** - * Query the top contributors to the article using the WikiWho API. - * This API has a throttling of 2000 requests a day. - * Supported for EN, DE, ES, EU, TR Wikipedias only - * @see https://api.wikiwho.net/ - */ + /** @inheritDoc */ async queryAuthors(): Promise { let langcodematch = bot.options.apiUrl.match(/([^/]*?)\.wikipedia\.org/); if (!langcodematch || !langcodematch[1]) { diff --git a/src/title.ts b/src/title.ts index 54cc376..83cbf86 100644 --- a/src/title.ts +++ b/src/title.ts @@ -29,19 +29,87 @@ export interface MwnTitle { title: string; namespace: number; fragment: string; + /** + * Get the namespace number + * + * Example: 6 for "File:Example_image.svg". + */ getNamespaceId(): number; + /** + * Get the namespace prefix (in the content language) + * + * Example: "File:" for "File:Example_image.svg". + * In #NS_MAIN this is '', otherwise namespace name plus ':' + */ + getNamespacePrefix(): string; + /** + * Get the main page name + * + * Example: "Example_image.svg" for "File:Example_image.svg". + */ getMain(): string; + /** + * Get the main page name (transformed by #text) + * Example: "Example image.svg" for "File:Example_image.svg". + */ getMainText(): string; + /** + * Get the full page name + * + * Example: "File:Example_image.svg". + * Most useful for API calls, anything that must identify the "title". + */ getPrefixedDb(): string; + /** + * Get the full page name (transformed by #text) + * + * Example: "File:Example image.svg" for "File:Example_image.svg". + */ getPrefixedText(): string; + /** + * Get the fragment (if any). + * + * Note that this method (by design) does not include the hash character and + * the value is not url encoded. + */ getFragment(): string | null; + /** + * Check if the title is in a talk namespace + */ isTalkPage(): boolean; + /** + * Get the title for the associated talk page + * Returns null if not available + */ getTalkPage(): MwnTitle | null; + /** + * Get the title for the subject page of a talk page + * Returns null if not available + */ getSubjectPage(): MwnTitle | null; + /** + * Check if the title can have an associated talk page + */ canHaveTalkPage(): boolean; + /** + * Get the extension of the page name (if any) + */ getExtension(): string | null; + /** + * Shortcut for appendable string to form the main page name. + * + * Returns a string like ".json", or "" if no extension. + */ getDotExtension(): string; + /** + * @alias #getPrefixedDb + * @method + */ toString(): string; + /** + * @alias #getPrefixedText + * @method + */ toText(): string; } @@ -128,30 +196,17 @@ export default function () { this.fragment = parsed.fragment; } - /** - * Get the namespace number - * - * Example: 6 for "File:Example_image.svg". - */ + /** @inheritDoc */ getNamespaceId(): number { return this.namespace; } - /** - * Get the namespace prefix (in the content language) - * - * Example: "File:" for "File:Example_image.svg". - * In #NS_MAIN this is '', otherwise namespace name plus ':' - */ + /** @inheritDoc */ getNamespacePrefix(): string { return getNamespacePrefix(this.namespace); } - /** - * Get the main page name - * - * Example: "Example_image.svg" for "File:Example_image.svg". - */ + /** @inheritDoc */ getMain(): string { if (Title.caseSensitiveNamespaces.indexOf(this.namespace) !== -1 || !this.title.length) { return this.title; @@ -159,54 +214,32 @@ export default function () { return Title.phpCharToUpper(this.title[0]) + this.title.slice(1); } - /** - * Get the main page name (transformed by #text) - * Example: "Example image.svg" for "File:Example_image.svg". - */ + /** @inheritDoc */ getMainText(): string { return this.getMain().replace(/_/g, ' '); } - /** - * Get the full page name - * - * Example: "File:Example_image.svg". - * Most useful for API calls, anything that must identify the "title". - */ + /** @inheritDoc */ getPrefixedDb(): string { return this.getNamespacePrefix() + this.getMain(); } - /** - * Get the full page name (transformed by #text) - * - * Example: "File:Example image.svg" for "File:Example_image.svg". - */ + /** @inheritDoc */ getPrefixedText(): string { return this.getPrefixedDb().replace(/_/g, ' '); } - /** - * Get the fragment (if any). - * - * Note that this method (by design) does not include the hash character and - * the value is not url encoded. - */ + /** @inheritDoc */ getFragment(): string | null { return this.fragment; } - /** - * Check if the title is in a talk namespace - */ + /** @inheritDoc */ isTalkPage(): boolean { return Title.isTalkNamespace(this.getNamespaceId()); } - /** - * Get the title for the associated talk page - * Returns null if not available - */ + /** @inheritDoc */ getTalkPage(): Title | null { if (!this.canHaveTalkPage()) { return null; @@ -214,24 +247,17 @@ export default function () { return this.isTalkPage() ? this : Title.makeTitle(this.getNamespaceId() + 1, this.getMainText()); } - /** - * Get the title for the subject page of a talk page - * Returns null if not available - */ + /** @inheritDoc */ getSubjectPage(): Title | null { return this.isTalkPage() ? Title.makeTitle(this.getNamespaceId() - 1, this.getMainText()) : this; } - /** - * Check if the title can have an associated talk page - */ + /** @inheritDoc */ canHaveTalkPage(): boolean { return this.getNamespaceId() >= NS_MAIN; } - /** - * Get the extension of the page name (if any) - */ + /** @inheritDoc */ getExtension(): string | null { let lastDot = this.title.lastIndexOf('.'); if (lastDot === -1) { @@ -240,11 +266,7 @@ export default function () { return this.title.slice(lastDot + 1) || null; } - /** - * Shortcut for appendable string to form the main page name. - * - * Returns a string like ".json", or "" if no extension. - */ + /** @inheritDoc */ getDotExtension(): string { let ext = this.getExtension(); return ext === null ? '' : '.' + ext; @@ -309,18 +331,12 @@ export default function () { return toUpperMap[chr] || chr.toUpperCase(); } - /** - * @alias #getPrefixedDb - * @method - */ + /** @inheritDoc */ toString(): string { return this.getPrefixedDb(); } - /** - * @alias #getPrefixedText - * @method - */ + /** @inheritDoc */ toText(): string { return this.getPrefixedText(); } diff --git a/src/user.ts b/src/user.ts index d65414d..ae35b49 100644 --- a/src/user.ts +++ b/src/user.ts @@ -28,15 +28,51 @@ export interface MwnUser { username: string; userpage: MwnPage; talkpage: MwnPage; + /** + * Get user's recent contributions + * @param {Object} options - additional API options + * @returns {Promise} + */ contribs(options?: ApiQueryUserContribsParams): Promise; contribsGen(options?: ApiQueryUserContribsParams): AsyncGenerator; + /** + * Get user's recent log actions + * @param {Object} options - additional API options + * @returns {Promise} + */ logs(options?: ApiQueryLogEventsParams): Promise; logsGen(options?: ApiQueryLogEventsParams): AsyncGenerator; + /** + * Get public information about the user + * @param {string|string[]} props - properties to fetch + * @returns {Promise} + */ info(props?: ApiQueryUsersParams['usprop']): Promise; + /** + * Get global user info for wikis with CentralAuth + * @param {string|string[]} props + */ globalinfo(props?: ApiQueryGlobalUserInfoParams['guiprop']): Promise; + /** + * Post a message on user's talk page + * @param {string} header + * @param {string} message + * @returns {Promise} + */ sendMessage(header: string, message: string): Promise; + /** + * Send the user an email + */ email(subject: string, message: string, options?: ApiEmailUserParams): Promise; + /** + * Block the user + * @param {Object} options - additional API options + */ block(options: ApiBlockParams): Promise; + /** + * Unblock the user + * @param {Object} options - additional API options + */ unblock(options: ApiUnblockParams): Promise; } @@ -64,11 +100,7 @@ export default function (bot: mwn) { // XXX: should these yield rather than return? - /** - * Get user's recent contributions - * @param {Object} options - additional API options - * @returns {Promise} - */ + /** @inheritDoc */ contribs(options?: ApiQueryUserContribsParams): Promise { return bot .request({ @@ -96,11 +128,7 @@ export default function (bot: mwn) { } } - /** - * Get user's recent log actions - * @param {Object} options - additional API options - * @returns {Promise} - */ + /** @inheritDoc */ logs(options?: ApiQueryLogEventsParams): Promise { return bot .request({ @@ -128,11 +156,7 @@ export default function (bot: mwn) { } } - /** - * Get public information about the user - * @param {string|string[]} props - properties to fetch - * @returns {Promise} - */ + /** @inheritDoc */ info(props?: ApiQueryUsersParams['usprop']): Promise { return bot .request({ @@ -147,10 +171,7 @@ export default function (bot: mwn) { .then((data) => data.query.users[0]); } - /** - * Get global user info for wikis with CentralAuth - * @param {string|string[]} props - */ + /** @inheritDoc */ globalinfo(props?: ApiQueryGlobalUserInfoParams['guiprop']): Promise { return bot .request({ @@ -164,19 +185,12 @@ export default function (bot: mwn) { }); } - /** - * Post a message on user's talk page - * @param {string} header - * @param {string} message - * @returns {Promise} - */ + /** @inheritDoc */ sendMessage(header: string, message: string): Promise { return this.talkpage.newSection(header, message); } - /** - * Send the user an email - */ + /** @inheritDoc */ email(subject: string, message: string, options?: ApiEmailUserParams): Promise { return bot .request({ @@ -202,10 +216,7 @@ export default function (bot: mwn) { }); } - /** - * Block the user - * @param {Object} options - additional API options - */ + /** @inheritDoc */ block(options: ApiBlockParams): Promise { return bot .request({ @@ -217,10 +228,7 @@ export default function (bot: mwn) { .then((data) => data.block); } - /** - * Unblock the user - * @param {Object} options - additional API options - */ + /** @inheritDoc */ unblock(options: ApiUnblockParams): Promise { return bot .request({ diff --git a/src/wikitext.ts b/src/wikitext.ts index a927205..67f6c2d 100644 --- a/src/wikitext.ts +++ b/src/wikitext.ts @@ -18,12 +18,33 @@ import type { ApiParseParams } from './api_params'; export interface MwnWikitextStatic { new (text: string): MwnWikitext; + + /** Static version of {@link MwnWikitext.parseTemplates} */ parseTemplates(wikitext: string, config: TemplateConfig): Template[]; + + /** + * Simple table parser. + * Parses tables provided: + * 1. It doesn't have any merged or joined cells. + * 2. It doesn't use any templates to produce any table markup. + * 3. Further restrictions may apply. + * + * Tables generated via mwn.table() class are intended to be parsable. + * + * This method throws when it finds an inconsistency (rather than silently + * cause undesired behaviour). + * + * @param {string} text + * @returns {Object[]} - each object in the returned array represents a row, + * with its keys being column names, and values the cell content + */ parseTable( text: string, ): { [column: string]: string; }[]; + + /** Static version of {@link MwnWikitext.parseSections} */ parseSections(text: string): Section[]; } export interface MwnWikitext extends Unbinder { @@ -32,10 +53,53 @@ export interface MwnWikitext extends Unbinder { files: Array; categories: Array; sections: Array
; + /** Parse links, file usages and categories from the wikitext */ parseLinks(): void; + /** + * Parses templates from wikitext. + * Returns an array of Template objects + * ```js + * let templates = parseTemplates("Hello {{foo |Bar|baz=qux |2=loremipsum|3=}} world"); + * console.log(templates[0]); // gives: + * { + * name: "foo", + * wikitext:"{{foo |Bar|baz=qux | 2 = loremipsum |3=}}", + * parameters: [ { name: 1, value: 'Bar', wikitext: '|Bar' }, + * { name: 'baz', value: 'qux', wikitext: '|baz=qux ' }, + * { name: '2', value: 'loremipsum', wikitext: '| 2 = loremipsum ' }, + * { name: '3', value: '', wikitext: '|3=' } + * ] + * } + *``` + * @param {TemplateConfig} config + * @returns {Template[]} + */ parseTemplates(config: TemplateConfig): Template[]; + /** + * Remove a template, link, file or category from the text + * CAUTION: If an entity with the very same wikitext exists earlier in the text, + * that one will be removed instead. + * @param {Object|Template} entity - anything with a wikitext attribute + * and end index + */ removeEntity(entity: Link | Template): void; + /** + * Parse sections from wikitext + * CAUTION: section header syntax in comments, nowiki tags, + * pre, source or syntaxhighlight tags can lead to wrong results. + * You're advised to run unbind() first. + * @returns {Section[]} array of + * section objects. Each section object has the level, header, index (of beginning) and content. + * Content *includes* the equal signs and the header. + * The top is represented as level 1, with header `null`. + */ parseSections(): Section[]; + /** + * Parse the text using the API. + * @see https://www.mediawiki.org/wiki/API:Parsing_wikitext + * @param {Object} [options] - additional API options + * @returns {Promise} + */ apiParse(options: ApiParseParams): Promise; } @@ -63,10 +127,29 @@ export interface Section { content?: string; } +/** + * Configuration for parsing templates. + */ export interface TemplateConfig { + /** + * Also parse templates within subtemplates. The other config parameters + * (namePredicate, templatePredicate, count) are *not* compatible + * with recursive mode. Expect unexpected results if used. + */ recursive?: boolean; + /** + * Include template in result only if the its name matches this predicate. + * More efficient than templatePredicate as the template parameters + * aren't parsed if name didn't match. + */ namePredicate?: (name: string) => boolean; + /** + * Include template in result only if it matches this predicate + */ templatePredicate?: (template: Template) => boolean; + /** + * Max number of templates to be parsed + */ count?: number; } @@ -145,9 +228,7 @@ export class Parameter { // which was in turn adapted from https://en.wikipedia.org/wiki/User:SD0001/parseAllTemplates.js // written by me. (cc-by-sa/GFDL) -/** - * @inheritdoc - */ +/** See {@link MwnWikitext.parseTemplates} */ export function parseTemplates(wikitext: string, config: TemplateConfig): Template[] { config = config || { recursive: false, @@ -309,22 +390,7 @@ function processTemplateText( return template; } -/** - * Simple table parser. - * Parses tables provided: - * 1. It doesn't have any merged or joined cells. - * 2. It doesn't use any templates to produce any table markup. - * 3. Further restrictions may apply. - * - * Tables generated via mwn.table() class are intended to be parsable. - * - * This method throws when it finds an inconsistency (rather than silently - * cause undesired behaviour). - * - * @param {string} text - * @returns {Object[]} - each object in the returned array represents a row, - * with its keys being column names, and values the cell content - */ +/** See {@link MwnWikitextStatic.parseTable} */ export function parseTable(text: string): { [column: string]: string }[] { text = text.trim(); const indexOfRawPipe = function (text: string) { @@ -402,10 +468,7 @@ export function parseTable(text: string): { [column: string]: string }[] { return output; } -// XXX: fix jsdocs -/** - * @inheritdoc - */ +/** See {@link MwnWikitext.parseSections} */ export function parseSections(text: string): Section[] { const rgx = /^(=+)(.*?)\1/gm; let sections: Section[] = [ @@ -514,7 +577,7 @@ export default function (bot: mwn) { super(wikitext); } - /** Parse links, file usages and categories from the wikitext */ + /** @inheritDoc */ parseLinks(): void { this.links = []; this.files = []; @@ -538,68 +601,22 @@ export default function (bot: mwn) { } } - /** - * Parses templates from wikitext. - * Returns an array of Template objects - * var templates = parseTemplates("Hello {{foo |Bar|baz=qux |2=loremipsum|3=}} world"); - * console.log(templates[0]); // gives: - { - name: "foo", - wikitext:"{{foo |Bar|baz=qux | 2 = loremipsum |3=}}", - parameters: [ { name: 1, value: 'Bar', wikitext: '|Bar' }, - { name: 'baz', value: 'qux', wikitext: '|baz=qux ' }, - { name: '2', value: 'loremipsum', wikitext: '| 2 = loremipsum ' }, - { name: '3', value: '', wikitext: '|3=' } - ] - } - * @param {{recursive: boolean, namePredicate: function, templatePredicate: function, - * count: number}} config - * @config {boolean} recursive - also parse templates within subtemplates. The other - * config parameters (namePredicate, templatePredicate, count) are *not* compatible - * with recursive mode. Expect unexpected results if used. - * @config {function} namePredicate - include template in result only if the its name - * matches this predicate. More efficient than templatePredicate as the template parameters - * aren't parsed if name didn't match. - * @config {function} templatePredicate - include template in result only if it matches - * this predicate - * @config {number} count - max number of templates to be parsed. - * @returns {Template[]} - */ + /** @inheritDoc */ parseTemplates(config: TemplateConfig): Template[] { return (this.templates = parseTemplates(this.text, config)); } - /** - * Remove a template, link, file or category from the text - * CAUTION: If an entity with the very same wikitext exists earlier in the text, - * that one will be removed instead. - * @param {Object|Template} entity - anything with a wikitext attribute - * and end index - */ + /** @inheritDoc */ removeEntity(entity: Link | Template) { this.text = this.text.replace(entity.wikitext, ''); } - /** - * Parse the text using the API. - * @see https://www.mediawiki.org/wiki/API:Parsing_wikitext - * @param {Object} [options] - additional API options - * @returns {Promise} - */ + /** @inheritDoc */ apiParse(options?: ApiParseParams): Promise { return bot.parseWikitext(this.text, options); } - /** - * Parse sections from wikitext - * CAUTION: section header syntax in comments, nowiki tags, - * pre, source or syntaxhighlight tags can lead to wrong results. - * You're advised to run unbind() first. - * @returns {{level: number, header: string, index: number, content: string}[]} array of - * section objects. Each section object has the level, header, index (of beginning) and content. - * Content *includes* the equal signs and the header. - * The top is represented as level 1, with header `null`. - */ + /** @inheritDoc */ parseSections(): Section[] { return (this.sections = parseSections(this.text)); } diff --git a/website/docs/11-integration-with-other-apis.md b/website/docs/11-integration-with-other-apis.md index 763daf2..be91f68 100644 --- a/website/docs/11-integration-with-other-apis.md +++ b/website/docs/11-integration-with-other-apis.md @@ -50,4 +50,4 @@ Fetch the list of top contributors to an article. Available for limited number o const page = new bot.page('Lorem ipsum'); const contributorData = await page.queryAuthors(); ``` -Return type is Promise<AuthorshipData>. +Return type is Promise<AuthorshipData>. diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 41caebf..adea55d 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -26,10 +26,9 @@ function HomepageHeader() { } export default function Home() { - const {siteConfig} = useDocusaurusContext(); return (