[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\n.vscode/\ndebug.log"
  },
  {
    "path": "README.md",
    "content": "# Hexo-Prism-Plugin [![NPM](https://img.shields.io/npm/dm/hexo-prism-plugin.svg)](https://www.npmjs.com/package/hexo-prism-plugin)\nSince `highlight.js` didn't support JSX syntax properly, I wrote this plugin to replace\nHexo's default code highlight plugin.\n\n## Install\n```\nnpm i -S hexo-prism-plugin\n```\n## Usage\nFirstly, you should edit your `_config.yml` by adding following configuration.\n```yaml\nprism_plugin:\n  mode: 'preprocess'    # realtime/preprocess\n  theme: 'default'\n  line_number: false    # default false\n  custom_css: 'path/to/your/custom.css'     # optional\n```\nAfter that, check `highlight` option in `_config.yml`. Make sure that default code highlight plugin is disabled.\n```yaml\nhighlight:\n  enable: false\n```\nFinally, clean and re-generate your project by running following commands:\n\n```\nhexo clean\n```\n\n```\nhexo generate\n```\n\n## Options\n- mode:\n  - realtime  (Parse code on browser in real time)\n  - preprocess  (Preprocess code in node)\n\n- theme:\n  - default\n  - coy\n  - dark\n  - funky\n  - okaidia\n  - solarizedlight\n  - tomorrow\n  - twilight\n  - atom-dark\n  - base16-ateliersulphurpool.light\n  - cb\n  - duotone-dark\n  - duotone-earth\n  - duotone-forest\n  - duotone-light\n  - duotone-sea\n  - duotone-space\n  - ghcolors\n  - hopscotch\n  - pojoaque\n  - vs\n  - xonokai\n\n- line_number:\n  - true (Show line numbers)\n  - false (Default, Hide line numbers)\n\n- no_assets\n  - true (Stop loading asset files)\n  - false (Default, load script and stylesheets files)\n\n## Themes\nYou can check out prism-themes project for additional theme preview:\n\nhttps://github.com/PrismJS/prism-themes#available-themes\n\n## Supported languages\nYou could find the supported languages here:\n\nhttp://prismjs.com/#languages-list\n\n## License\nMIT\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"hexo-prism-plugin\",\n  \"version\": \"2.3.0\",\n  \"description\": \"Hexo code highlight by Prism.js\",\n  \"main\": \"src/index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"Eric Huang\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"dir-resolve\": \"^1.0.2\",\n    \"hexo-fs\": \"^0.2.1\",\n    \"node-prismjs\": \"^0.1.0\",\n    \"prism-themes\": \"^1.0.0\",\n    \"prismjs\": \"^1.6.0\"\n  }\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "'use strict';\n\nconst fs = require('hexo-fs');\nconst path = require('path');\nconst Prism = require('node-prismjs');\nconst dirResolve = require('dir-resolve');\n\nconst map = {\n  '&#39;': '\\'',\n  '&amp;': '&',\n  '&gt;': '>',\n  '&lt;': '<',\n  '&quot;': '\"'\n};\n\nconst themeRegex = /^prism-(.*).css$/;\nconst regex = /<pre><code class=\"(.*)?\">([\\s\\S]*?)<\\/code><\\/pre>/igm;\nconst captionRegex = /<p><code>(?![\\s\\S]*<code)(.*?)\\s(.*?)\\n([\\s\\S]*)<\\/code><\\/p>/igm;\n\n/**\n * Unescape from Marked escape\n * @param {String} str\n * @return {String}\n */\nfunction unescape(str) {\n  if (!str || str === null) return '';\n  const re = new RegExp('(' + Object.keys(map).join('|') + ')', 'g');\n  return String(str).replace(re, (match) => map[match]);\n}\n\n/**\n * Wrap theme file to unified format\n * @param {String} basePath\n * @param {String} filename\n * @return {Object}\n */\nfunction toThemeMap(basePath, filename) {\n  const matches = filename.match(themeRegex);\n  if (!matches)\n    return;\n\n  return {\n    name: matches[1],\n    filename,\n    path: path.join(basePath, filename)\n  };\n}\n\nconst rootPath = hexo.config.root || '/';\nconst prismLineNumbersPluginDir = dirResolve('prismjs/plugins/line-numbers');\nconst prismThemeDir = dirResolve('prismjs/themes');\nconst extraThemeDir = dirResolve('prism-themes/themes');\nconst prismMainFile = require.resolve('prismjs');\nconst standardThemes = fs.listDirSync(prismThemeDir)\n  .map(themeFileName => toThemeMap(prismThemeDir, themeFileName));\nconst extraThemes = fs.listDirSync(extraThemeDir)\n  .map(themeFileName => toThemeMap(extraThemeDir, themeFileName));\n\n// Since the regex will not match for the default \"prism.css\" theme,\n// we filter the null theme out and manually add the default theme to the array\nconst themes = standardThemes.concat(extraThemes).filter(Boolean);\nthemes.push({\n  name: 'default',\n  filename: 'prism.css',\n  path: path.join(prismThemeDir, 'prism.css')\n});\n\n// If prism plugin has not been configured, it cannot be initialized properly.\nif (!hexo.config.prism_plugin) {\n  throw new Error('`prism_plugin` options should be added to _config.yml file');\n}\n\n// Plugin settings from config\nconst prismThemeName = hexo.config.prism_plugin.theme || 'default';\nconst mode = hexo.config.prism_plugin.mode || 'preprocess';\nconst line_number = hexo.config.prism_plugin.line_number || false;\nconst custom_css = hexo.config.prism_plugin.custom_css || null;\nconst no_assets = hexo.config.prism_plugin.no_assets || false;\n\nconst prismTheme = themes.find(theme => theme.name === prismThemeName);\nif (!prismTheme) {\n  throw new Error(\"Invalid theme \" + prismThemeName + \". Valid Themes: \\n\" + themes.map(t => t.name).concat('\\n'));\n}\nconst prismThemeFileName = prismTheme.filename;\nconst prismThemeFilePath = custom_css === null ? prismTheme.path : path.join(hexo.base_dir, custom_css);\n/**\n * Code transform for prism plugin.\n * @param {Object} data\n * @return {Object}\n */\nfunction PrismPlugin(data) {\n  // Patch for caption support\n  if (captionRegex.test(data.content)) {\n    // Attempt to parse the code\n    data.content = data.content.replace(captionRegex, (origin, lang, caption, code) => {\n      if (!lang || !caption || !code) return origin;\n      return `<figcaption>${caption}</figcaption><pre><code class=\"${lang}\">${code}</code></pre>`;\n    })\n  }\n\n  data.content = data.content.replace(regex, (origin, lang, code) => {\n    const lineNumbers = line_number ? 'line-numbers' : '';\n    const startTag = `<pre class=\"${lineNumbers} language-${lang}\"><code class=\"language-${lang}\">`;\n    const endTag = `</code></pre>`;\n    code = unescape(code);\n    let parsedCode = '';\n    if (Prism.languages[lang]) {\n      parsedCode = Prism.highlight(code, Prism.languages[lang]);\n    } else {\n      parsedCode = code;\n    }\n    if (line_number) {\n      const match = parsedCode.match(/\\n(?!$)/g);\n      const linesNum = match ? match.length + 1 : 1;\n      let lines = new Array(linesNum + 1);\n      lines = lines.join('<span></span>');\n      const startLine = '<span aria-hidden=\"true\" class=\"line-numbers-rows\">';\n      const endLine = '</span>';\n      parsedCode += startLine + lines + endLine;\n    }\n    return startTag + parsedCode + endTag;\n  });\n\n  return data;\n}\n\n/**\n * Copy asset to hexo public folder.\n */\nfunction copyAssets() {\n  const assets = [{\n    path: `css/${prismThemeFileName}`,\n    data: () => fs.createReadStream(prismThemeFilePath)\n  }];\n\n  // If line_number is enabled in plugin config add the corresponding stylesheet\n  if (line_number) {\n    assets.push({\n      path: 'css/prism-line-numbers.css',\n      data: () => fs.createReadStream(path.join(prismLineNumbersPluginDir, 'prism-line-numbers.css'))\n    });\n  }\n\n  // If prism plugin config mode is realtime include prism.js and line-numbers.js\n  if (mode === 'realtime') {\n    assets.push({\n      path: 'js/prism.js',\n      data: () => fs.createReadStream(prismMainFile)\n    });\n    if (line_number) {\n      assets.push({\n        path: 'js/prism-line-numbers.min.js',\n        data: () => fs.createReadStream(path.join(prismLineNumbersPluginDir, 'prism-line-numbers.min.js'))\n      });\n    }\n  }\n\n  return assets;\n}\n\n/**\n * Injects code to html for importing assets.\n * @param {String} code\n * @param {Object} data\n */\nfunction importAssets(code, data) {\n  const js = [];\n  const css = [\n    `<link rel=\"stylesheet\" href=\"${rootPath}css/${prismThemeFileName}\" type=\"text/css\">`\n  ];\n\n  if (line_number && custom_css === null) {\n    css.push(`<link rel=\"stylesheet\" href=\"${rootPath}css/prism-line-numbers.css\" type=\"text/css\">`);\n  }\n  if (mode === 'realtime') {\n    js.push(`<script src=\"${rootPath}js/prism.js\"></script>`);\n    if (line_number) {\n      js.push(`<script src=\"${rootPath}js/prism-line-numbers.min.js\"></script>`);\n    }\n  }\n  const imports = css.join('\\n') + js.join('\\n');\n\n  // Avoid duplicates\n  if (code.indexOf(imports) > -1) {\n    return code;\n  }\n  return code.replace(/<\\s*\\/\\s*head\\s*>/, imports + '</head>');;\n}\n\n// Register prism plugin\n// Set priority to make sure PrismPlugin executed first\n// Lower priority means that it will be executed first. The default priority is 10.\nhexo.extend.filter.register('after_post_render', PrismPlugin, 9);\n\nif (custom_css === null && !no_assets) {\n  // Register to append static assets\n  hexo.extend.generator.register('prism_assets', copyAssets);\n\n  // Register for importing static assets\n  hexo.extend.filter.register('after_render:html', importAssets);\n}\n"
  }
]