From edf4ca23f3e016aab71dd2fde12b13f33958ef45 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 9 Aug 2021 13:08:14 +0200 Subject: [PATCH] Add JSDoc based types --- .gitignore | 1 + package.json | 19 ++- packages/rehype-katex/index.js | 28 ++-- packages/rehype-katex/package.json | 15 +- packages/rehype-katex/test.js | 2 +- packages/rehype-katex/tsconfig.json | 4 + packages/rehype-katex/types/index.d.ts | 8 -- packages/rehype-katex/types/test.ts | 9 -- packages/rehype-katex/types/tsconfig.json | 9 -- packages/rehype-katex/types/tslint.json | 7 - packages/rehype-mathjax/browser.d.ts | 12 -- packages/rehype-mathjax/browser.js | 54 +++++--- packages/rehype-mathjax/chtml.d.ts | 43 ------ packages/rehype-mathjax/chtml.js | 23 ++-- packages/rehype-mathjax/index.d.ts | 48 ------- packages/rehype-mathjax/lib/create-input.js | 3 + .../rehype-mathjax/lib/create-output-chtml.js | 4 + .../rehype-mathjax/lib/create-output-svg.js | 4 + packages/rehype-mathjax/lib/create-plugin.js | 129 ++++++++++++++---- .../rehype-mathjax/lib/create-renderer.js | 43 ++++-- packages/rehype-mathjax/package.json | 28 +++- packages/rehype-mathjax/svg.js | 21 +-- packages/rehype-mathjax/test.ts | 42 ------ packages/rehype-mathjax/tsconfig.json | 13 +- packages/rehype-mathjax/tslint.json | 7 - packages/remark-html-katex/index.js | 87 ++++++------ packages/remark-html-katex/package.json | 15 +- packages/remark-html-katex/test.js | 21 ++- packages/remark-html-katex/tsconfig.json | 4 + packages/remark-html-katex/types/index.d.ts | 8 -- packages/remark-html-katex/types/test.ts | 11 -- .../remark-html-katex/types/tsconfig.json | 9 -- packages/remark-html-katex/types/tslint.json | 7 - packages/remark-math/index.js | 27 +++- packages/remark-math/package.json | 23 ++-- packages/remark-math/tsconfig.json | 4 + packages/remark-math/types/index.d.ts | 5 - packages/remark-math/types/test.ts | 7 - packages/remark-math/types/tsconfig.json | 9 -- packages/remark-math/types/tslint.json | 7 - packages/remark-math/util.js | 10 -- tsconfig.json | 16 +++ 42 files changed, 420 insertions(+), 426 deletions(-) create mode 100644 packages/rehype-katex/tsconfig.json delete mode 100644 packages/rehype-katex/types/index.d.ts delete mode 100644 packages/rehype-katex/types/test.ts delete mode 100644 packages/rehype-katex/types/tsconfig.json delete mode 100644 packages/rehype-katex/types/tslint.json delete mode 100644 packages/rehype-mathjax/browser.d.ts delete mode 100644 packages/rehype-mathjax/chtml.d.ts delete mode 100644 packages/rehype-mathjax/index.d.ts delete mode 100644 packages/rehype-mathjax/test.ts delete mode 100644 packages/rehype-mathjax/tslint.json create mode 100644 packages/remark-html-katex/tsconfig.json delete mode 100644 packages/remark-html-katex/types/index.d.ts delete mode 100644 packages/remark-html-katex/types/test.ts delete mode 100644 packages/remark-html-katex/types/tsconfig.json delete mode 100644 packages/remark-html-katex/types/tslint.json create mode 100644 packages/remark-math/tsconfig.json delete mode 100644 packages/remark-math/types/index.d.ts delete mode 100644 packages/remark-math/types/test.ts delete mode 100644 packages/remark-math/types/tsconfig.json delete mode 100644 packages/remark-math/types/tslint.json delete mode 100644 packages/remark-math/util.js create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 33d4929..53a29e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ coverage/ node_modules/ .DS_Store +*.d.ts *.log yarn.lock diff --git a/package.json b/package.json index 0abc081..c987296 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ ], "type": "module", "devDependencies": { + "@types/tape": "^4.0.0", "c8": "^7.0.0", - "dtslint": "^4.0.0", "lerna": "^4.0.0", "prettier": "^2.0.0", "rehype-parse": "^8.0.0", @@ -29,8 +29,10 @@ "remark-preset-wooorm": "^8.0.0", "remark-rehype": "^9.0.0", "remark-stringify": "^10.0.0", + "rimraf": "^3.0.0", "tape": "^5.0.0", "to-vfile": "^7.0.0", + "type-coverage": "^2.0.0", "typescript": "^4.0.0", "unified": "^10.0.0", "unist-builder": "^3.0.0", @@ -39,11 +41,11 @@ }, "scripts": { "postinstall": "lerna bootstrap --no-ci", + "build": "lerna run build", "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", "test-api": "lerna run test-api", "test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov npm run test-api", - "test-types": "lerna run test-types --concurrency 1 --stream", - "test": "npm run format && npm run test-coverage && npm run test-types" + "test": "npm run build && npm run format && npm run test-coverage" }, "prettier": { "tabWidth": 2, @@ -54,14 +56,17 @@ "trailingComma": "none" }, "xo": { - "prettier": true, - "rules": { - "@typescript-eslint/ban-types": "off" - } + "prettier": true }, "remarkConfig": { "plugins": [ "preset-wooorm" ] + }, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true } } diff --git a/packages/rehype-katex/index.js b/packages/rehype-katex/index.js index aa252eb..989b3a2 100644 --- a/packages/rehype-katex/index.js +++ b/packages/rehype-katex/index.js @@ -1,3 +1,8 @@ +/** + * @typedef {import('hast').Root} Root + * @typedef {import('katex').KatexOptions} Options + */ + import katex from 'katex' import {visit} from 'unist-util-visit' import {removePosition} from 'unist-util-remove-position' @@ -11,17 +16,22 @@ const parseHtml = unified().use(rehypeParse, {fragment: true}) const source = 'rehype-katex' +/** + * Plugin to transform `` and `
` + * with KaTeX. + * + * @type {import('unified').Plugin<[Options?]|void[], Root>} + */ export default function rehypeKatex(options) { const settings = options || {} const throwOnError = settings.throwOnError || false - return transformMath - - function transformMath(tree, file) { - visit(tree, 'element', onelement) - - function onelement(element) { - const classes = element.properties.className || [] + return (tree, file) => { + visit(tree, 'element', (element) => { + const classes = + element.properties && Array.isArray(element.properties.className) + ? element.properties.className + : [] const inline = classes.includes('math-inline') const displayMode = classes.includes('math-display') @@ -31,6 +41,7 @@ export default function rehypeKatex(options) { const value = toText(element) + /** @type {string} */ let result try { @@ -54,7 +65,8 @@ export default function rehypeKatex(options) { ) } + // @ts-expect-error: assume no `doctypes` in KaTeX result. element.children = removePosition(parseHtml.parse(result), true).children - } + }) } } diff --git a/packages/rehype-katex/package.json b/packages/rehype-katex/package.json index 0159284..031032e 100644 --- a/packages/rehype-katex/package.json +++ b/packages/rehype-katex/package.json @@ -32,10 +32,13 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { + "@types/hast": "^2.0.0", "@types/katex": "^0.11.0", "hast-util-to-text": "^3.0.0", "katex": "^0.13.0", @@ -45,9 +48,15 @@ "unist-util-visit": "^4.0.0" }, "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test.js", - "test-types": "dtslint types", - "test": "npm run test-api && npm run test-types" + "test": "npm run build && npm run test-api" }, - "xo": false + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/rehype-katex/test.js b/packages/rehype-katex/test.js index 7af82d4..3d28c08 100644 --- a/packages/rehype-katex/test.js +++ b/packages/rehype-katex/test.js @@ -42,7 +42,7 @@ test('rehype-katex', (t) => { t.deepEqual( unified() - .use(remarkParse, {position: false}) + .use(remarkParse) .use(remarkMath) .use(remarkRehype) .use(rehypeKatex) diff --git a/packages/rehype-katex/tsconfig.json b/packages/rehype-katex/tsconfig.json new file mode 100644 index 0000000..7e61871 --- /dev/null +++ b/packages/rehype-katex/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/rehype-katex/types/index.d.ts b/packages/rehype-katex/types/index.d.ts deleted file mode 100644 index 49a3185..0000000 --- a/packages/rehype-katex/types/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {Plugin} from 'unified' -import {KatexOptions} from 'katex' - -type RehypeKatexOptions = KatexOptions - -declare const rehypeKatex: Plugin<[RehypeKatexOptions?]> - -export = rehypeKatex diff --git a/packages/rehype-katex/types/test.ts b/packages/rehype-katex/types/test.ts deleted file mode 100644 index 9323ffa..0000000 --- a/packages/rehype-katex/types/test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import unified from 'unified' -import katex from 'rehype-katex' - -// $ExpectType Processor -unified().use(katex) -// $ExpectType Processor -unified().use(katex, {output: 'html'}) -// $ExpectError -unified().use(katex, {invalidProp: true}) diff --git a/packages/rehype-katex/types/tsconfig.json b/packages/rehype-katex/types/tsconfig.json deleted file mode 100644 index 728b585..0000000 --- a/packages/rehype-katex/types/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "lib": ["es2015", "dom"], - "strict": true, - "baseUrl": ".", - "paths": { "rehype-katex": ["."] }, - "esModuleInterop": true - } -} diff --git a/packages/rehype-katex/types/tslint.json b/packages/rehype-katex/types/tslint.json deleted file mode 100644 index 70c4494..0000000 --- a/packages/rehype-katex/types/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "dtslint/dtslint.json", - "rules": { - "semicolon": false, - "whitespace": false - } -} diff --git a/packages/rehype-mathjax/browser.d.ts b/packages/rehype-mathjax/browser.d.ts deleted file mode 100644 index 35cbab6..0000000 --- a/packages/rehype-mathjax/browser.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {Plugin} from 'unified' // eslint-disable-line import/no-extraneous-dependencies - -// http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block -type MathNotation = [string, string] -interface BrowserOptions { - displayMath?: MathNotation | MathNotation[] - inlineMath?: MathNotation | MathNotation[] -} - -declare const renderBrowser: Plugin<[BrowserOptions?]> - -export = renderBrowser diff --git a/packages/rehype-mathjax/browser.js b/packages/rehype-mathjax/browser.js index aa3a761..b88a8ba 100644 --- a/packages/rehype-mathjax/browser.js +++ b/packages/rehype-mathjax/browser.js @@ -1,25 +1,39 @@ +/** + * @typedef {import('hast').Root} Root + * @typedef {import('hast').Element} Element + * @typedef {import('./lib/create-plugin').MathNotation} MathNotation + * @typedef {import('./lib/create-plugin').BrowserOptions} Options + */ + import {createPlugin} from './lib/create-plugin.js' -const rehypeMathJaxBrowser = createPlugin( - 'rehypeMathJaxBrowser', - renderBrowser, - false, - true -) -export default rehypeMathJaxBrowser +const rehypeMathJaxBrowser = + /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ ( + createPlugin( + // To do next major: Make `options` match the format of MathJax options + // `{tex: ...}` + (_, options) => { + /** @type {MathNotation} */ + let display = ['\\[', '\\]'] + /** @type {MathNotation} */ + let inline = ['\\(', '\\)'] -// To do next major: Make `options` match the format of MathJax options -// `{tex: ...}` -function renderBrowser(options) { - const settings = options || {} - const display = settings.displayMath || ['\\[', '\\]'] - const inline = settings.inlineMath || ['\\(', '\\)'] + if ('displayMath' in options && options.displayMath) { + display = options.displayMath + } - return {render} + if ('inlineMath' in options && options.inlineMath) { + inline = options.inlineMath + } - function render(node, renderOptions) { - const delimiters = renderOptions.display ? display : inline - node.children.unshift({type: 'text', value: delimiters[0]}) - node.children.push({type: 'text', value: delimiters[1]}) - } -} + return { + render(node, options) { + const delimiters = options.display ? display : inline + node.children.unshift({type: 'text', value: delimiters[0]}) + node.children.push({type: 'text', value: delimiters[1]}) + } + } + } + ) + ) +export default rehypeMathJaxBrowser diff --git a/packages/rehype-mathjax/chtml.d.ts b/packages/rehype-mathjax/chtml.d.ts deleted file mode 100644 index 6f8e560..0000000 --- a/packages/rehype-mathjax/chtml.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -import {Plugin} from 'unified' // eslint-disable-line import/no-extraneous-dependencies - -// http://docs.mathjax.org/en/latest/options/output/chtml.html#the-configuration-block -interface MathJaxCHtmlOptions { - scale?: number - minScale?: number - matchFontHeight?: boolean - mtextInheritFont?: boolean - merrorInheritFont?: boolean - mathmlSpacing?: boolean - skipAttributes?: Record - exFactor?: number - displayAlign?: 'left' | 'center' | 'right' - displayIndent?: string - fontURL: string - adaptiveCSS?: boolean -} - -// http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block -interface MathJaxInputTexOptions { - packages: string[] - inlineMath: [[string, string]] - displayMath: [[string, string]] - processEscapes: boolean - processEnvironments: boolean - processRefs: boolean - digits: RegExp - tags: 'none' | 'ams' | 'all' - tagSide: 'left' | 'right' - tagIndent: string - useLabelIds: boolean - multlineWidth: string - maxMacros: number - maxBuffer: number - baseURL: string - formatError: (jax: any, error: any) => string -} - -declare const renderCHtml: Plugin< - [MathJaxCHtmlOptions & {tex?: Partial}] -> - -export = renderCHtml diff --git a/packages/rehype-mathjax/chtml.js b/packages/rehype-mathjax/chtml.js index 7aa2bcd..2ccff67 100644 --- a/packages/rehype-mathjax/chtml.js +++ b/packages/rehype-mathjax/chtml.js @@ -1,15 +1,20 @@ -import {createInput} from './lib/create-input.js' +/** + * @typedef {import('hast').Root} Root + * @typedef {import('./lib/create-plugin').CHtmlOptions} Options + */ + import {createOutputChtml} from './lib/create-output-chtml.js' import {createRenderer} from './lib/create-renderer.js' import {createPlugin} from './lib/create-plugin.js' -const rehypeMathJaxCHtml = createPlugin('rehypeMathJaxCHtml', renderCHtml, true) +const rehypeMathJaxCHtml = + /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ + ( + createPlugin( + (inputOptions, outputOptions) => + createRenderer(inputOptions, createOutputChtml(outputOptions)), + true + ) + ) export default rehypeMathJaxCHtml - -function renderCHtml(inputOptions, outputOptions) { - return createRenderer( - createInput(inputOptions), - createOutputChtml(outputOptions) - ) -} diff --git a/packages/rehype-mathjax/index.d.ts b/packages/rehype-mathjax/index.d.ts deleted file mode 100644 index f75e630..0000000 --- a/packages/rehype-mathjax/index.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Minimum TypeScript Version: 3.2 -import {Plugin} from 'unified' // eslint-disable-line import/no-extraneous-dependencies - -// Should be ported back to MathJax repo -// http://docs.mathjax.org/en/latest/options/output/svg.html#the-configuration-block -interface MathJaxSvgOptions { - scale: number - minScale: number - mtextInheritFont: boolean - merrorInheritFont: boolean - mathmlSpacing: boolean - skipAttributes: Record - exFactor: number - displayAlign: 'left' | 'center' | 'right' - displayIndent: string - fontCache: 'local' | 'global' - localID: string | null - internalSpeechTitles: true - titleID: number -} - -// http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block -interface MathJaxInputTexOptions { - packages: string[] - inlineMath: [[string, string]] - displayMath: [[string, string]] - processEscapes: boolean - processEnvironments: boolean - processRefs: boolean - digits: RegExp - tags: 'none' | 'ams' | 'all' - tagSide: 'left' | 'right' - tagIndent: string - useLabelIds: boolean - multlineWidth: string - maxMacros: number - maxBuffer: number - baseURL: string - formatError: (jax: any, error: any) => string -} - -type RenderSVGOptions = Partial - -declare const renderSvg: Plugin< - [(RenderSVGOptions & {tex?: Partial})?] -> - -export = renderSvg diff --git a/packages/rehype-mathjax/lib/create-input.js b/packages/rehype-mathjax/lib/create-input.js index a73859c..78e6f5d 100644 --- a/packages/rehype-mathjax/lib/create-input.js +++ b/packages/rehype-mathjax/lib/create-input.js @@ -1,6 +1,9 @@ import {TeX} from 'mathjax-full/js/input/tex.js' import {AllPackages} from 'mathjax-full/js/input/tex/AllPackages.js' +/** + * @param {unknown} options + */ export function createInput(options) { return new TeX(Object.assign({packages: AllPackages}, options)) } diff --git a/packages/rehype-mathjax/lib/create-output-chtml.js b/packages/rehype-mathjax/lib/create-output-chtml.js index e0e6b32..afc725b 100644 --- a/packages/rehype-mathjax/lib/create-output-chtml.js +++ b/packages/rehype-mathjax/lib/create-output-chtml.js @@ -1,5 +1,9 @@ import {CHTML} from 'mathjax-full/js/output/chtml.js' +/** + * @param {unknown} options + */ export function createOutputChtml(options) { + // @ts-expect-error: assume options work (mathjax types are not exported) return new CHTML(options) } diff --git a/packages/rehype-mathjax/lib/create-output-svg.js b/packages/rehype-mathjax/lib/create-output-svg.js index e3115a3..7fcc71c 100644 --- a/packages/rehype-mathjax/lib/create-output-svg.js +++ b/packages/rehype-mathjax/lib/create-output-svg.js @@ -1,5 +1,9 @@ import {SVG} from 'mathjax-full/js/output/svg.js' +/** + * @param {unknown} options + */ export function createOutputSvg(options) { + // @ts-expect-error: assume options work (mathjax types are not exported) return new SVG(options) } diff --git a/packages/rehype-mathjax/lib/create-plugin.js b/packages/rehype-mathjax/lib/create-plugin.js index 5cf9ccd..e7ee18d 100644 --- a/packages/rehype-mathjax/lib/create-plugin.js +++ b/packages/rehype-mathjax/lib/create-plugin.js @@ -1,44 +1,115 @@ +/** + * @typedef {import('hast').Root} Root + * @typedef {import('hast').Element} Element + * + * @typedef {[string, string]} MathNotation + * Markers to use for math. + * See: + * + * @typedef BrowserOptions + * Configuration. + * @property {MathNotation} [displayMath] + * Markers to use for blocks. + * @property {MathNotation} [inlineMath] + * Markers to use for inlines. + * + * @typedef MathJaxSvgOptions + * + * @property {number} [scale] + * @property {number} [minScale] + * @property {boolean} [mtextInheritFont] + * @property {boolean} [merrorInheritFont] + * @property {boolean} [mathmlSpacing] + * @property {Record} [skipAttributes] + * @property {number} [exFactor] + * @property {'left'|'center'|'right'} [displayAlign] + * @property {string} [displayIndent] + * @property {'local'|'global'} [fontCache] + * @property {string|null} [localID] + * @property {boolean} [internalSpeechTitles] + * @property {number} [titleID] + * + * @typedef MathJaxCHtmlOptions + * + * @property {number} [scale] + * @property {number} [minScale] + * @property {boolean} [matchFontHeight] + * @property {boolean} [mtextInheritFont] + * @property {boolean} [merrorInheritFont] + * @property {boolean} [mathmlSpacing] + * @property {Record} [skipAttributes] + * @property {number} [exFactor] + * @property {'left'|'center'|'right'} [displayAlign] + * @property {string} [displayIndent] + * @property {string} fontURL + * @property {boolean} [adaptiveCSS] + * + * @typedef MathJaxInputTexOptions + * + * @property {string[]} [packages] + * @property {MathNotation[]} [inlineMath] + * @property {MathNotation[]} [displayMath] + * @property {boolean} [processEscapes] + * @property {boolean} [processEnvironments] + * @property {boolean} [processRefs] + * @property {RegExp} [digits] + * @property {'none'|'ams'|'all'} [tags] + * @property {'left'|'right'} [tagSide] + * @property {string} [tagIndent] + * @property {boolean} [useLabelIds] + * @property {string} [multlineWidth] + * @property {number} [maxMacros] + * @property {number} [maxBuffer] + * @property {string} [baseURL] + * @property {(jax: any, error: any) => string} [formatError] + * + * @typedef {MathJaxCHtmlOptions & {tex?: MathJaxInputTexOptions}} CHtmlOptions + * @typedef {MathJaxSvgOptions & {tex?: MathJaxInputTexOptions}} SvgOptions + * + * @typedef {BrowserOptions|CHtmlOptions|SvgOptions} Options + * + * @typedef Renderer + * @property {(node: Element, options: {display: boolean}) => void} render + * @property {() => Element} [styleSheet] + * + * @callback CreateRenderer + * @param {MathJaxInputTexOptions} inputOptions + * @param {MathJaxCHtmlOptions|MathJaxSvgOptions|BrowserOptions} outputOptions + * @returns {Renderer} + */ + import {visit, SKIP} from 'unist-util-visit' // To do next major: Remove `chtml` and `browser` flags once all the options use // the same format. -export function createPlugin(displayName, createRenderer, chtml, browser) { - attacher.displayName = displayName - - return attacher - function attacher(options) { - if (chtml && (!options || !options.fontURL)) { +/** + * @param {CreateRenderer} createRenderer + * @param {boolean} [chtml=false] + */ +export function createPlugin(createRenderer, chtml) { + /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ + return (options = {}) => { + if (chtml && (!('fontURL' in options) || !options.fontURL)) { throw new Error( 'rehype-mathjax: missing `fontURL` in options, which must be set to a URL to reach MathJaX fonts' ) } - const inputOptions = browser ? options : (options || {}).tex - let outputOptions = options || {} - if ('tex' in outputOptions) { - outputOptions = Object.assign({}, outputOptions) - delete outputOptions.tex - } - - transform.displayName = displayName + 'Transform' - - return transform - - function transform(tree) { - const renderer = createRenderer(inputOptions, outputOptions) + // @ts-expect-error: hush. + const {tex, ...outputOptions} = options + return (tree) => { + const renderer = createRenderer(tex || {}, outputOptions) + /** @type {Root|Element} */ let context = tree let found = false - visit(tree, 'element', onelement) - - if (found && renderer.styleSheet) { - context.children.push(renderer.styleSheet()) - } - - function onelement(node) { - const classes = node.properties.className || [] + visit(tree, 'element', (node) => { + const classes = + node.properties && Array.isArray(node.properties.className) + ? node.properties.className + : [] const inline = classes.includes('math-inline') const display = classes.includes('math-display') @@ -54,6 +125,10 @@ export function createPlugin(displayName, createRenderer, chtml, browser) { renderer.render(node, {display}) return SKIP + }) + + if (found && renderer.styleSheet) { + context.children.push(renderer.styleSheet()) } } } diff --git a/packages/rehype-mathjax/lib/create-renderer.js b/packages/rehype-mathjax/lib/create-renderer.js index 8d86d5a..50e19ea 100644 --- a/packages/rehype-mathjax/lib/create-renderer.js +++ b/packages/rehype-mathjax/lib/create-renderer.js @@ -1,7 +1,16 @@ +/** + * @typedef {import('hast').Element} Element + * @typedef {import('mathjax-full/js/core/OutputJax').OutputJax} OutputJax + * @typedef {import('./create-plugin.js').CreateRenderer} CreateRenderer + * + * For some reason MathJax types can’t be imported. + */ + import {mathjax} from 'mathjax-full/js/mathjax.js' import {RegisterHTMLHandler} from 'mathjax-full/js/handlers/html.js' import {fromDom} from 'hast-util-from-dom' import {toText} from 'hast-util-to-text' +import {createInput} from './create-input.js' import {createAdaptor} from './create-adaptor.js' const adaptor = createAdaptor() @@ -19,23 +28,29 @@ const adaptor = createAdaptor() /* eslint-disable-next-line new-cap */ RegisterHTMLHandler(adaptor) -export function createRenderer(input, output) { +/** + * @type {CreateRenderer} + * @param {OutputJax} output + */ +export function createRenderer(inputOptions, output) { + const input = createInput(inputOptions) const doc = mathjax.document('', {InputJax: input, OutputJax: output}) - return {render, styleSheet} - - function render(node, options) { - node.children = [fromDom(doc.convert(toText(node), options))] - } - - function styleSheet() { - const value = adaptor.textContent(output.styleSheet(doc)) + return { + render(node, options) { + const domNode = doc.convert(toText(node), options) + // @ts-expect-error: assume no `doctypes` + node.children = [fromDom(domNode)] + }, + styleSheet() { + const value = adaptor.textContent(output.styleSheet(doc)) - return { - type: 'element', - tagName: 'style', - properties: {}, - children: [{type: 'text', value}] + return { + type: 'element', + tagName: 'style', + properties: {}, + children: [{type: 'text', value}] + } } } } diff --git a/packages/rehype-mathjax/package.json b/packages/rehype-mathjax/package.json index 190903c..19d5378 100644 --- a/packages/rehype-mathjax/package.json +++ b/packages/rehype-mathjax/package.json @@ -32,28 +32,50 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ "lib/", + "browser.d.ts", "browser.js", + "chtml.d.ts", "chtml.js", + "index.d.ts", "index.js", + "svg.d.ts", "svg.js" ], "browser": { "./lib/create-adaptor.js": "./lib/create-adaptor.browser.js" }, "dependencies": { + "@types/hast": "^2.0.0", "@types/mathjax": "^0.0.36", "hast-util-from-dom": "^4.0.0", "hast-util-to-text": "^3.0.0", "jsdom": "^16.0.0", "mathjax-full": "^3.0.0", + "unified": "^10.0.0", "unist-util-visit": "^4.0.0" }, + "devDependencies": { + "@types/jsdom": "^16.0.0" + }, "scripts": { + "build": "rimraf \"lib/**/*.d.ts\" \"test/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test/index.js", - "test-types": "dtslint", - "test": "npm run test-api && npm run test-types" + "test": "npm run build && npm run test-api" }, - "xo": false + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true, + "#": "needed `any`s", + "ignoreFiles": [ + "lib/create-plugin.d.ts", + "lib/create-renderer.d.ts", + "lib/create-renderer.js" + ] + } } diff --git a/packages/rehype-mathjax/svg.js b/packages/rehype-mathjax/svg.js index cbe9be5..54dbdcf 100644 --- a/packages/rehype-mathjax/svg.js +++ b/packages/rehype-mathjax/svg.js @@ -1,15 +1,18 @@ -import {createInput} from './lib/create-input.js' +/** + * @typedef {import('hast').Root} Root + * @typedef {import('./lib/create-plugin').SvgOptions} Options + */ + import {createOutputSvg} from './lib/create-output-svg.js' import {createRenderer} from './lib/create-renderer.js' import {createPlugin} from './lib/create-plugin.js' -const rehypeMathJaxSvg = createPlugin('rehypeMathJaxSvg', renderSvg) +const rehypeMathJaxSvg = + /** @type {import('unified').Plugin<[Options?]|void[], Root>} */ + ( + createPlugin((inputOptions, outputOptions) => + createRenderer(inputOptions, createOutputSvg(outputOptions)) + ) + ) export default rehypeMathJaxSvg - -function renderSvg(inputOptions, outputOptions) { - return createRenderer( - createInput(inputOptions), - createOutputSvg(outputOptions) - ) -} diff --git a/packages/rehype-mathjax/test.ts b/packages/rehype-mathjax/test.ts deleted file mode 100644 index 64312c5..0000000 --- a/packages/rehype-mathjax/test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import unified from 'unified' // eslint-disable-line import/no-extraneous-dependencies -import mathjax from 'rehype-mathjax' -import chtml from 'rehype-mathjax/chtml' -import browser from 'rehype-mathjax/browser' - -// $ExpectType Processor -unified().use(mathjax) -// $ExpectType Processor -unified().use(mathjax, {minScale: 3}) -// $ExpectType Processor -unified().use(mathjax, {minScale: 3, tex: {tags: 'ams'}}) -// $ExpectError -unified().use(mathjax, {invalidProp: true}) - -// $ExpectType Processor -unified().use(chtml, {fontURL: 'url'}) -// $ExpectType Processor -unified().use(chtml, {fontURL: 'url', tex: {tags: 'ams'}}) -// $ExpectError -unified().use(chtml) -// $ExpectError -unified().use(chtml, {}) -// $ExpectError -unified().use(chtml, {adaptiveCSS: true}) -// $ExpectError -unified().use(chtml, {fontURL: 'url', invalidProp: true}) - -// $ExpectType Processor -unified().use(browser) -// $ExpectType Processor -unified().use(browser, {displayMath: ['$$', '$$']}) -// $ExpectType Processor -unified().use(browser, { - displayMath: [ - ['$$', '$$'], - ['((', '))'] - ] -}) -// $ExpectError -unified().use(browser, {displayMath: ['$$']}) -// $ExpectError -unified().use(browser, {invalidProp: true}) diff --git a/packages/rehype-mathjax/tsconfig.json b/packages/rehype-mathjax/tsconfig.json index 20c9189..5fec52d 100644 --- a/packages/rehype-mathjax/tsconfig.json +++ b/packages/rehype-mathjax/tsconfig.json @@ -1,13 +1,4 @@ { - "compilerOptions": { - "lib": ["es2015", "dom"], - "strict": true, - "baseUrl": ".", - "paths": { - "rehype-mathjax": ["."], - "rehype-mathjax/chtml": ["./chtml"], - "rehype-mathjax/browser": ["./browser"] - }, - "esModuleInterop": true - } + "extends": "../../tsconfig.json", + "include": ["lib/**/*.js", "test/**/*.js", "*.js"] } diff --git a/packages/rehype-mathjax/tslint.json b/packages/rehype-mathjax/tslint.json deleted file mode 100644 index 70c4494..0000000 --- a/packages/rehype-mathjax/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "dtslint/dtslint.json", - "rules": { - "semicolon": false, - "whitespace": false - } -} diff --git a/packages/remark-html-katex/index.js b/packages/remark-html-katex/index.js index c16d980..e4c228d 100644 --- a/packages/remark-html-katex/index.js +++ b/packages/remark-html-katex/index.js @@ -1,3 +1,8 @@ +/** + * @typedef {import('mdast').Root} Root + * @typedef {import('katex').KatexOptions} Options + */ + import {visit} from 'unist-util-visit' import {removePosition} from 'unist-util-remove-position' import katex from 'katex' @@ -8,44 +13,50 @@ const parseHtml = unified().use(rehypeParse, {fragment: true}) const source = 'remark-html-katex' -export default function remarkHtmlKatex(options) { - const settings = options || {} - const throwOnError = settings.throwOnError || false - - return transform - - function transform(tree, file) { - visit(tree, ['inlineMath', 'math'], onmath) - - function onmath(node) { - const displayMode = node.type === 'math' - let result - - try { - result = katex.renderToString( - node.value, - Object.assign({}, settings, { - displayMode, - throwOnError: true - }) - ) - } catch (error) { - const fn = throwOnError ? 'fail' : 'message' - const origin = [source, error.name.toLowerCase()].join(':') - - file[fn](error.message, node.position, origin) - - result = katex.renderToString( - node.value, - Object.assign({}, settings, { - displayMode, - throwOnError: false, - strict: 'ignore' - }) - ) +/** + * Plugin to transform `inlineMath` and `math` nodes with KaTeX for + * `remark-html`. + * + * @type {import('unified').Plugin<[Options?]|void[], Root>} + */ +export default function remarkHtmlKatex(options = {}) { + const throwOnError = options.throwOnError || false + + return (tree, file) => { + visit(tree, (node) => { + if (node.type === 'inlineMath' || node.type === 'math') { + const displayMode = node.type === 'math' + /** @type {string} */ + let result + + try { + result = katex.renderToString( + node.value, + Object.assign({}, options, { + displayMode, + throwOnError: true + }) + ) + } catch (error) { + const fn = throwOnError ? 'fail' : 'message' + const origin = [source, error.name.toLowerCase()].join(':') + + file[fn](error.message, node.position, origin) + + result = katex.renderToString( + node.value, + Object.assign({}, options, { + displayMode, + throwOnError: false, + strict: 'ignore' + }) + ) + } + + const data = node.data || (node.data = {}) + + data.hChildren = removePosition(parseHtml.parse(result)).children } - - node.data.hChildren = removePosition(parseHtml.parse(result)).children - } + }) } } diff --git a/packages/remark-html-katex/package.json b/packages/remark-html-katex/package.json index 16d1541..449a1fa 100644 --- a/packages/remark-html-katex/package.json +++ b/packages/remark-html-katex/package.json @@ -30,11 +30,14 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ + "index.d.ts", "index.js" ], "dependencies": { "@types/katex": "^0.11.0", + "@types/mdast": "^3.0.0", "katex": "^0.13.0", "rehype-parse": "^8.0.0", "unified": "^10.0.0", @@ -42,9 +45,15 @@ "unist-util-visit": "^4.0.0" }, "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test.js", - "test-types": "dtslint types", - "test": "npm run test-api && npm run test-types" + "test": "npm run build && npm run test-api" }, - "xo": false + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-html-katex/test.js b/packages/remark-html-katex/test.js index 2075285..e9ae15f 100644 --- a/packages/remark-html-katex/test.js +++ b/packages/remark-html-katex/test.js @@ -50,7 +50,7 @@ test('remark-html-katex', (t) => { t.deepEqual( unified() - .use(remarkParse, {position: false}) + .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex, {macros}) .use(remarkHtml) @@ -70,7 +70,7 @@ test('remark-html-katex', (t) => { t.deepEqual( unified() - .use(remarkParse, {position: false}) + .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex, {errorColor: 'orange'}) .use(remarkHtml) @@ -122,7 +122,7 @@ test('remark-html-katex', (t) => { t.deepEqual( unified() - .use(remarkParse, {position: false}) + .use(remarkParse) .use(remarkMath) .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) .use(remarkHtml) @@ -138,5 +138,20 @@ test('remark-html-katex', (t) => { 'should support `strict: ignore`' ) + const pipeline = unified() + .use(remarkHtmlKatex, {errorColor: 'orange', strict: 'ignore'}) + .use(remarkHtml) + + t.deepEqual( + pipeline.stringify( + pipeline.runSync({ + type: 'root', + children: [{type: 'inlineMath', value: '\\alpha'}] + }) + ), + '
' + katex.renderToString('\\alpha') + '
\n', + 'should support generated nodes' + ) + t.end() }) diff --git a/packages/remark-html-katex/tsconfig.json b/packages/remark-html-katex/tsconfig.json new file mode 100644 index 0000000..7e61871 --- /dev/null +++ b/packages/remark-html-katex/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-html-katex/types/index.d.ts b/packages/remark-html-katex/types/index.d.ts deleted file mode 100644 index c826b5b..0000000 --- a/packages/remark-html-katex/types/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {Plugin} from 'unified' -import {KatexOptions} from 'katex' - -type HtmlKatexOptions = KatexOptions - -declare const htmlKatex: Plugin<[HtmlKatexOptions?]> - -export = htmlKatex diff --git a/packages/remark-html-katex/types/test.ts b/packages/remark-html-katex/types/test.ts deleted file mode 100644 index e301a45..0000000 --- a/packages/remark-html-katex/types/test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import unified from 'unified' -import html from 'remark-html-katex' - -// $ExpectType Processor -unified().use(html) -// $ExpectType Processor -unified().use(html, {output: 'mathml'}) -// $ExpectError -unified().use(html, {output: true}) -// $ExpectError -unified().use(html, {invalidProp: true}) diff --git a/packages/remark-html-katex/types/tsconfig.json b/packages/remark-html-katex/types/tsconfig.json deleted file mode 100644 index 38c7beb..0000000 --- a/packages/remark-html-katex/types/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "lib": ["es2015", "dom"], - "strict": true, - "baseUrl": ".", - "paths": {"remark-html-katex": ["."]}, - "esModuleInterop": true - } -} diff --git a/packages/remark-html-katex/types/tslint.json b/packages/remark-html-katex/types/tslint.json deleted file mode 100644 index 70c4494..0000000 --- a/packages/remark-html-katex/types/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "dtslint/dtslint.json", - "rules": { - "semicolon": false, - "whitespace": false - } -} diff --git a/packages/remark-math/index.js b/packages/remark-math/index.js index d8ab764..a6e2271 100644 --- a/packages/remark-math/index.js +++ b/packages/remark-math/index.js @@ -1,8 +1,20 @@ +/** + * @typedef {import('mdast').Root} Root + * + * @typedef {import('mdast-util-math')} DoNotTouchAsThisImportIncludesMathInTree + */ + import {math} from 'micromark-extension-math' import {mathFromMarkdown, mathToMarkdown} from 'mdast-util-math' +/** @type {boolean|undefined} */ let warningIssued +/** + * Plugin to support math. + * + * @type {import('unified').Plugin} + */ export default function remarkMath() { const data = this.data() @@ -27,10 +39,17 @@ export default function remarkMath() { add('fromMarkdownExtensions', mathFromMarkdown) add('toMarkdownExtensions', mathToMarkdown) + /** + * @param {string} field + * @param {unknown} value + */ function add(field, value) { - // Other extensions. - /* c8 ignore next */ - if (data[field]) data[field].push(value) - else data[field] = [value] + const list = /** @type {unknown[]} */ ( + // Other extensions + /* c8 ignore next 2 */ + data[field] ? data[field] : (data[field] = []) + ) + + list.push(value) } } diff --git a/packages/remark-math/package.json b/packages/remark-math/package.json index 369dbe6..534b3e0 100644 --- a/packages/remark-math/package.json +++ b/packages/remark-math/package.json @@ -29,20 +29,27 @@ "sideEffects": false, "type": "module", "main": "index.js", + "types": "index.d.ts", "files": [ - "block.js", - "index.js", - "inline.js", - "util.js" + "index.d.ts", + "index.js" ], "dependencies": { + "@types/mdast": "^3.0.0", "mdast-util-math": "^1.0.0", - "micromark-extension-math": "^1.0.0" + "micromark-extension-math": "^1.0.0", + "unified": "^10.0.0" }, "scripts": { + "build": "rimraf \"*.d.ts\" && tsc && type-coverage", "test-api": "node --conditions development test.js", - "test-types": "dtslint types", - "test": "npm run test-api && npm run test-types" + "test": "npm run build && npm run test-api" }, - "xo": false + "xo": false, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } } diff --git a/packages/remark-math/tsconfig.json b/packages/remark-math/tsconfig.json new file mode 100644 index 0000000..7e61871 --- /dev/null +++ b/packages/remark-math/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["*.js"] +} diff --git a/packages/remark-math/types/index.d.ts b/packages/remark-math/types/index.d.ts deleted file mode 100644 index 436c521..0000000 --- a/packages/remark-math/types/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import {Plugin} from 'unified' // eslint-disable-line import/no-extraneous-dependencies - -declare const remarkMath: Plugin<[]> - -export = remarkMath diff --git a/packages/remark-math/types/test.ts b/packages/remark-math/types/test.ts deleted file mode 100644 index f5995be..0000000 --- a/packages/remark-math/types/test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import unified from 'unified' // eslint-disable-line import/no-extraneous-dependencies -import math from 'remark-math' - -// $ExpectType Processor -unified().use(math) -// $ExpectError -unified().use(math, {invalidProp: true}) diff --git a/packages/remark-math/types/tsconfig.json b/packages/remark-math/types/tsconfig.json deleted file mode 100644 index 3440cc6..0000000 --- a/packages/remark-math/types/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "lib": ["es2015", "dom"], - "strict": true, - "baseUrl": ".", - "paths": {"remark-math": ["."]}, - "esModuleInterop": true - } -} diff --git a/packages/remark-math/types/tslint.json b/packages/remark-math/types/tslint.json deleted file mode 100644 index 70c4494..0000000 --- a/packages/remark-math/types/tslint.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "dtslint/dtslint.json", - "rules": { - "semicolon": false, - "whitespace": false - } -} diff --git a/packages/remark-math/util.js b/packages/remark-math/util.js deleted file mode 100644 index 0f61138..0000000 --- a/packages/remark-math/util.js +++ /dev/null @@ -1,10 +0,0 @@ -exports.isRemarkParser = isRemarkParser -exports.isRemarkCompiler = isRemarkCompiler - -function isRemarkParser(parser) { - return Boolean(parser && parser.prototype && parser.prototype.blockTokenizers) -} - -function isRemarkCompiler(compiler) { - return Boolean(compiler && compiler.prototype && compiler.prototype.visitors) -} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e31adf8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "include": ["*.js"], + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020"], + "module": "ES2020", + "moduleResolution": "node", + "allowJs": true, + "checkJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "strict": true + } +}