index.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. var Prism = require('prismjs');
  2. var languages = require('prismjs').languages;
  3. var path = require('path');
  4. var fs = require('fs');
  5. var cheerio = require('cheerio');
  6. var mkdirp = require('mkdirp');
  7. var DEFAULT_LANGUAGE = 'markup';
  8. var MAP_LANGUAGES = {
  9. 'py': 'python',
  10. 'js': 'javascript',
  11. 'rb': 'ruby',
  12. 'cs': 'csharp',
  13. 'html': 'markup'
  14. };
  15. // Base languages syntaxes (as of prism@1.6.0), extended by other syntaxes.
  16. // They need to be required before the others.
  17. var PRELUDE = [
  18. 'clike', 'javascript', 'markup', 'c', 'ruby', 'css',
  19. // The following depends on previous ones
  20. 'java', 'php'
  21. ];
  22. PRELUDE.map(requireSyntax);
  23. /**
  24. * Load the syntax definition for a language id
  25. */
  26. function requireSyntax(lang) {
  27. require('prismjs/components/prism-' + lang + '.js');
  28. }
  29. function getAssets() {
  30. var cssFiles = this.config.get('pluginsConfig.prism.css', []);
  31. var cssFolder = null;
  32. var cssNames = [];
  33. var cssName = null;
  34. if (cssFiles.length === 0) {
  35. cssFiles.push('prismjs/themes/prism.css');
  36. }
  37. cssFiles.forEach(function(cssFile) {
  38. var cssPath = require.resolve(cssFile);
  39. cssFolder = path.dirname(cssPath);
  40. cssName = path.basename(cssPath);
  41. cssNames.push(cssName);
  42. });
  43. return {
  44. assets: cssFolder,
  45. css: cssNames
  46. };
  47. }
  48. module.exports = {
  49. book: getAssets,
  50. ebook: function() {
  51. // Adding prism-ebook.css to the CSS collection forces Gitbook
  52. // reference to it in the html markup that is converted into a PDF.
  53. var assets = getAssets.call(this);
  54. assets.css.push('prism-ebook.css');
  55. return assets;
  56. },
  57. blocks: {
  58. code: function(block) {
  59. var highlighted = '';
  60. // Normalize language id
  61. var lang = block.kwargs.language || DEFAULT_LANGUAGE;
  62. lang = MAP_LANGUAGES[lang] || lang;
  63. // Try and find the language definition in components folder
  64. if (!languages[lang]) {
  65. try {
  66. requireSyntax(lang);
  67. } catch (e) {
  68. console.warn('Failed to load prism syntax: ' + lang);
  69. console.warn(e);
  70. }
  71. }
  72. if (!languages[lang]) lang = DEFAULT_LANGUAGE;
  73. // Check against html, prism "markup" works for this
  74. if (lang === 'html') {
  75. lang = 'markup';
  76. }
  77. try {
  78. // The process can fail (failed to parse)
  79. highlighted = Prism.highlight(block.body, languages[lang]);
  80. } catch (e) {
  81. console.warn('Failed to highlight:');
  82. console.warn(e);
  83. highlighted = block.body;
  84. }
  85. return highlighted;
  86. }
  87. },
  88. hooks: {
  89. // Manually copy prism-ebook.css into the temporary directory that Gitbook uses for inlining
  90. // styles from this plugin. The getAssets() (above) function can't be leveraged because
  91. // ebook-prism.css lives outside the folder referenced by this plugin's config.
  92. //
  93. // @Inspiration https://github.com/GitbookIO/plugin-styles-less/blob/master/index.js#L8
  94. init: function() {
  95. var book = this;
  96. if (book.output.name !== 'ebook') {
  97. // Logic below does not apply to non-ebook formats
  98. return;
  99. }
  100. var outputDirectory = path.join(book.output.root(), '/gitbook/gitbook-plugin-prism');
  101. var outputFile = path.resolve(outputDirectory, 'prism-ebook.css');
  102. var inputFile = path.resolve(__dirname, './prism-ebook.css');
  103. mkdirp.sync(outputDirectory);
  104. try {
  105. fs.writeFileSync(outputFile, fs.readFileSync(inputFile));
  106. } catch (e) {
  107. console.warn('Failed to write prism-ebook.css. See https://git.io/v1LHY for side effects.');
  108. console.warn(e);
  109. }
  110. },
  111. page: function(page) {
  112. var highlighted = false;
  113. var $ = cheerio.load(page.content);
  114. // Prism css styles target the <code> and <pre> blocks using
  115. // a substring CSS selector:
  116. //
  117. // code[class*="language-"], pre[class*="language-"]
  118. //
  119. // Adding "language-" to <pre> element should be sufficient to trigger
  120. // correct color theme.
  121. $('pre').each(function() {
  122. highlighted = true;
  123. const $this = $(this);
  124. $this.addClass('language-');
  125. });
  126. if (highlighted) {
  127. page.content = $.html();
  128. }
  129. return page;
  130. }
  131. }
  132. };