Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(vue-script-setup-converter): Remove defineComponent from import declaration #53

Merged
29 changes: 16 additions & 13 deletions packages/vue-script-setup-converter/src/lib/convertSrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -45,6 +43,7 @@ export const convertSrc = (input: string) => {
throw new Error("defineComponent is not found.");
}

const defineComponentImport = convertDefineComponentImport(sourceFile) ?? "";
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking here, convertDefineComponentImport looks good. I prefer converterImportDeclaration to defineComponentImport.

const pageMeta = convertPageMeta(callexpression, lang) ?? "";
const props = convertProps(callexpression, lang) ?? "";
const emits = convertEmits(callexpression, lang) ?? "";
Expand All @@ -55,20 +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")) {
removeNamedImportIdentifier(x, "defineComponent");
}
if (hasNamedImportIdentifier(x, "defineNuxtComponent")) {
removeNamedImportIdentifier(x, "defineNuxtComponent");
}
}
return x.getText();
})
);

statements.addStatements(defineComponentImport);

if (isDefineNuxtComponent(callexpression)) {
statements.addStatements(pageMeta);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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 = `<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
name: 'HelloWorld',
})
</script>`;

it("returns import declaration text removed defineComponent", () => {
const output = parseScript(source);
const expected = 'import { ref } from "vue";\n';

expect(output).toBe(expected);
});
});

describe("when only defineComponent is imported", () => {
const source = `<script>
import { defineComponent } from 'vue';

export default defineComponent({
name: 'HelloWorld',
})
</script>`;

it("returns blank", () => {
const output = parseScript(source);
const expected = "";

expect(output).toBe(expected);
});
});

describe("when defineNuxtComponent is imported", () => {
const source = `<script>
import { defineNuxtComponent, ref } from '#imports';

export default defineNuxtComponent({
name: 'HelloWorld',
})
</script>`;

it("returns import declaration text removed defineNuxtComponent", () => {
const output = parseScript(source);
const expected = 'import { ref } from "#imports";\n';

expect(output).toBe(expected);
});
});

describe("when only defineNuxtComponent is imported", () => {
const source = `<script>
import { defineNuxtComponent } from '#imports';

export default defineNuxtComponent({
name: 'HelloWorld',
})
</script>`;

it("returns blank", () => {
const output = parseScript(source);
const expected = "";

expect(output).toBe(expected);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { ImportDeclaration, SourceFile } from "ts-morph";
import { hasNamedImportIdentifier } from "../helpers/module";

export const convertDefineComponentImport = (sourceFile: SourceFile) => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it is a convert to defineComponent.

Suggested change
export const convertDefineComponentImport = (sourceFile: SourceFile) => {
export const convertImportDeclaration = (sourceFile: SourceFile) => {

let importDeclarationText = "";

sourceFile.getImportDeclarations().forEach((importDeclaration) => {
if (hasNamedImportIdentifier(importDeclaration, "defineComponent")) {
importDeclarationText = convertToImportDeclarationText(
importDeclaration,
"defineComponent"
);
}
if (hasNamedImportIdentifier(importDeclaration, "defineNuxtComponent")) {
importDeclarationText = convertToImportDeclarationText(
importDeclaration,
"defineNuxtComponent"
);
}
});

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()}';`;
};
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -53,38 +50,4 @@ describe("helpers/module", () => {
});
});
});

describe("removeNamedImportIdentifier", () => {
describe("when importDeclaration includes target namedImport", () => {
const source = `<script>import { defineComponent, ref } from 'vue';</script>`;

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 = `<script>import { ref } from 'vue';</script>`;

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';");
});
});
});
});
12 changes: 0 additions & 12 deletions packages/vue-script-setup-converter/src/lib/helpers/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,3 @@ 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();
}
});
};
Loading