diff --git a/__tests__/parsers/utils.test.js b/__tests__/parsers/utils.test.js deleted file mode 100644 index 44e4284e9..000000000 --- a/__tests__/parsers/utils.test.js +++ /dev/null @@ -1,62 +0,0 @@ -import { insertBlockTokenizerBefore, insertInlineTokenizerBefore } from '../../processor/parse/utils'; - -let self; - -beforeEach(() => { - self = { - Parser: { - prototype: { - blockTokenizers: {}, - blockMethods: ['one', 'two'], - inlineTokenizers: {}, - inlineMethods: ['one', 'two'], - }, - }, - }; -}); - -describe('insertBlockTokenizerBefore', () => { - it('correctly splices the tokenizer into place', () => { - insertBlockTokenizerBefore.call(self, { - name: 'test', - before: 'one', - tokenizer: () => {}, - }); - - expect(self.Parser.prototype.blockMethods).toStrictEqual(['test', 'one', 'two']); - expect(self.Parser.prototype.blockTokenizers).toHaveProperty('test'); - }); - - it('throws an error if the method does not exist', () => { - expect(() => - insertBlockTokenizerBefore.call(self, { - name: 'test', - before: 'oh no', - tokenizer: () => {}, - }) - ).toThrow("The 'oh no' tokenizer does not exist!"); - }); -}); - -describe('insertInlineTokenizerBefore', () => { - it('correctly splices the tokenizer into place', () => { - insertInlineTokenizerBefore.call(self, { - name: 'test', - before: 'one', - tokenizer: () => {}, - }); - - expect(self.Parser.prototype.inlineMethods).toStrictEqual(['test', 'one', 'two']); - expect(self.Parser.prototype.inlineTokenizers).toHaveProperty('test'); - }); - - it('throws an error if the method does not exist', () => { - expect(() => - insertInlineTokenizerBefore.call(self, { - name: 'test', - before: 'oh no', - tokenizer: () => {}, - }) - ).toThrow("The 'oh no' tokenizer does not exist!"); - }); -}); diff --git a/lib/mdx.ts b/lib/mdx.ts index 6245e89f4..403f7331c 100644 --- a/lib/mdx.ts +++ b/lib/mdx.ts @@ -6,12 +6,14 @@ import remarkStringify from 'remark-stringify'; import compilers from '../processor/compile'; import readmeToMdx from '../processor/transform/readme-to-mdx'; +import divTransformer from '../processor/transform'; export const mdx = (tree: any, { hast = false } = {}) => { const processor = unified() .use(hast ? rehypeRemark : undefined) .use(remarkMdx) .use(remarkGfm) + .use(divTransformer) .use(readmeToMdx) .use(remarkStringify) .use(compilers); diff --git a/processor/compile/break.js b/processor/compile/break.js deleted file mode 100644 index fb44beca3..000000000 --- a/processor/compile/break.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = function BreakCompiler() { - const { Compiler } = this; - const { visitors } = Compiler.prototype; - - // @note: We could save this as just '\n' when `correctnewlines: false`, but - // this is more portable. - visitors.break = function compile() { - return ' \n'; - }; -}; diff --git a/processor/compile/compatibility.ts b/processor/compile/compatibility.ts index a64c3258c..8da6c98f7 100644 --- a/processor/compile/compatibility.ts +++ b/processor/compile/compatibility.ts @@ -4,7 +4,7 @@ import { toMarkdown } from 'mdast-util-to-markdown'; import { toXast } from 'hast-util-to-xast'; import { toXml } from 'xast-util-to-xml'; import { NodeTypes } from '../../enums'; -import { formatHProps, formatProps } from '../utils'; +import { formatProps } from '../utils'; type CompatNodes = | { type: NodeTypes.glossary; data: { hProperties: { term: string } } } @@ -13,6 +13,8 @@ type CompatNodes = | { type: 'embed'; data: { hProperties: { [key: string]: string } } } | { type: 'escape'; value: string } | { type: 'figure'; children: [Image, { type: 'figcaption'; children: [{ type: 'text'; value: string }] }] } + | { type: 'i'; data: { hProperties: { className: string[] } } } + | { type: 'yaml'; value: string } | Html; /* @@ -80,6 +82,10 @@ const compatibility = (node: CompatNodes) => { return figureToImageBlock(node); case 'embed': return embedToEmbedBlock(node); + case 'i': + return `:${node.data.hProperties.className[1]}:`; + case 'yaml': + return `---\n${node.value}\n---`; default: throw new Error('Unhandled node type!'); } diff --git a/processor/compile/div.js b/processor/compile/div.js deleted file mode 100644 index 845ff1f53..000000000 --- a/processor/compile/div.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = function DivCompiler() { - const { Compiler } = this; - const { visitors } = Compiler.prototype; - - visitors.div = function compile(node) { - const data = node.data.hProperties; - - return ` -[block:${node.data.hName}] -${JSON.stringify(data, null, 2)} -[/block] -`; - }; -}; diff --git a/processor/compile/escape.js b/processor/compile/escape.js deleted file mode 100644 index acbdcce19..000000000 --- a/processor/compile/escape.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = function EscapeCompiler() { - const { Compiler } = this; - const { visitors } = Compiler.prototype; - - visitors.escape = function compile(node) { - return `\\${node.value}`; - }; -}; diff --git a/processor/compile/figure.js b/processor/compile/figure.js deleted file mode 100644 index 1b9f33311..000000000 --- a/processor/compile/figure.js +++ /dev/null @@ -1,40 +0,0 @@ -const { imgSizeByWidth } = require('../parse/magic-block-parser'); - -const compileImage = image => { - const { align, className, width } = image.data.hProperties || {}; - const img = { - image: [image.url, image.title, image.alt], - ...(align && { align }), - ...(width && { sizing: imgSizeByWidth[width] }), - ...(className === 'border' && { border: true }), - }; - - return img; -}; - -module.exports = function FigureCompiler() { - const { Compiler } = this; - const { visitors } = Compiler.prototype; - - visitors.figure = function figureCompiler(node) { - let image; - let caption; - - if (node.children) { - [image, caption] = node.children; - } else { - image = node; - } - - const img = compileImage(image); - if (caption) img.caption = this.block(caption); - - const block = { - images: [img], - }; - - return `[block:image] -${JSON.stringify(block, null, 2)} -[/block]`; - }; -}; diff --git a/processor/compile/glossary.js b/processor/compile/glossary.js deleted file mode 100644 index 415ad60a9..000000000 --- a/processor/compile/glossary.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = function RdmeGlossaryCompiler() { - const { Compiler } = this; - const { visitors } = Compiler.prototype; - - visitors['readme-glossary-item'] = node => `<>`; -}; diff --git a/processor/compile/i.js b/processor/compile/i.js deleted file mode 100644 index 723c89189..000000000 --- a/processor/compile/i.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = function FaEmojiCompiler() { - const { Compiler } = this; - const { visitors } = Compiler.prototype; - - visitors.i = function compile(node) { - return `:${node.data.hProperties.className[1]}:`; - }; -}; diff --git a/processor/compile/index.ts b/processor/compile/index.ts index 1c2005196..64d5c8f60 100644 --- a/processor/compile/index.ts +++ b/processor/compile/index.ts @@ -27,6 +27,8 @@ function compilers() { escape: compatibility, figure: compatibility, html: compatibility, + i: compatibility, + yaml: compatibility, }; toMarkdownExtensions.push({ extensions: [{ handlers }] }); diff --git a/processor/compile/magic-block.js b/processor/compile/magic-block.js deleted file mode 100644 index 4dfed6523..000000000 --- a/processor/compile/magic-block.js +++ /dev/null @@ -1,7 +0,0 @@ -const magicBlock = (type, data, parent) => { - return parent.type === 'root' - ? `[block:${type}]\n${JSON.stringify(data, null, 2)}\n[/block]\n` - : `[block:${type}]${JSON.stringify(data)}[/block]`; -}; - -export default magicBlock; diff --git a/processor/compile/pin.js b/processor/compile/pin.js deleted file mode 100644 index fd9aed084..000000000 --- a/processor/compile/pin.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = function PinCompiler() { - const { Compiler } = this; - const { visitors } = Compiler.prototype; - function compiler(node) { - return ['
', this.block(node), '
'].join('\n\n'); - } - visitors['rdme-pin'] = compiler; -}; diff --git a/processor/compile/reusable-content.js b/processor/compile/reusable-content.js deleted file mode 100644 index bdc34ef95..000000000 --- a/processor/compile/reusable-content.js +++ /dev/null @@ -1,11 +0,0 @@ -import { type } from '../transform/reusable-content'; - -export default function ReusableContentCompiler() { - const { writeTags = true } = this.data('reusableContent') || {}; - const { Compiler } = this; - const { visitors } = Compiler.prototype; - - visitors[type] = function (node) { - return writeTags ? `<${node.tag} />` : this.block(node); - }; -} diff --git a/processor/compile/table-head.js b/processor/compile/table-head.js deleted file mode 100644 index c88638806..000000000 --- a/processor/compile/table-head.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = function TableHeadCompiler() { - const { Compiler } = this; - const { visitors } = Compiler.prototype; - visitors.tableHead = function compile(node) { - return visitors.tableCell.call(this, node); - }; -}; diff --git a/processor/compile/var.js b/processor/compile/var.js deleted file mode 100644 index 74f94d721..000000000 --- a/processor/compile/var.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = function RdmeVarCompiler() { - const { Compiler } = this; - const { visitors } = Compiler.prototype; - - visitors['readme-variable'] = node => `<<${node.text}>>`; -}; diff --git a/processor/parse/compact-headings.js b/processor/parse/compact-headings.js deleted file mode 100644 index c5a1e27ff..000000000 --- a/processor/parse/compact-headings.js +++ /dev/null @@ -1,34 +0,0 @@ -const rgx = /^(#{1,6})(?!(?:#|\s))([^\n]+)\n/; - -function tokenizer(eat, value) { - if (!rgx.test(value)) return true; - - const [match, hash, text] = rgx.exec(value); - - const now = eat.now(); - now.column += match.length; - now.offset += match.length; - - return eat(match)({ - type: 'heading', - depth: hash.length, - children: this.tokenizeInline(text, now), - }); -} - -function parser() { - const { Parser } = this; - const tokenizers = Parser.prototype.blockTokenizers; - const methods = Parser.prototype.blockMethods; - - tokenizers.compactHeading = tokenizer; - methods.splice(methods.indexOf('newline'), 0, 'compactHeading'); -} - -module.exports = parser; - -module.exports.sanitize = sanitizeSchema => { - const tags = sanitizeSchema.tagNames; - tags.push('compactHeading'); - return parser; -}; diff --git a/processor/parse/escape.js b/processor/parse/escape.js deleted file mode 100644 index 9f1d298e3..000000000 --- a/processor/parse/escape.js +++ /dev/null @@ -1,46 +0,0 @@ -// @note: this is copied from https://github.com/remarkjs/remark/blob/remark-parse%407.0.2/packages/remark-parse/lib/tokenize/escape.js - -const lineFeed = '\n'; -const backslash = '\\'; - -// eslint-disable-next-line consistent-return -function escape(eat, value, silent) { - const self = this; - let character; - let node; - - if (value.charAt(0) === backslash) { - character = value.charAt(1); - - if (self.escape.indexOf(character) !== -1) { - if (silent) { - return true; - } - - if (character === lineFeed) { - node = { type: 'break' }; - } else { - node = { type: 'escape', value: character }; - } - - return eat(backslash + character)(node); - } - } -} - -function locate(value, fromIndex) { - return value.indexOf('\\', fromIndex); -} - -escape.locator = locate; - -function parser() { - const { Parser } = this; - const { inlineTokenizers } = Parser.prototype; - - inlineTokenizers.escape = escape; -} - -parser.sanitize = () => parser; - -export default parser; diff --git a/processor/parse/flavored/callout.js b/processor/parse/flavored/callout.js deleted file mode 100644 index c6b891745..000000000 --- a/processor/parse/flavored/callout.js +++ /dev/null @@ -1,194 +0,0 @@ -/* eslint-disable no-plusplus */ -/* eslint-disable consistent-return */ -import emojiRegex from 'emoji-regex'; -import interrupt from 'remark-parse/lib/util/interrupt'; -import trim from 'trim'; - -const themes = { - '\uD83D\uDCD8': 'info', - '\uD83D\uDEA7': 'warn', - '\u26A0\uFE0F': 'warn', - '\uD83D\uDC4D': 'okay', - '\u2705': 'okay', - '\u2757\uFE0F': 'error', - '\u2757': 'error', - '\uD83D\uDED1': 'error', - '\u2049\uFE0F': 'error', - '\u203C\uFE0F': 'error', - '\u2139\uFE0F': 'info', - '\u26A0': 'warn', -}; - -export const icons = Object.entries(themes).reduce((acc, [icon, theme]) => { - if (!acc[theme]) acc[theme] = []; - acc[theme].push(icon); - - return acc; -}, {}); - -const lineFeed = '\n'; -const tab = '\t'; -const space = ' '; -const greaterThan = '>'; -const regex = `^(${emojiRegex().source})(\\s+|$)`; - -// @note: Copied directly from remark-parse, but it's been updated to match our -// style conventions and to parse Callouts. -function blockquoteReadme(eat, value, silent) { - const self = this; - const offsets = self.offset; - const tokenizers = self.blockTokenizers; - const interruptors = self.interruptBlockquote; - const now = eat.now(); - let currentLine = now.line; - let length = value.length; - const values = []; - const contents = []; - const indents = []; - let index = 0; - let character; - let rest; - let nextIndex; - let content; - let line; - let startIndex; - let prefixed; - - while (index < length) { - character = value.charAt(index); - - if (character !== space && character !== tab) { - break; - } - - index++; - } - - if (value.charAt(index) !== greaterThan) { - return; - } - - if (silent) { - return true; - } - - index = 0; - - while (index < length) { - nextIndex = value.indexOf(lineFeed, index); - startIndex = index; - prefixed = false; - - if (nextIndex === -1) { - nextIndex = length; - } - - while (index < length) { - character = value.charAt(index); - - if (character !== space && character !== tab) { - break; - } - - index++; - } - - if (value.charAt(index) === greaterThan) { - index++; - prefixed = true; - - if (value.charAt(index) === space) { - index++; - } - } else { - index = startIndex; - } - - content = value.slice(index, nextIndex); - - if (!prefixed && !trim(content)) { - index = startIndex; - break; - } - - if (!prefixed) { - rest = value.slice(index); - - // check if the following code contains a possible block. - if (interrupt(interruptors, tokenizers, self, [eat, rest, true])) { - break; - } - } - - line = startIndex === index ? content : value.slice(startIndex, nextIndex); - - indents.push(index - startIndex); - values.push(line); - contents.push(content); - - index = nextIndex + 1; - } - - index = -1; - length = indents.length; - const add = eat(values.join(lineFeed)); - - while (++index < length) { - offsets[currentLine] = (offsets[currentLine] || 0) + indents[index]; - currentLine++; - } - - const [match, icon] = contents[0].match(regex) || []; - - if (icon) { - const title = trim(contents[0].slice(match.length)); - const body = trim(contents.slice(1).join(lineFeed)); - - const data = { - hName: 'rdme-callout', - hProperties: { - title, - value: body, - icon, - theme: themes[icon] || 'default', - }, - }; - - const exit = self.enterBlock(); - const children = [...self.tokenizeBlock(title, now), ...self.tokenizeBlock(body, now)]; - exit(); - - return add({ - type: 'rdme-callout', - children, - data, - }); - } - - const exit = self.enterBlock(); - const children = self.tokenizeBlock(contents.join(lineFeed), now); - exit(); - - return add({ type: 'blockquote', children }); -} - -function parser() { - const { Parser } = this; - const tokenizers = Parser.prototype.blockTokenizers; - - tokenizers.blockquote = blockquoteReadme; -} - -export default parser; - -export function sanitize(sanitizeSchema) { - const tags = sanitizeSchema.tagNames; - const attr = sanitizeSchema.attributes; - - tags.push('rdme-callout'); - attr['rdme-callout'] = ['icon', 'theme', 'title', 'value']; - - return parser; -} - -parser.sanitize = sanitize; diff --git a/processor/parse/html-block.js b/processor/parse/html-block.js deleted file mode 100644 index 3ee7e1197..000000000 --- a/processor/parse/html-block.js +++ /dev/null @@ -1,21 +0,0 @@ -const blockHtml = htmlTokenizer => { - return function tokenizer(...args) { - const node = htmlTokenizer.call(this, ...args); - - if (typeof node === 'object') { - node.block = true; - } - - return node; - }; -}; - -function parser() { - const { Parser } = this; - const tokenizers = Parser.prototype.blockTokenizers; - const { html } = tokenizers; - - tokenizers.html = blockHtml(html); -} - -export default parser; diff --git a/processor/parse/index.js b/processor/parse/index.js deleted file mode 100644 index 10205e1b3..000000000 --- a/processor/parse/index.js +++ /dev/null @@ -1,11 +0,0 @@ -export { default as magicBlockParser } from './magic-block-parser'; - -export { default as flavorCodeTabs } from './flavored/code-tabs'; -export { default as flavorCallout } from './flavored/callout'; -export { default as flavorEmbed } from './flavored/embed'; - -export { default as escape } from './escape'; -export { default as compactHeadings } from './compact-headings'; -export { default as variableParser } from './variable-parser'; -export { default as gemojiParser } from './gemoji-parser'; -export { default as htmlBlockParser } from './html-block'; diff --git a/processor/parse/magic-block-parser.js b/processor/parse/magic-block-parser.js deleted file mode 100644 index a71b236fa..000000000 --- a/processor/parse/magic-block-parser.js +++ /dev/null @@ -1,309 +0,0 @@ -/* eslint-disable consistent-return */ -const RGXP = /^\[block:(.*)\]([^]+?)\[\/block\]/; - -const WrapPinnedBlocks = (node, json) => { - if (!json.sidebar) return node; - return { - children: [node], - type: 'rdme-pin', - data: { - hName: 'rdme-pin', - className: 'pin', - }, - }; -}; - -const imgSizeValues = { - full: '100%', - original: 'auto', -}; - -const imgWidthBySize = new Proxy(imgSizeValues, { - get: (widths, size) => (size?.match(/^\d+$/) ? `${size}%` : size in widths ? widths[size] : size), -}); - -const imgSizeByWidth = new Proxy(new Map(Array.from(imgSizeValues).reverse()), { - get: (sizes, width) => { - const match = width?.match(/^(\d+)%$/); - return match ? match[1] : width in sizes ? sizes[width] : width; - }, -}); - -function tokenize({ compatibilityMode, safeMode, alwaysThrow }) { - return function (eat, value) { - let [match, type, json] = RGXP.exec(value) || []; - - if (!type) return; - - match = match.trim(); - type = type.trim(); - - try { - json = JSON.parse(json); - } catch (err) { - json = {}; - // eslint-disable-next-line no-console - console.error('Invalid Magic Block JSON:', err); - - if (alwaysThrow) { - throw new Error('Invalid Magic Block JSON'); - } - } - - if (Object.keys(json).length < 1) return eat(match); - - switch (type) { - case 'code': { - const children = json.codes.map(obj => ({ - type: 'code', - value: obj.code.trim(), - meta: obj.name || null, - lang: obj.language, - className: 'tab-panel', - data: { - hName: 'code', - hProperties: { - meta: obj.name || null, - lang: obj.language, - }, - }, - })); - if (children.length === 1) { - if (!children[0].value) return eat(match); // skip empty code tabs - if (children[0].name) return eat(match)(WrapPinnedBlocks(children[0], json)); - } - return eat(match)( - WrapPinnedBlocks( - { - children, - className: 'tabs', - data: { hName: 'code-tabs' }, - type: 'code-tabs', - }, - json - ) - ); - } - case 'api-header': { - const depth = json.level || (compatibilityMode ? 1 : 2); - return eat(match)( - WrapPinnedBlocks( - { - type: 'heading', - depth, - children: 'title' in json ? this.tokenizeInline(json.title, eat.now()) : [], - }, - json - ) - ); - } - case 'image': { - const imgs = json.images - .map(img => { - if (!('image' in img)) return null; - const [url, title, alt] = img.image; - - const block = { - type: 'image', - url, - title, - alt: alt || ('caption' in img ? img.caption : ''), - data: { - hProperties: { - ...(img.align && { align: img.align }), - className: img.border ? 'border' : '', - ...(img.sizing && { width: imgWidthBySize[img.sizing] }), - }, - }, - }; - - if (!img.caption) return block; - return { - type: 'figure', - url, - data: { hName: 'figure' }, - children: [ - block, - { - type: 'figcaption', - data: { hName: 'figcaption' }, - children: this.tokenizeBlock(img.caption, eat.now()), - }, - ], - }; - }) - .filter(e => e); // eslint-disable-line unicorn/prefer-array-find - const img = imgs[0]; - - if (!img || !img.url) return eat(match); - return eat(match)(WrapPinnedBlocks(img, json)); - } - case 'callout': { - const types = { - info: ['📘', 'info'], - success: ['👍', 'okay'], - warning: ['🚧', 'warn'], - danger: ['❗️', 'error'], - }; - json.type = json.type in types ? types[json.type] : [json.icon || '👍', json.type]; - const [icon, theme] = json.type; - if (!(json.title || json.body)) return eat(match); - return eat(match)( - WrapPinnedBlocks( - { - type: 'rdme-callout', - data: { - hName: 'rdme-callout', - hProperties: { - theme: theme || 'default', - icon, - title: json.title, - value: json.body, - }, - }, - children: [...this.tokenizeBlock(json.title, eat.now()), ...this.tokenizeBlock(json.body, eat.now())], - }, - json - ) - ); - } - case 'parameters': { - const { data, rows, cols } = json; - const tokenizeCell = this[compatibilityMode ? 'tokenizeBlock' : 'tokenizeInline'].bind(this); - - if (!Object.keys(data).length) return eat(match); // skip empty tables - - const sparseData = Object.entries(data).reduce((mapped, [key, v]) => { - let [row, col] = key.split('-'); - row = row === 'h' ? 0 : parseInt(row, 10) + 1; - col = parseInt(col, 10); - - mapped[row] ||= []; - mapped[row][col] = v; - - return mapped; - }, []); - - // The header row is not counted in the rows - const children = Array.from({ length: rows + 1 }, (_, y) => { - return { - type: 'tableRow', - children: Array.from({ length: cols }, (__, x) => ({ - type: y === 0 ? 'tableHead' : 'tableCell', - children: sparseData[y]?.[x] ? tokenizeCell(sparseData[y][x], eat.now()) : [{ type: 'text', value: '' }], - })), - }; - }); - - const table = { - type: 'table', - align: 'align' in json ? json.align : new Array(json.cols).fill('left'), - children, - }; - return eat(match)(WrapPinnedBlocks(table, json)); - } - case 'embed': { - const { title, url, html } = json; - try { - json.provider = new URL(url).hostname - .split(/(?:www)?\./) - .filter(i => i) - .join('.'); - } catch { - json.provider = url; - } - const data = { - ...json, - url, - html, - title, - }; - return eat(match)( - WrapPinnedBlocks( - { - type: 'embed', - children: [ - { - type: 'link', - url, - title: json.provider, - children: [{ type: 'text', value: title }], - }, - ], - data: { - hProperties: { - ...data, - href: url, - }, - hName: 'rdme-embed', - }, - }, - json - ) - ); - } - case 'html': { - return eat(match)( - WrapPinnedBlocks( - { - type: 'html-block', - data: { - hName: 'html-block', - hProperties: { - html: json.html, - runScripts: compatibilityMode, - safeMode, - }, - }, - }, - json - ) - ); - } - default: { - return eat(match)( - WrapPinnedBlocks( - { - type: 'div', - children: this.tokenizeBlock(json.text || json.html, eat.now()), - data: { - hName: type || 'div', - hProperties: json, - ...json, - }, - }, - json - ) - ); - } - } - }; -} - -function parser() { - const { Parser } = this; - const tokenizers = Parser.prototype.blockTokenizers; - const methods = Parser.prototype.blockMethods; - - tokenizers.magicBlocks = tokenize({ - compatibilityMode: this.data('compatibilityMode'), - safeMode: this.data('safeMode'), - alwaysThrow: this.data('alwaysThrow'), - }); - methods.splice(methods.indexOf('newline'), 0, 'magicBlocks'); -} - -module.exports = parser; - -module.exports.sanitize = sanitizeSchema => { - // const tags = sanitizeSchema.tagNames; - const attr = sanitizeSchema.attributes; - attr.li = ['className', 'checked']; - attr.pre = ['className', 'lang', 'meta']; - attr.code = ['className', 'lang', 'meta']; - attr.table = ['align']; - - return parser; -}; - -module.exports.imgSizeByWidth = imgSizeByWidth; diff --git a/processor/parse/utils.js b/processor/parse/utils.js deleted file mode 100644 index ae31fd489..000000000 --- a/processor/parse/utils.js +++ /dev/null @@ -1,21 +0,0 @@ -function insertTokenizerBefore({ name, before, tokenizer, type = 'block' }) { - const { Parser } = this; - const tokenizers = Parser.prototype[`${type}Tokenizers`]; - const methods = Parser.prototype[`${type}Methods`]; - - const index = methods.indexOf(before); - if (index === -1) { - throw new Error(`The '${before}' tokenizer does not exist!`); - } - - tokenizers[name] = tokenizer; - methods.splice(index, 0, name); -} - -export function insertBlockTokenizerBefore(args) { - insertTokenizerBefore.call(this, args); -} - -export function insertInlineTokenizerBefore(args) { - return insertTokenizerBefore.call(this, { ...args, type: 'inline' }); -} diff --git a/processor/parse/variable-parser.js b/processor/parse/variable-parser.js deleted file mode 100644 index cffc58524..000000000 --- a/processor/parse/variable-parser.js +++ /dev/null @@ -1,66 +0,0 @@ -const { VARIABLE_REGEXP } = require('@readme/variable'); - -function tokenizeVariable(eat, value, silent) { - // Modifies the regular expression to match from - // the start of the line - const match = new RegExp(`^${VARIABLE_REGEXP}`).exec(value); - - if (!match) return false; - - /* istanbul ignore if */ - if (silent) return true; - - // Escaped variables should just return the text - if (match[0].startsWith('\\')) { - return eat(match[0])({ - type: 'text', - value: match[0].replace(/\\/g, ''), - }); - } - - if (match[1].startsWith('glossary:')) { - return eat(match[0])({ - type: 'readme-glossary-item', - data: { - hName: 'readme-glossary-item', - hProperties: { term: match[1].replace('glossary:', '') }, - }, - }); - } - - return eat(match[0])({ - type: 'readme-variable', - text: match[1], - data: { hName: 'readme-variable', hProperties: { variable: match[1] } }, - }); -} - -function locate(value, fromIndex) { - return value.indexOf('<<', fromIndex); -} - -tokenizeVariable.locator = locate; - -function parser() { - const { Parser } = this; - const tokenizers = Parser.prototype.inlineTokenizers; - const methods = Parser.prototype.inlineMethods; - - tokenizers.variable = tokenizeVariable; - - methods.splice(methods.indexOf('text'), 0, 'variable'); -} - -module.exports = parser; - -module.exports.sanitize = sanitizeSchema => { - // This is for our custom variable tags <> - sanitizeSchema.tagNames.push('readme-variable'); - sanitizeSchema.attributes['readme-variable'] = ['variable']; - - // This is for our glossary variable tags <> - sanitizeSchema.tagNames.push('readme-glossary-item'); - sanitizeSchema.attributes['readme-glossary-item'] = ['term']; - - return parser; -}; diff --git a/processor/transform/div.ts b/processor/transform/div.ts new file mode 100644 index 000000000..38f15b35f --- /dev/null +++ b/processor/transform/div.ts @@ -0,0 +1,29 @@ +import { NodeTypes } from '../../enums'; +import { Node, Parent } from 'mdast'; +import { Transform } from 'mdast-util-from-markdown'; +import { TutorialTile } from '../../types'; + +import { visit } from 'unist-util-visit'; + +const divTransformer = (): Transform => tree => { + visit(tree, 'div', (node: Node, index, parent: Parent) => { + const type = node.data?.hName; + switch (type) { + // Check if the div is a tutorial-tile in disguise + case NodeTypes.tutorialTile: + const { hName, hProperties, ...rest } = node.data; + const tile = { + ...rest, + type: NodeTypes.tutorialTile, + } as TutorialTile; + parent.children.splice(index, 1, tile); + // idk what this is and/or just make it a paragraph + default: + node.type = type || 'paragraph'; + } + }); + + return tree; +}; + +export default divTransformer; diff --git a/processor/transform/index.ts b/processor/transform/index.ts index 6a213409c..64258de59 100644 --- a/processor/transform/index.ts +++ b/processor/transform/index.ts @@ -3,6 +3,7 @@ import codeTabsTransfromer from './code-tabs'; import embedTransformer from './embeds'; import imageTransformer from './images'; import gemojiTransformer from './gemoji+'; +import divTransformer from './div'; import injectComponents from './inject-components'; import readmeComponentsTransformer from './readme-components'; import readmeToMdx from './readme-to-mdx'; @@ -10,4 +11,4 @@ import variablesTransformer from './variables'; export { readmeComponentsTransformer, readmeToMdx, injectComponents, variablesTransformer }; -export default [calloutTransformer, codeTabsTransfromer, embedTransformer, imageTransformer, gemojiTransformer]; +export default [calloutTransformer, codeTabsTransfromer, divTransformer, embedTransformer, imageTransformer, gemojiTransformer]; diff --git a/processor/transform/readme-to-mdx.ts b/processor/transform/readme-to-mdx.ts index f8cc9507f..6b2612251 100644 --- a/processor/transform/readme-to-mdx.ts +++ b/processor/transform/readme-to-mdx.ts @@ -1,3 +1,4 @@ +import { Parent } from 'mdast'; import { NodeTypes } from '../../enums'; import { Transform } from 'mdast-util-from-markdown'; import { MdxJsxAttribute } from 'mdast-util-mdx-jsx'; @@ -5,6 +6,12 @@ import { MdxJsxAttribute } from 'mdast-util-mdx-jsx'; import { visit } from 'unist-util-visit'; const readmeToMdx = (): Transform => tree => { + // Unwrap pinned nodes, replace rdme-pin with its child node + visit(tree, 'rdme-pin', (node: Parent, i, parent) => { + const newNode = node.children[0]; + parent.children.splice(i, 1, newNode); + }); + visit(tree, NodeTypes.tutorialTile, (tile, index, parent) => { const attributes: MdxJsxAttribute[] = ['backgroundColor', 'emoji', 'id', 'link', 'slug', 'title'].map(name => { const value = tile[name];