diff --git a/src/analysis.ts b/src/analysis.ts index de6206c3..c0a9a9bf 100644 --- a/src/analysis.ts +++ b/src/analysis.ts @@ -37,6 +37,7 @@ import { AnalyzeGitOptions, GitOptions, } from './interfaces/analysis-options.interface'; +import { fromEntries } from './lib/utils'; const sleep = (duration: number) => new Promise(resolve => setTimeout(resolve, duration)); @@ -163,7 +164,7 @@ export async function analyzeBundle({ function normalizeResultFiles(files: IAnalysisFiles, baseDir: string): IAnalysisFiles { if (baseDir) { - return Object.fromEntries( + return fromEntries( Object.entries(files).map(([path, positions]) => { const filePath = resolveBundleFilePath(baseDir, path); return [filePath, positions]; @@ -178,7 +179,7 @@ const moveSuggestionIndexes = ( suggestions: { [index: string]: T }, ): { [index: string]: T } => { const entries = Object.entries(suggestions); - return Object.fromEntries( + return fromEntries( entries.map(([i, s]) => { return [`${parseInt(i, 10) + suggestionIndex + 1}`, s]; }), @@ -193,7 +194,7 @@ function mergeBundleResults(bundle: IFileBundle, analysisData: IBundleResult, li const newSuggestions = moveSuggestionIndexes(suggestionIndex, analysisData.analysisResults.suggestions); const suggestions = { ...bundle.analysisResults.suggestions, ...newSuggestions }; - const newFiles = Object.fromEntries( + const newFiles = fromEntries( Object.entries(analysisData.analysisResults.files).map(([fn, s]) => { return [fn, moveSuggestionIndexes(suggestionIndex, s)]; }), diff --git a/src/bundles.ts b/src/bundles.ts index 60de4f11..9d637117 100644 --- a/src/bundles.ts +++ b/src/bundles.ts @@ -17,6 +17,7 @@ import { } from './http'; import { MAX_PAYLOAD, MAX_UPLOAD_ATTEMPTS } from './constants'; import emitter from './emitter'; +import { fromEntries } from './lib/utils'; type BundleErrorCodes = CreateBundleErrorCodes | CheckBundleErrorCodes | ExtendBundleErrorCodes; @@ -35,7 +36,7 @@ async function* prepareRemoteBundle( const fileChunks = chunk(files, maxPayload / 300); emitter.createBundleProgress(0, fileChunks.length); for (const [i, chunkedFiles] of fileChunks.entries()) { - const paramFiles = Object.fromEntries(chunkedFiles.map(d => [d.bundlePath, d.hash])); + const paramFiles = fromEntries(chunkedFiles.map(d => [d.bundlePath, d.hash])); if (bundleId === null) { // eslint-disable-next-line no-await-in-loop diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 00000000..1d26d192 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,13 @@ +// We are using the same types that Object.fromEntries has +// eslint-disable-next-line import/prefer-default-export, @typescript-eslint/no-explicit-any +export function fromEntries(entries: Iterable): any; +export function fromEntries( + entries: Iterable, +): { + [k in PropertyKey]: T; +} { + return [...entries].reduce((obj, [key, val]) => { + obj[key] = val; // eslint-disable-line no-param-reassign + return obj; + }, {}); +} diff --git a/tests/api.spec.ts b/tests/api.spec.ts index e89957f6..0754bf95 100644 --- a/tests/api.spec.ts +++ b/tests/api.spec.ts @@ -1,6 +1,6 @@ import { baseURL, authHost, sessionToken, TEST_TIMEOUT } from './constants/base'; import { bundleFiles, bundleFilesFull } from './constants/sample'; - +import { fromEntries } from '../src/lib/utils'; import { getFilters, startSession, @@ -132,7 +132,7 @@ describe('Requests to public API', () => { it( 'creates bundle successfully', async () => { - const files = Object.fromEntries([...(await bundleFiles).entries()].map(([i, d]) => [d.bundlePath, `${i}`])); + const files = fromEntries([...(await bundleFiles).entries()].map(([i, d]) => [d.bundlePath, `${i}`])); const response = await createBundle({ baseURL, @@ -310,7 +310,7 @@ describe('Requests to public API', () => { 'test successful workflow with and without linters', async () => { // Create a bundle first - const files = Object.fromEntries((await bundleFilesFull).map(d => [d.bundlePath, d.hash])); + const files = fromEntries((await bundleFilesFull).map(d => [d.bundlePath, d.hash])); const bundleResponse = await createBundle({ baseURL, diff --git a/tests/utils.spec.ts b/tests/utils.spec.ts new file mode 100644 index 00000000..346c62ad --- /dev/null +++ b/tests/utils.spec.ts @@ -0,0 +1,60 @@ +import { fromEntries } from '../src/lib/utils'; +describe('fromEntries', () => { + it('Object transformations', async () => { + // Arrange + const obj = { a: 1, b: 2, c: 3 }; + const expected = { a: 2, b: 4, c: 6 }; + + // Action + const fromEntriesRes = fromEntries(Object.entries(obj).map(([key, val]) => [key, val * 2])); + + // Assert + expect(fromEntriesRes).toMatchObject(expected); + }); + + it('Converting an Array to an Object', async () => { + // Arrange + const arr = [ + ['0', 'a'], + ['1', 'b'], + ['2', 'c'], + ]; + const expected = { 0: 'a', 1: 'b', 2: 'c' }; + + // Action + const fromEntriesRes = fromEntries(arr); + + // Assert + expect(fromEntriesRes).toMatchObject(expected); + }); + + it('Converting a Map to an Object', async () => { + // Arrange + const map = new Map([ + ['foo', 12], + ['baz', 42], + ]); + const expected = { foo: 12, baz: 42 }; + + // Action + const fromEntriesRes = fromEntries(map); + + // Assert + expect(fromEntriesRes).toMatchObject(expected); + }); + + it('Duplicate key', async () => { + // Arrange + const arr = [ + ['foo', 1], + ['foo', 2], + ]; + const expected = { foo: 2 }; + + // Action + const fromEntriesRes = fromEntries(arr); + + // Assert + expect(fromEntriesRes).toMatchObject(expected); + }); +});