Skip to content

Commit

Permalink
feat: convert to defineAsyncComponent
Browse files Browse the repository at this point in the history
  • Loading branch information
wattanx committed May 10, 2024
1 parent 1ab6a17 commit 7815983
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 1 deletion.
4 changes: 4 additions & 0 deletions packages/vue-script-setup-converter/src/lib/convertSrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { convertPageMeta } from "./converter/pageMetaConverter";
import { convertProps } from "./converter/propsConverter";
import { convertSetup } from "./converter/setupConverter";
import { convertEmits } from "./converter/emitsConverter";
import { convertComponents } from "./converter/componentsConverter";

export const convertSrc = (input: string) => {
const {
Expand Down Expand Up @@ -48,6 +49,7 @@ export const convertSrc = (input: string) => {
const props = convertProps(callexpression, lang) ?? "";
const emits = convertEmits(callexpression, lang) ?? "";
const statement = convertSetup(callexpression) ?? "";
const components = convertComponents(callexpression) ?? "";

const statements = project.createSourceFile("new.tsx");

Expand Down Expand Up @@ -76,6 +78,8 @@ export const convertSrc = (input: string) => {
statements.addStatements(pageMeta);
}

statements.addStatements(components);

statements.addStatements(props);
statements.addStatements(emits);
statements.addStatements(statement);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { expect, test } from "vitest";
import type { CallExpression } from "ts-morph";
import { ScriptTarget, SyntaxKind, Project } from "ts-morph";
import { parse } from "@vue/compiler-sfc";
import { getNodeByKind } from "../helpers/node";
import { convertComponents } from "./componentsConverter";

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 callexpression = getNodeByKind(sourceFile, SyntaxKind.CallExpression);

const components = convertComponents(callexpression as CallExpression);

return components;
};

test("should be converted to defineAsyncComponent", () => {
const source = `<script>
import { defineComponent } from 'vue';
import HelloWorld from './HelloWorld.vue';
export default defineComponent({
components: {
HelloWorld,
MyComp: () => import('./MyComp.vue'),
Foo: () => import('./Foo.vue'),
}
})
`;
const output = parseScript(source);

expect(output).toMatchInlineSnapshot(
`
"const MyComp = defineAsyncComponent(() => import('./MyComp.vue'))
const Foo = defineAsyncComponent(() => import('./Foo.vue'))"
`
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type {
CallExpression,
ObjectLiteralElementLike,
PropertyAssignment,
} from "ts-morph";
import { Node, SyntaxKind } from "ts-morph";
import { getOptionsNode } from "../helpers/node";

export const convertComponents = (node: CallExpression) => {
const componentsNode = getOptionsNode(node, "components");

if (!componentsNode) {
return "";
}

return convertToDefineAsyncComponent(componentsNode);
};

const convertToDefineAsyncComponent = (node: PropertyAssignment) => {
const child = node.getInitializer();

if (!child) {
throw new Error("components is empty.");
}

if (!Node.isObjectLiteralExpression(child)) return "";

const properties = child.getProperties();

const filterdProperties = properties.filter(filterDynamicImport);

if (filterdProperties.length === 0) return "";

const value = filterdProperties
.map((x) => {
const propertyName = x.getName();
const arrowFunc = x.getFirstChildByKind(SyntaxKind.ArrowFunction);
return `const ${propertyName} = defineAsyncComponent(${arrowFunc!.getText()})`;
})
.join("\n");

return value;
};

// dynamicImportのあるPropertyAssignmentのみを抽出
const filterDynamicImport = (
property: Node
): property is PropertyAssignment => {
return Node.isPropertyAssignment(property) && hasDynamicImport(property);
};

const hasDynamicImport = (node: ObjectLiteralElementLike) => {
return node.getText().includes("() => import(");
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const getNodeByKind = (

export const getOptionsNode = (
node: CallExpression,
type: "name" | "layout" | "middleware" | "props" | "emits"
type: "name" | "layout" | "middleware" | "props" | "emits" | "components"
) => {
const expression = getNodeByKind(node, SyntaxKind.ObjectLiteralExpression);

Expand Down

0 comments on commit 7815983

Please sign in to comment.