Skip to content

Commit

Permalink
fix: update rehype-parse@9
Browse files Browse the repository at this point in the history
  • Loading branch information
azu committed Aug 9, 2024
1 parent 8341fcd commit 95a6606
Show file tree
Hide file tree
Showing 10 changed files with 819 additions and 623 deletions.
30 changes: 15 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,26 @@
"watch": "tsc --build --watch"
},
"dependencies": {
"@textlint/ast-node-types": "^13.0.5",
"@textlint/ast-node-types": "^14.0.5",
"neotraverse": "^0.6.15",
"rehype-parse": "^8.0.4",
"rehype-parse": "^9.0.0",
"structured-source": "^4.0.0",
"unified": "^10.1.2"
"unified": "^11.0.5"
},
"devDependencies": {
"@textlint/ast-tester": "^13.0.5",
"@textlint/kernel": "^13.0.5",
"@textlint/module-interop": "^13.0.5",
"@textlint/types": "^13.0.5",
"@types/glob": "^8.0.1",
"@types/mocha": "^10.0.1",
"@types/node": "^18.11.18",
"glob": "^8.1.0",
"mocha": "^10.2.0",
"textlint": "^13.0.5",
"@textlint/ast-tester": "^14.0.5",
"@textlint/kernel": "^14.0.5",
"@textlint/module-interop": "^14.0.5",
"@textlint/types": "^14.0.5",
"@types/glob": "^8.1.0",
"@types/mocha": "^10.0.7",
"@types/node": "^22.1.0",
"glob": "^11.0.0",
"mocha": "^10.7.3",
"textlint": "^14.0.5",
"textlint-rule-no-todo": "^2.0.0",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
"ts-node": "^10.9.2",
"typescript": "^5.5.4"
},
"packageManager": "[email protected]",
"email": "[email protected]"
Expand Down
319 changes: 161 additions & 158 deletions src/html-to-ast.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
// LICENSE : MIT
import type {
TxtBlockQuoteNode,
TxtBreakNode,
TxtCodeBlockNode,
TxtCodeNode,
TxtCommentNode,
TxtDeleteNode,
TxtDocumentNode,
TxtEmphasisNode,
TxtHeaderNode,
TxtHorizontalRuleNode,
TxtHtmlNode,
TxtImageNode,
TxtLinkNode,
TxtListItemNode,
TxtListNode,
TxtParagraphNode,
TxtParentNode,
TxtStrNode,
TxtStrongNode,
TxtTableCellNode,
TxtTableNode,
TxtTableRowNode
TxtBlockQuoteNode,
TxtBreakNode,
TxtCodeBlockNode,
TxtCodeNode,
TxtCommentNode,
TxtDeleteNode,
TxtDocumentNode,
TxtEmphasisNode,
TxtHeaderNode,
TxtHorizontalRuleNode,
TxtHtmlNode,
TxtImageNode,
TxtLinkNode,
TxtListItemNode,
TxtListNode,
TxtParagraphNode,
TxtParentNode,
TxtStrNode,
TxtStrongNode,
TxtTableCellNode,
TxtTableNode,
TxtTableRowNode
} from "@textlint/ast-node-types";
import { unified } from 'unified'
import rehypeParse from 'rehype-parse'
Expand All @@ -31,151 +31,154 @@ import type { Element, RootContent } from "hast";
import { nodeTypes, tagNameToType } from "./mapping.js";

