UNPKG

@awesome-fe/translate

Version:
324 lines 13.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.markdown = void 0; const unified = require("unified"); const custom_parser_plugin_1 = require("./remark-plugins/custom-parser-plugin"); const custom_compiler_plugin_1 = require("./remark-plugins/custom-compiler-plugin"); const mast_to_hast_handlers_1 = require("./remark-plugins/mast-to-hast-handlers"); const hast_to_mast_handlers_1 = require("./remark-plugins/hast-to-mast-handlers"); const remarkParse = require("remark-parse"); const remarkStringify = require("remark-stringify"); const rehypeParse = require("rehype-parse"); const rehypeStringify = require("rehype-stringify"); const remarkRehype = require("remark-rehype"); const rehypeRemark = require("rehype-remark"); const frontmatter = require("remark-frontmatter"); const stringWidth = require("string-width"); const common_1 = require("../common"); const js_yaml_1 = require("js-yaml"); const lodash_1 = require("lodash"); var markdown; (function (markdown_1) { function isLiteral(node) { return [ 'html', 'code', 'yaml', 'text', 'inlineCode', ].includes(node.type); } markdown_1.isLiteral = isLiteral; function isParent(node) { return [ 'root', 'paragraph', 'heading', 'blockquote', 'list', 'listItem', 'table', 'tableRow', 'tableCell', 'emphasis', 'strong', 'delete', 'footnote', ].includes(node.type); } markdown_1.isParent = isParent; function isTranslatableUnit(node) { return node.type === 'paragraph' || node.type === 'heading'; } markdown_1.isTranslatableUnit = isTranslatableUnit; function isLinkReference(node) { return node.type === 'linkReference'; } markdown_1.isLinkReference = isLinkReference; function isLink(node) { return node.type === 'link'; } markdown_1.isLink = isLink; function isTableRow(node) { return node.type === 'tableRow'; } markdown_1.isTableRow = isTableRow; function isTableCell(node) { return node.type === 'tableCell'; } markdown_1.isTableCell = isTableCell; function isTable(node) { return node.type === 'table'; } markdown_1.isTable = isTable; function isTableFamily(node) { return isTable(node) || isTableRow(node) || isTableCell(node); } markdown_1.isTableFamily = isTableFamily; function isYaml(node) { return node.type === 'yaml'; } markdown_1.isYaml = isYaml; function isListItem(node) { return node.type === 'listItem'; } markdown_1.isListItem = isListItem; function parse(markdown) { return unified().use(remarkParse) .use(frontmatter) .use(custom_parser_plugin_1.customParser) .parse(markdown); } markdown_1.parse = parse; function stringify(tree) { if (!tree) { return ''; } tree.children = tree.children ?? []; return unified().use(remarkStringify, stringifyOptions) .use(frontmatter) .use(custom_compiler_plugin_1.customCompiler) .stringify(tree) .replace(/ /g, '&nbsp;') .replace(/[ \t]+$/g, '') // 无法完美处理 list-item-visitor 出现两个空行或者少一个空行的问题,因此只好在这里做后期替换 .replace(/\n\n\n+/g, '\n\n') .trim(); } markdown_1.stringify = stringify; function toHtml(textOrAst) { let text; if (typeof textOrAst === 'string') { text = textOrAst; } else { text = stringify(textOrAst).replace(/^<p>([\s\S]*?)<\/p>$/gi, '$1'); } return unified().use(remarkParse) .use(frontmatter) .use(custom_parser_plugin_1.customParser) .use(remarkRehype, { handlers: mast_to_hast_handlers_1.mastToHastHandlers }) .use(rehypeStringify, { closeSelfClosing: true }) .processSync(text).contents.toString(); } markdown_1.toHtml = toHtml; function fromHtml(html) { return parse(mdFromHtml(html)); } markdown_1.fromHtml = fromHtml; function mdToHtml(md) { return toHtml(md); } markdown_1.mdToHtml = mdToHtml; function mdFromHtml(html) { if (!html) { return html; } return unified().use(rehypeParse) .use(rehypeRemark, { handlers: hast_to_mast_handlers_1.hastToMastHandlers }) .use(remarkStringify, stringifyOptions) .use(custom_compiler_plugin_1.customCompiler) .processSync(html).contents.toString().trim(); } markdown_1.mdFromHtml = mdFromHtml; function normalize(text) { return stringify(parse(text)); } markdown_1.normalize = normalize; function nodeContainsChinese(node) { if (!node) { return false; } if (isLiteral(node)) { return (0, common_1.containsChinese)(node.value); } else if (isLink(node)) { return (0, common_1.containsChinese)(node.title) || node.children.some(it => nodeContainsChinese(it)); } else if (isLinkReference(node)) { return (0, common_1.containsChinese)(node.label) || node.children.some(it => nodeContainsChinese(it)); } else if (['htmlRaw', 'anchor'].includes(node.type)) { return false; } else if (isParent(node)) { return node.children.some(it => nodeContainsChinese(it)); } else { return (0, common_1.containsChinese)(contentOf(node)); } } markdown_1.nodeContainsChinese = nodeContainsChinese; const stringifyOptions = { emphasis: '*', listItemIndent: 1, incrementListMarker: false, stringLength: stringWidth, paddedTable: true, fences: true, entities: false, }; function visit(node, parent, visitor) { if (isYaml(node)) { return handleFrontMatter(node, visitor); } else if (isTable(node)) { return handleTable(node, visitor); } else if (isTranslatableUnit(node) && !nodeContainsChinese(node)) { return handleParagraphAndHeadings(node, parent, visitor); } else if (isParent(node)) { return Promise.all(node.children.map(it => visit(it, node, visitor))).then(() => node); } } markdown_1.visit = visit; function handleFrontMatter(yaml, visitor) { const frontMatter = (0, js_yaml_1.safeLoad)(yaml.value) || {}; const entries = Object.entries(frontMatter); const tasks = entries.map(([key, value]) => { if (key.endsWith('$$origin')) { // 忽略保存的原文 return; } if ((0, common_1.containsChinese)(value)) { return visitor(frontMatter[`${key}$$origin`], value).then((result) => { if (result && (0, common_1.containsChinese)(result) && result !== value) { frontMatter[key] = result; } }); } else { return visitor(value, undefined).then((result) => { if (result && (0, common_1.containsChinese)(result)) { frontMatter[key] = result; frontMatter[`${key}$$origin`] = value; } }); } }); return Promise.all(tasks.filter(it => !!it)).then(() => { yaml.value = (0, js_yaml_1.safeDump)(frontMatter); return yaml; }); } function handleTable(table, visitor) { const tasks = []; // 倒序循环,以免插入的新节点影响循环本身 for (let rowIndex = table.children.length - 1; rowIndex >= 0; --rowIndex) { const originalRow = table.children[rowIndex]; const translationRow = table.children[rowIndex + 1]; // 原文和译文按行进行对照,只需要处理原文行,译文行是被动处理的 if (!nodeContainsChinese(originalRow)) { // 有译文行时要提取译文行,没有译文行时要插入译文行 if (nodeContainsChinese(translationRow)) { console.assert(originalRow.children.length === translationRow.children.length); for (let colIndex = 0; colIndex < originalRow.children.length; colIndex++) { const originalCell = originalRow.children[colIndex]; const translationCell = translationRow.children[colIndex]; const originalText = stringify(originalCell); const translationText = stringify(translationCell); tasks.push(visitor(originalText, translationText).then((result) => { if (result && (0, common_1.containsChinese)(result) && result !== translationText) { translationCell.children = parseCellContent(result); } })); } } else { let translationRow; for (let colIndex = 0; colIndex < originalRow.children.length; colIndex++) { const originalCell = originalRow.children[colIndex]; const originalText = stringify(originalCell); tasks.push(visitor(originalText, undefined).then((result) => { if (result && (0, common_1.containsChinese)(result)) { if (!translationRow) { translationRow = (0, lodash_1.cloneDeep)(originalRow); table.children.splice(rowIndex + 1, 0, translationRow); } const translationCell = translationRow.children[colIndex]; translationCell.children = parseCellContent(result); } })); } } } } return Promise.all(tasks).then(() => table); function parseCellContent(result) { const paragraph = parse(result).children[0]; return paragraph.children; } } function handleParagraphAndHeadings(originalNode, parent, visitor) { // 我们要处理的所有节点都必须是 Parent,因为它至少也会包含一个 text 子节点 const index = parent.children.indexOf(originalNode); const translationNode = parent.children[index + 1]; const originalText = contentOf(originalNode); if (translationNode && originalNode.type === translationNode.type && nodeContainsChinese(translationNode)) { const translationText = contentOf(translationNode); return visitor(originalText, translationText).then((result) => { if (!result || !(0, common_1.containsChinese)(result) || result === translationText) { return translationNode; } applyTranslation(translationNode, result); return translationNode; }); } else { return visitor(originalText, undefined).then((result) => { if (!result || !(0, common_1.containsChinese)(result)) { return originalNode; } const translationNode = createAndInsertTranslation(parent, originalNode); applyTranslation(translationNode, result); return translationNode; }); } function applyTranslation(translationNode, result) { if (isListItem(parent)) { // 如果是 listItem 被翻译了,则需要扩展成阔表模式,以便容纳两个段落 parent.spread = true; } const translation = parse(result).children[0]; Object.assign(translationNode, translation, { type: originalNode.type }); } function createAndInsertTranslation(parent, original) { const node = (0, lodash_1.cloneDeep)(original); const index = parent.children.indexOf(original); parent.children.splice(index + 1, 0, node); return node; } } function contentOf(node) { if (!node) { return; } const cloned = (0, lodash_1.cloneDeep)(node); if (cloned.type !== 'paragraph' && cloned.type !== 'heading') { cloned.type = 'paragraph'; } return stringify(cloned); } })(markdown = exports.markdown || (exports.markdown = {})); //# sourceMappingURL=markdown.js.map