@antora/content-aggregator 3.1.14 → 3.2.0-alpha.10

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.indexOf(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]) => {
@@ -131,7 +132,7 @@ async function collectFiles (sourcesByUrl, loadOpts, concurrency, fetchedUrls) {
131
132
  }
132
133
  return Promise.all(
133
134
  results.map(({ repo, authStatus, sources }) =>
134
- selectStartPathsForRepository(repo, authStatus, sources).then((startPaths) =>
135
+ selectStartPathsForRepository(repo, sources).then((startPaths) =>
135
136
  collectFilesFromStartPaths.bind(null, startPaths, repo, authStatus)
136
137
  )
137
138
  )
@@ -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 {
@@ -241,7 +249,7 @@ function extractCredentials (url) {
241
249
  return { displayUrl: url, url }
242
250
  }
243
251
 
244
- async function selectStartPathsForRepository (repo, authStatus, sources) {
252
+ async function selectStartPathsForRepository (repo, sources) {
245
253
  const startPaths = []
246
254
  const originUrls = {}
247
255
  for (const source of sources) {
@@ -290,6 +298,7 @@ async function selectReferences (source, repo, remote) {
290
298
  const isBare = managed || repo.noCheckout
291
299
  const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
292
300
  const noWorktree = managed ? undefined : false
301
+ const isLinkedWorktree = repo.worktree?.name
293
302
  const refs = new Map()
294
303
  if (
295
304
  tagPatterns &&
@@ -305,48 +314,63 @@ async function selectReferences (source, repo, remote) {
305
314
  }
306
315
  }
307
316
  }
308
- if (!branchPatterns) return [...refs.values()]
309
- let usePrimaryWorktree
310
- if (worktreePatterns) {
317
+ if (
318
+ !branchPatterns ||
319
+ !(branchPatterns = Array.isArray(branchPatterns)
320
+ ? branchPatterns.map((pattern) => String(pattern))
321
+ : splitRefPatterns(String(branchPatterns))).length
322
+ ) {
323
+ return [...refs.values()]
324
+ }
325
+ let useWorktree = false
326
+ if (!managed && (useWorktree = {})) {
311
327
  if (worktreePatterns === '.') {
312
- worktreePatterns = (usePrimaryWorktree = true) && []
328
+ isLinkedWorktree ? (useWorktree.linked = isLinkedWorktree) : isBare || (useWorktree.main = true)
329
+ worktreePatterns = []
330
+ } else if (!worktreePatterns) {
331
+ worktreePatterns = []
313
332
  } else if (worktreePatterns === true) {
314
- worktreePatterns = (usePrimaryWorktree = true) && ['*']
333
+ if (!isBare) useWorktree.main = true
334
+ // NOTE if we don't start at a linked worktree, linked worktree cannot be current worktree
335
+ if (isLinkedWorktree) useWorktree.linked = isLinkedWorktree
336
+ worktreePatterns = ['*']
337
+ } else if (worktreePatterns === '/.') {
338
+ if (!isBare) useWorktree.main = true
339
+ worktreePatterns = []
315
340
  } else {
316
- worktreePatterns = Array.isArray(worktreePatterns)
317
- ? worktreePatterns.map((pattern) => String(pattern))
318
- : splitRefPatterns(String(worktreePatterns))
319
- if (~worktreePatterns.indexOf('.')) {
320
- worktreePatterns = (usePrimaryWorktree = true) && worktreePatterns.filter((it) => it !== '.')
321
- }
341
+ worktreePatterns = (
342
+ Array.isArray(worktreePatterns)
343
+ ? worktreePatterns.map((pattern) => String(pattern))
344
+ : splitRefPatterns(String(worktreePatterns))
345
+ ).reduce((accum, it) => {
346
+ if (it === '/.') return (isBare || (useWorktree.main = true)) && accum
347
+ if (it === '.') {
348
+ isLinkedWorktree ? (useWorktree.linked = isLinkedWorktree) : isBare || (useWorktree.main = true)
349
+ } else {
350
+ accum.push(it)
351
+ }
352
+ return accum
353
+ }, [])
322
354
  }
323
- } else {
324
- worktreePatterns = []
355
+ if (!(useWorktree.main || useWorktree.linked)) useWorktree = false
325
356
  }
326
- const branchPatternsString = String(branchPatterns)
327
- if (branchPatternsString === 'HEAD' || branchPatternsString === '.') {
328
- const currentBranch = await getCurrentBranchName(repo, remote)
329
- if (currentBranch) {
330
- branchPatterns = [currentBranch]
331
- } else if (isBare) {
332
- return [...refs.values()]
333
- } else {
334
- // NOTE current branch is undefined when HEAD is detached
335
- const head = usePrimaryWorktree ? repo.dir : noWorktree
336
- refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
337
- return [...refs.values()]
338
- }
339
- } else if (
340
- (branchPatterns = Array.isArray(branchPatterns)
341
- ? branchPatterns.map((pattern) => String(pattern))
342
- : splitRefPatterns(branchPatternsString)).length
343
- ) {
357
+ let currentBranch
358
+ if (!isLinkedWorktree) {
344
359
  let headBranchIdx
345
- // NOTE we can assume at least two entries if HEAD or . are present
346
- if (~(headBranchIdx = branchPatterns.indexOf('HEAD')) || ~(headBranchIdx = branchPatterns.indexOf('.'))) {
347
- const currentBranch = await getCurrentBranchName(repo, remote)
348
- if (currentBranch) {
349
- // NOTE ignore if current branch is already in list
360
+ if (branchPatterns.length === 1 && (branchPatterns[0] === 'HEAD' || branchPatterns[0] === '.')) {
361
+ if ((currentBranch = await getCurrentBranchName(repo, remote).then((branch) => branch ?? false))) {
362
+ branchPatterns = [currentBranch]
363
+ } else if (isBare) {
364
+ return [...refs.values()]
365
+ } else {
366
+ // NOTE current branch is undefined when HEAD is detached
367
+ const head = useWorktree.main ? repo.dir : noWorktree
368
+ refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
369
+ return [...refs.values()]
370
+ }
371
+ } else if (~(headBranchIdx = branchPatterns.indexOf('HEAD')) || ~(headBranchIdx = branchPatterns.indexOf('.'))) {
372
+ // NOTE we can assume at least two entries if HEAD or . are present
373
+ if ((currentBranch = await getCurrentBranchName(repo, remote).then((branch) => branch ?? false))) {
350
374
  if (~branchPatterns.indexOf(currentBranch)) {
351
375
  branchPatterns.splice(headBranchIdx, 1)
352
376
  } else {
@@ -355,14 +379,12 @@ async function selectReferences (source, repo, remote) {
355
379
  } else if (isBare) {
356
380
  branchPatterns.splice(headBranchIdx, 1)
357
381
  } else {
358
- const head = usePrimaryWorktree ? repo.dir : noWorktree
382
+ const head = useWorktree.main ? repo.dir : noWorktree
359
383
  // NOTE current branch is undefined when HEAD is detached
360
384
  refs.set('HEAD', { shortname: 'HEAD', fullname: 'HEAD', type: 'branch', detached: true, head })
361
385
  branchPatterns.splice(headBranchIdx, 1)
362
386
  }
363
387
  }
364
- } else {
365
- return [...refs.values()]
366
388
  }
367
389
  // NOTE isomorphic-git includes HEAD in list of remote branches (see https://isomorphic-git.org/docs/listBranches)
368
390
  const remoteBranches = remote
@@ -374,22 +396,42 @@ async function selectReferences (source, repo, remote) {
374
396
  refs.set(shortname, { shortname, fullname, type: 'branch', remote, head: noWorktree })
375
397
  }
376
398
  }
377
- // NOTE only consider local branches if repo has a worktree or there are no remote tracking branches
378
- if (!isBare) {
379
- const localBranches = await git.listBranches(repo)
380
- if (localBranches.length) {
381
- const worktrees = await findWorktrees(repo, worktreePatterns, usePrimaryWorktree)
382
- for (const shortname of filterRefs(localBranches, branchPatterns, patternCache)) {
383
- const head = worktrees.get(shortname) || noWorktree
384
- refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head })
399
+ if (!managed) {
400
+ const localBranches = await git.listBranches(repo).then((branches) => {
401
+ if (branches.length || isBare) return branches
402
+ if (currentBranch != null) return [currentBranch]
403
+ return getCurrentBranchName(repo).then((branch) => (branch ? [branch] : []))
404
+ })
405
+ const worktrees = await findWorktrees(repo, worktreePatterns, useWorktree)
406
+ let onMatch
407
+ if ((useWorktree || worktreePatterns.length) && worktrees.size) {
408
+ const headNames = new Map()
409
+ worktrees.forEach(({ name, symbolicNames }, shortname) => {
410
+ if (name) {
411
+ const headName = 'HEAD@' + name
412
+ localBranches.push(headName)
413
+ headNames.set(headName, shortname)
414
+ }
415
+ if (symbolicNames) {
416
+ for (const symbolicName of symbolicNames) {
417
+ const symbolicHeadName = symbolicName === 'HEAD' ? symbolicName : 'HEAD@' + symbolicName
418
+ localBranches.push(symbolicHeadName)
419
+ headNames.set(symbolicHeadName, shortname)
420
+ }
421
+ }
422
+ })
423
+ onMatch = (candidate, { pattern }) => {
424
+ const shortname = headNames.get(candidate)
425
+ if (!shortname) return candidate
426
+ if (pattern === 'HEAD' || pattern.startsWith('HEAD@')) return shortname
385
427
  }
386
428
  }
387
- } else if (!managed || !remoteBranches.length) {
388
- const localBranches = await git.listBranches(repo)
389
429
  if (localBranches.length) {
390
- for (const shortname of filterRefs(localBranches, branchPatterns, patternCache)) {
391
- if (refs.has(shortname)) continue // NOTE prefer remote branches in bare repository
392
- refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head: noWorktree })
430
+ const preferRemote = isBare && remoteBranches.length > 0
431
+ for (const shortname of filterRefs(localBranches, branchPatterns, patternCache, onMatch)) {
432
+ if (preferRemote && refs.has(shortname)) continue
433
+ const head = (worktrees.get(shortname) || { head: false }).head
434
+ refs.set(shortname, { shortname, fullname: 'heads/' + shortname, type: 'branch', head })
393
435
  }
394
436
  }
395
437
  }
@@ -400,15 +442,13 @@ async function selectReferences (source, repo, remote) {
400
442
  * Returns the current branch name or undefined if the HEAD is detached.
401
443
  */
402
444
  function getCurrentBranchName (repo, remote) {
403
- let refPromise
404
- if (repo.noCheckout) {
405
- refPromise = git
406
- .resolveRef(Object.assign({ ref: 'refs/remotes/' + remote + '/HEAD', depth: 2 }, repo))
407
- .catch(() => git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo)))
408
- } else {
409
- refPromise = git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo))
410
- }
411
- return refPromise.then((ref) => (ref.startsWith('refs/') ? ref.replace(SHORTEN_REF_RX, '') : undefined))
445
+ return (
446
+ remote && repo.noCheckout
447
+ ? git
448
+ .resolveRef(Object.assign({ ref: 'refs/remotes/' + remote + '/HEAD', depth: 2 }, repo))
449
+ .catch(() => git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo)))
450
+ : git.resolveRef(Object.assign({ ref: 'HEAD', depth: 2 }, repo))
451
+ ).then((ref) => (ref.startsWith('refs/') ? ref.replace(SHORTEN_REF_RX, '') : undefined))
412
452
  }
413
453
 
414
454
  async function selectStartPaths (source, repo, ref) {
@@ -752,7 +792,7 @@ function loadComponentDescriptor (files, ref, version) {
752
792
  throw new Error(`version in ${COMPONENT_DESC_FILENAME} cannot have path segments: ${version}`)
753
793
  }
754
794
  data.version = version
755
- return camelCaseKeys(data, ['asciidoc'])
795
+ return camelCaseKeys(data, ['asciidoc', 'ext'])
756
796
  }
757
797
 
758
798
  function assignFileProperties (file, origin) {
@@ -769,18 +809,20 @@ function assignFileProperties (file, origin) {
769
809
  return file
770
810
  }
771
811
 
772
- function buildFetchOptions (repo, progress, displayUrl, credentialsFromUrl, gitPlugins, fetchTags, operation) {
812
+ function buildFetchOptions (repo, progress, displayUrl, credentialsFromUrl, gitPlugins, fetch, operation) {
773
813
  const { credentialManager, http, urlRouter } = gitPlugins
814
+ const corsProxy = false
815
+ const depth = fetch.depth || undefined
774
816
  const onAuth = resolveCredentials.bind(credentialManager, new Map().set(undefined, credentialsFromUrl))
775
817
  const onAuthFailure = onAuth
776
818
  const onAuthSuccess = (url) => credentialManager.approved({ url })
777
- const opts = Object.assign({ corsProxy: false, depth: 1, http, onAuth, onAuthFailure, onAuthSuccess }, repo)
819
+ const opts = Object.assign({ corsProxy, depth, http, onAuth, onAuthFailure, onAuthSuccess }, repo)
778
820
  if (urlRouter) opts.url = urlRouter.ensureGitSuffix(opts.url)
779
821
  if (progress) opts.onProgress = createProgressListener(progress, displayUrl, operation)
780
822
  if (operation === 'fetch') {
781
823
  opts.prune = true
782
- if (fetchTags) opts.tags = opts.pruneTags = true
783
- } else if (!fetchTags) {
824
+ if (fetch.tags) opts.tags = opts.pruneTags = true
825
+ } else if (!fetch.tags) {
784
826
  opts.noTags = true
785
827
  }
786
828
  return opts
@@ -875,7 +917,11 @@ function resolveCredentials (credentialsFromUrlHolder, url, auth) {
875
917
  }
876
918
 
877
919
  function identifyAuthStatus (credentialManager, credentials, url) {
878
- return credentials ? 'auth-embedded' : credentialManager.status({ url }) ? 'auth-required' : undefined
920
+ const status = credentialManager.status({ url })
921
+ if (credentials) {
922
+ return typeof status === 'string' && status.startsWith('requested,') ? 'auth-required' : 'auth-embedded'
923
+ }
924
+ if (status != null) return 'auth-required'
879
925
  }
880
926
 
881
927
  /**
@@ -1006,7 +1052,7 @@ function transformGitCloneError (err, displayUrl, authRequested) {
1006
1052
  }
1007
1053
 
1008
1054
  function splitRefPatterns (str) {
1009
- return ~str.indexOf('{') ? str.split(VENTILATED_CSV_RX) : str.split(CSV_RX)
1055
+ return str.split(~str.indexOf('{') ? VENTILATED_CSV_RX : CSV_RX)
1010
1056
  }
1011
1057
 
1012
1058
  function camelCaseKeys (o, stopPaths = [], p = '') {
@@ -1029,38 +1075,60 @@ function coerceToString (value) {
1029
1075
  return value == null ? '' : String(value)
1030
1076
  }
1031
1077
 
1032
- function findWorktrees (repo, patterns, usePrimaryWorktree) {
1033
- if (!(usePrimaryWorktree || patterns.length)) return new Map()
1034
- let worktreesDir
1078
+ function resolveRepositoryFromWorktree (repo) {
1079
+ return fsp
1080
+ .readFile(repo.gitdir, 'utf8')
1081
+ .then((contents) => contents.substr(8).trimEnd())
1082
+ .then((worktreeGitdir) =>
1083
+ fsp.readFile(ospath.join(worktreeGitdir, 'commondir'), 'utf8').then(
1084
+ (contents) => {
1085
+ const gitdir = ospath.join(worktreeGitdir, contents.trimEnd())
1086
+ const dir = ospath.basename(gitdir) === '.git' ? ospath.dirname(gitdir) : gitdir
1087
+ const name = ospath.basename(worktreeGitdir)
1088
+ return Object.assign(repo, { dir, gitdir, worktree: { gitdir: worktreeGitdir, name } })
1089
+ },
1090
+ () => repo
1091
+ )
1092
+ )
1093
+ }
1094
+
1095
+ function findWorktrees (repo, patterns, useWorktree) {
1096
+ const useLinkedWorktree = !!useWorktree.linked
1097
+ const mainWorktree = useWorktree.main
1098
+ ? getCurrentBranchName(repo).then((branch) => {
1099
+ if (!branch) return
1100
+ return [branch, { head: repo.dir, name: undefined, symbolicNames: useLinkedWorktree ? ['/.'] : ['/.', '.'] }]
1101
+ })
1102
+ : Promise.resolve()
1103
+ if (!(useLinkedWorktree || patterns.length)) return mainWorktree.then((entry) => new Map(entry && [entry]))
1104
+ const worktreesDir = ospath.join(repo.dir, repo.dir === repo.gitdir ? '' : '.git', 'worktrees')
1035
1105
  const patternCache = repo.cache[REF_PATTERN_CACHE_KEY]
1036
- return (
1037
- patterns.length
1038
- ? fsp
1039
- .readdir((worktreesDir = ospath.join(repo.dir, '.git', 'worktrees')))
1040
- .then((worktreeNames) => filterRefs(worktreeNames, patterns, patternCache), invariably.emptyArray)
1041
- .then((worktreeNames) =>
1042
- worktreeNames.length
1043
- ? Promise.all(
1044
- worktreeNames.map((worktreeName) => {
1045
- const gitdir = ospath.resolve(worktreesDir, worktreeName)
1046
- // NOTE uses name of worktree as branch name if HEAD is detached
1047
- return git
1048
- .currentBranch(Object.assign({}, repo, { gitdir }))
1049
- .then((branch = worktreeName) =>
1050
- fsp
1051
- .readFile(ospath.join(gitdir, 'gitdir'), 'utf8')
1052
- .then((contents) => ({ branch, dir: ospath.dirname(contents.trimEnd()) }))
1053
- )
1054
- })
1055
- ).then((entries) => entries.reduce((accum, it) => accum.set(it.branch, it.dir), new Map()))
1056
- : new Map()
1106
+ const scanWorktrees = patterns.length
1107
+ ? fsp
1108
+ .readdir(worktreesDir)
1109
+ .then((worktreeNames) => filterRefs(worktreeNames, patterns, patternCache), invariably.emptyArray)
1110
+ .then((worktreeNames) => {
1111
+ if (useLinkedWorktree && !~worktreeNames.indexOf(useWorktree.linked)) worktreeNames.push(useWorktree.linked)
1112
+ return worktreeNames
1113
+ })
1114
+ : Promise.resolve(useLinkedWorktree ? [useWorktree.linked] : [])
1115
+ return scanWorktrees
1116
+ .then((worktreeNames) =>
1117
+ Promise.all(
1118
+ worktreeNames.map((name) => {
1119
+ const symbolicNames = useLinkedWorktree && name === useWorktree.linked ? ['.', 'HEAD'] : undefined
1120
+ const gitdir = ospath.resolve(worktreesDir, name)
1121
+ // NOTE branch name defaults to worktree name if HEAD is detached
1122
+ return getCurrentBranchName(Object.assign({}, repo, { gitdir })).then((branch = name) =>
1123
+ fsp
1124
+ .readFile(ospath.join(gitdir, 'gitdir'), 'utf8')
1125
+ .then((contents) => [branch, { head: ospath.dirname(contents.trimEnd()), name, symbolicNames }])
1057
1126
  )
1058
- : Promise.resolve(new Map())
1059
- ).then((worktrees) =>
1060
- usePrimaryWorktree
1061
- ? git.currentBranch(repo).then((branch) => (branch ? worktrees.set(branch, repo.dir) : worktrees))
1062
- : worktrees
1063
- )
1127
+ })
1128
+ )
1129
+ )
1130
+ .then((entries) => new Map(entries))
1131
+ .then((worktrees) => mainWorktree.then((result) => (result ? worktrees.set(result[0], result[1]) : worktrees)))
1064
1132
  }
1065
1133
 
1066
1134
  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,17 +26,23 @@ 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) {
41
+ function filterRefs (candidates, patterns, cache, onMatch) {
34
42
  if (!(patterns = patterns.filter(compact)).length) return []
35
- const isMatch = createMatcher(patterns, cache)
43
+ const match = createMatcher(patterns, cache)
36
44
  return candidates.reduce((accum, candidate) => {
37
- if (isMatch(candidate)) accum.push(candidate)
45
+ if ((candidate = match(candidate, onMatch))) accum.push(candidate)
38
46
  return accum
39
47
  }, [])
40
48
  }
@@ -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.14",
3
+ "version": "3.2.0-alpha.10",
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.14",
36
+ "@antora/logger": "3.2.0-alpha.10",
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
  }