diff --git a/packages/gatsby-remark-prismjs/scripts/get-prism-language-dependencies.js b/packages/gatsby-remark-prismjs/scripts/get-prism-language-dependencies.js
new file mode 100644
index 0000000000000..1f4897fbe38dd
--- /dev/null
+++ b/packages/gatsby-remark-prismjs/scripts/get-prism-language-dependencies.js
@@ -0,0 +1,63 @@
+const https = require(`https`)
+const execSync = require(`child_process`).execSync
+const fs = require(`fs`)
+const path = require(`path`)
+
+const fileSavePath = path.resolve(__dirname, `../src/prism-language-dependencies.js`)
+
+function getVersion() {
+ const prismInfo = JSON.parse(execSync(`npm ls prismjs --json`))
+ return prismInfo.dependencies.prismjs.version
+}
+
+function processData(data, url) {
+ // `components.js`:
+ // var components = {
+ // "core": { ... },
+ // "languages": { ... },
+ // ...
+ // }
+ eval(data)
+ if (typeof components === `undefined`) {
+ throw new Error(`The content structure of \`components.js\` seems changed.`)
+ }
+
+ // eslint-disable-next-line no-undef
+ const languages = components.languages
+ const content = `// From ${JSON.stringify(url)}
+module.exports = ${JSON.stringify(languages, null, 2)}
+`
+
+ fs.writeFileSync(fileSavePath, content, `utf8`)
+}
+
+function requestData() {
+ const version = getVersion()
+ const url = `https://raw.githubusercontent.com/PrismJS/prism/v${version}/components.js`
+
+ https
+ .get(url, res => {
+ if (res.statusCode !== 200) {
+ throw new Error(`Request Failed.\nRequest URL: ${url}\nStatus Code: ${res.statusCode}`)
+ }
+
+ res.setEncoding(`utf8`)
+ let rawData = ``
+ res.on(`data`, chunk => {
+ rawData += chunk
+ })
+
+ res.on(`end`, () => {
+ try {
+ processData(rawData, url)
+ } catch (e) {
+ console.error(e.message)
+ }
+ })
+ })
+ .on(`error`, e => {
+ console.error(e.message)
+ })
+}
+
+requestData()
diff --git a/packages/gatsby-remark-prismjs/src/__tests__/__snapshots__/highlight-code.js.snap b/packages/gatsby-remark-prismjs/src/__tests__/__snapshots__/highlight-code.js.snap
index f52008094748b..967a967dde641 100644
--- a/packages/gatsby-remark-prismjs/src/__tests__/__snapshots__/highlight-code.js.snap
+++ b/packages/gatsby-remark-prismjs/src/__tests__/__snapshots__/highlight-code.js.snap
@@ -1,6 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`highlight code and lines with PrismJS highlights code 1`] = `
+exports[`highlight code and lines with PrismJS highlights code for language cpp 1`] = `
+"
+int sum(a, b) {
+ return a + b;
+}
+
+"
+`;
+
+exports[`highlight code and lines with PrismJS highlights code for language jsx 1`] = `
"
import React from \\"react\\"
diff --git a/packages/gatsby-remark-prismjs/src/__tests__/highlight-code.js b/packages/gatsby-remark-prismjs/src/__tests__/highlight-code.js
index 9e67f3389e4ec..69c59f8c7d6ee 100644
--- a/packages/gatsby-remark-prismjs/src/__tests__/highlight-code.js
+++ b/packages/gatsby-remark-prismjs/src/__tests__/highlight-code.js
@@ -1,5 +1,4 @@
const parseLineNumberRange = require(`../parse-line-number-range`)
-const highlightCode = require(`../highlight-code`)
describe(`highlight code and lines with PrismJS`, () => {
it(`parses numeric ranges from the languages variable`, () => {
@@ -46,10 +45,30 @@ describe(`highlight code and lines with PrismJS`, () => {
expect(parseLineNumberRange(`jsx`).splitLanguage).toEqual(`jsx`)
})
- it(`highlights code`, () => {
- const language = `jsx`
- const lineNumbersHighlight = [12, 13, 15]
- const code = `
+ describe(`highlights code for language`, () => {
+ afterEach(() => {
+ jest.resetModules()
+ })
+
+ it(`cpp`, () => {
+ const highlightCode = require(`../highlight-code`)
+ const language = `cpp`
+ const lineNumbersHighlight = [1, 2]
+ const code = `
+int sum(a, b) {
+ return a + b;
+}
+`
+ expect(
+ highlightCode(language, code, lineNumbersHighlight)
+ ).toMatchSnapshot()
+ })
+
+ it(`jsx`, () => {
+ const highlightCode = require(`../highlight-code`)
+ const language = `jsx`
+ const lineNumbersHighlight = [12, 13, 15]
+ const code = `
import React from "react"
class Counter extends React.Component {
@@ -76,8 +95,9 @@ class Counter extends React.Component {
export default Counter
`
- expect(
- highlightCode(language, code, lineNumbersHighlight)
- ).toMatchSnapshot()
+ expect(
+ highlightCode(language, code, lineNumbersHighlight)
+ ).toMatchSnapshot()
+ })
})
})
diff --git a/packages/gatsby-remark-prismjs/src/__tests__/load-prism-language.js b/packages/gatsby-remark-prismjs/src/__tests__/load-prism-language.js
new file mode 100644
index 0000000000000..b8f7b30496201
--- /dev/null
+++ b/packages/gatsby-remark-prismjs/src/__tests__/load-prism-language.js
@@ -0,0 +1,44 @@
+const loadPrismLanguage = require(`../load-prism-language`)
+
+describe(`load prism language`, () => {
+ afterEach(() => {
+ jest.resetModules()
+ })
+
+ it(`throw if language not support`, () => {
+ expect(() => loadPrismLanguage(`imnotalanguage`)).toThrow(
+ `Prism doesn't support language 'imnotalanguage'.`
+ )
+ })
+
+ it(`load supported language`, () => {
+ const language = `c`
+ const Prism = require(`prismjs`)
+
+ const languagesBeforeLoaded = Object.keys(Prism.languages)
+ expect(Prism.languages).not.toHaveProperty(language)
+
+ loadPrismLanguage(language)
+
+ const languagesAfterLoaded = Object.keys(Prism.languages)
+ expect(Prism.languages).toHaveProperty(language)
+ expect(languagesAfterLoaded.length).toBe(languagesBeforeLoaded.length + 1)
+ })
+
+ it(`also load the required language`, () => {
+ const language = `cpp`
+ const requiredLanguage = `c`
+ const Prism = require(`prismjs`)
+
+ const languagesBeforeLoaded = Object.keys(Prism.languages)
+ expect(Prism.languages).not.toHaveProperty(language)
+ expect(Prism.languages).not.toHaveProperty(requiredLanguage)
+
+ loadPrismLanguage(language)
+
+ const languagesAfterLoaded = Object.keys(Prism.languages)
+ expect(Prism.languages).toHaveProperty(language)
+ expect(Prism.languages).toHaveProperty(requiredLanguage)
+ expect(languagesAfterLoaded.length).toBe(languagesBeforeLoaded.length + 2)
+ })
+})
diff --git a/packages/gatsby-remark-prismjs/src/__tests__/parse-line-number-range.js b/packages/gatsby-remark-prismjs/src/__tests__/parse-line-number-range.js
index 25492d06d437e..de39d52c8b554 100644
--- a/packages/gatsby-remark-prismjs/src/__tests__/parse-line-number-range.js
+++ b/packages/gatsby-remark-prismjs/src/__tests__/parse-line-number-range.js
@@ -1,6 +1,6 @@
const parseLineNumberRange = require(`../parse-line-number-range`)
-describe(`parses numeric ranges from the languges markdown code directive`, () => {
+describe(`parses numeric ranges from the languages markdown code directive`, () => {
it(`parses numeric ranges from the languages variable`, () => {
expect(parseLineNumberRange(`jsx{1,5,7-8}`).highlightLines).toEqual([
1,
diff --git a/packages/gatsby-remark-prismjs/src/highlight-code.js b/packages/gatsby-remark-prismjs/src/highlight-code.js
index 61ed747fc940c..f2ec3f7e955c4 100644
--- a/packages/gatsby-remark-prismjs/src/highlight-code.js
+++ b/packages/gatsby-remark-prismjs/src/highlight-code.js
@@ -1,11 +1,13 @@
const Prism = require(`prismjs`)
const _ = require(`lodash`)
+const loadPrismLanguage = require(`./load-prism-language`)
+
module.exports = (language, code, lineNumbersHighlight = []) => {
// (Try to) load languages on demand.
if (!Prism.languages[language]) {
try {
- require(`prismjs/components/prism-${language}.js`)
+ loadPrismLanguage(language)
} catch (e) {
// Language wasn't loaded so let's bail.
return code
diff --git a/packages/gatsby-remark-prismjs/src/load-prism-language.js b/packages/gatsby-remark-prismjs/src/load-prism-language.js
new file mode 100644
index 0000000000000..af5b326443d13
--- /dev/null
+++ b/packages/gatsby-remark-prismjs/src/load-prism-language.js
@@ -0,0 +1,31 @@
+const Prism = require(`prismjs`)
+
+const languageDependencies = require(`./prism-language-dependencies`)
+
+module.exports = function loadPrismLanguage(language) {
+ if (Prism.languages[language]) {
+ // Don't load already loaded language
+ return
+ }
+
+ const languageData = languageDependencies[language]
+ if (!languageData) {
+ throw new Error(`Prism doesn't support language '${language}'.`)
+ }
+
+ if (languageData.option === `default`) {
+ // Default language has already been loaded by Prism
+ return
+ }
+
+ if (languageData.require) {
+ // Load the required language first
+ if (Array.isArray(languageData.require)) {
+ languageData.require.forEach(loadPrismLanguage)
+ } else {
+ loadPrismLanguage(languageData.require)
+ }
+ }
+
+ require(`prismjs/components/prism-${language}.js`)
+}
diff --git a/packages/gatsby-remark-prismjs/src/prism-language-dependencies.js b/packages/gatsby-remark-prismjs/src/prism-language-dependencies.js
new file mode 100644
index 0000000000000..70b803a09e564
--- /dev/null
+++ b/packages/gatsby-remark-prismjs/src/prism-language-dependencies.js
@@ -0,0 +1,543 @@
+// From "https://raw.githubusercontent.com/PrismJS/prism/v1.6.0/components.js"
+module.exports = {
+ "meta": {
+ "path": "components/prism-{id}",
+ "noCSS": true,
+ "examplesPath": "examples/prism-{id}",
+ "addCheckAll": true
+ },
+ "markup": {
+ "title": "Markup",
+ "aliasTitles": {
+ "html": "HTML",
+ "xml": "XML",
+ "svg": "SVG",
+ "mathml": "MathML"
+ },
+ "option": "default"
+ },
+ "css": {
+ "title": "CSS",
+ "option": "default"
+ },
+ "clike": {
+ "title": "C-like",
+ "option": "default"
+ },
+ "javascript": {
+ "title": "JavaScript",
+ "option": "default",
+ "require": "clike"
+ },
+ "abap": {
+ "title": "ABAP",
+ "owner": "dellagustin"
+ },
+ "actionscript": {
+ "title": "ActionScript",
+ "require": "javascript",
+ "owner": "Golmote"
+ },
+ "ada": {
+ "title": "Ada",
+ "owner": "Lucretia"
+ },
+ "apacheconf": {
+ "title": "Apache Configuration",
+ "owner": "GuiTeK"
+ },
+ "apl": {
+ "title": "APL",
+ "owner": "ngn"
+ },
+ "applescript": {
+ "title": "AppleScript",
+ "owner": "Golmote"
+ },
+ "asciidoc": {
+ "title": "AsciiDoc",
+ "owner": "Golmote"
+ },
+ "aspnet": {
+ "title": "ASP.NET (C#)",
+ "require": "markup",
+ "owner": "nauzilus"
+ },
+ "autoit": {
+ "title": "AutoIt",
+ "owner": "Golmote"
+ },
+ "autohotkey": {
+ "title": "AutoHotkey",
+ "owner": "aviaryan"
+ },
+ "bash": {
+ "title": "Bash",
+ "owner": "zeitgeist87"
+ },
+ "basic": {
+ "title": "BASIC",
+ "owner": "Golmote"
+ },
+ "batch": {
+ "title": "Batch",
+ "owner": "Golmote"
+ },
+ "bison": {
+ "title": "Bison",
+ "require": "c",
+ "owner": "Golmote"
+ },
+ "brainfuck": {
+ "title": "Brainfuck",
+ "owner": "Golmote"
+ },
+ "bro": {
+ "title": "Bro",
+ "owner": "wayward710"
+ },
+ "c": {
+ "title": "C",
+ "require": "clike",
+ "owner": "zeitgeist87"
+ },
+ "csharp": {
+ "title": "C#",
+ "require": "clike",
+ "owner": "mvalipour"
+ },
+ "cpp": {
+ "title": "C++",
+ "require": "c",
+ "owner": "zeitgeist87"
+ },
+ "coffeescript": {
+ "title": "CoffeeScript",
+ "require": "javascript",
+ "owner": "R-osey"
+ },
+ "crystal": {
+ "title": "Crystal",
+ "require": "ruby",
+ "owner": "MakeNowJust"
+ },
+ "css-extras": {
+ "title": "CSS Extras",
+ "require": "css",
+ "owner": "milesj"
+ },
+ "d": {
+ "title": "D",
+ "require": "clike",
+ "owner": "Golmote"
+ },
+ "dart": {
+ "title": "Dart",
+ "require": "clike",
+ "owner": "Golmote"
+ },
+ "diff": {
+ "title": "Diff",
+ "owner": "uranusjr"
+ },
+ "docker": {
+ "title": "Docker",
+ "owner": "JustinBeckwith"
+ },
+ "eiffel": {
+ "title": "Eiffel",
+ "owner": "Conaclos"
+ },
+ "elixir": {
+ "title": "Elixir",
+ "owner": "Golmote"
+ },
+ "erlang": {
+ "title": "Erlang",
+ "owner": "Golmote"
+ },
+ "fsharp": {
+ "title": "F#",
+ "require": "clike",
+ "owner": "simonreynolds7"
+ },
+ "fortran": {
+ "title": "Fortran",
+ "owner": "Golmote"
+ },
+ "gherkin": {
+ "title": "Gherkin",
+ "owner": "hason"
+ },
+ "git": {
+ "title": "Git",
+ "owner": "lgiraudel"
+ },
+ "glsl": {
+ "title": "GLSL",
+ "require": "clike",
+ "owner": "Golmote"
+ },
+ "go": {
+ "title": "Go",
+ "require": "clike",
+ "owner": "arnehormann"
+ },
+ "graphql": {
+ "title": "GraphQL",
+ "owner": "Golmote"
+ },
+ "groovy": {
+ "title": "Groovy",
+ "require": "clike",
+ "owner": "robfletcher"
+ },
+ "haml": {
+ "title": "Haml",
+ "require": "ruby",
+ "owner": "Golmote"
+ },
+ "handlebars": {
+ "title": "Handlebars",
+ "require": "markup",
+ "owner": "Golmote"
+ },
+ "haskell": {
+ "title": "Haskell",
+ "owner": "bholst"
+ },
+ "haxe": {
+ "title": "Haxe",
+ "require": "clike",
+ "owner": "Golmote"
+ },
+ "http": {
+ "title": "HTTP",
+ "owner": "danielgtaylor"
+ },
+ "icon": {
+ "title": "Icon",
+ "owner": "Golmote"
+ },
+ "inform7": {
+ "title": "Inform 7",
+ "owner": "Golmote"
+ },
+ "ini": {
+ "title": "Ini",
+ "owner": "aviaryan"
+ },
+ "j": {
+ "title": "J",
+ "owner": "Golmote"
+ },
+ "jade": {
+ "title": "Jade",
+ "require": "javascript",
+ "owner": "Golmote"
+ },
+ "java": {
+ "title": "Java",
+ "require": "clike",
+ "owner": "sherblot"
+ },
+ "jolie": {
+ "title": "Jolie",
+ "require": "clike",
+ "owner": "thesave"
+ },
+ "json": {
+ "title": "JSON",
+ "owner": "CupOfTea696"
+ },
+ "julia": {
+ "title": "Julia",
+ "owner": "cdagnino"
+ },
+ "keyman": {
+ "title": "Keyman",
+ "owner": "mcdurdin"
+ },
+ "kotlin": {
+ "title": "Kotlin",
+ "require": "clike",
+ "owner": "Golmote"
+ },
+ "latex": {
+ "title": "LaTeX",
+ "owner": "japborst"
+ },
+ "less": {
+ "title": "Less",
+ "require": "css",
+ "owner": "Golmote"
+ },
+ "livescript": {
+ "title": "LiveScript",
+ "owner": "Golmote"
+ },
+ "lolcode": {
+ "title": "LOLCODE",
+ "owner": "Golmote"
+ },
+ "lua": {
+ "title": "Lua",
+ "owner": "Golmote"
+ },
+ "makefile": {
+ "title": "Makefile",
+ "owner": "Golmote"
+ },
+ "markdown": {
+ "title": "Markdown",
+ "require": "markup",
+ "owner": "Golmote"
+ },
+ "matlab": {
+ "title": "MATLAB",
+ "owner": "Golmote"
+ },
+ "mel": {
+ "title": "MEL",
+ "owner": "Golmote"
+ },
+ "mizar": {
+ "title": "Mizar",
+ "owner": "Golmote"
+ },
+ "monkey": {
+ "title": "Monkey",
+ "owner": "Golmote"
+ },
+ "nasm": {
+ "title": "NASM",
+ "owner": "rbmj"
+ },
+ "nginx": {
+ "title": "nginx",
+ "owner": "westonganger",
+ "require": "clike"
+ },
+ "nim": {
+ "title": "Nim",
+ "owner": "Golmote"
+ },
+ "nix": {
+ "title": "Nix",
+ "owner": "Golmote"
+ },
+ "nsis": {
+ "title": "NSIS",
+ "owner": "idleberg"
+ },
+ "objectivec": {
+ "title": "Objective-C",
+ "require": "c",
+ "owner": "uranusjr"
+ },
+ "ocaml": {
+ "title": "OCaml",
+ "owner": "Golmote"
+ },
+ "oz": {
+ "title": "Oz",
+ "owner": "Golmote"
+ },
+ "parigp": {
+ "title": "PARI/GP",
+ "owner": "Golmote"
+ },
+ "parser": {
+ "title": "Parser",
+ "require": "markup",
+ "owner": "Golmote"
+ },
+ "pascal": {
+ "title": "Pascal",
+ "owner": "Golmote"
+ },
+ "perl": {
+ "title": "Perl",
+ "owner": "Golmote"
+ },
+ "php": {
+ "title": "PHP",
+ "require": "clike",
+ "owner": "milesj"
+ },
+ "php-extras": {
+ "title": "PHP Extras",
+ "require": "php",
+ "owner": "milesj"
+ },
+ "powershell": {
+ "title": "PowerShell",
+ "owner": "nauzilus"
+ },
+ "processing": {
+ "title": "Processing",
+ "require": "clike",
+ "owner": "Golmote"
+ },
+ "prolog": {
+ "title": "Prolog",
+ "owner": "Golmote"
+ },
+ "properties": {
+ "title": ".properties",
+ "owner": "Golmote"
+ },
+ "protobuf": {
+ "title": "Protocol Buffers",
+ "require": "clike",
+ "owner": "just-boris"
+ },
+ "puppet": {
+ "title": "Puppet",
+ "owner": "Golmote"
+ },
+ "pure": {
+ "title": "Pure",
+ "owner": "Golmote"
+ },
+ "python": {
+ "title": "Python",
+ "owner": "multipetros"
+ },
+ "q": {
+ "title": "Q",
+ "owner": "Golmote"
+ },
+ "qore": {
+ "title": "Qore",
+ "require": "clike",
+ "owner": "temnroegg"
+ },
+ "r": {
+ "title": "R",
+ "owner": "Golmote"
+ },
+ "jsx": {
+ "title": "React JSX",
+ "require": [
+ "markup",
+ "javascript"
+ ],
+ "owner": "vkbansal"
+ },
+ "reason": {
+ "title": "Reason",
+ "require": "clike",
+ "owner": "Golmote"
+ },
+ "rest": {
+ "title": "reST (reStructuredText)",
+ "owner": "Golmote"
+ },
+ "rip": {
+ "title": "Rip",
+ "owner": "ravinggenius"
+ },
+ "roboconf": {
+ "title": "Roboconf",
+ "owner": "Golmote"
+ },
+ "ruby": {
+ "title": "Ruby",
+ "require": "clike",
+ "owner": "samflores"
+ },
+ "rust": {
+ "title": "Rust",
+ "owner": "Golmote"
+ },
+ "sas": {
+ "title": "SAS",
+ "owner": "Golmote"
+ },
+ "sass": {
+ "title": "Sass (Sass)",
+ "require": "css",
+ "owner": "Golmote"
+ },
+ "scss": {
+ "title": "Sass (Scss)",
+ "require": "css",
+ "owner": "MoOx"
+ },
+ "scala": {
+ "title": "Scala",
+ "require": "java",
+ "owner": "jozic"
+ },
+ "scheme": {
+ "title": "Scheme",
+ "owner": "bacchus123"
+ },
+ "smalltalk": {
+ "title": "Smalltalk",
+ "owner": "Golmote"
+ },
+ "smarty": {
+ "title": "Smarty",
+ "require": "markup",
+ "owner": "Golmote"
+ },
+ "sql": {
+ "title": "SQL",
+ "owner": "multipetros"
+ },
+ "stylus": {
+ "title": "Stylus",
+ "owner": "vkbansal"
+ },
+ "swift": {
+ "title": "Swift",
+ "require": "clike",
+ "owner": "chrischares"
+ },
+ "tcl": {
+ "title": "Tcl",
+ "owner": "PeterChaplin"
+ },
+ "textile": {
+ "title": "Textile",
+ "require": "markup",
+ "owner": "Golmote"
+ },
+ "twig": {
+ "title": "Twig",
+ "require": "markup",
+ "owner": "brandonkelly"
+ },
+ "typescript": {
+ "title": "TypeScript",
+ "require": "javascript",
+ "owner": "vkbansal"
+ },
+ "verilog": {
+ "title": "Verilog",
+ "owner": "a-rey"
+ },
+ "vhdl": {
+ "title": "VHDL",
+ "owner": "a-rey"
+ },
+ "vim": {
+ "title": "vim",
+ "owner": "westonganger"
+ },
+ "wiki": {
+ "title": "Wiki markup",
+ "require": "markup",
+ "owner": "Golmote"
+ },
+ "xojo": {
+ "title": "Xojo (REALbasic)",
+ "owner": "Golmote"
+ },
+ "yaml": {
+ "title": "YAML",
+ "owner": "hason"
+ }
+}