mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: annotate md utils to compile with ts-check (#17600)
This commit is contained in:
parent
6fe551e6ac
commit
0a3cfc1c21
@ -22,6 +22,9 @@ const md = require('../markdown');
|
||||
const Documentation = require('./documentation');
|
||||
|
||||
/** @typedef {import('../markdown').MarkdownNode} MarkdownNode */
|
||||
/** @typedef {import('../markdown').MarkdownHeaderNode} MarkdownHeaderNode */
|
||||
/** @typedef {import('../markdown').MarkdownLiNode} MarkdownLiNode */
|
||||
/** @typedef {import('../markdown').MarkdownTextNode} MarkdownTextNode */
|
||||
|
||||
class ApiParser {
|
||||
/**
|
||||
@ -39,7 +42,7 @@ class ApiParser {
|
||||
bodyParts.push(fs.readFileSync(path.join(apiDir, name)).toString());
|
||||
}
|
||||
const body = md.parse(bodyParts.join('\n'));
|
||||
const params = paramsPath ? md.parse(fs.readFileSync(paramsPath).toString()) : null;
|
||||
const params = paramsPath ? md.parse(fs.readFileSync(paramsPath).toString()) : undefined;
|
||||
checkNoDuplicateParamEntries(params);
|
||||
const api = params ? applyTemplates(body, params) : body;
|
||||
/** @type {Map<string, Documentation.Class>} */
|
||||
@ -61,7 +64,7 @@ class ApiParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode} node
|
||||
* @param {MarkdownHeaderNode} node
|
||||
*/
|
||||
parseClass(node) {
|
||||
let extendsName = null;
|
||||
@ -80,7 +83,7 @@ class ApiParser {
|
||||
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode} spec
|
||||
* @param {MarkdownHeaderNode} spec
|
||||
*/
|
||||
parseMember(spec) {
|
||||
const match = spec.text.match(/(event|method|property|async method|optional method|optional async method): ([^.]+)\.(.*)/);
|
||||
@ -112,10 +115,13 @@ class ApiParser {
|
||||
if (match[1].includes('optional'))
|
||||
member.required = false;
|
||||
}
|
||||
const clazz = this.classes.get(match[2]);
|
||||
if (!member)
|
||||
throw new Error('Unknown member: ' + spec.text);
|
||||
|
||||
const clazz = /** @type {Documentation.Class} */(this.classes.get(match[2]));
|
||||
const existingMember = clazz.membersArray.find(m => m.name === name && m.kind === member.kind);
|
||||
if (existingMember && isTypeOverride(existingMember, member)) {
|
||||
for (const lang of member.langs.only) {
|
||||
for (const lang of member?.langs?.only || []) {
|
||||
existingMember.langs.types = existingMember.langs.types || {};
|
||||
existingMember.langs.types[lang] = returnType;
|
||||
}
|
||||
@ -125,7 +131,7 @@ class ApiParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode} spec
|
||||
* @param {MarkdownHeaderNode} spec
|
||||
*/
|
||||
parseArgument(spec) {
|
||||
const match = spec.text.match(/(param|option): (.*)/);
|
||||
@ -173,34 +179,35 @@ class ApiParser {
|
||||
}
|
||||
const p = this.parseProperty(spec);
|
||||
p.required = false;
|
||||
// @ts-ignore
|
||||
options.type.properties.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode} spec
|
||||
* @param {MarkdownHeaderNode} spec
|
||||
*/
|
||||
parseProperty(spec) {
|
||||
const param = childrenWithoutProperties(spec)[0];
|
||||
const text = param.text;
|
||||
const text = /** @type {string}*/(param.text);
|
||||
let typeStart = text.indexOf('<');
|
||||
while ('?e'.includes(text[typeStart - 1]))
|
||||
typeStart--;
|
||||
const name = text.substring(0, typeStart).replace(/\`/g, '').trim();
|
||||
const comments = extractComments(spec);
|
||||
const { type, optional } = this.parseType(param);
|
||||
const { type, optional } = this.parseType(/** @type {MarkdownLiNode} */(param));
|
||||
return Documentation.Member.createProperty(extractMetainfo(spec), name, type, comments, !optional);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode=} spec
|
||||
* @param {MarkdownLiNode} spec
|
||||
* @return {{ type: Documentation.Type, optional: boolean, experimental: boolean }}
|
||||
*/
|
||||
parseType(spec) {
|
||||
const arg = parseVariable(spec.text);
|
||||
const properties = [];
|
||||
for (const child of spec.children || []) {
|
||||
const { name, text } = parseVariable(child.text);
|
||||
for (const child of /** @type {MarkdownLiNode[]} */ (spec.children) || []) {
|
||||
const { name, text } = parseVariable(/** @type {string} */(child.text));
|
||||
const comments = /** @type {MarkdownNode[]} */ ([{ type: 'text', text }]);
|
||||
const childType = this.parseType(child);
|
||||
properties.push(Documentation.Member.createProperty({ langs: {}, experimental: childType.experimental, since: 'v1.0' }, name, childType.type, comments, !childType.optional));
|
||||
@ -271,7 +278,7 @@ function applyTemplates(body, params) {
|
||||
if (!template)
|
||||
throw new Error('Bad template: ' + prop.text);
|
||||
const children = childrenWithoutProperties(template);
|
||||
const { name: argName } = parseVariable(children[0].text);
|
||||
const { name: argName } = parseVariable(children[0].text || '');
|
||||
newChildren.push({
|
||||
type: node.type,
|
||||
text: name + argName,
|
||||
@ -301,7 +308,7 @@ function applyTemplates(body, params) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode} item
|
||||
* @param {MarkdownHeaderNode} item
|
||||
* @returns {MarkdownNode[]}
|
||||
*/
|
||||
function extractComments(item) {
|
||||
@ -323,7 +330,7 @@ function parseApi(apiDir, paramsPath) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode} spec
|
||||
* @param {MarkdownHeaderNode} spec
|
||||
* @returns {import('./documentation').Metainfo}
|
||||
*/
|
||||
function extractMetainfo(spec) {
|
||||
@ -339,7 +346,7 @@ function extractMetainfo(spec) {
|
||||
* @returns {import('./documentation').Langs}
|
||||
*/
|
||||
function extractLangs(spec) {
|
||||
for (const child of spec.children) {
|
||||
for (const child of spec.children || []) {
|
||||
if (child.type !== 'li' || child.liType !== 'bullet' || !child.text.startsWith('langs:'))
|
||||
continue;
|
||||
|
||||
@ -347,7 +354,7 @@ function extractLangs(spec) {
|
||||
/** @type {Object<string, string>} */
|
||||
const aliases = {};
|
||||
for (const p of child.children || []) {
|
||||
const match = p.text.match(/alias-(\w+)[\s]*:(.*)/);
|
||||
const match = /** @type {string}*/(p.text).match(/alias-(\w+)[\s]*:(.*)/);
|
||||
if (match)
|
||||
aliases[match[1].trim()] = match[2].trim();
|
||||
}
|
||||
@ -362,7 +369,7 @@ function extractLangs(spec) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode} spec
|
||||
* @param {MarkdownHeaderNode} spec
|
||||
* @returns {string}
|
||||
*/
|
||||
function extractSince(spec) {
|
||||
@ -377,7 +384,7 @@ function extractSince(spec) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode} spec
|
||||
* @param {MarkdownHeaderNode} spec
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function extractExperimental(spec) {
|
||||
@ -389,12 +396,12 @@ function extractSince(spec) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode} spec
|
||||
* @param {MarkdownHeaderNode} spec
|
||||
* @returns {MarkdownNode[]}
|
||||
*/
|
||||
function childrenWithoutProperties(spec) {
|
||||
return (spec.children || []).filter(c => {
|
||||
const isProperty = c.liType === 'bullet' && (c.text.startsWith('langs:') || c.text.startsWith('since:') || c.text === 'experimental');
|
||||
const isProperty = c.type === 'li' && c.liType === 'bullet' && (c.text.startsWith('langs:') || c.text.startsWith('since:') || c.text === 'experimental');
|
||||
return !isProperty;
|
||||
});
|
||||
}
|
||||
@ -405,11 +412,12 @@ function childrenWithoutProperties(spec) {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isTypeOverride(existingMember, member) {
|
||||
if (!existingMember.langs.only)
|
||||
if (!existingMember.langs.only || !member.langs.only)
|
||||
return true;
|
||||
if (member.langs.only.every(l => existingMember.langs.only.includes(l))) {
|
||||
const existingOnly = existingMember.langs.only;
|
||||
if (member.langs.only.every(l => existingOnly.includes(l))) {
|
||||
return true;
|
||||
} else if (member.langs.only.some(l => existingMember.langs.only.includes(l))) {
|
||||
} else if (member.langs.only.some(l => existingOnly.includes(l))) {
|
||||
throw new Error(`Ambiguous language override for: ${member.name}`);
|
||||
}
|
||||
return false;
|
||||
|
@ -149,19 +149,19 @@ class Documentation {
|
||||
}
|
||||
/**
|
||||
* @param {Documentation.Class|Documentation.Member|null} classOrMember
|
||||
* @param {MarkdownNode[]} nodes
|
||||
* @param {MarkdownNode[] | undefined} nodes
|
||||
*/
|
||||
this._patchLinks = (classOrMember, nodes) => patchLinks(classOrMember, nodes, classesMap, membersMap, linkRenderer);
|
||||
|
||||
for (const clazz of this.classesArray)
|
||||
clazz.visit(item => this._patchLinks(item, item.spec));
|
||||
clazz.visit(item => this._patchLinks?.(item, item.spec));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode[]} nodes
|
||||
*/
|
||||
renderLinksInText(nodes) {
|
||||
this._patchLinks(null, nodes);
|
||||
this._patchLinks?.(null, nodes);
|
||||
}
|
||||
|
||||
generateSourceCodeComments() {
|
||||
@ -192,11 +192,8 @@ Documentation.Class = class {
|
||||
this.extends = extendsName;
|
||||
this.comment = '';
|
||||
this.index();
|
||||
const match = name.match(/(API|JS|CDP|[A-Z])(.*)/);
|
||||
const match = /** @type {string[]} */(name.match(/(API|JS|CDP|[A-Z])(.*)/));
|
||||
this.varName = match[1].toLowerCase() + match[2];
|
||||
}
|
||||
|
||||
index() {
|
||||
/** @type {!Map<string, !Documentation.Member>} */
|
||||
this.members = new Map();
|
||||
/** @type {!Map<string, !Documentation.Member>} */
|
||||
@ -211,6 +208,16 @@ Documentation.Class = class {
|
||||
this.events = new Map();
|
||||
/** @type {!Array<!Documentation.Member>} */
|
||||
this.eventsArray = [];
|
||||
}
|
||||
|
||||
index() {
|
||||
this.members = new Map();
|
||||
this.properties = new Map();
|
||||
this.propertiesArray = [];
|
||||
this.methods = new Map();
|
||||
this.methodsArray = [];
|
||||
this.events = new Map();
|
||||
this.eventsArray = [];
|
||||
|
||||
for (const member of this.membersArray) {
|
||||
this.members.set(member.name, member);
|
||||
@ -342,7 +349,7 @@ Documentation.Member = class {
|
||||
/** @type {!Map<string, !Documentation.Member>} */
|
||||
this.args = new Map();
|
||||
this.index();
|
||||
/** @type {!Documentation.Class} */
|
||||
/** @type {!Documentation.Class | null} */
|
||||
this.clazz = null;
|
||||
/** @type {Documentation.Member=} */
|
||||
this.enclosingMethod = undefined;
|
||||
@ -357,13 +364,13 @@ Documentation.Member = class {
|
||||
this.alias = name;
|
||||
this.overloadIndex = 0;
|
||||
if (name.includes('#')) {
|
||||
const match = name.match(/(.*)#(.*)/);
|
||||
const match = /** @type {string[]} */(name.match(/(.*)#(.*)/));
|
||||
this.alias = match[1];
|
||||
this.overloadIndex = (+match[2]) - 1;
|
||||
}
|
||||
/**
|
||||
* Param is true and option false
|
||||
* @type {Boolean}
|
||||
* @type {Boolean | null}
|
||||
*/
|
||||
this.paramOrOption = null;
|
||||
}
|
||||
@ -376,7 +383,9 @@ Documentation.Member = class {
|
||||
this.args.set(arg.name, arg);
|
||||
arg.enclosingMethod = this;
|
||||
if (arg.name === 'options') {
|
||||
// @ts-ignore
|
||||
arg.type.properties.sort((p1, p2) => p1.name.localeCompare(p2.name));
|
||||
// @ts-ignore
|
||||
arg.type.properties.forEach(p => p.enclosingMethod = this);
|
||||
}
|
||||
}
|
||||
@ -386,6 +395,8 @@ Documentation.Member = class {
|
||||
* @param {string} lang
|
||||
*/
|
||||
filterForLanguage(lang) {
|
||||
if (!this.type)
|
||||
return;
|
||||
if (this.langs.aliases && this.langs.aliases[lang])
|
||||
this.alias = this.langs.aliases[lang];
|
||||
if (this.langs.types && this.langs.types[lang])
|
||||
@ -397,8 +408,10 @@ Documentation.Member = class {
|
||||
continue;
|
||||
const overriddenArg = (arg.langs.overrides && arg.langs.overrides[lang]) || arg;
|
||||
overriddenArg.filterForLanguage(lang);
|
||||
// @ts-ignore
|
||||
if (overriddenArg.name === 'options' && !overriddenArg.type.properties.length)
|
||||
continue;
|
||||
// @ts-ignore
|
||||
overriddenArg.type.filterForLanguage(lang);
|
||||
argsArray.push(overriddenArg);
|
||||
}
|
||||
@ -406,10 +419,12 @@ Documentation.Member = class {
|
||||
}
|
||||
|
||||
filterOutExperimental() {
|
||||
if (!this.type)
|
||||
return;
|
||||
this.type.filterOutExperimental();
|
||||
const argsArray = [];
|
||||
for (const arg of this.argsArray) {
|
||||
if (arg.experimental)
|
||||
if (arg.experimental || !arg.type)
|
||||
continue;
|
||||
arg.type.filterOutExperimental();
|
||||
argsArray.push(arg);
|
||||
@ -418,7 +433,7 @@ Documentation.Member = class {
|
||||
}
|
||||
|
||||
clone() {
|
||||
const result = new Documentation.Member(this.kind, { langs: this.langs, experimental: this.experimental, since: this.since }, this.name, this.type.clone(), this.argsArray.map(arg => arg.clone()), this.spec, this.required);
|
||||
const result = new Documentation.Member(this.kind, { langs: this.langs, experimental: this.experimental, since: this.since }, this.name, this.type?.clone(), this.argsArray.map(arg => arg.clone()), this.spec, this.required);
|
||||
result.alias = this.alias;
|
||||
result.async = this.async;
|
||||
result.paramOrOption = this.paramOrOption;
|
||||
@ -512,6 +527,7 @@ Documentation.Type = class {
|
||||
if (!inUnion && (parsedType.union || parsedType.unionName)) {
|
||||
const type = new Documentation.Type(parsedType.unionName || '');
|
||||
type.union = [];
|
||||
// @ts-ignore
|
||||
for (let t = parsedType; t; t = t.union) {
|
||||
const nestedUnion = !!t.unionName && t !== parsedType;
|
||||
type.union.push(Documentation.Type.fromParsedType(t, !nestedUnion));
|
||||
@ -524,15 +540,17 @@ Documentation.Type = class {
|
||||
if (parsedType.args) {
|
||||
const type = new Documentation.Type('function');
|
||||
type.args = [];
|
||||
// @ts-ignore
|
||||
for (let t = parsedType.args; t; t = t.next)
|
||||
type.args.push(Documentation.Type.fromParsedType(t));
|
||||
type.returnType = parsedType.retType ? Documentation.Type.fromParsedType(parsedType.retType) : null;
|
||||
type.returnType = parsedType.retType ? Documentation.Type.fromParsedType(parsedType.retType) : undefined;
|
||||
return type;
|
||||
}
|
||||
|
||||
if (parsedType.template) {
|
||||
const type = new Documentation.Type(parsedType.name);
|
||||
type.templates = [];
|
||||
// @ts-ignore
|
||||
for (let t = parsedType.template; t; t = t.next)
|
||||
type.templates.push(Documentation.Type.fromParsedType(t));
|
||||
return type;
|
||||
@ -597,7 +615,7 @@ Documentation.Type = class {
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Documentation.Member[]}
|
||||
* @returns {Documentation.Member[] | undefined}
|
||||
*/
|
||||
sortedProperties() {
|
||||
if (!this.properties)
|
||||
@ -653,7 +671,7 @@ Documentation.Type = class {
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ParsedType} type
|
||||
* @param {ParsedType | null} type
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isStringUnion(type) {
|
||||
@ -744,7 +762,7 @@ function matchingBracket(str, open, close) {
|
||||
|
||||
/**
|
||||
* @param {Documentation.Class|Documentation.Member|null} classOrMember
|
||||
* @param {MarkdownNode[]} spec
|
||||
* @param {MarkdownNode[]|undefined} spec
|
||||
* @param {Map<string, Documentation.Class>} classesMap
|
||||
* @param {Map<string, Documentation.Member>} membersMap
|
||||
* @param {Renderer} linkRenderer
|
||||
@ -790,16 +808,17 @@ function patchLinks(classOrMember, spec, classesMap, membersMap, linkRenderer) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MarkdownNode[]} spec
|
||||
* @param {MarkdownNode[] | undefined} spec
|
||||
*/
|
||||
function generateSourceCodeComment(spec) {
|
||||
const comments = (spec || []).filter(n => !n.type.startsWith('h') && (n.type !== 'li' || n.liType !== 'default')).map(c => md.clone(c));
|
||||
md.visitAll(comments, node => {
|
||||
if (node.codeLang && node.codeLang.includes('tab=js-js'))
|
||||
node.type = 'null';
|
||||
if (node.liType === 'bullet')
|
||||
if (node.type === 'li' && node.liType === 'bullet')
|
||||
node.liType = 'default';
|
||||
if (node.type === 'note') {
|
||||
// @ts-ignore
|
||||
node.type = 'text';
|
||||
node.text = '> NOTE: ' + node.text;
|
||||
}
|
||||
|
@ -17,14 +17,52 @@
|
||||
// @ts-check
|
||||
|
||||
/** @typedef {{
|
||||
* type: 'text' | 'li' | 'code' | 'properties' | 'h0' | 'h1' | 'h2' | 'h3' | 'h4' | 'note' | 'null',
|
||||
* type: string,
|
||||
* text?: string,
|
||||
* children?: MarkdownNode[],
|
||||
* codeLang?: string,
|
||||
* noteType?: string,
|
||||
* lines?: string[],
|
||||
* liType?: 'default' | 'bullet' | 'ordinal',
|
||||
* children?: MarkdownNode[]
|
||||
* }} MarkdownNode */
|
||||
* }} MarkdownBaseNode */
|
||||
|
||||
/** @typedef {MarkdownBaseNode & {
|
||||
* type: 'text',
|
||||
* text: string,
|
||||
* }} MarkdownTextNode */
|
||||
|
||||
/** @typedef {MarkdownBaseNode & {
|
||||
* type: 'h0' | 'h1' | 'h2' | 'h3' | 'h4',
|
||||
* text: string,
|
||||
* children: MarkdownNode[]
|
||||
* }} MarkdownHeaderNode */
|
||||
|
||||
/** @typedef {MarkdownBaseNode & {
|
||||
* type: 'li',
|
||||
* text: string,
|
||||
* liType: 'default' | 'bullet' | 'ordinal',
|
||||
* children: MarkdownNode[]
|
||||
* }} MarkdownLiNode */
|
||||
|
||||
/** @typedef {MarkdownBaseNode & {
|
||||
* type: 'code',
|
||||
* lines: string[],
|
||||
* codeLang: string,
|
||||
* }} MarkdownCodeNode */
|
||||
|
||||
/** @typedef {MarkdownBaseNode & {
|
||||
* type: 'note',
|
||||
* text: string,
|
||||
* noteType: string,
|
||||
* }} MarkdownNoteNode */
|
||||
|
||||
/** @typedef {MarkdownBaseNode & {
|
||||
* type: 'null',
|
||||
* }} MarkdownNullNode */
|
||||
|
||||
/** @typedef {MarkdownBaseNode & {
|
||||
* type: 'properties',
|
||||
* lines: string[],
|
||||
* }} MarkdownPropsNode */
|
||||
|
||||
/** @typedef {MarkdownTextNode | MarkdownLiNode | MarkdownCodeNode | MarkdownNoteNode | MarkdownHeaderNode | MarkdownNullNode | MarkdownPropsNode } MarkdownNode */
|
||||
|
||||
function flattenWrappedLines(content) {
|
||||
const inLines = content.replace(/\r\n/g, '\n').split('\n');
|
||||
@ -110,13 +148,13 @@ function buildTree(lines) {
|
||||
else
|
||||
break;
|
||||
}
|
||||
headerStack[0].children.push(node);
|
||||
/** @type {MarkdownNode[]}*/(headerStack[0].children).push(node);
|
||||
headerStack.unshift(node);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remaining items respect indent-based nesting.
|
||||
const [, indent, content] = line.match('^([ ]*)(.*)');
|
||||
const [, indent, content] = /** @type {string[]} */ (line.match('^([ ]*)(.*)'));
|
||||
if (content.startsWith('```')) {
|
||||
/** @type {MarkdownNode} */
|
||||
const node = {
|
||||
@ -143,10 +181,10 @@ function buildTree(lines) {
|
||||
|
||||
if (content.startsWith(':::')) {
|
||||
/** @type {MarkdownNode} */
|
||||
const node = {
|
||||
const node = /** @type {MarkdownNoteNode} */ ({
|
||||
type: 'note',
|
||||
noteType: content.substring(3)
|
||||
};
|
||||
});
|
||||
line = lines[++i];
|
||||
const tokens = [];
|
||||
while (!line.trim().startsWith(':::')) {
|
||||
@ -184,16 +222,17 @@ function buildTree(lines) {
|
||||
const liType = content.match(/^(-|1.|\*) /);
|
||||
const node = /** @type {MarkdownNode} */({ type: 'text', text: content });
|
||||
if (liType) {
|
||||
node.type = 'li';
|
||||
node.text = content.substring(liType[0].length);
|
||||
const liNode = /** @type {MarkdownLiNode} */(node);
|
||||
liNode.type = 'li';
|
||||
liNode.text = content.substring(liType[0].length);
|
||||
if (content.startsWith('1.'))
|
||||
node.liType = 'ordinal';
|
||||
liNode.liType = 'ordinal';
|
||||
else if (content.startsWith('*'))
|
||||
node.liType = 'bullet';
|
||||
liNode.liType = 'bullet';
|
||||
else
|
||||
node.liType = 'default';
|
||||
liNode.liType = 'default';
|
||||
}
|
||||
const match = node.text.match(/\*\*langs: (.*)\*\*(.*)/);
|
||||
const match = node.text?.match(/\*\*langs: (.*)\*\*(.*)/);
|
||||
if (match) {
|
||||
node.codeLang = match[1];
|
||||
node.text = match[2];
|
||||
@ -220,7 +259,7 @@ function render(nodes, maxColumns) {
|
||||
for (let node of nodes) {
|
||||
if (node.type === 'null')
|
||||
continue;
|
||||
innerRenderMdNode('', node, lastNode, result, maxColumns);
|
||||
innerRenderMdNode('', node, /** @type {MarkdownNode} */ (lastNode), result, maxColumns);
|
||||
lastNode = node;
|
||||
}
|
||||
return result.join('\n');
|
||||
@ -240,9 +279,10 @@ function innerRenderMdNode(indent, node, lastNode, result, maxColumns) {
|
||||
};
|
||||
|
||||
if (node.type.startsWith('h')) {
|
||||
const headerNode = /** @type {MarkdownHeaderNode} */ (node);
|
||||
newLine();
|
||||
const depth = +node.type.substring(1);
|
||||
result.push(`${'#'.repeat(depth)} ${node.text}`);
|
||||
result.push(`${'#'.repeat(depth)} ${headerNode.text}`);
|
||||
let lastNode = node;
|
||||
for (const child of node.children || []) {
|
||||
innerRenderMdNode('', child, lastNode, result, maxColumns);
|
||||
|
Loading…
x
Reference in New Issue
Block a user