chore(script): enhance transforming lodash test codes script (#546)

* Handle import lodash methods

* Supports assert.deepStrictEqual

* Enhance brokenSyntax

* Update assert

* Enhance import

* Enhance Error log

* Enhance brokenSyntax and add commenting delete local variable
This commit is contained in:
Dayong Lee 2024-09-18 16:15:02 +09:00 committed by GitHub
parent c21874ef3b
commit f53bfd5c34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 179 additions and 27 deletions

View File

@ -1,21 +1,54 @@
export function formatBrokenSyntax(source: string): string { function fixExpectFunction(source: string): string {
// Fix broken syntax const expectMatched = source.match(/expect(\(.+\)).toEqual.+\);/g);
const brokenMatched = source.match(/(?<=,)[\s\d\w?]+\[.+\).toEqual\((?!\[).+\]\);/g);
if (brokenMatched != null) { if (!expectMatched) {
for (const match of brokenMatched) { return source;
const splited = match.split(').toEqual(');
const formatted = `).toEqual(${splited.join(', ')}`;
source = source.replace(match, formatted);
}
} }
// Remove deleting local variable const splitMatched = expectMatched.map(match => match.split('.toEqual('));
const deleteMatched = source.match(/delete [\w\d]+;/g); const brokenSyntax = splitMatched.map(([expect]) => {
if (deleteMatched != null) { let count = 0;
for (const match of deleteMatched) {
source = source.replace(match, ''); for (const char of expect) {
if (['(', '{', '['].includes(char)) {
count++;
} else if ([')', '}', ']'].includes(char)) {
count--;
}
}
return count > 0;
});
for (let i = 0; i < expectMatched.length; i++) {
if (brokenSyntax[i]) {
const front = splitMatched[i][0];
const startWithBracket = front.match(/expect\([{[].+[}\]],/);
const startWithFunction = front.match(/expect\(.+\(.+\),/);
const start = startWithFunction
? startWithFunction[0].length
: startWithBracket
? startWithBracket[0].length
: front.indexOf(',') + 1;
const fixed = `${front.slice(0, start)}).toEqual(${front.slice(start, front.length - 1)}, ${splitMatched[i][1]}`;
source = source.replace(expectMatched[i], fixed);
} }
} }
return source; return source;
} }
export function formatBrokenSyntax(source: string): string {
// Fix broken syntax
source = fixExpectFunction(source);
// `delete localvariable` to comment
source = source.replace(
/delete [^.[\]]+;/g,
match => `// Deleting local variable in strict mode. So commenting it out.\n// ${match}`
);
return source;
}

View File

@ -0,0 +1,53 @@
import type { Collection, JSCodeshift } from 'jscodeshift';
export function transformAssert(root: Collection, jscodeshift: JSCodeshift): void {
// Change `assert.deepStrictEqual(a, b, c)` to `expect(a, c).toEqual(b)`
root
.find(jscodeshift.CallExpression, {
callee: {
type: 'MemberExpression',
object: {
name: 'assert',
},
property: {
type: 'Identifier',
},
},
})
.replaceWith(({ node }) => {
if (node.callee.type === 'MemberExpression' && node.callee.property.type === 'Identifier') {
const expectArguments =
node.arguments.length === 3 ? [node.arguments[0], node.arguments[2]] : [node.arguments[0]];
const expect = jscodeshift.callExpression(jscodeshift.identifier('expect'), expectArguments);
switch (node.callee.property.name) {
case 'deepStrictEqual':
return jscodeshift.memberExpression(
expect,
jscodeshift.callExpression(jscodeshift.identifier('toEqual'), [node.arguments[1]])
);
case 'deepEqual':
return jscodeshift.memberExpression(
expect,
jscodeshift.callExpression(jscodeshift.identifier('toEqual'), [node.arguments[1]])
);
case 'strictEqual':
return jscodeshift.memberExpression(
expect,
jscodeshift.callExpression(jscodeshift.identifier('toBe'), [node.arguments[1]])
);
case 'notEqual':
return jscodeshift.memberExpression(
expect,
jscodeshift.callExpression(jscodeshift.identifier('not.toBe'), [node.arguments[1]])
);
case 'notStrictEqual':
return jscodeshift.memberExpression(
expect,
jscodeshift.callExpression(jscodeshift.identifier('not.toBe'), [node.arguments[1]])
);
}
}
return node;
});
}

View File

@ -35,15 +35,44 @@ export function transformImport(root: Collection, jscodeshift: JSCodeshift): voi
.remove(); .remove();
// Add import { describe, it, expect } from 'vitest'; // Add import { describe, it, expect } from 'vitest';
const vitestImport = jscodeshift.importDeclaration( const vitestImport = root.find(jscodeshift.ImportDeclaration, {
[ source: {
jscodeshift.importSpecifier(jscodeshift.identifier('describe')), value: 'vitest',
jscodeshift.importSpecifier(jscodeshift.identifier('it')), },
jscodeshift.importSpecifier(jscodeshift.identifier('expect')), specifiers: [
{
type: 'ImportSpecifier',
imported: {
name: 'describe',
},
},
{
type: 'ImportSpecifier',
imported: {
name: 'it',
},
},
{
type: 'ImportSpecifier',
imported: {
name: 'expect',
},
},
], ],
jscodeshift.literal('vitest') });
);
astPath.value.program.body.unshift(vitestImport); if (!vitestImport.length) {
astPath.value.program.body.unshift(
jscodeshift.importDeclaration(
[
jscodeshift.importSpecifier(jscodeshift.identifier('describe')),
jscodeshift.importSpecifier(jscodeshift.identifier('it')),
jscodeshift.importSpecifier(jscodeshift.identifier('expect')),
],
jscodeshift.literal('vitest')
)
);
}
// Remove import from 'lodash' // Remove import from 'lodash'
root root
@ -53,4 +82,28 @@ export function transformImport(root: Collection, jscodeshift: JSCodeshift): voi
}, },
}) })
.remove(); .remove();
// Change '../src/merge' to '../index'
const methodSet = new Set<string>();
root
.find(jscodeshift.ImportDeclaration, {
source: {
value: (value: string) => value.startsWith('../src/'),
},
})
.forEach(({ node }) => {
if (node.specifiers && node.specifiers[0].type === 'ImportDefaultSpecifier' && node.specifiers[0].local) {
methodSet.add(node.specifiers[0].local.name);
}
})
.remove();
if (methodSet.size) {
const methodImport = jscodeshift.importDeclaration(
Array.from(methodSet).map(method => jscodeshift.importSpecifier(jscodeshift.identifier(method))),
jscodeshift.literal('../index')
);
astPath.value.program.body.unshift(methodImport);
}
} }

