Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/codemods/__snapshots__/exponentiation.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`exponentiation > should handle Math.pow in expressions 1`] = `
"const result = (a + b) ** (c - d) * 2;
const value = 10 + (x) ** (y);
"
`;

exports[`exponentiation > should handle nested Math.pow calls 1`] = `
"const result = ((2) ** (3)) ** (4);
"
`;

exports[`exponentiation > should not change code without Math.pow 1`] = `
"
const result = x ** y;
const squared = 2 ** 3;
const value = Math.sqrt(4);
"
`;

exports[`exponentiation > should replace Math.pow with exponentiation operator 1`] = `
"const result = (2) ** (3);
const squared = (x) ** (2);
const complex = (base) ** (exponent);
"
`;
57 changes: 57 additions & 0 deletions src/codemods/exponentiation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {describe, it, expect} from 'vitest';
import {codemod} from './exponentiation.js';

describe('exponentiation', () => {
it('should replace Math.pow with exponentiation operator', () => {
const source = `
const result = Math.pow(2, 3);
const squared = Math.pow(x, 2);
const complex = Math.pow(base, exponent);
`;
const result = codemod.apply({source});
expect(result).toMatchSnapshot();
});

it('should handle nested Math.pow calls', () => {
const source = `
const result = Math.pow(Math.pow(2, 3), 4);
`;
const result = codemod.apply({source});
expect(result).toMatchSnapshot();
});

it('should handle Math.pow in expressions', () => {
const source = `
const result = Math.pow(a + b, c - d) * 2;
const value = 10 + Math.pow(x, y);
`;
const result = codemod.apply({source});
expect(result).toMatchSnapshot();
});

it('should not change code without Math.pow', () => {
const source = `
const result = x ** y;
const squared = 2 ** 3;
const value = Math.sqrt(4);
`;
const result = codemod.apply({source});
expect(result).toMatchSnapshot();
});

describe('test', () => {
it('should detect Math.pow usage', () => {
const source = `
const result = Math.pow(2, 3);
`;
expect(codemod.test({source})).toBe(true);
});

it('should not detect when there is no Math.pow', () => {
const source = `
const result = x ** y;
`;
expect(codemod.test({source})).toBe(false);
});
});
});
51 changes: 51 additions & 0 deletions src/codemods/exponentiation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {parse, Lang, type Edit, type NapiConfig} from '@ast-grep/napi';
import type {Options, CodeMod} from '../shared.js';

const mathPowRule: NapiConfig = {
rule: {
pattern: 'Math.pow($BASE, $EXPONENT)'
}
};

export const codemod: CodeMod = {
test(options: Options): boolean {
const ast = parse(Lang.TypeScript, options.source);
const root = ast.root();

return root.has(mathPowRule);
},
apply(options: Options): string {
let source = options.source;

while (true) {
const ast = parse(Lang.TypeScript, source);
const root = ast.root();

const mathPowCalls = root.findAll(mathPowRule);
if (mathPowCalls.length === 0) {
break;
}

const edits: Edit[] = [];

for (const node of mathPowCalls) {
const base = node.getMatch('BASE');
const exponent = node.getMatch('EXPONENT');
if (base && exponent) {
const baseText = base.text();
const exponentText = exponent.text();
const edit = node.replace(`(${baseText}) ** (${exponentText})`);
edits.push(edit);
}
}

const newSource = root.commitEdits(edits);
if (newSource === source) {
break;
}
source = newSource;
}

return source;
}
};
1 change: 1 addition & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export {codemod as arrayIncludes} from './codemods/array-includes.js';
export {codemod as arrayToReversed} from './codemods/array-to-reversed.js';
export {codemod as arrayToSorted} from './codemods/array-to-sorted.js';
export {codemod as arrayToSpliced} from './codemods/array-to-spliced.js';
export {codemod as exponentiation} from './codemods/exponentiation.js';
export {codemod as nullishCoalescing} from './codemods/nullish-coalescing.js';
export {codemod as postcssSignFunctions} from './codemods/postcss-sign-functions.js';
export {codemod as stringIncludes} from './codemods/string-includes.js';