@decantr/verifier 3.5.3 → 3.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +120 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2363,6 +2363,17 @@ var STYLE_EXTENSIONS = /* @__PURE__ */ new Set([".css", ".scss", ".sass", ".less
|
|
|
2363
2363
|
var PAGE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".vue", ".svelte", ".html"]);
|
|
2364
2364
|
var ROUTE_VARIABLE_NAMES = "(?:path|pathname|route|currentPath|currentRoute|locationPath|activePath)";
|
|
2365
2365
|
var ROUTE_ASSET_EXTENSION_RE = /\.(?:avif|bmp|css|gif|ico|jpeg|jpg|js|json|map|mp4|pdf|png|svg|webp|woff2?)$/i;
|
|
2366
|
+
var JSX_ROUTE_PATH_RE = /<(?:Route|[A-Z][\w.]*Route)\b[^>]*\bpath\s*=\s*(?:"([^"]+)"|'([^']+)'|{\s*"([^"]+)"\s*}|{\s*'([^']+)'\s*}|{\s*`([^`]+)`\s*})/g;
|
|
2367
|
+
var OBJECT_ROUTE_PATH_RE = /\bpath\s*:\s*(?:"([^"]+)"|'([^']+)'|`([^`]+)`)/g;
|
|
2368
|
+
var STATIC_HTML_ENTRY_FILES = [
|
|
2369
|
+
"index.html",
|
|
2370
|
+
"docs/index.html",
|
|
2371
|
+
"src/index.html",
|
|
2372
|
+
"public/index.html",
|
|
2373
|
+
"dist/index.html"
|
|
2374
|
+
];
|
|
2375
|
+
var STATIC_HTML_ROUTE_DIRS = ["demos", "examples"];
|
|
2376
|
+
var MAX_STATIC_HTML_CANDIDATES = 1e3;
|
|
2366
2377
|
var MAX_FILE_READ_BYTES = 512 * 1024;
|
|
2367
2378
|
var MAX_WALK_FILES = 5e3;
|
|
2368
2379
|
var MAX_REPORT_ROUTES = 80;
|
|
@@ -2460,6 +2471,10 @@ function dependencyVersion(dependencies, names) {
|
|
|
2460
2471
|
function hasAnyFile(projectRoot, paths) {
|
|
2461
2472
|
return paths.some((path) => existsSync3(join3(projectRoot, path)));
|
|
2462
2473
|
}
|
|
2474
|
+
function hasStaticHtmlSurface(projectRoot) {
|
|
2475
|
+
if (hasAnyFile(projectRoot, STATIC_HTML_ENTRY_FILES)) return true;
|
|
2476
|
+
return STATIC_HTML_ROUTE_DIRS.some((dir) => existsSync3(join3(projectRoot, dir, "index.html")));
|
|
2477
|
+
}
|
|
2463
2478
|
function detectPackageManager(projectRoot, pkg) {
|
|
2464
2479
|
const declared = pkg?.packageManager?.split("@")[0];
|
|
2465
2480
|
if (declared === "npm" || declared === "pnpm" || declared === "yarn" || declared === "bun") {
|
|
@@ -2472,12 +2487,12 @@ function detectPackageManager(projectRoot, pkg) {
|
|
|
2472
2487
|
return "unknown";
|
|
2473
2488
|
}
|
|
2474
2489
|
function detectPrimaryLanguage(projectRoot, packageJsonPresent) {
|
|
2475
|
-
if (packageJsonPresent) return "javascript";
|
|
2490
|
+
if (packageJsonPresent) return hasStaticHtmlSurface(projectRoot) ? "html" : "javascript";
|
|
2476
2491
|
if (hasAnyFile(projectRoot, ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"]))
|
|
2477
2492
|
return "python";
|
|
2478
2493
|
if (hasAnyFile(projectRoot, ["go.mod"])) return "go";
|
|
2479
2494
|
if (hasAnyFile(projectRoot, ["Cargo.toml"])) return "rust";
|
|
2480
|
-
if (
|
|
2495
|
+
if (hasStaticHtmlSurface(projectRoot)) return "html";
|
|
2481
2496
|
return "unknown";
|
|
2482
2497
|
}
|
|
2483
2498
|
function detectProject(projectRoot) {
|
|
@@ -2510,7 +2525,7 @@ function detectProject(projectRoot) {
|
|
|
2510
2525
|
} else if (dependencies.react) {
|
|
2511
2526
|
framework = "react";
|
|
2512
2527
|
frameworkVersion = dependencyVersion(dependencies, ["react"]);
|
|
2513
|
-
} else if (
|
|
2528
|
+
} else if (hasStaticHtmlSurface(projectRoot)) {
|
|
2514
2529
|
framework = "html";
|
|
2515
2530
|
}
|
|
2516
2531
|
return {
|
|
@@ -2629,9 +2644,12 @@ function fileRouteFromPath(file, baseDir) {
|
|
|
2629
2644
|
function scanFileRoutes(projectRoot, baseDir, extensions = PAGE_EXTENSIONS) {
|
|
2630
2645
|
const fullBase = join3(projectRoot, baseDir);
|
|
2631
2646
|
if (!existsSync3(fullBase)) return [];
|
|
2632
|
-
return walkFiles(fullBase, { extensions }).
|
|
2647
|
+
return walkFiles(fullBase, { extensions }).flatMap((file) => {
|
|
2648
|
+
const baseName = file.split("/").pop() ?? file;
|
|
2649
|
+
const routeFileName = baseName.slice(0, -extname3(baseName).length);
|
|
2650
|
+
if (routeFileName.startsWith("_")) return [];
|
|
2633
2651
|
const rel = relative3(projectRoot, join3(fullBase, file)).replace(/\\/g, "/");
|
|
2634
|
-
return { path: fileRouteFromPath(rel, baseDir), file: rel, hasLayout: false };
|
|
2652
|
+
return [{ path: fileRouteFromPath(rel, baseDir), file: rel, hasLayout: false }];
|
|
2635
2653
|
});
|
|
2636
2654
|
}
|
|
2637
2655
|
function scanReactRouter(projectRoot) {
|
|
@@ -2642,15 +2660,17 @@ function scanReactRouter(projectRoot) {
|
|
|
2642
2660
|
const content = readTextFile(join3(projectRoot, file));
|
|
2643
2661
|
if (!content) continue;
|
|
2644
2662
|
if (content.includes("HashRouter") || content.includes("createHashRouter")) hashRouting = true;
|
|
2645
|
-
const
|
|
2646
|
-
|
|
2647
|
-
const route
|
|
2648
|
-
|
|
2663
|
+
for (const match of content.matchAll(JSX_ROUTE_PATH_RE)) {
|
|
2664
|
+
const literal = firstRouteLiteralMatch(match);
|
|
2665
|
+
for (const route of normalizeDetectedRouteLiterals(literal || "/")) {
|
|
2666
|
+
routes.set(route, { path: route, file, hasLayout: false });
|
|
2667
|
+
}
|
|
2649
2668
|
}
|
|
2650
|
-
const
|
|
2651
|
-
|
|
2652
|
-
const route
|
|
2653
|
-
|
|
2669
|
+
for (const match of content.matchAll(OBJECT_ROUTE_PATH_RE)) {
|
|
2670
|
+
const literal = firstRouteLiteralMatch(match);
|
|
2671
|
+
for (const route of normalizeDetectedRouteLiterals(literal || "/")) {
|
|
2672
|
+
routes.set(route, { path: route, file, hasLayout: false });
|
|
2673
|
+
}
|
|
2654
2674
|
}
|
|
2655
2675
|
for (const route of detectPathnameBranchRoutes(content)) {
|
|
2656
2676
|
routes.set(route, { path: route, file, hasLayout: false });
|
|
@@ -2661,24 +2681,98 @@ function scanReactRouter(projectRoot) {
|
|
|
2661
2681
|
}
|
|
2662
2682
|
return { routes: [...routes.values()], hashRouting };
|
|
2663
2683
|
}
|
|
2664
|
-
function
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2684
|
+
function firstRouteLiteralMatch(match) {
|
|
2685
|
+
return match.slice(1).find((value) => typeof value === "string") ?? null;
|
|
2686
|
+
}
|
|
2687
|
+
function normalizeDetectedRouteLiterals(value) {
|
|
2688
|
+
const withoutHash = value.trim().split("#")[0];
|
|
2689
|
+
const cleaned = withoutHash.includes("?") && !withoutHash.endsWith(")?") ? withoutHash.split("?")[0] : withoutHash;
|
|
2690
|
+
if (!cleaned || cleaned === "/") return ["/"];
|
|
2691
|
+
if (cleaned === "*" || cleaned === "**" || cleaned.startsWith("#")) return [];
|
|
2692
|
+
if (cleaned === "/*" || cleaned === "/**") return [];
|
|
2693
|
+
if (!cleaned.startsWith("/") || cleaned.startsWith("//")) return [];
|
|
2694
|
+
if (ROUTE_ASSET_EXTENSION_RE.test(cleaned)) return [];
|
|
2695
|
+
const optionalGroup = cleaned.match(/^\/\(([^()]+)\)\?$/);
|
|
2696
|
+
if (optionalGroup) {
|
|
2697
|
+
return [
|
|
2698
|
+
"/",
|
|
2699
|
+
...optionalGroup[1].split("|").map((segment) => segment.trim()).filter(Boolean).map((segment) => `/${segment}`)
|
|
2700
|
+
];
|
|
2701
|
+
}
|
|
2702
|
+
const withoutTrailingWildcard = cleaned.endsWith("*") && !cleaned.endsWith("/*") && !/:\w+\*$/.test(cleaned) ? cleaned.slice(0, -1) : cleaned;
|
|
2703
|
+
const normalized = withoutTrailingWildcard.replace(/\/+$/g, "") || "/";
|
|
2704
|
+
if (normalized === "/*" || normalized === "/**") return [];
|
|
2705
|
+
return [normalized];
|
|
2671
2706
|
}
|
|
2672
2707
|
function collectRouteLiterals(pattern, content, routes) {
|
|
2673
2708
|
let count = 0;
|
|
2674
2709
|
for (const match of content.matchAll(pattern)) {
|
|
2675
|
-
const route
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2710
|
+
for (const route of normalizeDetectedRouteLiterals(match[1] ?? "")) {
|
|
2711
|
+
routes.add(route);
|
|
2712
|
+
count += 1;
|
|
2713
|
+
}
|
|
2679
2714
|
}
|
|
2680
2715
|
return count;
|
|
2681
2716
|
}
|
|
2717
|
+
function htmlRouteFromFile(file) {
|
|
2718
|
+
let withoutExt = file.slice(0, -extname3(file).length).replace(/\\/g, "/");
|
|
2719
|
+
if (withoutExt.endsWith("/index")) withoutExt = withoutExt.slice(0, -"/index".length);
|
|
2720
|
+
if (withoutExt === "index" || withoutExt === "docs" || withoutExt === "src" || withoutExt === "public" || withoutExt === "dist") {
|
|
2721
|
+
return "/";
|
|
2722
|
+
}
|
|
2723
|
+
return `/${withoutExt.split("/").filter(Boolean).join("/")}` || "/";
|
|
2724
|
+
}
|
|
2725
|
+
function collectStaticHtmlFiles(dir, projectRoot, files, depth = 0) {
|
|
2726
|
+
if (depth > 5 || files.length >= MAX_STATIC_HTML_CANDIDATES) return;
|
|
2727
|
+
let entries;
|
|
2728
|
+
try {
|
|
2729
|
+
entries = readdirSync3(dir).sort();
|
|
2730
|
+
} catch {
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
for (const entry of entries) {
|
|
2734
|
+
if (files.length >= MAX_STATIC_HTML_CANDIDATES) return;
|
|
2735
|
+
if (entry.startsWith(".") || entry === "node_modules") continue;
|
|
2736
|
+
const fullPath = join3(dir, entry);
|
|
2737
|
+
try {
|
|
2738
|
+
const stat = statSync2(fullPath);
|
|
2739
|
+
if (stat.isDirectory()) {
|
|
2740
|
+
collectStaticHtmlFiles(fullPath, projectRoot, files, depth + 1);
|
|
2741
|
+
} else if (stat.isFile() && /\.(?:html|htm)$/i.test(entry)) {
|
|
2742
|
+
files.push(relative3(projectRoot, fullPath).replace(/\\/g, "/"));
|
|
2743
|
+
}
|
|
2744
|
+
} catch {
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
function staticHtmlFilePriority(file) {
|
|
2749
|
+
const base = file.split("/").pop()?.toLowerCase();
|
|
2750
|
+
if (base === "index.html" || base === "index.htm") return 0;
|
|
2751
|
+
if (base === "default.html" || base === "default.htm") return 1;
|
|
2752
|
+
return 2;
|
|
2753
|
+
}
|
|
2754
|
+
function scanStaticHtmlRoutes(projectRoot) {
|
|
2755
|
+
const routes = /* @__PURE__ */ new Map();
|
|
2756
|
+
for (const file of STATIC_HTML_ENTRY_FILES) {
|
|
2757
|
+
if (!existsSync3(join3(projectRoot, file))) continue;
|
|
2758
|
+
routes.set("/", { path: "/", file, hasLayout: false });
|
|
2759
|
+
break;
|
|
2760
|
+
}
|
|
2761
|
+
for (const dir of STATIC_HTML_ROUTE_DIRS) {
|
|
2762
|
+
const fullDir = join3(projectRoot, dir);
|
|
2763
|
+
if (!existsSync3(fullDir)) continue;
|
|
2764
|
+
const files = [];
|
|
2765
|
+
collectStaticHtmlFiles(fullDir, projectRoot, files);
|
|
2766
|
+
for (const file of files.sort(
|
|
2767
|
+
(a, b) => staticHtmlFilePriority(a) - staticHtmlFilePriority(b) || a.localeCompare(b)
|
|
2768
|
+
)) {
|
|
2769
|
+
const routePath = htmlRouteFromFile(file);
|
|
2770
|
+
if (routes.has(routePath)) continue;
|
|
2771
|
+
routes.set(routePath, { path: routePath, file, hasLayout: false });
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
return [...routes.values()].slice(0, MAX_REPORT_ROUTES);
|
|
2775
|
+
}
|
|
2682
2776
|
function detectPathnameBranchRoutes(content) {
|
|
2683
2777
|
const routes = /* @__PURE__ */ new Set();
|
|
2684
2778
|
const comparison = new RegExp(
|
|
@@ -2738,18 +2832,8 @@ function scanRoutes(projectRoot, detection) {
|
|
|
2738
2832
|
if (reactRouter.routes.length > 0)
|
|
2739
2833
|
return { strategy: "react-router", routes: reactRouter.routes };
|
|
2740
2834
|
if (pagesRoutes.length > 0) return { strategy: "pages-router", routes: pagesRoutes };
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
strategy: "static-html",
|
|
2744
|
-
routes: [{ path: "/", file: "index.html", hasLayout: false }]
|
|
2745
|
-
};
|
|
2746
|
-
}
|
|
2747
|
-
if (existsSync3(join3(projectRoot, "docs", "index.html"))) {
|
|
2748
|
-
return {
|
|
2749
|
-
strategy: "static-html",
|
|
2750
|
-
routes: [{ path: "/", file: "docs/index.html", hasLayout: false }]
|
|
2751
|
-
};
|
|
2752
|
-
}
|
|
2835
|
+
const staticHtmlRoutes = scanStaticHtmlRoutes(projectRoot);
|
|
2836
|
+
if (staticHtmlRoutes.length > 0) return { strategy: "static-html", routes: staticHtmlRoutes };
|
|
2753
2837
|
return { strategy: "none", routes: [] };
|
|
2754
2838
|
}
|
|
2755
2839
|
function countComponents(projectRoot, routes) {
|