From d0c5b4f63118ba5c3b3197d52b104a847272439d Mon Sep 17 00:00:00 2001 From: Paul Gottschling Date: Mon, 9 Dec 2024 14:47:04 -0500 Subject: [PATCH] Convert lint-teleport-docs-links to a lintRule Apply gravitational/docs#505 Also fix type errors required to transpile our remark linters from TypeScript. --- server/lint-teleport-docs-links.ts | 28 ++++++++++++---------------- server/rehype-hljs.ts | 26 ++++++++++++++------------ server/remark-code-snippet.ts | 26 ++++++++++++-------------- 3 files changed, 38 insertions(+), 42 deletions(-) diff --git a/server/lint-teleport-docs-links.ts b/server/lint-teleport-docs-links.ts index a81eec8..7e6e37a 100644 --- a/server/lint-teleport-docs-links.ts +++ b/server/lint-teleport-docs-links.ts @@ -1,9 +1,9 @@ +import { lintRule } from "unified-lint-rule"; +import { visit } from "unist-util-visit"; +import type { Link } from "mdast"; +import type { EsmNode, MdxAnyElement, MdxastNode } from "./types-unist"; import type { Node } from "unist"; -import type { Transformer } from "unified"; -import type { Root, Link as MdastLink } from "mdast"; -import type { EsmNode, MdxAnyElement } from "./types-unist"; -import { visit } from "unist-util-visit"; import { isExternalLink, isHash, isPage } from "../utils/url"; interface ObjectHref { @@ -29,17 +29,13 @@ const isAnAbsoluteDocsLink = (href: string): boolean => { ); }; -export function remarkLintTeleportDocsLinks(): Transformer { - return (root: Root, vfile) => { - visit(root, (node: Node) => { - if ( - node.type == "link" && - isAnAbsoluteDocsLink((node as MdastLink).url) - ) { +export const remarkLintTeleportDocsLinks = lintRule( + "remark-lint:absolute-docs-links", + (root: Node, vfile) => { + visit(root, undefined, (node: Node) => { + if (node.type == "link" && isAnAbsoluteDocsLink((node as Link).url)) { vfile.message( - `Link reference ${ - (node as MdastLink).url - } must be a relative link to an *.mdx page`, + `Link reference ${(node as Link).url} must be a relative link to an *.mdx page`, node.position ); return; @@ -58,5 +54,5 @@ export function remarkLintTeleportDocsLinks(): Transformer { } } }); - }; -} + } +); diff --git a/server/rehype-hljs.ts b/server/rehype-hljs.ts index c91f6ed..6a45018 100644 --- a/server/rehype-hljs.ts +++ b/server/rehype-hljs.ts @@ -4,6 +4,7 @@ import rehypeHighlight, { Options as RehypeHighlightOptions, } from "rehype-highlight"; import { common } from "lowlight"; +import type { Node as UnistNode, Parent as UnistParent } from "unist"; import { visit, CONTINUE, SKIP } from "unist-util-visit"; import { v4 as uuid } from "uuid"; import remarkParse from "remark-parse"; @@ -20,11 +21,11 @@ const makePlaceholder = (): string => { const placeholderPattern = "var[a-z0-9]{32}"; export const rehypeHLJS = (options?: RehypeHighlightOptions): Transformer => { - return (root: Parent, file: VFile) => { + return (root: UnistNode, file: VFile) => { // We only visit text nodes inside code snippets that include either the // { + const isPossibleVarContainer = (node: UnistParent) => { let textValue; if ( node.type === "text" || @@ -56,14 +57,15 @@ export const rehypeHLJS = (options?: RehypeHighlightOptions): Transformer => { visit( root, isPossibleVarContainer, - (node: Node, index: number, parent: Parent) => { + (node: UnistNode, index: number, parent: Parent) => { const varPattern = new RegExp("]+/>", "g"); + const unknownText = node as unknown; let txt: Text; if (node.type == "text") { - txt = node; + txt = unknownText as Text; } else { // isPossibleVarContainer enforces having a single child text node - txt = node.children[0]; + txt = (unknownText as Parent).children[0] as Text; } const newVal = txt.value.replace(varPattern, (match) => { @@ -72,18 +74,18 @@ export const rehypeHLJS = (options?: RehypeHighlightOptions): Transformer => { // its properties. The result should be a small HTML AST with a root // node and one child, the Var node. const varElement = unified() - .use(remarkParse) + // Converting to "any" since, for some reason, the type of + // remarkParse doesn't match the signature of "use" despite this + // being a common use case in the unified documentation. + .use(remarkParse as any) .use(remarkMDX) .parse(match); - placeholdersToVars[placeholder] = varElement.children[0]; + placeholdersToVars[placeholder] = (varElement as Parent).children[0]; return placeholder; }); - if (node.type == "text") { - node.value = newVal; - } else { - node.children[0].value = newVal; - } + + txt.value = newVal; } ); diff --git a/server/remark-code-snippet.ts b/server/remark-code-snippet.ts index 2481509..344edcf 100644 --- a/server/remark-code-snippet.ts +++ b/server/remark-code-snippet.ts @@ -24,7 +24,8 @@ */ import type { Transformer } from "unified"; -import type { Code as MdastCode } from "mdast"; +import type { Code as MdastCode, Text } from "mdast"; +import type { Node } from "unist"; import type { MdxastNode, MdxJsxAttribute, @@ -37,10 +38,10 @@ const RULE_ID = "code-snippet"; const isCode = (langs: string[]) => - (node: MdxastNode): node is MdastCode => - node.type === "code" && langs.includes(node.lang); + (node: Node): node is MdastCode => + node.type === "code" && langs.includes((node as MdastCode).lang); -const getTextChildren = (contentValue: string): MdxastNode => ({ +const getTextChildren = (contentValue: string): Text => ({ type: "text", value: contentValue, }); @@ -72,9 +73,9 @@ const getVariableNode = ( }; }; -const getChildrenNode = (content: string): MdxastNode[] => { +const getChildrenNode = (content: string): Array => { const hasVariable = content?.includes(" = []; if (hasVariable) { const contentVars = content.match(/(?:\)/gm); @@ -111,7 +112,7 @@ const getChildrenNode = (content: string): MdxastNode[] => { return nodeChildren; }; -const getCommandNode = (content: string, prefix = "$"): MdxJsxFlowElement => { +const getCommandNode = (content: string, prefix = "$") => { const children = getChildrenNode(content); return { @@ -135,7 +136,7 @@ const getCommandNode = (content: string, prefix = "$"): MdxJsxFlowElement => { }; }; -const getLineNode = (content: string, attributes = []): MdxJsxFlowElement => { +const getLineNode = (content: string, attributes = []) => { const children = getChildrenNode(content); return { @@ -149,7 +150,7 @@ const getLineNode = (content: string, attributes = []): MdxJsxFlowElement => { const getCommentNode = ( content: string, attributes: MdxJsxAttribute[] = [] -): MdxJsxFlowElement => ({ +) => ({ type: "mdxJsxFlowElement", name: "commandcomment", attributes, @@ -161,10 +162,7 @@ const getCommentNode = ( ], }); -const getCodeLine = ( - content: string, - attributes: MdxJsxAttribute[] = [] -): MdxJsxFlowElement => { +const getCodeLine = (content: string, attributes: MdxJsxAttribute[] = []) => { const children = getChildrenNode(content); return { @@ -185,7 +183,7 @@ export default function remarkCodeSnippet({ langs = ["code"], lint = false, }: RemarkCodeSnippetOptions): Transformer { - return (root, vfile) => { + return (root: Node, vfile) => { visit( root, isCode(langs),