Skip to content

Commit

Permalink
Remove cheerio from Page#render (github#17566)
Browse files Browse the repository at this point in the history
* Write our plugin

* Include it

* Move the RegEx

* Don't rewriteLocalLinks with cheerio anymore

* Process after HTML ast is generated

* Use the same logic as before, just to see if it'll pass

* Don't require languageCode/version

* Only work on local links

* Needs an href

* Only update href if there's a new one to use

* Check for node.properties

* Some links are just mean

* Move use-english-headings to be a plugin

* Bail if no englishHeadings were passed

* Install rehype-wrap

* Wrap ol > li img in div.procedural-image-wrapper

* Test for platform without cheerio

* Use a plugin for rewriteAssetPathsToS3

* Remove cheerio from page.render

* Fix require paths

* SImplify

* Fix some 🐛s

* Use our own rehype-wrap

* Move rewriteAssetPathsToS3 after HTML AST

* Remove some console logs

* Fix check for includesPlatformSpecificContent

* Rename ast => tree
  • Loading branch information
JasonEtco authored Jan 29, 2021
1 parent 1d348ee commit bd63f47
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 33 deletions.
36 changes: 13 additions & 23 deletions lib/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ const path = require('path')
const cheerio = require('cheerio')
const patterns = require('./patterns')
const getMapTopicContent = require('./get-map-topic-content')
const rewriteAssetPathsToS3 = require('./rewrite-asset-paths-to-s3')
const getApplicableVersions = require('./get-applicable-versions')
const encodeBracketedParentheses = require('./encode-bracketed-parentheses')
const generateRedirectsForPermalinks = require('./redirects/permalinks')
const getEnglishHeadings = require('./get-english-headings')
const useEnglishHeadings = require('./use-english-headings')
const getTocItems = require('./get-toc-items')
const pathUtils = require('./path-utils')
const Permalink = require('./permalink')
Expand Down Expand Up @@ -154,6 +152,12 @@ class Page {
this.markdown = await this.getMarkdown()
}

// use English IDs/anchors for translated headings, so links don't break (see #8572)
if (this.languageCode !== 'en') {
const englishHeadings = getEnglishHeadings(this, context.pages)
context.englishHeadings = englishHeadings
}

this.intro = await renderContent(this.rawIntro, context)
this.introPlainText = await renderContent(this.rawIntro, context, { textOnly: true })
this.title = await renderContent(this.rawTitle, context, { textOnly: true, encodeEntities: true })
Expand Down Expand Up @@ -185,6 +189,7 @@ class Page {
}))
}

context.relativePath = this.relativePath
const html = await renderContent(markdown, context)

// product frontmatter may contain liquid
Expand Down Expand Up @@ -227,29 +232,14 @@ class Page {
})
}

const $ = cheerio.load(html)

// set a flag so layout knows whether to render a mac/windows/linux switcher element
this.includesPlatformSpecificContent = $('[class^="platform-"], .mac, .windows, .linux').length > 0

// rewrite asset paths to s3 if it's a dotcom article on any GHE version
// or if it's an enterprise article on any GHE version EXCEPT latest version
rewriteAssetPathsToS3($, context.currentVersion, this.relativePath)

// use English IDs/anchors for translated headings, so links don't break (see #8572)
if (this.languageCode !== 'en') {
const englishHeadings = getEnglishHeadings(this, context.pages)
if (englishHeadings) useEnglishHeadings($, englishHeadings)
}

// wrap ordered list images in a container div
$('ol > li img').each((i, el) => {
$(el).wrap('<div class="procedural-image-wrapper" />')
})

const cleanedHTML = $('body').html()
this.includesPlatformSpecificContent = (
html.includes('extended-markdown mac') ||
html.includes('extended-markdown windows') ||
html.includes('extended-markdown linux')
)

return cleanedHTML
return html
}

// Allow other modules (like custom liquid tags) to make one-off requests
Expand Down
6 changes: 6 additions & 0 deletions lib/render-content/create-processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const graphql = require('highlightjs-graphql').definer
const remarkCodeExtra = require('remark-code-extra')
const codeHeader = require('./plugins/code-header')
const rewriteLocalLinks = require('./plugins/rewrite-local-links')
const useEnglishHeadings = require('./plugins/use-english-headings')
const rewriteAssetPathsToS3 = require('./plugins/rewrite-asset-paths-to-s3')
const wrapInElement = require('./plugins/wrap-in-element')

