react-markdown-doc-loader
Version:
React backend for markdown-doc-loader
143 lines (125 loc) • 3.74 kB
JavaScript
/**
* Modified from https://github.com/PrismJS/prism/blob/master/tests/helper/prism-loader.js
*
* Prism doesn't offically support Node.js
*
* */
const fs = require("fs");
const path = require("path");
const components = require("prismjs/components.json");
const getLoader = require("prismjs/dependencies");
const languagesCatalog = components.languages;
/**
* @typedef PrismLoaderContext
* @property {import('prismjs/components/prism-core')} Prism The Prism instance.
* @property {Set<string>} loaded A set of loaded components.
*/
/** @type {Map<string, string>} */
const fileSourceCache = new Map();
/** @type {() => any} */
let coreSupplierFunction = null;
/** @type {Map<string, (Prism: any) => void>} */
const languageCache = new Map();
module.exports = {
languages: Object.keys(languagesCatalog).filter(function (l) {
return l !== "meta";
}),
/**
* Creates a new Prism instance with the given language loaded
*
* @param {string|string[]} languages
* @returns {import('prismjs/components/prism-core')}
*/
createInstance(languages) {
let context = {
loaded: new Set(),
Prism: this.createEmptyPrism(),
};
context = this.loadLanguages(languages, context);
return context.Prism;
},
/**
* Loads the given languages and appends the config to the given Prism object.
*
* @private
* @param {string|string[]} languages
* @param {PrismLoaderContext} context
* @returns {PrismLoaderContext}
*/
loadLanguages(languages, context) {
if (typeof languages === "string") {
languages = [languages];
}
getLoader(components, languages, [...context.loaded]).load((id) => {
if (!languagesCatalog[id]) {
throw new Error(`Language '${id}' not found.`);
}
// get the function which adds the language from cache
let languageFunction = languageCache.get(id);
if (languageFunction === undefined) {
// make a function from the code which take "Prism" as an argument, so the language grammar
// references the function argument
const func = new Function("Prism", this.loadComponentSource(id));
languageCache.set(id, (languageFunction = (Prism) => func(Prism)));
}
languageFunction(context.Prism);
context.loaded.add(id);
});
return context;
},
/**
* Creates a new empty prism instance
*
* @private
* @returns {Prism}
*/
createEmptyPrism() {
if (!coreSupplierFunction) {
const source = this.loadComponentSource("core");
// Core exports itself in 2 ways:
// 1) it uses `module.exports = Prism` which what we'll use
// 2) it uses `global.Prism = Prism` which we want to sabotage to prevent leaking
const func = new Function("module", "global", source);
coreSupplierFunction = () => {
const module = {
// that's all we care about
exports: {},
};
func(module, {});
return module.exports;
};
}
const Prism = coreSupplierFunction();
return Prism;
},
/**
* Loads the given component's file source as string
*
* @private
* @param {string} name
* @returns {string}
*/
loadComponentSource(name) {
return this.loadFileSource(
path.join(
path.dirname(require.resolve("prismjs")),
"components",
"prism-" + name + ".js"
)
);
},
/**
* Loads the given file source as string
*
* @private
* @param {string} src
* @returns {string}
*/
loadFileSource(src) {
let content = fileSourceCache.get(src);
if (content === undefined) {
fileSourceCache.set(src, (content = fs.readFileSync(src, "utf8")));
}
return content;
},
};