From ebb348c2ff6d571ace2793bdcee065e7995a2a48 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 18:35:22 +0400 Subject: [PATCH 01/21] feat(util): add if-diff, readline helper --- src/util/helpers.ts | 110 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/src/util/helpers.ts b/src/util/helpers.ts index 55698cac6..5dcbbeb80 100644 --- a/src/util/helpers.ts +++ b/src/util/helpers.ts @@ -1,3 +1,4 @@ +import {createInterface} from 'node:readline/promises'; import {exec} from 'node:child_process'; import {promisify} from 'node:util'; @@ -6,6 +7,8 @@ import {logger} from './logger'; import {STRINGS} from '../config'; +import {Difference} from '../types/lib/compare'; + const {ISSUE_TEMPLATE} = STRINGS; /** @@ -44,3 +47,110 @@ export const mergeObjects = (defaults: any, input: any) => { * Promise version of Node's `exec` from `child-process`. */ export const execPromise = promisify(exec); + +/** + * Converts given `value` to either `1` or `0`. + */ +const convertToXorable = (value: any) => { + if (typeof value === 'number') { + return value !== 0 ? 1 : 0; + } + + if (typeof value === 'boolean') { + return value ? 1 : 0; + } + + if (typeof value === 'string') { + return value.length > 0 ? 1 : 0; + } + + return 0; +}; + +/** + * If one of the `valuesToCheck` values is undefined, then set `missing`, otherwise `exists`. + */ +const setValuesIfMissing = (response: Difference) => { + const source = convertToXorable(response.source); + const target = convertToXorable(response.target); + + if (source ^ target) { + ['source', 'target'].forEach(value => { + response[value] = response[value] ? 'exists' : 'missing'; + }); + + return response; + } + + return response; +}; + +/** + * Checks if objects are primitive types. + */ +export const oneIsPrimitive = (source: any, target: any) => { + // eslint-disable-next-line eqeqeq + if (source == null || target == null) { + return true; + } + + return source !== Object(source) && target !== Object(target); +}; + +/** + * Format not matching message for CLI logging. + */ +export const formatNotMatchingLog = (message: Difference) => { + const flattenMessage = setValuesIfMissing(message); + + Object.keys(flattenMessage).forEach(key => { + if (key === 'message' || key === 'path') { + console.log(message[key]); + } else { + console.log(`${key}: ${message[key]}`); + } + }); +}; + +/** + * Checks if there is data piped, then collects it. + * Otherwise returns empty string. + */ +const collectPipedData = async () => { + if (process.stdin.isTTY) { + return; + } + + const readline = createInterface({ + input: process.stdin, + }); + + const data: string[] = []; + + for await (const line of readline) { + data.push(line); + } + + return data.join('\n'); +}; + +/** + * Checks if there is piped data, tries to parse yaml from it. + * Throws error if there is piped info, but there is no valid manifest. + */ +export const parseManifestFromStdin = async () => { + const pipedSourceManifest = await collectPipedData(); + + if (!pipedSourceManifest) { + return ''; + } + + const regex = /# start((?:.*\n)+?)# end/; + const match = regex.exec(pipedSourceManifest); + + if (!match) { + throw new Error('Manifest not found in STDIN.'); + } + + return match![1]; +}; From 73cf421f730a5411247913a991ff43c0116e80fb Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 18:35:58 +0400 Subject: [PATCH 02/21] feat(util): add if-diff strategy to args --- src/util/args.ts | 54 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/src/util/args.ts b/src/util/args.ts index 6bffad5ed..a78d0a507 100644 --- a/src/util/args.ts +++ b/src/util/args.ts @@ -7,21 +7,21 @@ import {logger} from './logger'; import {CONFIG, STRINGS} from '../config'; -import {ManifestProcessArgs, ProcessArgsOutputs} from '../types/process-args'; +import {IFDiffArgs, IEArgs, ProcessArgsOutputs} from '../types/process-args'; +import {LoadDiffParams} from '../types/util/args'; const {CliInputError} = ERRORS; -const {IE} = CONFIG; -const {ARGS, HELP} = IE; +const {IE, IF_DIFF} = CONFIG; const {FILE_IS_NOT_YAML, MANIFEST_IS_MISSING, NO_OUTPUT} = STRINGS; /** - * Validates process arguments + * Validates `ie` process arguments. */ const validateAndParseProcessArgs = () => { try { - return parse(ARGS, HELP); + return parse(IE.ARGS, IE.HELP); } catch (error) { if (error instanceof Error) { throw new CliInputError(error.message); @@ -52,7 +52,7 @@ const prependFullFilePath = (filePath: string) => { * If it is, then returns object containing full path. * 4. If params are missing or invalid, then rejects with `CliInputError`. */ -export const parseArgs = (): ProcessArgsOutputs => { +export const parseIEProcessArgs = (): ProcessArgsOutputs => { const { manifest, output, @@ -81,3 +81,45 @@ export const parseArgs = (): ProcessArgsOutputs => { throw new CliInputError(MANIFEST_IS_MISSING); }; + +/** -- IF Diff -- */ + +/** + * Parses `if-diff` process arguments. + */ +const validateAndParseIfDiffArgs = () => { + try { + return parse(IF_DIFF.ARGS, IF_DIFF.HELP); + } catch (error) { + if (error instanceof Error) { + throw new CliInputError(error.message); + } + + throw error; + } +}; + +/** + * Checks for `source` and `target` flags to be present. + */ +export const parseIfDiffArgs = () => { + const {source, target} = validateAndParseIfDiffArgs(); + + if (target) { + if (checkIfFileIsYaml(target)) { + const response: LoadDiffParams = { + targetPath: prependFullFilePath(target), + }; + + if (source && checkIfFileIsYaml(source)) { + response.sourcePath = prependFullFilePath(source); + } + + return response; + } + + throw new CliInputError(FILE_IS_NOT_YAML); // change to one of the source or target parts are not a yaml + } + + throw new CliInputError(MANIFEST_IS_MISSING); // change to one of the source or target are missing +}; From 4b986e4ac2fd4a0dd4cd46ecb72c5f3f10a48d91 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 18:37:54 +0400 Subject: [PATCH 03/21] feat(types): init util args --- src/types/util/args.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/types/util/args.ts diff --git a/src/types/util/args.ts b/src/types/util/args.ts new file mode 100644 index 000000000..d9debf3cd --- /dev/null +++ b/src/types/util/args.ts @@ -0,0 +1,4 @@ +export type LoadDiffParams = { + sourcePath?: string; + targetPath: string; +}; From ecd30194426da12a5154b4a4ccd59dd5c353e30e Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 18:39:15 +0400 Subject: [PATCH 04/21] feat(types): init compare lib types --- src/types/lib/compare.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/types/lib/compare.ts diff --git a/src/types/lib/compare.ts b/src/types/lib/compare.ts new file mode 100644 index 000000000..756fca9eb --- /dev/null +++ b/src/types/lib/compare.ts @@ -0,0 +1,6 @@ +export type Difference = { + path?: string; + source?: any; + target?: any; + [key: string]: any; +}; From 1b591a408d454fa51335d4a182a5039a3472f5cb Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 18:51:26 +0400 Subject: [PATCH 05/21] feat(types): introduce IFDiffArgs to process args --- src/types/process-args.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/types/process-args.ts b/src/types/process-args.ts index 3ba4eab31..7dfdcb929 100644 --- a/src/types/process-args.ts +++ b/src/types/process-args.ts @@ -1,10 +1,15 @@ -export interface ManifestProcessArgs { +export interface IEArgs { manifest?: string; output?: string; 'override-params'?: string; stdout?: boolean; } +export interface IFDiffArgs { + source?: string; + target: string; +} + export interface Options { outputPath?: string; stdout?: boolean; From 7ec755218d078c4164f2a51704e7e176330ad808 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 18:51:52 +0400 Subject: [PATCH 06/21] fix(lib): typo in parameterize --- src/lib/parameterize.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/parameterize.ts b/src/lib/parameterize.ts index 35e965ec6..ab26156c0 100644 --- a/src/lib/parameterize.ts +++ b/src/lib/parameterize.ts @@ -11,7 +11,7 @@ const {REJECTING_OVERRIDE, UNKNOWN_PARAM} = STRINGS; /** * Parameters manager. Provides get aggregation method and combine functionality. */ -const Parametrize = () => { +const Parameterize = () => { let parametersStorage = PARAMETERS; /** @@ -63,4 +63,4 @@ const Parametrize = () => { }; }; -export const parameterize = Parametrize(); +export const parameterize = Parameterize(); From c4b066eecdd66742244cd4daad360598d95d4ea3 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 18:53:14 +0400 Subject: [PATCH 07/21] fix(lib): implement if diff files loader --- src/lib/load.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/lib/load.ts b/src/lib/load.ts index a024444df..b8ac3841b 100644 --- a/src/lib/load.ts +++ b/src/lib/load.ts @@ -1,8 +1,14 @@ +import * as YAML from 'js-yaml'; + import {openYamlFileAsObject} from '../util/yaml'; import {readAndParseJson} from '../util/json'; +import {parseManifestFromStdin} from '../util/helpers'; import {PARAMETERS} from '../config'; + import {Parameters} from '../types/parameters'; +import {LoadDiffParams} from '../types/util/args'; +import {Manifest} from '../types/manifest'; /** * Parses manifest file as an object. Checks if parameter file is passed via CLI, then loads it too. @@ -22,3 +28,28 @@ export const load = async (inputPath: string, paramPath?: string) => { parameters, }; }; + +/** + * Loads files to compare. As a source file checks if data is piped and then decides which one to take. + */ +export const loadIfDiffFiles = async (params: LoadDiffParams) => { + const {sourcePath, targetPath} = params; + const pipedSourceManifest = await parseManifestFromStdin(); + + if (!sourcePath && !pipedSourceManifest) { + throw new Error('Source is invalid.'); + } + + const loadFromSource = + sourcePath && (await openYamlFileAsObject(sourcePath!)); + const loadFromSTDIN = + pipedSourceManifest && (await YAML.load(pipedSourceManifest!)); + + const rawSourceManifest = loadFromSource || loadFromSTDIN; + const rawTargetManifest = await openYamlFileAsObject(targetPath); + + return { + rawSourceManifest, + rawTargetManifest, + }; +}; From 8c2546bb68aba81fa104415253c1db6001107a3a Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 18:54:09 +0400 Subject: [PATCH 08/21] feat(lib): implement compare --- src/lib/compare.ts | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/lib/compare.ts diff --git a/src/lib/compare.ts b/src/lib/compare.ts new file mode 100644 index 000000000..d920b0072 --- /dev/null +++ b/src/lib/compare.ts @@ -0,0 +1,52 @@ +import {oneIsPrimitive} from '../util/helpers'; + +import {Difference} from '../types/lib/compare'; + +/** + * 1. If objects are not of the same type or are primitive types, compares directly. + * 2. Gets the keys from both objects. + * 3. Checks for keys present in both objects. + * 4. If both are arrays, checks their elements. + * 5. Checks for differences in values for common keys. + * 6. If all common keys are checked and no differences are found, return empty object. + */ +export const compare = (source: any, target: any, path = ''): Difference => { + if (oneIsPrimitive(source, target)) { + return source !== target + ? { + path, + source: source, + target: target, + } + : {}; + } + + const keys1 = Object.keys(source); + const keys2 = Object.keys(target); + + const allKeys = new Set([...keys1, ...keys2]); + + if (Array.isArray(source) && Array.isArray(target)) { + if (source.length !== target.length) { + return {path, source: source, target: target}; + } + + for (let i = 0; i < source.length; i++) { + return compare(source[i], target[i], path ? `${path}[${i}]` : `${i}`); + } + } + + for (const key of allKeys) { + const result = compare( + source[key], + target[key], + path ? `${path}.${key}` : key + ); + + if (Object.keys(result).length) { + return result; + } + } + + return {}; +}; From 270af5ccac081287afb63ec0bd21efcc5f8daccb Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 18:55:00 +0400 Subject: [PATCH 09/21] feat(config): implement if-diff args --- src/config/config.ts | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/config/config.ts b/src/config/config.ts index 9a3f8d57e..4b23f9aa5 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -2,7 +2,7 @@ import {ArgumentConfig, ParseOptions} from 'ts-command-line-args'; import {STRINGS} from './strings'; -import {ManifestProcessArgs} from '../types/process-args'; +import {IFDiffArgs, IEArgs} from '../types/process-args'; const {DISCLAIMER_MESSAGE} = STRINGS; @@ -39,7 +39,7 @@ export const CONFIG = { alias: 'h', description: '[prints out the above help instruction]', }, - } as ArgumentConfig, + } as ArgumentConfig, HELP: { helpArg: 'help', headerContentSections: [ @@ -50,6 +50,33 @@ export const CONFIG = { ], } as ParseOptions, }, + IF_DIFF: { + ARGS: { + source: { + type: String, + optional: true, + alias: 's', + description: '[path to the source file]', + }, + target: { + type: String, + optional: false, + alias: 't', + description: '[path to the target file', + }, + } as ArgumentConfig, + HELP: { + helpArg: 'help', + headerContentSections: [ + {header: 'Impact Framework', content: 'IF-Diff Helpful keywords:'}, + ], + footerContentSections: [ + {header: 'Green Software Foundation', content: DISCLAIMER_MESSAGE}, + ], + } as ParseOptions, + SUCCESS_MESSAGE: 'Files match!', + FAILURE_MESSAGE: 'Files do not match!', + }, GITHUB_PATH: 'https://github.com', NATIVE_PLUGIN: 'if-plugins', AGGREGATION_ADDITIONAL_PARAMS: ['timestamp', 'duration'], From dd0b0033b3f84b23271ed3560e2a469c9a0ead6d Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 18:55:34 +0400 Subject: [PATCH 10/21] feat(builtins): tune export log --- src/builtins/export-log.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/builtins/export-log.ts b/src/builtins/export-log.ts index 1daf88824..3120f3386 100644 --- a/src/builtins/export-log.ts +++ b/src/builtins/export-log.ts @@ -10,7 +10,10 @@ export const ExportLog = () => { ...context, tree, }; - console.log(YAML.dump(outputFile, {noRefs: true})); + + console.log(`# start +${YAML.dump(outputFile, {noRefs: true})} +# end`); }; return {execute}; From 0d3c0eacdf7796a75ad379ba9c6f73ea4ce45aab Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 19:06:49 +0400 Subject: [PATCH 11/21] test(util): change parseArgs to parseIEProcessArgs --- src/__tests__/unit/util/args.test.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/__tests__/unit/util/args.test.ts b/src/__tests__/unit/util/args.test.ts index d3c5d7364..cefda328d 100644 --- a/src/__tests__/unit/util/args.test.ts +++ b/src/__tests__/unit/util/args.test.ts @@ -48,7 +48,7 @@ jest.mock('ts-command-line-args', () => ({ import path = require('path'); -import {parseArgs} from '../../../util/args'; +import {parseIEProcessArgs} from '../../../util/args'; import {ERRORS} from '../../../util/errors'; import {STRINGS} from '../../../config'; @@ -60,14 +60,14 @@ const {MANIFEST_IS_MISSING, FILE_IS_NOT_YAML} = STRINGS; describe('util/args: ', () => { const originalEnv = process.env; - describe('parseArgs(): ', () => { + describe('parseIEProcessArgs(): ', () => { it('throws error if there is no argument passed.', () => { expect.assertions(5); process.env.result = 'error'; // used for mocking try { - parseArgs(); + parseIEProcessArgs(); } catch (error) { expect(error).toEqual(MANIFEST_IS_MISSING); } @@ -75,7 +75,7 @@ describe('util/args: ', () => { process.env.result = 'throw-error-object'; try { - parseArgs(); + parseIEProcessArgs(); } catch (error) { expect(error).toBeInstanceOf(CliInputError); expect(error).toEqual(new CliInputError(MANIFEST_IS_MISSING)); @@ -84,7 +84,7 @@ describe('util/args: ', () => { process.env.result = 'manifest-is-missing'; try { - parseArgs(); + parseIEProcessArgs(); } catch (error) { expect(error).toBeInstanceOf(CliInputError); expect(error).toEqual(new CliInputError(MANIFEST_IS_MISSING)); @@ -96,7 +96,7 @@ describe('util/args: ', () => { process.env.result = 'manifest'; - const result = parseArgs(); + const result = parseIEProcessArgs(); const manifestPath = 'manifest-mock.yml'; const expectedResult = { inputPath: path.normalize(`${processRunningPath}/${manifestPath}`), @@ -113,7 +113,7 @@ describe('util/args: ', () => { process.env.result = 'absolute-path'; - const result = parseArgs(); + const result = parseIEProcessArgs(); const manifestPath = 'manifest-mock.yml'; const expectedResult = { inputPath: path.normalize(`${processRunningPath}/${manifestPath}`), @@ -128,7 +128,7 @@ describe('util/args: ', () => { process.env.result = 'override-params'; - const result = parseArgs(); + const result = parseIEProcessArgs(); const manifestPath = 'manifest-mock.yml'; const expectedResult = { inputPath: path.normalize(`${processRunningPath}/${manifestPath}`), @@ -144,7 +144,7 @@ describe('util/args: ', () => { process.env.result = 'manifest-output'; - const result = parseArgs(); + const result = parseIEProcessArgs(); const manifestPath = 'manifest-mock.yml'; const outputPath = 'output-mock.yml'; const expectedResult = { @@ -164,7 +164,7 @@ describe('util/args: ', () => { process.env.result = 'not-yaml'; try { - parseArgs(); + parseIEProcessArgs(); } catch (error) { expect(error).toBeInstanceOf(CliInputError); expect(error).toEqual(new CliInputError(FILE_IS_NOT_YAML)); From 06d17f02fdfd033376664602814b6b673f4c36ce Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 19:12:46 +0400 Subject: [PATCH 12/21] test(builtins): fix export log test --- src/__tests__/unit/builtins/export-log.test.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/__tests__/unit/builtins/export-log.test.ts b/src/__tests__/unit/builtins/export-log.test.ts index 9c93274df..f6990ab1e 100644 --- a/src/__tests__/unit/builtins/export-log.test.ts +++ b/src/__tests__/unit/builtins/export-log.test.ts @@ -9,10 +9,12 @@ describe('builtins/export-log:', () => { const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); await ExportLog().execute(tree, context); + const expectedMessage = `# start +${YAML.dump({...context, tree}, {noRefs: true})} +# end`; + expect(mockConsoleLog).toHaveBeenCalled(); - expect(mockConsoleLog).toHaveBeenCalledWith( - YAML.dump({...context, tree}, {noRefs: true}) - ); + expect(mockConsoleLog).toHaveBeenCalledWith(expectedMessage); mockConsoleLog.mockRestore(); }); @@ -21,10 +23,12 @@ describe('builtins/export-log:', () => { const mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(); await ExportLog().execute({}, context); + const expectedMessage = `# start +${YAML.dump({...context, tree: {}}, {noRefs: true})} +# end`; + expect(mockConsoleLog).toHaveBeenCalled(); - expect(mockConsoleLog).toHaveBeenCalledWith( - YAML.dump({...context, tree: {}}, {noRefs: true}) - ); + expect(mockConsoleLog).toHaveBeenCalledWith(expectedMessage); mockConsoleLog.mockRestore(); }); From 6c4c25e5e7d6337e26a14c0cbd1c89d469c0e86d Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 19:21:01 +0400 Subject: [PATCH 13/21] fix(src): rename parse args --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 17eec0334..041086559 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,7 @@ import {initialize} from './lib/initialize'; import {load} from './lib/load'; import {parameterize} from './lib/parameterize'; -import {parseArgs} from './util/args'; +import {parseIEProcessArgs} from './util/args'; import {andHandle} from './util/helpers'; import {logger} from './util/logger'; import {validateManifest} from './util/validations'; @@ -17,7 +17,7 @@ import {STRINGS} from './config'; const {DISCLAIMER_MESSAGE} = STRINGS; const impactEngine = async () => { - const options = parseArgs(); + const options = parseIEProcessArgs(); logger.info(DISCLAIMER_MESSAGE); const {inputPath, paramPath, outputOptions} = options; From 84057c680f52c7029a505606fc5d62f7a6721648 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 19:21:33 +0400 Subject: [PATCH 14/21] feat(src): implement diff functionality --- src/diff.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/diff.ts diff --git a/src/diff.ts b/src/diff.ts new file mode 100644 index 000000000..9179cb4a8 --- /dev/null +++ b/src/diff.ts @@ -0,0 +1,46 @@ +#!/usr/bin/env node +/* eslint-disable no-process-exit */ +import {loadIfDiffFiles} from './lib/load'; +import {compare} from './lib/compare'; + +import {parseIfDiffArgs} from './util/args'; +import {formatNotMatchingLog} from './util/helpers'; +import {validateManifest} from './util/validations'; + +import {CONFIG} from './config'; + +const {IF_DIFF} = CONFIG; +const {SUCCESS_MESSAGE, FAILURE_MESSAGE} = IF_DIFF; + +const IfDiff = async () => { + const {sourcePath, targetPath} = parseIfDiffArgs(); + + const {rawSourceManifest, rawTargetManifest} = await loadIfDiffFiles({ + targetPath, + sourcePath, + }); + const [sourceManifest, targetManifest] = [ + rawSourceManifest, + rawTargetManifest, + ].map(validateManifest); + const result = compare(sourceManifest, targetManifest); + + if (Object.keys(result).length) { + formatNotMatchingLog({ + message: FAILURE_MESSAGE, + ...result, + }); + + process.exit(1); + } + + console.log(SUCCESS_MESSAGE); + process.exit(0); +}; + +IfDiff().catch(error => { + if (error instanceof Error) { + console.log(error); + process.exit(2); + } +}); From 0b454df1c923b8f1661b9d00c6d84449fe48a2f7 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Wed, 15 May 2024 21:16:11 +0400 Subject: [PATCH 15/21] feat(package): init if-diff script --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1be080bba..6477ec7a2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "email": "info@gsf.com" }, "bin": { - "ie": "./build/index.js" + "ie": "./build/index.js", + "if-diff": "./build/diff.js" }, "bugs": { "url": "https://github.com/Green-Software-Foundation/if/issues/new?assignees=&labels=feedback&projects=&template=feedback.md&title=Feedback+-+" @@ -67,6 +68,7 @@ "fix": "gts fix", "fix:package": "fixpack", "ie": "npx ts-node src/index.ts", + "if-diff": "npx ts-node src/diff.ts", "lint": "gts lint", "prepare": "husky install", "prepublish": "npm run build", From 85e309086117eb6874b1b4533e841871b8e3268b Mon Sep 17 00:00:00 2001 From: MariamKhalatova Date: Thu, 23 May 2024 01:40:00 +0400 Subject: [PATCH 16/21] fix(lib): fixed array members validation issue --- src/lib/compare.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/compare.ts b/src/lib/compare.ts index d920b0072..cc83f87fc 100644 --- a/src/lib/compare.ts +++ b/src/lib/compare.ts @@ -32,7 +32,7 @@ export const compare = (source: any, target: any, path = ''): Difference => { } for (let i = 0; i < source.length; i++) { - return compare(source[i], target[i], path ? `${path}[${i}]` : `${i}`); + compare(source[i], target[i], path ? `${path}[${i}]` : `${i}`); } } From 8a855dbdee4e04df4764d3a4bbed2afb17767506 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Fri, 24 May 2024 16:10:01 +0400 Subject: [PATCH 17/21] fix(util): add objects to convert to xorable --- src/util/helpers.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util/helpers.ts b/src/util/helpers.ts index 5dcbbeb80..1f71a66a3 100644 --- a/src/util/helpers.ts +++ b/src/util/helpers.ts @@ -64,6 +64,10 @@ const convertToXorable = (value: any) => { return value.length > 0 ? 1 : 0; } + if (typeof value === 'object') { + return 1; + } + return 0; }; From 27f3fd0be889ff5e06737b220c19f468c65a2161 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Fri, 24 May 2024 16:10:25 +0400 Subject: [PATCH 18/21] fix(lib): remove array length check in compare --- src/lib/compare.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lib/compare.ts b/src/lib/compare.ts index cc83f87fc..39f89eb04 100644 --- a/src/lib/compare.ts +++ b/src/lib/compare.ts @@ -27,10 +27,6 @@ export const compare = (source: any, target: any, path = ''): Difference => { const allKeys = new Set([...keys1, ...keys2]); if (Array.isArray(source) && Array.isArray(target)) { - if (source.length !== target.length) { - return {path, source: source, target: target}; - } - for (let i = 0; i < source.length; i++) { compare(source[i], target[i], path ? `${path}[${i}]` : `${i}`); } From ce1a43099683d949ebd5ce6317546ce5b0b6fff7 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Fri, 24 May 2024 16:55:17 +0400 Subject: [PATCH 19/21] feat(util): add equality checker to helpers --- src/util/helpers.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/util/helpers.ts b/src/util/helpers.ts index 1f71a66a3..b1b8d8f4d 100644 --- a/src/util/helpers.ts +++ b/src/util/helpers.ts @@ -48,6 +48,21 @@ export const mergeObjects = (defaults: any, input: any) => { */ export const execPromise = promisify(exec); +/** + * `If-diff` equality checker. + */ +export const checkIfEqual = (source: any, target: any) => { + if (source === target) { + return true; + } + + if (source === '*' || target === '*') { + return true; + } + + return false; +}; + /** * Converts given `value` to either `1` or `0`. */ From b91819d92376006e5045df72bb78986b90299082 Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Fri, 24 May 2024 16:55:42 +0400 Subject: [PATCH 20/21] feat(lib): introduce equality checker to compare --- src/lib/compare.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/compare.ts b/src/lib/compare.ts index 39f89eb04..12bfb95b8 100644 --- a/src/lib/compare.ts +++ b/src/lib/compare.ts @@ -1,4 +1,4 @@ -import {oneIsPrimitive} from '../util/helpers'; +import {checkIfEqual, oneIsPrimitive} from '../util/helpers'; import {Difference} from '../types/lib/compare'; @@ -12,13 +12,13 @@ import {Difference} from '../types/lib/compare'; */ export const compare = (source: any, target: any, path = ''): Difference => { if (oneIsPrimitive(source, target)) { - return source !== target - ? { + return checkIfEqual(source, target) + ? {} + : { path, source: source, target: target, - } - : {}; + }; } const keys1 = Object.keys(source); From 6e96ca7849874123d354259e4e51acc6a008134e Mon Sep 17 00:00:00 2001 From: Narek Hovhannisyan Date: Fri, 24 May 2024 16:58:43 +0400 Subject: [PATCH 21/21] fix(src): use logger for errors --- src/diff.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/diff.ts b/src/diff.ts index 9179cb4a8..7deb09e35 100644 --- a/src/diff.ts +++ b/src/diff.ts @@ -8,6 +8,7 @@ import {formatNotMatchingLog} from './util/helpers'; import {validateManifest} from './util/validations'; import {CONFIG} from './config'; +import {logger} from './util/logger'; const {IF_DIFF} = CONFIG; const {SUCCESS_MESSAGE, FAILURE_MESSAGE} = IF_DIFF; @@ -40,7 +41,7 @@ const IfDiff = async () => { IfDiff().catch(error => { if (error instanceof Error) { - console.log(error); + logger.error(error); process.exit(2); } });