From d5b8a1856517a937801142bd55857032f408b97a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=89=E1=85=A1=E1=86=BC?= =?UTF-8?q?=E1=84=83=E1=85=AE?= Date: Tue, 27 May 2025 05:34:39 +0900 Subject: [PATCH] feature: add change-parameter-object options --- .../change-parameter-object/optionsSchema.ts | 24 ++++ .../transformer.test.ts | 33 +++++ .../change-parameter-object/transformer.ts | 105 +++++++++++++++ .../transformers/component/_category_.json | 2 +- website/transformers/function/_category_.json | 7 + .../function/change-parameter-object.md | 124 ++++++++++++++++++ 6 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 package/src/transformers/function/change-parameter-object/optionsSchema.ts create mode 100644 package/src/transformers/function/change-parameter-object/transformer.test.ts create mode 100644 package/src/transformers/function/change-parameter-object/transformer.ts create mode 100644 website/transformers/function/_category_.json create mode 100644 website/transformers/function/change-parameter-object.md diff --git a/package/src/transformers/function/change-parameter-object/optionsSchema.ts b/package/src/transformers/function/change-parameter-object/optionsSchema.ts new file mode 100644 index 0000000..c687628 --- /dev/null +++ b/package/src/transformers/function/change-parameter-object/optionsSchema.ts @@ -0,0 +1,24 @@ +import { z } from "zod"; +import zodToJsonSchema from "zod-to-json-schema"; + +export const optionsSchema = z + .object({ + functionSourceType: z + .enum(["absolute", "relative"]) + .optional() + .describe("function source's type. you can choose absolute or relative"), + functionNameType: z + .enum(["default", "named"]) + .optional() + .describe("function name's type. you can choose default or named"), + functionSource: z + .string() + .describe("function source. you can choose package name or target path"), + functionName: z.string().describe("function name"), + objectKeys: z.string().array().describe("function parameter object keys"), + }) + .describe("remove props"); + +export const optionsSchemaJsonSchema = zodToJsonSchema(optionsSchema); + +export type OptionsSchema = z.infer; diff --git a/package/src/transformers/function/change-parameter-object/transformer.test.ts b/package/src/transformers/function/change-parameter-object/transformer.test.ts new file mode 100644 index 0000000..b7f526a --- /dev/null +++ b/package/src/transformers/function/change-parameter-object/transformer.test.ts @@ -0,0 +1,33 @@ +import { it, expect, describe } from "vitest"; +import transform from "./transformer"; +import { OptionsSchema } from "./optionsSchema"; +import getTransformParms from "../../../utils/getTransformPaths"; + +describe("general case", () => { + it("named export", () => { + const input = ` +import { dummyFunction } from "foo"; +dummyFunction("hello", "this is dashboard page"); +`; + const expectedOuput = ` +import { dummyFunction } from "foo"; +dummyFunction({ + "title": "hello", + "description": "this is dashboard page" +}); +`; + const transformParms = getTransformParms({ + input, + options: { + functionSourceType: "absolute", + functionNameType: "named", + functionSource: "foo", + functionName: "dummyFunction", + objectKeys: ["title", "description"], + }, + }); + const output = transform(...transformParms); + + expect(output).toEqual(expectedOuput); + }); +}); diff --git a/package/src/transformers/function/change-parameter-object/transformer.ts b/package/src/transformers/function/change-parameter-object/transformer.ts new file mode 100644 index 0000000..1997bd8 --- /dev/null +++ b/package/src/transformers/function/change-parameter-object/transformer.ts @@ -0,0 +1,105 @@ +import { OptionsSchema } from "./optionsSchema"; +import getConvertedPath from "../../../utils/getConvertedPath"; +import type { API, FileInfo } from "jscodeshift"; + +function transformer(file: FileInfo, api: API, options: OptionsSchema) { + const sourceCode = file.source; + const jscodeshift = api.jscodeshift; + + const { + functionSourceType = "absolute", + functionNameType = "default", + functionSource, + functionName, + objectKeys, + } = options; + + const convertedComponentSource = getConvertedPath({ + type: functionSourceType, + currentPath: file.path, + targetPath: functionSource, + }); + + let convertedComponentName: null | string = null; + + jscodeshift(sourceCode) + .find(jscodeshift.ImportDeclaration) + .filter((node) => node.value.source.value === convertedComponentSource) + .forEach((node) => { + node.value.specifiers?.forEach((specifier) => { + if ( + specifier.type === "ImportDefaultSpecifier" && + functionNameType === "default" + ) { + return (convertedComponentName = + specifier.local?.type === "Identifier" + ? specifier.local.name + : functionName); + } + + if ( + specifier.type === "ImportSpecifier" && + functionNameType === "named" && + specifier.imported.type === "Identifier" && + specifier.imported.name === functionName + ) { + return (convertedComponentName = + specifier.local?.type === "Identifier" + ? specifier.local.name + : functionName); + } + + if ( + specifier.type === "ImportNamespaceSpecifier" && + functionNameType === "default" + ) { + return (convertedComponentName = + specifier.local?.type === "Identifier" + ? specifier.local.name + : functionName); + } + + if ( + specifier.type === "ImportNamespaceSpecifier" && + functionNameType === "named" + ) { + return (convertedComponentName = functionName); + } + }); + }); + + if (!convertedComponentName) { + return sourceCode; + } + + console.log(convertedComponentName); + + return jscodeshift(sourceCode) + .find(jscodeshift.CallExpression) + .filter((node) => { + return ( + node.value.callee.type === "Identifier" && + node.value.callee.name === convertedComponentName && + node.scope.isGlobal + ); + }) + .forEach((node) => { + console.log(node.value.arguments); + node.value.arguments = [ + jscodeshift.objectExpression( + node.value.arguments + .filter((argument) => argument.type !== "SpreadElement") + .map((argument, index) => { + return jscodeshift.objectProperty( + jscodeshift.literal(objectKeys[index]), + argument + ); + }) + .filter(Boolean) + ), + ]; + }) + .toSource(); +} + +export default transformer; diff --git a/website/transformers/component/_category_.json b/website/transformers/component/_category_.json index 640bf58..9517fec 100644 --- a/website/transformers/component/_category_.json +++ b/website/transformers/component/_category_.json @@ -1,5 +1,5 @@ { - "label": "Components", + "label": "Component", "position": 3, "link": { "type": "generated-index" diff --git a/website/transformers/function/_category_.json b/website/transformers/function/_category_.json new file mode 100644 index 0000000..9f4bfc7 --- /dev/null +++ b/website/transformers/function/_category_.json @@ -0,0 +1,7 @@ +{ + "label": "Function", + "position": 4, + "link": { + "type": "generated-index" + } +} diff --git a/website/transformers/function/change-parameter-object.md b/website/transformers/function/change-parameter-object.md new file mode 100644 index 0000000..e412e45 --- /dev/null +++ b/website/transformers/function/change-parameter-object.md @@ -0,0 +1,124 @@ +--- +sidebar_position: 1 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import CodeBlock from "@theme/CodeBlock"; + +# Change Parameter Object + +Modify the function argument to take an object. + + + + + {`import { dummyFunction } from "foo"; +dummyFunction("hello", "this is dashboard page");`} + + + + + + {`import { dummyFunction } from "foo"; + +dummyFunction({ +"title": "hello", +"description": "this is dashboard page" +});`} + + + + + +## Options + +```typescript +type Options = { + functionSourceType?: "absolute" | "relative"; + functionNameType?: "default" | "named"; + functionSource: string; + functionName: string; + objectKeys: string[]; +}; +``` + +- functionSourceType: componentSource's type. you can choose absolute or relative +- functionNameType: changed target specifier +- functionSource: component source. you can choose package name or target path +- functionName: props to add's component name +- objectKeys: add props's name + +## Usage + +The usage method differs depending on the source type of the import statement to be converted. + +### absolute + +This is a situation where the source of the import module you want to change is an absolute path. + +```typescript title="option.ts" +const option = { + functionSourceType: "absolute", + functionNameType: "named", + functionSource: "foo", + functionName: "dummyFunction", + objectKeys: ["title", "description"], +}; +``` + + + + + {`import { dummyFunction } from "foo"; +dummyFunction("hello", "this is dashboard page");`} + + + + + + {`import { dummyFunction } from "foo"; + +dummyFunction({ +"title": "hello", +"description": "this is dashboard page" +});`} + + + + + +### relative + +This is a situation where the source of the import module you want to change is an absolute path. + +```typescript title="option.ts" +const option = { + functionSourceType: "relative", + functionNameType: "named", + functionSource: path.join(process.cwd(), "../test/foo.ts"),, + functionName: "dummyFunction", + objectKeys: ["title", "description"], +}; +``` + + + + + {`import { dummyFunction } from "../test/foo.ts"; +dummyFunction("hello", "this is dashboard page");`} + + + + + + {`import { dummyFunction } from "../test/foo.ts"; + +dummyFunction({ +"title": "hello", +"description": "this is dashboard page" +});`} + + + +