Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cli update and typescript conversion wip (new branch name) #69

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft
23 changes: 17 additions & 6 deletions command.js → command.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
const spec = require('conventional-changelog-config-spec')
const { getConfiguration } = require('./lib/configuration')
const defaults = require('./defaults')
/*
I haven't been able to find API docs for yargs that explain the runtime semantics thoroughly enough to describe the types,
and the types give do not appear to agree with the usage below. This leads me to believe the types are either incorrect or incomplete,
and adding types to Yargs is outside of scope.

As a compromise, `argv` is manually typed, and the type must be updated as changes are made below. There is a
[separate isssue](https://github.com/absolute-version/commit-and-tag-version/issues/31) for refactoring the API to bring the CLI
options to parity with the JSON options, which would likely involve breaking changes.
*/

import spec from 'conventional-changelog-config-spec/versions/2.1.0/schema.json'
import { getConfiguration } from './lib/configuration'
import defaults from './defaults'

const yargs = require('yargs')
.usage('Usage: $0 [options]')
Expand Down Expand Up @@ -126,7 +136,7 @@ const yargs = require('yargs')
default: defaults.npmPublishHint,
describe: 'Customized publishing hint'
})
.check((argv) => {
.check((argv: any) => {
if (typeof argv.scripts !== 'object' || Array.isArray(argv.scripts)) {
throw Error('scripts must be an object')
} else if (typeof argv.skip !== 'object' || Array.isArray(argv.skip)) {
Expand All @@ -148,11 +158,12 @@ const yargs = require('yargs')
.wrap(97)

Object.keys(spec.properties).forEach((propertyKey) => {
const property = spec.properties[propertyKey]
const propName = propertyKey as keyof typeof spec.properties;
const property = spec.properties[propName]
yargs.option(propertyKey, {
type: property.type,
describe: property.description,
default: defaults[propertyKey] ? defaults[propertyKey] : property.default,
default: defaults[propName] ? defaults[propName] : property.default,
group: 'Preset Configuration:'
})
})
Expand Down
43 changes: 0 additions & 43 deletions defaults.js

This file was deleted.

52 changes: 52 additions & 0 deletions defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import spec from "conventional-changelog-config-spec/versions/2.1.0/schema.json";
import { PrettyPrint } from "type-helpers";

const defaults = {
infile: "CHANGELOG.md",
firstRelease: false,
sign: false,
noVerify: false,
commitAll: false,
silent: false,
tagPrefix: "v",
releaseCount: 1,
scripts: {},
skip: {},
dryRun: false,
tagForce: false,
gitTagFallback: true,
preset: require.resolve("conventional-changelog-conventionalcommits"),
npmPublishHint: undefined,
/**
* Sets the default for `header` (provided by the spec) for backwards
* compatibility. This should be removed in the next major version.
*/
header:
"# Changelog\n\nAll notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.\n",
packageFiles: ["package.json", "bower.json", "manifest.json"],
bumpFiles: [
"package.json",
"bower.json",
"manifest.json",
"package-lock.json",
"npm-shrinkwrap.json",
],
} as const;

type Defaults = PrettyPrint<
typeof defaults &
Readonly<{
[key in keyof typeof spec.properties]: typeof spec.properties[key]["default"];
}>
>;

/**
* Merge in defaults provided by the spec
*/
Object.keys(spec.properties).forEach((propertyKey) => {
const property = spec.properties[propertyKey as keyof typeof spec.properties];
// @ts-expect-error - We used a const assertion to infer literal types for intellisense, so TS thinks defaults is readonly.
defaults[propertyKey] = property.default;
});

export default defaults as Defaults;
19 changes: 10 additions & 9 deletions lib/configuration.js → lib/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
const path = require('path')
const findUp = require('find-up')
const { readFileSync } = require('fs')
import path from 'path'
import findUp from 'find-up'
import { readFileSync } from 'fs'
import { Config } from './opts'

const CONFIGURATION_FILES = [
'.versionrc',
'.versionrc.cjs',
'.versionrc.json',
'.versionrc.cjs',
'.versionrc.js'
]
] as const;

module.exports.getConfiguration = function () {
let config = {}
export async function getConfiguration () {
let config: Partial<Config> = {}
const configPath = findUp.sync(CONFIGURATION_FILES)
if (!configPath) {
return config
}
const ext = path.extname(configPath)
if (ext === '.js' || ext === '.cjs') {
const jsConfiguration = require(configPath)
const jsConfiguration = await import(configPath)
if (typeof jsConfiguration === 'function') {
config = jsConfiguration()
} else {
config = jsConfiguration
}
} else {
config = JSON.parse(readFileSync(configPath))
config = JSON.parse(readFileSync(configPath, 'utf-8'))
}

/**
Expand Down
117 changes: 117 additions & 0 deletions lib/opts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { getConfiguration as getConfigFromFile } from "../configuration";
import { isin } from "../../type-helpers";
import type { PrettyPrint } from "../../type-helpers";

type Release = "minor" | "major" | "patch";
type Task = "changelog" | "commit" | "tag";
type Hook =
| "prerelease"
| "prebump"
| "postbump"
| "prechangelog"
| "postchangelog"
| "precommit"
| "postcommit"
| "pretag"
| "posttag";

type TypePrefixes = Array<
(
| { section: string; hidden?: boolean | undefined }
| { hidden: true; section?: string | undefined }
) & { type: string }
>;

type ConfigFiles = Array<
| string
| { filename: string; type: "json" | "gradle" | "plain-text" }
| { filename: string; updater: string }
>;

/**
* __THIS SHOULD NOT CHANGE.__
* @deprecated
*
* The configuration options for `standard-version` as of version 9.5 (The last version prior to the fork; deprecated).
*/
export type LegacyConfig =
| {
packageFiles: ConfigFiles;
bumpFiles: ConfigFiles;
releaseAs: Release;
prerelease: string | boolean;
infile: string;
message: string;
firstRelease: boolean;
sign: boolean;
noVerify: boolean;
commitAll: boolean;
silent: boolean;
tagPrefix: string;
scripts: Record<Hook, string>;
skip: Record<Task, string>;
dryRun: boolean;
gitTagFallback: boolean;
path: string;
changelogHeader: string;
preset: string;
lernaPackage: string;
header: string;
types: TypePrefixes;
preMajor: boolean;
commitUrlFormat: string;
compareUrlFormat: string;
issueUrlFormat: string;
userUrlFormat: string;
releaseCommitMessageFormat: string;
issuePrefixes: string[];
}
| undefined;

/**
* The configuration object for commit-and-tag-version, which is a superset of the conventional-changelog-config-spec (as of version 2.1.0)
* This may or may not maintain backwards compatibility with standard-version (as of version 9.5.0).
*/
export type Config =
| PrettyPrint<
LegacyConfig & {
npmPublishHint: string;
releaseCount: number;
tagForce: boolean;
}
>
| undefined;

/** The configuration options that are not supported by standard-version (as of version 9.5.0). */
const catVOnlyFeatures = [
"npmPublishHint",
"releaseCount",
"tagForce",
] as const satisfies ReadonlyArray<
Exclude<keyof Exclude<Config, undefined>, keyof LegacyConfig>
>;

export const getMergedConfig = async (
cwd?: string
): Promise<Partial<Config>> => {
const searchDir = cwd ?? process.cwd();
const pkgJson = (await import("path")).join(searchDir, "package.json");
const legacyConf: LegacyConfig = (await import(pkgJson))["standard-version"];
const modernConf: Config = (await import(pkgJson))["commit-and-tag-version"];

Object.keys(legacyConf ?? {}).forEach((key) => {
if (catVOnlyFeatures.includes(key as any)) {
console.warn(
`The "${key}" option is a feature of commit-and-tag-version, and is not supported by standard-version.${"\n"}Please move this option to the 'commit-and-tag-version' key.${"\n"}In a future version, this will throw an error.`
);
}
if (modernConf && isin(modernConf, key as any)) {
console.warn(
`"standard-version"."${key}" in package.json is being overridden by "commit-and-tag-version"."${key}". in package.json`
);
}
});

const configFromFile = await getConfigFromFile();
return { ...(legacyConf ?? {}), ...(modernConf ?? {}), ...(configFromFile ?? {}) };
};
Loading