@antora/ui-loader 3.0.0-alpha.8 → 3.0.0-beta.3
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/load-ui.js +81 -58
- package/package.json +7 -7
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/load-ui.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { compile: bracesToGroup } = require('braces')
|
|
3
4
|
const camelCaseKeys = require('camelcase-keys')
|
|
4
5
|
const concat = require('simple-concat')
|
|
5
6
|
const { createHash } = require('crypto')
|
|
@@ -8,17 +9,25 @@ const { File, MemoryFile, ReadableFile } = require('./file')
|
|
|
8
9
|
const { promises: fsp } = require('fs')
|
|
9
10
|
const { concat: get } = require('simple-get')
|
|
10
11
|
const getCacheDir = require('cache-directory')
|
|
11
|
-
const
|
|
12
|
+
const globStream = require('glob-stream')
|
|
12
13
|
const ospath = require('path')
|
|
13
14
|
const { posix: path } = ospath
|
|
15
|
+
const picomatch = require('picomatch')
|
|
14
16
|
const posixify = ospath.sep === '\\' ? (p) => p.replace(/\\/g, '/') : undefined
|
|
15
|
-
const { Transform } = require('stream')
|
|
17
|
+
const { pipeline, Transform } = require('stream')
|
|
16
18
|
const map = (transform, flush = undefined) => new Transform({ objectMode: true, transform, flush })
|
|
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}$/
|
|
@@ -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.
|
|
@@ -73,32 +82,28 @@ async function loadUi (playbook) {
|
|
|
73
82
|
const cachePath = ospath.join(absCacheDir, `${sha1(bundleUrl)}.zip`)
|
|
74
83
|
return fetch && bundle.snapshot
|
|
75
84
|
? downloadBundle(bundleUrl, cachePath, createAgent(bundleUrl, playbook.network || {}))
|
|
76
|
-
: fsp
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
: fsp.stat(cachePath).then(
|
|
86
|
+
(stat) => new File({ path: cachePath, stat }),
|
|
87
|
+
() => downloadBundle(bundleUrl, cachePath, createAgent(bundleUrl, playbook.network || {}))
|
|
88
|
+
)
|
|
80
89
|
})
|
|
81
90
|
} else {
|
|
82
91
|
const localPath = expandPath(bundleUrl, { dot: startDir })
|
|
83
|
-
resolveBundle = fsp
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
.catch(() => {
|
|
92
|
+
resolveBundle = fsp.stat(localPath).then(
|
|
93
|
+
(stat) => new File({ path: localPath, stat }),
|
|
94
|
+
() => {
|
|
87
95
|
throw new Error(
|
|
88
96
|
`Specified UI ${path.extname(localPath) ? 'bundle' : 'directory'} does not exist: ` +
|
|
89
97
|
(bundleUrl === localPath ? bundleUrl : `${localPath} (resolved from url: ${bundleUrl})`)
|
|
90
98
|
)
|
|
91
|
-
}
|
|
99
|
+
}
|
|
100
|
+
)
|
|
92
101
|
}
|
|
93
102
|
const files = await Promise.all([
|
|
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(bundleFile.path).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}` })))
|
|
@@ -149,16 +154,16 @@ function ensureCacheDir (customCacheDir, startDir) {
|
|
|
149
154
|
? getCacheDir('antora' + (process.env.NODE_ENV === 'test' ? '-test' : '')) || ospath.resolve('.antora/cache')
|
|
150
155
|
: expandPath(customCacheDir, { dot: startDir })
|
|
151
156
|
const cacheDir = ospath.join(baseCacheDir, UI_CACHE_FOLDER)
|
|
152
|
-
return fsp
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
.catch((err) => {
|
|
157
|
+
return fsp.mkdir(cacheDir, { recursive: true }).then(
|
|
158
|
+
() => cacheDir,
|
|
159
|
+
(err) => {
|
|
156
160
|
throw Object.assign(err, { message: `Failed to create UI cache directory: ${cacheDir}; ${err.message}` })
|
|
157
|
-
}
|
|
161
|
+
}
|
|
162
|
+
)
|
|
158
163
|
}
|
|
159
164
|
|
|
160
165
|
function createAgent (url, { httpProxy, httpsProxy, noProxy }) {
|
|
161
|
-
if (httpsProxy || httpProxy) {
|
|
166
|
+
if ((httpsProxy || httpProxy) && noProxy !== '*') {
|
|
162
167
|
const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent')
|
|
163
168
|
const shouldProxy = require('should-proxy')
|
|
164
169
|
const proxy = url.startsWith('https:')
|
|
@@ -280,16 +285,7 @@ function srcSupplementalFiles (filesSpec, startDir) {
|
|
|
280
285
|
const cwd = expandPath(filesSpec, { dot: startDir })
|
|
281
286
|
return fsp
|
|
282
287
|
.access(cwd)
|
|
283
|
-
.then(
|
|
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
|
-
)
|
|
288
|
+
.then(() => srcFs(cwd))
|
|
293
289
|
.catch((err) => {
|
|
294
290
|
// Q: should we skip unreadable files?
|
|
295
291
|
throw Object.assign(err, { message: `problem encountered while reading ui.supplemental_files: ${err.message}` })
|
|
@@ -297,17 +293,6 @@ function srcSupplementalFiles (filesSpec, startDir) {
|
|
|
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,52 @@ function resolveOut (file, outputDir = '_') {
|
|
|
364
339
|
return { dirname, basename, path: path.join(dirname, basename) }
|
|
365
340
|
}
|
|
366
341
|
|
|
342
|
+
function srcFs (cwd) {
|
|
343
|
+
return new Promise((resolve, reject, cache = {}, files = new Map()) =>
|
|
344
|
+
pipeline(
|
|
345
|
+
globStream(UI_SRC_GLOB, Object.assign({ cache, cwd }, UI_SRC_OPTS)),
|
|
346
|
+
map(({ path: abspathPosix }, _, next) => {
|
|
347
|
+
const abspath = posixify ? ospath.normalize(abspathPosix) : abspathPosix
|
|
348
|
+
const relpath = abspath.substr(cwd.length + 1)
|
|
349
|
+
symlinkAwareStat(abspath).then(
|
|
350
|
+
(stat) => {
|
|
351
|
+
if (stat.isDirectory()) return next()
|
|
352
|
+
fsp.readFile(abspath).then(
|
|
353
|
+
(contents) => {
|
|
354
|
+
const path_ = posixify ? posixify(relpath) : relpath
|
|
355
|
+
files.set(path_, new File({ cwd, path: path_, contents, stat, local: true }))
|
|
356
|
+
next()
|
|
357
|
+
},
|
|
358
|
+
(readErr) => {
|
|
359
|
+
next(Object.assign(readErr, { message: readErr.message.replace(`'${abspath}'`, relpath) }))
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
},
|
|
363
|
+
(statErr) => {
|
|
364
|
+
if (statErr.symlink) {
|
|
365
|
+
statErr.message =
|
|
366
|
+
statErr.code === 'ELOOP'
|
|
367
|
+
? `Symbolic link cycle detected at ${relpath}`
|
|
368
|
+
: `Broken symbolic link detected at ${relpath}`
|
|
369
|
+
} else {
|
|
370
|
+
statErr.message = statErr.message.replace(`'${abspath}'`, relpath)
|
|
371
|
+
}
|
|
372
|
+
next(statErr)
|
|
373
|
+
}
|
|
374
|
+
)
|
|
375
|
+
}),
|
|
376
|
+
(err) => (err ? reject(err) : resolve(files))
|
|
377
|
+
)
|
|
378
|
+
)
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function symlinkAwareStat (path_) {
|
|
382
|
+
return fsp.lstat(path_).then((lstat) => {
|
|
383
|
+
if (!lstat.isSymbolicLink()) return lstat
|
|
384
|
+
return fsp.stat(path_).catch((statErr) => {
|
|
385
|
+
throw Object.assign(statErr, { symlink: true })
|
|
386
|
+
})
|
|
387
|
+
})
|
|
388
|
+
}
|
|
389
|
+
|
|
367
390
|
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.3",
|
|
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",
|
|
23
24
|
"gulp-vinyl-zip": "~2.5",
|
|
24
25
|
"hpagent": "~0.1.0",
|
|
25
26
|
"js-yaml": "~4.1",
|
|
26
|
-
"
|
|
27
|
+
"picomatch": "~2.3",
|
|
27
28
|
"should-proxy": "~1.0",
|
|
28
29
|
"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,5 @@
|
|
|
45
45
|
"static site",
|
|
46
46
|
"web publishing"
|
|
47
47
|
],
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "45da95a2e2dea538379d2d9f42013d2208fb86c3"
|
|
49
49
|
}
|