docs: introduce project and keep local reports out of source delivery
This commit is contained in:
1 parent
db5762abd4
commit
d37ce57d09
45 files changed
+232
-14820
No files matched your search
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* 静态源码门禁:解析应用的 JS/TS/Vue,检查显式导入和机器/旧部署路径依赖。
|
||||
* 这是构建前检查,不替代浏览器业务验收,也不会启动或修改生产服务。
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { createRequire } from "node:module";
|
||||
@@ -13,6 +17,7 @@ const require = createRequire(import.meta.url);
|
||||
const failures = [];
|
||||
const files = [];
|
||||
function walk(directory) {
|
||||
// 仅递归 app/;不扫描 node_modules、构建产物或本地 docs 报告。
|
||||
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
||||
const file = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) walk(file);
|
||||
@@ -27,11 +32,13 @@ const dependencies = new Set(
|
||||
Object.keys({ ...manifest.dependencies, ...manifest.devDependencies }),
|
||||
);
|
||||
function existsModule(file) {
|
||||
// 与当前源码使用的简写导入形式对齐,允许目录下的 index.js / index.ts。
|
||||
return ["", ".js", ".ts", ".json", ".vue", "/index.js", "/index.ts"].some(
|
||||
(extension) => fs.existsSync(file + extension),
|
||||
);
|
||||
}
|
||||
function checkImport(source, owner) {
|
||||
// # 开头的虚拟模块由 Nuxt 生成,本地文件与 npm 包则分别核对实际路径及依赖声明。
|
||||
if (source.startsWith("#")) return; // Nuxt-generated public/virtual modules.
|
||||
if (source.startsWith("~/")) {
|
||||
if (!existsModule(path.join(root, "app", source.slice(2))))
|
||||
@@ -53,6 +60,7 @@ function checkImport(source, owner) {
|
||||
}
|
||||
}
|
||||
function inspect(node, owner) {
|
||||
// 按 AST 检查,不把注释中的历史说明误判成运行时路径依赖。
|
||||
if (!node || typeof node !== "object") return;
|
||||
if (
|
||||
[
|
||||
@@ -64,6 +72,7 @@ function inspect(node, owner) {
|
||||
)
|
||||
checkImport(node.source.value, owner);
|
||||
if (node.type === "CallExpression" && node.callee.type === "Import") {
|
||||
// 静态字符串的懒加载同样要验证;动态计算的路径不在本门禁可证明范围内。
|
||||
const value = node.arguments[0];
|
||||
if (value.type === "StringLiteral") checkImport(value.value, owner);
|
||||
}
|
||||
@@ -81,6 +90,7 @@ function inspect(node, owner) {
|
||||
`${owner}:${node.loc?.start.line ?? 1}: runtime depends on deployment/machine path`,
|
||||
);
|
||||
for (const [key, value] of Object.entries(node)) {
|
||||
// 跳过位置和注释等元数据,仅递归语法节点,避免重复或无意义遍历。
|
||||
if (["loc", "start", "end", "extra", "comments", "tokens"].includes(key))
|
||||
continue;
|
||||
if (Array.isArray(value)) for (const item of value) inspect(item, owner);
|
||||
@@ -94,6 +104,7 @@ for (const file of files) {
|
||||
const source = fs.readFileSync(file, "utf8");
|
||||
try {
|
||||
if (file.endsWith(".vue")) {
|
||||
// script setup 先展开编译宏,再解析脚本;有 template 时另外检查模板编译错误。
|
||||
components++;
|
||||
const { descriptor, errors } = parseVue(source, { filename: file });
|
||||
if (errors.length) throw Error(errors.join("\n"));
|
||||
@@ -129,6 +140,7 @@ for (const file of files) {
|
||||
failures.push(`${file}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
// 一次报告全部失败并以非零状态退出,方便开发者和 CI 定位多个问题。
|
||||
if (failures.length) {
|
||||
console.error(
|
||||
failures.map((failure) => failure.replaceAll(root + "/", "")).join("\n"),
|
||||
|
||||
Reference in new issue
Block a user