first commit
This commit is contained in:
commit
db5762abd4
687 files changed
+171307
No files matched your search
@@ -0,0 +1,140 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { createRequire } from "node:module";
|
||||
import { parse as parseJavaScript } from "@babel/parser";
|
||||
import {
|
||||
parse as parseVue,
|
||||
compileScript,
|
||||
compileTemplate,
|
||||
} from "@vue/compiler-sfc";
|
||||
|
||||
const root = path.resolve(import.meta.dirname, "..");
|
||||
const require = createRequire(import.meta.url);
|
||||
const failures = [];
|
||||
const files = [];
|
||||
function walk(directory) {
|
||||
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
||||
const file = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) walk(file);
|
||||
else files.push(file);
|
||||
}
|
||||
}
|
||||
walk(path.join(root, "app"));
|
||||
const manifest = JSON.parse(
|
||||
fs.readFileSync(path.join(root, "package.json"), "utf8"),
|
||||
);
|
||||
const dependencies = new Set(
|
||||
Object.keys({ ...manifest.dependencies, ...manifest.devDependencies }),
|
||||
);
|
||||
function existsModule(file) {
|
||||
return ["", ".js", ".ts", ".json", ".vue", "/index.js", "/index.ts"].some(
|
||||
(extension) => fs.existsSync(file + extension),
|
||||
);
|
||||
}
|
||||
function checkImport(source, owner) {
|
||||
if (source.startsWith("#")) return; // Nuxt-generated public/virtual modules.
|
||||
if (source.startsWith("~/")) {
|
||||
if (!existsModule(path.join(root, "app", source.slice(2))))
|
||||
failures.push(`${owner}: missing ${source}`);
|
||||
} else if (source.startsWith(".")) {
|
||||
if (!existsModule(path.resolve(path.dirname(owner), source)))
|
||||
failures.push(`${owner}: missing ${source}`);
|
||||
} else {
|
||||
const name = source.startsWith("@")
|
||||
? source.split("/").slice(0, 2).join("/")
|
||||
: source.split("/")[0];
|
||||
if (!dependencies.has(name))
|
||||
failures.push(`${owner}: undeclared dependency ${name}`);
|
||||
try {
|
||||
require.resolve(source);
|
||||
} catch (error) {
|
||||
failures.push(`${owner}: cannot resolve ${source}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
function inspect(node, owner) {
|
||||
if (!node || typeof node !== "object") return;
|
||||
if (
|
||||
[
|
||||
"ImportDeclaration",
|
||||
"ExportNamedDeclaration",
|
||||
"ExportAllDeclaration",
|
||||
].includes(node.type) &&
|
||||
node.source
|
||||
)
|
||||
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);
|
||||
}
|
||||
const literal =
|
||||
node.type === "StringLiteral"
|
||||
? node.value
|
||||
: node.type === "TemplateElement"
|
||||
? (node.value.cooked ?? node.value.raw)
|
||||
: null;
|
||||
if (
|
||||
literal !== null &&
|
||||
/(?:\.output\/|\/releases\/|\/Users\/|\/www\/wwwroot\/)/.test(literal)
|
||||
)
|
||||
failures.push(
|
||||
`${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);
|
||||
else if (value && typeof value === "object") inspect(value, owner);
|
||||
}
|
||||
}
|
||||
let components = 0;
|
||||
let modules = 0;
|
||||
for (const file of files) {
|
||||
if (!/\.(vue|js|ts)$/.test(file)) continue;
|
||||
const source = fs.readFileSync(file, "utf8");
|
||||
try {
|
||||
if (file.endsWith(".vue")) {
|
||||
components++;
|
||||
const { descriptor, errors } = parseVue(source, { filename: file });
|
||||
if (errors.length) throw Error(errors.join("\n"));
|
||||
const script = descriptor.scriptSetup
|
||||
? compileScript(descriptor, { id: "source-check" }).content
|
||||
: descriptor.script?.content || "";
|
||||
inspect(
|
||||
parseJavaScript(script, {
|
||||
sourceType: "module",
|
||||
plugins: ["typescript"],
|
||||
}),
|
||||
file,
|
||||
);
|
||||
if (descriptor.template) {
|
||||
const result = compileTemplate({
|
||||
source: descriptor.template.content,
|
||||
filename: file,
|
||||
id: "source-check",
|
||||
});
|
||||
if (result.errors.length) throw Error(result.errors.join("\n"));
|
||||
}
|
||||
} else {
|
||||
modules++;
|
||||
inspect(
|
||||
parseJavaScript(source, {
|
||||
sourceType: "module",
|
||||
plugins: ["typescript"],
|
||||
}),
|
||||
file,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
failures.push(`${file}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
if (failures.length) {
|
||||
console.error(
|
||||
failures.map((failure) => failure.replaceAll(root + "/", "")).join("\n"),
|
||||
);
|
||||
process.exitCode = 1;
|
||||
} else
|
||||
console.log(
|
||||
`Source gate passed: ${components} Vue files, ${modules} modules; imports resolve without any historic release.`,
|
||||
);
|
||||
Reference in new issue
Block a user