vite-plugin-markdown
Version:
Import markdown files in vite
170 lines • 7.66 kB
JavaScript
;
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return privateMap.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to set private field on non-instance");
}
privateMap.set(receiver, value);
return value;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _exports, _contextCode;
Object.defineProperty(exports, "__esModule", { value: true });
exports.plugin = exports.Mode = void 0;
const front_matter_1 = __importDefault(require("front-matter"));
const markdown_it_1 = __importDefault(require("markdown-it"));
const htmlparser2_1 = require("htmlparser2");
const domhandler_1 = require("domhandler");
var Mode;
(function (Mode) {
Mode["TOC"] = "toc";
Mode["HTML"] = "html";
Mode["REACT"] = "react";
Mode["VUE"] = "vue";
})(Mode = exports.Mode || (exports.Mode = {}));
const markdownCompiler = (options) => {
var _a, _b, _c;
if (options.markdownIt) {
if (options.markdownIt instanceof markdown_it_1.default || (((_b = (_a = options.markdownIt) === null || _a === void 0 ? void 0 : _a.constructor) === null || _b === void 0 ? void 0 : _b.name) === 'MarkdownIt')) {
return options.markdownIt;
}
else if (typeof options.markdownIt === 'object') {
return markdown_it_1.default(options.markdownIt);
}
}
else if (options.markdown) {
return { render: options.markdown };
}
return markdown_it_1.default({ html: true, xhtmlOut: (_c = options.mode) === null || _c === void 0 ? void 0 : _c.includes(Mode.REACT) }); // TODO: xhtmlOut should be got rid of in next major update
};
class ExportedContent {
constructor() {
_exports.set(this, []);
_contextCode.set(this, '');
}
addContext(contextCode) {
__classPrivateFieldSet(this, _contextCode, __classPrivateFieldGet(this, _contextCode) + `${contextCode}\n`);
}
addExporting(exported) {
__classPrivateFieldGet(this, _exports).push(exported);
}
export() {
return [__classPrivateFieldGet(this, _contextCode), `export { ${__classPrivateFieldGet(this, _exports).join(', ')} }`].join('\n');
}
}
_exports = new WeakMap(), _contextCode = new WeakMap();
const tf = (code, id, options) => {
var _a, _b, _c, _d;
if (!id.endsWith('.md'))
return null;
const content = new ExportedContent();
const fm = front_matter_1.default(code);
content.addContext(`const attributes = ${JSON.stringify(fm.attributes)}`);
content.addExporting('attributes');
const html = markdownCompiler(options).render(fm.body);
if ((_a = options.mode) === null || _a === void 0 ? void 0 : _a.includes(Mode.HTML)) {
content.addContext(`const html = ${JSON.stringify(html)}`);
content.addExporting('html');
}
if ((_b = options.mode) === null || _b === void 0 ? void 0 : _b.includes(Mode.TOC)) {
const root = htmlparser2_1.parseDOM(html);
const indicies = root.filter(rootSibling => rootSibling instanceof domhandler_1.Element && ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(rootSibling.tagName));
const toc = indicies.map(index => ({
level: index.tagName.replace('h', ''),
content: htmlparser2_1.DomUtils.getInnerHTML(index)
}));
content.addContext(`const toc = ${JSON.stringify(toc)}`);
content.addExporting('toc');
}
if ((_c = options.mode) === null || _c === void 0 ? void 0 : _c.includes(Mode.REACT)) {
const root = htmlparser2_1.parseDOM(html, { lowerCaseTags: false });
const subComponentNamespace = 'SubReactComponent';
const markCodeAsPre = (node) => {
var _a;
if (node instanceof domhandler_1.Element) {
if (node.tagName.match(/^[A-Z].+/)) {
node.tagName = `${subComponentNamespace}.${node.tagName}`;
}
if (['pre', 'code'].includes(node.tagName) && ((_a = node.attribs) === null || _a === void 0 ? void 0 : _a.class)) {
node.attribs.className = node.attribs.class;
delete node.attribs.class;
}
if (node.tagName === 'code') {
const codeContent = htmlparser2_1.DomUtils.getInnerHTML(node, { decodeEntities: true });
node.attribs.dangerouslySetInnerHTML = `vfm{{ __html: \`${codeContent.replace(/([\\`])/g, "\\$1")}\`}}vfm`;
node.childNodes = [];
}
if (node.childNodes.length > 0) {
node.childNodes.forEach(markCodeAsPre);
}
}
};
root.forEach(markCodeAsPre);
const h = htmlparser2_1.DomUtils.getOuterHTML(root, { selfClosingTags: true }).replace(/"vfm{{/g, '{{').replace(/}}vfm"/g, '}}');
const reactCode = `
const markdown =
<div>
${h}
</div>
`;
const compiledReactCode = `
function (props) {
Object.keys(props).forEach(function (key) {
SubReactComponent[key] = props[key]
})
${require('@babel/core').transformSync(reactCode, { ast: false, presets: ['@babel/preset-react'] }).code}
return markdown
}
`;
content.addContext(`import React from "react"\nconst ${subComponentNamespace} = {}\nconst ReactComponent = ${compiledReactCode}`);
content.addExporting('ReactComponent');
}
if ((_d = options.mode) === null || _d === void 0 ? void 0 : _d.includes(Mode.VUE)) {
const root = htmlparser2_1.parseDOM(html);
// Top-level <pre> tags become <pre v-pre>
root.forEach((node) => {
if (node instanceof domhandler_1.Element) {
if (['pre', 'code'].includes(node.tagName)) {
node.attribs['v-pre'] = 'true';
}
}
});
// Any <code> tag becomes <code v-pre> excepting under `<pre>`
const markCodeAsPre = (node) => {
if (node instanceof domhandler_1.Element) {
if (node.tagName === 'code')
node.attribs['v-pre'] = 'true';
if (node.childNodes.length > 0)
node.childNodes.forEach(markCodeAsPre);
}
};
root.forEach(markCodeAsPre);
const { code: compiledVueCode } = require('@vue/compiler-sfc').compileTemplate({ source: htmlparser2_1.DomUtils.getOuterHTML(root, { decodeEntities: true }), filename: id, id });
content.addContext(compiledVueCode.replace('\nexport function render(', '\nfunction vueRender(') + `\nconst VueComponent = { render: vueRender }\nVueComponent.__hmrId = ${JSON.stringify(id)}\nconst VueComponentWith = (components) => ({ components, render: vueRender })\n`);
content.addExporting('VueComponent');
content.addExporting('VueComponentWith');
}
return {
code: content.export()
};
};
const plugin = (options = {}) => {
return {
name: 'vite-plugin-markdown',
enforce: 'pre',
transform(code, id) {
return tf(code, id, options);
}
};
};
exports.plugin = plugin;
exports.default = exports.plugin;
exports.default = exports.plugin;
//# sourceMappingURL=index.js.map