@antora/content-aggregator 3.1.10 → 3.1.11

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.
@@ -1,32 +1,32 @@
1
1
  'use strict'
2
2
 
3
3
  const computeOrigin = require('./compute-origin')
4
- const { createHash } = require('crypto')
4
+ const { createHash } = require('node:crypto')
5
5
  const createGitHttpPlugin = require('./git-plugin-http')
6
6
  const decodeUint8Array = require('./decode-uint8-array')
7
7
  const deepClone = require('./deep-clone')
8
- const EventEmitter = require('events')
8
+ const EventEmitter = require('node:events')
9
9
  const expandPath = require('@antora/expand-path-helper')
10
10
  const File = require('./file')
11
11
  const filterRefs = require('./filter-refs')
12
- const fs = require('fs')
12
+ const fs = require('node:fs')
13
13
  const { promises: fsp } = fs
14
14
  const getCacheDir = require('cache-directory')
15
15
  const GitCredentialManagerStore = require('./git-credential-manager-store')
16
16
  const git = require('./git')
17
17
  const { NotFoundError, ObjectTypeError, UnknownTransportError, UrlParseError } = git.Errors
18
18
  const { globStream } = require('fast-glob')
19
- const { inspect } = require('util')
19
+ const { inspect } = require('node:util')
20
20
  const invariably = require('./invariably')
21
21
  const logger = require('./logger')
22
22
  const { makeMatcherRx, versionMatcherOpts: VERSION_MATCHER_OPTS } = require('./matcher')
23
23
  const MultiProgress = require('multi-progress') // calls require('progress') as a peer dependencies
24
- const ospath = require('path')
24
+ const ospath = require('node:path')
25
25
  const { posix: path } = ospath
26
26
  const posixify = require('./posixify')
27
27
  const removeGitSuffix = require('./remove-git-suffix')
28
28
  const { fs: resolvePathGlobsFs, git: resolvePathGlobsGit } = require('./resolve-path-globs')
29
- const { pipeline, Writable } = require('stream')
29
+ const { pipeline, Writable } = require('node:stream')
30
30
  const forEach = (write) => new Writable({ objectMode: true, write })
31
31
  const userRequire = require('@antora/user-require-helper')
32
32
  const yaml = require('js-yaml')
@@ -102,7 +102,8 @@ function aggregateContent (playbook) {
102
102
  }, new Map())
103
103
  const progress = quiet ? undefined : createProgress(sourcesByUrl.keys(), process.stdout)
104
104
  const refPatternCache = Object.assign(new Map(), { braces: new Map() })
105
- const loadOpts = { cacheDir, fetch, gitPlugins, progress, startDir, refPatternCache }
105
+ const fetchConfig = { always: fetch, depth: Math.max(0, gitConfig.fetchDepth ?? 1) }
106
+ const loadOpts = { cacheDir, fetch: fetchConfig, gitPlugins, progress, startDir, refPatternCache }
106
107
  return collectFiles(sourcesByUrl, loadOpts, concurrency).then(buildAggregate, (err) => {
107
108
  progress?.terminate()
108
109
  throw err
@@ -110,11 +111,11 @@ function aggregateContent (playbook) {
110
111
  })
111
112
  }
112
113
 
