@decantr/verifier 3.0.2 → 3.1.0

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
@@ -1137,7 +1137,7 @@ function arbitraryInlineStyleValue(property, value, tokenHints) {
1137
1137
  if (isAcceptedTokenValue(normalizedValue, tokenHints)) return null;
1138
1138
  if (/(?:^|[\s,(])#[0-9a-f]{3,8}\b|(?:rgba?|hsla?|oklch|oklab|lch|lab|color-mix)\(/i.test(
1139
1139
  normalizedValue
1140
- ) || /var\(--[A-Za-z0-9_-]+\)/i.test(normalizedValue)) {
1140
+ )) {
1141
1141
  return `${normalizedProperty}: ${normalizedValue}`;
1142
1142
  }
1143
1143
  return null;
@@ -2010,6 +2010,8 @@ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
2010
2010
  ]);
2011
2011
  var STYLE_EXTENSIONS = /* @__PURE__ */ new Set([".css", ".scss", ".sass", ".less"]);
2012
2012
  var PAGE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".vue", ".svelte", ".html"]);
2013
+ var ROUTE_VARIABLE_NAMES = "(?:path|pathname|route|currentPath|currentRoute|locationPath|activePath)";
2014
+ var ROUTE_ASSET_EXTENSION_RE = /\.(?:avif|bmp|css|gif|ico|jpeg|jpg|js|json|map|mp4|pdf|png|svg|webp|woff2?)$/i;
2013
2015
  var MAX_FILE_READ_BYTES = 512 * 1024;
2014
2016
  var MAX_WALK_FILES = 5e3;
2015
2017
  var MAX_REPORT_ROUTES = 80;
