@antora/ui-loader 3.0.0-alpha.9 → 3.0.0-beta.4
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/README.md +1 -1
- package/lib/constants.js +1 -1
- package/lib/file.js +1 -1
- package/lib/load-ui.js +88 -63
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ The UI Loader is a component in Antora responsible for downloading and caching t
|
|
|
4
4
|
The UI files are combined with the content files at the end of the Antora documentation pipeline to produce a complete site.
|
|
5
5
|
|
|
6
6
|
[Antora](https://antora.org) is a modular static site generator designed for creating documentation sites from AsciiDoc documents.
|
|
7
|
-
Its site generator
|
|
7
|
+
Its site generator aggregates documents from versioned content repositories and processes them using [Asciidoctor](https://asciidoctor.org).
|
|
8
8
|
|
|
9
9
|
## Copyright and License
|
|
10
10
|
|
package/lib/constants.js
CHANGED
package/lib/file.js
CHANGED
package/lib/load-ui.js
CHANGED
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { compile: bracesToGroup } = require('braces')
|
|
3
4
|
const camelCaseKeys = require('camelcase-keys')
|
|
4
|
-
const concat = require('simple-concat')
|
|
5
5
|
const { createHash } = require('crypto')
|
|
6
6
|
const expandPath = require('@antora/expand-path-helper')
|
|
7
7
|
const { File, MemoryFile, ReadableFile } = require('./file')
|
|
8
8
|
const { promises: fsp } = require('fs')
|
|
9
9
|
const { concat: get } = require('simple-get')
|
|
10
10
|
const getCacheDir = require('cache-directory')
|
|
11
|
-
const
|
|
11
|
+
const globStream = require('glob-stream')
|
|
12
12
|
const ospath = require('path')
|
|
13
13
|
const { posix: path } = ospath
|
|
14
|
+
const picomatch = require('picomatch')
|
|
14
15
|
const posixify = ospath.sep === '\\' ? (p) => p.replace(/\\/g, '/') : undefined
|
|
15
|
-
const { Transform } = require('stream')
|
|
16
|
-
const
|
|
16
|
+
const { pipeline, Transform, Writable } = require('stream')
|
|
17
|
+
const forEach = (write, final) => new Writable({ objectMode: true, write, final })
|
|
18
|
+
const map = (transform) => new Transform({ objectMode: true, transform })
|
|
17
19
|
const UiCatalog = require('./ui-catalog')
|
|
18
20
|
const yaml = require('js-yaml')
|
|
19
|
-
const vfs = require('vinyl-fs')
|
|
20
21
|
const vzip = require('gulp-vinyl-zip')
|
|
21
22
|
|
|
23
|
+
const STATIC_FILE_MATCHER_OPTS = {
|
|
24
|
+
expandRange: (begin, end, step, opts) => bracesToGroup(opts ? `{${begin}..${end}..${step}}` : `{${begin}..${end}}`),
|
|
25
|
+
fastpaths: false,
|
|
26
|
+
nobracket: true,
|
|
27
|
+
noquantifiers: true,
|
|
28
|
+
regex: false,
|
|
29
|
+
strictSlashes: true,
|
|
30
|
+
}
|
|
22
31
|
const { UI_CACHE_FOLDER, UI_DESC_FILENAME, UI_SRC_GLOB, UI_SRC_OPTS } = require('./constants')
|
|
23
32
|
const URI_SCHEME_RX = /^https?:\/\//
|
|
24
33
|
const EXT_RX = /\.[a-z]{2,3}$/
|
|
@@ -31,7 +40,7 @@ const EXT_RX = /\.[a-z]{2,3}$/
|
|
|
31
40
|
* the playbook. If the path is a URI, it downloads the file and caches it at a
|
|
32
41
|
* unique path to avoid this step in future calls. It then reads all the files
|
|
33
42
|
* from the bundle into memory, skipping any files that fall outside of the
|
|
34
|
-
* start path specified in the ui.startPath property of the playbook.
|
|
43
|
+
* start path specified in the ui.startPath property of the playbook. Finally,
|
|
35
44
|
* it classifies the files and adds them to a UiCatalog, which is then
|
|
36
45
|
* returned.
|
|
37
46
|
*
|
|
@@ -48,7 +57,7 @@ const EXT_RX = /\.[a-z]{2,3}$/
|
|
|
48
57
|
* @param {String} [playbook.network.noProxy=undefined] - The list of domains and IPs that should not be proxied.
|
|
49
58
|
* @param {Object} playbook.ui - The UI configuration object for Antora.
|
|
50
59
|
* @param {String} playbook.ui.bundle - The UI bundle configuration.
|
|
51
|
-
* @param {String} playbook.ui.bundle.url - The path (relative or absolute) or
|
|
60
|
+
* @param {String} playbook.ui.bundle.url - The path (relative or absolute) or URL
|
|
52
61
|
* of the UI bundle to use.
|
|
53
62
|
* @param {String} [playbook.ui.bundle.startPath=''] - The path inside the bundle from
|
|
54
63
|
* which to start reading files.
|
|
@@ -94,11 +103,7 @@ async function loadUi (playbook) {
|
|
|
94
103
|
resolveBundle.then((bundleFile) =>
|
|
95
104
|
new Promise((resolve, reject) =>
|
|
96
105
|
bundleFile.isDirectory()
|
|
97
|
-
?
|
|
98
|
-
.src(UI_SRC_GLOB, Object.assign({ cwd: bundleFile.path }, UI_SRC_OPTS))
|
|
99
|
-
.on('error', reject)
|
|
100
|
-
.pipe(relativizeFiles())
|
|
101
|
-
.pipe(collectFiles(resolve))
|
|
106
|
+
? srcFs(ospath.join(bundleFile.path, bundle.startPath || '', '.')).then(resolve, reject)
|
|
102
107
|
: vzip
|
|
103
108
|
.src(bundleFile.path)
|
|
104
109
|
.on('error', (err) => reject(Object.assign(err, { message: `not a valid zip file; ${err.message}` })))
|
|
@@ -181,12 +186,12 @@ function downloadBundle (url, to, agent) {
|
|
|
181
186
|
.on('error', (err) =>
|
|
182
187
|
reject(Object.assign(err, { message: `not a valid zip file; ${err.message}`, summary: 'Invalid UI bundle' }))
|
|
183
188
|
)
|
|
184
|
-
.on('finish',
|
|
189
|
+
.on('finish', () =>
|
|
185
190
|
fsp
|
|
186
191
|
.mkdir(ospath.dirname(to), { recursive: true })
|
|
187
192
|
.then(() => fsp.writeFile(to, contents))
|
|
188
193
|
.then(() => resolve(new File({ path: to, stat: { isDirectory: () => false } })))
|
|
189
|
-
|
|
194
|
+
)
|
|
190
195
|
})
|
|
191
196
|
}).catch((err) => {
|
|
192
197
|
const wrapped = new Error(`${err.summary || 'Failed to download UI bundle'}: ${url}`)
|
|
@@ -229,25 +234,25 @@ function bufferizeContents () {
|
|
|
229
234
|
return map((file, _, next) => {
|
|
230
235
|
// NOTE gulp-vinyl-zip automatically converts the contents of an empty file to a Buffer
|
|
231
236
|
if (file.isStream()) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
file.contents
|
|
235
|
-
|
|
236
|
-
|
|
237
|
+
const buffer = []
|
|
238
|
+
pipeline(
|
|
239
|
+
file.contents,
|
|
240
|
+
forEach((chunk, _, done) => buffer.push(chunk) && done()),
|
|
241
|
+
(err) => (err ? next(err) : next(null, Object.assign(file, { contents: Buffer.concat(buffer) })))
|
|
242
|
+
)
|
|
237
243
|
} else {
|
|
238
244
|
next(null, file)
|
|
239
245
|
}
|
|
240
246
|
})
|
|
241
247
|
}
|
|
242
248
|
|
|
243
|
-
function collectFiles (
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
(file, _, next) => {
|
|
249
|
+
function collectFiles (resolve, files = new Map()) {
|
|
250
|
+
return forEach(
|
|
251
|
+
(file, _, done) => {
|
|
247
252
|
files.set(file.path, file)
|
|
248
|
-
|
|
253
|
+
done()
|
|
249
254
|
},
|
|
250
|
-
() => done(files)
|
|
255
|
+
(done) => done() || resolve(files)
|
|
251
256
|
)
|
|
252
257
|
}
|
|
253
258
|
|
|
@@ -278,36 +283,16 @@ function srcSupplementalFiles (filesSpec, startDir) {
|
|
|
278
283
|
).then((files) => files.reduce((accum, file) => accum.set(file.path, file) && accum, new Map()))
|
|
279
284
|
} else {
|
|
280
285
|
const cwd = expandPath(filesSpec, { dot: startDir })
|
|
281
|
-
return fsp
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
() =>
|
|
285
|
-
new Promise((resolve, reject) =>
|
|
286
|
-
vfs
|
|
287
|
-
.src(UI_SRC_GLOB, Object.assign({ cwd, dot: true }, UI_SRC_OPTS))
|
|
288
|
-
.on('error', reject)
|
|
289
|
-
.pipe(relativizeFiles())
|
|
290
|
-
.pipe(collectFiles(resolve))
|
|
291
|
-
)
|
|
292
|
-
)
|
|
293
|
-
.catch((err) => {
|
|
286
|
+
return fsp.access(cwd).then(
|
|
287
|
+
() => srcFs(cwd),
|
|
288
|
+
(err) => {
|
|
294
289
|
// Q: should we skip unreadable files?
|
|
295
290
|
throw Object.assign(err, { message: `problem encountered while reading ui.supplemental_files: ${err.message}` })
|
|
296
|
-
}
|
|
291
|
+
}
|
|
292
|
+
)
|
|
297
293
|
}
|
|
298
294
|
}
|
|
299
295
|
|
|
300
|
-
function relativizeFiles () {
|
|
301
|
-
return map((file, _, next) => {
|
|
302
|
-
if (file.isNull()) {
|
|
303
|
-
next()
|
|
304
|
-
} else {
|
|
305
|
-
const path_ = posixify ? posixify(file.relative) : file.relative
|
|
306
|
-
next(null, new File({ cwd: file.cwd, path: path_, contents: file.contents, stat: file.stat, local: true }))
|
|
307
|
-
}
|
|
308
|
-
})
|
|
309
|
-
}
|
|
310
|
-
|
|
311
296
|
function mergeFiles (files, supplementalFiles) {
|
|
312
297
|
if (supplementalFiles.size) supplementalFiles.forEach((file) => files.set(file.path, file))
|
|
313
298
|
return files
|
|
@@ -318,15 +303,9 @@ function loadConfig (files, outputDir) {
|
|
|
318
303
|
if (configFile) {
|
|
319
304
|
files.delete(UI_DESC_FILENAME)
|
|
320
305
|
const config = camelCaseKeys(yaml.load(configFile.contents.toString()), { deep: true })
|
|
321
|
-
if (outputDir !== undefined) config.outputDir = outputDir
|
|
322
306
|
const staticFiles = config.staticFiles
|
|
323
|
-
if (staticFiles)
|
|
324
|
-
|
|
325
|
-
config.staticFiles = [staticFiles]
|
|
326
|
-
} else if (staticFiles.length === 0) {
|
|
327
|
-
delete config.staticFiles
|
|
328
|
-
}
|
|
329
|
-
}
|
|
307
|
+
if (staticFiles && staticFiles.length) config.isStaticFile = picomatch(staticFiles, STATIC_FILE_MATCHER_OPTS)
|
|
308
|
+
if (outputDir !== undefined) config.outputDir = outputDir
|
|
330
309
|
return config
|
|
331
310
|
} else {
|
|
332
311
|
return { outputDir }
|
|
@@ -334,7 +313,7 @@ function loadConfig (files, outputDir) {
|
|
|
334
313
|
}
|
|
335
314
|
|
|
336
315
|
function classifyFile (file, config) {
|
|
337
|
-
if (config.
|
|
316
|
+
if (config.isStaticFile && config.isStaticFile(file.path)) {
|
|
338
317
|
file.type = 'static'
|
|
339
318
|
file.out = resolveOut(file, '')
|
|
340
319
|
} else if (file.isDot()) {
|
|
@@ -345,10 +324,6 @@ function classifyFile (file, config) {
|
|
|
345
324
|
return file
|
|
346
325
|
}
|
|
347
326
|
|
|
348
|
-
function isStaticFile (file, staticFiles) {
|
|
349
|
-
return minimatchAll(file.path, staticFiles)
|
|
350
|
-
}
|
|
351
|
-
|
|
352
327
|
function resolveType (file) {
|
|
353
328
|
const firstPathSegment = file.path.split('/', 1)[0]
|
|
354
329
|
if (firstPathSegment === 'layouts') return 'layout'
|
|
@@ -364,4 +339,54 @@ function resolveOut (file, outputDir = '_') {
|
|
|
364
339
|
return { dirname, basename, path: path.join(dirname, basename) }
|
|
365
340
|
}
|
|
366
341
|
|
|
342
|
+
function srcFs (cwd) {
|
|
343
|
+
const relpathStart = cwd.length + 1
|
|
344
|
+
return new Promise((resolve, reject, cache = Object.create(null), files = new Map()) =>
|
|
345
|
+
pipeline(
|
|
346
|
+
globStream(UI_SRC_GLOB, Object.assign({ cache, cwd }, UI_SRC_OPTS)),
|
|
347
|
+
forEach(({ path: abspathPosix }, _, done) => {
|
|
348
|
+
if (Array.isArray(cache[abspathPosix])) return done() // detects some directories, but not all
|
|
349
|
+
const abspath = posixify ? ospath.normalize(abspathPosix) : abspathPosix
|
|
350
|
+
const relpath = abspath.substr(relpathStart)
|
|
351
|
+
symlinkAwareStat(abspath).then(
|
|
352
|
+
(stat) => {
|
|
353
|
+
if (stat.isDirectory()) return done() // detects remaining directories
|
|
354
|
+
fsp.readFile(abspath).then(
|
|
355
|
+
(contents) => {
|
|
356
|
+
const path_ = posixify ? posixify(relpath) : relpath
|
|
357
|
+
files.set(path_, new File({ cwd, path: path_, contents, stat, local: true }))
|
|
358
|
+
done()
|
|
359
|
+
},
|
|
360
|
+
(readErr) => {
|
|
361
|
+
done(Object.assign(readErr, { message: readErr.message.replace(`'${abspath}'`, relpath) }))
|
|
362
|
+
}
|
|
363
|
+
)
|
|
364
|
+
},
|
|
365
|
+
(statErr) => {
|
|
366
|
+
if (statErr.symlink) {
|
|
367
|
+
statErr.message =
|
|
368
|
+
statErr.code === 'ELOOP'
|
|
369
|
+
? `Symbolic link cycle detected at ${relpath}`
|
|
370
|
+
: `Broken symbolic link detected at ${relpath}`
|
|
371
|
+
} else {
|
|
372
|
+
statErr.message = statErr.message.replace(`'${abspath}'`, relpath)
|
|
373
|
+
}
|
|
374
|
+
done(statErr)
|
|
375
|
+
}
|
|
376
|
+
)
|
|
377
|
+
}),
|
|
378
|
+
(err) => (err ? reject(err) : resolve(files))
|
|
379
|
+
)
|
|
380
|
+
)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function symlinkAwareStat (path_) {
|
|
384
|
+
return fsp.lstat(path_).then((lstat) => {
|
|
385
|
+
if (!lstat.isSymbolicLink()) return lstat
|
|
386
|
+
return fsp.stat(path_).catch((statErr) => {
|
|
387
|
+
throw Object.assign(statErr, { symlink: true })
|
|
388
|
+
})
|
|
389
|
+
})
|
|
390
|
+
}
|
|
391
|
+
|
|
367
392
|
module.exports = loadUi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/ui-loader",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-beta.4",
|
|
4
4
|
"description": "Downloads a UI bundle, if necessary, and loads the files into a UI catalog for use in an Antora documentation pipeline.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"author": "OpenDevise Inc. (https://opendevise.com)",
|
|
@@ -18,20 +18,20 @@
|
|
|
18
18
|
"main": "lib/index.js",
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@antora/expand-path-helper": "~2.0",
|
|
21
|
+
"braces": "~3.0",
|
|
21
22
|
"cache-directory": "~2.0",
|
|
22
|
-
"camelcase-keys": "~
|
|
23
|
+
"camelcase-keys": "~7.0",
|
|
24
|
+
"glob-stream": "~7.0",
|
|
23
25
|
"gulp-vinyl-zip": "~2.5",
|
|
24
26
|
"hpagent": "~0.1.0",
|
|
25
27
|
"js-yaml": "~4.1",
|
|
26
|
-
"
|
|
28
|
+
"picomatch": "~2.3",
|
|
27
29
|
"should-proxy": "~1.0",
|
|
28
|
-
"simple-concat": "~1.0",
|
|
29
30
|
"simple-get": "~4.0",
|
|
30
|
-
"vinyl": "~2.2"
|
|
31
|
-
"vinyl-fs": "~3.0"
|
|
31
|
+
"vinyl": "~2.2"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
34
|
-
"node": ">=
|
|
34
|
+
"node": ">=12.21.0"
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"lib/"
|
|
@@ -45,5 +45,6 @@
|
|
|
45
45
|
"static site",
|
|
46
46
|
"web publishing"
|
|
47
47
|
],
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "8a142499e9f1a9e0631777796e06dd6c010d3a90",
|
|
49
|
+
"readmeFilename": "README.md"
|
|
49
50
|
}
|