import { Command , flags } from "@oclif/command" ; import { OutputFlags } from "@oclif/parser" ; import fs from "fs-extra" ; import { ApiDifferencer } from "./apiDifferencer" ; import { ApiChanges } from "./changes/apiChanges" ; import { ApiCollectionChanges } from "./changes/apiCollectionChanges" ; import { diffRamlDirectories } from "./diffDirectories" ; import { allCommonFlags } from "../common/flags" ; export class DiffCommand extends Command { static description = `Compute the difference between two API specifications
This command has three modes: ruleset, diff-only, and directory.
- Ruleset mode (default) compares two files and applies a ruleset to determine if any changes are breaking.
- Diff-only mode compares two files to determine if there are any differences, without applying a ruleset.
- Directory mode finds all exchange.json files in two directories recursively and compares all the spec files described in them. Applies the default ruleset.

In ruleset and diff-only mode, the arguments must be API specification (RAML) files.
In directory mode, the arguments must be directories that contain exchange.json files.

Exit statuses:
  0 - No breaking changes (ruleset mode) or no differences (diff-only / directory)
  1 - Breaking changes (ruleset mode) or differences found (diff only / directory)
  2 - Evaluation could not be completed` ; static flags = { ... allCommonFlags ( ) , ruleset : flags . string ( { char : "r" , description : `[default:${ ApiDifferencer . DEFAULT_RULES_PACKAGE_PATH }] Path to ruleset to apply to diff` , env : "DIFF_RULESET" , exclusive : [ "diff-only" , "dir" ] , } ) , "diff-only" : flags . boolean ( { description : "Only show differences without evaluating a ruleset" , default : false , exclusive : [ "ruleset" , "dir" , "format" ] , } ) , dir : flags . boolean ( { description : "Find the differences for files in two directory trees and applies default ruleset" , default : false , exclusive : [ "ruleset" , "diff-only" ] , } ) , "out-file" : flags . string ( { char : "o" , description : "File to store the computed difference" , } ) , format : flags . enum ( { char : "f" , description : "Format of the output. Defaults to JSON if --out-file is specified, otherwise text." , options : [ "json" , "console" ] , } ) , } ; static args = [ { name : "base" , required : true , description : "The base API spec file (ruleset / diff-only mode) or directory" , } , { name : "new" , required : true , description : "The new API spec file (ruleset / diff-only mode) or directory" , } , ] ; param changes param flags protected async _saveOrLog ( changes , flags ) { const file = flags [ "out-file" ] ; if ( file ) { if ( flags . format === "console" ) { await fs . writeFile ( file , changes . toFormattedString ( "console" ) ) ; } else { await fs . writeJson ( file , changes ) ; } } else { if ( flags . format === "json" || flags [ "diff-only" ] ) { this . log ( JSON . stringify ( changes , null , 2 ) ) ; } else { this . log ( changes . toFormattedString ( "console" ) ) ; } } } param baseApis param newApis param flags protected async _diffDirs ( baseApis , newApis , flags ) { let apiCollectionChanges ; try { apiCollectionChanges = await diffRamlDirectories ( baseApis , newApis ) ; await this . _saveOrLog ( apiCollectionChanges , flags ) ; } catch ( err ) { this . error ( err . message , { exit : 2 } ) ; } if ( apiCollectionChanges . hasChanges ( ) ) { this . exit ( 1 ) ; } } param baseApis param newApis param flags protected async _diffFiles ( baseApis , newApis , flags ) { let apiChanges ; try { const apiDifferencer = new ApiDifferencer ( baseApis , newApis ) ; apiChanges = await apiDifferencer . findChanges ( ) ; await this . _saveOrLog ( apiChanges , flags ) ; } catch ( err ) { this . error ( err . message , { exit : 2 } ) ; } if ( apiChanges . hasChanges ( ) ) { this . exit ( 1 ) ; } } param baseApis param newApis param flags protected async _diffFilesUsingRuleset ( baseApis , newApis , flags ) { let apiChanges ; const apiDifferencer = new ApiDifferencer ( baseApis , newApis ) ; try { apiChanges = await apiDifferencer . findAndCategorizeChanges ( flags . ruleset ) ; await this . _saveOrLog ( apiChanges , flags ) ; } catch ( err ) { this . error ( err . message , { exit : 2 } ) ; } if ( apiChanges . hasBreakingChanges ( ) ) { this . exit ( 1 ) ; } } async run ( ) { const { args , flags } = this . parse ( DiffCommand ) ; const baseApis = args . base ; const newApis = args . new ; if ( ! ( await fs . pathExists ( baseApis ) ) ) { this . error ( `File or directory not found: ${ baseApis }` ) ; } if ( ! ( await fs . pathExists ( newApis ) ) ) { this . error ( `File or directory not found: ${ newApis }` ) ; } if ( flags . dir ) { await this . _diffDirs ( baseApis , newApis , flags ) ; } else if ( flags [ "diff-only" ] ) { await this . _diffFiles ( baseApis , newApis , flags ) ; } else { await this . _diffFilesUsingRuleset ( baseApis , newApis , flags ) ; } this . exit ( ) ; } }