Skip to content

Commit

Permalink
feat: invert boolean logic
Browse files Browse the repository at this point in the history
  • Loading branch information
j4k0xb committed Dec 24, 2023
1 parent 5303ebc commit e80e5c1
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 0 deletions.
17 changes: 17 additions & 0 deletions apps/docs/src/concepts/unminify.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ console.log(a); // [!code ++]
Infinity // [!code ++]
```

## invert-boolean-logic

```js
!(a == b) // [!code --]
a != b // [!code ++]
```

```js
!(a || b || c) // [!code --]
!a && !b && !c // [!code ++]
```

```js
!(a && b && c) // [!code --]
!a || !b || !c // [!code ++]
```

## json-parse

```js
Expand Down
34 changes: 34 additions & 0 deletions packages/webcrack/src/unminify/test/invert-boolean-logic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { test } from 'vitest';
import { testTransform } from '../../../test';
import invertBooleanLogic from '../transforms/invert-boolean-logic';

const expectJS = testTransform(invertBooleanLogic);

test('loose equal', () =>
expectJS('!(a == b);').toMatchInlineSnapshot(`a != b;`));

test('strict equal', () =>
expectJS('!(a === b);').toMatchInlineSnapshot(`a !== b;`));

test('not equal', () =>
expectJS('!(a != b);').toMatchInlineSnapshot(`a == b;`));

test('not strict equal', () =>
expectJS('!(a !== b);').toMatchInlineSnapshot(`a === b;`));

test('greater than', () =>
expectJS('!(a > b);').toMatchInlineSnapshot(`a <= b;`));

test('less than', () => expectJS('!(a < b);').toMatchInlineSnapshot(`a >= b;`));

test('greater than or equal', () =>
expectJS('!(a >= b);').toMatchInlineSnapshot(`a < b;`));

test('less than or equal', () =>
expectJS('!(a <= b);').toMatchInlineSnapshot(`a > b;`));

test('logical or', () =>
expectJS('!(a || b || c);').toMatchInlineSnapshot(`!a && !b && !c;`));

test('logical and', () =>
expectJS('!(a && b && c);').toMatchInlineSnapshot(`!a || !b || !c;`));
1 change: 1 addition & 0 deletions packages/webcrack/src/unminify/transforms/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { default as blockStatements } from './block-statements';
export { default as computedProperties } from './computed-properties';
export { default as infinity } from './infinity';
export { default as invertBooleanLogic } from './invert-boolean-logic';
export { default as jsonParse } from './json-parse';
export { default as logicalToIf } from './logical-to-if';
export { default as mergeElseIf } from './merge-else-if';
Expand Down
71 changes: 71 additions & 0 deletions packages/webcrack/src/unminify/transforms/invert-boolean-logic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as t from '@babel/types';
import * as m from '@codemod/matchers';
import { Transform } from '../../ast-utils';

const INVERTED_BINARY_OPERATORS = {
'==': '!=',
'===': '!==',
'!=': '==',
'!==': '===',
'>': '<=',
'<': '>=',
'>=': '<',
'<=': '>',
} as const;

const INVERTED_LOGICAL_OPERATORS = {
'||': '&&',
'&&': '||',
} as const;

export default {
name: 'invert-boolean-logic',
tags: ['safe'],
visitor: () => {
const logicalExpression = m.logicalExpression(
m.or(...Object.values(INVERTED_LOGICAL_OPERATORS)),
);
const logicalMatcher = m.unaryExpression('!', logicalExpression);

const binaryExpression = m.capture(
m.binaryExpression(m.or(...Object.values(INVERTED_BINARY_OPERATORS))),
);
const binaryMatcher = m.unaryExpression('!', binaryExpression);

return {
UnaryExpression: {
exit(path) {
const { argument } = path.node;

if (binaryMatcher.match(path.node)) {
binaryExpression.current!.operator =
INVERTED_BINARY_OPERATORS[
binaryExpression.current!
.operator as keyof typeof INVERTED_BINARY_OPERATORS
];

path.replaceWith(binaryExpression.current!);
this.changes++;
} else if (logicalMatcher.match(path.node)) {
let current = argument;
while (logicalExpression.match(current)) {
current.operator =
INVERTED_LOGICAL_OPERATORS[
current.operator as keyof typeof INVERTED_LOGICAL_OPERATORS
];

current.right = t.unaryExpression('!', current.right);
if (current.left.type !== 'LogicalExpression') {
current.left = t.unaryExpression('!', current.left);
}
current = current.left;
}

path.replaceWith(argument);
this.changes++;
}
},
},
};
},
} satisfies Transform;

0 comments on commit e80e5c1

Please sign in to comment.