diff --git a/.scripts/tests/_internal/formatter/brokenSyntax.ts b/.scripts/tests/_internal/formatter/brokenSyntax.ts index 404fdfa0..37362043 100644 --- a/.scripts/tests/_internal/formatter/brokenSyntax.ts +++ b/.scripts/tests/_internal/formatter/brokenSyntax.ts @@ -1,21 +1,54 @@ -export function formatBrokenSyntax(source: string): string { - // Fix broken syntax - const brokenMatched = source.match(/(?<=,)[\s\d\w?]+\[.+\).toEqual\((?!\[).+\]\);/g); - if (brokenMatched != null) { - for (const match of brokenMatched) { - const splited = match.split(').toEqual('); - const formatted = `).toEqual(${splited.join(', ')}`; - source = source.replace(match, formatted); - } +function fixExpectFunction(source: string): string { + const expectMatched = source.match(/expect(\(.+\)).toEqual.+\);/g); + + if (!expectMatched) { + return source; } - // Remove deleting local variable - const deleteMatched = source.match(/delete [\w\d]+;/g); - if (deleteMatched != null) { - for (const match of deleteMatched) { - source = source.replace(match, ''); + const splitMatched = expectMatched.map(match => match.split('.toEqual(')); + const brokenSyntax = splitMatched.map(([expect]) => { + let count = 0; + + 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; } + +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; +} diff --git a/.scripts/tests/_internal/transform/assert.ts b/.scripts/tests/_internal/transform/assert.ts new file mode 100644 index 00000000..2838b673 --- /dev/null +++ b/.scripts/tests/_internal/transform/assert.ts @@ -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; + }); +} diff --git a/.scripts/tests/_internal/transform/import.ts b/.scripts/tests/_internal/transform/import.ts index 36e0d292..1e8a61a2 100644 --- a/.scripts/tests/_internal/transform/import.ts +++ b/.scripts/tests/_internal/transform/import.ts @@ -35,15 +35,44 @@ export function transformImport(root: Collection, jscodeshift: JSCodeshift): voi .remove(); // Add import { describe, it, expect } from 'vitest'; - const vitestImport = jscodeshift.importDeclaration( - [ - jscodeshift.importSpecifier(jscodeshift.identifier('describe')), - jscodeshift.importSpecifier(jscodeshift.identifier('it')), - jscodeshift.importSpecifier(jscodeshift.identifier('expect')), + const vitestImport = root.find(jscodeshift.ImportDeclaration, { + source: { + value: 'vitest', + }, + 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' root @@ -53,4 +82,28 @@ export function transformImport(root: Collection, jscodeshift: JSCodeshift): voi }, }) .remove(); + + // Change '../src/merge' to '../index' + const methodSet = new Set(); + 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); + } } diff --git a/.scripts/tests/_internal/transform/lodashStable.ts b/.scripts/tests/_internal/transform/lodashStable.ts index 76c67249..b5f9aac5 100644 --- a/.scripts/tests/_internal/transform/lodashStable.ts +++ b/.scripts/tests/_internal/transform/lodashStable.ts @@ -1,7 +1,9 @@ import type { Collection, JSCodeshift } from 'jscodeshift'; 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 .find(jscodeshift.CallExpression, { callee: { @@ -28,10 +30,19 @@ export function transformLodashStable(root: Collection, jscodeshift: JSCodeshift node.callee.object = node.arguments[0] as any; return jscodeshift.callExpression(node.callee, node.arguments.slice(1)); } - // Remove lodashStable from the callee - return jscodeshift.callExpression(node.callee.property, node.arguments); + node.callee.object = jscodeshift.identifier('esToolkit'); + needImportEsToolkit = true; } return node; }); + + if (needImportEsToolkit) { + const esToolkitImport = jscodeshift.importDeclaration( + [jscodeshift.importNamespaceSpecifier(jscodeshift.identifier('esToolkit'))], + jscodeshift.literal('../index') + ); + + astPath.value.program.body.unshift(esToolkitImport); + } } diff --git a/.scripts/tests/transform-lodash-test.ts b/.scripts/tests/transform-lodash-test.ts index fa43571d..76c7cc7d 100644 --- a/.scripts/tests/transform-lodash-test.ts +++ b/.scripts/tests/transform-lodash-test.ts @@ -2,17 +2,19 @@ import type { API, FileInfo } from 'jscodeshift'; import { formatBrokenSyntax } from './_internal/formatter/brokenSyntax'; import { transformImport } from './_internal/transform/import'; import { transformLodashStable } from './_internal/transform/lodashStable'; +import { transformAssert } from './_internal/transform/assert'; export default function transform(file: FileInfo, { jscodeshift }: API) { try { const root = jscodeshift(formatBrokenSyntax(file.source)); - - transformImport(root, jscodeshift); transformLodashStable(root, jscodeshift); + transformImport(root, jscodeshift); + transformAssert(root, jscodeshift); return root.toSource(); } catch (error) { if (error instanceof Error) { + console.error(`File Path: ${file.path}`); console.error(`Error Messaging: ${error.message}`); console.error('Please resolve the error before continuing.'); console.error('If you need help, please open an issue on GitHub.');