UNPKG

wikirefs

Version:

wikiref utilities (including [[wikilinks]]).

284 lines (283 loc) 9.38 kB
/* eslint indent: 0 */ /* eslint @typescript-eslint/no-namespace: 0 */ //// // note: // // at the time of writing, constants are mainly a means of defining a // controlled vocabulary that is referenced across the entire WikiBonsai // project (https://github.com/wikibonsai/wikibonsai/blob/main/docs/TERMS.md). declare namespace CONST { // const names // "direction" const DIR: { readonly FORE: "fore"; readonly BACK: "back"; }; // kind of relationships // (used for short-hand) const REL: { readonly FAM: { readonly FAM: "fam"; readonly ANCESTOR: "ancestor"; readonly PARENT: "parent"; readonly SIBLING: "sibling"; readonly CHILD: "child"; readonly DESCENDANT: "descendant"; readonly LINEAGE: "lineage"; }; readonly REF: { readonly REF: "ref"; readonly ATTR: "attr"; readonly LINK: "link"; readonly EMBED: "embed"; readonly DIR: { readonly FORE: "fore"; readonly BACK: "back"; }; }; }; // wiki construct kinds const WIKI: { readonly REF: "wikiref"; readonly ATTR: "wikiattr"; readonly LINK: "wikilink"; readonly EMBED: "wikiembed"; }; // type kinds // (embeds don't have types) const TYPE: { readonly REF: "reftype"; readonly ATTR: "attrtype"; readonly LINK: "linktype"; }; const MEDIA: { readonly MD: "markdown"; readonly PDF: "pdf"; readonly AUD: "audio"; readonly IMG: "image"; readonly VID: "video"; }; // uri formats // (used for markdown link conversion) const URI: { readonly FNAME: "filename"; readonly ABSPATH: "absolute"; readonly RELPATH: "relative"; }; // const marker symbols const MARKER: { readonly EMBED: "!"; readonly PREFIX: ":"; readonly TYPE: "::"; readonly OPEN: "[["; readonly HEADER: "#"; readonly LABEL: "|"; readonly CLOSE: "]]"; }; // const file extensions const EXTS: { readonly MD: ".md"; readonly PDF: ".pdf"; readonly AUD: Set<string>; readonly IMG: Set<string>; readonly VID: Set<string>; }; // glob const GLOB_MEDIA: string; } interface MatchAttr { text: string; start: number; type: [ string, number ]; filenames: [ string, number ][]; listFormat: "comma" | "mkdn" | "none"; } declare namespace RGX { // <------------------------------------------------------------------------> // markdown/wikilink special/reserved characters // <------------------------------------------------------------------------> const _MKDN: { readonly ATX_HEADER: RegExp; readonly SETEXT_HEADER: RegExp; readonly BULLET: RegExp; readonly LINK: RegExp; readonly IMAGE: RegExp; }; const MARKER: { readonly EMBED: RegExp; readonly NOT_EMBED: RegExp; readonly PREFIX: RegExp; readonly TYPE: RegExp; readonly OPEN: RegExp; readonly HEADER: RegExp; readonly LABEL: RegExp; readonly CLOSE: RegExp; }; // <------------------------------------------------------------------------> // valid characters for wikilink text (wikitext) // <------------------------------------------------------------------------> const VALID_CHARS: { readonly TYPE: RegExp; readonly FILENAME: RegExp; readonly HEADER: RegExp; readonly LABEL: RegExp; }; // <------------------------------------------------------------------------> // capture groups for wikilink text (wikitext) // <------------------------------------------------------------------------> const CAP_GRP: { readonly TYPE: RegExp; readonly FILENAME: RegExp; readonly HEADER: RegExp; readonly LABEL: RegExp; }; const ATTR_UTIL: { readonly LIST_COMMA: RegExp; readonly PREFIX: RegExp; }; // <------------------------------------------------------------------------> // for full parsing (w/ line-by-line utilities) // <------------------------------------------------------------------------> // for line-by-line attr processing const ATTR_LINE: { readonly TYPE: RegExp; readonly LIST_ITEM: RegExp; }; // full regex -- can be used with 'g' (global) option const WIKI: { BASE: RegExp; LINK: RegExp; EMBED: RegExp; _ATTR: RegExp; ATTR(content: string): MatchAttr[]; }; // <------------------------------------------------------------------------> // utilities to extract out target wikitext // (useful for entity interactions, renaming things, etc.) // <------------------------------------------------------------------------> const GET: { readonly LINKTYPE: RegExp; readonly ATTRTYPE: RegExp; readonly REFTYPE: RegExp; readonly FILENAME: RegExp; readonly HEADER: RegExp; }; } // todo: // - handle 'wikiattr' individually interface ConvertOpts { kind?: "wikiref" | "wikilink" | "wikiembed"; // CONST.WIKI... format?: "filename" | "relative" | "absolute"; // CONST.URL... ext?: boolean; fnameToUriHash?: Record<string, string>; uriToFnameHash?: Record<string, string>; } /** * Remove pointless wikilink labels: empty `|`, or `|target` when label duplicates the target * (including `[[file#header|file#header]]`). Applies to `[[...]]` and `![[...]]`. */ declare function stripEmptyWikiLinkLabels(content: string): string; declare function mkdnToWiki(content: string, opts?: ConvertOpts): string | undefined; declare function wikiToMkdn(content: string, opts?: ConvertOpts): string | undefined; // todo: // - should filename validity be checked here, or at a higher level? (duplicate filenames + valid filename chars) // - add ability to update only certain kinds of wikirefs (see 'wikiToMkdn' for logic) declare function renameFileName(oldFileName: string, newFileName: string, content: string, opts?: { escape?: boolean; }): string; // alias declare function rename(oldFileName: string, newFileName: string, content: string, opts?: { escape?: boolean; }): string; declare function renameHeader(oldHeader: string, newHeader: string, content: string, opts?: { filename?: string; escape?: boolean; }): string; // alias declare function rehead(oldHeader: string, newHeader: string, content: string, opts?: { filename?: string; escape?: boolean; }): string; declare function retypeRefType(oldRefType: string, newRefType: string, content: string, opts?: { escape?: boolean; }): string; declare function retypeAttrType(oldAttrType: string, newAttrType: string, content: string, opts?: { escape?: boolean; }): string; declare function retypeLinkType(oldLinkType: string, newLinkType: string, content: string, opts?: { escape?: boolean; }): string; // types interface ScanOpts { filename?: string; kind?: string; skipEsc?: boolean; } interface ScanTxt { text: string; start: number; } // wikirefs (grouped constructs) interface ScanAttr { kind: "wikiattr"; match: string; start: number; type: ScanTxt; filenames: ScanTxt[]; listFormat: "comma" | "mkdn" | "none"; } interface ScanLink { kind: "wikilink"; match: string; start: number; type?: ScanTxt; filename: ScanTxt; header?: ScanTxt; label?: ScanTxt; } interface ScanEmbed { kind: "wikiembed"; match: string; start: number; filename: ScanTxt; header?: ScanTxt; media: string; } type ScanRef = ScanAttr | ScanLink | ScanEmbed; // filenames (flat) interface ScannedFileName { filename: ScanTxt; kind: "wikiattr" | "wikilink" | "wikiembed"; type?: ScanTxt; header?: ScanTxt; label?: ScanTxt; media?: string; listFormat?: "comma" | "mkdn" | "none"; } // scan result interface ScanResult { wikirefs: ScanRef[]; filenames: ScannedFileName[]; } // scan declare function scan(content: string, opts?: ScanOpts): ScanResult; declare function getMediaKind(filename: string): string; declare function isMedia(filename: string): boolean; declare function slugify(text: string): string; /** * Extract the markdown section for a given header, for embed rendering. * The section runs from the end of the matching header line until the next * header of the same or higher level, or end of content. * * @param content - Full markdown document * @param headerRef - Header identifier: id/slug (e.g. "header-text") or raw text (e.g. "Header Text") * @returns The section markdown (after the header line), or undefined if no matching header */ declare function getHeaderSection(content: string, headerRef: string): string | undefined; export { CONST, MatchAttr, RGX, ConvertOpts, stripEmptyWikiLinkLabels, mkdnToWiki, wikiToMkdn, renameFileName, rename, renameHeader, rehead, retypeRefType, retypeAttrType, retypeLinkType, ScanOpts, ScanTxt, ScanAttr, ScanLink, ScanEmbed, ScanRef, ScannedFileName, ScanResult, scan, getMediaKind, isMedia, slugify, getHeaderSection };