function mapNodeType(node: RootContent, parent: TraverseContext | undefined) {
if (parent) {
const parentNode = parent.parent?.node;
if (parentNode?.tagName === "script" || parentNode?.tagName === "style") {
return "CodeBlock";
}
if (parent) {
const parentNode = parent.parent?.node;
if (parentNode?.tagName === "script" || parentNode?.tagName === "style") {
return "CodeBlock";
}
if (node.type === "element") {
// @ts-expect-error: tagName is string
const mappedType = tagNameToType[node.tagName];
if (mappedType) {
// p => Paragraph...
return mappedType;
} else {
// other case, return original tagName
return node.tagName;
}
} else if (node.type === "doctype") {
return "doctype";
} else if (node.type in nodeTypes) {
// mappable node type
// text => Str
return nodeTypes[node.type];
}
if (node.type === "element") {
// @ts-expect-error: tagName is string
const mappedType = tagNameToType[node.tagName];
if (mappedType) {
// p => Paragraph...
return mappedType;
} else {
// other case, return original tagName
return node.tagName;
}
// The other node is original node.type because it is not defined in textlint's AST
// Almost rule should not handle this node.
// https://github.com/textlint/textlint-plugin-html/issues/19
return node.type;
} else if (node.type === "doctype") {
return "doctype";
} else if (node.type in nodeTypes) {
// mappable node type
// text => Str
return nodeTypes[node.type];
}
// The other node is original node.type because it is not defined in textlint's AST
// Almost rule should not handle this node.
// https://github.com/textlint/textlint-plugin-html/issues/19
return node.type;
}

export type ParseOptions = {
debug: boolean;
debug: boolean;
}

export function parse(html: string, options?: ParseOptions) {
const isDebug = process.env.DEBUG?.startsWith("textlint:html") ?? options?.debug ?? false;
const parseHtml = unified().use(rehypeParse)
const ast = parseHtml.parse(html);
const src = new StructuredSource(html);
const tr = traverse(ast);
const getNearParentWithPosition = (context: TraverseContext): Element | undefined => {
if ("position" in context.node) {
return context.node;
const isDebug = process.env.DEBUG?.startsWith("textlint:html") ?? options?.debug ?? false;
const parseHtml = unified().use(rehypeParse)
const ast = parseHtml.parse(html);
const src = new StructuredSource(html);
const tr = traverse(ast);
const getNearParentWithPosition = (context: TraverseContext): Element | undefined => {
if ("position" in context.node) {
return context.node;
}
if (context.parent) {
return getNearParentWithPosition(context.parent);
}
return;
}
tr.forEach(function (node) {
if (typeof node === "object" && !Array.isArray(node)) {
// it is not leaf node
if (!("type" in node)) {
return;
}
// backup
if (isDebug) {
Object.defineProperty(node, "_debug_type", {
value: node.type,
})
}
// avoid conflict <input type="text" />
// AST node has type and position
if (node.type) {
// case: element => Html or ...
node.type = mapNodeType(node, this.parent);
} else {
// We can not use "Html" type because some rule ignore node under the "Html" node.
// So, We use "unknown" type instead of "Html".
// https://github.com/textlint-ja/textlint-rule-no-synonyms/issues/4
node.type = "unknown" as const;
}
// map `range`, `loc` and `raw` to node
if (typeof node.position === "object") {
const position = node.position;
// TxtNode's line start with 1
// TxtNode's column start with 0
const positionCompensated = {
start: { line: position.start.line, column: position.start.column - 1 },
end: { line: position.end.line, column: position.end.column - 1 }
} as const;
const range = src.locationToRange(positionCompensated);
node.loc = positionCompensated;
node.range = range;
node.raw = html.slice(range[0], range[1]);
} else if (this.parent?.node) {
const parentNode = getNearParentWithPosition(this.parent);
if (!parentNode) {
return;
}
if (context.parent) {
return getNearParentWithPosition(context.parent);
const position = parentNode.position;
if (!position) {
return;
}
return;
}
tr.forEach(function (node) {
if (typeof node === "object" && !Array.isArray(node)) {
// it is not leaf node
if (!("type" in node)) {
return;
}
// backup
if (isDebug) {
Object.defineProperty(node, "_debug_type", {
value: node.type,
})
}
// avoid conflict <input type="text" />
// AST node has type and position
if (node.type) {
// case: element => Html or ...
node.type = mapNodeType(node, this.parent);
} else {
// We can not use "Html" type because some rule ignore node under the "Html" node.
// So, We use "unknown" type instead of "Html".
// https://github.com/textlint-ja/textlint-rule-no-synonyms/issues/4
node.type = "unknown" as const;
}
// map `range`, `loc` and `raw` to node
if (typeof node.position === "object") {
const position = node.position;
// TxtNode's line start with 1
// TxtNode's column start with 0
const positionCompensated = {
start: { line: position.start.line, column: position.start.column - 1 },
end: { line: position.end.line, column: position.end.column - 1 }
} as const;
const range = src.locationToRange(positionCompensated);
node.loc = positionCompensated;
node.range = range;
node.raw = html.slice(range[0], range[1]);
} else if (this.parent?.node) {
const parentNode = getNearParentWithPosition(this.parent);
if (!parentNode) {
return;
}
const position = parentNode.position;
if (!position) {
return;
}
// TxtNode's line start with 1
// TxtNode's column start with 0
const positionCompensated = {
start: { line: position.start.line, column: position.start.column - 1 },
end: { line: position.end.line, column: position.end.column - 1 }
} as const;
const range = src.locationToRange(positionCompensated);
node.loc = positionCompensated;
node.range = range;
node.raw = html.slice(range[0], range[1]);
}
// === properties ===
// map `url` to Link node
const txtNode = node as TxtBlockQuoteNode |
TxtBreakNode |
TxtCodeBlockNode |
TxtCommentNode |
TxtDeleteNode |
TxtDocumentNode |
TxtEmphasisNode |
TxtHeaderNode |
TxtHorizontalRuleNode |
TxtHtmlNode |
TxtImageNode |
TxtLinkNode |
TxtListItemNode |
TxtListNode |
TxtParagraphNode |
TxtCodeNode |
TxtStrNode |
TxtStrongNode |
TxtTableNode |
TxtTableRowNode |
TxtTableCellNode;
if (txtNode.type === "Link") {
if (txtNode.properties.href !== undefined) txtNode.url = txtNode.properties.href;
if (txtNode.properties.title !== undefined) txtNode.title = txtNode.properties.title;
} else if (txtNode.type === "Image") {
if (txtNode.properties.alt !== undefined) txtNode.alt = txtNode.properties.alt;
if (txtNode.properties.title !== undefined) txtNode.title = txtNode.properties.title;
if (txtNode.properties.src !== undefined) txtNode.url = txtNode.properties.src;
} else if (txtNode.type === "Header") {
const depth = Number(txtNode.tagName.slice(1)) as TxtHeaderNode["depth"];
if (depth > 0 && depth < 7) txtNode.depth = depth;
} else if (txtNode.type === "List") {
if (txtNode.tagName === "ul") {
txtNode.ordered = false;
} else if (txtNode.tagName === "ol") {
txtNode.ordered = true;
}
}
// TxtNode's line start with 1
// TxtNode's column start with 0
const positionCompensated = {
start: { line: position.start.line, column: position.start.column - 1 },
end: { line: position.end.line, column: position.end.column - 1 }
} as const;
const range = src.locationToRange(positionCompensated);
node.loc = positionCompensated;
node.range = range;
node.raw = html.slice(range[0], range[1]);
}
// === properties ===
// map `url` to Link node
const txtNode = node as (TxtBlockQuoteNode |
TxtBreakNode |
TxtCodeBlockNode |
TxtCommentNode |
TxtDeleteNode |
TxtDocumentNode |
TxtEmphasisNode |
TxtHeaderNode |
TxtHorizontalRuleNode |
TxtHtmlNode |
TxtImageNode |
TxtLinkNode |
TxtListItemNode |
TxtListNode |
TxtParagraphNode |
TxtCodeNode |
TxtStrNode |
TxtStrongNode |
TxtTableNode |
TxtTableRowNode |
TxtTableCellNode) & {
properties: Record<string, string | undefined>;
tagName: string;
}
if (txtNode.type === "Link") {
if (txtNode.properties.href !== undefined) txtNode.url = txtNode.properties.href;
if (txtNode.properties.title !== undefined) txtNode.title = txtNode.properties.title;
} else if (txtNode.type === "Image") {
if (txtNode.properties.alt !== undefined) txtNode.alt = txtNode.properties.alt;
if (txtNode.properties.title !== undefined) txtNode.title = txtNode.properties.title;
if (txtNode.properties.src !== undefined) txtNode.url = txtNode.properties.src;
} else if (txtNode.type === "Header") {
const depth = Number(txtNode.tagName.slice(1)) as TxtHeaderNode["depth"];
if (depth > 0 && depth < 7) txtNode.depth = depth;
} else if (txtNode.type === "List") {
if (txtNode.tagName === "ul") {
txtNode.ordered = false;
} else if (txtNode.tagName === "ol") {
txtNode.ordered = true;
}
});
return ast as any as TxtParentNode;
}
}
});
return ast as any as TxtParentNode;
}
Loading

0 comments on commit 95a6606

Please sign in to comment.