Skip to content

Commit

Permalink
Fix gatsby-remark-prismjs highlight bug (#1921)
Browse files Browse the repository at this point in the history
* prism: add scripts to download language dependencies from github

* Fix loading prism language

If language has dependencies, should load dependencies first.
e.g. cpp depends on c, should load c first.
  • Loading branch information
vincentbel authored and KyleAMathews committed Aug 29, 2017
1 parent 759e827 commit ae18f24
Show file tree
Hide file tree
Showing 8 changed files with 723 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -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()
Original file line number Diff line number Diff line change
@@ -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`] = `
"<span class=\\"gatsby-highlight-code-line\\">
</span><span class=\\"gatsby-highlight-code-line\\"><span class=\\"token keyword\\">int</span> <span class=\\"token function\\">sum</span><span class=\\"token punctuation\\">(</span>a<span class=\\"token punctuation\\">,</span> b<span class=\\"token punctuation\\">)</span> <span class=\\"token punctuation\\">{</span>
</span> <span class=\\"token keyword\\">return</span> a <span class=\\"token operator\\">+</span> b<span class=\\"token punctuation\\">;</span>
<span class=\\"token punctuation\\">}</span>
"
`;
exports[`highlight code and lines with PrismJS highlights code for language jsx 1`] = `
"
<span class=\\"token keyword\\">import</span> React <span class=\\"token keyword\\">from</span> <span class=\\"token string\\">\\"react\\"</span>
Expand Down
36 changes: 28 additions & 8 deletions packages/gatsby-remark-prismjs/src/__tests__/highlight-code.js
Original file line number Diff line number Diff line change
@@ -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`, () => {
Expand Down Expand Up @@ -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 {
Expand All @@ -76,8 +95,9 @@ class Counter extends React.Component {
export default Counter
`
expect(
highlightCode(language, code, lineNumbersHighlight)
).toMatchSnapshot()
expect(
highlightCode(language, code, lineNumbersHighlight)
).toMatchSnapshot()
})
})
})
Original file line number Diff line number Diff line change
@@ -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)
})
})
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
4 changes: 3 additions & 1 deletion packages/gatsby-remark-prismjs/src/highlight-code.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down
31 changes: 31 additions & 0 deletions packages/gatsby-remark-prismjs/src/load-prism-language.js
Original file line number Diff line number Diff line change
@@ -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`)
}
Loading

0 comments on commit ae18f24

Please sign in to comment.