@decantr/verifier 3.0.1 → 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 +68 -8
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
-
)
|
|
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
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
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
|
-
|
|
2300
|
-
const route = match[1] || "/";
|
|
2301
|
-
if (route
|
|
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(
|