diff --git a/package.json b/package.json index 052e56811..c29b4ab50 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,9 @@ }, "npm": false }, + "resolutions": { + "type-fest": "^0.13.1" + }, "volta": { "node": "12.16.2", "yarn": "1.22.4" diff --git a/packages/cli/README.md b/packages/cli/README.md index f96113a57..5dc1beec1 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -117,10 +117,9 @@ Checkup uses [cosmiconfig](https://github.com/davidtheclark/cosmiconfig) to find The search stops when one of these is found, and Checkup uses that object. -The .checkuprc file (without extension) can be in JSON or YAML format. You can add a filename extension to help your text editor provide syntax checking and highlighting: +The .checkuprc file (without extension) can be in JSON or JavaScript format. You can add a filename extension to help your text editor provide syntax checking and highlighting: - .checkup.json -- .checkup.yaml / .checkup.yml - .checkup.js You can also specify an explicit path to a configuration via the command line, which will override any configurations found in any `.checkuprc.*` files diff --git a/packages/cli/package.json b/packages/cli/package.json index d86df015f..0545144cb 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -13,6 +13,7 @@ "@checkup/core": "0.0.10", "@oclif/command": "^1", "@oclif/config": "^1", + "@oclif/errors": "^1.2.2", "@oclif/plugin-help": "^3", "chalk": "^4.0.0", "checkup-plugin-ember": "0.0.10", diff --git a/packages/cli/src/commands/generate.ts b/packages/cli/src/commands/generate.ts index 71d5511da..15bd3ab1c 100644 --- a/packages/cli/src/commands/generate.ts +++ b/packages/cli/src/commands/generate.ts @@ -2,12 +2,12 @@ import * as chalk from 'chalk'; import * as debug from 'debug'; import { basename, join } from 'path'; +import { existsSync, readdirSync } from 'fs'; import Command from '@oclif/command'; import { IConfig } from '@oclif/config'; import { createEnv } from 'yeoman-environment'; import { flags } from '@oclif/command'; -import { readdirSync } from 'fs'; import { rmdirSync } from 'fs'; export interface Options { @@ -20,6 +20,7 @@ export interface Options { export default class GenerateCommand extends Command { private _generators!: string[]; + private baseDir: string; static description = 'Runs a generator to scaffold Checkup code'; @@ -49,6 +50,7 @@ export default class GenerateCommand extends Command { constructor(argv: string[], config: IConfig) { super(argv, config); + this.baseDir = process.cwd(); this.debug = debug('checkup:generator'); } @@ -90,18 +92,26 @@ export default class GenerateCommand extends Command { env.register(require.resolve(`../generators/${type}`), `checkup:${type}`); - await new Promise((resolve, reject) => { - env.run(`checkup:${type}`, generatorOptions, (err: Error | null) => { - if (err) { - reject(err); - } else { - // this is ugly, but I couldn't find the correct configuration to ignore - // generating the yeoman repository directory in the cwd - rmdirSync(join(process.cwd(), '.yo-repository')); - - resolve(); - } + try { + await new Promise((resolve, reject) => { + env.run(`checkup:${type}`, generatorOptions, (err: Error | null) => { + if (err) { + reject(err); + } else { + // this is ugly, but I couldn't find the correct configuration to ignore + // generating the yeoman repository directory in the cwd + let yoRepoPath = join(this.baseDir, '.yo-repository'); + + if (existsSync(yoRepoPath)) { + rmdirSync(yoRepoPath); + } + + resolve(); + } + }); }); - }); + } catch (error) { + this.error(error); + } } } diff --git a/packages/cli/src/generators/base-generator.ts b/packages/cli/src/generators/base-generator.ts new file mode 100644 index 000000000..3e48dc7ba --- /dev/null +++ b/packages/cli/src/generators/base-generator.ts @@ -0,0 +1,10 @@ +import * as Generator from 'yeoman-generator'; +import * as chalk from 'chalk'; + +import { getVersion } from '../helpers/get-version'; + +export default class GeneratorBase extends Generator { + headline(name: string) { + this.log(`Generating ${chalk.bold.white(name)} ${chalk.dim(`(checkup v${getVersion()})`)}`); + } +} diff --git a/packages/cli/src/generators/config.ts b/packages/cli/src/generators/config.ts index 6c535c69b..d3af1bafb 100644 --- a/packages/cli/src/generators/config.ts +++ b/packages/cli/src/generators/config.ts @@ -1,4 +1,4 @@ -import * as Generator from 'yeoman-generator'; +import * as chalk from 'chalk'; import { CheckupConfigFormat, @@ -8,8 +8,9 @@ import { } from '@checkup/core'; import { Answers } from 'inquirer'; +import BaseGenerator from './base-generator'; -export default class ConfigGenerator extends Generator { +export default class ConfigGenerator extends BaseGenerator { private answers!: Answers; private configService!: CheckupConfigService; @@ -24,6 +25,8 @@ export default class ConfigGenerator extends Generator { } async prompting() { + this.headline('checkup config'); + this.answers = await this.prompt([ { name: 'format', @@ -41,6 +44,7 @@ export default class ConfigGenerator extends Generator { } async writing() { - this.configService.write(); + let configPath = this.configService.write(); + this.log(` ${chalk.green('create')} ${configPath}`); } } diff --git a/packages/cli/src/generators/plugin.ts b/packages/cli/src/generators/plugin.ts index 45231bbad..e51e8784b 100644 --- a/packages/cli/src/generators/plugin.ts +++ b/packages/cli/src/generators/plugin.ts @@ -1,18 +1,12 @@ -import * as Generator from 'yeoman-generator'; -import * as chalk from 'chalk'; import * as path from 'path'; -import { getVersion } from '../helpers/get-version'; +import { Answers } from 'inquirer'; +import BaseGenerator from './base-generator'; const PLUGIN_DIR_PATTERN = /checkup-plugin-.*/; -export default class PluginGenerator extends Generator { - answers!: { - typescript: boolean; - description: string; - author: string; - repository: string; - }; +export default class PluginGenerator extends BaseGenerator { + answers!: Answers; private get _ext() { return this.options.typescript ? 'ts' : 'js'; @@ -29,11 +23,9 @@ export default class PluginGenerator extends Generator { } async prompting() { - this.log( - `Adding ${chalk.bold.white(this.options.name)} plugin. Version: ${chalk.bold.white( - getVersion() - )}` - ); + this._normalizeName(); + + this.headline(this.options.name); const defaults = { typescript: true, @@ -73,8 +65,6 @@ export default class PluginGenerator extends Generator { ]); } - this._normalizeName(); - this.options.typescript = this.answers.typescript; this.options.description = this.answers.description; this.options.author = this.answers.author; diff --git a/packages/cli/src/generators/task.ts b/packages/cli/src/generators/task.ts index d88b49685..5a2cf5e19 100644 --- a/packages/cli/src/generators/task.ts +++ b/packages/cli/src/generators/task.ts @@ -1,15 +1,15 @@ -import * as Generator from 'yeoman-generator'; +import * as Errors from '@oclif/errors'; import * as _ from 'lodash'; -import * as chalk from 'chalk'; import * as path from 'path'; import * as t from '@babel/types'; import { Category, Priority } from '@checkup/core'; +import { Answers } from 'inquirer'; import AstTransformer from '../helpers/ast'; +import BaseGenerator from './base-generator'; import { Options } from '../commands/generate'; import { PackageJson } from 'type-fest'; -import { getVersion } from '../helpers/get-version'; interface TaskOptions extends Options { taskResultClass: string; @@ -20,14 +20,9 @@ interface TaskOptions extends Options { priority: string; } -export default class TaskGenerator extends Generator { +export default class TaskGenerator extends BaseGenerator { packageJson!: PackageJson; - - answers!: { - typescript: boolean; - category: string; - priority: string; - }; + answers!: Answers; private get _ts() { let devDeps = this.packageJson.devDependencies; @@ -49,14 +44,10 @@ export default class TaskGenerator extends Generator { !this.packageJson || !(this.packageJson.keywords && this.packageJson.keywords.includes('oclif-plugin')) ) { - throw new Error('not in a plugin directory'); + Errors.error('You must be in a Checkup plugin directory in order to run the task generator'); } - this.log( - `Adding a ${chalk.bold.white(this.options.name)} task to ${chalk.bold.white( - this.packageJson.name - )}. Version: ${chalk.bold.white(getVersion())}` - ); + this.headline(`${this.options.name}-task`); const defaults = { typescript: true, diff --git a/packages/core/__tests__/configuration/__snapshots__/checkup-config-service-test.ts.snap b/packages/core/__tests__/configuration/__snapshots__/checkup-config-service-test.ts.snap index c65dc529f..2e4da6e01 100644 --- a/packages/core/__tests__/configuration/__snapshots__/checkup-config-service-test.ts.snap +++ b/packages/core/__tests__/configuration/__snapshots__/checkup-config-service-test.ts.snap @@ -25,9 +25,3 @@ exports[`checkup-config-service should write the config on calling write for Jav \\"tasks\\": {} }" `; - -exports[`checkup-config-service should write the config on calling write for YAML files 1`] = ` -"plugins: [] -tasks: {} -" -`; diff --git a/packages/core/__tests__/configuration/cosmiconfig-service-test.ts b/packages/core/__tests__/configuration/cosmiconfig-service-test.ts index 07582ce85..c6b8df429 100644 --- a/packages/core/__tests__/configuration/cosmiconfig-service-test.ts +++ b/packages/core/__tests__/configuration/cosmiconfig-service-test.ts @@ -1,13 +1,13 @@ +import * as path from 'path'; + import { CheckupConfig, CheckupConfigFormat } from '../../src'; + import { CheckupProject } from '@checkup/test-helpers'; -import * as path from 'path'; -import * as yaml from 'js-yaml'; import CosmiconfigService from '../../src/configuration/cosmiconfig-service'; describe('cosmiconfig-service-factory', () => { const formatToWriteMapper: Record string> = { JSON: (config) => JSON.stringify(config, null, 2), - YAML: (config) => yaml.safeDump(config), JavaScript: (config) => `module.exports = ${JSON.stringify(config, null, 2)}`, }; const defaultConfig = { @@ -42,7 +42,7 @@ describe('cosmiconfig-service-factory', () => { expect(result).toBeNull(); }); - it.each([[CheckupConfigFormat.JSON], [CheckupConfigFormat.YAML]])( + it.each([[CheckupConfigFormat.JSON], [CheckupConfigFormat.JavaScript]])( 'should correctly search extensionless %s config files', async (configFormat: CheckupConfigFormat) => { project.files['.checkuprc'] = formatToWriteMapper[configFormat](defaultConfig); @@ -58,8 +58,6 @@ describe('cosmiconfig-service-factory', () => { it.each([ ['.checkuprc.js', CheckupConfigFormat.JavaScript], ['.checkuprc.json', CheckupConfigFormat.JSON], - ['.checkuprc.yml', CheckupConfigFormat.YAML], - ['.checkuprc.yaml', CheckupConfigFormat.YAML], ['checkup.config.js', CheckupConfigFormat.JavaScript], ])( 'should correctly search config files of type %s', @@ -74,7 +72,7 @@ describe('cosmiconfig-service-factory', () => { } ); - it.each([[CheckupConfigFormat.JSON], [CheckupConfigFormat.YAML]])( + it.each([[CheckupConfigFormat.JSON], [CheckupConfigFormat.JavaScript]])( 'should correctly load extensionless %s config files', async (configFormat: CheckupConfigFormat) => { project.files['.checkuprc'] = formatToWriteMapper[configFormat](defaultConfig); @@ -92,8 +90,6 @@ describe('cosmiconfig-service-factory', () => { it.each([ ['.checkuprc.js', CheckupConfigFormat.JavaScript], ['.checkuprc.json', CheckupConfigFormat.JSON], - ['.checkuprc.yml', CheckupConfigFormat.YAML], - ['.checkuprc.yaml', CheckupConfigFormat.YAML], ['checkup.config.js', CheckupConfigFormat.JavaScript], ])( 'should correctly load config files of type %s', diff --git a/packages/core/__tests__/configuration/get-initialization-loader-test.ts b/packages/core/__tests__/configuration/get-initialization-loader-test.ts index ce380466f..c84adaeb5 100644 --- a/packages/core/__tests__/configuration/get-initialization-loader-test.ts +++ b/packages/core/__tests__/configuration/get-initialization-loader-test.ts @@ -1,5 +1,6 @@ -import { CheckupConfigFormat } from '../../src'; import * as path from 'path'; + +import { CheckupConfigFormat } from '../../src'; import getInitializationConfigLoader from '../../src/configuration/loaders/get-initialization-loader'; describe('get-initialization-loader', () => { @@ -23,12 +24,4 @@ describe('get-initialization-loader', () => { expect(loaderValue.filepath).toEqual(path.join('.', '.checkuprc')); expect(loaderValue.config).toStrictEqual(defaultConfig); }); - - it('yaml config loader returns correct value', async () => { - const loaderValue = await getInitializationConfigLoader('.', CheckupConfigFormat.YAML)(); - - expect(loaderValue.format).toEqual(CheckupConfigFormat.YAML); - expect(loaderValue.filepath).toEqual(path.join('.', '.checkuprc')); - expect(loaderValue.config).toStrictEqual(defaultConfig); - }); }); diff --git a/packages/core/package.json b/packages/core/package.json index 5be9a07ae..c12741450 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -14,13 +14,11 @@ "fp-ts": "^2.5.4", "globby": "^11.0.0", "io-ts": "^2.2.1", - "js-yaml": "^3.13.1", "resolve": "^1.17.0" }, "devDependencies": { "@types/chalk": "^2.2.0", "@types/eslint": "^6.8.0", - "@types/js-yaml": "^3.12.3", "@types/resolve": "^1.14.0", "eslint-plugin-jest": "^23.8.2" }, diff --git a/packages/core/src/configuration/checkup-config-service.ts b/packages/core/src/configuration/checkup-config-service.ts index 657d6dfbf..50b673d9f 100644 --- a/packages/core/src/configuration/checkup-config-service.ts +++ b/packages/core/src/configuration/checkup-config-service.ts @@ -1,10 +1,10 @@ import * as debug from 'debug'; import * as fs from 'fs'; -import * as yaml from 'js-yaml'; import { CheckupConfig, CheckupConfigFormat, CheckupConfigLoader } from '../types/configuration'; import { RuntimeCheckupConfig } from '../types/runtime-types'; +import { basename } from 'path'; import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; @@ -18,7 +18,6 @@ export default class CheckupConfigService { (config: CheckupConfig) => string > = { JSON: (config) => JSON.stringify(config, null, 2), - YAML: (config) => yaml.safeDump(config), JavaScript: (config) => `module.exports = ${JSON.stringify(config, null, 2)}`, }; private readonly configPath: string; @@ -46,6 +45,8 @@ export default class CheckupConfigService { write() { const configToWrite = CheckupConfigService.formatToWriteMapper[this.format](this.get()); fs.writeFileSync(this.configPath, configToWrite); + + return basename(this.configPath); } /** diff --git a/packages/core/src/configuration/cosmiconfig-service.ts b/packages/core/src/configuration/cosmiconfig-service.ts index 20ae949cf..7e2920ba1 100644 --- a/packages/core/src/configuration/cosmiconfig-service.ts +++ b/packages/core/src/configuration/cosmiconfig-service.ts @@ -3,11 +3,9 @@ import * as path from 'path'; import { CheckupConfigFormat, CosmiconfigServiceResult } from '../types/configuration'; import { cosmiconfig, defaultLoaders } from 'cosmiconfig'; -type ConfigExtension = '.js' | '.json' | '.yml' | '.yaml'; +type ConfigExtension = '.js' | '.json'; const EXTENSION_TO_FORMAT: Record = { '.js': CheckupConfigFormat.JavaScript, - '.yml': CheckupConfigFormat.YAML, - '.yaml': CheckupConfigFormat.YAML, '.json': CheckupConfigFormat.JSON, }; @@ -16,29 +14,22 @@ const EXTENSION_TO_FORMAT: Record = { * data from the loaded config, like {@link CheckupConfigFormat}. */ export default class CosmiconfigService { - private static readonly MODULE_NAME = 'checkup'; private cosmiconfig: ReturnType; private outputFormat: CheckupConfigFormat | undefined; constructor() { - this.cosmiconfig = cosmiconfig(CosmiconfigService.MODULE_NAME, { - searchPlaces: [ - `.${CosmiconfigService.MODULE_NAME}rc`, - `.${CosmiconfigService.MODULE_NAME}rc.json`, - `.${CosmiconfigService.MODULE_NAME}rc.yaml`, - `.${CosmiconfigService.MODULE_NAME}rc.yml`, - `.${CosmiconfigService.MODULE_NAME}rc.js`, - `${CosmiconfigService.MODULE_NAME}.config.js`, - ], + this.cosmiconfig = cosmiconfig('checkup', { + searchPlaces: ['.checkuprc', '.checkuprc.json', '.checkuprc.js', 'checkup.config.js'], loaders: { noExt: (filepath, content) => { try { JSON.parse(content); this.outputFormat = CheckupConfigFormat.JSON; + return defaultLoaders['.json'](filepath, content); } catch { - this.outputFormat = CheckupConfigFormat.YAML; + this.outputFormat = CheckupConfigFormat.JavaScript; + return defaultLoaders['.js'](filepath, content); } - return defaultLoaders['noExt'](filepath, content); }, }, }); diff --git a/packages/core/src/types/configuration.ts b/packages/core/src/types/configuration.ts index 5edb4dc71..2d5a7f5b4 100644 --- a/packages/core/src/types/configuration.ts +++ b/packages/core/src/types/configuration.ts @@ -9,7 +9,6 @@ export type TaskConfig = t.TypeOf; export enum CheckupConfigFormat { JSON = 'JSON', - YAML = 'YAML', JavaScript = 'JavaScript', } diff --git a/yarn.lock b/yarn.lock index e9e5ff616..5cda2d59c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1006,11 +1006,6 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" -"@types/js-yaml@^3.12.3": - version "3.12.3" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.3.tgz#abf383c5b639d0aa8b8c4a420d6a85f703357d6c" - integrity sha512-otRe77JNNWzoVGLKw8TCspKswRoQToys4tuL6XYVBFxjgeM0RUrx7m3jkaTdxILxeGry3zM8mGYkGXMeQ02guA== - "@types/json-schema@*", "@types/json-schema@^7.0.3": version "7.0.4" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" @@ -8013,31 +8008,11 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.10.0.tgz#7f06b2b9fbfc581068d1341ffabd0349ceafc642" - integrity sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw== - -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== - -type-fest@^0.13.1: +type-fest@^0.10.0, type-fest@^0.11.0, type-fest@^0.13.1, type-fest@^0.6.0, type-fest@^0.8.1: version "0.13.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"