From f4f74e223f1162450a5f4822a31ef0786ac228e5 Mon Sep 17 00:00:00 2001 From: Andrew Bastin Date: Thu, 9 Sep 2021 01:03:46 +0530 Subject: [PATCH] refactor: a volar types shim generator --- .gitignore | 3 + modules/emit-volar-types.ts | 129 ++++++++++++++++++++++++++++++++++++ nuxt.config.js | 1 + 3 files changed, 133 insertions(+) create mode 100644 modules/emit-volar-types.ts diff --git a/.gitignore b/.gitignore index 9d566a677..211118bff 100644 --- a/.gitignore +++ b/.gitignore @@ -104,3 +104,6 @@ tests/*/screenshots # Tests videos tests/*/videos + +# Andrew's crazy Volar shim generator +shims-volar.d.ts diff --git a/modules/emit-volar-types.ts b/modules/emit-volar-types.ts new file mode 100644 index 000000000..bd130d3ec --- /dev/null +++ b/modules/emit-volar-types.ts @@ -0,0 +1,129 @@ +import { resolve } from "path" +import { Module } from "@nuxt/types" +import ts from "typescript" + +const { readdir, writeFile } = require("fs").promises + +function titleCase(str: string): string { + return str[0].toUpperCase() + str.substring(1) +} + +async function* getFilesInDir(dir: string): AsyncIterable { + const dirents = await readdir(dir, { withFileTypes: true }) + for (const dirent of dirents) { + const res = resolve(dir, dirent.name) + if (dirent.isDirectory()) { + yield* getFilesInDir(res) + } else { + yield res + } + } +} + +async function getAllVueComponentPaths(): Promise { + const vueFilePaths: string[] = [] + + for await (const f of getFilesInDir("./components")) { + if (f.endsWith(".vue")) { + const componentsIndex = f.split("/").indexOf("components") + + vueFilePaths.push( + `./${f + .split("/") + .slice(componentsIndex + 1) + .join("/")}` + ) + } + } + + return vueFilePaths +} + +function resolveComponentName(filename: string): string { + const index = filename.split("/").indexOf("components") + + return filename + .split("/") + .slice(index + 1) + .map((x) => x.split(".vue")[0]) // Remove extension + .filter((x) => x.toUpperCase() !== x.toLowerCase()) + .map((x) => titleCase(x)) // titlecase it + .join("") +} + +function createTSImports(components: [string, string][]) { + return components.map(([componentName, componentPath]) => { + return ts.factory.createImportDeclaration( + undefined, + undefined, + ts.factory.createImportClause( + false, + ts.factory.createIdentifier(componentName), + undefined + ), + ts.factory.createStringLiteral(componentPath) + ) + }) +} + +function createTSProps(components: [string, string][]) { + return components.map(([componentName]) => { + return ts.factory.createPropertySignature( + undefined, + ts.factory.createIdentifier(componentName), + undefined, + ts.factory.createTypeQueryNode(ts.factory.createIdentifier(componentName)) + ) + }) +} + +function generateTypeScriptDef(components: [string, string][]) { + const statements = [ + ...createTSImports(components), + ts.factory.createModuleDeclaration( + undefined, + [ts.factory.createModifier(ts.SyntaxKind.DeclareKeyword)], + ts.factory.createIdentifier("global"), + ts.factory.createModuleBlock([ + ts.factory.createInterfaceDeclaration( + undefined, + undefined, + ts.factory.createIdentifier("__VLS_GlobalComponents"), + undefined, + undefined, + [...createTSProps(components)] + ), + ]), + ts.NodeFlags.ExportContext | + ts.NodeFlags.GlobalAugmentation | + ts.NodeFlags.ContextFlags + ), + ] + + const source = ts.factory.createSourceFile( + statements, + ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), + ts.NodeFlags.None + ) + + const printer = ts.createPrinter({ + newLine: ts.NewLineKind.LineFeed, + }) + + return printer.printFile(source) +} + +const module: Module<{}> = async function () { + if (!this.nuxt.options.dev) return + + const results = await getAllVueComponentPaths() + const fileComponentNameCombo: [string, string][] = results.map((x) => [ + resolveComponentName(x), + x, + ]) + const typescriptString = generateTypeScriptDef(fileComponentNameCombo) + + await writeFile(resolve("shims-volar.d.ts"), typescriptString) +} + +export default module diff --git a/nuxt.config.js b/nuxt.config.js index b77f39f75..c053be3e0 100644 --- a/nuxt.config.js +++ b/nuxt.config.js @@ -133,6 +133,7 @@ export default { "@nuxtjs/composition-api/module", // https://github.com/antfu/unplugin-vue2-script-setup "unplugin-vue2-script-setup/nuxt", + "~/modules/emit-volar-types.ts", ], // Modules (https://go.nuxtjs.dev/config-modules)