micromark-extension-lemmy-spoiler
Version:
micromark extension to support lemmy spoiler format
117 lines (109 loc) • 2.75 kB
JavaScript
/**
* @typedef {import('micromark-util-types').CompileContext} CompileContext
* @typedef {import('micromark-util-types').Handle} _Handle
* @typedef {import('micromark-util-types').HtmlExtension} HtmlExtension
*/
/**
* @typedef Spoiler
* Structure representing a spoiler.
* @property {SpoilerType} type
* Kind.
* @property {string} name
* Name of spoiler.
* @property {string | undefined} [content]
* Compiled HTML content inside spoiler.
* @property {number | undefined} [_fenceCount]
* Private :)
*
* @typedef {'spoiler'} SpoilerType
* Kind.
*/
/**
* Create an extension for `micromark` to support spoilers when serializing
* to HTML.
*
* @returns {HtmlExtension}
* Extension for `micromark` that can be passed in `htmlExtensions`, to
* support spoilers when serializing to HTML.
*/
export function spoilerHtml() {
return {
enter: {
spoiler() {
enter.call(this);
},
spoilerContent() {
this.buffer();
},
spoilerName() {
this.buffer();
}
},
exit: {
spoiler: exit,
spoilerContent: exitContainerContent,
spoilerFence: exitContainerFence,
spoilerName: exitName
}
};
/**
* @this {CompileContext}
*/
function enter() {
let stack = this.getData('spoilerStack');
if (!stack) this.setData('spoilerStack', stack = []);
stack.push({
type: 'spoiler',
name: ''
});
}
/**
* @this {CompileContext}
* @type {_Handle}
*/
function exitName() {
const stack = this.getData('spoilerStack');
const data = this.resume();
// Is there a way to do trimEnd in spoiler.js?
stack[stack.length - 1].name = data;
}
/**
* @this {CompileContext}
* @type {_Handle}
*/
function exitContainerContent() {
const data = this.resume();
const stack = this.getData('spoilerStack');
stack[stack.length - 1].content = data;
}
/**
* @this {CompileContext}
* @type {_Handle}
*/
function exitContainerFence() {
const stack = this.getData('spoilerStack');
const spoiler = stack[stack.length - 1];
if (!spoiler._fenceCount) spoiler._fenceCount = 0;
spoiler._fenceCount++;
if (spoiler._fenceCount === 1) this.setData('slurpOneLineEnding', true);
}
/**
* @this {CompileContext}
* @type {_Handle}
*/
function exit() {
const stack = this.getData('spoilerStack');
const spoiler = stack.pop();
this.tag('<details>');
this.lineEndingIfNeeded();
this.tag('<summary>');
this.raw(spoiler.name);
this.tag('</summary>');
if (spoiler.content) {
this.lineEndingIfNeeded();
this.raw(spoiler.content);
}
this.lineEndingIfNeeded();
this.tag('</details>');
}
}