diff --git a/src/commands/schema/compare.ts b/src/commands/schema/compare.ts index cff84748..dd1312cd 100644 --- a/src/commands/schema/compare.ts +++ b/src/commands/schema/compare.ts @@ -8,6 +8,7 @@ import {Schema} from 'ts-json-schema-generator' import {SnapshotCommand} from '../../snapshot-command' import {getAllFiles, SchemaGenerator, Schemas} from './generate' import {bold, cyan, red, underline} from 'chalk' +import {getKeyNameFromFilename} from '../../util' export type SchemaComparison = Array<{ op: Operation; path: (string | number)[]; value: any }> @@ -122,7 +123,7 @@ export default class SchemaCompare extends SnapshotCommand { } else { for (const file of schemaFiles) { const schema = JSON.parse(fs.readFileSync(file).toString('utf8')) as Schema - const key = path.basename(file.replace(/-/g, ':')).replace('.json', '') + const key = path.basename(getKeyNameFromFilename(file)) if (file.split(path.sep).includes('hooks')) { schemas.hooks[key] = schema } else { diff --git a/src/commands/schema/generate.ts b/src/commands/schema/generate.ts index 0c0718a6..ea5884df 100644 --- a/src/commands/schema/generate.ts +++ b/src/commands/schema/generate.ts @@ -4,12 +4,13 @@ import {Flags} from '@oclif/core' import {createGenerator, Schema} from 'ts-json-schema-generator' import {SnapshotCommand} from '../../snapshot-command' import {red} from 'chalk' +import {getSchemaFileName} from '../../util' export type SchemasMap = { [key: string]: Schema; } -export type Schemas = { commands: SchemasMap; hooks: SchemasMap} +export type Schemas = { commands: SchemasMap; hooks: SchemasMap } export type GenerateResponse = string[]; @@ -128,7 +129,7 @@ export class SchemaGenerator { const hooks = this.base.config.pjson.oclif?.hooks ?? {} const hookId = Object.keys(hooks).find(key => { - const hookFiles = (Array.isArray(hooks[key]) ? hooks[key] : [hooks[key]])as string[] + const hookFiles = (Array.isArray(hooks[key]) ? hooks[key] : [hooks[key]]) as string[] const hookFileNames = hookFiles.map(f => path.basename(f).split('.')[0]) const currentFileName = path.basename(file).split('.')[0] return hookFileNames.includes(currentFileName) @@ -183,58 +184,58 @@ export class SchemaGenerator { } export default class SchemaGenerate extends SnapshotCommand { - public static flags = { - filepath: Flags.string({ - description: 'directory to save the generated schema files; can use "{version}" to insert the current CLI/plugin version', - default: './schemas', - }), - singlefile: Flags.boolean({ - description: 'put generated schema into a single file', - default: false, - }), - ignorevoid: Flags.boolean({ - description: 'ignore commands that return void', - default: true, - }), - }; - - public async run(): Promise { - const {flags} = await this.parse(SchemaGenerate) - const generator = new SchemaGenerator(this, flags.ignorevoid) - - const schemas = await generator.generate() - - const directory = flags.filepath.replace('{version}', this.config.version) - fs.mkdirSync(directory, {recursive: true}) - - const files: string[] = [] - if (flags.singlefile) { - const filePath = path.join(directory, 'schema.json') - fs.writeFileSync(filePath, JSON.stringify(schemas, null, 2)) + public static flags = { + filepath: Flags.string({ + description: 'directory to save the generated schema files; can use "{version}" to insert the current CLI/plugin version', + default: './schemas', + }), + singlefile: Flags.boolean({ + description: 'put generated schema into a single file', + default: false, + }), + ignorevoid: Flags.boolean({ + description: 'ignore commands that return void', + default: true, + }), + } + + public async run(): Promise { + const {flags} = await this.parse(SchemaGenerate) + const generator = new SchemaGenerator(this, flags.ignorevoid) + + const schemas = await generator.generate() + + const directory = flags.filepath.replace('{version}', this.config.version) + fs.mkdirSync(directory, {recursive: true}) + + const files: string[] = [] + if (flags.singlefile) { + const filePath = path.join(directory, 'schema.json') + fs.writeFileSync(filePath, JSON.stringify(schemas, null, 2)) + this.log(`Generated JSON schema file "${filePath}"`) + files.push(filePath) + } else { + for (const [cmdId, schema] of Object.entries(schemas.commands)) { + const fileName = getSchemaFileName(cmdId) + const filePath = path.join(directory, fileName) + fs.writeFileSync(filePath, JSON.stringify(schema, null, 2)) this.log(`Generated JSON schema file "${filePath}"`) files.push(filePath) - } else { - for (const [cmdId, schema] of Object.entries(schemas.commands)) { - const fileName = `${cmdId.replace(/:/g, '-')}.json` - const filePath = path.join(directory, fileName) + } + + if (Object.values(schemas.hooks).length > 0) { + const hooksDir = path.join(directory, 'hooks') + fs.mkdirSync(hooksDir, {recursive: true}) + for (const [hookId, schema] of Object.entries(schemas.hooks)) { + const fileName = getSchemaFileName(hookId) + const filePath = path.join(hooksDir, fileName) fs.writeFileSync(filePath, JSON.stringify(schema, null, 2)) this.log(`Generated JSON schema file "${filePath}"`) files.push(filePath) } - - if (Object.values(schemas.hooks).length > 0) { - const hooksDir = path.join(directory, 'hooks') - fs.mkdirSync(hooksDir, {recursive: true}) - for (const [hookId, schema] of Object.entries(schemas.hooks)) { - const fileName = `${hookId.replace(/:/g, '-')}.json` - const filePath = path.join(hooksDir, fileName) - fs.writeFileSync(filePath, JSON.stringify(schema, null, 2)) - this.log(`Generated JSON schema file "${filePath}"`) - files.push(filePath) - } - } } - - return files } + + return files + } } diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 00000000..5981dd9f --- /dev/null +++ b/src/util.ts @@ -0,0 +1,22 @@ +import * as path from 'path' + +/** + * Get the file name for a given command ID replacing "-" with "__" and ":" with "-" + * @param cmdId - command ID + * @returns {string} - file name + */ +export const getSchemaFileName = (cmdId: string): string => { + const baseName = cmdId.replace(/-/g, '__').replace(/:/g, '-') + return `${baseName}.json` +} + +/** + * Get the command ID from a given file name replacing "-" with ":" and "__" with "-" + * @param file - file name + * @returns {string} - command ID + */ +export const getKeyNameFromFilename = (file: string): string => { + return path.basename(file.replace(/-/g, ':')) + .replace(/__/g, '-') + .replace('.json', '') +} diff --git a/test/util.test.ts b/test/util.test.ts new file mode 100644 index 00000000..879af58d --- /dev/null +++ b/test/util.test.ts @@ -0,0 +1,40 @@ +import {expect} from '@oclif/test' +import {getKeyNameFromFilename} from '../src/util' +import {getSchemaFileName} from '../lib/util' + +describe('util test', () => { + describe('getKeyNameFromFilename', () => { + it('should return correct command id when only hyphens in file name a-b-c.json', () => { + expect(getKeyNameFromFilename('a-b-c.json')).to.equal('a:b:c') + }) + it('should return correct command id when escaped hyphens in file name a-b-c__d.json', () => { + expect(getKeyNameFromFilename('a-b-c__d.json')).to.equal('a:b:c-d') + }) + it('should return correct command id when escaped hyphens in file name a__b-c__d.json', () => { + expect(getKeyNameFromFilename('a__b-c__d.json')).to.equal('a-b:c-d') + }) + it('should return correct command id when escaped hyphens in file name a-b__c__d.json', () => { + expect(getKeyNameFromFilename('a-b__c__d.json')).to.equal('a:b-c-d') + }) + it('should return correct command id when underscore in file name a-b-c_d.json', () => { + expect(getKeyNameFromFilename('a-b-c_d.json')).to.equal('a:b:c_d') + }) + }) + describe('getSchemaFileName', () => { + it('should return correct file name when only ":" in command id a:b:c', () => { + expect(getSchemaFileName('a:b:c')).to.equal('a-b-c.json') + }) + it('should return correct file name when hyphens in command id a:b:c-d', () => { + expect(getSchemaFileName('a:b:c-d')).to.equal('a-b-c__d.json') + }) + it('should return correct file name when hyphens in command id a-b:c-d', () => { + expect(getSchemaFileName('a-b:c-d')).to.equal('a__b-c__d.json') + }) + it('should return correct file name when hyphens in command id a:b-c-d', () => { + expect(getSchemaFileName('a:b-c-d')).to.equal('a-b__c__d.json') + }) + it('should return correct file name when underscore in command id a:b:c_d', () => { + expect(getSchemaFileName('a:b:c_d')).to.equal('a-b-c_d.json') + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index f5fc5b85..17ac9942 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3334,16 +3334,11 @@ isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isbinaryfile@^4.0.10: +isbinaryfile@^4.0.10, isbinaryfile@^4.0.8: version "4.0.10" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== -isbinaryfile@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.8.tgz#5d34b94865bd4946633ecc78a026fc76c5b11fcf" - integrity sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -4862,20 +4857,13 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^7.0.0: +rxjs@^7.0.0, rxjs@^7.2.0: version "7.8.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== dependencies: tslib "^2.1.0" -rxjs@^7.2.0: - version "7.5.4" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.4.tgz#3d6bd407e6b7ce9a123e76b1e770dc5761aa368d" - integrity sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ== - dependencies: - tslib "^2.1.0" - safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"