rsnext/packages/next/export/worker.js
JJ Kasper 244b0e700e
Add AMP validation on export (#6794)
* Add err.sh link and pool validation results
to wait to show error until export is finished

* Fix wording in amp-export-validation err.sh

* Update validation error message

Co-Authored-By: ijjk <22380829+ijjk@users.noreply.github.com>

* Update ways to fix text

Co-Authored-By: ijjk <22380829+ijjk@users.noreply.github.com>

* Update why the error occurred wording

Co-Authored-By: ijjk <22380829+ijjk@users.noreply.github.com>

* Update wording some more

Co-Authored-By: ijjk <22380829+ijjk@users.noreply.github.com>
2019-03-26 16:21:27 -05:00

110 lines
3.3 KiB
JavaScript

import mkdirpModule from 'mkdirp'
import { promisify } from 'util'
import { extname, join, dirname, sep } from 'path'
import { cleanAmpPath } from 'next-server/dist/server/utils'
import { renderToHTML } from 'next-server/dist/server/render'
import { writeFile } from 'fs'
import Sema from 'async-sema'
import AmpHtmlValidator from 'amphtml-validator'
import { loadComponents } from 'next-server/dist/server/load-components'
const envConfig = require('next-server/config')
const mkdirp = promisify(mkdirpModule)
global.__NEXT_DATA__ = {
nextExport: true
}
process.on(
'message',
async ({
distDir,
buildId,
exportPaths,
exportPathMap,
outDir,
renderOpts,
serverRuntimeConfig,
concurrency
}) => {
const sema = new Sema(concurrency, { capacity: exportPaths.length })
try {
const work = async path => {
await sema.acquire()
const { page, query = {} } = exportPathMap[path]
const ampOpts = { amphtml: Boolean(query.amp), hasAmp: query.hasAmp, ampPath: query.ampPath }
delete query.hasAmp
delete query.ampPath
const req = { url: path }
const res = {}
envConfig.setConfig({
serverRuntimeConfig,
publicRuntimeConfig: renderOpts.runtimeConfig
})
if (query.ampOnly) {
delete query.ampOnly
path = cleanAmpPath(path)
}
// replace /docs/index.amp with /docs.amp
path = path.replace(/(?<!^)\/index\.amp$/, '.amp')
let htmlFilename = `${path}${sep}index.html`
const pageExt = extname(page)
const pathExt = extname(path)
// Make sure page isn't a folder with a dot in the name e.g. `v1.2`
if (pageExt !== pathExt && pathExt !== '') {
// If the path has an extension, use that as the filename instead
htmlFilename = path
} else if (path === '/') {
// If the path is the root, just use index.html
htmlFilename = 'index.html'
}
const baseDir = join(outDir, dirname(htmlFilename))
const htmlFilepath = join(outDir, htmlFilename)
await mkdirp(baseDir)
const components = await loadComponents(distDir, buildId, page)
const html = await renderToHTML(req, res, page, query, { ...components, ...renderOpts, ...ampOpts })
if (ampOpts.amphtml) {
const validator = await AmpHtmlValidator.getInstance()
const result = validator.validateString(html)
const errors = result.errors.filter(e => e.severity === 'ERROR')
const warnings = result.errors.filter(e => e.severity !== 'ERROR')
if (warnings.length || errors.length) {
process.send({
type: 'amp-validation',
payload: {
page,
result: {
errors,
warnings
}
}
})
}
}
await new Promise((resolve, reject) =>
writeFile(
htmlFilepath,
html,
'utf8',
err => (err ? reject(err) : resolve())
)
)
process.send({ type: 'progress' })
sema.release()
}
await Promise.all(exportPaths.map(work))
process.send({ type: 'done' })
} catch (err) {
console.error(err)
process.send({ type: 'error', payload: err })
}
}
)