Skip to content

Instantly share code, notes, and snippets.

@chandlerprall
Last active February 11, 2021 22:16
Show Gist options
  • Save chandlerprall/0667ec5bbbdc0d3a615ffe50f8a10ab1 to your computer and use it in GitHub Desktop.
Save chandlerprall/0667ec5bbbdc0d3a615ffe50f8a10ab1 to your computer and use it in GitHub Desktop.
writing a remark 13 variable plugin
// reference plugin:
// https://github.com/remarkjs/remark-gfm/blob/main/index.js
// which wraps:
// https://github.com/micromark/micromark-extension-gfm-strikethrough/blob/56aef3a974c1b9baee72279e93f9c1fe7205a9af/index.js#L23
const remark = require('remark');
const Compiler = (tree) => {
return tree;
};
function identityCompiler() {
this.Compiler = Compiler;
}
function plugin() {
const data = this.data();
add('micromarkExtensions', create());
// add('fromMarkdownExtensions', fromMarkdown)
function add(field, value) {
if (data[field]) data[field].push(value)
else data[field] = [value]
}
function create() {
const openTokenizer = {
tokenize: openTokenize,
// resolveAll: resolveAllStrikethrough
}
return {
text: {
123: openTokenizer,
125: openTokenizer,
}
};
const remark = require('remark');
const visit = require('unist-util-visit');
const Compiler = (tree) => {
return tree;
};
function identityCompiler() {
this.Compiler = Compiler;
}
function fromMarkdown() {
function enterExtension(token) {
this.enter({type: 'placeholder', children: [], value: token.text}, token);
}
function exitExtension(token) {
this.exit(token);
}
return {
transforms: [],
canContainEols: [],
enter: {
placeholder: enterExtension,
},
exit: {
placeholder: exitExtension,
}
};
}
function plugin(options) {
const { mapping } = options;
return function() {
const data = this.data();
add('micromarkExtensions', create());
add('fromMarkdownExtensions', fromMarkdown());
function add(field, value) {
if (data[field]) data[field].push(value)
else data[field] = [value]
}
function create() {
const tokenizer = {
tokenize: tokenize,
};
return {
text: {
123: tokenizer,
},
};
}
const OPEN_BRACE = 123;
const CLOSE_BRACE = 125;
function tokenize(effects, ok, notok) {
let walkingState = 'starting'; // 'starting' | 'consuming' | 'ending'
let openBraceCount = 0;
let closeBraceCount = 0;
let consumed = '';
function start(charCode) {
effects.enter('placeholder');
return walk(charCode);
}
function walk(charCode) {
if (walkingState === 'starting') {
if (charCode === OPEN_BRACE) {
openBraceCount++;
if (openBraceCount === 2) {
walkingState = 'consuming';
}
effects.consume(charCode);
return walk;
} else {
return notok(charCode);
}
} else if (walkingState === 'consuming') {
const char = String.fromCharCode(charCode);
if (char.match(/[a-zA-Z0-9_-]/)) {
consumed += char;
effects.consume(charCode);
return walk;
} else if (charCode === CLOSE_BRACE) {
walkingState = 'ending';
closeBraceCount++;
effects.consume(charCode);
return walk;
} else {
return notok(charCode);
}
} else if (walkingState === 'ending') {
if (charCode === CLOSE_BRACE) {
closeBraceCount++;
if (closeBraceCount === 2) {
// celebrate
effects.consume(charCode);
const token = effects.exit('placeholder');
token.text = consumed;
return ok(charCode);
}
effects.consume(charCode);
return walk;
} else {
return notok(charCode);
}
}
}
return start;
}
return function transform(tree) {
visit(
tree,
'placeholder',
(node) => {
const variableName = node.value;
node.type = 'text';
delete node.children;
if (mapping.hasOwnProperty(variableName)) {
// mapping exists 🚀
node.value = mapping[variableName];
} else {
// bad 👎
node.value = `{variable "${variableName}" undefined}`
}
}
)
return tree;
}
}
}
result = remark()
.use(identityCompiler)
.use(plugin({ mapping: { cat: 'Erwin' } }))
.process(
'check out this {{dog}}!!',
(err, file) => {
if (err) {
console.error(err);
} else {
console.log(file.result.children[0]);
}
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment