refactor: a volar types shim generator

This commit is contained in:
Andrew Bastin
2021-09-09 01:03:46 +05:30
parent c3e881ed77
commit f4f74e223f
3 changed files with 133 additions and 0 deletions

3
.gitignore vendored
View File

@@ -104,3 +104,6 @@ tests/*/screenshots
# Tests videos
tests/*/videos
# Andrew's crazy Volar shim generator
shims-volar.d.ts

129
modules/emit-volar-types.ts Normal file
View File

@@ -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<string> {
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<string[]> {
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

View File

@@ -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)