diff --git a/.gitignore b/.gitignore index 59a44957..6ae51556 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ lib/ .nyc_output/ oclif.manifest.json /package-lock.json -yarn-error.json \ No newline at end of file +yarn-error.json + +.idea/* diff --git a/schemas/snapshot-compare.json b/schemas/snapshot-compare.json index afee4154..41e5e48f 100644 --- a/schemas/snapshot-compare.json +++ b/schemas/snapshot-compare.json @@ -61,9 +61,31 @@ ], "additionalProperties": false } + }, + "alias": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "removed": { + "type": "boolean" + }, + "added": { + "type": "boolean" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } } }, "required": [ + "alias", "flags", "name", "plugin" diff --git a/schemas/snapshot-generate.json b/schemas/snapshot-generate.json index ee1fa34a..cb5b4d61 100644 --- a/schemas/snapshot-generate.json +++ b/schemas/snapshot-generate.json @@ -5,28 +5,38 @@ "Snapshots": { "type": "array", "items": { - "type": "object", - "properties": { - "command": { - "type": "string" - }, - "plugin": { + "$ref": "#/definitions/SnapshotEntry" + } + }, + "SnapshotEntry": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "plugin": { + "type": "string" + }, + "flags": { + "type": "array", + "items": { "type": "string" - }, - "flags": { - "type": "array", - "items": { - "type": "string" - } } }, - "required": [ - "command", - "plugin", - "flags" - ], - "additionalProperties": false - } + "alias": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "command", + "plugin", + "flags", + "alias" + ], + "additionalProperties": false } } } \ No newline at end of file diff --git a/src/commands/snapshot/compare.ts b/src/commands/snapshot/compare.ts index 6a690dba..f4d60ef0 100644 --- a/src/commands/snapshot/compare.ts +++ b/src/commands/snapshot/compare.ts @@ -15,6 +15,7 @@ interface Change { type CommandChange = { plugin: string; flags: Change[]; + alias: Change[]; } & Change; export type CompareResponse = { @@ -50,8 +51,11 @@ export default class Compare extends SnapshotCommand { }) if (updatedCommand) { - const {changedFlags} = this.diffCommandFlags(initialCommand.flags, updatedCommand.flags) - if (changedFlags.length > 0) { + const changedFlags = this.diffCommandProperty(initialCommand.flags, updatedCommand.flags).changedProperty + const changeAlias = this.diffCommandProperty(initialCommand.alias, updatedCommand.alias).changedProperty + + if (changeAlias.length > 0 || changedFlags.length > 0) { + updatedCommand.alias = changeAlias diffCommands.push(updatedCommand) } } else { @@ -76,62 +80,80 @@ export default class Compare extends SnapshotCommand { removedCommands.forEach(command => { this.log(chalk.red(`\t-${command}`)) }) + addedCommands.forEach(command => { this.log(chalk.green(`\t+${command}`)) }) - const removedFlags: string[] = [] - if (diffCommands.length > 0) { - diffCommands.forEach(command => { - this.log(`\t ${command.name}`) + const removedProperties: string[] = [] - command.flags.forEach(flag => { - if (flag.added || flag.removed) { - const color = flag.added ? chalk.green : chalk.red - this.log(color(`\t\t${flag.added ? '+' : '-'}${flag.name}`)) + diffCommands.forEach(command => { + this.log(`\t${command.name}`) + + const printCommandDiff = (properties: Change[], propertyName: string) => { + if (properties.find(prop => prop.added || prop.removed)) this.log(`\t ${propertyName}:`) + properties.forEach(prop => { + if (prop.added || prop.removed) { + const color = prop.added ? chalk.green : chalk.red + this.log(color(`\t\t${prop.added ? '+' : '-'}${prop.name}`)) } - if (flag.removed) removedFlags.push() + if (prop.removed) removedProperties.push() }) - }) - } + } - this.log(`${EOL}Command or flag differences detected. If intended, please update the snapshot file and run again.`) + printCommandDiff(command.flags, 'Flags') + printCommandDiff(command.alias, 'Aliases') + }) - // Check if existent commands have been deleted - if (removedCommands.length > 0 || removedFlags.length > 0) { + this.log(`${EOL}Command, flag, or alias differences detected. If intended, please update the snapshot file and run again.`) + + // Check if existent commands, or properties (flags, aliases) have been deleted + if (removedCommands.length > 0 || removedProperties.length > 0) { this.log(chalk.red(`${EOL}Since there are deletions, a major version bump is required.`)) } - return {addedCommands, removedCommands, removedFlags, diffCommands} + return {addedCommands, removedCommands, removedFlags: removedCommands, diffCommands} } /** - * Compares a flag snapshot with the current command's flags - * @param {string[]} initialFlags Flag list from the snapshot - * @param {string[]} updatedFlags Flag list from runtime - * @param {string} initialCommand Command the flags belong to - * @return {boolean} true if no changes, false otherwise - */ + * @deprecated in favor of diffCommandProperty + * Compares a flag snapshot with the current command's flags + * @param {string[]} initialFlags Flag list from the snapshot + * @param {string[]} updatedFlags Flag list from runtime + * @return {boolean} true if no changes, false otherwise + */ public diffCommandFlags(initialFlags: string[], updatedFlags: Change[]) { - const updatedFlagNames = updatedFlags.map(updatedFlag => updatedFlag.name) - const addedFlags = _.difference(updatedFlagNames, initialFlags) - const removedFlags = _.difference(initialFlags, updatedFlagNames) - const changedFlags: Change[] = [] - - updatedFlags.forEach(updatedFlag => { - if (addedFlags.includes(updatedFlag.name)) { - updatedFlag.added = true - changedFlags.push(updatedFlag) + const diffedFlags = this.diffCommandProperty(initialFlags, updatedFlags) + return {addedFlags: diffedFlags.addedProperty, removedFlags: diffedFlags.removedProperty, updatedFlags: diffedFlags.updated, changedFlags: diffedFlags.changedProperty} + } + + /** + * compares two command's properties to + * @return a list of added, removed, updated, and changed properties + * @param initial initial command property to compare against + * @param updated generated command property to compare with + */ + public diffCommandProperty(initial: string[], updated: Change[]) { + const updatedPropertyNames = updated.map(update => update.name) + const addedProperty = _.difference(updatedPropertyNames, initial) + const removedProperty = _.difference(initial, updatedPropertyNames) + const changedProperty: Change[] = [] + + updated.forEach(update => { + if (addedProperty.includes(update.name)) { + update.added = true + changedProperty.push(update) } }) - removedFlags.forEach(removedFlag => { - changedFlags.push({name: removedFlag, removed: true}) + + removedProperty.forEach(remove => { + changedProperty.push({name: remove, removed: true}) // The removed flags in not included in the updated flags, but we want it to // so it shows removed. - updatedFlags.push({name: removedFlag, removed: true}) + updated.push({name: remove, removed: true}) }) - return {addedFlags, removedFlags, updatedFlags, changedFlags} + return {addedProperty, removedProperty, updated, changedProperty} } get changed(): CommandChange[] { @@ -140,14 +162,15 @@ export default class Compare extends SnapshotCommand { name: command.id, plugin: command.pluginName || '', flags: Object.entries(command.flags).map(flagName => ({name: flagName[0]})), + alias: command.aliases.map(alias => ({name: alias})), } }) } public async run(): Promise { const {flags} = await this.parse(Compare) - const oldCommandFlags = JSON.parse(fs.readFileSync(flags.filepath).toString('utf8')) as SnapshotEntry[] - const resultnewCommandFlags = this.changed - return this.compareSnapshot(oldCommandFlags, resultnewCommandFlags) + const oldCommands = JSON.parse(fs.readFileSync(flags.filepath).toString('utf8')) as SnapshotEntry[] + const newCommands = this.changed + return this.compareSnapshot(oldCommands, newCommands) } } diff --git a/src/commands/snapshot/generate.ts b/src/commands/snapshot/generate.ts index 346fac24..b24815e5 100644 --- a/src/commands/snapshot/generate.ts +++ b/src/commands/snapshot/generate.ts @@ -1,12 +1,8 @@ import {Flags} from '@oclif/core' import * as fs from 'fs' -import {SnapshotCommand} from '../../snapshot-command' +import {SnapshotCommand, SnapshotEntry} from '../../snapshot-command' -export type Snapshots = { - command: string; - plugin: string; - flags: string[]; -}[] +export type Snapshots = SnapshotEntry[] export default class Generate extends SnapshotCommand { public static flags = { diff --git a/src/snapshot-command.ts b/src/snapshot-command.ts index 6da1b850..30371136 100644 --- a/src/snapshot-command.ts +++ b/src/snapshot-command.ts @@ -5,6 +5,7 @@ export type SnapshotEntry = { command: string; plugin: string; flags: string[]; + alias: string[]; } export abstract class SnapshotCommand extends Command { @@ -22,6 +23,7 @@ export abstract class SnapshotCommand extends Command { command: command.id, plugin: command.pluginName, flags: Object.keys(command.flags).sort(), + alias: command.aliases, } }) as SnapshotEntry[] } diff --git a/test/commands/schema.compare.test.ts b/test/commands/schema.compare.test.ts index 73da85cf..04d7c58d 100644 --- a/test/commands/schema.compare.test.ts +++ b/test/commands/schema.compare.test.ts @@ -20,7 +20,7 @@ describe('schema:compare', () => { .command(['schema:compare', '--filepath=./test/schemas']) .it('shows changes', ctx => { expect(ctx.stdout).to.contain('Found the following schema changes:') - expect(ctx.stdout).to.contain('- definitions.Snapshots.items.properties.flags.type was not found in latest schema') + expect(ctx.stdout).to.contain('- definitions.Snapshots.items.additionalProperties was added to latest schema') expect(ctx.stdout).to.contain('- commands.snapshot:compare was not found in latest schema') expect(ctx.stdout).to.contain('If intended, please update the schema file(s) and run again.') })