UNPKG

@vuetify/github-releaser

Version:
375 lines (318 loc) 9.91 kB
/** Copied from conventional-changelog-core because THEY DON'T FUCKING EXPOSE ANYTHING */ import fs from 'node:fs/promises' import path from 'node:path' import { exec } from 'node:child_process' import { URL, fileURLToPath } from 'node:url' import hostedGitInfo from 'hosted-git-info' import parseRepositoryUrl from '@hutson/parse-repository-url' import getSemverTags from 'git-semver-tags' import normalizePackageData from 'normalize-package-data' const dirname = fileURLToPath(new URL('.', import.meta.url)) const rhosts = /github|bitbucket|gitlab/i // sv-SEis used for yyyy-mm-dd format const dateFormatter = Intl.DateTimeFormat('sv-SE', { timeZone: 'UTC', }) function guessNextTag (previousTag, version) { if (previousTag) { if (previousTag[0] === 'v' && version[0] !== 'v') { return 'v' + version } if (previousTag[0] !== 'v' && version[0] === 'v') { return version.replace(/^v/, '') } return version } if (version[0] !== 'v') { return 'v' + version } return version } function omitUndefinedValueProps (obj) { if (!obj) { return {} } const omittedObj = {} for (const key in obj) { if (obj[key] !== undefined) { omittedObj[key] = obj[key] } } return omittedObj } function getRemoteOriginUrl (cwd) { return new Promise((resolve, reject) => { exec('git config --get remote.origin.url', { cwd }, (err, stdout) => { if (err) { reject(err) } else { resolve(stdout.trim()) } }) }) } export default async function mergeConfig (options, context, gitRawCommitsOpts, parserOpts, writerOpts, gitRawExecOpts) { let pkgPromise options = omitUndefinedValueProps(options) context = context || {} gitRawCommitsOpts = gitRawCommitsOpts || {} gitRawExecOpts = { cwd: options?.cwd, ...gitRawExecOpts || {}, } const rtag = options && options.tagPrefix ? new RegExp(`tag:\\s*[=]?${options.tagPrefix}(.+?)[,)]`, 'gi') : /tag:\s*[v=]?(.+?)[,)]/gi options = { append: false, releaseCount: 1, skipUnstable: false, debug: function () {}, transform: function (commit, cb) { if (typeof commit.gitTags === 'string') { const match = rtag.exec(commit.gitTags) rtag.lastIndex = 0 if (match) { commit.version = match[1] } } if (commit.committerDate) { commit.committerDate = dateFormatter.format(new Date(commit.committerDate)) } cb(null, commit) }, lernaPackage: null, ...options, pkg: { transform: function (pkg) { return pkg }, ...options?.pkg, }, } options.warn = options.warn || options.debug if (options.pkg) { if (options.pkg.path) { pkgPromise = import('read-pkg').then(async ({ parsePackage }) => { const json = await fs.readFile(options.pkg.path, 'utf-8') return parsePackage(json) }) } else { pkgPromise = import('read-package-up').then(async ({ readPackageUp }) => { const { packageJson } = await readPackageUp({ cwd: options.cwd }) return packageJson }) } } const presetConfig = typeof options.config === 'function' ? options.config() : options.config const [ configObj, pkgObj, tagsObj, gitRemoteOriginUrlObj, ] = await Promise.allSettled([ presetConfig, pkgPromise, getSemverTags({ lernaTags: !!options.lernaPackage, package: options.lernaPackage, tagPrefix: options.tagPrefix, skipUnstable: options.skipUnstable, cwd: options.cwd, }), getRemoteOriginUrl(options.cwd), ]) let config let pkg let fromTag let repo let hostOpts let semverTags = [] if (options.config) { if (configObj.status === 'fulfilled') { config = configObj.value } else { options.warn(configObj.reason.toString()) config = {} } } else { config = {} } context = { ...context, ...config.context, } if (options.pkg) { if (pkgObj.status === 'fulfilled') { pkg = pkgObj.value || {} pkg = options.pkg.transform(pkg) } else if (options.pkg.path) { options.warn(pkgObj.reason.toString()) } } if ((!pkg || !pkg.repository || !pkg.repository.url) && gitRemoteOriginUrlObj.status === 'fulfilled') { pkg = pkg || {} pkg.repository = pkg.repository || {} pkg.repository.url = gitRemoteOriginUrlObj.value normalizePackageData(pkg) } if (pkg) { context.version = context.version || pkg.version try { const repositoryURL = typeof pkg.repository === 'string' ? pkg.repository : pkg.repository.url if (repositoryURL) { // Remove parseRepositoryUrl when https://github.com/npm/hosted-git-info/issues/39 is fixed repo = hostedGitInfo.fromUrl(repositoryURL) || parseRepositoryUrl(repositoryURL) } } catch (err) { repo = {} } if (repo.browse) { const browse = repo.browse() if (!context.host) { if (repo.domain) { const parsedBrowse = new URL(browse) if (parsedBrowse.origin.indexOf('//') !== -1) { context.host = parsedBrowse.protocol + '//' + repo.domain } else { context.host = parsedBrowse.protocol + repo.domain } } else { context.host = null } } context.owner = context.owner || repo.user || '' context.repository = context.repository || repo.project if (repo.host && repo.project && repo.user) { context.repoUrl = browse } else { context.repoUrl = context.host } } context.packageData = pkg } context.version = context.version || '' if (tagsObj.status === 'fulfilled') { semverTags = context.gitSemverTags = tagsObj.value fromTag = semverTags[options.releaseCount - 1] const lastTag = semverTags[0] if (lastTag === context.version || lastTag === 'v' + context.version) { if (options.outputUnreleased) { context.version = 'Unreleased' } else { options.outputUnreleased = false } } } if (typeof options.outputUnreleased !== 'boolean') { options.outputUnreleased = true } if (context.host && (!context.issue || !context.commit || !parserOpts || !parserOpts.referenceActions)) { let type if (context.host) { const match = context.host.match(rhosts) if (match) { type = match[0] } } else if (repo && repo.type) { type = repo.type } if (type) { hostOpts = JSON.parse(await fs.readFile(path.join(dirname, '.', 'hosts', `${type}.json`), 'utf8')) context = { issue: hostOpts.issue, commit: hostOpts.commit, ...context, } } else { options.warn('Host: "' + context.host + '" does not exist') hostOpts = {} } } else { hostOpts = {} } if (context.resetChangelog) { fromTag = null } gitRawCommitsOpts = { format: '%B%n-hash-%n%H%n-gitTags-%n%d%n-committerDate-%n%ci', from: fromTag, merges: false, debug: options.debug, ...config.commits, ...gitRawCommitsOpts, } if (options.append) { gitRawCommitsOpts.reverse = gitRawCommitsOpts.reverse || true } parserOpts = { ...config.parser, warn: options.warn, ...parserOpts, } if (hostOpts.referenceActions && parserOpts) { parserOpts.referenceActions = hostOpts.referenceActions } if (!parserOpts.issuePrefixes?.length && hostOpts.issuePrefixes) { parserOpts.issuePrefixes = hostOpts.issuePrefixes } writerOpts = { finalizeContext: function (context, writerOpts, filteredCommits, keyCommit, originalCommits) { const firstCommit = originalCommits[0] const lastCommit = originalCommits[originalCommits.length - 1] const firstCommitHash = firstCommit ? firstCommit.hash : null const lastCommitHash = lastCommit ? lastCommit.hash : null if ((!context.currentTag || !context.previousTag) && keyCommit) { const match = /tag:\s*(.+?)[,)]/gi.exec(keyCommit.gitTags) const currentTag = context.currentTag context.currentTag = currentTag || match ? match[1] : null const index = semverTags.indexOf(context.currentTag) // if `keyCommit.gitTags` is not a semver if (index === -1) { context.currentTag = currentTag || null } else { const previousTag = context.previousTag = semverTags[index + 1] if (!previousTag) { if (options.append) { context.previousTag = context.previousTag || firstCommitHash } else { context.previousTag = context.previousTag || lastCommitHash } } } } else { context.previousTag = context.previousTag || semverTags[0] if (context.version === 'Unreleased') { if (options.append) { context.currentTag = context.currentTag || lastCommitHash } else { context.currentTag = context.currentTag || firstCommitHash } } else if (!context.currentTag) { if (options.lernaPackage) { context.currentTag = options.lernaPackage + '@' + context.version } else if (options.tagPrefix) { context.currentTag = options.tagPrefix + context.version } else { context.currentTag = guessNextTag(semverTags[0], context.version) } } } if (typeof context.linkCompare !== 'boolean' && context.previousTag && context.currentTag) { context.linkCompare = true } return context }, debug: options.debug, ...config.writer, reverse: options.append, doFlush: options.outputUnreleased, ...writerOpts, } return { options, context, gitRawCommitsOpts, parserOpts, writerOpts, gitRawExecOpts, } }