Skip to content

Commit

Permalink
feat(diplodoc/term): new terms
Browse files Browse the repository at this point in the history
BREAKING CHANGE
New term plugin. Previous plugin implement deferred template render.
Now, template renders inline. What this means:
1. Rendering term definition before titles breaks navigation.

So far this is the only known issue, if you want secure yourself while
working with term, you should place term definition at the end of file.
  • Loading branch information
v8tenko committed Jul 27, 2023
1 parent 1cc04dc commit 9bf89ef
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 96 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 2 additions & 6 deletions src/transform/md.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,11 @@ function initParser(md: MarkdownIt, options: OptionsType, env: EnvType) {
};
}

function initCompiler(md: MarkdownIt, options: OptionsType, env: EnvType<{termTokens?: Token[]}>) {
function initCompiler(md: MarkdownIt, options: OptionsType, env: EnvType) {
const {needToSanitizeHtml = false, sanitizeOptions} = options;

return (tokens: Token[]) => {
// TODO: define postprocess step on term plugin
const {termTokens = []} = env;
delete env.termTokens;

const html = md.renderer.render([...tokens, ...termTokens], md.options, env);
const html = md.renderer.render(tokens, md.options, env);

return needToSanitizeHtml ? sanitizeHtml(html, sanitizeOptions) : html;
};
Expand Down
55 changes: 40 additions & 15 deletions src/transform/plugins/term/termDefinitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export function termDefinitions(md: MarkdownIt, options: MarkdownItPluginOpts) {

const newLineReg = new RegExp(/^(\r\n|\r|\n)/);
const termReg = new RegExp(/^\[\*(\w+)\]:/);

let currentLine = startLine;

// Allow multiline term definition
Expand Down Expand Up @@ -74,7 +73,16 @@ export function termDefinitions(md: MarkdownIt, options: MarkdownItPluginOpts) {
return false;
}

return processTermDefinition(md, options, state, currentLine, endLine, label, title);
return processTermDefinition(
md,
options,
state,
currentLine,
startLine,
endLine,
label,
title,
);
};
}

Expand All @@ -83,6 +91,7 @@ function processTermDefinition(
options: MarkdownItPluginOpts,
state: StateBlock,
currentLine: number,
startLine: number,
endLine: number,
label: string,
title: string,
Expand Down Expand Up @@ -121,32 +130,48 @@ function processTermDefinition(
state.env.terms[':' + label] = title;
}

const termNodes = [];

token = new state.Token('template_open', 'template', 1);
token.map = [startLine, currentLine + 1];
token.attrSet('id', ':' + label + '_template');
termNodes.push(token);
token.attrSet('label', label);

token = new state.Token('term_open', 'dfn', 1);
state.tokens.push(token);

token = new state.Token('dfn_open', 'dfn', 1);
token.attrSet('class', 'yfm yfm-term_dfn');
token.attrSet('id', ':' + label + '_element');
token.attrSet('role', 'tooltip');
termNodes.push(token);

termNodes.push(...md.parse(title, {}));
state.tokens.push(token);

token = new state.Token('term_close', 'dfn', -1);
termNodes.push(token);
const titleTokens = md.parse(title, state.env);

token = new state.Token('template_close', 'template', -1);
termNodes.push(token);
for (const titleToken of titleTokens) {
if (titleToken.children?.length) {
titleToken.content = '';
}

if (!titleToken.map) {
state.tokens.push(titleToken);
continue;
}

if (!state.env.termTokens) {
state.env.termTokens = [];
const [start, end] = titleToken.map;

titleToken.map = [start + startLine, end + startLine];
state.tokens.push(titleToken);
}

state.env.termTokens.push(...termNodes);
token = new state.Token('dfn_close', 'dfn', -1);

state.tokens.push(token);

token = new state.Token('template_close', 'template', -1);

state.tokens.push(token);

/** current line links to end of term definition */
state.line = currentLine + 1;

return true;
}
144 changes: 70 additions & 74 deletions test/term.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,100 +32,96 @@ describe('Terms', () => {
const input = readFileSync(inputPath, 'utf8');
const result = transformYfm(input, inputPath);

expect(clearRandomId(result)).toEqual(
'<h1>Web</h1>\n' +
'<p>The <i class="yfm yfm-term_title" term-key=":html" aria-describedby=":html_element" id="">HTML</i> specification</p>\n' +
'<template id=":html_template"><dfn class="yfm yfm-term_dfn" id=":html_element" role="tooltip"><p>The HyperText Markup Language or <strong>HTML</strong> is the standard markup language for documents designed to be displayed in a web browser.</p>\n' +
'</dfn></template>',
);
expect(clearRandomId(result)).toEqual(`\
<template id=":html_template" label="html"><dfn class="yfm yfm-term_dfn" id=":html_element" role="tooltip"><p>The HyperText Markup Language or <strong>HTML</strong> is the standard markup language for documents designed to be displayed in a web browser.</p>
</dfn></template><h1>Web</h1>
<p>The <i class="yfm yfm-term_title" term-key=":html" aria-describedby=":html_element" id="">HTML</i> specification</p>
`);
});

test('Should create term in table with definition template', () => {
const inputPath = resolve(__dirname, './mocks/term/table.md');
const input = readFileSync(inputPath, 'utf8');
const result = transformYfm(input, inputPath);

expect(clearRandomId(result)).toEqual(
'<h1>Web</h1>\n' +
'<table>\n' +
'<thead>\n' +
'<tr>\n' +
'<th>Language</th>\n' +
'<th style="text-align:center">Initial release</th>\n' +
'</tr>\n' +
'</thead>\n' +
'<tbody>\n' +
'<tr>\n' +
'<td><i class="yfm yfm-term_title" term-key=":html" aria-describedby=":html_element" id="">HTML</i></td>\n' +
'<td style="text-align:center">1993</td>\n' +
'</tr>\n' +
'</tbody>\n' +
'</table>\n' +
'<template id=":html_template"><dfn class="yfm yfm-term_dfn" id=":html_element" role="tooltip"><p>The HyperText Markup Language or <strong>HTML</strong> is the standard markup language for documents designed to be displayed in a web browser.</p>\n' +
'</dfn></template>',
);
expect(clearRandomId(result)).toEqual(`\
<template id=":html_template" label="html"><dfn class="yfm yfm-term_dfn" id=":html_element" role="tooltip"><p>The HyperText Markup Language or <strong>HTML</strong> is the standard markup language for documents designed to be displayed in a web browser.</p>
</dfn></template><h1>Web</h1>
<table>
<thead>
<tr>
<th>Language</th>
<th style="text-align:center">Initial release</th>
</tr>
</thead>
<tbody>
<tr>
<td><i class="yfm yfm-term_title" term-key=":html" aria-describedby=":html_element" id="">HTML</i></td>
<td style="text-align:center">1993</td>
</tr>
</tbody>
</table>
`);
});

test('Should create term in code with definition template', () => {
const inputPath = resolve(__dirname, './mocks/term/code.md');
const input = readFileSync(inputPath, 'utf8');
const result = transformYfm(input, inputPath);

expect(clearRandomId(result)).toEqual(
'<h1>Web</h1>\n' +
'\n' +
' <div class="yfm-clipboard">\n' +
' <pre><code class="hljs"><i class="yfm yfm-term_title" term-key=":html" id="">HTML</i>: Lorem\n' +
'</code></pre>\n' +
'\n' +
' <svg width="16" height="16" viewBox="0 0 24 24" class="yfm-clipboard-button" data-animation="3">\n' +
' <path\n' +
' fill="currentColor"\n' +
' d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"\n' +
' />\n' +
' <path\n' +
' stroke="currentColor"\n' +
' fill="transparent"\n' +
' strokeWidth="1.5"\n' +
' d="M9.5 13l3 3l5 -5"\n' +
' visibility="hidden"\n' +
' >\n' +
' <animate\n' +
' id="visibileAnimation-3"\n' +
' attributeName="visibility"\n' +
' from="hidden"\n' +
' to="visible"\n' +
' dur="0.2s"\n' +
' fill="freeze"\n' +
' begin=""\n' +
' />\n' +
' <animate\n' +
' id="hideAnimation-3"\n' +
' attributeName="visibility"\n' +
' from="visible"\n' +
' to="hidden"\n' +
' dur="1s"\n' +
' begin="visibileAnimation-3.end+1"\n' +
' fill="freeze"\n' +
' />\n' +
' </path>\n' +
' </svg>\n' +
' </div>\n' +
'<template id=":html_template"><dfn class="yfm yfm-term_dfn" id=":html_element" role="tooltip"><p>The HyperText Markup Language or <strong>HTML</strong> is the standard markup language for documents designed to be displayed in a web browser.</p>\n' +
'</dfn></template>',
);
expect(clearRandomId(result)).toEqual(`\
<template id=":html_template" label="html"><dfn class="yfm yfm-term_dfn" id=":html_element" role="tooltip"><p>The HyperText Markup Language or <strong>HTML</strong> is the standard markup language for documents designed to be displayed in a web browser.</p>
</dfn></template><h1>Web</h1>
<div class="yfm-clipboard">
<pre><code class="hljs"><i class="yfm yfm-term_title" term-key=":html" id="">HTML</i>: Lorem
</code></pre>
<svg width="16" height="16" viewBox="0 0 24 24" class="yfm-clipboard-button" data-animation="10">
<path
fill="currentColor"
d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"
/>
<path
stroke="currentColor"
fill="transparent"
strokeWidth="1.5"
d="M9.5 13l3 3l5 -5"
visibility="hidden"
>
<animate
id="visibileAnimation-10"
attributeName="visibility"
from="hidden"
to="visible"
dur="0.2s"
fill="freeze"
begin=""
/>
<animate
id="hideAnimation-10"
attributeName="visibility"
from="visible"
to="hidden"
dur="1s"
begin="visibileAnimation-10.end+1"
fill="freeze"
/>
</path>
</svg>
</div>
`);
});

test('Term should use content from include', () => {
const inputPath = resolve(__dirname, './mocks/term/includeContent.md');
const input = readFileSync(inputPath, 'utf8');
const result = transformYfm(input, inputPath);

expect(clearRandomId(result)).toEqual(
'<h1>Web</h1>\n' +
'<p>The <i class="yfm yfm-term_title" term-key=":html" aria-describedby=":html_element" id="">HTML</i> specification</p>\n' +
'<template id=":html_template"><dfn class="yfm yfm-term_dfn" id=":html_element" role="tooltip"><p>The HyperText Markup Language or <strong>HTML</strong> is the standard markup language for documents designed to be displayed in a web browser.</p>\n' +
'</dfn></template>',
);
expect(clearRandomId(result)).toEqual(`\
<template id=":html_template" label="html"><dfn class="yfm yfm-term_dfn" id=":html_element" role="tooltip"><p>The HyperText Markup Language or <strong>HTML</strong> is the standard markup language for documents designed to be displayed in a web browser.</p>
</dfn></template><h1>Web</h1>
<p>The <i class="yfm yfm-term_title" term-key=":html" aria-describedby=":html_element" id="">HTML</i> specification</p>
`);
});
});

0 comments on commit 9bf89ef

Please sign in to comment.