@antora/content-aggregator 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/aggregate-content.js +163 -193
- package/lib/constants.js +2 -13
- package/lib/filter-refs.js +42 -0
- package/lib/git-credential-manager-store.js +7 -9
- package/lib/index.js +1 -1
- package/lib/invariably.js +3 -0
- package/lib/matcher.js +32 -0
- package/lib/resolve-path-globs.js +21 -28
- package/package.json +10 -7
- package/lib/promise-all-settled-polyfill.js +0 -11
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The Content Aggregator is a component in Antora responsible for fetching and aggregating content distributed across multiple local and remote git repositories for use in an Antora documentation pipeline.
|
|
4
4
|
|
|
5
5
|
[Antora](https://antora.org) is a modular static site generator designed for creating documentation sites from AsciiDoc documents.
|
|
6
|
-
Its site generator
|
|
6
|
+
Its site generator aggregates documents from versioned content repositories and processes them using [Asciidoctor](https://asciidoctor.org).
|
|
7
7
|
|
|
8
8
|
## Copyright and License
|
|
9
9
|
|
package/lib/aggregate-content.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
if (!Promise.allSettled) require('./promise-all-settled-polyfill')
|
|
4
|
-
|
|
5
3
|
const camelCaseKeys = require('camelcase-keys')
|
|
6
4
|
const { createHash } = require('crypto')
|
|
7
5
|
const createHttpPlugin = require('./git-plugin-http')
|
|
@@ -9,6 +7,7 @@ const decodeUint8Array = require('./decode-uint8-array')
|
|
|
9
7
|
const EventEmitter = require('events')
|
|
10
8
|
const expandPath = require('@antora/expand-path-helper')
|
|
11
9
|
const File = require('./file')
|
|
10
|
+
const filterRefs = require('./filter-refs')
|
|
12
11
|
const flattenDeep = require('./flatten-deep')
|
|
13
12
|
const fs = require('fs')
|
|
14
13
|
const { promises: fsp } = fs
|
|
@@ -16,18 +15,17 @@ const getCacheDir = require('cache-directory')
|
|
|
16
15
|
const GitCredentialManagerStore = require('./git-credential-manager-store')
|
|
17
16
|
const git = require('./git')
|
|
18
17
|
const { NotFoundError, ObjectTypeError, UnknownTransportError, UrlParseError } = git.Errors
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const MultiProgress = require('multi-progress')
|
|
18
|
+
const globStream = require('glob-stream')
|
|
19
|
+
const invariably = require('./invariably')
|
|
20
|
+
const { makeMatcherRx, versionMatcherOpts: VERSION_MATCHER_OPTS } = require('./matcher')
|
|
21
|
+
const MultiProgress = require('multi-progress') // calls require('progress') as a peer dependencies
|
|
23
22
|
const ospath = require('path')
|
|
24
23
|
const { posix: path } = ospath
|
|
25
24
|
const posixify = ospath.sep === '\\' ? (p) => p.replace(/\\/g, '/') : undefined
|
|
26
25
|
const { fs: resolvePathGlobsFs, git: resolvePathGlobsGit } = require('./resolve-path-globs')
|
|
27
|
-
const {
|
|
28
|
-
const
|
|
26
|
+
const { pipeline, Writable } = require('stream')
|
|
27
|
+
const forEach = (write) => new Writable({ objectMode: true, write })
|
|
29
28
|
const userRequire = require('@antora/user-require-helper')
|
|
30
|
-
const vfs = require('vinyl-fs')
|
|
31
29
|
const yaml = require('js-yaml')
|
|
32
30
|
|
|
33
31
|
const {
|
|
@@ -39,11 +37,10 @@ const {
|
|
|
39
37
|
GIT_CORE,
|
|
40
38
|
GIT_OPERATION_LABEL_LENGTH,
|
|
41
39
|
GIT_PROGRESS_PHASES,
|
|
42
|
-
|
|
40
|
+
REF_PATTERN_CACHE_KEY,
|
|
43
41
|
SYMLINK_FILE_MODE,
|
|
44
42
|
VALID_STATE_FILENAME,
|
|
45
43
|
} = require('./constants')
|
|
46
|
-
|
|
47
44
|
const ANY_SEPARATOR_RX = /[:/]/
|
|
48
45
|
const CSV_RX = /\s*,\s*/
|
|
49
46
|
const VENTILATED_CSV_RX = /\s*,\s+/
|
|
@@ -101,7 +98,8 @@ function aggregateContent (playbook) {
|
|
|
101
98
|
return accum.set(source.url, [...(accum.get(source.url) || []), Object.assign({}, sourceDefaults, source)])
|
|
102
99
|
}, new Map())
|
|
103
100
|
const progress = !quiet && createProgress(sourcesByUrl.keys(), process.stdout)
|
|
104
|
-
const
|
|
101
|
+
const refPatternCache = Object.assign(new Map(), { braces: new Map() })
|
|
102
|
+
const loadOpts = { cacheDir, fetch, gitPlugins, progress, startDir, refPatternCache }
|
|
105
103
|
return collectFiles(sourcesByUrl, loadOpts, fetchConcurrency).then(buildAggregate, (err) => {
|
|
106
104
|
progress && progress.terminate()
|
|
107
105
|
throw err
|
|
@@ -122,14 +120,14 @@ async function collectFiles (sourcesByUrl, loadOpts, concurrency) {
|
|
|
122
120
|
})
|
|
123
121
|
),
|
|
124
122
|
])
|
|
125
|
-
let
|
|
123
|
+
let rejection, started
|
|
126
124
|
const startedContinuations = []
|
|
127
125
|
const recordRejection = (err) => {
|
|
128
|
-
|
|
126
|
+
rejection = err
|
|
129
127
|
}
|
|
130
128
|
const runTask = (primary, continuation, idx) =>
|
|
131
129
|
primary().then((value) => {
|
|
132
|
-
if (!
|
|
130
|
+
if (!rejection) startedContinuations[idx] = continuation(value).catch(recordRejection)
|
|
133
131
|
}, recordRejection)
|
|
134
132
|
if (tasks.length > concurrency) {
|
|
135
133
|
started = []
|
|
@@ -140,17 +138,16 @@ async function collectFiles (sourcesByUrl, loadOpts, concurrency) {
|
|
|
140
138
|
)
|
|
141
139
|
started.push(current)
|
|
142
140
|
if (pending.push(current) < concurrency) continue
|
|
143
|
-
|
|
144
|
-
break
|
|
141
|
+
await Promise.race(pending)
|
|
142
|
+
if (rejection) break
|
|
145
143
|
}
|
|
146
144
|
} else {
|
|
147
145
|
started = tasks.map(([primary, continuation], idx) => runTask(primary, continuation, idx))
|
|
148
146
|
}
|
|
149
|
-
return Promise.
|
|
150
|
-
Promise.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return continuationOutcomes.map(({ value }) => value)
|
|
147
|
+
return Promise.all(started).then(() =>
|
|
148
|
+
Promise.all(startedContinuations).then((result) => {
|
|
149
|
+
if (rejection) throw rejection
|
|
150
|
+
return result
|
|
154
151
|
})
|
|
155
152
|
)
|
|
156
153
|
}
|
|
@@ -173,13 +170,14 @@ function buildAggregate (componentVersionBuckets) {
|
|
|
173
170
|
|
|
174
171
|
async function loadRepository (url, opts) {
|
|
175
172
|
let authStatus, dir, repo
|
|
173
|
+
const cache = { [REF_PATTERN_CACHE_KEY]: opts.refPatternCache }
|
|
176
174
|
if (~url.indexOf(':') && GIT_URI_DETECTOR_RX.test(url)) {
|
|
177
175
|
let credentials, displayUrl
|
|
178
176
|
;({ displayUrl, url, credentials } = extractCredentials(url))
|
|
179
177
|
const { cacheDir, fetch, fetchTags, gitPlugins, progress } = opts
|
|
180
178
|
dir = ospath.join(cacheDir, generateCloneFolderName(displayUrl))
|
|
181
179
|
// NOTE the presence of the url property on the repo object implies the repository is remote
|
|
182
|
-
repo = { cache
|
|
180
|
+
repo = { cache, dir, fs, gitdir: dir, noCheckout: true, url }
|
|
183
181
|
const validStateFile = ospath.join(dir, VALID_STATE_FILENAME)
|
|
184
182
|
try {
|
|
185
183
|
await fsp.access(validStateFile)
|
|
@@ -204,7 +202,7 @@ async function loadRepository (url, opts) {
|
|
|
204
202
|
authStatus = await git.getConfig(Object.assign({ path: 'remote.origin.private' }, repo))
|
|
205
203
|
}
|
|
206
204
|
} catch (gitErr) {
|
|
207
|
-
await rmdir(dir)
|
|
205
|
+
await fsp['rm' in fsp ? 'rm' : 'rmdir'](dir, { recursive: true, force: true })
|
|
208
206
|
if (gitErr.rethrow) throw transformGitCloneError(gitErr, displayUrl)
|
|
209
207
|
const fetchOpts = buildFetchOptions(repo, progress, displayUrl, credentials, gitPlugins, fetchTags, 'clone')
|
|
210
208
|
await git
|
|
@@ -225,15 +223,12 @@ async function loadRepository (url, opts) {
|
|
|
225
223
|
}
|
|
226
224
|
} else if (await isDirectory((dir = expandPath(url, { dot: opts.startDir })))) {
|
|
227
225
|
const gitdir = ospath.join(dir, '.git')
|
|
228
|
-
repo = (await isDirectory(gitdir))
|
|
229
|
-
? { cache: {}, dir, fs, gitdir }
|
|
230
|
-
: { cache: {}, dir, fs, gitdir: dir, noCheckout: true }
|
|
226
|
+
repo = (await isDirectory(gitdir)) ? { cache, dir, fs, gitdir } : { cache, dir, fs, gitdir: dir, noCheckout: true }
|
|
231
227
|
try {
|
|
232
228
|
await git.resolveRef(Object.assign({ ref: 'HEAD', depth: 1 }, repo))
|
|
233
229
|
} catch {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
)
|
|
230
|
+
const msg = `Local content source must be a git repository: ${dir}${url !== dir ? ' (url: ' + url + ')' : ''}`
|
|
231
|
+
throw new Error(msg)
|
|
237
232
|
}
|
|
238
233
|
} else {
|
|
239
234
|
throw new Error(`Local content source does not exist: ${dir}${url !== dir ? ' (url: ' + url + ')' : ''}`)
|
|
@@ -271,99 +266,102 @@ async function collectFilesFromSource (source, repo, remoteName, authStatus) {
|
|
|
271
266
|
async function selectReferences (source, repo, remote) {
|
|
272
267
|
let { branches: branchPatterns, tags: tagPatterns, worktrees: worktreePatterns = '.' } = source
|
|
273
268
|
const isBare = repo.noCheckout
|
|
269
|
+
const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
|
|
274
270
|
const noWorktree = repo.url ? undefined : null
|
|
275
271
|
const refs = new Map()
|
|
276
|
-
if (
|
|
277
|
-
tagPatterns
|
|
272
|
+
if (
|
|
273
|
+
tagPatterns &&
|
|
274
|
+
(tagPatterns = Array.isArray(tagPatterns)
|
|
278
275
|
? tagPatterns.map((pattern) => String(pattern))
|
|
279
|
-
: String(tagPatterns).
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
276
|
+
: splitRefPatterns(String(tagPatterns))).length
|
|
277
|
+
) {
|
|
278
|
+
const tags = await git.listTags(repo)
|
|
279
|
+
if (tags.length) {
|
|
280
|
+
for (const shortname of filterRefs(tags, tagPatterns, patternCache)) {
|
|
283
281
|
// NOTE tags are stored using symbol keys to distinguish them from branches
|
|
284
282
|
refs.set(Symbol(shortname), { shortname, fullname: 'tags/' + shortname, type: 'tag', head: noWorktree })
|
|
285
283
|
}
|
|
286
284
|
}
|
|
287
285
|
}
|
|
288
|
-
if (branchPatterns)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
286
|
+
if (!branchPatterns) return [...refs.values()]
|
|
287
|
+
if (worktreePatterns) {
|
|
288
|
+
if (worktreePatterns === '.') {
|
|
289
|
+
worktreePatterns = ['.']
|
|
290
|
+
} else if (worktreePatterns === true) {
|
|
291
|
+
worktreePatterns = ['.', '*']
|
|
292
|
+
} else {
|
|
293
|
+
worktreePatterns = Array.isArray(worktreePatterns)
|
|
294
|
+
? worktreePatterns.map((pattern) => String(pattern))
|
|
295
|
+
: splitRefPatterns(String(worktreePatterns))
|
|
299
296
|
}
|
|
300
|
-
|
|
301
|
-
|
|
297
|
+
}
|
|
298
|
+
const branchPatternsString = String(branchPatterns)
|
|
299
|
+
if (branchPatternsString === 'HEAD' || branchPatternsString === '.') {
|
|
300
|
+
const currentBranch = await getCurrentBranchName(repo, remote)
|
|
301
|
+
if (currentBranch) {
|
|
302
|
+
branchPatterns = [currentBranch]
|
|
303
|
+
} else if (isBare) {
|
|
304
|
+
return [...refs.values()]
|
|
305
|
+
} else {
|
|
306
|
+
// NOTE current branch is undefined when HEAD is detached
|
|
307
|
+
const head = worktreePatterns[0] === '.' ? repo.dir : noWorktree
|
|
308
|
+
refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
|
|
309
|
+
return [...refs.values()]
|
|
310
|
+
}
|
|
311
|
+
} else if (
|
|
312
|
+
(branchPatterns = Array.isArray(branchPatterns)
|
|
313
|
+
? branchPatterns.map((pattern) => String(pattern))
|
|
314
|
+
: splitRefPatterns(branchPatternsString)).length
|
|
315
|
+
) {
|
|
316
|
+
let headBranchIdx
|
|
317
|
+
// NOTE we can assume at least two entries if HEAD or . are present
|
|
318
|
+
if (~(headBranchIdx = branchPatterns.indexOf('HEAD')) || ~(headBranchIdx = branchPatterns.indexOf('.'))) {
|
|
302
319
|
const currentBranch = await getCurrentBranchName(repo, remote)
|
|
303
320
|
if (currentBranch) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
if (!isBare) {
|
|
307
|
-
// NOTE current branch is undefined when HEAD is detached
|
|
308
|
-
const head = worktreePatterns[0] === '.' ? repo.dir : noWorktree
|
|
309
|
-
refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
|
|
310
|
-
}
|
|
311
|
-
return [...refs.values()]
|
|
312
|
-
}
|
|
313
|
-
} else if (
|
|
314
|
-
(branchPatterns = Array.isArray(branchPatterns)
|
|
315
|
-
? branchPatterns.map((pattern) => String(pattern))
|
|
316
|
-
: branchPatternsString.split(CSV_RX)).length
|
|
317
|
-
) {
|
|
318
|
-
let headBranchIdx
|
|
319
|
-
// NOTE we can assume at least two entries if HEAD or . are present
|
|
320
|
-
if (~(headBranchIdx = branchPatterns.indexOf('HEAD')) || ~(headBranchIdx = branchPatterns.indexOf('.'))) {
|
|
321
|
-
const currentBranch = await getCurrentBranchName(repo, remote)
|
|
322
|
-
if (currentBranch) {
|
|
323
|
-
// NOTE ignore if current branch is already in list
|
|
324
|
-
if (~branchPatterns.indexOf(currentBranch)) {
|
|
325
|
-
branchPatterns.splice(headBranchIdx, 1)
|
|
326
|
-
} else {
|
|
327
|
-
branchPatterns[headBranchIdx] = currentBranch
|
|
328
|
-
}
|
|
329
|
-
} else {
|
|
330
|
-
if (!isBare) {
|
|
331
|
-
let head = noWorktree
|
|
332
|
-
if (worktreePatterns[0] === '.') {
|
|
333
|
-
worktreePatterns = worktreePatterns.slice(1)
|
|
334
|
-
head = repo.dir
|
|
335
|
-
}
|
|
336
|
-
// NOTE current branch is undefined when HEAD is detached
|
|
337
|
-
refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
|
|
338
|
-
}
|
|
321
|
+
// NOTE ignore if current branch is already in list
|
|
322
|
+
if (~branchPatterns.indexOf(currentBranch)) {
|
|
339
323
|
branchPatterns.splice(headBranchIdx, 1)
|
|
324
|
+
} else {
|
|
325
|
+
branchPatterns[headBranchIdx] = currentBranch
|
|
340
326
|
}
|
|
327
|
+
} else if (isBare) {
|
|
328
|
+
branchPatterns.splice(headBranchIdx, 1)
|
|
329
|
+
} else {
|
|
330
|
+
let head = noWorktree
|
|
331
|
+
if (worktreePatterns[0] === '.') {
|
|
332
|
+
worktreePatterns = worktreePatterns.slice(1)
|
|
333
|
+
head = repo.dir
|
|
334
|
+
}
|
|
335
|
+
// NOTE current branch is undefined when HEAD is detached
|
|
336
|
+
refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
|
|
337
|
+
branchPatterns.splice(headBranchIdx, 1)
|
|
341
338
|
}
|
|
342
|
-
} else {
|
|
343
|
-
return [...refs.values()]
|
|
344
339
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
340
|
+
} else {
|
|
341
|
+
return [...refs.values()]
|
|
342
|
+
}
|
|
343
|
+
// NOTE isomorphic-git includes HEAD in list of remote branches (see https://isomorphic-git.org/docs/listBranches)
|
|
344
|
+
const remoteBranches = (await git.listBranches(Object.assign({ remote }, repo))).filter((it) => it !== 'HEAD')
|
|
345
|
+
if (remoteBranches.length) {
|
|
346
|
+
for (const shortname of filterRefs(remoteBranches, branchPatterns, patternCache)) {
|
|
347
|
+
const fullname = 'remotes/' + remote + '/' + shortname
|
|
348
|
+
refs.set(shortname, { shortname, fullname, type: 'branch', remote, head: noWorktree })
|
|
352
349
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
350
|
+
}
|
|
351
|
+
// NOTE only consider local branches if repo has a worktree or there are no remote tracking branches
|
|
352
|
+
if (!isBare) {
|
|
353
|
+
const localBranches = await git.listBranches(repo)
|
|
354
|
+
if (localBranches.length) {
|
|
355
|
+
const worktrees = await findWorktrees(repo, worktreePatterns)
|
|
356
|
+
for (const shortname of filterRefs(localBranches, branchPatterns, patternCache)) {
|
|
357
|
+
const head = worktrees.get(shortname) || noWorktree
|
|
358
|
+
refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head })
|
|
362
359
|
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
360
|
+
}
|
|
361
|
+
} else if (!remoteBranches.length) {
|
|
362
|
+
const localBranches = await git.listBranches(repo)
|
|
363
|
+
if (localBranches.length) {
|
|
364
|
+
for (const shortname of filterRefs(localBranches, branchPatterns, patternCache)) {
|
|
367
365
|
refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head: noWorktree })
|
|
368
366
|
}
|
|
369
367
|
}
|
|
@@ -437,28 +435,11 @@ function collectFilesFromStartPath (startPath, repo, authStatus, ref, worktreePa
|
|
|
437
435
|
}
|
|
438
436
|
|
|
439
437
|
function readFilesFromWorktree (worktreePath, startPath) {
|
|
440
|
-
const cwd = ospath.join(worktreePath, startPath)
|
|
438
|
+
const cwd = ospath.join(worktreePath, startPath, '.') // . shaves off trailing slash
|
|
441
439
|
return fsp.stat(cwd).then(
|
|
442
|
-
(
|
|
443
|
-
if (!
|
|
444
|
-
return
|
|
445
|
-
vfs
|
|
446
|
-
.src(CONTENT_SRC_GLOB, Object.assign({ cwd }, CONTENT_SRC_OPTS))
|
|
447
|
-
.on('error', (err) => {
|
|
448
|
-
if (err.code === 'ENOENT' && err.syscall === 'stat') {
|
|
449
|
-
try {
|
|
450
|
-
if (fs.lstatSync(err.path).isSymbolicLink()) {
|
|
451
|
-
err.message = `Broken symbolic link detected at ${ospath.relative(cwd, err.path)}`
|
|
452
|
-
}
|
|
453
|
-
} catch {}
|
|
454
|
-
} else if (err.code === 'ELOOP') {
|
|
455
|
-
err.message = `Symbolic link cycle detected at ${ospath.relative(cwd, err.path)}`
|
|
456
|
-
}
|
|
457
|
-
reject(err)
|
|
458
|
-
})
|
|
459
|
-
.pipe(relativizeFiles())
|
|
460
|
-
.pipe(collectDataFromStream(resolve))
|
|
461
|
-
)
|
|
440
|
+
(startPathStat) => {
|
|
441
|
+
if (!startPathStat.isDirectory()) throw new Error(`the start path '${startPath}' is not a directory`)
|
|
442
|
+
return srcFs(cwd)
|
|
462
443
|
},
|
|
463
444
|
() => {
|
|
464
445
|
throw new Error(`the start path '${startPath}' does not exist`)
|
|
@@ -466,40 +447,43 @@ function readFilesFromWorktree (worktreePath, startPath) {
|
|
|
466
447
|
)
|
|
467
448
|
}
|
|
468
449
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
450
|
+
function srcFs (cwd) {
|
|
451
|
+
const relpathStart = cwd.length + 1
|
|
452
|
+
return new Promise((resolve, reject, cache = Object.create(null), files = []) =>
|
|
453
|
+
pipeline(
|
|
454
|
+
globStream(CONTENT_SRC_GLOB, Object.assign({ cache, cwd }, CONTENT_SRC_OPTS)),
|
|
455
|
+
forEach(({ path: abspathPosix }, _, done) => {
|
|
456
|
+
if (Array.isArray(cache[abspathPosix])) return done() // detects some directories, but not all
|
|
457
|
+
const abspath = posixify ? ospath.normalize(abspathPosix) : abspathPosix
|
|
458
|
+
const relpath = abspath.substr(relpathStart)
|
|
459
|
+
symlinkAwareStat(abspath).then(
|
|
460
|
+
(stat) => {
|
|
461
|
+
if (stat.isDirectory()) return done() // detects remaining directories
|
|
462
|
+
fsp.readFile(abspath).then(
|
|
463
|
+
(contents) => {
|
|
464
|
+
files.push(new File({ path: posixify ? posixify(relpath) : relpath, contents, stat, src: { abspath } }))
|
|
465
|
+
done()
|
|
466
|
+
},
|
|
467
|
+
(readErr) => {
|
|
468
|
+
done(Object.assign(readErr, { message: readErr.message.replace(`'${abspath}'`, relpath) }))
|
|
469
|
+
}
|
|
470
|
+
)
|
|
471
|
+
},
|
|
472
|
+
(statErr) => {
|
|
473
|
+
if (statErr.symlink) {
|
|
474
|
+
statErr.message =
|
|
475
|
+
statErr.code === 'ELOOP'
|
|
476
|
+
? `Symbolic link cycle detected at ${relpath}`
|
|
477
|
+
: `Broken symbolic link detected at ${relpath}`
|
|
478
|
+
} else {
|
|
479
|
+
statErr.message = statErr.message.replace(`'${abspath}'`, relpath)
|
|
480
|
+
}
|
|
481
|
+
done(statErr)
|
|
482
|
+
}
|
|
483
|
+
)
|
|
484
|
+
}),
|
|
485
|
+
(err) => (err ? reject(err) : resolve(files))
|
|
486
|
+
)
|
|
503
487
|
)
|
|
504
488
|
}
|
|
505
489
|
|
|
@@ -601,7 +585,7 @@ function readGitSymlink (repo, root, parent, { oid }, following) {
|
|
|
601
585
|
let targetParent
|
|
602
586
|
if (parent.dirname) {
|
|
603
587
|
const dirname = parent.dirname + '/'
|
|
604
|
-
target = path.join(dirname, target)
|
|
588
|
+
target = path.join(dirname, target) // join doesn't remove trailing separator
|
|
605
589
|
if (target.startsWith(dirname)) {
|
|
606
590
|
target = target.substr(dirname.length)
|
|
607
591
|
targetParent = parent
|
|
@@ -609,10 +593,12 @@ function readGitSymlink (repo, root, parent, { oid }, following) {
|
|
|
609
593
|
targetParent = root
|
|
610
594
|
}
|
|
611
595
|
} else {
|
|
612
|
-
target = path.normalize(target)
|
|
596
|
+
target = path.normalize(target) // normalize doesn't remove trailing separator
|
|
613
597
|
targetParent = root
|
|
614
598
|
}
|
|
615
|
-
|
|
599
|
+
const targetSegments = target.split('/')
|
|
600
|
+
if (!targetSegments[targetSegments.length - 1]) targetSegments.pop()
|
|
601
|
+
return readGitObjectAtPath(repo, root, targetParent, targetSegments, following)
|
|
616
602
|
})
|
|
617
603
|
}
|
|
618
604
|
const err = { name: 'SymbolicLinkCycleError', code: 'SymbolicLinkCycleError', oid }
|
|
@@ -688,7 +674,7 @@ function loadComponentDescriptor (files, ref, version) {
|
|
|
688
674
|
matched = version[refname]
|
|
689
675
|
} else if (
|
|
690
676
|
!Object.entries(version).some(([pattern, replacement]) => {
|
|
691
|
-
const result = refname.replace(
|
|
677
|
+
const result = refname.replace(makeMatcherRx(pattern, VERSION_MATCHER_OPTS), '\0' + replacement)
|
|
692
678
|
if (result === refname) return false
|
|
693
679
|
matched = result.substr(1)
|
|
694
680
|
return true
|
|
@@ -918,34 +904,13 @@ function isDirectory (url) {
|
|
|
918
904
|
return fsp.stat(url).then((stat) => stat.isDirectory(), invariably.false)
|
|
919
905
|
}
|
|
920
906
|
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
return fsp
|
|
927
|
-
.readdir(dir, { withFileTypes: true })
|
|
928
|
-
.then((lst) =>
|
|
929
|
-
Promise.all(
|
|
930
|
-
lst.map((it) =>
|
|
931
|
-
it.isDirectory()
|
|
932
|
-
? rmdir(ospath.join(dir, it.name))
|
|
933
|
-
: fsp.unlink(ospath.join(dir, it.name)).catch((unlinkErr) => {
|
|
934
|
-
if (unlinkErr.code !== 'ENOENT') throw unlinkErr
|
|
935
|
-
})
|
|
936
|
-
)
|
|
937
|
-
)
|
|
938
|
-
)
|
|
939
|
-
.then(() => fsp.rmdir(dir))
|
|
940
|
-
.catch((err) => {
|
|
941
|
-
if (err.code === 'ENOENT') return
|
|
942
|
-
if (err.code === 'ENOTDIR') {
|
|
943
|
-
return fsp.unlink(dir).catch((unlinkErr) => {
|
|
944
|
-
if (unlinkErr.code !== 'ENOENT') throw unlinkErr
|
|
945
|
-
})
|
|
946
|
-
}
|
|
947
|
-
throw err
|
|
907
|
+
function symlinkAwareStat (path_) {
|
|
908
|
+
return fsp.lstat(path_).then((lstat) => {
|
|
909
|
+
if (!lstat.isSymbolicLink()) return lstat
|
|
910
|
+
return fsp.stat(path_).catch((statErr) => {
|
|
911
|
+
throw Object.assign(statErr, { symlink: true })
|
|
948
912
|
})
|
|
913
|
+
})
|
|
949
914
|
}
|
|
950
915
|
|
|
951
916
|
function tagsSpecified (sources) {
|
|
@@ -1027,6 +992,10 @@ function transformGitCloneError (err, displayUrl) {
|
|
|
1027
992
|
return wrappedErr
|
|
1028
993
|
}
|
|
1029
994
|
|
|
995
|
+
function splitRefPatterns (str) {
|
|
996
|
+
return ~str.indexOf('{') ? str.split(VENTILATED_CSV_RX) : str.split(CSV_RX)
|
|
997
|
+
}
|
|
998
|
+
|
|
1030
999
|
function coerceToString (value) {
|
|
1031
1000
|
return value == null ? '' : String(value)
|
|
1032
1001
|
}
|
|
@@ -1039,10 +1008,11 @@ function findWorktrees (repo, patterns) {
|
|
|
1039
1008
|
if (!patterns.length) return new Map()
|
|
1040
1009
|
const linkedOnly = patterns[0] === '.' ? !(patterns = patterns.slice(1)) : true
|
|
1041
1010
|
let worktreesDir
|
|
1011
|
+
const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
|
|
1042
1012
|
return (patterns.length
|
|
1043
1013
|
? fsp
|
|
1044
1014
|
.readdir((worktreesDir = ospath.join(repo.dir, '.git', 'worktrees')))
|
|
1045
|
-
.then((worktreeNames) =>
|
|
1015
|
+
.then((worktreeNames) => filterRefs(worktreeNames, patterns, patternCache), invariably.emptyArray)
|
|
1046
1016
|
.then((worktreeNames) =>
|
|
1047
1017
|
worktreeNames.length
|
|
1048
1018
|
? Promise.all(
|
package/lib/constants.js
CHANGED
|
@@ -4,23 +4,12 @@ module.exports = Object.freeze({
|
|
|
4
4
|
COMPONENT_DESC_FILENAME: 'antora.yml',
|
|
5
5
|
CONTENT_CACHE_FOLDER: 'content',
|
|
6
6
|
CONTENT_SRC_GLOB: '**/*[!~]',
|
|
7
|
-
CONTENT_SRC_OPTS: { follow: true, nomount: true, nosort: true, nounique: true,
|
|
7
|
+
CONTENT_SRC_OPTS: { follow: true, nomount: true, nosort: true, nounique: true, strict: false, uniqueBy: (m) => m },
|
|
8
8
|
FILE_MODES: { 100644: 0o100666 & ~process.umask(), 100755: 0o100777 & ~process.umask() },
|
|
9
9
|
GIT_CORE: 'antora',
|
|
10
10
|
GIT_OPERATION_LABEL_LENGTH: 8,
|
|
11
11
|
GIT_PROGRESS_PHASES: ['Counting objects', 'Compressing objects', 'Receiving objects', 'Resolving deltas'],
|
|
12
|
-
|
|
13
|
-
bash: true,
|
|
14
|
-
debug: false,
|
|
15
|
-
dot: true,
|
|
16
|
-
fastpaths: false,
|
|
17
|
-
lookbehinds: false,
|
|
18
|
-
noextglob: true,
|
|
19
|
-
noglobstar: true,
|
|
20
|
-
nonegate: true,
|
|
21
|
-
noquantifiers: true,
|
|
22
|
-
strictSlashes: true,
|
|
23
|
-
},
|
|
12
|
+
REF_PATTERN_CACHE_KEY: Symbol('RefPatternCache'),
|
|
24
13
|
SYMLINK_FILE_MODE: '120000',
|
|
25
14
|
VALID_STATE_FILENAME: 'valid',
|
|
26
15
|
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { makeMatcherRx, refMatcherOpts: getMatcherOpts, MATCH_ALL_RX } = require('./matcher')
|
|
4
|
+
|
|
5
|
+
function compileRx (pattern, opts) {
|
|
6
|
+
if (pattern === '*' || pattern === '**') return MATCH_ALL_RX
|
|
7
|
+
return pattern.charAt() === '!' // do our own negate
|
|
8
|
+
? Object.defineProperty(makeMatcherRx(pattern.substr(1), opts), 'negated', { value: true })
|
|
9
|
+
: makeMatcherRx(pattern, opts)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function createMatcher (patterns, cache, opts) {
|
|
13
|
+
const rxs = patterns.map(
|
|
14
|
+
(pattern) =>
|
|
15
|
+
cache.get(pattern) || cache.set(pattern, compileRx(pattern, opts || (opts = getMatcherOpts(cache)))).get(pattern)
|
|
16
|
+
)
|
|
17
|
+
if (rxs[0].negated) rxs.unshift(MATCH_ALL_RX)
|
|
18
|
+
return (candidate) => {
|
|
19
|
+
let matched
|
|
20
|
+
for (const rx of rxs) {
|
|
21
|
+
let voteIfMatched = true
|
|
22
|
+
if (matched) {
|
|
23
|
+
if (!rx.negated) continue
|
|
24
|
+
voteIfMatched = false
|
|
25
|
+
} else if (rx.negated) {
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
if (rx.test(candidate)) matched = voteIfMatched
|
|
29
|
+
}
|
|
30
|
+
return matched
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function filterRefs (candidates, patterns, cache = Object.assign(new Map(), { braces: new Map() })) {
|
|
35
|
+
const isMatch = createMatcher(patterns, cache)
|
|
36
|
+
return candidates.reduce((accum, candidate) => {
|
|
37
|
+
if (isMatch(candidate)) accum.push(candidate)
|
|
38
|
+
return accum
|
|
39
|
+
}, [])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = filterRefs
|
|
@@ -2,17 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
const { homedir } = require('os')
|
|
4
4
|
const expandPath = require('@antora/expand-path-helper')
|
|
5
|
-
const invariably = { void: () => undefined }
|
|
6
5
|
const { promises: fsp } = require('fs')
|
|
6
|
+
const invariably = require('./invariably')
|
|
7
7
|
const ospath = require('path')
|
|
8
8
|
|
|
9
9
|
class GitCredentialManagerStore {
|
|
10
10
|
configure ({ config, startDir }) {
|
|
11
11
|
this.entries = undefined
|
|
12
|
+
this.path = undefined
|
|
12
13
|
this.urls = {}
|
|
13
|
-
if ((this.contents = (config = config || {}).contents)
|
|
14
|
-
this.path = undefined
|
|
15
|
-
} else {
|
|
14
|
+
if (!(this.contents = (config = config || {}).contents) && config.path) {
|
|
16
15
|
this.path = expandPath(config.path, { dot: startDir })
|
|
17
16
|
}
|
|
18
17
|
return this
|
|
@@ -35,14 +34,13 @@ class GitCredentialManagerStore {
|
|
|
35
34
|
'git',
|
|
36
35
|
'credentials'
|
|
37
36
|
)
|
|
38
|
-
contentsPromise = fsp
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
.catch(() =>
|
|
37
|
+
contentsPromise = fsp.access(homeGitCredentialsPath).then(
|
|
38
|
+
() => fsp.readFile(homeGitCredentialsPath, 'utf8'),
|
|
39
|
+
() =>
|
|
42
40
|
fsp
|
|
43
41
|
.access(xdgConfigGitCredentialsPath)
|
|
44
42
|
.then(() => fsp.readFile(xdgConfigGitCredentialsPath, 'utf8'), invariably.void)
|
|
45
|
-
|
|
43
|
+
)
|
|
46
44
|
}
|
|
47
45
|
contentsPromise.then((contents) => {
|
|
48
46
|
if (contents) {
|
package/lib/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Responsible for aggregating the content from multiple repositories and
|
|
7
7
|
* references into a raw aggregate of virtual files that can be organized by a
|
|
8
|
-
* subsequent step in the
|
|
8
|
+
* subsequent step in the generator.
|
|
9
9
|
*
|
|
10
10
|
* @namespace content-aggregator
|
|
11
11
|
*/
|
package/lib/matcher.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { compile: bracesToGroup, expand: expandBraces } = require('braces')
|
|
4
|
+
const { makeRe: makeMatcherRx } = require('picomatch')
|
|
5
|
+
|
|
6
|
+
const BASE_OPTS = {
|
|
7
|
+
bash: true,
|
|
8
|
+
dot: true,
|
|
9
|
+
expandRange: (begin, end, step, opts) => bracesToGroup(opts ? `{${begin}..${end}..${step}}` : `{${begin}..${end}}`),
|
|
10
|
+
fastpaths: false,
|
|
11
|
+
nobracket: true,
|
|
12
|
+
noglobstar: true,
|
|
13
|
+
nonegate: true,
|
|
14
|
+
noquantifiers: true,
|
|
15
|
+
regex: false,
|
|
16
|
+
strictSlashes: true,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
MATCH_ALL_RX: { test: () => true },
|
|
21
|
+
expandBraces,
|
|
22
|
+
makeMatcherRx,
|
|
23
|
+
pathMatcherOpts: Object.assign({}, BASE_OPTS, { dot: false }),
|
|
24
|
+
refMatcherOpts: (cache) =>
|
|
25
|
+
Object.assign({}, BASE_OPTS, {
|
|
26
|
+
expandRange: (begin, end, step, opts) => {
|
|
27
|
+
const pattern = opts ? `{${begin}..${end}..${step}}` : `{${begin}..${end}}`
|
|
28
|
+
return cache.braces.get(pattern) || cache.braces.set(pattern, bracesToGroup(pattern)).get(pattern)
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
versionMatcherOpts: Object.assign({}, BASE_OPTS, { nonegate: false }),
|
|
32
|
+
}
|
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { expand: expandBraces } = require('braces')
|
|
4
3
|
const flattenDeep = require('./flatten-deep')
|
|
5
4
|
const { promises: fsp } = require('fs')
|
|
6
5
|
const git = require('./git')
|
|
7
|
-
const invariably =
|
|
8
|
-
const {
|
|
6
|
+
const invariably = require('./invariably')
|
|
7
|
+
const { expandBraces, makeMatcherRx, pathMatcherOpts: MATCHER_OPTS } = require('./matcher')
|
|
9
8
|
|
|
10
|
-
const
|
|
11
|
-
const RX_MAGIC_DETECTOR = /[*{]/
|
|
12
|
-
const RX_QUESTION_MARK = /\?/g
|
|
13
|
-
const PICOMATCH_OPTS = { nobracket: true, noextglob: true, noglobstar: true, noquantifiers: true }
|
|
14
|
-
const PICOMATCH_NEGATED_OPTS = { nobracket: true, noextglob: true, noquantifiers: true }
|
|
9
|
+
const NON_GLOB_SPECIAL_CHARS_RX = /[.+?^${}()|[\]\\]/g
|
|
10
|
+
const RX_MAGIC_DETECTOR = /[*{(]/
|
|
15
11
|
|
|
16
12
|
function resolvePathGlobs (base, patterns, listDirents, retrievePath, tree = { path: '' }) {
|
|
17
13
|
return patterns.reduce((paths, pattern) => {
|
|
18
14
|
if (pattern.charAt() === '!') {
|
|
19
15
|
return paths.then((resolvedPaths) => {
|
|
20
16
|
if (resolvedPaths.length) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return resolvedPaths.filter(rx.test.bind(rx))
|
|
17
|
+
const rx = makeMatcherRx(pattern.substr(1), MATCHER_OPTS)
|
|
18
|
+
return resolvedPaths.filter((it) => !rx.test(it))
|
|
24
19
|
} else {
|
|
25
20
|
return resolvedPaths
|
|
26
21
|
}
|
|
@@ -38,15 +33,14 @@ async function glob (base, patternSegments, listDirents, retrievePath, { oid, pa
|
|
|
38
33
|
let patternSegment = patternSegments[0]
|
|
39
34
|
patternSegments = patternSegments.slice(1)
|
|
40
35
|
if (RX_MAGIC_DETECTOR.test(patternSegment)) {
|
|
41
|
-
let isMatch
|
|
42
|
-
let explicit
|
|
36
|
+
let isMatch, explicit
|
|
43
37
|
if (patternSegment === '*') {
|
|
44
38
|
isMatch = (it) => it.charAt() !== '.'
|
|
39
|
+
} else if (~patternSegment.indexOf('(')) {
|
|
40
|
+
isMatch = (isMatch = makeMatcherRx(patternSegment, MATCHER_OPTS)).test.bind(isMatch)
|
|
45
41
|
} else if (~patternSegment.indexOf('{')) {
|
|
46
42
|
if (globbed) {
|
|
47
|
-
|
|
48
|
-
if (~patternSegment.indexOf('?')) patternSegment = patternSegment.replace(RX_QUESTION_MARK, '\\?')
|
|
49
|
-
isMatch = (isMatch = makePicomatchRx(patternSegment, PICOMATCH_OPTS)).test.bind(isMatch)
|
|
43
|
+
isMatch = (isMatch = makeMatcherRx(patternSegment, MATCHER_OPTS)).test.bind(isMatch)
|
|
50
44
|
} else if (~patternSegment.indexOf('*')) {
|
|
51
45
|
const [wildPatterns, literals] = expandBraces(patternSegment).reduce(
|
|
52
46
|
([wild, literal], it) => (~it.indexOf('*') ? [[...wild, it], literal] : [wild, [...literal, it]]),
|
|
@@ -58,7 +52,7 @@ async function glob (base, patternSegments, listDirents, retrievePath, { oid, pa
|
|
|
58
52
|
return expandBraces(patternSegment).map((it) => joinPath(path, it))
|
|
59
53
|
}
|
|
60
54
|
} else {
|
|
61
|
-
isMatch = (isMatch =
|
|
55
|
+
isMatch = (isMatch = makeSingleMatcherRx(patternSegment)).test.bind(isMatch)
|
|
62
56
|
}
|
|
63
57
|
let dirents = await listDirents(base, oid || path)
|
|
64
58
|
if (explicit) dirents = dirents.filter((dirent) => !explicit.has(dirent.name))
|
|
@@ -90,22 +84,15 @@ async function glob (base, patternSegments, listDirents, retrievePath, { oid, pa
|
|
|
90
84
|
})
|
|
91
85
|
} else if ((patternSegment += '/' + patternSegments.join('/')).indexOf('{')) {
|
|
92
86
|
return expandBraces(patternSegment).map((it) => joinPath(path, it))
|
|
93
|
-
} else {
|
|
94
|
-
return [joinPath(path, patternSegment)]
|
|
95
87
|
}
|
|
88
|
+
return [joinPath(path, patternSegment)]
|
|
96
89
|
} else if (globbed) {
|
|
97
90
|
return (await retrievePath(base, { oid, path }, patternSegment)) ? [joinPath(path, patternSegment)] : []
|
|
98
|
-
} else {
|
|
99
|
-
return [joinPath(path, patternSegment)]
|
|
100
91
|
}
|
|
92
|
+
return [joinPath(path, patternSegment)]
|
|
101
93
|
}
|
|
102
94
|
}
|
|
103
95
|
|
|
104
|
-
function regexpEscapeWithGlob (str) {
|
|
105
|
-
// we don't escape "-" since it's meaningless in a literal
|
|
106
|
-
return str.replace(RX_ESCAPE_EXCEPT_GLOB, '\\$&').replace('*', '.*')
|
|
107
|
-
}
|
|
108
|
-
|
|
109
96
|
function extractMagicBase (patternSegments, base) {
|
|
110
97
|
let nextSegment
|
|
111
98
|
if (patternSegments.length) {
|
|
@@ -135,12 +122,18 @@ function makeAlternationMatcherRx (patterns) {
|
|
|
135
122
|
return new RegExp('^(?:' + patterns.map(patternToRx).join('|') + ')$')
|
|
136
123
|
}
|
|
137
124
|
|
|
138
|
-
function
|
|
125
|
+
function makeSingleMatcherRx (pattern) {
|
|
139
126
|
return new RegExp('^' + patternToRx(pattern) + '$')
|
|
140
127
|
}
|
|
141
128
|
|
|
142
129
|
function patternToRx (pattern) {
|
|
143
|
-
return (
|
|
130
|
+
return (
|
|
131
|
+
(pattern.charAt() === '.' ? '' : '(?!\\.)') +
|
|
132
|
+
pattern
|
|
133
|
+
.replace(NON_GLOB_SPECIAL_CHARS_RX, '\\$&')
|
|
134
|
+
.replace('\\\\*', '\\x2a')
|
|
135
|
+
.replace('*', '.*?')
|
|
136
|
+
)
|
|
144
137
|
}
|
|
145
138
|
|
|
146
139
|
function readdirWithFileTypes (dir) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/content-aggregator",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-beta.4",
|
|
4
4
|
"description": "Fetches and aggregates content from distributed sources for use in an Antora documentation pipeline.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"author": "OpenDevise Inc. (https://opendevise.com)",
|
|
@@ -21,21 +21,23 @@
|
|
|
21
21
|
"@antora/user-require-helper": "~2.0",
|
|
22
22
|
"braces": "~3.0",
|
|
23
23
|
"cache-directory": "~2.0",
|
|
24
|
-
"camelcase-keys": "~
|
|
24
|
+
"camelcase-keys": "~7.0",
|
|
25
|
+
"glob-stream": "~7.0",
|
|
25
26
|
"hpagent": "~0.1.0",
|
|
26
27
|
"isomorphic-git": "~1.10",
|
|
27
28
|
"js-yaml": "~4.1",
|
|
28
|
-
"matcher": "~4.0",
|
|
29
29
|
"multi-progress": "~4.0",
|
|
30
30
|
"picomatch": "~2.3",
|
|
31
31
|
"progress": "~2.0",
|
|
32
32
|
"should-proxy": "~1.0",
|
|
33
33
|
"simple-get": "~4.0",
|
|
34
|
-
"vinyl": "~2.2"
|
|
35
|
-
|
|
34
|
+
"vinyl": "~2.2"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"node-git-server": "~0.6"
|
|
36
38
|
},
|
|
37
39
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
40
|
+
"node": ">=12.21.0"
|
|
39
41
|
},
|
|
40
42
|
"files": [
|
|
41
43
|
"lib/"
|
|
@@ -50,5 +52,6 @@
|
|
|
50
52
|
"static site",
|
|
51
53
|
"web publishing"
|
|
52
54
|
],
|
|
53
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "8a142499e9f1a9e0631777796e06dd6c010d3a90",
|
|
56
|
+
"readmeFilename": "README.md"
|
|
54
57
|
}
|