@antora/ui-loader
Advanced tools
Comparing version 1.0.0-alpha.5 to 1.0.0-alpha.6
'use strict' | ||
module.exports = Object.freeze({ | ||
SUPPLEMENTAL_FILES_GLOB: '**/*', | ||
UI_CACHE_PATH: '.antora-cache/ui', | ||
UI_CONFIG_FILENAME: 'ui.yml', | ||
}) |
@@ -15,6 +15,8 @@ 'use strict' | ||
const yaml = require('js-yaml') | ||
const vfs = require('vinyl-fs') | ||
const vzip = require('gulp-vinyl-zip') | ||
const { UI_CACHE_PATH, UI_CONFIG_FILENAME } = require('./constants') | ||
const { UI_CACHE_PATH, UI_CONFIG_FILENAME, SUPPLEMENTAL_FILES_GLOB } = require('./constants') | ||
const URI_SCHEME_RX = /^https?:\/\// | ||
const EXT_RX = /\.[a-z]{2,3}$/ | ||
@@ -47,18 +49,13 @@ /** | ||
async function loadUi (playbook) { | ||
const { bundle: bundleUri, startPath, outputDir } = playbook.ui | ||
const startDir = playbook.dir || '.' | ||
const { bundle: bundleUri, startPath, supplementalFiles: supplementalFilesSpec, outputDir } = playbook.ui | ||
let resolveBundle | ||
if (isUrl(bundleUri)) { | ||
// TODO add support for a forced update flag | ||
const cachePath = getCachePath(sha1(bundleUri) + '.zip') | ||
resolveBundle = fs.pathExists(cachePath).then((exists) => { | ||
if (exists) { | ||
return cachePath | ||
} else { | ||
return get(bundleUri, { encoding: null }).then(({ body }) => | ||
fs.outputFile(cachePath, body).then(() => cachePath) | ||
) | ||
} | ||
if (exists) return cachePath | ||
return get(bundleUri, { encoding: null }).then(({ body }) => fs.outputFile(cachePath, body).then(() => cachePath)) | ||
}) | ||
} else { | ||
const localPath = ospath.resolve(playbook.dir || process.cwd(), bundleUri) | ||
const localPath = ospath.resolve(startDir, bundleUri) | ||
resolveBundle = fs.pathExists(localPath).then((exists) => { | ||
@@ -73,20 +70,23 @@ if (exists) { | ||
const bundlePath = await resolveBundle | ||
const files = await Promise.all([ | ||
resolveBundle.then( | ||
(bundlePath) => | ||
new Promise((resolve, reject) => { | ||
vzip | ||
.src(bundlePath) | ||
.on('error', reject) | ||
.pipe(selectFilesStartingFrom(startPath)) | ||
.pipe(bufferizeContents()) | ||
.on('error', reject) | ||
.pipe(collectFiles(resolve)) | ||
}) | ||
), | ||
srcSupplementalFiles(supplementalFilesSpec, startDir), | ||
]).then(([bundleFiles, supplementalFiles]) => mergeFiles(bundleFiles, supplementalFiles)) | ||
const files = await new Promise((resolve, reject) => { | ||
vzip | ||
.src(bundlePath) | ||
.on('error', reject) | ||
.pipe(selectFilesStartingFrom(startPath)) | ||
.pipe(bufferizeContents()) | ||
.on('error', reject) | ||
.pipe(collectFiles(resolve)) | ||
}) | ||
const config = loadConfig(files, outputDir) | ||
return files.reduce((catalog, file) => { | ||
catalog.addFile(classifyFile(file, config)) | ||
return catalog | ||
}, new UiCatalog()) | ||
const catalog = new UiCatalog() | ||
files.forEach((file) => catalog.addFile(classifyFile(file, config))) | ||
return catalog | ||
} | ||
@@ -156,11 +156,81 @@ | ||
function collectFiles (done) { | ||
const accum = [] | ||
return map((file, _, next) => accum.push(file) && next(), () => done(accum)) | ||
const files = new Map() | ||
return map((file, _, next) => files.set(file.path, file) && next(), () => done(files)) | ||
} | ||
function srcSupplementalFiles (filesSpec, startDir) { | ||
if (!filesSpec) { | ||
return new Map() | ||
} else if (Array.isArray(filesSpec)) { | ||
return Promise.all( | ||
filesSpec.reduce((accum, { path: path_, contents: contents_ }) => { | ||
if (!path_) { | ||
return accum | ||
} else if (contents_) { | ||
if (~contents_.indexOf('\n') || !EXT_RX.test(contents_)) { | ||
accum.push(createMemoryFile(path_, contents_)) | ||
} else { | ||
contents_ = ospath.resolve(startDir, contents_) | ||
accum.push( | ||
fs | ||
.stat(contents_) | ||
.then((stat) => fs.readFile(contents_).then((contents) => new File({ path: path_, contents, stat }))) | ||
) | ||
} | ||
} else { | ||
accum.push(createMemoryFile(path_)) | ||
} | ||
return accum | ||
}, []) | ||
).then((files) => files.reduce((accum, file) => accum.set(file.path, file) && accum, new Map())) | ||
} else { | ||
const base = ospath.resolve(startDir, filesSpec) | ||
return fs | ||
.access(base) | ||
.then( | ||
() => | ||
new Promise((resolve, reject) => { | ||
vfs | ||
.src(SUPPLEMENTAL_FILES_GLOB, { base, cwd: base, removeBOM: false }) | ||
.on('error', reject) | ||
.pipe(relativizeFiles()) | ||
.pipe(collectFiles(resolve)) | ||
}) | ||
) | ||
.catch((err) => { | ||
// Q: should we skip unreadable files? | ||
throw new Error('problem encountered while reading ui.supplemental_files: ' + err.message) | ||
}) | ||
} | ||
} | ||
function createMemoryFile (path_, contents = []) { | ||
const stat = new fs.Stats() | ||
stat.size = contents.length | ||
stat.mode = 33188 | ||
return new File({ path: path_, contents: Buffer.from(contents), stat }) | ||
} | ||
function relativizeFiles () { | ||
return map((file, _, next) => { | ||
if (file.isNull()) { | ||
next() | ||
} else { | ||
next( | ||
null, | ||
new File({ path: posixify ? posixify(file.relative) : file.relative, contents: file.contents, stat: file.stat }) | ||
) | ||
} | ||
}) | ||
} | ||
function mergeFiles (files, supplementalFiles) { | ||
if (supplementalFiles.size) supplementalFiles.forEach((file) => files.set(file.path, file)) | ||
return files | ||
} | ||
function loadConfig (files, outputDir) { | ||
const configFileIdx = files.findIndex((file) => file.path === UI_CONFIG_FILENAME) | ||
if (~configFileIdx) { | ||
const configFile = files[configFileIdx] | ||
files.splice(configFileIdx, 1) | ||
const configFile = files.get(UI_CONFIG_FILENAME) | ||
if (configFile) { | ||
files.delete(UI_CONFIG_FILENAME) | ||
const config = yaml.safeLoad(configFile.contents.toString()) | ||
@@ -167,0 +237,0 @@ config.outputDir = outputDir |
{ | ||
"name": "@antora/ui-loader", | ||
"version": "1.0.0-alpha.5", | ||
"version": "1.0.0-alpha.6", | ||
"description": "Downloads a UI bundle, if necessary, and loads the files into a UI catalog for use in an Antora documentation pipeline.", | ||
@@ -27,3 +27,4 @@ "license": "MPL-2.0", | ||
"through2": "^2.0.3", | ||
"vinyl": "^2.1.0" | ||
"vinyl": "^2.1.0", | ||
"vinyl-fs": "^3.0.2" | ||
}, | ||
@@ -30,0 +31,0 @@ "engines": { |
@@ -6,3 +6,4 @@ # Antora UI Loader | ||
[Antora](https://antora.org) is a modular static site generator designed for making documentation sites from AsciiDoc aggregated from versioned content repositories and processed with Asciidoctor. | ||
[Antora](https://antora.org) is a modular static site generator designed for creating documentation sites from AsciiDoc documents. | ||
Its site generator pipeline aggregates documents from versioned content repositories and processes them using [Asciidoctor](http://asciidoctor.org). | ||
@@ -9,0 +10,0 @@ ## Copyright and License |
12232
319
14
10
+ Addedvinyl-fs@^3.0.2