版博士V2.0程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 15 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = markdownItPrism;
  6. var _prismjs = _interopRequireDefault(require("prismjs"));
  7. var _components = _interopRequireDefault(require("prismjs/components/"));
  8. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  9. function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
  10. function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
  11. function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
  12. function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
  13. function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
  14. function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
  15. function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
  16. function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
  17. function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
  18. function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
  19. const SPECIFIED_LANGUAGE_META_KEY = 'de.joshuagleitze.markdown-it-prism.specifiedLanguage';
  20. const DEFAULTS = {
  21. highlightInlineCode: false,
  22. plugins: [],
  23. init: () => {// do nothing by default
  24. },
  25. defaultLanguageForUnknown: undefined,
  26. defaultLanguageForUnspecified: undefined,
  27. defaultLanguage: undefined
  28. };
  29. /**
  30. * Loads the provided `lang` into prism.
  31. *
  32. * @param lang
  33. * Code of the language to load.
  34. * @return The Prism language object for the provided {@code lang} code. {@code undefined} if the language is not known to Prism.
  35. */
  36. function loadPrismLang(lang) {
  37. if (!lang) return undefined;
  38. let langObject = _prismjs.default.languages[lang];
  39. if (langObject === undefined) {
  40. (0, _components.default)([lang]);
  41. langObject = _prismjs.default.languages[lang];
  42. }
  43. return langObject;
  44. }
  45. /**
  46. * Loads the provided Prism plugin.
  47. * @param name
  48. * Name of the plugin to load.
  49. * @throws {Error} If there is no plugin with the provided `name`.
  50. */
  51. function loadPrismPlugin(name) {
  52. try {
  53. require(`prismjs/plugins/${name}/prism-${name}`);
  54. } catch (e) {
  55. throw new Error(`Cannot load Prism plugin "${name}". Please check the spelling.`);
  56. }
  57. }
  58. /**
  59. * Select the language to use for highlighting, based on the provided options and the specified language.
  60. *
  61. * @param options
  62. * The options that were used to initialise the plugin.
  63. * @param lang
  64. * Code of the language to highlight the text in.
  65. * @return The name of the language to use and the Prism language object for that language.
  66. */
  67. function selectLanguage(options, lang) {
  68. let langToUse = lang;
  69. if (langToUse === '' && options.defaultLanguageForUnspecified !== undefined) {
  70. langToUse = options.defaultLanguageForUnspecified;
  71. }
  72. let prismLang = loadPrismLang(langToUse);
  73. if (prismLang === undefined && options.defaultLanguageForUnknown !== undefined) {
  74. langToUse = options.defaultLanguageForUnknown;
  75. prismLang = loadPrismLang(langToUse);
  76. }
  77. return [langToUse, prismLang];
  78. }
  79. /**
  80. * Highlights the provided text using Prism.
  81. *
  82. * @param markdownit
  83. * The markdown-it instance.
  84. * @param options
  85. * The options that have been used to initialise the plugin.
  86. * @param text
  87. * The text to highlight.
  88. * @param lang
  89. * Code of the language to highlight the text in.
  90. * @return If Prism knows the language that {@link selectLanguage} returns for `lang`, the `text` highlighted for that language. Otherwise, `text`
  91. * html-escaped.
  92. */
  93. function highlight(markdownit, options, text, lang) {
  94. return highlightWithSelectedLanguage(markdownit, options, text, selectLanguage(options, lang));
  95. }
  96. /**
  97. * Highlights the provided text using Prism.
  98. *
  99. * @param markdownit
  100. * The markdown-it instance.
  101. * @param options
  102. * The options that have been used to initialise the plugin.
  103. * @param text
  104. * The text to highlight.
  105. * @param lang
  106. * The selected Prism language to use for highlighting.
  107. * @return If Prism knows the language that {@link selectLanguage} returns for `lang`, the `text` highlighted for that language. Otherwise, `text`
  108. * html-escaped.
  109. */
  110. function highlightWithSelectedLanguage(markdownit, options, text, [langToUse, prismLang]) {
  111. return prismLang ? _prismjs.default.highlight(text, prismLang, langToUse) : markdownit.utils.escapeHtml(text);
  112. }
  113. /**
  114. * Construct the class name for the provided `lang`.
  115. *
  116. * @param markdownit
  117. * The markdown-it instance.
  118. * @param lang
  119. * The selected language.
  120. * @return the class to use for `lang`.
  121. */
  122. function languageClass(markdownit, lang) {
  123. return markdownit.options.langPrefix + lang;
  124. }
  125. /**
  126. * A {@link RuleCore} that searches for and extracts language specifications on inline code tokens.
  127. */
  128. function inlineCodeLanguageRule(state) {
  129. var _iterator = _createForOfIteratorHelper(state.tokens),
  130. _step;
  131. try {
  132. for (_iterator.s(); !(_step = _iterator.n()).done;) {
  133. const inlineToken = _step.value;
  134. if (inlineToken.type === 'inline' && inlineToken.children !== null) {
  135. var _iterator2 = _createForOfIteratorHelper(inlineToken.children.entries()),
  136. _step2;
  137. try {
  138. for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
  139. const _step2$value = _slicedToArray(_step2.value, 2),
  140. index = _step2$value[0],
  141. token = _step2$value[1];
  142. if (token.type === 'code_inline' && index + 1 < inlineToken.children.length) {
  143. extractInlineCodeSpecifiedLanguage(token, inlineToken.children[index + 1]);
  144. }
  145. }
  146. } catch (err) {
  147. _iterator2.e(err);
  148. } finally {
  149. _iterator2.f();
  150. }
  151. }
  152. }
  153. } catch (err) {
  154. _iterator.e(err);
  155. } finally {
  156. _iterator.f();
  157. }
  158. }
  159. /**
  160. * Searches for a language specification after an inline code token (e.g. ``{language=cpp}). If present, extracts the language, sets
  161. * it on `inlineCodeToken`’s meta, and removes the specification.
  162. *
  163. * @param inlineCodeToken
  164. * The inline code token for which to extract the language.
  165. * @param followingToken
  166. * The token immediately following the `inlineCodeToken`.
  167. */
  168. function extractInlineCodeSpecifiedLanguage(inlineCodeToken, followingToken) {
  169. const languageSpecificationMatch = followingToken.content.match(/^\{((?:[^\s}]+\s)*)language=([^\s}]+)((?:\s[^\s}]+)*)}/);
  170. if (languageSpecificationMatch !== null) {
  171. inlineCodeToken.meta = _objectSpread(_objectSpread({}, inlineCodeToken.meta), {}, {
  172. [SPECIFIED_LANGUAGE_META_KEY]: languageSpecificationMatch[2]
  173. });
  174. followingToken.content = followingToken.content.slice(languageSpecificationMatch[0].length);
  175. if (languageSpecificationMatch[1] || languageSpecificationMatch[3]) {
  176. followingToken.content = `{${languageSpecificationMatch[1] || ''}${(languageSpecificationMatch[3] || ' ').slice(1)}}${followingToken.content}`;
  177. }
  178. }
  179. }
  180. /**
  181. * Patch the `<pre>` and `<code>` tags produced by the `existingRule` for fenced code blocks.
  182. *
  183. * @param markdownit
  184. * The markdown-it instance.
  185. * @param options
  186. * The options that have been used to initialise the plugin.
  187. * @param existingRule
  188. * The previously configured render rule for fenced code blocks.
  189. */
  190. function applyCodeAttributes(markdownit, options, existingRule) {
  191. return (tokens, idx, renderOptions, env, self) => {
  192. const fenceToken = tokens[idx];
  193. const info = fenceToken.info ? markdownit.utils.unescapeAll(fenceToken.info).trim() : '';
  194. const lang = info.split(/(\s+)/g)[0];
  195. const _selectLanguage = selectLanguage(options, lang),
  196. _selectLanguage2 = _slicedToArray(_selectLanguage, 1),
  197. langToUse = _selectLanguage2[0];
  198. if (!langToUse) {
  199. return existingRule(tokens, idx, renderOptions, env, self);
  200. } else {
  201. fenceToken.info = langToUse;
  202. const existingResult = existingRule(tokens, idx, renderOptions, env, self);
  203. const langClass = languageClass(markdownit, markdownit.utils.escapeHtml(langToUse));
  204. return existingResult.replace(/<((?:pre|code)[^>]*?)(?:\s+class="([^"]*)"([^>]*))?>/g, (match, tagStart, existingClasses, tagEnd) => existingClasses !== null && existingClasses !== void 0 && existingClasses.includes(langClass) ? match : `<${tagStart} class="${existingClasses ? `${existingClasses} ` : ''}${langClass}"${tagEnd || ''}>`);
  205. }
  206. };
  207. }
  208. /**
  209. * Renders inline code tokens by highlighting them with Prism.
  210. *
  211. * @param markdownit
  212. * The markdown-it instance.
  213. * @param options
  214. * The options that have been used to initialise the plugin.
  215. * @param existingRule
  216. * The previously configured render rule for inline code.
  217. */
  218. function renderInlineCode(markdownit, options, existingRule) {
  219. return (tokens, idx, renderOptions, env, self) => {
  220. const inlineCodeToken = tokens[idx];
  221. const specifiedLanguage = inlineCodeToken.meta ? inlineCodeToken.meta[SPECIFIED_LANGUAGE_META_KEY] || '' : '';
  222. const _selectLanguage3 = selectLanguage(options, specifiedLanguage),
  223. _selectLanguage4 = _slicedToArray(_selectLanguage3, 2),
  224. langToUse = _selectLanguage4[0],
  225. prismLang = _selectLanguage4[1];
  226. if (!langToUse) {
  227. return existingRule(tokens, idx, renderOptions, env, self);
  228. } else {
  229. const highlighted = highlightWithSelectedLanguage(markdownit, options, inlineCodeToken.content, [langToUse, prismLang]);
  230. inlineCodeToken.attrJoin('class', languageClass(markdownit, langToUse));
  231. return `<code${self.renderAttrs(inlineCodeToken)}>${highlighted}</code>`;
  232. }
  233. };
  234. }
  235. /**
  236. * Checks whether an option represents a valid Prism language
  237. *
  238. * @param options
  239. * The options that have been used to initialise the plugin.
  240. * @param optionName
  241. * The key of the option inside {@code options} that shall be checked.
  242. * @throws {Error} If the option is not set to a valid Prism language.
  243. */
  244. function checkLanguageOption(options, optionName) {
  245. const language = options[optionName];
  246. if (language !== undefined && loadPrismLang(language) === undefined) {
  247. throw new Error(`Bad option ${optionName}: There is no Prism language '${language}'.`);
  248. }
  249. }
  250. /**
  251. * ‘the most basic rule to render a token’ (https://github.com/markdown-it/markdown-it/blob/master/docs/examples/renderer_rules.md)
  252. */
  253. function renderFallback(tokens, idx, options, env, self) {
  254. return self.renderToken(tokens, idx, options);
  255. }
  256. /**
  257. * Initialisation function of the plugin. This function is not called directly by clients, but is rather provided
  258. * to MarkdownIt’s {@link MarkdownIt.use} function.
  259. *
  260. * @param markdownit
  261. * The markdown it instance the plugin is being registered to.
  262. * @param useroptions
  263. * The options this plugin is being initialised with.
  264. */
  265. function markdownItPrism(markdownit, useroptions) {
  266. const options = Object.assign({}, DEFAULTS, useroptions);
  267. checkLanguageOption(options, 'defaultLanguage');
  268. checkLanguageOption(options, 'defaultLanguageForUnknown');
  269. checkLanguageOption(options, 'defaultLanguageForUnspecified');
  270. options.defaultLanguageForUnknown = options.defaultLanguageForUnknown || options.defaultLanguage;
  271. options.defaultLanguageForUnspecified = options.defaultLanguageForUnspecified || options.defaultLanguage;
  272. options.plugins.forEach(loadPrismPlugin);
  273. options.init(_prismjs.default); // register ourselves as highlighter
  274. markdownit.options.highlight = (text, lang) => highlight(markdownit, options, text, lang);
  275. markdownit.renderer.rules.fence = applyCodeAttributes(markdownit, options, markdownit.renderer.rules.fence || renderFallback);
  276. if (options.highlightInlineCode) {
  277. markdownit.core.ruler.after('inline', 'prism_inline_code_language', inlineCodeLanguageRule);
  278. markdownit.renderer.rules.code_inline = renderInlineCode(markdownit, options, markdownit.renderer.rules.code_inline || renderFallback);
  279. }
  280. }
  281. module.exports = exports.default;
  282. module.exports.default = exports.default;