@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 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 (hasAnyFile(projectRoot, ["index.html", "docs/index.html"])) return "html";
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 (hasAnyFile(projectRoot, ["index.html", "docs/index.html"])) {
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 }).map((file) => {
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 jsxRouteRegex = /<Route\b[^>]*\bpath\s*=\s*["']([^"']+)["']/g;
2646
- for (const match of content.matchAll(jsxRouteRegex)) {
2647
- const route = normalizeDetectedRouteLiteral(match[1] || "/");
2648
- if (route) routes.set(route, { path: route, file, hasLayout: false });
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 objectRouteRegex = /\bpath\s*:\s*["']([^"']+)["']/g;
2651
- for (const match of content.matchAll(objectRouteRegex)) {
2652
- const route = normalizeDetectedRouteLiteral(match[1] || "/");
2653
- if (route) routes.set(route, { path: route, file, hasLayout: false });
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 normalizeDetectedRouteLiteral(value) {
2665
- const cleaned = value.trim().split(/[?#]/)[0];
2666
- if (!cleaned || cleaned === "/") return "/";
2667
- if (cleaned === "*" || cleaned === "**" || cleaned.startsWith("#")) return null;
2668
- if (!cleaned.startsWith("/") || cleaned.startsWith("//")) return null;
2669
- if (ROUTE_ASSET_EXTENSION_RE.test(cleaned)) return null;
2670
- return cleaned.replace(/\/+$/g, "") || "/";
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 = normalizeDetectedRouteLiteral(match[1] ?? "");
2676
- if (!route) continue;
2677
- routes.add(route);
2678
- count += 1;
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
- if (existsSync3(join3(projectRoot, "index.html"))) {
2742
- return {
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) {