View File

@ -1,7 +1,9 @@
import type { Collection, JSCodeshift } from 'jscodeshift'; import type { Collection, JSCodeshift } from 'jscodeshift';
export function transformLodashStable(root: Collection, jscodeshift: JSCodeshift): void { export function transformLodashStable(root: Collection, jscodeshift: JSCodeshift): void {
// Replace lodashStable.each and lodashStable.map with Array.prototype.forEach and Array.prototype.map const astPath = root.getAST()[0];
let needImportEsToolkit = false;
root root
.find(jscodeshift.CallExpression, { .find(jscodeshift.CallExpression, {
callee: { callee: {
@ -28,10 +30,19 @@ export function transformLodashStable(root: Collection, jscodeshift: JSCodeshift
node.callee.object = node.arguments[0] as any; node.callee.object = node.arguments[0] as any;
return jscodeshift.callExpression(node.callee, node.arguments.slice(1)); return jscodeshift.callExpression(node.callee, node.arguments.slice(1));
} }
// Remove lodashStable from the callee // Remove lodashStable from the callee
return jscodeshift.callExpression(node.callee.property, node.arguments); node.callee.object = jscodeshift.identifier('esToolkit');
needImportEsToolkit = true;
} }
return node; return node;
}); });
if (needImportEsToolkit) {
const esToolkitImport = jscodeshift.importDeclaration(
[jscodeshift.importNamespaceSpecifier(jscodeshift.identifier('esToolkit'))],
jscodeshift.literal('../index')
);
astPath.value.program.body.unshift(esToolkitImport);
}
} }

View File

@ -2,17 +2,19 @@ import type { API, FileInfo } from 'jscodeshift';
import { formatBrokenSyntax } from './_internal/formatter/brokenSyntax'; import { formatBrokenSyntax } from './_internal/formatter/brokenSyntax';
import { transformImport } from './_internal/transform/import'; import { transformImport } from './_internal/transform/import';
import { transformLodashStable } from './_internal/transform/lodashStable'; import { transformLodashStable } from './_internal/transform/lodashStable';
import { transformAssert } from './_internal/transform/assert';
export default function transform(file: FileInfo, { jscodeshift }: API) { export default function transform(file: FileInfo, { jscodeshift }: API) {
try { try {
const root = jscodeshift(formatBrokenSyntax(file.source)); const root = jscodeshift(formatBrokenSyntax(file.source));
transformImport(root, jscodeshift);
transformLodashStable(root, jscodeshift); transformLodashStable(root, jscodeshift);
transformImport(root, jscodeshift);
transformAssert(root, jscodeshift);
return root.toSource(); return root.toSource();
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
console.error(`File Path: ${file.path}`);
console.error(`Error Messaging: ${error.message}`); console.error(`Error Messaging: ${error.message}`);
console.error('Please resolve the error before continuing.'); console.error('Please resolve the error before continuing.');
console.error('If you need help, please open an issue on GitHub.'); console.error('If you need help, please open an issue on GitHub.');