module.exports = function createProcessor (context) {
return unified()
Expand All @@ -19,9 +22,12 @@ module.exports = function createProcessor (context) {
.use(emoji)
.use(remark2rehype, { allowDangerousHTML: true })
.use(slug)
.use(useEnglishHeadings, context)
.use(autolinkHeadings, { behavior: 'wrap' })
.use(highlight, { languages: { graphql }, subset: false })
.use(raw)
.use(rewriteAssetPathsToS3, context)
.use(wrapInElement, { selector: 'ol > li img', wrapper: 'div.procedural-image-wrapper' })
.use(rewriteLocalLinks, { languageCode: context.currentLanguage, version: context.currentVersion })
.use(html)
}
45 changes: 45 additions & 0 deletions lib/render-content/plugins/rewrite-asset-paths-to-s3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const visit = require('unist-util-visit')
const latestEnterpriseRelease = require('../../enterprise-server-releases').latest
const nonEnterpriseDefaultVersion = require('../../non-enterprise-default-version')
const { getS3BucketPathFromVersion } = require('../../s3-bucket-path-utils')
const allVersions = require('../../all-versions')
const s3BasePath = 'https://github-images.s3.amazonaws.com'

const matcher = node => (
node.type === 'element' &&
node.tagName === 'img' &&
node.properties.src &&
node.properties.src.startsWith('/assets/images')
)

// This module rewrites asset paths on Enterprise versions to S3 paths.
// Source example: /assets/images/foo.png
// Rewritten: https://github-images.s3.amazonaws.com/enterprise/2.20/assets/images/foo.png
// The one exception is Admin pages on the latest GHES release.
module.exports = function rewriteAssetPathsToS3 ({ currentVersion, relativePath }) {
// Bail if we don't have a relativePath in this context
if (!relativePath) return

// skip if this is the homepage
if (relativePath === 'index.md') return

// if the current version is non-enterprise, do not rewrite
if (currentVersion === nonEnterpriseDefaultVersion) return

// the relativePath starts with the product, like /admin/foo or /github/foo
const product = relativePath.split('/')[0]

// if this is an Admin page on the latest GHES release, do not rewrite
if (product === 'admin' && allVersions[currentVersion].currentRelease === latestEnterpriseRelease) return

// if the version is enterprise-server@2.22, use `enterprise/2.22` as the bucket path
// otherwise, use the plan name, e.g., `github-ae`
const bucketPath = getS3BucketPathFromVersion(currentVersion)

return tree => {
visit(tree, matcher, node => {
// Rewrite the node's src
node.properties.src = `${s3BasePath}/${bucketPath}${node.properties.src}`
})
}
}
4 changes: 2 additions & 2 deletions lib/render-content/plugins/rewrite-local-links.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ module.exports = function rewriteLocalLinks ({ languageCode, version }) {
// There's no languageCode or version passed, so nothing to do
if (!languageCode || !version) return

return ast => {
visit(ast, matcher, node => {
return tree => {
visit(tree, matcher, node => {
const newHref = getNewHref(node, languageCode, version)
if (newHref) {
node.properties.href = newHref
Expand Down
26 changes: 26 additions & 0 deletions lib/render-content/plugins/use-english-headings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const GithubSlugger = require('github-slugger')
const Entities = require('html-entities').XmlEntities
const visit = require('unist-util-visit')
const slugger = new GithubSlugger()
const entities = new Entities()

const matcher = node => (
node.type === 'element' &&
['h2', 'h3', 'h4'].includes(node.tagName)
)

// replace translated IDs and links in headings with English
module.exports = function useEnglishHeadings ({ englishHeadings }) {
if (!englishHeadings) return
return tree => {
visit(tree, matcher, node => {
const text = node.children.find(n => n.type === 'text').value
// find English heading in the collection
const englishHeading = englishHeadings[entities.encode(text)]
// get English slug
const englishSlug = slugger.slug(englishHeading)
// use English slug for heading ID and link
node.properties.id = englishSlug
})
}
}
33 changes: 33 additions & 0 deletions lib/render-content/plugins/wrap-in-element.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const visit = require('unist-util-visit')
const { selectAll } = require('hast-util-select')
const parseSelector = require('hast-util-parse-selector')

/*
* Attacher
*/
module.exports = options => {
options = options || {}
const selector = options.selector || options.select || 'body'
const wrapper = options.wrapper || options.wrap

/*
* Transformer
*/
return tree => {
if (typeof wrapper !== 'string') {
throw new TypeError('Expected a `string` as wrapper')
}

if (typeof selector !== 'string') {
throw new TypeError('Expected a `string` as selector')
}

for (const match of selectAll(options.selector, tree)) {
visit(tree, match, (node, i, parent) => {
const wrapper = parseSelector('div')
wrapper.children = [node]
parent.children[i] = wrapper
})
}
}
}
67 changes: 59 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
"got": "^9.6.0",
"gray-matter": "^4.0.1",
"hast-util-from-parse5": "^6.0.1",
"hast-util-parse-selector": "^2.2.5",
"hast-util-select": "^4.0.2",
"hastscript": "^6.0.0",
"helmet": "^3.21.2",
"highlightjs-graphql": "^1.0.2",
Expand Down

0 comments on commit bd63f47

Please sign in to comment.