Skip to content

Commit

Permalink
feat(betterer ✨): borrow merge conflict handler from yarn (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
phenomnomnominal authored Jul 1, 2020
1 parent a726472 commit 5c396eb
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 3 deletions.
5 changes: 3 additions & 2 deletions .betterer.results
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ exports[`new eslint rules`] = {
[29, 19, 8, "Don\'t use \`Function\` as a type. The \`Function\` type accepts any function-like value.\\nIt provides no type safety when calling the function, which can be a common source of bugs.\\nIt also accepts things like class declarations, which will throw at runtime as they will not be called with \`new\`.\\nIf you are expecting the function to accept certain arguments, you should explicitly define the function shape.", "4136871687"],
[152, 6, 9, "Unsafe assignment of an any value.", "166477157"]
],
"packages/betterer/src/errors.ts:29397898": [
"packages/betterer/src/errors.ts:2984110573": [
[7, 96, 10, "Invalid type \\"string | Error | BettererError\\" of template literal expression.", "3805115554"],
[8, 99, 11, "Invalid type \\"string | Error | BettererError\\" of template literal expression.", "2365839858"],
[9, 99, 11, "Invalid type \\"string | Error | BettererError\\" of template literal expression.", "2365839858"],
[11, 91, 12, "Invalid type \\"string | Error | BettererError\\" of template literal expression.", "362595323"],
[12, 70, 12, "Invalid type \\"string | Error | BettererError\\" of template literal expression.", "362595323"],
[13, 65, 8, "Invalid type \\"string | Error | BettererError\\" of template literal expression.", "92641249"],
[14, 67, 8, "Invalid type \\"string | Error | BettererError\\" of template literal expression.", "92641249"]
[14, 67, 8, "Invalid type \\"string | Error | BettererError\\" of template literal expression.", "92641249"],
[17, 59, 11, "Invalid type \\"string | Error | BettererError\\" of template literal expression.", "2365839858"]
],
"packages/betterer/src/register.ts:845928349": [
[17, 8, 39, "Unsafe assignment of an any value.", "2552944516"],
Expand Down
4 changes: 4 additions & 0 deletions packages/betterer/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ export const COULDNT_LOAD_REPORTER = registerError((reporterName) => `could not
export const NO_REPORTER_LOADED = registerError((reporterName) => `"${reporterName}" didn't create a reporter. 😔`);
export const UNKNOWN_HOOK_NAME = registerError((hookName) => `"${hookName}" is not a valid reporter hook name. 😔`);
export const HOOK_NOT_A_FUNCTION = registerError((hookName) => `"${hookName}" is not a function. 😔`);

export const COULDNT_RESOLVE_MERGE_CONFLICT = registerError(
(resultsPath) => `could not resolve merge conflict in "${resultsPath}". 😔`
);
64 changes: 63 additions & 1 deletion packages/betterer/src/results/reader.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import * as assert from 'assert';
import { promises as fs } from 'fs';

import { requireText } from '../require';
import { COULDNT_READ_RESULTS } from '../errors';
import { COULDNT_READ_RESULTS, COULDNT_RESOLVE_MERGE_CONFLICT } from '../errors';
import { BettererExpectedResults } from './types';

const MERGE_CONFLICT_ANCESTOR = '|||||||';
const MERGE_CONFLICT_END = '>>>>>>>';
const MERGE_CONFLICT_SEP = '=======';
const MERGE_CONFLICT_START = '<<<<<<<';

export async function read(resultsPath: string): Promise<BettererExpectedResults> {
let file = '';
try {
Expand All @@ -12,9 +18,65 @@ export async function read(resultsPath: string): Promise<BettererExpectedResults
return {};
}

if (hasMergeConflicts(file)) {
try {
const [ours, theirs] = extractConflicts(file);
return { ...requireText(ours), ...requireText(theirs) };
} catch (e) {
throw COULDNT_RESOLVE_MERGE_CONFLICT(resultsPath, e);
}
}

try {
return requireText(file);
} catch {
throw COULDNT_READ_RESULTS(resultsPath);
}
}

function hasMergeConflicts(str: string): boolean {
return str.includes(MERGE_CONFLICT_START) && str.includes(MERGE_CONFLICT_SEP) && str.includes(MERGE_CONFLICT_END);
}

function extractConflicts(file: string): [string, string] {
const ours = [];
const theirs = [];
const lines = file.split(/\r?\n/g);
let skip = false;

while (lines.length) {
const line = lines.shift();
assert(line);
if (line.startsWith(MERGE_CONFLICT_START)) {
// get our file
while (lines.length) {
const conflictLine = lines.shift();
assert(conflictLine);
if (conflictLine === MERGE_CONFLICT_SEP) {
skip = false;
break;
} else if (skip || conflictLine.startsWith(MERGE_CONFLICT_ANCESTOR)) {
skip = true;
continue;
} else {
ours.push(conflictLine);
}
}

// get their file
while (lines.length) {
const conflictLine = lines.shift();
assert(conflictLine);
if (conflictLine.startsWith(MERGE_CONFLICT_END)) {
break;
} else {
theirs.push(conflictLine);
}
}
} else {
ours.push(line);
theirs.push(line);
}
}
return [ours.join('\n'), theirs.join('\n')];
}
34 changes: 34 additions & 0 deletions test/__snapshots__/betterer-conflict.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`betterer should work when there is a merge conflict in the results file 1`] = `
Array [
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ / / _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_) | __/ |_| || __/ | | __/ |
/ | / |_.__/ /___|/__|/__/___|_| /___|_|
",
" ☀️ betterer info 💬 - ",
"running \\"no raw console calls\\"!",
" ☀️ betterer warn 🚨 - ",
"\\"no raw console calls\\" stayed the same. 😐",
" ☀️ betterer info 💬 - ",
"1 test got checked. 🤔",
" ☀️ betterer warn 🚨 - ",
"1 test stayed the same. 😐",
]
`;

exports[`betterer should work when there is a merge conflict in the results file 2`] = `
"// BETTERER RESULTS V1.
exports[\`no raw console calls\`] = {
timestamp: 0,
value: \`{
\\"src/index.ts:1884655653\\": [
[0, 0, 11, \\"TSQuery match\\", \\"3870399096\\"]
]
}\`
};
"
`;
68 changes: 68 additions & 0 deletions test/betterer-conflict.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { betterer } from '@betterer/betterer';

import { createFixture } from './fixture';

describe('betterer', () => {
it('should work when there is a merge conflict in the results file', async () => {
const { logs, paths, readFile, cleanup } = await createFixture('test-betterer-conflict', {
'.betterer.ts': `
import { tsquery } from '@betterer/tsquery';
export default {
'no raw console calls': tsquery(
'./tsconfig.json',
'CallExpression > PropertyAccessExpression[expression.name="console"][name.name="log"]'
)
};
`,
'.betterer.results': `
// BETTERER RESULTS V1.
exports[\`no raw console calls\`] = {
timestamp: 0,
value: \`{
|||<<<<<<< our-change
\\"src/index.ts:315583663\\": [
[0, 0, 11, \\"TSQuery match\\", \\"3870399096\\"],
[1, 0, 11, \\"TSQuery match\\", \\"3870399096\\"]
]
|||=======
\\"src/index.ts:913095150\\": [
[0, 0, 11, \\"TSQuery match\\", \\"3870399096\\"]
]
|||>>>>>>> their-change
}\`
};
`.replace(/\|||/g, ''), // Mess with it a bit so that tooling doesn't think this is a real conflict:
'tsconfig.json': `
{
"compilerOptions": {
"noEmit": true,
"lib": ["esnext"],
"moduleResolution": "node",
"target": "ES5",
"typeRoots": ["../../node_modules/@types/"],
"resolveJsonModule": true
},
"include": ["./src/**/*", ".betterer.ts"]
}
`,
'src/index.ts': `
console.log('foo');
console.info('foo');
`
});

const configPaths = [paths.config];
const resultsPath = paths.results;

await betterer({ configPaths, resultsPath });

expect(logs).toMatchSnapshot();

const result = await readFile(resultsPath);

expect(result).toMatchSnapshot();

await cleanup();
});
});

0 comments on commit 5c396eb

Please sign in to comment.