@antora/ui-loader 3.1.7 → 3.1.8
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/lib/constants.js +6 -7
- package/lib/file.js +83 -10
- package/lib/load-ui.js +58 -99
- package/package.json +5 -5
package/lib/constants.js
CHANGED
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
module.exports = Object.freeze({
|
|
4
4
|
UI_CACHE_FOLDER: 'ui',
|
|
5
5
|
UI_DESC_FILENAME: 'ui.yml',
|
|
6
|
-
UI_SRC_GLOB: '
|
|
6
|
+
UI_SRC_GLOB: '**/!(*~)',
|
|
7
7
|
UI_SRC_OPTS: {
|
|
8
|
+
braceExpansion: false,
|
|
8
9
|
dot: true,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
nounique: true,
|
|
14
|
-
strict: false,
|
|
10
|
+
ignore: ['.git'],
|
|
11
|
+
objectMode: true,
|
|
12
|
+
onlyFiles: false,
|
|
13
|
+
unique: false,
|
|
15
14
|
},
|
|
16
15
|
})
|
package/lib/file.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { constants: fsc } = require('fs')
|
|
4
|
+
const { posix: path } = require('path')
|
|
3
5
|
const { Readable } = require('stream')
|
|
4
|
-
const { Stats } = require('fs')
|
|
5
6
|
const Vinyl = require('vinyl')
|
|
6
7
|
|
|
7
8
|
const DEFAULT_FILE_MODE = 0o100666 & ~process.umask()
|
|
9
|
+
const invariably = { true: () => true, false: () => false }
|
|
8
10
|
|
|
9
11
|
class File extends Vinyl {
|
|
10
12
|
get path () {
|
|
@@ -27,21 +29,92 @@ class File extends Vinyl {
|
|
|
27
29
|
class MemoryFile extends File {
|
|
28
30
|
constructor (file) {
|
|
29
31
|
const contents = file.contents || Buffer.alloc(0)
|
|
30
|
-
const stat =
|
|
32
|
+
const stat = {
|
|
33
|
+
mode: DEFAULT_FILE_MODE,
|
|
34
|
+
size: contents.length,
|
|
35
|
+
isDirectory: invariably.false,
|
|
36
|
+
isFile: invariably.true,
|
|
37
|
+
isSymbolicLink: invariably.false,
|
|
38
|
+
}
|
|
31
39
|
super(Object.assign({}, file, { contents, stat }))
|
|
32
40
|
}
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
class
|
|
36
|
-
constructor (
|
|
37
|
-
super({ objectMode: true })
|
|
38
|
-
this.
|
|
43
|
+
class ZipReadable extends Readable {
|
|
44
|
+
constructor (zipFile, options = {}) {
|
|
45
|
+
super({ objectMode: true, highWaterMark: 1 })
|
|
46
|
+
if ((this._closeable = (this._zipFile = zipFile).reader.fd != null) && !zipFile.autoClose) {
|
|
47
|
+
throw new Error('ZipReadable requires file-based ZipFile to be initialized with autoClose:true option')
|
|
48
|
+
}
|
|
49
|
+
if (!zipFile.lazyEntries) {
|
|
50
|
+
throw new Error('ZipReadable requires ZipFile to be initialized with lazyEntries:true option')
|
|
51
|
+
}
|
|
52
|
+
if ((this._startPath = options.startPath) && (this._startPath = path.join('/', this._startPath + '/')) !== '/') {
|
|
53
|
+
this._startPath = this._startPath.slice(1)
|
|
54
|
+
} else {
|
|
55
|
+
this._startPath = undefined
|
|
56
|
+
}
|
|
57
|
+
this._init()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_init () {
|
|
61
|
+
const zipFile = this._zipFile
|
|
62
|
+
zipFile
|
|
63
|
+
.on('entry', (entry) => {
|
|
64
|
+
const mode = this.getFileMode(entry)
|
|
65
|
+
if ((mode & fsc.S_IFMT) === fsc.S_IFDIR) return zipFile.readEntry()
|
|
66
|
+
let path_ = entry.fileName
|
|
67
|
+
if (this._startPath) {
|
|
68
|
+
if (path_.length < this._startPath.length || !path_.startsWith(this._startPath)) return zipFile.readEntry()
|
|
69
|
+
path_ = path_.slice(this._startPath.length)
|
|
70
|
+
}
|
|
71
|
+
const isLink = (mode & fsc.S_IFMT) === fsc.S_IFLNK
|
|
72
|
+
const stat = {
|
|
73
|
+
mode,
|
|
74
|
+
mtime: entry.getLastModDate(),
|
|
75
|
+
size: entry.uncompressedSize,
|
|
76
|
+
isDirectory: invariably.false,
|
|
77
|
+
isFile: invariably[!isLink],
|
|
78
|
+
isSymbolicLink: invariably[isLink],
|
|
79
|
+
}
|
|
80
|
+
const file = { path: path_, stat }
|
|
81
|
+
if (stat.size === 0) {
|
|
82
|
+
file.contents = Buffer.alloc(0)
|
|
83
|
+
this.push(new File(file))
|
|
84
|
+
} else {
|
|
85
|
+
zipFile.openReadStream(entry, (readErr, readStream) => {
|
|
86
|
+
if (readErr) {
|
|
87
|
+
zipFile.close()
|
|
88
|
+
this.emit('error', readErr)
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
if (isLink) {
|
|
92
|
+
const buffer = []
|
|
93
|
+
readStream
|
|
94
|
+
.on('data', (chunk) => buffer.push(chunk))
|
|
95
|
+
.on('error', (readStreamErr) => this.emit('error', readStreamErr))
|
|
96
|
+
.on('end', () => {
|
|
97
|
+
file.symlink = (buffer.length === 1 ? buffer[0] : Buffer.concat(buffer)).toString()
|
|
98
|
+
this.push(new File(file))
|
|
99
|
+
})
|
|
100
|
+
} else {
|
|
101
|
+
file.contents = readStream
|
|
102
|
+
this.push(new File(file))
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
.on(this._closeable ? 'close' : 'end', () => zipFile.emittedError || this.push(null))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
_read (_n) {
|
|
111
|
+
this._zipFile.readEntry()
|
|
39
112
|
}
|
|
40
113
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
114
|
+
getFileMode ({ externalFileAttributes }) {
|
|
115
|
+
const attr = externalFileAttributes >> 16 || 33188
|
|
116
|
+
return [448, 56, 7].map((mask) => attr & mask).reduce((a, b) => a + b, attr & fsc.S_IFMT)
|
|
44
117
|
}
|
|
45
118
|
}
|
|
46
119
|
|
|
47
|
-
module.exports = { File, MemoryFile,
|
|
120
|
+
module.exports = { File, MemoryFile, ZipReadable }
|
package/lib/load-ui.js
CHANGED
|
@@ -3,22 +3,23 @@
|
|
|
3
3
|
const { compile: bracesToGroup } = require('braces')
|
|
4
4
|
const { createHash } = require('crypto')
|
|
5
5
|
const expandPath = require('@antora/expand-path-helper')
|
|
6
|
-
const { File, MemoryFile,
|
|
6
|
+
const { File, MemoryFile, ZipReadable } = require('./file')
|
|
7
7
|
const { promises: fsp } = require('fs')
|
|
8
8
|
const { concat: get } = require('simple-get')
|
|
9
9
|
const getCacheDir = require('cache-directory')
|
|
10
|
-
const globStream = require('glob
|
|
10
|
+
const { globStream } = require('fast-glob')
|
|
11
11
|
const { inspect } = require('util')
|
|
12
|
+
const invariably = { false: () => false, void: () => undefined }
|
|
12
13
|
const ospath = require('path')
|
|
13
14
|
const { posix: path } = ospath
|
|
14
15
|
const picomatch = require('picomatch')
|
|
15
16
|
const posixify = ospath.sep === '\\' ? (p) => p.replace(/\\/g, '/') : undefined
|
|
16
|
-
const { pipeline,
|
|
17
|
+
const { pipeline, PassThrough, Writable } = require('stream')
|
|
17
18
|
const forEach = (write, final) => new Writable({ objectMode: true, write, final })
|
|
18
|
-
const
|
|
19
|
+
const through = () => new PassThrough({ objectMode: true })
|
|
19
20
|
const UiCatalog = require('./ui-catalog')
|
|
20
21
|
const yaml = require('js-yaml')
|
|
21
|
-
const
|
|
22
|
+
const yauzl = require('yauzl')
|
|
22
23
|
|
|
23
24
|
const STATIC_FILE_MATCHER_OPTS = {
|
|
24
25
|
expandRange: (begin, end, step, opts) => bracesToGroup(opts ? `{${begin}..${end}..${step}}` : `{${begin}..${end}}`),
|
|
@@ -104,13 +105,10 @@ async function loadUi (playbook) {
|
|
|
104
105
|
new Promise((resolve, reject) =>
|
|
105
106
|
bundleFile.isDirectory()
|
|
106
107
|
? srcFs(ospath.join(bundleFile.path, bundle.startPath || '', '.')).then(resolve, reject)
|
|
107
|
-
:
|
|
108
|
-
.src(bundleFile.path)
|
|
108
|
+
: srcZip(bundleFile.path, { startPath: bundle.startPath })
|
|
109
109
|
.on('error', (err) => reject(Object.assign(err, { message: `not a valid zip file; ${err.message}` })))
|
|
110
|
-
.pipe(
|
|
111
|
-
.pipe(bufferizeContents())
|
|
110
|
+
.pipe(bufferizeContentsAndCollectFiles(resolve))
|
|
112
111
|
.on('error', reject)
|
|
113
|
-
.pipe(collectFiles(resolve))
|
|
114
112
|
).catch((err) => {
|
|
115
113
|
const msg =
|
|
116
114
|
`Failed to read UI ${bundleFile.isDirectory() ? 'directory' : 'bundle'}: ` +
|
|
@@ -179,8 +177,7 @@ function downloadBundle (url, to, agent) {
|
|
|
179
177
|
const message = `Response code ${response.statusCode} (${response.statusMessage})`
|
|
180
178
|
return reject(Object.assign(new Error(message), { name: 'HTTPError' }))
|
|
181
179
|
}
|
|
182
|
-
|
|
183
|
-
.pipe(vzip.src())
|
|
180
|
+
srcZip(contents, { testOnly: true })
|
|
184
181
|
.on('error', (err) =>
|
|
185
182
|
reject(Object.assign(err, { message: `not a valid zip file; ${err.message}`, summary: 'Invalid UI bundle' }))
|
|
186
183
|
)
|
|
@@ -188,7 +185,7 @@ function downloadBundle (url, to, agent) {
|
|
|
188
185
|
fsp
|
|
189
186
|
.mkdir(ospath.dirname(to), { recursive: true })
|
|
190
187
|
.then(() => fsp.writeFile(to, contents))
|
|
191
|
-
.then(() => resolve(new File({ path: to, stat: { isDirectory:
|
|
188
|
+
.then(() => resolve(new File({ path: to, stat: { isDirectory: invariably.false } })))
|
|
192
189
|
)
|
|
193
190
|
})
|
|
194
191
|
}).catch((err) => {
|
|
@@ -200,62 +197,6 @@ function downloadBundle (url, to, agent) {
|
|
|
200
197
|
})
|
|
201
198
|
}
|
|
202
199
|
|
|
203
|
-
function selectFilesStartingFrom (startPath) {
|
|
204
|
-
if (!startPath || (startPath = path.join('/', startPath + '/')) === '/') {
|
|
205
|
-
return map((file, _, next) => {
|
|
206
|
-
if (file.isNull()) {
|
|
207
|
-
next()
|
|
208
|
-
} else {
|
|
209
|
-
next(
|
|
210
|
-
null,
|
|
211
|
-
new File({ path: posixify ? posixify(file.path) : file.path, contents: file.contents, stat: file.stat })
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
})
|
|
215
|
-
} else {
|
|
216
|
-
startPath = startPath.substr(1)
|
|
217
|
-
const startPathOffset = startPath.length
|
|
218
|
-
return map((file, _, next) => {
|
|
219
|
-
if (file.isNull()) {
|
|
220
|
-
next()
|
|
221
|
-
} else {
|
|
222
|
-
const path_ = posixify ? posixify(file.path) : file.path
|
|
223
|
-
if (path_.length > startPathOffset && path_.startsWith(startPath)) {
|
|
224
|
-
next(null, new File({ path: path_.substr(startPathOffset), contents: file.contents, stat: file.stat }))
|
|
225
|
-
} else {
|
|
226
|
-
next()
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
})
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function bufferizeContents () {
|
|
234
|
-
return map((file, _, next) => {
|
|
235
|
-
// NOTE gulp-vinyl-zip automatically converts the contents of an empty file to a Buffer
|
|
236
|
-
if (file.isStream()) {
|
|
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
|
-
)
|
|
243
|
-
} else {
|
|
244
|
-
next(null, file)
|
|
245
|
-
}
|
|
246
|
-
})
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function collectFiles (resolve, files = new Map()) {
|
|
250
|
-
return forEach(
|
|
251
|
-
(file, _, done) => {
|
|
252
|
-
files.set(file.path, file)
|
|
253
|
-
done()
|
|
254
|
-
},
|
|
255
|
-
(done) => done() || resolve(files)
|
|
256
|
-
)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
200
|
function srcSupplementalFiles (filesSpec, startDir) {
|
|
260
201
|
if (!filesSpec) return new Map()
|
|
261
202
|
let cwd
|
|
@@ -352,17 +293,15 @@ function resolveOut (file, outputDir = '_') {
|
|
|
352
293
|
}
|
|
353
294
|
|
|
354
295
|
function srcFs (cwd) {
|
|
355
|
-
return new Promise((resolve, reject,
|
|
296
|
+
return new Promise((resolve, reject, files = new Map()) =>
|
|
356
297
|
pipeline(
|
|
357
|
-
globStream(UI_SRC_GLOB, Object.assign({
|
|
358
|
-
forEach(({ path:
|
|
359
|
-
if ((
|
|
360
|
-
const
|
|
361
|
-
const relpath =
|
|
362
|
-
|
|
298
|
+
globStream(UI_SRC_GLOB, Object.assign({ cwd }, UI_SRC_OPTS)),
|
|
299
|
+
forEach(({ path: relpath, dirent }, _, done) => {
|
|
300
|
+
if (dirent.isDirectory()) return done()
|
|
301
|
+
const relpathPosix = relpath
|
|
302
|
+
const abspath = posixify ? ospath.join(cwd, (relpath = ospath.normalize(relpath))) : cwd + '/' + relpath
|
|
303
|
+
fsp.stat(abspath).then(
|
|
363
304
|
(stat) => {
|
|
364
|
-
if (stat.isDirectory()) return done() // detects directories that slipped through cache check
|
|
365
|
-
const relpathPosix = posixify ? posixify(relpath) : relpath
|
|
366
305
|
fsp.readFile(abspath).then(
|
|
367
306
|
(contents) => {
|
|
368
307
|
files.set(relpathPosix, new File({ cwd, path: relpathPosix, contents, stat, local: true }))
|
|
@@ -373,16 +312,18 @@ function srcFs (cwd) {
|
|
|
373
312
|
}
|
|
374
313
|
)
|
|
375
314
|
},
|
|
376
|
-
(statErr) =>
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
315
|
+
(statErr) =>
|
|
316
|
+
dirent.isSymbolicLink()
|
|
317
|
+
? fsp
|
|
318
|
+
.readlink(abspath)
|
|
319
|
+
.then(
|
|
320
|
+
(symlink) =>
|
|
321
|
+
(statErr.code === 'ELOOP' ? 'ELOOP: symbolic link cycle, ' : 'ENOENT: broken symbolic link, ') +
|
|
322
|
+
`${relpath} -> ${symlink}`,
|
|
323
|
+
() => statErr.message.replace(`'${abspath}'`, relpath)
|
|
324
|
+
)
|
|
325
|
+
.then((message) => done(Object.assign(statErr, { message })))
|
|
326
|
+
: done(Object.assign(statErr, { message: statErr.message.replace(`'${abspath}'`, relpath) }))
|
|
386
327
|
)
|
|
387
328
|
}),
|
|
388
329
|
(err) => (err ? reject(err) : resolve(files))
|
|
@@ -390,18 +331,36 @@ function srcFs (cwd) {
|
|
|
390
331
|
)
|
|
391
332
|
}
|
|
392
333
|
|
|
393
|
-
function
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
.then((symlink) => {
|
|
401
|
-
throw Object.assign(statErr, { symlink })
|
|
402
|
-
})
|
|
403
|
-
)
|
|
334
|
+
function srcZip (file, options = {}) {
|
|
335
|
+
const result = options.testOnly // is it necessary to close streams in this case, or just sink()?
|
|
336
|
+
? forEach((file_, _, done) => (file_.isStream() ? file_.contents.on('close', done).destroy() : done()))
|
|
337
|
+
: through()
|
|
338
|
+
yauzl[file instanceof Buffer ? 'fromBuffer' : 'open'](file, { lazyEntries: true }, (err, zipFile) => {
|
|
339
|
+
if (err) return result.emit('error', err)
|
|
340
|
+
new ZipReadable(zipFile, options).pipe(result)
|
|
404
341
|
})
|
|
342
|
+
return result
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function bufferizeContentsAndCollectFiles (resolve, files = new Map()) {
|
|
346
|
+
return forEach(
|
|
347
|
+
(file, _, done) => {
|
|
348
|
+
if (file.isStream()) {
|
|
349
|
+
const buffer = []
|
|
350
|
+
file.contents
|
|
351
|
+
.on('data', (chunk) => buffer.push(chunk))
|
|
352
|
+
.on('end', () => {
|
|
353
|
+
file.contents = buffer.length === 1 ? buffer[0] : Buffer.concat(buffer)
|
|
354
|
+
files.set(file.path, file)
|
|
355
|
+
done()
|
|
356
|
+
})
|
|
357
|
+
} else {
|
|
358
|
+
files.set(file.path, file)
|
|
359
|
+
done()
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
(done) => done() || resolve(files)
|
|
363
|
+
)
|
|
405
364
|
}
|
|
406
365
|
|
|
407
366
|
function transformError (err, msg) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/ui-loader",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.8",
|
|
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)",
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@antora/expand-path-helper": "~2.0",
|
|
29
|
-
"@vscode/gulp-vinyl-zip": "~2.5",
|
|
30
29
|
"braces": "~3.0",
|
|
31
30
|
"cache-directory": "~2.0",
|
|
32
|
-
"glob
|
|
31
|
+
"fast-glob": "~3.3",
|
|
33
32
|
"hpagent": "~1.2",
|
|
34
33
|
"js-yaml": "~4.1",
|
|
35
|
-
"picomatch": "~
|
|
34
|
+
"picomatch": "~4.0",
|
|
36
35
|
"should-proxy": "~1.0",
|
|
37
36
|
"simple-get": "~4.0",
|
|
38
|
-
"vinyl": "~
|
|
37
|
+
"vinyl": "~3.0",
|
|
38
|
+
"yauzl": "~3.1"
|
|
39
39
|
},
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=16.0.0"
|