You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
8.1 KiB
204 lines
8.1 KiB
// https://gitlab.com/antora/antora/-/issues/132#note_712132072 |
|
'use strict' |
|
|
|
const { posix: path } = require('path') |
|
|
|
module.exports.register = function({ config }) { |
|
this.on('contentClassified', ({ contentCatalog }) => { |
|
contentCatalog.getComponents().forEach(component => { |
|
const componentName = component.name; |
|
const generationToVersion = new Map(); |
|
component.versions.forEach(version => { |
|
const generation = getGeneration(version.version); |
|
const original = generationToVersion.get(generation); |
|
if (original === undefined || (original.prerelease && !version.prerelease)) { |
|
generationToVersion.set(generation, version); |
|
} |
|
}); |
|
|
|
const versionToGeneration = Array.from(generationToVersion.entries()).reduce((acc, entry) => { |
|
const [ generation, version ] = entry; |
|
acc.set(version.version, generation); |
|
return acc; |
|
}, new Map()); |
|
|
|
contentCatalog.findBy({ component: componentName }).forEach((file) => { |
|
const candidateVersion = file.src.version; |
|
if (versionToGeneration.has(candidateVersion)) { |
|
const generation = versionToGeneration.get(candidateVersion); |
|
if (file.out) { |
|
if (file.out) { |
|
file.out.dirname = file.out.dirname.replace(candidateVersion, generation) |
|
file.out.path = file.out.path.replace(candidateVersion, generation); |
|
} |
|
} |
|
if (file.pub) { |
|
file.pub.url = file.pub.url.replace(candidateVersion, generation) |
|
} |
|
} |
|
}); |
|
versionToGeneration.forEach((generation, mappedVersion) => { |
|
contentCatalog.getComponent(componentName).versions.filter(version => version.version === mappedVersion).forEach((version) => { |
|
version.url = version.url.replace(mappedVersion, generation); |
|
}) |
|
const symbolicVersionAlias = createSymbolicVersionAlias( |
|
componentName, |
|
mappedVersion, |
|
generation, |
|
'redirect:to' |
|
) |
|
symbolicVersionAlias.src.version = generation; |
|
contentCatalog.addFile(symbolicVersionAlias); |
|
}); |
|
}) |
|
}) |
|
} |
|
|
|
function createSymbolicVersionAlias (component, version, symbolicVersionSegment, strategy) { |
|
if (symbolicVersionSegment == null || symbolicVersionSegment === version) return |
|
const family = 'alias' |
|
const baseVersionAliasSrc = { component, module: 'ROOT', family, relative: '', basename: '', stem: '', extname: '' } |
|
const symbolicVersionAliasSrc = Object.assign({}, baseVersionAliasSrc, { version: symbolicVersionSegment }) |
|
const symbolicVersionAlias = { |
|
src: symbolicVersionAliasSrc, |
|
pub: computePub( |
|
symbolicVersionAliasSrc, |
|
computeOut(symbolicVersionAliasSrc, family, symbolicVersionSegment), |
|
family |
|
), |
|
} |
|
const originalVersionAliasSrc = Object.assign({}, baseVersionAliasSrc, { version }) |
|
const originalVersionSegment = computeVersionSegment(component, version, 'original') |
|
const originalVersionAlias = { |
|
src: originalVersionAliasSrc, |
|
pub: computePub( |
|
originalVersionAliasSrc, |
|
computeOut(originalVersionAliasSrc, family, originalVersionSegment), |
|
family |
|
), |
|
} |
|
if (strategy === 'redirect:to') { |
|
originalVersionAlias.out = undefined |
|
originalVersionAlias.rel = symbolicVersionAlias |
|
return originalVersionAlias |
|
} else { |
|
symbolicVersionAlias.out = undefined |
|
symbolicVersionAlias.rel = originalVersionAlias |
|
return symbolicVersionAlias |
|
} |
|
} |
|
|
|
|
|
function computeOut (src, family, version, htmlUrlExtensionStyle) { |
|
let { component, module: module_, basename, extname, relative, stem } = src |
|
if (component === 'ROOT') component = '' |
|
if (module_ === 'ROOT') module_ = '' |
|
let indexifyPathSegment = '' |
|
let familyPathSegment = '' |
|
|
|
if (family === 'page') { |
|
if (stem !== 'index' && htmlUrlExtensionStyle === 'indexify') { |
|
basename = 'index.html' |
|
indexifyPathSegment = stem |
|
} else if (extname === '.adoc') { |
|
basename = stem + '.html' |
|
} |
|
} else if (family === 'image') { |
|
familyPathSegment = '_images' |
|
} else if (family === 'attachment') { |
|
familyPathSegment = '_attachments' |
|
} |
|
const modulePath = path.join(component, version, module_) |
|
const dirname = path.join(modulePath, familyPathSegment, path.dirname(relative), indexifyPathSegment) |
|
const path_ = path.join(dirname, basename) |
|
const moduleRootPath = path.relative(dirname, modulePath) || '.' |
|
const rootPath = path.relative(dirname, '') || '.' |
|
|
|
return { dirname, basename, path: path_, moduleRootPath, rootPath } |
|
} |
|
|
|
function computePub (src, out, family, version, htmlUrlExtensionStyle) { |
|
const pub = {} |
|
let url |
|
if (family === 'nav') { |
|
const component = src.component || 'ROOT' |
|
const urlSegments = component === 'ROOT' ? [] : [component] |
|
if (version) urlSegments.push(version) |
|
const module_ = src.module || 'ROOT' |
|
if (module_ !== 'ROOT') urlSegments.push(module_) |
|
// an artificial URL used for resolving page references in navigation model |
|
url = '/' + urlSegments.join('/') + '/' |
|
pub.moduleRootPath = '.' |
|
} else if (family === 'page') { |
|
const urlSegments = out.path.split('/') |
|
const lastUrlSegmentIdx = urlSegments.length - 1 |
|
if (htmlUrlExtensionStyle === 'drop') { |
|
// drop just the .html extension or, if the filename is index.html, the whole segment |
|
const lastUrlSegment = urlSegments[lastUrlSegmentIdx] |
|
urlSegments[lastUrlSegmentIdx] = |
|
lastUrlSegment === 'index.html' ? '' : lastUrlSegment.substr(0, lastUrlSegment.length - 5) |
|
} else if (htmlUrlExtensionStyle === 'indexify') { |
|
urlSegments[lastUrlSegmentIdx] = '' |
|
} |
|
url = '/' + urlSegments.join('/') |
|
} else { |
|
url = '/' + out.path |
|
if (family === 'alias' && !src.relative.length) pub.splat = true |
|
} |
|
|
|
pub.url = ~url.indexOf(' ') ? url.replace(SPACE_RX, '%20') : url |
|
|
|
if (out) { |
|
pub.moduleRootPath = out.moduleRootPath |
|
pub.rootPath = out.rootPath |
|
} |
|
|
|
return pub |
|
} |
|
|
|
function computeVersionSegment (name, version, mode) { |
|
if (mode === 'original') return !version || version === 'master' ? '' : version |
|
const strategy = this.latestVersionUrlSegmentStrategy |
|
// NOTE: special exception; revisit in Antora 3 |
|
if (!version || version === 'master') { |
|
if (mode !== 'alias') return '' |
|
if (strategy === 'redirect:to') return |
|
} |
|
if (strategy === 'redirect:to' || strategy === (mode === 'alias' ? 'redirect:from' : 'replace')) { |
|
const component = this.getComponent(name) |
|
const componentVersion = component && this.getComponentVersion(component, version) |
|
if (componentVersion) { |
|
const segment = |
|
componentVersion === component.latest |
|
? this.latestVersionUrlSegment |
|
: componentVersion === component.latestPrerelease |
|
? this.latestPrereleaseVersionUrlSegment |
|
: undefined |
|
return segment == null ? version : segment |
|
} |
|
} |
|
return version |
|
} |
|
|
|
function getGeneration(version) { |
|
if (!version) return version; |
|
const firstIndex = version.indexOf('.') |
|
if (firstIndex < 0) { |
|
return version; |
|
} |
|
const secondIndex = version.indexOf('.', firstIndex + 1); |
|
const result = version.substr(0, secondIndex); |
|
return result; |
|
} |
|
|
|
function out(args) { |
|
console.log(JSON.stringify(args, no_data, 2)); |
|
} |
|
|
|
|
|
function no_data(key, value) { |
|
if (key == "data" || key == "files") { |
|
return value ? "__data__" : value; |
|
} |
|
return value; |
|
}
|
|
|