prism/tests/helper/prism-loader.js

130 lines
3.2 KiB
JavaScript

'use strict';
const fs = require('fs');
const components = require('../../components.json');
const getLoader = require('../../dependencies');
const languagesCatalog = components.languages;
const coreChecks = require('./checks');
/**
* @typedef PrismLoaderContext
* @property {import('../../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 = {
/**
* Creates a new Prism instance with the given language loaded
*
* @param {string|string[]} languages
* @returns {import('../../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();
coreChecks(Prism);
return Prism;
},
/**
* Loads the given component's file source as string
*
* @private
* @param {string} name
* @returns {string}
*/
loadComponentSource(name) {
return this.loadFileSource(__dirname + '/../../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;
}
};