index.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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. 'csharp': 'cs',
  13. 'html': 'markup'
  14. };
  15. function getAssets() {
  16. var cssFiles = this.config.get('pluginsConfig.prism.css', []);
  17. var cssFolder = null;
  18. var cssNames = [];
  19. var cssName = null;
  20. if (cssFiles.length === 0) {
  21. cssFiles.push('prismjs/themes/prism.css');
  22. }
  23. cssFiles.forEach(function(cssFile) {
  24. var cssPath = require.resolve(cssFile);
  25. cssFolder = path.dirname(cssPath);
  26. cssName = path.basename(cssPath);
  27. cssNames.push(cssName);
  28. });
  29. return {
  30. assets: cssFolder,
  31. css: cssNames
  32. };
  33. }
  34. module.exports = {
  35. book: getAssets,
  36. ebook: function() {
  37. // Adding prism-pdf.css to the CSS collection forces Gitbook to add a reference to it in the html
  38. // markup that is converted into a PDF.
  39. var assets = getAssets.call(this);
  40. assets.css.push('prism-pdf.css');
  41. return assets;
  42. },
  43. blocks: {
  44. code: function(block) {
  45. var highlighted = '';
  46. // Normalize language id
  47. var lang = block.kwargs.language || DEFAULT_LANGUAGE;
  48. lang = MAP_LANGUAGES[lang] || lang;
  49. // Try and find the language definition in components folder
  50. if (!languages[lang]) {
  51. try {
  52. require('prismjs/components/prism-' + lang + '.js');
  53. } catch (e) {
  54. console.warn('Failed to load prism syntax: ' + lang);
  55. console.warn(e);
  56. }
  57. }
  58. if (!languages[lang]) lang = DEFAULT_LANGUAGE;
  59. // Check against html, prism "markup" works for this
  60. if (lang === 'html') {
  61. lang = 'markup';
  62. }
  63. try {
  64. // The process can fail (failed to parse)
  65. highlighted = Prism.highlight(block.body, languages[lang]);
  66. } catch (e) {
  67. console.warn('Failed to highlight:');
  68. console.warn(e);
  69. highlighted = block.body;
  70. }
  71. return highlighted;
  72. }
  73. },
  74. hooks: {
  75. // Copy prism-pdf.css into the temporary directory that Gitbook uses for inlining
  76. // styles from this plugin. This is done manually because prism-pdf.css lives outside
  77. // the asset folder referenced above in getAssets().
  78. //
  79. // Inspired by https://github.com/GitbookIO/plugin-styles-less/blob/master/index.js#L8
  80. init: function() {
  81. var book = this;
  82. if (book.output.name !== 'ebook') {
  83. // Output path is different for websites so just skip the logic below since it
  84. // is only valid for ebooks
  85. return;
  86. }
  87. var outputDirectory = path.join(book.output.root(), '/gitbook/gitbook-plugin-prism');
  88. var outputFile = path.resolve(outputDirectory, 'prism-pdf.css');
  89. var inputFile = path.resolve(__dirname, './prism-pdf.css');
  90. mkdirp.sync(outputDirectory);
  91. try {
  92. fs.writeFileSync(outputFile, fs.readFileSync(inputFile));
  93. } catch (e) {
  94. console.warn('Failed to write prism-pdf.css. See https://git.io/v1LHY for side effects.');
  95. console.warn(e);
  96. }
  97. },
  98. page: function(page) {
  99. var highlighted = false;
  100. var $ = cheerio.load(page.content);
  101. // Prism css styles target the <code> and <pre> blocks using
  102. // a substring CSS selector:
  103. //
  104. // code[class*="language-"], pre[class*="language-"]
  105. //
  106. // Adding "language-" to <pre> element should be sufficient to trigger
  107. // correct color theme.
  108. $('pre').each(function() {
  109. highlighted = true;
  110. const $this = $(this);
  111. $this.addClass('language-');
  112. });
  113. if (highlighted) {
  114. page.content = $.html();
  115. }
  116. return page;
  117. }
  118. }
  119. };