beesbuild
Version:
构建工具链
214 lines (213 loc) • 7.13 kB
JavaScript
import path from "path";
import * as compiler from "@vue/compiler-sfc";
import chalk from "chalk-unified";
import { consola, parseManifest } from "@beesbuild/utils";
import { copy, existsSync, readFileSync } from "fs-extra";
import { Project } from "ts-morph";
import glob from "fast-glob";
import { defu } from "defu";
import { BUILD_CACHE_DIR } from "../variables.mjs";
import { handleTypeOption } from "./utils.mjs";
import { vueTsc } from "./vue-tsc.mjs";
import { parse } from "./parseSfc.mjs";
function compileVueFile(file, flag = true) {
var _a, _b, _c;
const result = {
lang: "js"
};
try {
const errors = [];
const content = readFileSync(path.join(file), "utf-8");
const hasTsNoCheck = content.includes("@ts-nocheck");
const sfc = flag ? compiler.parse(content, {
templateParseOptions: {
// there are no components at SFC parsing level
isNativeTag: () => true,
// preserve all whitespaces
isPreTag: () => true,
parseMode: "sfc",
onError: (e) => {
errors.push(e);
},
comments: true
}
}) : parse(content);
const { script, scriptSetup } = sfc.descriptor;
if (script || scriptSetup) {
result.content = (hasTsNoCheck ? "// @ts-nocheck\n" : "") + ((_a = script == null ? void 0 : script.content) != null ? _a : "");
const lang = (_c = (_b = scriptSetup == null ? void 0 : scriptSetup.lang) != null ? _b : script == null ? void 0 : script.lang) != null ? _c : "js";
if (scriptSetup) {
const compiled = compiler.compileScript(sfc.descriptor, {
id: "xxx",
isProd: true
});
result.content += compiled.content;
result.imports = compiled.imports;
}
result.lang = lang;
}
} catch (e) {
handleCatch(e);
}
function handleCatch(e) {
console.error(path.basename(file), "error:compileVueFile");
console.error(e, "error:compileVueFile");
}
return result;
}
function createTypesProject(ctx) {
const {
compilerOptions,
tsConfigFilePath,
rootDir,
entries,
output,
typesCacheDir,
entryFilepath,
isVue
} = handleTypeOption(ctx);
const options = {
compilerOptions,
// 是否跳过添加指定 tsconfig.json 中的源文件。
skipAddingFilesFromTsConfig: true
// 提供 ts 配置文件路径并从 tsconfig 添加文件时,跳过解析文件依赖项。
// skipFileDependencyResolution: true,
// 跳过加载 lib 文件。与编译器 API 不同,ts-morph 不会从 node_modules 文件夹加载它们,而是从其他一些 JS 代码加载它们,并使用假路径来表示它们的存在。如果您想使用自定义 lib 文件文件夹路径,请使用 libFolderPath 选项提供一个路径。
// skipLoadingLibFiles: true,
// 用于加载 lib 文件的文件夹。
// libFolderPath: undefined,
// 是否使用内存文件系统。
// useInMemoryFileSystem: false,
// 创建解析主机以指定自定义模块和/或类型引用指令解析。
// resolutionHost: undefined,
};
if (existsSync(tsConfigFilePath)) {
options.tsConfigFilePath = tsConfigFilePath;
}
const project = new Project(options);
function addSourceFiles() {
if (isVue) return [];
const typingsDtsPath = path.resolve(rootDir, "typings", "env.d.ts");
if (existsSync(typingsDtsPath)) project.addSourceFileAtPath(typingsDtsPath);
const extIncludes = ["jsx", "js", "tsx", "ts", "dts"];
const sourceFiles = [];
for (const { input: filePath } of entries) {
const { ext } = parseManifest(filePath, rootDir);
if (ext === "vue") {
const { lang, content } = compileVueFile(filePath);
if (content) {
const createFilePath = path.join(`${path.relative(rootDir, filePath)}.${lang}`);
const sourceFile = project.createSourceFile(createFilePath, content);
sourceFiles.push(sourceFile);
}
} else if (extIncludes.includes(ext)) {
const sourceFile = project.addSourceFileAtPath(filePath);
sourceFiles.push(sourceFile);
}
}
return sourceFiles;
}
function typeCheck(check) {
var _a;
if (check !== true && ((_a = ctx.argv) == null ? void 0 : _a.debug)) return;
if (isVue) {
try {
vueTsc(ctx, { noEmit: true });
} catch (e) {
consola.error(e);
const err = new Error(`\u751F\u6210dts\u5931\u8D25\u3002${ctx.options.stub}`);
consola.error(err);
}
return;
}
const diagnostics = project.getPreEmitDiagnostics().filter((diagnostic) => {
const code = diagnostic.getCode();
let message = diagnostic.getMessageText();
if (typeof message !== "string") {
message = message.getMessageText();
}
return !(code === 6307 && message.includes("Projects must list all files or use an 'include' pattern"));
});
if (diagnostics.length > 0) {
const aaa = project.formatDiagnosticsWithColorAndContext(diagnostics);
consola.error(aaa);
const err = new Error(`\u751F\u6210dts\u5931\u8D25\u3002${ctx.options.stub}`);
consola.error(err);
}
}
function generate(options2) {
var _a;
if (isVue) return vueTsc(ctx);
const emitOptions = defu(
{
emitOnlyDtsFiles: true,
customTransformers: {}
},
options2
);
const result = project.emitToMemory(emitOptions);
if ((_a = ctx.argv) == null ? void 0 : _a.debug) {
for (const file of result.getFiles()) {
const p = path.relative(rootDir, file.filePath);
consola.success(chalk.green(`Definition for file: ${chalk.bold(p)} generated`));
}
}
return result.saveFiles();
}
function copyFiles() {
let src = path.resolve(rootDir, ".", BUILD_CACHE_DIR, output.name);
const files = glob.sync(`./*.d.ts`, {
cwd: src,
onlyFiles: true,
deep: 1,
absolute: true
});
const directories = glob.sync(`./*`, {
cwd: src,
onlyDirectories: true,
deep: 1,
absolute: true
});
if (files.length === 0 && directories.length === 1) {
src = directories[0];
}
consola.info(chalk.cyan(`Start for copy types file: ${path.relative(rootDir, src)} generated`));
return copy(src, output.path, { overwrite: true });
}
return {
typesCacheDir,
entryFilepath,
output,
options,
project,
addSourceFiles,
typeCheck,
generate,
copy: copyFiles,
isVue
};
}
const typesBuild = async (ctx) => {
const types = createTypesProject(ctx);
types.addSourceFiles();
consola.success("Added source types files");
if (!types.isVue) {
types.typeCheck();
consola.success("Type check passed!");
}
try {
await types.generate();
consola.success(chalk.green("Generate types files success"));
} catch (e) {
consola.error(chalk.green("Generate types files fail"));
consola.error(e);
} finally {
await types.copy();
consola.success(chalk.green("Copy types files success"));
}
return types;
};
export {
createTypesProject,
typesBuild
};