antora-ui-camel/gulp.d/tasks/build-preview-pages.js (146 lines of code) (raw):

'use strict' // NOTE remove patch after upgrading from asciidoctor.js to @asciidoctor/core Error.call = (self, ...args) => { const err = new Error(...args) return Object.assign(self, { message: err.message, stack: err.stack }) } const asciidoctor = require('@asciidoctor/core')() const data = require('gulp-data') const fs = require('fs-extra') const handlebars = require('handlebars') const { obj: map } = require('through2') const merge = require('merge-stream') const ospath = require('path') const path = ospath.posix const requireFromString = require('require-from-string') const template = require('gulp-template') const vfs = require('vinyl-fs') const yaml = require('js-yaml') const ASCIIDOC_ATTRIBUTES = { experimental: '', icons: 'font', sectanchors: '', 'source-highlighter': 'highlight.js' } module.exports = (src, previewSrc, previewDest, sink = () => map()) => (done) => Promise.all([ loadSampleUiModel(previewSrc), toPromise( merge( compileLayouts(src), registerPartials(src), registerHelpers(src), registerTemplatedHelpers(src), copyImages(previewSrc, previewDest) ) ), ]) .then(([baseUiModel, { layouts }]) => [{ ...baseUiModel, env: process.env }, layouts]) .then(([baseUiModel, layouts]) => vfs .src('**/*.adoc', { base: previewSrc, cwd: previewSrc }) .pipe( map((file, enc, next) => { const siteRootPath = path.relative(ospath.dirname(file.path), ospath.resolve(previewSrc)) const uiModel = { ...baseUiModel } uiModel.page = { ...uiModel.page } uiModel.siteRootPath = siteRootPath uiModel.siteRootUrl = path.join(siteRootPath, 'index.html') uiModel.uiRootPath = path.join(siteRootPath, '_') if (file.stem === '404') { uiModel.page = { layout: '404', title: 'Page Not Found' } } else { const doc = asciidoctor.load(file.contents, { safe: 'safe', attributes: ASCIIDOC_ATTRIBUTES }) uiModel.page.attributes = Object.entries(doc.getAttributes()) .filter(([name, val]) => name.startsWith('page-')) .reduce((accum, [name, val]) => { accum[name.substr(5)] = val return accum }, {}) uiModel.page.layout = doc.getAttribute('page-layout', 'default') uiModel.page.title = doc.getDocumentTitle() uiModel.page.contents = Buffer.from(doc.convert()) } file.extname = '.html' try { file.contents = Buffer.from(layouts.get(uiModel.page.layout)(uiModel)) next(null, file) } catch (e) { next(transformHandlebarsError(e, uiModel.page.layout)) } }) ) .pipe(vfs.dest(previewDest)) .on('error', (e) => done) .pipe(sink()) ) function loadSampleUiModel (src) { return fs.readFile(ospath.join(src, 'ui-model.yml'), 'utf8').then((contents) => yaml.safeLoad(contents)) } function registerPartials (src) { return vfs.src('partials/*.hbs', { base: src, cwd: src }).pipe( map((file, enc, next) => { handlebars.registerPartial(file.stem, file.contents.toString()) next() }) ) } function registerHelpers (src) { handlebars.registerHelper('resolvePage', resolvePage) handlebars.registerHelper('resolvePageURL', resolvePageURL) return vfs.src('helpers/*.js', { base: src, cwd: src }).pipe( map((file, enc, next) => { handlebars.registerHelper(file.stem, requireFromString(file.contents.toString())) next() }) ) } function registerTemplatedHelpers (src) { return vfs .src('helpers/*.js.template', { base: src, cwd: src }) .pipe(data(() => ({ manifest: fs.readFileSync('./public/_/data/rev-manifest.json').toString() }))) .pipe(template()) .pipe( map((file, enc, next) => { handlebars.registerHelper(file.stem.replace('.js', ''), requireFromString(file.contents.toString())) next() }) ) } function compileLayouts (src) { const layouts = new Map() return vfs.src('layouts/*.hbs', { base: src, cwd: src }).pipe( map( (file, enc, next) => { const srcName = path.join(src, file.relative) layouts.set(file.stem, handlebars.compile(file.contents.toString(), { preventIndent: true, srcName })) next() }, function (done) { this.push({ layouts }) done() } ) ) } function copyImages (src, dest) { return vfs .src('**/*.{png,svg}', { base: src, cwd: src }) .pipe(vfs.dest(dest)) .pipe(map((file, enc, next) => next())) } function resolvePage (spec, context = {}) { if (spec) return { pub: { url: resolvePageURL(spec) } } } function resolvePageURL (spec, context = {}) { if (spec) return '/' + (spec = spec.split(':').pop()).slice(0, spec.lastIndexOf('.')) + '.html' } function transformHandlebarsError ({ message, stack }, layout) { const m = stack.match(/^ *at Object\.ret \[as (.+?)\]/m) const templatePath = `src/${m ? 'partials/' + m[1] : 'layouts/' + layout}.hbs` const err = new Error(`${message}${~message.indexOf('\n') ? '\n^ ' : ' '}in UI template ${templatePath}`) err.stack = [err.toString()].concat(stack.substr(message.length + 8)).join('\n') return err } function toPromise (stream) { return new Promise((resolve, reject, data = {}) => stream .on('error', reject) .on('data', (chunk) => chunk.constructor === Object && Object.assign(data, chunk)) .on('finish', () => resolve(data)) ) }