import process from 'process' import path from 'path' import fs from 'fs/promises' import * as vueCompiler from 'vue/compiler-sfc' import { Project } from 'ts-morph' import glob from 'fast-glob' import { bold } from 'chalk' import { errorAndExit, green, yellow } from './utils/log' import { buildOutput, epRoot, pkgRoot, projRoot } from './utils/paths' import typeSafe from './type-safe.json' import { excludeFiles, pathRewriter } from './utils/pkg' import type { SourceFile } from 'ts-morph' const TSCONFIG_PATH = path.resolve(projRoot, 'tsconfig.json') const outDir = path.resolve(buildOutput, 'types') // Type safe list. The TS errors are not all fixed yet, so we need a list of which files are fixed with TS errors to prevent accidental TS errors. const typeSafePaths = typeSafe.map((_path) => { let safePath = path.resolve(projRoot, _path) if (_path.endsWith('/')) safePath += path.sep return safePath }) /** * fork = require( https://github.com/egoist/vue-dts-gen/blob/main/src/index.ts */ export const generateTypesDefinitions = async () => { const project = new Project({ compilerOptions: { emitDeclarationOnly: true, outDir, baseUrl: projRoot, paths: { '@element-plus/*': ['packages/*'], }, preserveSymlinks: true, types: [ path.resolve(projRoot, 'typings/env'), 'unplugin-vue-define-options', ], }, tsConfigFilePath: TSCONFIG_PATH, skipAddingFilesFromTsConfig: true, }) const filePaths = excludeFiles( await glob(['**/*.{js,ts,vue}', '!element-plus/**/*'], { cwd: pkgRoot, absolute: true, onlyFiles: true, }) ) const epPaths = excludeFiles( await glob('**/*.{js,ts,vue}', { cwd: epRoot, onlyFiles: true, }) ) const sourceFiles: SourceFile[] = [] await Promise.all([ ...filePaths.map(async (file) => { if (file.endsWith('.vue')) { const content = await fs.readFile(file, 'utf-8') const sfc = vueCompiler.parse(content) const { script, scriptSetup } = sfc.descriptor if (script || scriptSetup) { let content = '' let isTS = false if (script && script.content) { content += script.content if (script.lang === 'ts') isTS = true } if (scriptSetup) { const compiled = vueCompiler.compileScript(sfc.descriptor, { id: 'xxx', }) content += compiled.content if (scriptSetup.lang === 'ts') isTS = true } const sourceFile = project.createSourceFile( path.relative(process.cwd(), file) + (isTS ? '.ts' : '.js'), content ) sourceFiles.push(sourceFile) } } else { const sourceFile = project.addSourceFileAtPath(file) sourceFiles.push(sourceFile) } }), ...epPaths.map(async (file) => { const content = await fs.readFile(path.resolve(epRoot, file), 'utf-8') sourceFiles.push( project.createSourceFile(path.resolve(pkgRoot, file), content) ) }), ]) const diagnostics = project.getPreEmitDiagnostics().filter((diagnostic) => { const filePath = diagnostic.getSourceFile()?.getFilePath() return ( filePath && typeSafePaths.some((safePath) => filePath.startsWith(safePath)) ) }) if (diagnostics.length > 0) { console.log(project.formatDiagnosticsWithColorAndContext(diagnostics)) process.exit(1) } await project.emit({ emitOnlyDtsFiles: true, }) const tasks = sourceFiles.map(async (sourceFile) => { const relativePath = path.relative(pkgRoot, sourceFile.getFilePath()) yellow(`Generating definition for file: ${bold(relativePath)}`) const emitOutput = sourceFile.getEmitOutput() const emitFiles = emitOutput.getOutputFiles() if (emitFiles.length === 0) { errorAndExit(new Error(`Emit no file: ${bold(relativePath)}`)) } const tasks = emitFiles.map(async (outputFile) => { const filepath = outputFile.getFilePath() await fs.mkdir(path.dirname(filepath), { recursive: true, }) await fs.writeFile( filepath, pathRewriter('esm')(outputFile.getText()), 'utf8' ) green(`Definition for file: ${bold(relativePath)} generated`) }) await Promise.all(tasks) }) await Promise.all(tasks) }