site/node_modules/mathjax-full/ts/components/loader.ts

306 lines
10 KiB
TypeScript
Raw Permalink Normal View History

2024-10-14 06:09:33 +00:00
/*************************************************************
*
* Copyright (c) 2018-2022 The MathJax Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview A dynamic loader for loading MathJax components based
* on a user configuration, while handling timing of
* dependencies properly
*
* @author dpvc@mathjax.org (Davide Cervone)
*/
import {MathJax as MJGlobal, MathJaxObject as MJObject, MathJaxLibrary,
MathJaxConfig as MJConfig, combineWithMathJax, combineDefaults} from './global.js';
import {Package, PackageError, PackageReady, PackageFailed} from './package.js';
export {Package, PackageError, PackageReady, PackageFailed} from './package.js';
export {MathJaxLibrary} from './global.js';
import {FunctionList} from '../util/FunctionList.js';
/*
* The current directory (for webpack), and the browser document (if any)
*/
declare var __dirname: string;
declare var document: Document;
/**
* Function used to determine path to a given package.
*/
export type PathFilterFunction = (data: {name: string, original: string, addExtension: boolean}) => boolean;
export type PathFilterList = (PathFilterFunction | [PathFilterFunction, number])[];
/**
* Update the configuration structure to include the loader configuration
*/
export interface MathJaxConfig extends MJConfig {
loader?: {
paths?: {[name: string]: string}; // The path prefixes for use in locations
source?: {[name: string]: string}; // The URLs for the extensions, e.g., tex: [mathjax]/input/tex.js
dependencies?: {[name: string]: string[]}; // The dependencies for each package
provides?: {[name: string]: string[]}; // The sub-packages provided by each package
load?: string[]; // The packages to load (found in locations or [mathjax]/name])
ready?: PackageReady; // A function to call when MathJax is ready
failed?: PackageFailed; // A function to call when MathJax fails to load
require?: (url: string) => any; // A function for loading URLs
pathFilters?: PathFilterList; // List of path filters (and optional priorities) to add
versionWarnings?: boolean; // True means warn when extension version doesn't match MJ version
[name: string]: any; // Other configuration blocks
};
}
/**
* Update the MathJax object to inclide the loader information
*/
export interface MathJaxObject extends MJObject {
_: MathJaxLibrary;
config: MathJaxConfig;
loader: {
ready: (...names: string[]) => Promise<string[]>; // Get a promise for when all the named packages are loaded
load: (...names: string[]) => Promise<string>; // Load the packages and return a promise for when ready
preLoad: (...names: string[]) => void; // Indicate that packages are already loaded by hand
defaultReady: () => void; // The function performed when all packages are loaded
getRoot: () => string; // Find the root URL for the MathJax files
checkVersion: (name: string, version: string) => boolean; // Check the version of an extension
pathFilters: FunctionList; // the filters to use for looking for package paths
};
startup?: any;
}
/**
* Functions used to filter the path to a package
*/
export const PathFilters: {[name: string]: PathFilterFunction} = {
/**
* Look up the path in the configuration's source list
*/
source: (data) => {
if (CONFIG.source.hasOwnProperty(data.name)) {
data.name = CONFIG.source[data.name];
}
return true;
},
/**
* Add [mathjax] before any relative path, and add .js if needed
*/
normalize: (data) => {
const name = data.name;
if (!name.match(/^(?:[a-z]+:\/)?\/|[a-z]:\\|\[/i)) {
data.name = '[mathjax]/' + name.replace(/^\.\//, '');
}
if (data.addExtension && !name.match(/\.[^\/]+$/)) {
data.name += '.js';
}
return true;
},
/**
* Recursively replace path prefixes (e.g., [mathjax], [tex], etc.)
*/
prefix: (data) => {
let match;
while ((match = data.name.match(/^\[([^\]]*)\]/))) {
if (!CONFIG.paths.hasOwnProperty(match[1])) break;
data.name = CONFIG.paths[match[1]] + data.name.substr(match[0].length);
}
return true;
}
};
/**
* The implementation of the dynamic loader
*/
export namespace Loader {
/**
* The version of MathJax that is running.
*/
const VERSION = MJGlobal.version;
/**
* The versions of all the loaded extensions.
*/
export const versions: Map<string, string> = new Map();
/**
* Get a promise that is resolved when all the named packages have been loaded.
*
* @param {string[]} names The packages to wait for
* @returns {Promise} A promise that resolves when all the named packages are ready
*/
export function ready(...names: string[]): Promise<string[]> {
if (names.length === 0) {
names = Array.from(Package.packages.keys());
}
const promises = [];
for (const name of names) {
const extension = Package.packages.get(name) || new Package(name, true);
promises.push(extension.promise);
}
return Promise.all(promises);
}
/**
* Load the named packages and return a promise that is resolved when they are all loaded
*
* @param {string[]} names The packages to load
* @returns {Promise} A promise that resolves when all the named packages are ready
*/
export function load(...names: string[]): Promise<void | string[]> {
if (names.length === 0) {
return Promise.resolve();
}
const promises = [];
for (const name of names) {
let extension = Package.packages.get(name);
if (!extension) {
extension = new Package(name);
extension.provides(CONFIG.provides[name]);
}
extension.checkNoLoad();
promises.push(extension.promise.then(() => {
if (!CONFIG.versionWarnings) return;
if (extension.isLoaded && !versions.has(Package.resolvePath(name))) {
console.warn(`No version information available for component ${name}`);
}
}) as Promise<null>);
}
Package.loadAll();
return Promise.all(promises);
}
/**
* Indicate that the named packages are being loaded by hand (e.g., as part of a larger package).
*
* @param {string[]} names The packages to load
*/
export function preLoad(...names: string[]) {
for (const name of names) {
let extension = Package.packages.get(name);
if (!extension) {
extension = new Package(name, true);
extension.provides(CONFIG.provides[name]);
}
extension.loaded();
}
}
/**
* The default function to perform when all the packages are loaded
*/
export function defaultReady() {
if (typeof MathJax.startup !== 'undefined') {
MathJax.config.startup.ready();
}
}
/**
* Get the root location for where the MathJax package files are found
*
* @returns {string} The root location (directory for node.js, URL for browser)
*/
export function getRoot(): string {
let root = __dirname + '/../../es5';
if (typeof document !== 'undefined') {
const script = document.currentScript || document.getElementById('MathJax-script');
if (script) {
root = (script as HTMLScriptElement).src.replace(/\/[^\/]*$/, '');
}
}
return root;
}
/**
* Check the version of an extension and report an error if not correct
*
* @param {string} name The name of the extension being checked
* @param {string} version The version of the extension to check
* @param {string} type The type of extension (future code may use this to check ranges of versions)
* @return {boolean} True if there was a mismatch, false otherwise
*/
export function checkVersion(name: string, version: string, _type?: string): boolean {
versions.set(Package.resolvePath(name), VERSION);
if (CONFIG.versionWarnings && version !== VERSION) {
console.warn(`Component ${name} uses ${version} of MathJax; version in use is ${VERSION}`);
return true;
}
return false;
}
/**
* The filters to use to modify the paths used to obtain the packages
*/
export const pathFilters = new FunctionList();
/**
* The default filters to use.
*/
pathFilters.add(PathFilters.source, 0);
pathFilters.add(PathFilters.normalize, 10);
pathFilters.add(PathFilters.prefix, 20);
}
/**
* Export the global MathJax object for convenience
*/
export const MathJax = MJGlobal as MathJaxObject;
/*
* If the loader hasn't been added to the MathJax variable,
* Add the loader configuration, library, and data objects.
* Add any path filters from the configuration.
*/
if (typeof MathJax.loader === 'undefined') {
combineDefaults(MathJax.config, 'loader', {
paths: {
mathjax: Loader.getRoot()
},
source: {},
dependencies: {},
provides: {},
load: [],
ready: Loader.defaultReady.bind(Loader),
failed: (error: PackageError) => console.log(`MathJax(${error.package || '?'}): ${error.message}`),
require: null,
pathFilters: [],
versionWarnings: true
});
combineWithMathJax({
loader: Loader
});
//
// Add any path filters from the configuration
//
for (const filter of MathJax.config.loader.pathFilters) {
if (Array.isArray(filter)) {
Loader.pathFilters.add(filter[0], filter[1]);
} else {
Loader.pathFilters.add(filter);
}
}
}
/**
* Export the loader configuration for convenience
*/
export const CONFIG = MathJax.config.loader;