@antora/assembler 1.0.0-beta.8 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/asciidoctor/jsonl-logger.rb +25 -0
- package/lib/assemble-content.js +93 -70
- package/lib/configure.js +36 -15
- package/lib/constants.js +22 -0
- package/lib/filter-component-versions.js +25 -62
- package/lib/load-config.js +74 -39
- package/lib/log-command.js +20 -0
- package/lib/produce-assembly-file.js +375 -407
- package/lib/produce-assembly-files.js +51 -60
- package/lib/select-mutable-attributes.js +1 -0
- package/lib/util/create-resource-key.js +7 -0
- package/lib/util/generate-scoped-id.js +25 -0
- package/lib/util/matcher.js +150 -0
- package/lib/util/parse-resource-ref.js +36 -0
- package/lib/util/resolver.js +36 -0
- package/lib/util/rewriter.js +206 -0
- package/lib/util/rx.js +5 -0
- package/lib/util/unconvert-inline-asciidoc.js +22 -14
- package/package.json +14 -12
- package/lib/util/compute-out.js +0 -57
- package/lib/util/create-asciidoc-file.js +0 -17
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const computeOut = require('./util/compute-out')
|
|
4
|
-
const createAsciiDocFile = require('./util/create-asciidoc-file')
|
|
5
3
|
const filterComponentVersions = require('./filter-component-versions')
|
|
6
4
|
const produceAssemblyFile = require('./produce-assembly-file')
|
|
7
5
|
const selectMutableAttributes = require('./select-mutable-attributes')
|
|
8
6
|
|
|
9
7
|
const ATTR_REF_RX = /\\?\{(\w[\w-]*)\}/g
|
|
10
|
-
const IMAGE_MACRO_RX = /^image::?(.+?)\[(.*?)\]$/
|
|
11
8
|
|
|
12
|
-
function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig,
|
|
13
|
-
const {
|
|
14
|
-
|
|
9
|
+
function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, selectAssemblyProfile) {
|
|
10
|
+
const { assembly: assemblyConfig } = assemblerConfig
|
|
11
|
+
selectAssemblyProfile ??= (componentVersion) => ({
|
|
12
|
+
attributes: assemblyConfig.attributes,
|
|
15
13
|
doctype: assemblyConfig.doctype,
|
|
16
14
|
insertStartPage: assemblyConfig.insertStartPage,
|
|
17
15
|
rootLevel: assemblyConfig.rootLevel,
|
|
@@ -21,40 +19,51 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
|
|
|
21
19
|
embedReferenceStyle: assemblyConfig.embedReferenceStyle,
|
|
22
20
|
linkReferenceStyle: assemblyConfig.linkReferenceStyle,
|
|
23
21
|
dropExplicitXrefText: assemblyConfig.dropExplicitXrefText,
|
|
22
|
+
revdate: assemblyConfig.revdate,
|
|
23
|
+
logger: assemblyConfig.logger, // for tests
|
|
24
24
|
})
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
delete assemblerAsciiDocAttributes.revdate
|
|
28
|
-
delete assemblerAsciiDocAttributes['source-highlighter']
|
|
29
|
-
const publishableFiles = contentCatalog.getFiles().filter((file) => file.out)
|
|
25
|
+
const baseAssemblyAttributes = assemblyConfig.attributes
|
|
26
|
+
const publishableFiles = contentCatalog.getFiles().filter((file) => file.out && file.pub)
|
|
30
27
|
let siteRoot
|
|
31
28
|
const configMdc = assemblerConfig.file ? { file: { path: assemblerConfig.file } } : {}
|
|
32
|
-
return filterComponentVersions(contentCatalog.getComponents(), assemblerConfig.componentVersionFilter
|
|
29
|
+
return filterComponentVersions(contentCatalog.getComponents(), assemblerConfig.componentVersionFilter).reduce(
|
|
33
30
|
(accum, componentVersion) => {
|
|
34
|
-
const assemblyModel =
|
|
35
|
-
|
|
31
|
+
const assemblyModel = selectAssemblyProfile(componentVersion)
|
|
32
|
+
const { attributes: assemblyAttributes, logger, navigation, rootLevel } = assemblyModel
|
|
33
|
+
if (!navigation) return accum
|
|
34
|
+
const contextualLogger = logger ? { warn: logger.warn.bind(logger, configMdc) } : undefined
|
|
36
35
|
const { name: componentName, version, title } = componentVersion
|
|
37
36
|
const componentVersionAsciiDocConfig = getAsciiDocConfigWithAsciidoctorReducerExtension(componentVersion)
|
|
37
|
+
let sourceHighlighter
|
|
38
|
+
if ('source-highlighter' in assemblyAttributes) {
|
|
39
|
+
sourceHighlighter = assemblyAttributes['source-highlighter']
|
|
40
|
+
delete assemblyAttributes['source-highlighter']
|
|
41
|
+
}
|
|
38
42
|
const mergedAsciiDocAttributes = collateAsciiDocAttributes(
|
|
39
|
-
Object.assign({
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
Object.assign({}, componentVersionAsciiDocConfig.attributes),
|
|
44
|
+
assemblyAttributes,
|
|
45
|
+
contextualLogger
|
|
42
46
|
)
|
|
43
47
|
const mergedAsciiDocConfig = Object.assign({}, componentVersionAsciiDocConfig, {
|
|
44
48
|
attributes: mergedAsciiDocAttributes,
|
|
45
49
|
})
|
|
46
|
-
assemblyModel.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
assemblyModel.filetype = baseAssemblyAttributes['assembler-filetype']
|
|
51
|
+
const outDirname = (assemblyModel.outDirname = contentCatalog.createFile({
|
|
52
|
+
src: {
|
|
53
|
+
component: componentName,
|
|
54
|
+
version,
|
|
55
|
+
componentVersion,
|
|
56
|
+
module: 'ROOT',
|
|
57
|
+
family: 'export',
|
|
58
|
+
relative: 'index.adoc',
|
|
59
|
+
},
|
|
60
|
+
}).out.dirname)
|
|
61
|
+
assemblyModel.pubRoot = outDirname ? '/' + outDirname : ''
|
|
53
62
|
assemblyModel.siteRoot =
|
|
54
63
|
siteRoot === undefined
|
|
55
64
|
? (siteRoot ??= ((val) => {
|
|
56
65
|
if (!val) return null
|
|
57
|
-
if (val.charAt(val.length - 1) === '/') val = val.
|
|
66
|
+
if (val.charAt(val.length - 1) === '/') val = val.substring(0, val.length - 1)
|
|
58
67
|
if (!val || val.charAt() === '/') return { path: val }
|
|
59
68
|
return { url: val, path: extractUrlPath(val) }
|
|
60
69
|
})(mergedAsciiDocAttributes['site-url'] || mergedAsciiDocAttributes['primary-site-url']))
|
|
@@ -64,21 +73,10 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
|
|
|
64
73
|
if (linkRefStyle === 'absolute' && siteRoot?.url == null) linkRefStyle = 'root-relative'
|
|
65
74
|
if (linkRefStyle === 'root-relative' && siteRoot?.path == null) linkRefStyle = 'relative'
|
|
66
75
|
assemblyModel.linkReferenceStyle = linkRefStyle
|
|
67
|
-
} else {
|
|
76
|
+
} else if (!(assemblyModel.filetype === 'pdf' && assemblyModel.linkReferenceStyle === 'relative')) {
|
|
68
77
|
assemblyModel.linkReferenceStyle = 'absolute'
|
|
69
78
|
}
|
|
70
|
-
const auxiliaryImages = new Set()
|
|
71
|
-
Object.entries(mergedAsciiDocAttributes).forEach(([name, val]) => {
|
|
72
|
-
const match = name.endsWith('-image') && val.startsWith('image:') && IMAGE_MACRO_RX.exec(val)
|
|
73
|
-
if (!(match && isResourceRef(match[1]))) return
|
|
74
|
-
// Q should we allow image to be resolved relative to component version?
|
|
75
|
-
const image = contentCatalog.resolveResource(match[1], undefined, 'image', ['image'])
|
|
76
|
-
if (!image?.out) return
|
|
77
|
-
mergedAsciiDocAttributes[name] = `image:${image.out.path}[${match[2]}]`
|
|
78
|
-
auxiliaryImages.add(image)
|
|
79
|
-
})
|
|
80
79
|
const rootEntry = { content: title }
|
|
81
|
-
const { navigation, rootLevel } = assemblyModel
|
|
82
80
|
let startPage =
|
|
83
81
|
'startPage' in componentVersion
|
|
84
82
|
? componentVersion.startPage
|
|
@@ -86,23 +84,24 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
|
|
|
86
84
|
if (startPage && startPage.src.component === componentName && startPage.src.version === version) {
|
|
87
85
|
if (assemblyModel.insertStartPage) {
|
|
88
86
|
const navtitle = startPage.asciidoc?.navtitle || rootEntry.content
|
|
89
|
-
Object.assign(rootEntry, { navtitle, url: startPage.pub.url, urlType: 'internal' })
|
|
87
|
+
Object.assign(rootEntry, { navtitle, url: startPage.pub.url, urlType: 'internal', roles: ['page'] })
|
|
90
88
|
}
|
|
91
89
|
} else {
|
|
92
90
|
// Q: should we always use a reference page as startPage for computing mutableAttributes?
|
|
93
|
-
startPage =
|
|
91
|
+
startPage = contentCatalog.createFile({
|
|
94
92
|
src: {
|
|
95
93
|
component: componentVersion.name,
|
|
96
94
|
version: componentVersion.version,
|
|
95
|
+
componentVersion,
|
|
97
96
|
module: 'ROOT',
|
|
98
97
|
family: 'page',
|
|
99
|
-
relative: '.
|
|
98
|
+
relative: 'index.adoc',
|
|
100
99
|
origin: (componentVersion.origins || [])[0],
|
|
101
100
|
},
|
|
102
101
|
})
|
|
102
|
+
startPage.path = ['modules', startPage.src.module, startPage.src.family + 's', startPage.src.relative].join('/')
|
|
103
103
|
}
|
|
104
104
|
const mutableAttributes = selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
|
|
105
|
-
delete mutableAttributes.doctype // Q: should this be in selectMutableAttributes?
|
|
106
105
|
prepareOutlines(navigation, rootEntry, rootLevel).reduce((any, outline) => {
|
|
107
106
|
const assemblyFile = produceAssemblyFile(
|
|
108
107
|
loadAsciiDoc,
|
|
@@ -115,16 +114,14 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
|
|
|
115
114
|
assemblyModel
|
|
116
115
|
)
|
|
117
116
|
if (!assemblyFile) return any
|
|
118
|
-
if
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
// NOTE restore source highlighter for conversion if defined in Assembler config
|
|
118
|
+
if (sourceHighlighter !== undefined) {
|
|
119
|
+
assemblyFile.asciidoc.attributes['source-highlighter'] = sourceHighlighter
|
|
120
|
+
} else if (assemblyModel.filetype !== 'html') {
|
|
121
|
+
delete assemblyFile.asciidoc.attributes['source-highlighter']
|
|
121
122
|
}
|
|
122
|
-
accum.push(assemblyFile)
|
|
123
|
-
return true
|
|
123
|
+
return !!accum.push(assemblyFile)
|
|
124
124
|
}, false)
|
|
125
|
-
sourceHighlighter
|
|
126
|
-
? (mergedAsciiDocAttributes['source-highlighter'] = sourceHighlighter)
|
|
127
|
-
: delete mergedAsciiDocAttributes['source-highlighter']
|
|
128
125
|
return accum
|
|
129
126
|
},
|
|
130
127
|
[]
|
|
@@ -150,15 +147,11 @@ function includedInNav (items, url) {
|
|
|
150
147
|
return items.find((it) => it.url === url || includedInNav(it.items || [], url))
|
|
151
148
|
}
|
|
152
149
|
|
|
153
|
-
function isResourceRef (target) {
|
|
154
|
-
return ~target.indexOf(':') && !(~target.indexOf('://') || (target.startsWith('data:') && ~target.indexOf(',')))
|
|
155
|
-
}
|
|
156
|
-
|
|
157
150
|
function prepareOutlines (navigation, rootEntry, rootLevel) {
|
|
158
151
|
const singleEntry = navigation.length === 1
|
|
159
152
|
const items = navigation.reduce((accum, it) => {
|
|
160
153
|
if (!it.content || (singleEntry && it.url ? it.url === rootEntry.url : it.content === rootEntry.content)) {
|
|
161
|
-
accum.push(...it.items)
|
|
154
|
+
it.items && accum.push(...it.items)
|
|
162
155
|
} else {
|
|
163
156
|
accum.push(it)
|
|
164
157
|
}
|
|
@@ -166,7 +159,7 @@ function prepareOutlines (navigation, rootEntry, rootLevel) {
|
|
|
166
159
|
}, [])
|
|
167
160
|
if (rootLevel === 0) {
|
|
168
161
|
if (rootEntry.url && includedInNav(items, rootEntry.url)) {
|
|
169
|
-
for (const p of ['url', 'urlType']) delete rootEntry[p]
|
|
162
|
+
for (const p of ['roles', 'url', 'urlType']) delete rootEntry[p]
|
|
170
163
|
}
|
|
171
164
|
return [Object.assign(rootEntry, { index: true, items })]
|
|
172
165
|
}
|
|
@@ -177,20 +170,18 @@ function prepareOutlines (navigation, rootEntry, rootLevel) {
|
|
|
177
170
|
return items
|
|
178
171
|
}
|
|
179
172
|
|
|
180
|
-
function collateAsciiDocAttributes (collated, additional,
|
|
173
|
+
function collateAsciiDocAttributes (collated, additional, logger) {
|
|
181
174
|
Object.entries(additional).forEach(([name, val]) => {
|
|
182
175
|
if (val && val.constructor === String) {
|
|
183
176
|
let alias
|
|
184
177
|
val = val.replace(ATTR_REF_RX, (ref, refname) => {
|
|
185
|
-
if (ref.charAt() === '\\') return ref.
|
|
178
|
+
if (ref.charAt() === '\\') return ref.substring(1)
|
|
186
179
|
const refval = collated[refname]
|
|
187
180
|
if (refval == null || refval === false) {
|
|
188
181
|
if (refname in collated && ref === val) {
|
|
189
182
|
alias = refval
|
|
190
183
|
} else if (collated['attribute-missing'] === 'warn') {
|
|
191
|
-
|
|
192
|
-
logger.warn(mdc, "Skipping reference to missing attribute '%s' in value of '%s' attribute", refname, name)
|
|
193
|
-
}
|
|
184
|
+
logger?.warn("Skipping reference to missing attribute '%s' in value of '%s' attribute", refname, name)
|
|
194
185
|
}
|
|
195
186
|
return ref
|
|
196
187
|
}
|
|
@@ -17,6 +17,7 @@ function selectMutableAttributes (loadAsciiDoc, contentCatalog, referencePage, a
|
|
|
17
17
|
]
|
|
18
18
|
// we could consider using an Asciidoctor extension here to grab attributes passed via the API instead
|
|
19
19
|
const immutableAttributeNames = doc.attribute_overrides.$keys()['$-'](additionalMutableNames)
|
|
20
|
+
immutableAttributeNames.push('doctype')
|
|
20
21
|
return Object.entries(doc.getAttributes()).reduce((accum, [name, val]) => {
|
|
21
22
|
if (!immutableAttributeNames.includes(name)) accum[name] = val
|
|
22
23
|
return accum
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const HTML_RESERVED_ID_NAMES = 'content header footnotes footer footer-text premable toc toctitle'.split(' ')
|
|
4
|
+
|
|
5
|
+
function generateScopedId (componentSrc, componentVersion, separators, filetype, asXrefTarget) {
|
|
6
|
+
let { component, module: mod, relative } = componentSrc
|
|
7
|
+
let id = relative.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
|
|
8
|
+
let { coordinate: coordinateSeparator, prefix: prefixSeparator } = separators
|
|
9
|
+
if (component !== componentVersion.name) {
|
|
10
|
+
id = [component, mod === 'ROOT' ? '' : mod, id].join(coordinateSeparator)
|
|
11
|
+
} else if (mod !== 'ROOT') {
|
|
12
|
+
if (asXrefTarget && coordinateSeparator === ':' && /(?:pass|stem)$/.test(mod) && /^[a-z]+(?:,[a-z-]+)*$/.test(id)) {
|
|
13
|
+
prefixSeparator = ''
|
|
14
|
+
mod = mod.replace(/(?:pass|stem)$/, '\\$&')
|
|
15
|
+
}
|
|
16
|
+
id = mod + coordinateSeparator + id
|
|
17
|
+
} else if (filetype === 'html' && HTML_RESERVED_ID_NAMES.includes(id)) {
|
|
18
|
+
id = prefixSeparator + id
|
|
19
|
+
prefixSeparator = ''
|
|
20
|
+
}
|
|
21
|
+
if (prefixSeparator && !/^[\p{Alpha}_:]/u.test(id)) id = prefixSeparator + id
|
|
22
|
+
return id
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = generateScopedId
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const SPECIAL = { '.': true, '[': true, ']': true, '^': true, $: true, '}': true, ')': true, '|': true }
|
|
4
|
+
const QUANTIFIER_ALTERNATE = { '*': '.*?', '+': '\\+', '?': '.' }
|
|
5
|
+
const QUANTIFIERS = { '*': true, '+': true, '?': true, '@': true, '!': true }
|
|
6
|
+
const RS = '\x1e'
|
|
7
|
+
|
|
8
|
+
const BRACE_RANGE_RX = /^\{(?:([a-z])\.\.([a-z])|(0|[1-9][0-9]*)\.\.(0|[1-9][0-9]*))\}/
|
|
9
|
+
|
|
10
|
+
function filterCollection (candidates, patterns, opts = {}) {
|
|
11
|
+
const rxs = patterns.map(opts.compilePattern ?? compilePattern)
|
|
12
|
+
const test = opts.test ?? ((rx, candidate) => rx.test(candidate))
|
|
13
|
+
return candidates.filter((candidate) => {
|
|
14
|
+
let matched = false
|
|
15
|
+
for (const rx of rxs) {
|
|
16
|
+
let voteIfMatched = true
|
|
17
|
+
if (matched) {
|
|
18
|
+
if (!rx.negated) continue
|
|
19
|
+
voteIfMatched = false
|
|
20
|
+
} else if (rx.negated) {
|
|
21
|
+
voteIfMatched = false
|
|
22
|
+
}
|
|
23
|
+
if (test(rx, candidate)) matched = voteIfMatched
|
|
24
|
+
}
|
|
25
|
+
return matched
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function compilePattern (str) {
|
|
30
|
+
const negated = str.charAt() === '!' ? typeof (str = str.substring(1)) === 'string' : false
|
|
31
|
+
if (str === '**') return Object.assign(new RegExp(), { globstar: true, negated })
|
|
32
|
+
if (str === '*') return Object.assign(new RegExp(), { star: true, negated })
|
|
33
|
+
const buffer = { result: '', brace: undefined, group: undefined }
|
|
34
|
+
let target = 'result'
|
|
35
|
+
let nestedTarget
|
|
36
|
+
for (let i = 0, len = str.length; i < len; i++) {
|
|
37
|
+
const c = str[i]
|
|
38
|
+
if (target === 'brace') {
|
|
39
|
+
if (c === '}') {
|
|
40
|
+
const braceBuffer = buffer.brace
|
|
41
|
+
let braceResult
|
|
42
|
+
if (~braceBuffer.indexOf(RS)) {
|
|
43
|
+
braceResult = braceBuffer === RS ? '' : '(?:' + braceBuffer.replace(/\x1e/g, '|') + ')'
|
|
44
|
+
} else {
|
|
45
|
+
const m = BRACE_RANGE_RX.exec('{' + braceBuffer.replace('\\.\\.', '..') + '}')
|
|
46
|
+
braceResult = m[1] != null ? `[${m[1]}-${m[2]}]` : rangeRe(+m[3], +m[4])
|
|
47
|
+
}
|
|
48
|
+
buffer[(target = nestedTarget === 'group' ? 'group' : 'result')] += braceResult
|
|
49
|
+
buffer.brace = nestedTarget = undefined
|
|
50
|
+
continue
|
|
51
|
+
} else if (c === ',') {
|
|
52
|
+
buffer.brace += RS
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
} else if (target === 'group') {
|
|
56
|
+
if (c === ')') {
|
|
57
|
+
const { group: groupBuffer, groupQuantifier } = buffer
|
|
58
|
+
let groupResult = '(?:' + groupBuffer + ')'
|
|
59
|
+
if (groupQuantifier === '!') {
|
|
60
|
+
groupResult = '(?!' + groupResult + '.*?)'
|
|
61
|
+
} else if (groupQuantifier !== '@') {
|
|
62
|
+
groupResult += groupQuantifier
|
|
63
|
+
}
|
|
64
|
+
buffer[(target = nestedTarget === 'brace' ? 'brace' : 'result')] += groupResult
|
|
65
|
+
buffer.group = buffer.groupQuantifier = nestedTarget = undefined
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (SPECIAL[c]) {
|
|
70
|
+
buffer[target] += '\\' + c
|
|
71
|
+
} else if (c === '(' || QUANTIFIERS[c]) {
|
|
72
|
+
const hasQuantifier = c !== '('
|
|
73
|
+
const atGroupStart = hasQuantifier ? str[i + 1] === '(' : true
|
|
74
|
+
if (target !== 'group' && atGroupStart && isGroup(str.substring(hasQuantifier ? i + 1 : i))) {
|
|
75
|
+
if (target === 'brace') nestedTarget = 'brace'
|
|
76
|
+
buffer[(target = 'group')] = ''
|
|
77
|
+
buffer.groupQuantifier = hasQuantifier ? ++i > 0 && c : ''
|
|
78
|
+
} else if (hasQuantifier) {
|
|
79
|
+
buffer[target] += (QUANTIFIER_ALTERNATE[c] ?? c) + (atGroupStart ? ++i > 0 && '\\(' : '')
|
|
80
|
+
} else {
|
|
81
|
+
buffer[target] += '\\('
|
|
82
|
+
}
|
|
83
|
+
} else if (c === '{') {
|
|
84
|
+
if (target !== 'brace' && isBrace(str.substring(i))) {
|
|
85
|
+
if (target === 'group') nestedTarget = 'group'
|
|
86
|
+
buffer[(target = 'brace')] = ''
|
|
87
|
+
} else {
|
|
88
|
+
buffer[target] += '\\' + c
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
buffer[target] += c
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return Object.assign(new RegExp('^' + cleanup(buffer, target, nestedTarget) + '$'), { negated })
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function cleanup (buffer, target, nestedTarget) {
|
|
98
|
+
if (target === 'result') return buffer.result
|
|
99
|
+
if (target === 'brace') {
|
|
100
|
+
buffer.result += '\\{' + buffer.brace.replace(/\x1e/g, ',')
|
|
101
|
+
buffer.brace = undefined
|
|
102
|
+
} else if (target === 'group') {
|
|
103
|
+
const groupQuantifier = buffer.groupQuantifier
|
|
104
|
+
buffer.result += (QUANTIFIER_ALTERNATE[groupQuantifier] ?? groupQuantifier) + '\\(' + buffer.group
|
|
105
|
+
buffer.group = buffer.groupQuantifier = undefined
|
|
106
|
+
}
|
|
107
|
+
return nestedTarget ? cleanup(buffer, nestedTarget) : buffer.result
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isBrace (str) {
|
|
111
|
+
const endBraceIdx = str.indexOf('}')
|
|
112
|
+
const candidate = ~endBraceIdx ? str.substring(0, endBraceIdx + 1) : ''
|
|
113
|
+
if (!candidate || ~candidate.indexOf('{', 1)) return false
|
|
114
|
+
return ~candidate.indexOf(',') || BRACE_RANGE_RX.test(candidate)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isGroup (str) {
|
|
118
|
+
const endBraceIdx = str.indexOf(')')
|
|
119
|
+
const candidate = ~endBraceIdx ? str.substring(0, endBraceIdx + 1) : ''
|
|
120
|
+
return candidate ? !~candidate.indexOf('(', 1) : false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function rangeRe (min, max) {
|
|
124
|
+
if (min === max) return String(min)
|
|
125
|
+
min > max && ([min, max] = [max, min])
|
|
126
|
+
if (min < 10 && max < 10) return `[${min}${min + 1 === max ? '' : '-'}${max}]`
|
|
127
|
+
const result = []
|
|
128
|
+
let minStr, minStrLen, toStr
|
|
129
|
+
while (min <= max) {
|
|
130
|
+
let to = (minStrLen = (minStr = String(min)).length) < String(max).length ? 10 ** minStrLen - 1 : max
|
|
131
|
+
const limit = 10 ** minStrLen
|
|
132
|
+
for (let i = 10; i <= limit; i *= 10) {
|
|
133
|
+
const candidate = min + (i - (min % i) || i) - 1
|
|
134
|
+
if (candidate > max) break
|
|
135
|
+
to = candidate
|
|
136
|
+
}
|
|
137
|
+
toStr = String(to)
|
|
138
|
+
let rangesForChunk = ''
|
|
139
|
+
for (let i = 0, len = minStr.length; i < len; i++) {
|
|
140
|
+
const lhs = minStr[i]
|
|
141
|
+
const rhs = toStr[i]
|
|
142
|
+
rangesForChunk += lhs === rhs ? lhs : `[${lhs}${+lhs + 1 === +rhs ? '' : '-'}${rhs}]`
|
|
143
|
+
}
|
|
144
|
+
result.push(rangesForChunk)
|
|
145
|
+
min = to + 1
|
|
146
|
+
}
|
|
147
|
+
return result.length > 1 ? `(?:${result.join('|')})` : result[0]
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = { filterCollection, compilePattern }
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function parseResourceRef (ref, ctx = {}, family = undefined, contentCatalog = undefined) {
|
|
4
|
+
const atIdx = ref.indexOf('@')
|
|
5
|
+
let firstColonIdx = ref.indexOf(':')
|
|
6
|
+
let component, version, module_
|
|
7
|
+
if (~atIdx && (~firstColonIdx ? atIdx < firstColonIdx : true)) {
|
|
8
|
+
if ((version = ref.substring(0, atIdx)) === '_') version = ''
|
|
9
|
+
ref = ref.substring(atIdx + 1)
|
|
10
|
+
if (~firstColonIdx) firstColonIdx -= atIdx + 1
|
|
11
|
+
}
|
|
12
|
+
const addColons = ~firstColonIdx ? (~ref.indexOf(':', firstColonIdx + 1) ? '' : ':') : '::'
|
|
13
|
+
const segments = (addColons + ref).split(':')
|
|
14
|
+
if ((component = segments[0])) {
|
|
15
|
+
module_ = segments[1] || 'ROOT'
|
|
16
|
+
version ??= contentCatalog?.getComponent(component)?.latest.version
|
|
17
|
+
} else {
|
|
18
|
+
component = ctx.component
|
|
19
|
+
version ??= ctx.version
|
|
20
|
+
module_ = segments[1] || ctx.module || 'ROOT'
|
|
21
|
+
}
|
|
22
|
+
let relative = segments.length > 3 ? segments.slice(2).join(':') : segments[2]
|
|
23
|
+
const dollarIdx = relative.indexOf('$')
|
|
24
|
+
if (~dollarIdx) {
|
|
25
|
+
family = relative.substring(0, dollarIdx) || family
|
|
26
|
+
relative = relative.substring(dollarIdx + 1)
|
|
27
|
+
}
|
|
28
|
+
if (relative.charAt() === '.' && relative.charAt(1) === '/') {
|
|
29
|
+
const ctxRelative = ctx.relative
|
|
30
|
+
const topic = ctxRelative ? ctxRelative.substring(0, (ctxRelative.lastIndexOf('/') + 1 || 1) - 1) : undefined
|
|
31
|
+
relative = (topic ? topic + '/' : '') + relative.substring(2)
|
|
32
|
+
}
|
|
33
|
+
return { component, version, module: module_, family, relative }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = parseResourceRef
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const path = require('node:path/posix')
|
|
4
|
+
|
|
5
|
+
function resolveEmbedTarget (resource, outDirname, referenceStyle, escapeForInline) {
|
|
6
|
+
const target =
|
|
7
|
+
referenceStyle === 'output-relative' ? resource.out.path : path.relative(outDirname + '/', resource.out.path)
|
|
8
|
+
return escapeForInline ? target.replace(/_/g, '{underscore}') : target
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function resolveLinkTarget (resource, siteRoot, pubRoot, referenceStyle, escapeForInline, ensurePrefix = true) {
|
|
12
|
+
let target
|
|
13
|
+
if (resource.site?.url) {
|
|
14
|
+
target = ['', resource.pub.url]
|
|
15
|
+
} else {
|
|
16
|
+
switch (referenceStyle) {
|
|
17
|
+
case 'absolute':
|
|
18
|
+
target = ['', siteRoot.url + resource.pub.url]
|
|
19
|
+
break
|
|
20
|
+
case 'root-relative':
|
|
21
|
+
target = [ensurePrefix ? 'link:' : '', siteRoot.path + resource.pub.url]
|
|
22
|
+
break
|
|
23
|
+
default:
|
|
24
|
+
target = [ensurePrefix ? 'link:' : '', computeRelativeUrl(pubRoot + '/', resource.pub.url)]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (escapeForInline) target[1] = target[1].replace(/_/g, '{underscore}')
|
|
28
|
+
return target.join('')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function computeRelativeUrl (from, to) {
|
|
32
|
+
const rel = path.relative(from, to)
|
|
33
|
+
return to.charAt(to.length - 1) === '/' ? rel + '/' : rel
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = { resolveEmbedTarget, resolveLinkTarget }
|