@@ -2290,19 +2292,58 @@ function scanReactRouter(projectRoot) {
2290
2292
  if (!content) continue;
2291
2293
  if (content.includes("HashRouter") || content.includes("createHashRouter")) hashRouting = true;
2292
2294
  const jsxRouteRegex = /<Route\b[^>]*\bpath\s*=\s*["']([^"']+)["']/g;
2293
- let match;
2294
- while ((match = jsxRouteRegex.exec(content)) !== null) {
2295
- const route = match[1] || "/";
2296
- routes.set(route, { path: route, file, hasLayout: false });
2295
+ for (const match of content.matchAll(jsxRouteRegex)) {
2296
+ const route = normalizeDetectedRouteLiteral(match[1] || "/");
2297
+ if (route) routes.set(route, { path: route, file, hasLayout: false });
2297
2298
  }
2298
2299
  const objectRouteRegex = /\bpath\s*:\s*["']([^"']+)["']/g;
2299
- while ((match = objectRouteRegex.exec(content)) !== null) {
2300
- const route = match[1] || "/";
2301
- if (route.startsWith("/")) routes.set(route, { path: route, file, hasLayout: false });
2300
+ for (const match of content.matchAll(objectRouteRegex)) {
2301
+ const route = normalizeDetectedRouteLiteral(match[1] || "/");
2302
+ if (route) routes.set(route, { path: route, file, hasLayout: false });
2303
+ }
2304
+ for (const route of detectPathnameBranchRoutes(content)) {
2305
+ routes.set(route, { path: route, file, hasLayout: false });
2302
2306
  }
2303
2307
  }
2304
2308
  return { routes: [...routes.values()], hashRouting };
2305
2309
  }
2310
+ function normalizeDetectedRouteLiteral(value) {
2311
+ const cleaned = value.trim().split(/[?#]/)[0];
2312
+ if (!cleaned || cleaned === "/") return "/";
2313
+ if (cleaned === "*" || cleaned === "**" || cleaned.startsWith("#")) return null;
2314
+ if (!cleaned.startsWith("/") || cleaned.startsWith("//")) return null;
2315
+ if (ROUTE_ASSET_EXTENSION_RE.test(cleaned)) return null;
2316
+ return cleaned.replace(/\/+$/g, "") || "/";
2317
+ }
2318
+ function collectRouteLiterals(pattern, content, routes) {
2319
+ let count = 0;
2320
+ for (const match of content.matchAll(pattern)) {
2321
+ const route = normalizeDetectedRouteLiteral(match[1] ?? "");
2322
+ if (!route) continue;
2323
+ routes.add(route);
2324
+ count += 1;
2325
+ }
2326
+ return count;
2327
+ }
2328
+ function detectPathnameBranchRoutes(content) {
2329
+ const routes = /* @__PURE__ */ new Set();
2330
+ const comparison = new RegExp(
2331
+ `\\b${ROUTE_VARIABLE_NAMES}\\b\\s*(?:===|!==|==|!=)\\s*["'\`](\\/[^"'\`]+)["'\`]`,
2332
+ "g"
2333
+ );
2334
+ const reversedComparison = new RegExp(
2335
+ `["'\`](\\/[^"'\`]+)["'\`]\\s*(?:===|!==|==|!=)\\s*\\b${ROUTE_VARIABLE_NAMES}\\b`,
2336
+ "g"
2337
+ );
2338
+ const strongMatches = collectRouteLiterals(comparison, content, routes) + collectRouteLiterals(reversedComparison, content, routes) + collectRouteLiterals(/\bcase\s+["'`](\/[^"'`]+)["'`]\s*:/g, content, routes);
2339
+ const hasPathnameSignal = /\b(?:window\.|document\.)?location\.pathname\b|\bpathname\b/.test(
2340
+ content
2341
+ );
2342
+ if (strongMatches > 0 || hasPathnameSignal) {
2343
+ collectRouteLiterals(/\b(?:href|to)\s*=\s*["'`](\/[^"'`]+)["'`]/g, content, routes);
2344
+ }
2345
+ return [...routes];
2346
+ }
2306
2347
  function scanRoutes(projectRoot, detection) {
2307
2348
  const appRoutes = ["src/app", "app"].flatMap(
2308
2349
  (dir) => existsSync3(join3(projectRoot, dir)) ? walkNextAppRoutes(join3(projectRoot, dir), projectRoot, []) : []
@@ -4965,6 +5006,24 @@ function behaviorRepairPayload(input) {
4965
5006
  ...input.componentPaths?.length ? { component_paths: input.componentPaths } : {}
4966
5007
  };
4967
5008
  }
5009
+ function isLocalPatternComponentPath(pattern, relativeFile) {
5010
+ const normalizedFile = relativeFile.replace(/\\/g, "/");
5011
+ return (pattern.componentPaths ?? []).some((componentPath) => {
5012
+ const normalizedComponentPath = componentPath.replace(/\\/g, "/");
5013
+ return normalizedFile === normalizedComponentPath || normalizedFile.endsWith(`/${normalizedComponentPath}`);
5014
+ });
5015
+ }
5016
+ function isFormPrimitiveDefinitionFile(pattern, relativeFile) {
5017
+ if (isLocalPatternComponentPath(pattern, relativeFile)) return true;
5018
+ const normalizedFile = relativeFile.replace(/\\/g, "/");
5019
+ if (!/(?:^|\/)(?:components|ui|shared|packages\/[^/]+\/src)\//i.test(normalizedFile)) {
5020
+ return false;
5021
+ }
5022
+ const base = basename2(normalizedFile).toLowerCase();
5023
+ return /^(?:input|select|textarea|field|form|form-field|form-control|label|checkbox|radio|switch|combobox|button)\.[cm]?[jt]sx?$/.test(
5024
+ base
5025
+ );
5026
+ }
4968
5027
  function sourceAuditFilePath(projectRoot, file) {
4969
5028
  return isAbsolute2(file) ? file : join4(projectRoot, file);
4970
5029
  }
@@ -5033,6 +5092,7 @@ function appendBehaviorObligationFindings(findings, projectRoot, sourceFiles) {
5033
5092
  continue;
5034
5093
  }
5035
5094
  if (!/<\s*(?:form|input|select|textarea|button)\b/i.test(code)) continue;
5095
+ if (isFormPrimitiveDefinitionFile(formPattern, relativeFile)) continue;
5036
5096
  const signals = analyzeAstSignals(relativeFile, code);
5037
5097
  if (behaviorPatternHasObligation(formPattern, "label-associated") && signals.formControlWithoutLabelCount > 0) {
5038
5098
  findings.push(