113
- async function collectFiles (sourcesByUrl, loadOpts, concurrency, fetchedUrls) {
114
+ async function collectFiles (sourcesByUrl, loadOpts, concurrency, fetchedUrls = []) {
114
115
  const loadTasks = [...sourcesByUrl.entries()].map(([url, sources]) => {
115
116
  const loadOptsForUrl = Object.assign({}, loadOpts)
116
- if (loadOpts.fetch && fetchedUrls && fetchedUrls.length && fetchedUrls.includes(url)) loadOptsForUrl.fetch = false
117
- if (tagsSpecified(sources)) loadOptsForUrl.fetchTags = true
117
+ if (loadOpts.fetch.always && fetchedUrls.length && fetchedUrls.includes(url)) loadOptsForUrl.fetch.always = false
118
+ if (tagsSpecified(sources)) loadOptsForUrl.fetch.tags = true
118
119
  return loadRepository.bind(null, url, loadOptsForUrl, { url, sources })
119
120
  })
120
121
  return gracefulPromiseAllWithLimit(loadTasks, concurrency.fetch).then(([results, rejections]) => {
@@ -163,7 +164,7 @@ async function loadRepository (url, opts, result = {}) {
163
164
  if (~url.indexOf(':') && GIT_URI_DETECTOR_RX.test(url)) {
164
165
  let credentials, displayUrl
165
166
  ;({ displayUrl, url, credentials } = extractCredentials(url))
166
- const { cacheDir, fetch, fetchTags, gitPlugins, progress } = opts
167
+ const { cacheDir, fetch, gitPlugins, progress } = opts
167
168
  dir = ospath.join(cacheDir, generateCloneFolderName(displayUrl))
168
169
  // NOTE the presence of the url property on the repo object implies the repository is remote
169
170
  repo = { cache, dir, fs, gitdir: dir, noCheckout: true, url }
@@ -171,9 +172,9 @@ async function loadRepository (url, opts, result = {}) {
171
172
  const validStateFile = ospath.join(dir, VALID_STATE_FILENAME)
172
173
  try {
173
174
  await fsp.access(validStateFile)
174
- if (fetch) {
175
+ if (fetch.always) {
175
176
  await fsp.unlink(validStateFile)
176
- const fetchOpts = buildFetchOptions(repo, progress, displayUrl, credentials, gitPlugins, fetchTags, 'fetch')
177
+ const fetchOpts = buildFetchOptions(repo, progress, displayUrl, credentials, gitPlugins, fetch, 'fetch')
177
178
  await git
178
179
  .fetch(fetchOpts)
179
180
  .then(() => {
@@ -193,7 +194,7 @@ async function loadRepository (url, opts, result = {}) {
193
194
  } catch (gitErr) {
194
195
  await fsp['rm' in fsp ? 'rm' : 'rmdir'](dir, { recursive: true, force: true })
195
196
  if (gitErr.rethrow) throw transformGitCloneError(gitErr, displayUrl)
196
- const fetchOpts = buildFetchOptions(repo, progress, displayUrl, credentials, gitPlugins, fetchTags, 'clone')
197
+ const fetchOpts = buildFetchOptions(repo, progress, displayUrl, credentials, gitPlugins, fetch, 'clone')
197
198
  await git
198
199
  .clone(fetchOpts)
199
200
  .then(() => git.resolveRef(Object.assign({ ref: 'HEAD', depth: 1 }, repo)))
@@ -210,8 +211,15 @@ async function loadRepository (url, opts, result = {}) {
210
211
  .then(() => fetchOpts.onProgress?.finish())
211
212
  }
212
213
  } else if (await isDirectory((dir = expandPath(url, { dot: opts.startDir })))) {
213
- const gitdir = ospath.join(dir, '.git')
214
- repo = (await isDirectory(gitdir)) ? { cache, dir, fs, gitdir } : { cache, dir, fs, gitdir: dir, noCheckout: true }
214
+ const dotgit = ospath.join(dir, '.git')
215
+ const dotgitStat = await fsp.stat(dotgit).catch(() => ({ isFile: invariably.false, isDirectory: invariably.false }))
216
+ if (dotgitStat.isDirectory()) {
217
+ repo = { cache, dir, fs, gitdir: dotgit }
218
+ } else if (dotgitStat.isFile()) {
219
+ repo = await resolveRepositoryFromWorktree({ cache, dir, fs, gitdir: dotgit })
220
+ } else {
221
+ repo = { cache, dir, fs, gitdir: dir, noCheckout: true }
222
+ }
215
223
  try {
216
224
  await git.resolveRef(Object.assign({ ref: 'HEAD', depth: 1 }, repo))
217
225
  } catch {
@@ -246,14 +254,27 @@ async function selectStartPathsForRepository (repo, authStatus, sources) {
246
254
  const originUrls = {}
247
255
  for (const source of sources) {
248
256
  const { version, editUrl } = source
249
- // NOTE if repository is managed (has a url property), we can assume the remote name is origin
250
- // TODO if the repo has no remotes, then remoteName should be undefined
251
- const remoteName = repo.url ? 'origin' : source.remote || 'origin'
252
- const originUrl = repo.url || (originUrls[remoteName] ||= await resolveRemoteUrl(repo, remoteName))
257
+ let remoteName, originUrl
258
+ if (repo.url) {
259
+ remoteName = 'origin' // NOTE if repository is managed (has url property), we can assume remote name is origin
260
+ originUrl = repo.url
261
+ } else {
262
+ remoteName = source.remote || 'origin'
263
+ originUrl =
264
+ remoteName in originUrls
265
+ ? originUrls[remoteName]
266
+ : (originUrls[remoteName] = await resolveRemoteUrl(repo, remoteName))
267
+ if (!originUrl) {
268
+ remoteName = undefined
269
+ if ((originUrl = posixify ? 'file:///' + posixify(repo.dir) : 'file://' + repo.dir).indexOf(' ')) {
270
+ originUrl = originUrl.replace(SPACE_RX, '%20')
271
+ }
272
+ }
273
+ }
253
274
  const refs = await selectReferences(source, repo, remoteName)
254
275
  if (refs.length) {
255
276
  for (const ref of refs) {
256
- for (const startPath of await selectStartPaths(source, repo, remoteName, ref)) {
277
+ for (const startPath of await selectStartPaths(source, repo, ref)) {
257
278
  startPaths.push({ startPath, ref, originUrl, editUrl, version })
258
279
  }
259
280
  }
@@ -272,7 +293,7 @@ async function selectStartPathsForRepository (repo, authStatus, sources) {
272
293
 
273
294
  // QUESTION should we resolve HEAD to a ref eagerly to avoid having to do a match on it?
274
295
  async function selectReferences (source, repo, remote) {
275
- let { branches: branchPatterns, tags: tagPatterns, worktrees: worktreePatterns = '.' } = source
296
+ let { branches: branchPatterns, tags: tagPatterns, worktrees: worktreePatterns } = source
276
297
  const managed = 'url' in repo
277
298
  const isBare = managed || repo.noCheckout
278
299
  const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
@@ -292,7 +313,16 @@ async function selectReferences (source, repo, remote) {
292
313
  }
293
314
  }
294
315
  }
295
- if (!branchPatterns) return [...refs.values()]
316
+ if (
317
+ !branchPatterns ||
318
+ !(branchPatterns = Array.isArray(branchPatterns)
319
+ ? branchPatterns.map((pattern) => String(pattern))
320
+ : splitRefPatterns(String(branchPatterns))).length
321
+ ) {
322
+ return [...refs.values()]
323
+ }
324
+ const worktreeName = repo.worktreeName // possibly switch to worktree property ({ name, dir}) in future
325
+ if (worktreeName) branchPatterns = branchPatterns.map((it) => (it === 'HEAD' ? 'HEAD@' + worktreeName : it))
296
326
  if (worktreePatterns) {
297
327
  if (worktreePatterns === '.') {
298
328
  worktreePatterns = ['.']
@@ -302,14 +332,14 @@ async function selectReferences (source, repo, remote) {
302
332
  worktreePatterns = Array.isArray(worktreePatterns)
303
333
  ? worktreePatterns.map((pattern) => String(pattern))
304
334
  : splitRefPatterns(String(worktreePatterns))
335
+ if (worktreeName) worktreePatterns = worktreePatterns.map((it) => (it === '@' ? worktreeName : it))
305
336
  }
306
337
  } else {
307
- worktreePatterns = []
338
+ worktreePatterns = worktreePatterns === undefined ? [worktreeName || '.'] : []
308
339
  }
309
- const branchPatternsString = String(branchPatterns)
310
- if (branchPatternsString === 'HEAD' || branchPatternsString === '.') {
311
- const currentBranch = await getCurrentBranchName(repo, remote)
312
- if (currentBranch) {
340
+ let currentBranch
341
+ if (branchPatterns.length === 1 && (branchPatterns[0] === 'HEAD' || branchPatterns[0] === '.')) {
342
+ if ((currentBranch = await getCurrentBranchName(repo, remote).then((branch) => branch ?? false))) {
313
343
  branchPatterns = [currentBranch]
314
344
  } else if (isBare) {
315
345
  return [...refs.values()]
@@ -319,17 +349,11 @@ async function selectReferences (source, repo, remote) {
319
349
  refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
320
350
  return [...refs.values()]
321
351
  }
322
- } else if (
323
- (branchPatterns = Array.isArray(branchPatterns)
324
- ? branchPatterns.map((pattern) => String(pattern))
325
- : splitRefPatterns(branchPatternsString)).length
326
- ) {
352
+ } else {
327
353
  let headBranchIdx
328
354
  // NOTE we can assume at least two entries if HEAD or . are present
329
355
  if (~(headBranchIdx = branchPatterns.indexOf('HEAD')) || ~(headBranchIdx = branchPatterns.indexOf('.'))) {
330
- const currentBranch = await getCurrentBranchName(repo, remote)
331
- if (currentBranch) {
332
- // NOTE ignore if current branch is already in list
356
+ if ((currentBranch = await getCurrentBranchName(repo, remote).then((branch) => branch ?? false))) {
333
357
  if (~branchPatterns.indexOf(currentBranch)) {
334
358
  branchPatterns.splice(headBranchIdx, 1)
335
359
  } else {
@@ -348,11 +372,11 @@ async function selectReferences (source, repo, remote) {
348
372
  branchPatterns.splice(headBranchIdx, 1)
349
373
  }
350
374
  }
351
- } else {
352
- return [...refs.values()]
353
375
  }
354
376
  // NOTE isomorphic-git includes HEAD in list of remote branches (see https://isomorphic-git.org/docs/listBranches)
355
- const remoteBranches = (await git.listBranches(Object.assign({ remote }, repo))).filter((it) => it !== 'HEAD')
377
+ const remoteBranches = remote
378
+ ? (await git.listBranches(Object.assign({ remote }, repo))).filter((it) => it !== 'HEAD')
379
+ : []
356
380
  if (remoteBranches.length) {
357
381
  for (const shortname of filterRefs(remoteBranches, branchPatterns, patternCache)) {
358
382
  const fullname = 'remotes/' + remote + '/' + shortname
@@ -361,11 +385,27 @@ async function selectReferences (source, repo, remote) {
361
385
  }
362
386
  // NOTE only consider local branches if repo has a worktree or there are no remote tracking branches
363
387
  if (!isBare) {
364
- const localBranches = await git.listBranches(repo)
388
+ const localBranches = await git.listBranches(repo).then((branches) => {
389
+ if (branches.length) return branches
390
+ if (currentBranch == null) return getCurrentBranchName(repo).then((branch) => (branch ? [branch] : []))
391
+ return currentBranch ? [currentBranch] : []
392
+ })
365
393
  if (localBranches.length) {
366
394
  const worktrees = await findWorktrees(repo, worktreePatterns)
367
- for (const shortname of filterRefs(localBranches, branchPatterns, patternCache)) {
368
- const head = worktrees.get(shortname) || noWorktree
395
+ let onMatch
396
+ if ((worktreePatterns.join('') || '.') !== '.') {
397
+ const symbolicNames = new Map()
398
+ worktrees.forEach(({ name, symbolicName = 'HEAD@' + name }, shortname) => {
399
+ localBranches.push(symbolicName)
400
+ symbolicNames.set(symbolicName, shortname)
401
+ })
402
+ onMatch = (candidate, { pattern }) => {
403
+ const shortname = symbolicNames.get(candidate)
404
+ return shortname ? (pattern.startsWith('HEAD@') ? shortname : undefined) : candidate
405
+ }
406
+ }
407
+ for (const shortname of filterRefs(localBranches, branchPatterns, patternCache, onMatch)) {
408
+ const head = (worktrees.get(shortname) || { head: noWorktree }).head
369
409
  refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head })
370
410
  }
371
411
  }
@@ -385,18 +425,16 @@ async function selectReferences (source, repo, remote) {
385
425
  * Returns the current branch name or undefined if the HEAD is detached.
386
426
  */
387
427
  function getCurrentBranchName (repo, remote) {
388
- let refPromise
389
- if (repo.noCheckout) {
390
- refPromise = git
391
- .resolveRef(Object.assign({ ref: 'refs/remotes/' + remote + '/HEAD', depth: 2 }, repo))
392
- .catch(() => git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo)))
393
- } else {
394
- refPromise = git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo))
395
- }
396
- return refPromise.then((ref) => (ref.startsWith('refs/') ? ref.replace(SHORTEN_REF_RX, '') : undefined))
428
+ return (
429
+ remote && repo.noCheckout
430
+ ? git
431
+ .resolveRef(Object.assign({ ref: 'refs/remotes/' + remote + '/HEAD', depth: 2 }, repo))
432
+ .catch(() => git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo)))
433
+ : git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo))
434
+ ).then((ref) => (ref.startsWith('refs/') ? ref.replace(SHORTEN_REF_RX, '') : undefined))
397
435
  }
398
436
 
399
- async function selectStartPaths (source, repo, remoteName, ref) {
437
+ async function selectStartPaths (source, repo, ref) {
400
438
  const url = repo.url
401
439
  const displayUrl = url || repo.dir
402
440
  const worktreePath = ref.head
@@ -711,18 +749,18 @@ function loadComponentDescriptor (files, ref, version) {
711
749
  if (!version) {
712
750
  if (version === undefined) throw new Error(`${COMPONENT_DESC_FILENAME} is missing a version`)
713
751
  if (version === false) throw new Error(`${COMPONENT_DESC_FILENAME} has an invalid version`)
714
- version = '' + (typeof version === 'number' ? version : '')
752
+ version = typeof version === 'number' ? '' + version : ''
715
753
  } else if (version === true) {
716
754
  version = ref.shortname.replace(PATH_SEPARATOR_RX, '-')
717
755
  } else if (version.constructor === Object) {
718
756
  const refname = ref.shortname
719
757
  let matched
720
758
  if (refname in version) {
721
- matched = version[refname]
759
+ matched = '' + (version[refname] ?? '')
722
760
  } else if (
723
761
  !Object.entries(version).some(([pattern, replacement]) => {
724
- const result = refname.replace(makeMatcherRx(pattern, VERSION_MATCHER_OPTS), '\0' + replacement)
725
- if (result === refname) return false
762
+ const result = refname.replace(makeMatcherRx(pattern, VERSION_MATCHER_OPTS), '\0' + (replacement ?? ''))
763
+ if (result === refname) return false // no match
726
764
  matched = result.substr(1)
727
765
  return true
728
766
  })
@@ -737,7 +775,7 @@ function loadComponentDescriptor (files, ref, version) {
737
775
  throw new Error(`version in ${COMPONENT_DESC_FILENAME} cannot have path segments: ${version}`)
738
776
  }
739
777
  data.version = version
740
- return camelCaseKeys(data, ['asciidoc'])
778
+ return camelCaseKeys(data, ['asciidoc', 'ext'])
741
779
  }
742
780
 
743
781
  function assignFileProperties (file, origin) {
@@ -754,18 +792,20 @@ function assignFileProperties (file, origin) {
754
792
  return file
755
793
  }
756
794
 
757
- function buildFetchOptions (repo, progress, displayUrl, credentialsFromUrl, gitPlugins, fetchTags, operation) {
795
+ function buildFetchOptions (repo, progress, displayUrl, credentialsFromUrl, gitPlugins, fetch, operation) {
758
796
  const { credentialManager, http, urlRouter } = gitPlugins
797
+ const corsProxy = false
798
+ const depth = fetch.depth || undefined
759
799
  const onAuth = resolveCredentials.bind(credentialManager, new Map().set(undefined, credentialsFromUrl))
760
800
  const onAuthFailure = onAuth
761
801
  const onAuthSuccess = (url) => credentialManager.approved({ url })
762
- const opts = Object.assign({ corsProxy: false, depth: 1, http, onAuth, onAuthFailure, onAuthSuccess }, repo)
802
+ const opts = Object.assign({ corsProxy, depth, http, onAuth, onAuthFailure, onAuthSuccess }, repo)
763
803
  if (urlRouter) opts.url = urlRouter.ensureGitSuffix(opts.url)
764
804
  if (progress) opts.onProgress = createProgressListener(progress, displayUrl, operation)
765
805
  if (operation === 'fetch') {
766
806
  opts.prune = true
767
- if (fetchTags) opts.tags = opts.pruneTags = true
768
- } else if (!fetchTags) {
807
+ if (fetch.tags) opts.tags = opts.pruneTags = true
808
+ } else if (!fetch.tags) {
769
809
  opts.noTags = true
770
810
  }
771
811
  return opts
@@ -860,7 +900,11 @@ function resolveCredentials (credentialsFromUrlHolder, url, auth) {
860
900
  }
861
901
 
862
902
  function identifyAuthStatus (credentialManager, credentials, url) {
863
- return credentials ? 'auth-embedded' : credentialManager.status({ url }) ? 'auth-required' : undefined
903
+ const status = credentialManager.status({ url })
904
+ if (credentials) {
905
+ return typeof status === 'string' && status.startsWith('requested,') ? 'auth-required' : 'auth-embedded'
906
+ }
907
+ if (status != null) return 'auth-required'
864
908
  }
865
909
 
866
910
  /**
@@ -885,21 +929,18 @@ function generateCloneFolderName (url) {
885
929
  *
886
930
  * @param {Repository} repo - The repository on which to operate.
887
931
  * @param {String} remoteName - The name of the remote to resolve.
888
- * @returns {String} The URL of the specified remote, if defined, or the file URI to the local repository.
932
+ * @returns {String} The URL of the specified remote, if defined
889
933
  */
890
934
  function resolveRemoteUrl (repo, remoteName) {
891
935
  return git.getConfig(Object.assign({ path: 'remote.' + remoteName + '.url' }, repo)).then((url) => {
892
- if (url) {
893
- if (url.startsWith('https://') || url.startsWith('http://')) {
894
- return ~url.indexOf('@') ? url.replace(URL_AUTH_CLEANER_RX, '$1') : url
895
- }
896
- if (url.startsWith('git@')) return 'https://' + url.substr(4).replace(':', '/')
897
- if (url.startsWith('ssh://')) {
898
- return 'https://' + url.substr(url.indexOf('@') + 1 || 6).replace(URL_PORT_CLEANER_RX, '$1')
899
- }
936
+ if (!url) return
937
+ if (url.startsWith('https://') || url.startsWith('http://')) {
938
+ return ~url.indexOf('@') ? url.replace(URL_AUTH_CLEANER_RX, '$1') : url
939
+ }
940
+ if (url.startsWith('git@')) return 'https://' + url.substr(4).replace(':', '/')
941
+ if (url.startsWith('ssh://')) {
942
+ return 'https://' + url.substr(url.indexOf('@') + 1 || 6).replace(URL_PORT_CLEANER_RX, '$1')
900
943
  }
901
- url = posixify ? 'file:///' + posixify(repo.dir) : 'file://' + repo.dir
902
- return ~url.indexOf(' ') ? url.replace(SPACE_RX, '%20') : url
903
944
  })
904
945
  }
905
946
 
@@ -1017,39 +1058,50 @@ function coerceToString (value) {
1017
1058
  return value == null ? '' : String(value)
1018
1059
  }
1019
1060
 
1061
+ function resolveRepositoryFromWorktree (repo) {
1062
+ return fsp
1063
+ .readFile(repo.gitdir, 'utf8')
1064
+ .then((contents) => contents.substr(8).trimEnd())
1065
+ .then((worktreeGitdir) =>
1066
+ fsp.readFile(ospath.join(worktreeGitdir, 'commondir'), 'utf8').then(
1067
+ (contents) => {
1068
+ const gitdir = ospath.join(worktreeGitdir, contents.trimEnd())
1069
+ const dir = ospath.basename(gitdir) === '.git' ? ospath.dirname(gitdir) : gitdir
1070
+ return Object.assign(repo, { dir, gitdir, worktreeName: ospath.basename(worktreeGitdir) })
1071
+ },
1072
+ () => repo
1073
+ )
1074
+ )
1075
+ }
1076
+
1020
1077
  function findWorktrees (repo, patterns) {
1021
1078
  if (!patterns.length) return new Map()
1022
- const linkedOnly = patterns[0] === '.' ? !(patterns = patterns.slice(1)) : true
1023
- let worktreesDir
1079
+ const mainWorktree =
1080
+ patterns[0] === '.' && (patterns = patterns.slice(1))
1081
+ ? getCurrentBranchName(repo).then((branch) => branch && [branch, { head: repo.dir, name: '.' }])
1082
+ : Promise.resolve()
1083
+ const worktreesDir = patterns.length ? ospath.join(repo.dir, '.git', 'worktrees') : undefined
1024
1084
  const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
1025
1085
  return (
1026
- patterns.length
1086
+ worktreesDir
1027
1087
  ? fsp
1028
- .readdir((worktreesDir = ospath.join(repo.dir, '.git', 'worktrees')))
1088
+ .readdir(worktreesDir)
1029
1089
  .then((worktreeNames) => filterRefs(worktreeNames, patterns, patternCache), invariably.emptyArray)
1030
1090
  .then((worktreeNames) =>
1031
- worktreeNames.length
1032
- ? Promise.all(
1033
- worktreeNames.map((worktreeName) => {
1034
- const gitdir = ospath.resolve(worktreesDir, worktreeName)
1035
- // NOTE uses name of worktree as branch name if HEAD is detached
1036
- return git
1037
- .currentBranch(Object.assign({}, repo, { gitdir }))
1038
- .then((branch = worktreeName) =>
1039
- fsp
1040
- .readFile(ospath.join(gitdir, 'gitdir'), 'utf8')
1041
- .then((contents) => ({ branch, dir: ospath.dirname(contents.trimEnd()) }))
1042
- )
1043
- })
1044
- ).then((entries) => entries.reduce((accum, it) => accum.set(it.branch, it.dir), new Map()))
1045
- : new Map()
1091
+ Promise.all(
1092
+ worktreeNames.map((worktreeName) => {
1093
+ const gitdir = ospath.resolve(worktreesDir, worktreeName)
1094
+ // NOTE branch name defaults to worktree name if HEAD is detached
1095
+ return getCurrentBranchName(Object.assign({}, repo, { gitdir })).then((branch = worktreeName) =>
1096
+ fsp
1097
+ .readFile(ospath.join(gitdir, 'gitdir'), 'utf8')
1098
+ .then((contents) => [branch, { head: ospath.dirname(contents.trimEnd()), name: worktreeName }])
1099
+ )
1100
+ })
1101
+ )
1046
1102
  )
1047
- : Promise.resolve(new Map())
1048
- ).then((worktrees) =>
1049
- linkedOnly
1050
- ? worktrees
1051
- : git.currentBranch(repo).then((branch) => (branch ? worktrees.set(branch, repo.dir) : worktrees))
1052
- )
1103
+ : Promise.resolve()
1104
+ ).then((entries = []) => mainWorktree.then((entry) => new Map(entry ? entries.push(entry) && entries : entries)))
1053
1105
  }
1054
1106
 
1055
1107
  async function gracefulPromiseAllWithLimit (tasks, limit = Infinity) {
@@ -1,10 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const { posix: path } = require('path')
3
+ const { posix: path } = require('node:path')
4
4
  const posixify = require('./posixify')
5
5
  const removeGitSuffix = require('./remove-git-suffix')
6
6
 
7
- const EDIT_URL_TEMPLATE_VAR_RX = /\{(web_url|ref(?:hash|name|type)|path)\}/g
7
+ const EDIT_URL_TEMPLATE_VAR_RX = /\{(web_url|ref(?:hash|name|type)?|path)\}/g
8
8
  const HOSTED_GIT_REPO_RX = /^(?:https?:\/\/|.+@)(git(?:hub|lab)\.com|bitbucket\.org|pagure\.io)[/:](.+?)(?:\.git)?$/
9
9
 
10
10
  function computeOrigin (url, authStatus, gitdir, ref, startPath, worktreePath = undefined, editUrl = true) {
@@ -33,14 +33,16 @@ function computeOrigin (url, authStatus, gitdir, ref, startPath, worktreePath =
33
33
  category = 'f'
34
34
  } else if (host === 'bitbucket.org') {
35
35
  action = 'src'
36
- } else if (reftype === 'branch') {
37
- action = 'edit'
36
+ } else {
37
+ if (reftype === 'branch') action = 'edit'
38
+ if (host.startsWith('gitlab.')) action = '-/' + action
38
39
  }
39
40
  origin.editUrlPattern = 'https://' + path.join(match[1], match[2], action, refname, category, startPath, '%s')
40
41
  }
41
42
  } else if (editUrl) {
42
43
  const vars = {
43
44
  path: () => (startPath ? path.join(startPath, '%s') : '%s'),
45
+ ref: () => 'refs/' + (reftype === 'branch' ? 'heads' : reftype) + '/' + refname,
44
46
  refhash: () => refhash,
45
47
  reftype: () => reftype,
46
48
  refname: () => refname,
@@ -3,4 +3,4 @@
3
3
  module.exports = (({ StringDecoder }) => {
4
4
  const decoder = new StringDecoder()
5
5
  return decoder.write.bind(decoder)
6
- })(require('string_decoder'))
6
+ })(require('node:string_decoder'))
@@ -4,9 +4,11 @@ const { makeMatcherRx, refMatcherOpts: getMatcherOpts, MATCH_ALL_RX } = require(
4
4
 
5
5
  function compileRx (pattern, opts) {
6
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)
7
+ const rx =
8
+ pattern.charAt() === '!' // we handle negate ourselves
9
+ ? Object.defineProperty(makeMatcherRx((pattern = pattern.substr(1)), opts), 'negated', { value: true })
10
+ : makeMatcherRx(pattern, opts)
11
+ return Object.defineProperty(rx, 'pattern', { value: pattern })
10
12
  }
11
13
 
12
14
  function createMatcher (patterns, cache = Object.assign(new Map(), { braces: new Map() })) {
@@ -14,8 +16,8 @@ function createMatcher (patterns, cache = Object.assign(new Map(), { braces: new
14
16
  (pattern) => cache.get(pattern) || cache.set(pattern, compileRx(pattern, getMatcherOpts(cache))).get(pattern)
15
17
  )
16
18
  if (rxs[0].negated) rxs.unshift(MATCH_ALL_RX)
17
- return (candidate) => {
18
- let matched
19
+ return (candidate, onMatch) => {
20
+ let matched, symbolic
19
21
  for (const rx of rxs) {
20
22
  let voteIfMatched = true
21
23
  if (matched) {
@@ -24,16 +26,22 @@ function createMatcher (patterns, cache = Object.assign(new Map(), { braces: new
24
26
  } else if (rx.negated) {
25
27
  continue
26
28
  }
27
- if (rx.test(candidate)) matched = voteIfMatched
29
+ if (rx.test(candidate) || (symbolic && rx.test(symbolic) && (candidate = symbolic))) {
30
+ if (onMatch) {
31
+ if (!(matched = onMatch(candidate, rx))) continue
32
+ ;[symbolic, candidate] = [candidate, matched]
33
+ }
34
+ matched = voteIfMatched && candidate
35
+ }
28
36
  }
29
37
  return matched
30
38
  }
31
39
  }
32
40
 
33
- function filterRefs (candidates, patterns, cache) {
34
- const isMatch = createMatcher(patterns, cache)
41
+ function filterRefs (candidates, patterns, cache, onMatch) {
42
+ const match = createMatcher(patterns, cache)
35
43
  return candidates.reduce((accum, candidate) => {
36
- if (isMatch(candidate)) accum.push(candidate)
44
+ if ((candidate = match(candidate, onMatch))) accum.push(candidate)
37
45
  return accum
38
46
  }, [])
39
47
  }
@@ -1,10 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const { homedir } = require('os')
3
+ const { homedir } = require('node:os')
4
4
  const expandPath = require('@antora/expand-path-helper')
5
- const { promises: fsp } = require('fs')
5
+ const { promises: fsp } = require('node:fs')
6
6
  const invariably = require('./invariably')
7
- const ospath = require('path')
7
+ const ospath = require('node:path')
8
8
 
9
9
  class GitCredentialManagerStore {
10
10
  configure ({ config, startDir }) {
@@ -86,11 +86,11 @@ class GitCredentialManagerStore {
86
86
  }
87
87
 
88
88
  async approved ({ url }) {
89
- this.urls[url] = 'approved'
89
+ this.urls[url] = (url in this.urls ? this.urls[url] + ',' : '') + 'approved'
90
90
  }
91
91
 
92
92
  async rejected ({ url, auth }) {
93
- this.urls[url] = 'rejected'
93
+ this.urls[url] = (url in this.urls ? this.urls[url] + ',' : '') + 'rejected'
94
94
  const statusCode = 401
95
95
  const statusMessage = 'HTTP Basic: Access Denied'
96
96
  const err = new Error(`HTTP Error: ${statusCode} ${statusMessage}`)
package/lib/git.js CHANGED
@@ -1,3 +1,10 @@
1
1
  'use strict'
2
2
 
3
- module.exports = require('isomorphic-git')
3
+ const zlib = require('node:zlib')
4
+ const { promisify } = require('node:util')
5
+
6
+ module.exports = ((pakoModuleId) => {
7
+ const git = require('isomorphic-git')
8
+ require(pakoModuleId).inflate = promisify(zlib.inflate)
9
+ return git
10
+ })('pako')
package/lib/matcher.js CHANGED
@@ -17,7 +17,7 @@ const BASE_OPTS = {
17
17
  }
18
18
 
19
19
  module.exports = {
20
- MATCH_ALL_RX: { test: () => true },
20
+ MATCH_ALL_RX: Object.defineProperty({ test: () => true }, 'pattern', { value: '*' }),
21
21
  expandBraces,
22
22
  makeMatcherRx: makeRe,
23
23
  pathMatcherOpts: Object.assign({}, BASE_OPTS, { dot: false }),
package/lib/posixify.js CHANGED
@@ -1,3 +1,3 @@
1
1
  'use strict'
2
2
 
3
- module.exports = require('path').sep === '\\' ? (p) => p.replace(/\\/g, '/') : undefined
3
+ module.exports = require('node:path').sep === '\\' ? (p) => p.replace(/\\/g, '/') : undefined
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const deepFlatten = require('./deep-flatten')
4
- const { promises: fsp } = require('fs')
4
+ const { promises: fsp } = require('node:fs')
5
5
  const git = require('./git')
6
6
  const invariably = require('./invariably')
7
7
  const { expandBraces, makeMatcherRx, pathMatcherOpts: MATCHER_OPTS } = require('./matcher')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/content-aggregator",
3
- "version": "3.1.10",
3
+ "version": "3.1.11",
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)",
@@ -13,7 +13,8 @@
13
13
  "homepage": "https://antora.org",
14
14
  "repository": {
15
15
  "type": "git",
16
- "url": "git+https://gitlab.com/antora/antora.git"
16
+ "url": "git+https://gitlab.com/antora/antora.git",
17
+ "directory": "packages/content-aggregator"
17
18
  },
18
19
  "bugs": {
19
20
  "url": "https://gitlab.com/antora/antora/issues"
@@ -32,7 +33,7 @@
32
33
  },
33
34
  "dependencies": {
34
35
  "@antora/expand-path-helper": "~3.0",
35
- "@antora/logger": "3.1.10",
36
+ "@antora/logger": "3.1.11",
36
37
  "@antora/user-require-helper": "~3.0",
37
38
  "braces": "~3.0",
38
39
  "cache-directory": "~2.0",
@@ -48,7 +49,7 @@
48
49
  "vinyl": "~3.0"
49
50
  },
50
51
  "engines": {
51
- "node": ">=16.0.0"
52
+ "node": ">=18.0.0"
52
53
  },
53
54
  "files": [
54
55
  "lib/"
@@ -65,7 +66,7 @@
65
66
  ],
66
67
  "scripts": {
67
68
  "test": "_mocha",
68
- "prepublishOnly": "npx -y downdoc --prepublish",
69
- "postpublish": "npx -y downdoc --postpublish"
69
+ "prepublishOnly": "npx -y downdoc@latest --prepublish",
70
+ "postpublish": "npx -y downdoc@latest --postpublish"
70
71
  }
71
72
  }