From cd39faff2e35a7d7ec06cc631deb799ddf019991 Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 02:20:59 +0900 Subject: [PATCH 01/13] hasNamedImportIdentifier --- .../src/lib/helper.ts | 1 + .../src/lib/helpers/module.test.ts | 53 +++++++++++++++++++ .../src/lib/helpers/module.ts | 12 +++++ 3 files changed, 66 insertions(+) create mode 100644 packages/vue-script-setup-converter/src/lib/helpers/module.test.ts create mode 100644 packages/vue-script-setup-converter/src/lib/helpers/module.ts diff --git a/packages/vue-script-setup-converter/src/lib/helper.ts b/packages/vue-script-setup-converter/src/lib/helper.ts index 87994c0..7fc9c95 100644 --- a/packages/vue-script-setup-converter/src/lib/helper.ts +++ b/packages/vue-script-setup-converter/src/lib/helper.ts @@ -1,3 +1,4 @@ +// TODO: Move to helpers/node.ts import { SyntaxKind, Node, PropertyAssignment, CallExpression } from "ts-morph"; export const getNodeByKind = ( diff --git a/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts b/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts new file mode 100644 index 0000000..990952b --- /dev/null +++ b/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts @@ -0,0 +1,53 @@ +import { expect, describe, it } from "vitest"; +import { ScriptTarget, Project, ImportDeclaration } from "ts-morph"; +import { parse } from "@vue/compiler-sfc"; +import { hasNamedImportIdentifier } from "./module"; + +const getSourceFile = (input: string, lang: "js" | "ts" = "js") => { + const { + descriptor: { script }, + } = parse(input); + + const project = new Project({ + tsConfigFilePath: "tsconfig.json", + compilerOptions: { + target: ScriptTarget.Latest, + }, + }); + + return project.createSourceFile("s.tsx", script?.content ?? ""); +}; + +describe("helpers/module", () => { + describe("hasNamedImportIdentifier", () => { + describe("when importDeclaration includes target namedImport", () => { + const source = ``; + + it("returns true", () => { + const sourceFile = getSourceFile(source); + const importDeclaration = sourceFile.getImportDeclaration("vue"); + const result = hasNamedImportIdentifier( + importDeclaration as ImportDeclaration, + "defineComponent" + ); + + expect(result).toBe(true); + }); + }); + + describe("when importDeclaration does not include target namedImport", () => { + const source = ``; + + it("returns true", () => { + const sourceFile = getSourceFile(source); + const importDeclaration = sourceFile.getImportDeclaration("vue"); + const result = hasNamedImportIdentifier( + importDeclaration as ImportDeclaration, + "defineComponent" + ); + + expect(result).toBe(false); + }); + }); + }); +}); diff --git a/packages/vue-script-setup-converter/src/lib/helpers/module.ts b/packages/vue-script-setup-converter/src/lib/helpers/module.ts new file mode 100644 index 0000000..46d110f --- /dev/null +++ b/packages/vue-script-setup-converter/src/lib/helpers/module.ts @@ -0,0 +1,12 @@ +import type { ImportDeclaration } from "ts-morph"; + +export const hasNamedImportIdentifier = ( + importDeclaration: ImportDeclaration, + identifier: string +): boolean => { + return Boolean( + importDeclaration.getNamedImports().find((namedImport) => { + return namedImport.getName() === identifier; + }) + ); +}; From de32c29e27551f18f6cfdfc35e2b714b82a84add Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 16:55:11 +0900 Subject: [PATCH 02/13] removeNamedImportIdentifier --- .../src/lib/helpers/module.test.ts | 39 ++++++++++++++++++- .../src/lib/helpers/module.ts | 12 ++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts b/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts index 990952b..13eac5b 100644 --- a/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts +++ b/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts @@ -1,7 +1,10 @@ import { expect, describe, it } from "vitest"; import { ScriptTarget, Project, ImportDeclaration } from "ts-morph"; import { parse } from "@vue/compiler-sfc"; -import { hasNamedImportIdentifier } from "./module"; +import { + hasNamedImportIdentifier, + removeNamedImportIdentifier, +} from "./module"; const getSourceFile = (input: string, lang: "js" | "ts" = "js") => { const { @@ -50,4 +53,38 @@ describe("helpers/module", () => { }); }); }); + + describe("removeNamedImportIdentifier", () => { + describe("when importDeclaration includes target namedImport", () => { + const source = ``; + + it("removes namedImport from importDeclaration", () => { + const sourceFile = getSourceFile(source); + const importDeclaration = sourceFile.getImportDeclaration("vue"); + + if (!importDeclaration) + throw new Error("importDeclaration is not found."); + + removeNamedImportIdentifier(importDeclaration, "defineComponent"); + + expect(importDeclaration.getText()).toBe("import { ref } from 'vue';"); + }); + }); + + describe("when importDeclaration does not include target namedImport", () => { + const source = ``; + + it("makes no change to importDeclaration", () => { + const sourceFile = getSourceFile(source); + const importDeclaration = sourceFile.getImportDeclaration("vue"); + + if (!importDeclaration) + throw new Error("importDeclaration is not found."); + + removeNamedImportIdentifier(importDeclaration, "defineComponent"); + + expect(importDeclaration.getText()).toBe("import { ref } from 'vue';"); + }); + }); + }); }); diff --git a/packages/vue-script-setup-converter/src/lib/helpers/module.ts b/packages/vue-script-setup-converter/src/lib/helpers/module.ts index 46d110f..cbf96e3 100644 --- a/packages/vue-script-setup-converter/src/lib/helpers/module.ts +++ b/packages/vue-script-setup-converter/src/lib/helpers/module.ts @@ -10,3 +10,15 @@ export const hasNamedImportIdentifier = ( }) ); }; + +// NOTE: This function makes a side effect +export const removeNamedImportIdentifier = ( + importDeclaration: ImportDeclaration, + identifier: string +): void => { + importDeclaration.getNamedImports().forEach((namedImport) => { + if (namedImport.getName() === identifier) { + namedImport.remove(); + } + }); +}; From 05833b21c4510456d587cf84ccc74a52cf765233 Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 17:01:03 +0900 Subject: [PATCH 03/13] feat(vue-script-setup-converter): Remove defineComponent from import declaration --- .../src/lib/__snapshots__/convertSrc.test.ts.snap | 4 ++-- .../src/lib/convertSrc.test.ts | 4 ++-- .../src/lib/convertSrc.ts | 15 ++++++++++++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap b/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap index 9acbf06..cbced71 100644 --- a/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap +++ b/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap @@ -15,7 +15,7 @@ const onSubmit = () => { `; exports[`snapshot > lang=js 1`] = ` -"import { defineComponent, toRefs, computed, ref } from 'vue'; +"import { toRefs, computed, ref } from 'vue'; const props = defineProps({ msg: { type: String, @@ -35,7 +35,7 @@ const count = ref(0); `; exports[`snapshot > lang=ts 1`] = ` -"import { defineComponent, toRefs, computed, ref } from 'vue'; +"import { toRefs, computed, ref } from 'vue'; type Props = { msg?: string; foo: string; diff --git a/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts b/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts index 31b90b0..4ebb259 100644 --- a/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts +++ b/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts @@ -122,7 +122,7 @@ export default defineComponent({ `); expect(output).toMatchInlineSnapshot( ` - "import { defineComponent, toRefs, computed } from 'vue'; + "import { toRefs, computed } from 'vue'; type Props = { msg?: string; }; const props = withDefaults(defineProps(), { msg: 'HelloWorld' }); const { msg } = toRefs(props); @@ -157,7 +157,7 @@ export default defineComponent({ expect(output).toMatchInlineSnapshot( ` "import type { PropType } from 'vue'; - import { defineComponent, computed } from 'vue'; + import { computed } from 'vue'; type Props = { msg?: string; }; const props = withDefaults(defineProps(), { msg: 'HelloWorld' }); const newMsg = computed(() => props.msg + '- HelloWorld'); diff --git a/packages/vue-script-setup-converter/src/lib/convertSrc.ts b/packages/vue-script-setup-converter/src/lib/convertSrc.ts index 8b9b735..1e98846 100644 --- a/packages/vue-script-setup-converter/src/lib/convertSrc.ts +++ b/packages/vue-script-setup-converter/src/lib/convertSrc.ts @@ -8,6 +8,10 @@ import { } from "ts-morph"; import { parse } from "@vue/compiler-sfc"; import { getNodeByKind } from "./helper"; +import { + hasNamedImportIdentifier, + removeNamedImportIdentifier, +} from "./helpers/module"; import { convertPageMeta } from "./converter/pageMetaConverter"; import { convertProps } from "./converter/propsConverter"; import { convertSetup } from "./converter/setupConverter"; @@ -52,7 +56,16 @@ export const convertSrc = (input: string) => { sourceFile .getStatements() .filter((state) => !Node.isExportAssignment(state)) - .map((x) => x.getText()) + .map((x) => { + if ( + x.isKind(SyntaxKind.ImportDeclaration) && + hasNamedImportIdentifier(x, "defineComponent") + ) { + removeNamedImportIdentifier(x, "defineComponent"); + return x.getText(); + } + return x.getText(); + }) ); if (isDefineNuxtComponent(callexpression)) { From 968989c49a696a1fce3030b34ea810a7a2b4e3a3 Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 17:09:09 +0900 Subject: [PATCH 04/13] Support defineNuxtComponent --- .../src/lib/__snapshots__/convertSrc.test.ts.snap | 2 +- .../src/lib/convertSrc.ts | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap b/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap index cbced71..ee75511 100644 --- a/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap +++ b/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`snapshot > defineNuxtComponent 1`] = ` -"import { defineNuxtComponent, useNuxtApp } from '#imports'; +"import { useNuxtApp } from '#imports'; definePageMeta({ name: 'HelloWorld', layout: 'test-layout', middleware: 'test-middleware' }); diff --git a/packages/vue-script-setup-converter/src/lib/convertSrc.ts b/packages/vue-script-setup-converter/src/lib/convertSrc.ts index 1e98846..d41319e 100644 --- a/packages/vue-script-setup-converter/src/lib/convertSrc.ts +++ b/packages/vue-script-setup-converter/src/lib/convertSrc.ts @@ -57,12 +57,13 @@ export const convertSrc = (input: string) => { .getStatements() .filter((state) => !Node.isExportAssignment(state)) .map((x) => { - if ( - x.isKind(SyntaxKind.ImportDeclaration) && - hasNamedImportIdentifier(x, "defineComponent") - ) { - removeNamedImportIdentifier(x, "defineComponent"); - return x.getText(); + if (x.isKind(SyntaxKind.ImportDeclaration)) { + if (hasNamedImportIdentifier(x, "defineComponent")) { + removeNamedImportIdentifier(x, "defineComponent"); + } + if (hasNamedImportIdentifier(x, "defineNuxtComponent")) { + removeNamedImportIdentifier(x, "defineNuxtComponent"); + } } return x.getText(); }) From f0a5ca605d3528b7b13dc2c24ef3efb66f357b49 Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 20:11:29 +0900 Subject: [PATCH 05/13] Do not make side effects --- .../lib/__snapshots__/convertSrc.test.ts.snap | 6 ++-- .../src/lib/convertSrc.test.ts | 4 +-- .../src/lib/convertSrc.ts | 10 ++++--- .../src/lib/helpers/module.test.ts | 30 +++++++++++++++---- .../src/lib/helpers/module.ts | 19 ++++++++++-- 5 files changed, 52 insertions(+), 17 deletions(-) diff --git a/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap b/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap index ee75511..5b90de0 100644 --- a/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap +++ b/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`snapshot > defineNuxtComponent 1`] = ` -"import { useNuxtApp } from '#imports'; +"import { useNuxtApp } from "#imports"; definePageMeta({ name: 'HelloWorld', layout: 'test-layout', middleware: 'test-middleware' }); @@ -15,7 +15,7 @@ const onSubmit = () => { `; exports[`snapshot > lang=js 1`] = ` -"import { toRefs, computed, ref } from 'vue'; +"import { toRefs, computed, ref } from "vue"; const props = defineProps({ msg: { type: String, @@ -35,7 +35,7 @@ const count = ref(0); `; exports[`snapshot > lang=ts 1`] = ` -"import { toRefs, computed, ref } from 'vue'; +"import { toRefs, computed, ref } from "vue"; type Props = { msg?: string; foo: string; diff --git a/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts b/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts index 4ebb259..2dea472 100644 --- a/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts +++ b/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts @@ -122,7 +122,7 @@ export default defineComponent({ `); expect(output).toMatchInlineSnapshot( ` - "import { toRefs, computed } from 'vue'; + "import { toRefs, computed } from "vue"; type Props = { msg?: string; }; const props = withDefaults(defineProps(), { msg: 'HelloWorld' }); const { msg } = toRefs(props); @@ -157,7 +157,7 @@ export default defineComponent({ expect(output).toMatchInlineSnapshot( ` "import type { PropType } from 'vue'; - import { computed } from 'vue'; + import { computed } from "vue"; type Props = { msg?: string; }; const props = withDefaults(defineProps(), { msg: 'HelloWorld' }); const newMsg = computed(() => props.msg + '- HelloWorld'); diff --git a/packages/vue-script-setup-converter/src/lib/convertSrc.ts b/packages/vue-script-setup-converter/src/lib/convertSrc.ts index d41319e..491f3fa 100644 --- a/packages/vue-script-setup-converter/src/lib/convertSrc.ts +++ b/packages/vue-script-setup-converter/src/lib/convertSrc.ts @@ -59,10 +59,12 @@ export const convertSrc = (input: string) => { .map((x) => { if (x.isKind(SyntaxKind.ImportDeclaration)) { if (hasNamedImportIdentifier(x, "defineComponent")) { - removeNamedImportIdentifier(x, "defineComponent"); - } - if (hasNamedImportIdentifier(x, "defineNuxtComponent")) { - removeNamedImportIdentifier(x, "defineNuxtComponent"); + return removeNamedImportIdentifier(x, "defineComponent").getText(); + } else if (hasNamedImportIdentifier(x, "defineNuxtComponent")) { + return removeNamedImportIdentifier( + x, + "defineNuxtComponent" + ).getText(); } } return x.getText(); diff --git a/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts b/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts index 13eac5b..e409226 100644 --- a/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts +++ b/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts @@ -56,23 +56,40 @@ describe("helpers/module", () => { describe("removeNamedImportIdentifier", () => { describe("when importDeclaration includes target namedImport", () => { - const source = ``; + const source = ``; it("removes namedImport from importDeclaration", () => { const sourceFile = getSourceFile(source); const importDeclaration = sourceFile.getImportDeclaration("vue"); + if (!importDeclaration) + throw new Error("importDeclaration is not found."); + + const result = removeNamedImportIdentifier( + importDeclaration, + "defineComponent" + ); + + expect(result.getText()).toBe('import { ref } from "vue";'); + }); + + it("makes no change to original importDeclaration", () => { + const sourceFile = getSourceFile(source); + const importDeclaration = sourceFile.getImportDeclaration("vue"); + if (!importDeclaration) throw new Error("importDeclaration is not found."); removeNamedImportIdentifier(importDeclaration, "defineComponent"); - expect(importDeclaration.getText()).toBe("import { ref } from 'vue';"); + expect(importDeclaration.getText()).toBe( + 'import { defineComponent, ref } from "vue";' + ); }); }); describe("when importDeclaration does not include target namedImport", () => { - const source = ``; + const source = ``; it("makes no change to importDeclaration", () => { const sourceFile = getSourceFile(source); @@ -81,9 +98,12 @@ describe("helpers/module", () => { if (!importDeclaration) throw new Error("importDeclaration is not found."); - removeNamedImportIdentifier(importDeclaration, "defineComponent"); + const result = removeNamedImportIdentifier( + importDeclaration, + "defineComponent" + ); - expect(importDeclaration.getText()).toBe("import { ref } from 'vue';"); + expect(result.getText()).toBe('import { ref } from "vue";'); }); }); }); diff --git a/packages/vue-script-setup-converter/src/lib/helpers/module.ts b/packages/vue-script-setup-converter/src/lib/helpers/module.ts index cbf96e3..4b66e03 100644 --- a/packages/vue-script-setup-converter/src/lib/helpers/module.ts +++ b/packages/vue-script-setup-converter/src/lib/helpers/module.ts @@ -11,14 +11,27 @@ export const hasNamedImportIdentifier = ( ); }; -// NOTE: This function makes a side effect export const removeNamedImportIdentifier = ( importDeclaration: ImportDeclaration, identifier: string -): void => { - importDeclaration.getNamedImports().forEach((namedImport) => { +): ImportDeclaration => { + if (!hasNamedImportIdentifier(importDeclaration, identifier)) { + return importDeclaration; + } + + const sourceFile = importDeclaration.getSourceFile(); + const newImportDeclaration = sourceFile.addImportDeclaration({ + moduleSpecifier: importDeclaration.getModuleSpecifierValue(), + namedImports: importDeclaration + .getNamedImports() + .map((namedImport) => namedImport.getText()), + }); + + newImportDeclaration.getNamedImports().forEach((namedImport) => { if (namedImport.getName() === identifier) { namedImport.remove(); } }); + + return newImportDeclaration; }; From 539a3de9bd4100726da8f8e80449f9e7a9c9ae5f Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 21:01:59 +0900 Subject: [PATCH 06/13] convertDefineComponentImport --- .../defineComponentImportConverter.test.ts | 66 +++++++++++++++++++ .../defineComponentImportConverter.ts | 26 ++++++++ 2 files changed, 92 insertions(+) create mode 100644 packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts create mode 100644 packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts diff --git a/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts b/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts new file mode 100644 index 0000000..ef3fd8c --- /dev/null +++ b/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts @@ -0,0 +1,66 @@ +import { expect, describe, it } from "vitest"; +import { ScriptTarget, Project } from "ts-morph"; +import { parse } from "@vue/compiler-sfc"; +import prettier from "prettier"; +import parserTypeScript from "prettier/parser-typescript"; +import { convertDefineComponentImport } from "./defineComponentImportConverter"; + +const parseScript = (input: string, lang: "js" | "ts" = "js") => { + const { + descriptor: { script }, + } = parse(input); + + const project = new Project({ + tsConfigFilePath: "tsconfig.json", + compilerOptions: { + target: ScriptTarget.Latest, + }, + }); + + const sourceFile = project.createSourceFile("s.tsx", script?.content ?? ""); + const convertedImportDeclarationText = + convertDefineComponentImport(sourceFile); + + const formatedText = prettier.format(convertedImportDeclarationText, { + parser: "typescript", + plugins: [parserTypeScript], + }); + + return formatedText; +}; + +describe("convertDefineComponentImport", () => { + describe("when defineComponent is imported", () => { + const source = ``; + + it("returns import declaration text removed defineComponent", () => { + const output = parseScript(source); + const expected = 'import { ref } from "vue";\n'; + + expect(output).toBe(expected); + }); + }); + + describe("when defineNuxtComponent is imported", () => { + const source = ``; + + it("returns import declaration text removed defineNuxtComponent", () => { + const output = parseScript(source); + const expected = 'import { ref } from "#imports";\n'; + + expect(output).toBe(expected); + }); + }); +}); diff --git a/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts b/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts new file mode 100644 index 0000000..4ecf954 --- /dev/null +++ b/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts @@ -0,0 +1,26 @@ +import type { SourceFile } from "ts-morph"; +import { + hasNamedImportIdentifier, + removeNamedImportIdentifier, +} from "../helpers/module"; + +export const convertDefineComponentImport = (sourceFile: SourceFile) => { + let importDeclarationText = ""; + + sourceFile.getImportDeclarations().forEach((importDeclaration) => { + if (hasNamedImportIdentifier(importDeclaration, "defineComponent")) { + importDeclarationText = removeNamedImportIdentifier( + importDeclaration, + "defineComponent" + ).getText(); + } + if (hasNamedImportIdentifier(importDeclaration, "defineNuxtComponent")) { + importDeclarationText = removeNamedImportIdentifier( + importDeclaration, + "defineNuxtComponent" + ).getText(); + } + }); + + return importDeclarationText; +}; From cea4082c3263f99326f8ea715e336013cd060f60 Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 21:16:49 +0900 Subject: [PATCH 07/13] Use convertDefineComponentImport in convertSrc --- .../src/lib/convertSrc.ts | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/vue-script-setup-converter/src/lib/convertSrc.ts b/packages/vue-script-setup-converter/src/lib/convertSrc.ts index 491f3fa..cd6580b 100644 --- a/packages/vue-script-setup-converter/src/lib/convertSrc.ts +++ b/packages/vue-script-setup-converter/src/lib/convertSrc.ts @@ -8,10 +8,8 @@ import { } from "ts-morph"; import { parse } from "@vue/compiler-sfc"; import { getNodeByKind } from "./helper"; -import { - hasNamedImportIdentifier, - removeNamedImportIdentifier, -} from "./helpers/module"; +import { hasNamedImportIdentifier } from "./helpers/module"; +import { convertDefineComponentImport } from "./converter/defineComponentImportConverter"; import { convertPageMeta } from "./converter/pageMetaConverter"; import { convertProps } from "./converter/propsConverter"; import { convertSetup } from "./converter/setupConverter"; @@ -45,6 +43,7 @@ export const convertSrc = (input: string) => { throw new Error("defineComponent is not found."); } + const defineComponentImport = convertDefineComponentImport(sourceFile) ?? ""; const pageMeta = convertPageMeta(callexpression, lang) ?? ""; const props = convertProps(callexpression, lang) ?? ""; const emits = convertEmits(callexpression, lang) ?? ""; @@ -55,22 +54,24 @@ export const convertSrc = (input: string) => { statements.addStatements( sourceFile .getStatements() - .filter((state) => !Node.isExportAssignment(state)) + .filter((state) => { + if (Node.isExportAssignment(state)) return false; + if ( + Node.isImportDeclaration(state) && + (hasNamedImportIdentifier(state, "defineComponent") || + hasNamedImportIdentifier(state, "defineNuxtComponent")) + ) + return false; + + return true; + }) .map((x) => { - if (x.isKind(SyntaxKind.ImportDeclaration)) { - if (hasNamedImportIdentifier(x, "defineComponent")) { - return removeNamedImportIdentifier(x, "defineComponent").getText(); - } else if (hasNamedImportIdentifier(x, "defineNuxtComponent")) { - return removeNamedImportIdentifier( - x, - "defineNuxtComponent" - ).getText(); - } - } return x.getText(); }) ); + statements.addStatements(defineComponentImport); + if (isDefineNuxtComponent(callexpression)) { statements.addStatements(pageMeta); } From 005dcbee47457e63fe5746337cee36a0c037ba7f Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 21:39:57 +0900 Subject: [PATCH 08/13] fix: Do not make side effects to sourceFile --- .../lib/__snapshots__/convertSrc.test.ts.snap | 6 +- .../src/lib/convertSrc.test.ts | 4 +- .../defineComponentImportConverter.ts | 31 +++++++--- .../src/lib/helpers/module.test.ts | 59 +------------------ .../src/lib/helpers/module.ts | 25 -------- 5 files changed, 28 insertions(+), 97 deletions(-) diff --git a/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap b/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap index 5b90de0..ee75511 100644 --- a/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap +++ b/packages/vue-script-setup-converter/src/lib/__snapshots__/convertSrc.test.ts.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`snapshot > defineNuxtComponent 1`] = ` -"import { useNuxtApp } from "#imports"; +"import { useNuxtApp } from '#imports'; definePageMeta({ name: 'HelloWorld', layout: 'test-layout', middleware: 'test-middleware' }); @@ -15,7 +15,7 @@ const onSubmit = () => { `; exports[`snapshot > lang=js 1`] = ` -"import { toRefs, computed, ref } from "vue"; +"import { toRefs, computed, ref } from 'vue'; const props = defineProps({ msg: { type: String, @@ -35,7 +35,7 @@ const count = ref(0); `; exports[`snapshot > lang=ts 1`] = ` -"import { toRefs, computed, ref } from "vue"; +"import { toRefs, computed, ref } from 'vue'; type Props = { msg?: string; foo: string; diff --git a/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts b/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts index 2dea472..4ebb259 100644 --- a/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts +++ b/packages/vue-script-setup-converter/src/lib/convertSrc.test.ts @@ -122,7 +122,7 @@ export default defineComponent({ `); expect(output).toMatchInlineSnapshot( ` - "import { toRefs, computed } from "vue"; + "import { toRefs, computed } from 'vue'; type Props = { msg?: string; }; const props = withDefaults(defineProps(), { msg: 'HelloWorld' }); const { msg } = toRefs(props); @@ -157,7 +157,7 @@ export default defineComponent({ expect(output).toMatchInlineSnapshot( ` "import type { PropType } from 'vue'; - import { computed } from "vue"; + import { computed } from 'vue'; type Props = { msg?: string; }; const props = withDefaults(defineProps(), { msg: 'HelloWorld' }); const newMsg = computed(() => props.msg + '- HelloWorld'); diff --git a/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts b/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts index 4ecf954..4573bd0 100644 --- a/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts +++ b/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts @@ -1,26 +1,39 @@ -import type { SourceFile } from "ts-morph"; -import { - hasNamedImportIdentifier, - removeNamedImportIdentifier, -} from "../helpers/module"; +import type { ImportDeclaration, SourceFile } from "ts-morph"; +import { hasNamedImportIdentifier } from "../helpers/module"; export const convertDefineComponentImport = (sourceFile: SourceFile) => { let importDeclarationText = ""; sourceFile.getImportDeclarations().forEach((importDeclaration) => { if (hasNamedImportIdentifier(importDeclaration, "defineComponent")) { - importDeclarationText = removeNamedImportIdentifier( + importDeclarationText = convertToImportDeclarationText( importDeclaration, "defineComponent" - ).getText(); + ); } if (hasNamedImportIdentifier(importDeclaration, "defineNuxtComponent")) { - importDeclarationText = removeNamedImportIdentifier( + importDeclarationText = convertToImportDeclarationText( importDeclaration, "defineNuxtComponent" - ).getText(); + ); } }); return importDeclarationText; }; + +const convertToImportDeclarationText = ( + importDeclaration: ImportDeclaration, + identifier: string +) => { + const filteredNamedImports = importDeclaration + .getNamedImports() + .map((namedImport) => namedImport.getText()) + .filter((text) => text !== identifier); + + if (filteredNamedImports.length === 0) return ""; + + return `import { ${filteredNamedImports.join( + "," + )} } from '${importDeclaration.getModuleSpecifierValue()}';`; +}; diff --git a/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts b/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts index e409226..990952b 100644 --- a/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts +++ b/packages/vue-script-setup-converter/src/lib/helpers/module.test.ts @@ -1,10 +1,7 @@ import { expect, describe, it } from "vitest"; import { ScriptTarget, Project, ImportDeclaration } from "ts-morph"; import { parse } from "@vue/compiler-sfc"; -import { - hasNamedImportIdentifier, - removeNamedImportIdentifier, -} from "./module"; +import { hasNamedImportIdentifier } from "./module"; const getSourceFile = (input: string, lang: "js" | "ts" = "js") => { const { @@ -53,58 +50,4 @@ describe("helpers/module", () => { }); }); }); - - describe("removeNamedImportIdentifier", () => { - describe("when importDeclaration includes target namedImport", () => { - const source = ``; - - it("removes namedImport from importDeclaration", () => { - const sourceFile = getSourceFile(source); - const importDeclaration = sourceFile.getImportDeclaration("vue"); - - if (!importDeclaration) - throw new Error("importDeclaration is not found."); - - const result = removeNamedImportIdentifier( - importDeclaration, - "defineComponent" - ); - - expect(result.getText()).toBe('import { ref } from "vue";'); - }); - - it("makes no change to original importDeclaration", () => { - const sourceFile = getSourceFile(source); - const importDeclaration = sourceFile.getImportDeclaration("vue"); - - if (!importDeclaration) - throw new Error("importDeclaration is not found."); - - removeNamedImportIdentifier(importDeclaration, "defineComponent"); - - expect(importDeclaration.getText()).toBe( - 'import { defineComponent, ref } from "vue";' - ); - }); - }); - - describe("when importDeclaration does not include target namedImport", () => { - const source = ``; - - it("makes no change to importDeclaration", () => { - const sourceFile = getSourceFile(source); - const importDeclaration = sourceFile.getImportDeclaration("vue"); - - if (!importDeclaration) - throw new Error("importDeclaration is not found."); - - const result = removeNamedImportIdentifier( - importDeclaration, - "defineComponent" - ); - - expect(result.getText()).toBe('import { ref } from "vue";'); - }); - }); - }); }); diff --git a/packages/vue-script-setup-converter/src/lib/helpers/module.ts b/packages/vue-script-setup-converter/src/lib/helpers/module.ts index 4b66e03..46d110f 100644 --- a/packages/vue-script-setup-converter/src/lib/helpers/module.ts +++ b/packages/vue-script-setup-converter/src/lib/helpers/module.ts @@ -10,28 +10,3 @@ export const hasNamedImportIdentifier = ( }) ); }; - -export const removeNamedImportIdentifier = ( - importDeclaration: ImportDeclaration, - identifier: string -): ImportDeclaration => { - if (!hasNamedImportIdentifier(importDeclaration, identifier)) { - return importDeclaration; - } - - const sourceFile = importDeclaration.getSourceFile(); - const newImportDeclaration = sourceFile.addImportDeclaration({ - moduleSpecifier: importDeclaration.getModuleSpecifierValue(), - namedImports: importDeclaration - .getNamedImports() - .map((namedImport) => namedImport.getText()), - }); - - newImportDeclaration.getNamedImports().forEach((namedImport) => { - if (namedImport.getName() === identifier) { - namedImport.remove(); - } - }); - - return newImportDeclaration; -}; From a28d604672bb60f7d4141ea5f52933a33c090af2 Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 21:44:17 +0900 Subject: [PATCH 09/13] test: Add tests to defineComponentImportConverter.test.ts --- .../defineComponentImportConverter.test.ts | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts b/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts index ef3fd8c..9d9afb1 100644 --- a/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts +++ b/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts @@ -47,6 +47,23 @@ describe("convertDefineComponentImport", () => { }); }); + describe("when only defineComponent is imported", () => { + const source = ``; + + it("returns blank", () => { + const output = parseScript(source); + const expected = ""; + + expect(output).toBe(expected); + }); + }); + describe("when defineNuxtComponent is imported", () => { const source = ``; + + it("returns blank", () => { + const output = parseScript(source); + const expected = ""; + + expect(output).toBe(expected); + }); + }); }); From 25d78059b5f176f7e2a02417b7da6c4ea84009ca Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 22:24:22 +0900 Subject: [PATCH 10/13] renamed: packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts -> packages/vue-script-setup-converter/src/lib/converter/importDeclarationConverter.ts --- packages/vue-script-setup-converter/src/lib/convertSrc.ts | 2 +- ...portConverter.test.ts => importDeclarationConverter.test.ts} | 2 +- ...omponentImportConverter.ts => importDeclarationConverter.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename packages/vue-script-setup-converter/src/lib/converter/{defineComponentImportConverter.test.ts => importDeclarationConverter.test.ts} (96%) rename packages/vue-script-setup-converter/src/lib/converter/{defineComponentImportConverter.ts => importDeclarationConverter.ts} (100%) diff --git a/packages/vue-script-setup-converter/src/lib/convertSrc.ts b/packages/vue-script-setup-converter/src/lib/convertSrc.ts index cd6580b..313d51b 100644 --- a/packages/vue-script-setup-converter/src/lib/convertSrc.ts +++ b/packages/vue-script-setup-converter/src/lib/convertSrc.ts @@ -9,7 +9,7 @@ import { import { parse } from "@vue/compiler-sfc"; import { getNodeByKind } from "./helper"; import { hasNamedImportIdentifier } from "./helpers/module"; -import { convertDefineComponentImport } from "./converter/defineComponentImportConverter"; +import { convertDefineComponentImport } from "./converter/importDeclarationConverter"; import { convertPageMeta } from "./converter/pageMetaConverter"; import { convertProps } from "./converter/propsConverter"; import { convertSetup } from "./converter/setupConverter"; diff --git a/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts b/packages/vue-script-setup-converter/src/lib/converter/importDeclarationConverter.test.ts similarity index 96% rename from packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts rename to packages/vue-script-setup-converter/src/lib/converter/importDeclarationConverter.test.ts index 9d9afb1..17d4c18 100644 --- a/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.test.ts +++ b/packages/vue-script-setup-converter/src/lib/converter/importDeclarationConverter.test.ts @@ -3,7 +3,7 @@ import { ScriptTarget, Project } from "ts-morph"; import { parse } from "@vue/compiler-sfc"; import prettier from "prettier"; import parserTypeScript from "prettier/parser-typescript"; -import { convertDefineComponentImport } from "./defineComponentImportConverter"; +import { convertDefineComponentImport } from "./importDeclarationConverter"; const parseScript = (input: string, lang: "js" | "ts" = "js") => { const { diff --git a/packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts b/packages/vue-script-setup-converter/src/lib/converter/importDeclarationConverter.ts similarity index 100% rename from packages/vue-script-setup-converter/src/lib/converter/defineComponentImportConverter.ts rename to packages/vue-script-setup-converter/src/lib/converter/importDeclarationConverter.ts From fe59bc939b96ac4fe916653497943bcff4fb0686 Mon Sep 17 00:00:00 2001 From: INOUE Takuya Date: Mon, 6 May 2024 22:27:08 +0900 Subject: [PATCH 11/13] Rename convertDefineComponentImport to convertImportDeclaration --- packages/vue-script-setup-converter/src/lib/convertSrc.ts | 6 +++--- .../src/lib/converter/importDeclarationConverter.test.ts | 7 +++---- .../src/lib/converter/importDeclarationConverter.ts | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/vue-script-setup-converter/src/lib/convertSrc.ts b/packages/vue-script-setup-converter/src/lib/convertSrc.ts index 313d51b..e098022 100644 --- a/packages/vue-script-setup-converter/src/lib/convertSrc.ts +++ b/packages/vue-script-setup-converter/src/lib/convertSrc.ts @@ -9,7 +9,7 @@ import { import { parse } from "@vue/compiler-sfc"; import { getNodeByKind } from "./helper"; import { hasNamedImportIdentifier } from "./helpers/module"; -import { convertDefineComponentImport } from "./converter/importDeclarationConverter"; +import { convertImportDeclaration } from "./converter/importDeclarationConverter"; import { convertPageMeta } from "./converter/pageMetaConverter"; import { convertProps } from "./converter/propsConverter"; import { convertSetup } from "./converter/setupConverter"; @@ -43,7 +43,7 @@ export const convertSrc = (input: string) => { throw new Error("defineComponent is not found."); } - const defineComponentImport = convertDefineComponentImport(sourceFile) ?? ""; + const importDeclaration = convertImportDeclaration(sourceFile) ?? ""; const pageMeta = convertPageMeta(callexpression, lang) ?? ""; const props = convertProps(callexpression, lang) ?? ""; const emits = convertEmits(callexpression, lang) ?? ""; @@ -70,7 +70,7 @@ export const convertSrc = (input: string) => { }) ); - statements.addStatements(defineComponentImport); + statements.addStatements(importDeclaration); if (isDefineNuxtComponent(callexpression)) { statements.addStatements(pageMeta); diff --git a/packages/vue-script-setup-converter/src/lib/converter/importDeclarationConverter.test.ts b/packages/vue-script-setup-converter/src/lib/converter/importDeclarationConverter.test.ts index 17d4c18..31ba535 100644 --- a/packages/vue-script-setup-converter/src/lib/converter/importDeclarationConverter.test.ts +++ b/packages/vue-script-setup-converter/src/lib/converter/importDeclarationConverter.test.ts @@ -3,7 +3,7 @@ import { ScriptTarget, Project } from "ts-morph"; import { parse } from "@vue/compiler-sfc"; import prettier from "prettier"; import parserTypeScript from "prettier/parser-typescript"; -import { convertDefineComponentImport } from "./importDeclarationConverter"; +import { convertImportDeclaration } from "./importDeclarationConverter"; const parseScript = (input: string, lang: "js" | "ts" = "js") => { const { @@ -18,8 +18,7 @@ const parseScript = (input: string, lang: "js" | "ts" = "js") => { }); const sourceFile = project.createSourceFile("s.tsx", script?.content ?? ""); - const convertedImportDeclarationText = - convertDefineComponentImport(sourceFile); + const convertedImportDeclarationText = convertImportDeclaration(sourceFile); const formatedText = prettier.format(convertedImportDeclarationText, { parser: "typescript", @@ -29,7 +28,7 @@ const parseScript = (input: string, lang: "js" | "ts" = "js") => { return formatedText; }; -describe("convertDefineComponentImport", () => { +describe("convertImportDeclaration", () => { describe("when defineComponent is imported", () => { const source = `