UNPKG

vite-plugin-dts-build

Version:

Fast .d.ts builds for Vite (worker + incremental) with optional dual ESM/CJS support.

214 lines (213 loc) 6.78 kB
import { resolve, join, dirname, relative, sep } from "node:path"; import { workerData, parentPort } from "node:worker_threads"; import { cwd } from "node:process"; import ts from "typescript"; import { copy } from "fs-extra"; import { a as printWarn } from "../log-iFlaTbB3.js"; const { readConfigFile, parseJsonConfigFileContent, createProgram, createEmitAndSemanticDiagnosticsBuilderProgram, createSolutionBuilderHost, createSolutionBuilder, getParsedCommandLineOfConfigFile, getPreEmitDiagnostics, sys, formatDiagnostic, getLineAndCharacterOfPosition, flattenDiagnosticMessageText } = ts; const PROJECT_ROOT = cwd(); const WORKER_DATA = workerData; const tsconfigPath = resolve( WORKER_DATA.tsconfigPath ?? join(PROJECT_ROOT, "tsconfig.json") ); const configFile = readConfigFile(tsconfigPath, sys.readFile); if (configFile.error) { throw new Error(errFormatDiagnostic(configFile.error)); } if (WORKER_DATA.include) { configFile.config.include = stringToStringArray(WORKER_DATA.include); } if (WORKER_DATA.exclude) { configFile.config.exclude = stringToStringArray(WORKER_DATA.exclude); } const parsedConfig = parseJsonConfigFileContent( configFile.config, sys, dirname(tsconfigPath) ); if (parsedConfig.errors.length > 0) { parsedConfig.errors.forEach((error) => { reportDiagnostic(error); }); throw new Error("Failed to parse tsconfig.json"); } const compilerOptions = { incremental: true, // assumeChangesOnlyAffectDirectDependencies: false, declaration: true, // declarationMap: false, emitDeclarationOnly: true, // sourceMap: false, // inlineSourceMap: false, // traceResolution: false, ...parsedConfig.options, ...WORKER_DATA.compilerOptions ?? {} }; const distDir = WORKER_DATA.outDir ?? compilerOptions.declarationDir ?? compilerOptions.outDir ?? join(PROJECT_ROOT, "dist"); const cacheDir = WORKER_DATA.cacheDir ?? join(PROJECT_ROOT, ".tsBuildCache"); compilerOptions.declarationDir = cacheDir; if (shouldWarnSourceMapDepth()) { warnIfSourceMapDepthMismatch(); } const buildOptions = { dry: false, force: false, verbose: false, stopBuildOnErrors: false, ...WORKER_DATA.buildOptions ?? {} }; const checkerMode = compilerOptions?.noEmit === true || buildOptions?.dry === true; if (WORKER_DATA.mode === "compile") { (async () => await runCompile())(); } else { (async () => await runBuild())(); } async function runCompile() { const program = createProgram({ rootNames: parsedConfig.fileNames, options: compilerOptions }); const emitResult = program.emit(); const allDiagnostics = getPreEmitDiagnostics(program).concat( emitResult.diagnostics ); allDiagnostics.forEach((diagnostic) => { if (diagnostic.file && diagnostic.start !== void 0) { const { line, character } = getLineAndCharacterOfPosition( diagnostic.file, diagnostic.start ); const message = flattenDiagnosticMessageText( diagnostic.messageText, "\n" ); console.error( `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}` ); } else { console.error(flattenDiagnosticMessageText(diagnostic.messageText, "\n")); } }); const exitCode = emitResult.emitSkipped ? 1 : 0; return await copyToDist(exitCode); } async function runBuild() { const host = createSolutionBuilderHost( sys, createBuilderProgram, reportDiagnostic, reportSolutionBuilderStatus, reportErrorSummary ); const parseConfigHost = { fileExists: sys.fileExists, readFile: sys.readFile, readDirectory: sys.readDirectory, useCaseSensitiveFileNames: sys.useCaseSensitiveFileNames, getCurrentDirectory: sys.getCurrentDirectory, onUnRecoverableConfigFileDiagnostic: reportDiagnostic }; host.getParsedCommandLine = (fileName) => getParsedCommandLineOfConfigFile( fileName, compilerOptions, parseConfigHost ); const builder = createSolutionBuilder(host, [tsconfigPath], buildOptions); const exitCode = builder.build(); return await copyToDist(exitCode); } function createBuilderProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics, projectReferences) { return createEmitAndSemanticDiagnosticsBuilderProgram( rootNames, { ...options ?? {}, ...compilerOptions }, host, oldProgram, configFileParsingDiagnostics, projectReferences ); } function reportDiagnostic(diagnostic) { console.error(errFormatDiagnostic(diagnostic)); } function reportSolutionBuilderStatus(diagnostic) { console.info(errFormatDiagnostic(diagnostic)); } function reportErrorSummary(errorCount) { if (errorCount !== 0) { console.error(`${errorCount} errors occurred.`); } } function copyToDist(exitCode) { return new Promise((resolve2, reject) => { if (exitCode === 0) { if (checkerMode) { parentPort?.postMessage("check-end"); resolve2(); } else { parentPort?.postMessage("build-end"); parentPort?.once("message", () => { copy(`${cacheDir}/`, `${distDir}/`).then(() => { parentPort?.close(); resolve2(); }).catch((error) => { console.error("failed:", error); reject(error); }); }); } } else { reject(); } }); } function shouldWarnSourceMapDepth() { const sourceMapEnabled = Boolean( compilerOptions.sourceMap || compilerOptions.inlineSourceMap || compilerOptions.declarationMap ); return sourceMapEnabled; } function warnIfSourceMapDepthMismatch() { const roots = parsedConfig.fileNames; const entry = roots[0]; if (entry == null) return; const depthFromJs = computeRelativeDepth(entry, distDir); const depthFromTs = computeRelativeDepth(entry, cacheDir); if (depthFromJs !== depthFromTs) { printWarn( `SourceMap relative path depth mismatch: distDir -> entry depth(${depthFromJs}) !== cacheDir -> entry depth(${depthFromTs}). This may cause broken relative source paths inside *.map files after copying. Consider aligning directory nesting. distDir: ${relative(entry, distDir)} cacheDir: ${relative(entry, cacheDir)}` ); } } function computeRelativeDepth(fromFile, toDir) { const relPath = relative(fromFile, toDir); if (relPath === "") return 0; const segments = relPath.split(sep); const dirSegments = segments.slice(0, -1).filter((seg) => seg !== "" && seg !== "."); return dirSegments.length; } function stringToStringArray(value) { return typeof value === "string" ? [value] : value; } function errFormatDiagnostic(diagnostic) { return formatDiagnostic(diagnostic, { getCanonicalFileName: (fileName) => fileName, getCurrentDirectory: sys.getCurrentDirectory, getNewLine: () => sys.newLine }); }