UNPKG

gridsome-plugin-remark-markdown-snippets

Version:

Gridsome markdown remark transformer plugin to inject external markdown files into another file, allowing use with variables as well.

105 lines (85 loc) 3.75 kB
const fs = require("fs"); const path = require("path"); const visit = require("unist-util-visit-parents"); const normalize = require("normalize-path"); const unified = require("unified"); const toVFile = require("to-vfile"); const remarkParse = require("remark-parse"); const remarkAttr = require("remark-attr"); const frontMatter = require("remark-frontmatter"); const appRoot = require('app-root-path'); const DEFAULT_SNIPPET_IDENTIFIER = "snippet:", DEFAULT_FILENAME_MATCH = /:([^{]*[^{])/g, DEFAULT_ATTRIBUTES_MATCH = /([^{]+?)(?=}+?)/g, DEFAULT_ATTRIBUTE_SEARCH = /([^"={]+?)="([^"]+?)"|(data-[^"= ]+?)(?=[" ])/gm; module.exports = options => { return async (tree, file) => { const parentFileDir = file.data.node ? formatSlideDirectory(file.data.node.fileInfo) : "content"; const snippetsDir = options ? options.path : parentFileDir; const snippetPath = path.resolve(appRoot.toString(), snippetsDir); try { if (!fs.existsSync(snippetPath)) { throw Error(`Invalid directory specified "${snippetPath}"`); } visit (tree, node => node.type === "paragraph" && node.children[0].type === "inlineCode" && node.children[0].value.startsWith(DEFAULT_SNIPPET_IDENTIFIER), (node, ancestors) => { const parent = ancestors[ancestors.length - 1]; const nodeIndex = parent.children.indexOf(node); const { value } = node.children[0]; const snippetFilename = extractFilename(value); const attributesArray = value.match(DEFAULT_ATTRIBUTES_MATCH); let attributesString; if(attributesArray) { attributesString = attributesArray[0] } else { attributesString = null; } const astNodes = fileToAST(node, snippetPath, snippetFilename, attributesString); parent.children.splice(nodeIndex, 1, ...astNodes); return nodeIndex + (astNodes.length - 1); }); } catch (error) { console.log(error) throw error } } } function extractAttributes (attributeString) { const regexMatches = attributeString.matchAll(DEFAULT_ATTRIBUTE_SEARCH); const attributeObject = {}; for (const match of regexMatches) { attributeObject[match[1].trim()] = match[2].trim(); } return attributeObject } function fileToAST (node, directory, filename, attributes) { let filePath = normalize(path.join(directory, filename)); if (!fs.existsSync(filePath)) { throw Error(`Invalid snippet specified; no such file "${filePath}"`); } let code = toVFile.readSync(filePath, "utf8"); if(attributes) { const attributesObject = extractAttributes(attributes); const variableKeys = Object.keys(attributesObject); variableKeys.forEach(variable => { code.contents = code.contents.replaceAll(`$${variable}`, attributesObject[variable]); }) } const tree = unified() .use(remarkParse) .use(remarkAttr) .use(frontMatter, ["yaml"]) .parse(code); return [...tree.children]; } function extractFilename (snippetString) { const regexMatches = [...snippetString.matchAll(DEFAULT_FILENAME_MATCH)]; return regexMatches[0][1]; } function formatSlideDirectory (fileInfo) { const { path, name, extension } = fileInfo; return path.split(`${name}${extension}`)[0] }