md2hwp
Version:
Convert Markdown to HWP (Hangul Word Processor) format
242 lines (241 loc) • 8.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MarkdownParser = void 0;
const marked_1 = require("marked");
class MarkdownParser {
parse(markdown) {
const tokens = marked_1.marked.lexer(markdown);
return this.tokensToContent(tokens);
}
tokensToContent(tokens) {
const contents = [];
for (const token of tokens) {
const content = this.tokenToContent(token);
if (content) {
if (Array.isArray(content)) {
contents.push(...content);
}
else {
contents.push(content);
}
}
}
return contents;
}
tokenToContent(token) {
switch (token.type) {
case 'heading':
return this.parseHeading(token);
case 'paragraph':
return this.parseParagraph(token);
case 'list':
return this.parseList(token);
case 'table':
return this.parseTable(token);
case 'code':
return this.parseCode(token);
case 'space':
return null;
default:
if ('text' in token) {
return {
type: 'paragraph',
content: token.text
};
}
return null;
}
}
parseHeading(token) {
return {
type: 'heading',
level: token.depth,
content: this.extractText(token.text)
};
}
parseParagraph(token) {
const children = this.parseInlineTokens(token.text);
if (children.length === 1 && !children[0].style) {
return {
type: 'paragraph',
content: children[0].content
};
}
return {
type: 'paragraph',
children
};
}
parseInlineTokens(text) {
const tokens = marked_1.marked.lexer(text, { breaks: true });
const inlineTokens = [];
for (const token of tokens) {
if ('tokens' in token && Array.isArray(token.tokens)) {
inlineTokens.push(...token.tokens);
}
}
if (inlineTokens.length === 0) {
return [{
type: 'paragraph',
content: text
}];
}
const contents = [];
for (const token of inlineTokens) {
if (token.type === 'text') {
contents.push({
type: 'paragraph',
content: token.text
});
}
else if (token.type === 'strong') {
contents.push({
type: 'paragraph',
content: this.extractText(token.text),
style: { bold: true }
});
}
else if (token.type === 'em') {
contents.push({
type: 'paragraph',
content: this.extractText(token.text),
style: { italic: true }
});
}
else if (token.type === 'codespan') {
contents.push({
type: 'paragraph',
content: token.text,
style: {}
});
}
else if (token.type === 'link') {
contents.push({
type: 'paragraph',
content: `${token.text} (${token.href})`
});
}
else if (token.type === 'image') {
contents.push({
type: 'image',
src: token.href,
alt: token.text
});
}
}
return contents.length > 0 ? contents : [{
type: 'paragraph',
content: text
}];
}
parseList(token) {
const children = [];
for (const item of token.items) {
// Check if item has nested tokens (including nested lists)
if ('tokens' in item && Array.isArray(item.tokens) && item.tokens.length > 0) {
// Parse each token in the item (text, nested lists, etc.)
for (const subToken of item.tokens) {
// For text tokens within lists, parse inline elements
if (subToken.type === 'text' && 'text' in subToken) {
const inlineElements = this.parseInlineTokens(subToken.text);
if (inlineElements.length > 1) {
children.push({
type: 'paragraph',
children: inlineElements
});
}
else if (inlineElements.length === 1) {
children.push(inlineElements[0]);
}
}
else {
// For other tokens (lists, etc.), parse normally
const parsed = this.tokenToContent(subToken);
if (parsed) {
if (Array.isArray(parsed)) {
children.push(...parsed);
}
else {
children.push(parsed);
}
}
}
}
}
else {
// Fallback: Parse inline elements (bold, italic, etc.) within list items
const inlineElements = this.parseInlineTokens(item.text);
// Handle list items with mixed content (bold + normal text)
if (inlineElements.length > 1) {
// Create a paragraph with children for mixed content
children.push({
type: 'paragraph',
children: inlineElements
});
}
else if (inlineElements.length === 1 && !inlineElements[0].style) {
// Single plain text element - add as simple paragraph
children.push({
type: 'paragraph',
content: inlineElements[0].content
});
}
else if (inlineElements.length === 1) {
// Single styled element - keep children structure
children.push({
type: 'paragraph',
children: inlineElements
});
}
else {
// Fallback to plain text
const text = this.extractText(item.text);
children.push({
type: 'paragraph',
content: text
});
}
}
}
return {
type: 'list',
children
};
}
parseTable(token) {
const rows = [];
if (token.header && token.header.length > 0) {
const headerCells = token.header.map(cell => ({
content: this.extractText(cell.text)
}));
rows.push({ cells: headerCells });
}
for (const row of token.rows) {
const cells = row.map(cell => ({
content: this.extractText(cell.text)
}));
rows.push({ cells });
}
return {
type: 'table',
rows
};
}
parseCode(token) {
return {
type: 'code',
content: token.text,
language: token.lang || undefined
};
}
extractText(input) {
if (typeof input === 'string') {
return input;
}
if (input && typeof input === 'object' && 'text' in input) {
return this.extractText(input.text);
}
return String(input);
}
}
exports.MarkdownParser = MarkdownParser;