@drskillissue/ganko 0.2.61 → 0.2.72
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/README.md +1 -1
- package/dist/{chunk-BK7TC7DN.js → chunk-MJKIL7DJ.js} +520 -339
- package/dist/chunk-MJKIL7DJ.js.map +1 -0
- package/dist/{chunk-5OEDGKHL.js → chunk-NFDA6LAI.js} +17 -15
- package/dist/chunk-NFDA6LAI.js.map +1 -0
- package/dist/eslint-plugin.cjs +500 -333
- package/dist/eslint-plugin.cjs.map +1 -1
- package/dist/eslint-plugin.js +1 -1
- package/dist/index.cjs +555 -373
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +24 -23
- package/dist/index.js.map +1 -1
- package/dist/rules-manifest.cjs +16 -14
- package/dist/rules-manifest.cjs.map +1 -1
- package/dist/rules-manifest.js +1 -1
- package/package.json +1 -2
- package/dist/chunk-5OEDGKHL.js.map +0 -1
- package/dist/chunk-BK7TC7DN.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -151,9 +151,29 @@ function canonicalPath(path) {
|
|
|
151
151
|
return canonical;
|
|
152
152
|
}
|
|
153
153
|
var LOG_LEVELS = ["trace", "debug", "info", "warning", "error", "critical", "off"];
|
|
154
|
+
var Level = {
|
|
155
|
+
Trace: 0,
|
|
156
|
+
Debug: 1,
|
|
157
|
+
Info: 2,
|
|
158
|
+
Warning: 3,
|
|
159
|
+
Error: 4,
|
|
160
|
+
Critical: 5,
|
|
161
|
+
Off: 6
|
|
162
|
+
};
|
|
163
|
+
var LOG_LEVEL_ORDER = {
|
|
164
|
+
trace: Level.Trace,
|
|
165
|
+
debug: Level.Debug,
|
|
166
|
+
info: Level.Info,
|
|
167
|
+
warning: Level.Warning,
|
|
168
|
+
error: Level.Error,
|
|
169
|
+
critical: Level.Critical,
|
|
170
|
+
off: Level.Off
|
|
171
|
+
};
|
|
154
172
|
var noopLogger = {
|
|
155
|
-
enabled: false,
|
|
156
173
|
level: "off",
|
|
174
|
+
isLevelEnabled() {
|
|
175
|
+
return false;
|
|
176
|
+
},
|
|
157
177
|
trace() {
|
|
158
178
|
},
|
|
159
179
|
debug() {
|
|
@@ -2790,7 +2810,7 @@ var GraphCache = class {
|
|
|
2790
2810
|
const key = canonicalPath(path);
|
|
2791
2811
|
const cached = this.solids.get(key);
|
|
2792
2812
|
const hit = cached !== void 0 && cached.version === version;
|
|
2793
|
-
if (this.log.
|
|
2813
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`hasSolidGraph: ${key} v=${version} cached=${cached?.version ?? "none"} hit=${hit} (${this.solids.size} total)`);
|
|
2794
2814
|
return hit;
|
|
2795
2815
|
}
|
|
2796
2816
|
/**
|
|
@@ -2807,7 +2827,7 @@ var GraphCache = class {
|
|
|
2807
2827
|
const key = canonicalPath(path);
|
|
2808
2828
|
this.solids.set(key, { version, graph });
|
|
2809
2829
|
this.solidGeneration++;
|
|
2810
|
-
if (this.log.
|
|
2830
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`setSolidGraph: ${key} v=${version} (${this.solids.size} total) solidGen=${this.solidGeneration}`);
|
|
2811
2831
|
}
|
|
2812
2832
|
/**
|
|
2813
2833
|
* Get a cached SolidGraph without building on miss.
|
|
@@ -2823,10 +2843,10 @@ var GraphCache = class {
|
|
|
2823
2843
|
const key = canonicalPath(path);
|
|
2824
2844
|
const cached = this.solids.get(key);
|
|
2825
2845
|
if (cached !== void 0 && cached.version === version) {
|
|
2826
|
-
if (this.log.
|
|
2846
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getCachedSolidGraph HIT: ${key} v=${version}`);
|
|
2827
2847
|
return cached.graph;
|
|
2828
2848
|
}
|
|
2829
|
-
if (this.log.
|
|
2849
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getCachedSolidGraph MISS: ${key} v=${version}`);
|
|
2830
2850
|
return null;
|
|
2831
2851
|
}
|
|
2832
2852
|
/**
|
|
@@ -2843,15 +2863,15 @@ var GraphCache = class {
|
|
|
2843
2863
|
const key = canonicalPath(path);
|
|
2844
2864
|
const cached = this.solids.get(key);
|
|
2845
2865
|
if (cached !== void 0 && cached.version === version) {
|
|
2846
|
-
if (this.log.
|
|
2866
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getSolidGraph HIT: ${key} v=${version}`);
|
|
2847
2867
|
return cached.graph;
|
|
2848
2868
|
}
|
|
2849
|
-
if (this.log.
|
|
2869
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getSolidGraph MISS: ${key} v=${version} (was ${cached?.version ?? "uncached"})`);
|
|
2850
2870
|
const t0 = performance.now();
|
|
2851
2871
|
const graph = build();
|
|
2852
2872
|
this.solids.set(key, { version, graph });
|
|
2853
2873
|
this.solidGeneration++;
|
|
2854
|
-
if (this.log.
|
|
2874
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getSolidGraph BUILT: ${key} v=${version} in ${performance.now() - t0}ms (${this.solids.size} total) solidGen=${this.solidGeneration}`);
|
|
2855
2875
|
return graph;
|
|
2856
2876
|
}
|
|
2857
2877
|
/**
|
|
@@ -2865,14 +2885,14 @@ var GraphCache = class {
|
|
|
2865
2885
|
*/
|
|
2866
2886
|
getCSSGraph(build) {
|
|
2867
2887
|
if (this.css !== null && this.css.generation === this.cssGeneration) {
|
|
2868
|
-
if (this.log.
|
|
2888
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getCSSGraph HIT: gen=${this.cssGeneration}`);
|
|
2869
2889
|
return this.css.graph;
|
|
2870
2890
|
}
|
|
2871
|
-
if (this.log.
|
|
2891
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getCSSGraph MISS: currentGen=${this.cssGeneration} cachedGen=${this.css?.generation ?? "none"}`);
|
|
2872
2892
|
const t0 = performance.now();
|
|
2873
2893
|
const graph = build();
|
|
2874
2894
|
this.css = { generation: this.cssGeneration, graph };
|
|
2875
|
-
if (this.log.
|
|
2895
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getCSSGraph BUILT: gen=${this.cssGeneration} in ${performance.now() - t0}ms`);
|
|
2876
2896
|
return graph;
|
|
2877
2897
|
}
|
|
2878
2898
|
/**
|
|
@@ -2887,10 +2907,10 @@ var GraphCache = class {
|
|
|
2887
2907
|
const solidGen = this.solidGeneration;
|
|
2888
2908
|
const cssGen = this.cssGeneration;
|
|
2889
2909
|
if (this.layout !== null && this.layout.solidGeneration === solidGen && this.layout.cssGeneration === cssGen) {
|
|
2890
|
-
if (this.log.
|
|
2910
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getLayoutGraph HIT: solidGen=${solidGen} cssGen=${cssGen}`);
|
|
2891
2911
|
return this.layout.graph;
|
|
2892
2912
|
}
|
|
2893
|
-
if (this.log.
|
|
2913
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(
|
|
2894
2914
|
`getLayoutGraph MISS: solidGen=${solidGen} cssGen=${cssGen} cached=${this.layout !== null}`
|
|
2895
2915
|
);
|
|
2896
2916
|
const t0 = performance.now();
|
|
@@ -2900,7 +2920,7 @@ var GraphCache = class {
|
|
|
2900
2920
|
cssGeneration: cssGen,
|
|
2901
2921
|
graph
|
|
2902
2922
|
};
|
|
2903
|
-
if (this.log.
|
|
2923
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getLayoutGraph BUILT: in ${performance.now() - t0}ms`);
|
|
2904
2924
|
return graph;
|
|
2905
2925
|
}
|
|
2906
2926
|
/**
|
|
@@ -2915,7 +2935,7 @@ var GraphCache = class {
|
|
|
2915
2935
|
invalidate(path) {
|
|
2916
2936
|
const key = canonicalPath(path);
|
|
2917
2937
|
const kind = classifyFile(key);
|
|
2918
|
-
if (this.log.
|
|
2938
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`invalidate: ${key} kind=${kind} solids=${this.solids.size} hasCrossFileResults=${this.crossFileResults !== null} hasLayout=${this.layout !== null}`);
|
|
2919
2939
|
if (kind === "solid") {
|
|
2920
2940
|
const had = this.solids.has(key);
|
|
2921
2941
|
this.solids.delete(key);
|
|
@@ -2923,7 +2943,7 @@ var GraphCache = class {
|
|
|
2923
2943
|
this.crossFileResults = null;
|
|
2924
2944
|
this.solidGeneration++;
|
|
2925
2945
|
this.layout = null;
|
|
2926
|
-
if (this.log.
|
|
2946
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`invalidate SOLID: ${key} wasInCache=${had} solids=${this.solids.size} solidGen=${this.solidGeneration}`);
|
|
2927
2947
|
}
|
|
2928
2948
|
if (kind === "css") {
|
|
2929
2949
|
this.crossFileDiagnostics.delete(key);
|
|
@@ -2931,7 +2951,7 @@ var GraphCache = class {
|
|
|
2931
2951
|
this.cssGeneration++;
|
|
2932
2952
|
this.css = null;
|
|
2933
2953
|
this.layout = null;
|
|
2934
|
-
if (this.log.
|
|
2954
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`invalidate CSS: ${key} newCssGen=${this.cssGeneration}`);
|
|
2935
2955
|
}
|
|
2936
2956
|
}
|
|
2937
2957
|
/**
|
|
@@ -2940,7 +2960,7 @@ var GraphCache = class {
|
|
|
2940
2960
|
* Called on workspace-level events like config changes.
|
|
2941
2961
|
*/
|
|
2942
2962
|
invalidateAll() {
|
|
2943
|
-
if (this.log.
|
|
2963
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`invalidateAll: solids=${this.solids.size} solidGen=${this.solidGeneration} cssGen=${this.cssGeneration}`);
|
|
2944
2964
|
this.solids.clear();
|
|
2945
2965
|
this.crossFileDiagnostics.clear();
|
|
2946
2966
|
this.crossFileResults = null;
|
|
@@ -2956,7 +2976,7 @@ var GraphCache = class {
|
|
|
2956
2976
|
* Used by cross-file analysis which needs all SolidGraphs.
|
|
2957
2977
|
*/
|
|
2958
2978
|
getAllSolidGraphs() {
|
|
2959
|
-
if (this.log.
|
|
2979
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getAllSolidGraphs: ${this.solids.size} graphs`);
|
|
2960
2980
|
const out = new Array(this.solids.size);
|
|
2961
2981
|
let i = 0;
|
|
2962
2982
|
for (const entry of this.solids.values()) {
|
|
@@ -3012,10 +3032,10 @@ var GraphCache = class {
|
|
|
3012
3032
|
const solidMatch = this.crossFileResults.solidGeneration === this.solidGeneration;
|
|
3013
3033
|
const cssMatch = this.crossFileResults.cssGeneration === this.cssGeneration;
|
|
3014
3034
|
if (solidMatch && cssMatch) {
|
|
3015
|
-
if (this.log.
|
|
3035
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(`getCachedCrossFileResults HIT: ${this.crossFileResults?.byFile.size} files`);
|
|
3016
3036
|
return this.crossFileResults.byFile;
|
|
3017
3037
|
}
|
|
3018
|
-
if (this.log.
|
|
3038
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(
|
|
3019
3039
|
`getCachedCrossFileResults MISS: solidMatch=${solidMatch} cssMatch=${cssMatch} cachedSolidGen=${this.crossFileResults?.solidGeneration} currentSolidGen=${this.solidGeneration} cachedCssGen=${this.crossFileResults?.cssGeneration} currentCssGen=${this.cssGeneration}`
|
|
3020
3040
|
);
|
|
3021
3041
|
return null;
|
|
@@ -3046,7 +3066,7 @@ var GraphCache = class {
|
|
|
3046
3066
|
cssGeneration: this.cssGeneration,
|
|
3047
3067
|
byFile
|
|
3048
3068
|
};
|
|
3049
|
-
if (this.log.
|
|
3069
|
+
if (this.log.isLevelEnabled(Level.Debug)) this.log.debug(
|
|
3050
3070
|
`setCachedCrossFileResults: ${allDiagnostics.length} diags across ${byFile.size} files solidGen=${this.solidGeneration} cssGen=${this.cssGeneration}`
|
|
3051
3071
|
);
|
|
3052
3072
|
this.crossFileDiagnostics.clear();
|
|
@@ -3358,6 +3378,35 @@ var TypeResolver = class {
|
|
|
3358
3378
|
return false;
|
|
3359
3379
|
}
|
|
3360
3380
|
}
|
|
3381
|
+
/**
|
|
3382
|
+
* Strict array check: only matches Array<T>, T[], ReadonlyArray<T>, and tuples.
|
|
3383
|
+
* Does NOT match typed arrays (Uint8Array, Buffer, etc.) or other types with
|
|
3384
|
+
* a numeric index signature.
|
|
3385
|
+
*/
|
|
3386
|
+
isStrictArrayType(node) {
|
|
3387
|
+
try {
|
|
3388
|
+
const tsType = this.checker.getTypeAtLocation(node);
|
|
3389
|
+
return this.checkIsStrictArrayType(tsType);
|
|
3390
|
+
} catch {
|
|
3391
|
+
return false;
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
checkIsStrictArrayType(tsType) {
|
|
3395
|
+
if (tsType.flags & 524288) {
|
|
3396
|
+
const objFlags = getObjectFlags(tsType);
|
|
3397
|
+
if (objFlags & 8) return true;
|
|
3398
|
+
if (objFlags & 4) {
|
|
3399
|
+
const name = tsType.getSymbol()?.getName();
|
|
3400
|
+
if (name === "Array" || name === "ReadonlyArray") return true;
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
if (tsType.isUnion()) {
|
|
3404
|
+
for (const t of tsType.types) {
|
|
3405
|
+
if (this.checkIsStrictArrayType(t)) return true;
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
return false;
|
|
3409
|
+
}
|
|
3361
3410
|
checkIsArrayType(tsType) {
|
|
3362
3411
|
if (tsType.flags & 524288) {
|
|
3363
3412
|
const objFlags = getObjectFlags(tsType);
|
|
@@ -3409,7 +3458,7 @@ var TypeResolver = class {
|
|
|
3409
3458
|
if (exprTsType.flags & import_typescript2.default.TypeFlags.Any) return false;
|
|
3410
3459
|
if (targetTsType.flags & import_typescript2.default.TypeFlags.Any) return false;
|
|
3411
3460
|
const result = this.checker.isTypeAssignableTo(exprTsType, targetTsType);
|
|
3412
|
-
if (this.logger.
|
|
3461
|
+
if (this.logger.isLevelEnabled(Level.Debug)) {
|
|
3413
3462
|
const exprStr = this.checker.typeToString(exprTsType);
|
|
3414
3463
|
const targetStr = this.checker.typeToString(targetTsType);
|
|
3415
3464
|
this.logger.debug(`isUnnecessaryCast: expr="${exprStr.slice(0, 120)}" target="${targetStr.slice(0, 120)}" assignable=${result} exprFlags=${exprTsType.flags} targetFlags=${targetTsType.flags}`);
|
|
@@ -3926,6 +3975,7 @@ function getStaticStringFromJSXValue(node) {
|
|
|
3926
3975
|
const expression = node.expression;
|
|
3927
3976
|
if (!expression) return null;
|
|
3928
3977
|
if (import_typescript4.default.isStringLiteral(expression)) return expression.text;
|
|
3978
|
+
if (import_typescript4.default.isNumericLiteral(expression)) return expression.text;
|
|
3929
3979
|
if (import_typescript4.default.isNoSubstitutionTemplateLiteral(expression)) return expression.text;
|
|
3930
3980
|
if (import_typescript4.default.isTemplateExpression(expression) && expression.templateSpans.length === 0) {
|
|
3931
3981
|
return expression.head.text;
|
|
@@ -10373,6 +10423,9 @@ function typeIncludesString(graph, node) {
|
|
|
10373
10423
|
function typeIsArray(graph, node) {
|
|
10374
10424
|
return graph.typeResolver.isArrayType(node);
|
|
10375
10425
|
}
|
|
10426
|
+
function typeIsStrictArray(graph, node) {
|
|
10427
|
+
return graph.typeResolver.isStrictArrayType(node);
|
|
10428
|
+
}
|
|
10376
10429
|
function getArrayElementKind(graph, node) {
|
|
10377
10430
|
return graph.typeResolver.getArrayElementKind(node);
|
|
10378
10431
|
}
|
|
@@ -20168,6 +20221,7 @@ var preferSetLookupInLoop = defineSolidRule({
|
|
|
20168
20221
|
options: options77,
|
|
20169
20222
|
check(graph, emit) {
|
|
20170
20223
|
const reported = /* @__PURE__ */ new Set();
|
|
20224
|
+
const graphHasTypes = hasTypeInfo(graph);
|
|
20171
20225
|
for (const method of LINEAR_SEARCH_METHODS) {
|
|
20172
20226
|
const calls = getCallsByMethodName(graph, method);
|
|
20173
20227
|
for (let i = 0, len = calls.length; i < len; i++) {
|
|
@@ -20185,6 +20239,7 @@ var preferSetLookupInLoop = defineSolidRule({
|
|
|
20185
20239
|
const loop = getEnclosingLoop(call.node);
|
|
20186
20240
|
if (!loop) continue;
|
|
20187
20241
|
if (!isDeclaredOutsideLoop2(loop, variable)) continue;
|
|
20242
|
+
if (graphHasTypes && !typeIsStrictArray(graph, callee.expression)) continue;
|
|
20188
20243
|
if (isStringLikeReceiver(graph, callee.expression, variable)) continue;
|
|
20189
20244
|
const key = `${loop.pos}:var:${variable.id}`;
|
|
20190
20245
|
if (reported.has(key)) continue;
|
|
@@ -29780,148 +29835,8 @@ function checkParagraphSpacing(graph, emit, policy, name) {
|
|
|
29780
29835
|
}
|
|
29781
29836
|
}
|
|
29782
29837
|
|
|
29783
|
-
// src/css/rules/a11y/css-policy-touch-target.ts
|
|
29784
|
-
var messages125 = {
|
|
29785
|
-
heightTooSmall: "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
|
|
29786
|
-
widthTooSmall: "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
|
|
29787
|
-
paddingTooSmall: "Horizontal padding `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`."
|
|
29788
|
-
};
|
|
29789
|
-
function classifyRule(rule) {
|
|
29790
|
-
if (rule.elementKinds.has("button")) return "button";
|
|
29791
|
-
if (rule.elementKinds.has("input")) return "input";
|
|
29792
|
-
return null;
|
|
29793
|
-
}
|
|
29794
|
-
var HEIGHT_PROPERTIES = /* @__PURE__ */ new Set(["height", "min-height"]);
|
|
29795
|
-
var WIDTH_PROPERTIES = /* @__PURE__ */ new Set(["width", "min-width"]);
|
|
29796
|
-
var HPADDING_PROPERTIES = /* @__PURE__ */ new Set(["padding-left", "padding-right", "padding-inline", "padding-inline-start", "padding-inline-end"]);
|
|
29797
|
-
var cssPolicyTouchTarget = defineCSSRule({
|
|
29798
|
-
id: "css-policy-touch-target",
|
|
29799
|
-
severity: "warn",
|
|
29800
|
-
messages: messages125,
|
|
29801
|
-
meta: {
|
|
29802
|
-
description: "Enforce minimum interactive element sizes per accessibility policy.",
|
|
29803
|
-
fixable: false,
|
|
29804
|
-
category: "css-a11y"
|
|
29805
|
-
},
|
|
29806
|
-
options: {},
|
|
29807
|
-
check(graph, emit) {
|
|
29808
|
-
const policy = getActivePolicy();
|
|
29809
|
-
if (policy === null) return;
|
|
29810
|
-
const name = getActivePolicyName() ?? "";
|
|
29811
|
-
const decls = graph.declarationsForProperties(
|
|
29812
|
-
"height",
|
|
29813
|
-
"min-height",
|
|
29814
|
-
"width",
|
|
29815
|
-
"min-width",
|
|
29816
|
-
"padding-left",
|
|
29817
|
-
"padding-right",
|
|
29818
|
-
"padding-inline",
|
|
29819
|
-
"padding-inline-start",
|
|
29820
|
-
"padding-inline-end"
|
|
29821
|
-
);
|
|
29822
|
-
for (let i = 0; i < decls.length; i++) {
|
|
29823
|
-
const d = decls[i];
|
|
29824
|
-
if (!d) continue;
|
|
29825
|
-
if (!d.rule) continue;
|
|
29826
|
-
const prop = d.property.toLowerCase();
|
|
29827
|
-
const kind = classifyRule(d.rule);
|
|
29828
|
-
if (!kind) continue;
|
|
29829
|
-
if (isVisuallyHiddenInput(d.rule)) continue;
|
|
29830
|
-
const px = parsePxValue(d.value);
|
|
29831
|
-
if (px === null) continue;
|
|
29832
|
-
if (HEIGHT_PROPERTIES.has(prop)) {
|
|
29833
|
-
const min = kind === "button" ? policy.minButtonHeight : policy.minInputHeight;
|
|
29834
|
-
if (px >= min) continue;
|
|
29835
|
-
emitCSSDiagnostic(
|
|
29836
|
-
emit,
|
|
29837
|
-
d.file.path,
|
|
29838
|
-
d.startLine,
|
|
29839
|
-
d.startColumn,
|
|
29840
|
-
cssPolicyTouchTarget,
|
|
29841
|
-
"heightTooSmall",
|
|
29842
|
-
resolveMessage(messages125.heightTooSmall, {
|
|
29843
|
-
property: d.property,
|
|
29844
|
-
value: d.value.trim(),
|
|
29845
|
-
resolved: String(Math.round(px * 100) / 100),
|
|
29846
|
-
min: String(min),
|
|
29847
|
-
element: kind,
|
|
29848
|
-
policy: name
|
|
29849
|
-
})
|
|
29850
|
-
);
|
|
29851
|
-
continue;
|
|
29852
|
-
}
|
|
29853
|
-
if (WIDTH_PROPERTIES.has(prop)) {
|
|
29854
|
-
const min = kind === "button" ? policy.minButtonWidth : policy.minTouchTarget;
|
|
29855
|
-
if (px >= min) continue;
|
|
29856
|
-
emitCSSDiagnostic(
|
|
29857
|
-
emit,
|
|
29858
|
-
d.file.path,
|
|
29859
|
-
d.startLine,
|
|
29860
|
-
d.startColumn,
|
|
29861
|
-
cssPolicyTouchTarget,
|
|
29862
|
-
"widthTooSmall",
|
|
29863
|
-
resolveMessage(messages125.widthTooSmall, {
|
|
29864
|
-
property: d.property,
|
|
29865
|
-
value: d.value.trim(),
|
|
29866
|
-
resolved: String(Math.round(px * 100) / 100),
|
|
29867
|
-
min: String(min),
|
|
29868
|
-
element: kind,
|
|
29869
|
-
policy: name
|
|
29870
|
-
})
|
|
29871
|
-
);
|
|
29872
|
-
continue;
|
|
29873
|
-
}
|
|
29874
|
-
if (kind === "button" && HPADDING_PROPERTIES.has(prop)) {
|
|
29875
|
-
if (px >= policy.minButtonHorizontalPadding) continue;
|
|
29876
|
-
emitCSSDiagnostic(
|
|
29877
|
-
emit,
|
|
29878
|
-
d.file.path,
|
|
29879
|
-
d.startLine,
|
|
29880
|
-
d.startColumn,
|
|
29881
|
-
cssPolicyTouchTarget,
|
|
29882
|
-
"paddingTooSmall",
|
|
29883
|
-
resolveMessage(messages125.paddingTooSmall, {
|
|
29884
|
-
value: d.value.trim(),
|
|
29885
|
-
resolved: String(Math.round(px * 100) / 100),
|
|
29886
|
-
min: String(policy.minButtonHorizontalPadding),
|
|
29887
|
-
element: kind,
|
|
29888
|
-
policy: name
|
|
29889
|
-
})
|
|
29890
|
-
);
|
|
29891
|
-
}
|
|
29892
|
-
}
|
|
29893
|
-
}
|
|
29894
|
-
});
|
|
29895
|
-
function isVisuallyHiddenInput(rule) {
|
|
29896
|
-
if (!hasPositionAbsoluteOrFixed(rule)) return false;
|
|
29897
|
-
if (!hasOpacityZero(rule)) return false;
|
|
29898
|
-
return true;
|
|
29899
|
-
}
|
|
29900
|
-
function hasPositionAbsoluteOrFixed(rule) {
|
|
29901
|
-
const positionDecls = rule.declarationIndex.get("position");
|
|
29902
|
-
if (!positionDecls || positionDecls.length === 0) return false;
|
|
29903
|
-
for (let i = 0; i < positionDecls.length; i++) {
|
|
29904
|
-
const decl = positionDecls[i];
|
|
29905
|
-
if (!decl) continue;
|
|
29906
|
-
const v = decl.value.trim().toLowerCase();
|
|
29907
|
-
if (v === "absolute" || v === "fixed") return true;
|
|
29908
|
-
}
|
|
29909
|
-
return false;
|
|
29910
|
-
}
|
|
29911
|
-
function hasOpacityZero(rule) {
|
|
29912
|
-
const opacityDecls = rule.declarationIndex.get("opacity");
|
|
29913
|
-
if (!opacityDecls || opacityDecls.length === 0) return false;
|
|
29914
|
-
for (let i = 0; i < opacityDecls.length; i++) {
|
|
29915
|
-
const decl = opacityDecls[i];
|
|
29916
|
-
if (!decl) continue;
|
|
29917
|
-
const v = decl.value.trim();
|
|
29918
|
-
if (v === "0") return true;
|
|
29919
|
-
}
|
|
29920
|
-
return false;
|
|
29921
|
-
}
|
|
29922
|
-
|
|
29923
29838
|
// src/css/rules/a11y/css-policy-typography.ts
|
|
29924
|
-
var
|
|
29839
|
+
var messages125 = {
|
|
29925
29840
|
fontTooSmall: "Font size `{{value}}` ({{resolved}}px) is below the `{{context}}` minimum of `{{min}}px` for policy `{{policy}}`.",
|
|
29926
29841
|
lineHeightTooSmall: "Line height `{{value}}` is below the `{{context}}` minimum of `{{min}}` for policy `{{policy}}`."
|
|
29927
29842
|
};
|
|
@@ -29957,7 +29872,7 @@ function resolveLineHeightContext(d, p) {
|
|
|
29957
29872
|
var cssPolicyTypography = defineCSSRule({
|
|
29958
29873
|
id: "css-policy-typography",
|
|
29959
29874
|
severity: "warn",
|
|
29960
|
-
messages:
|
|
29875
|
+
messages: messages125,
|
|
29961
29876
|
meta: {
|
|
29962
29877
|
description: "Enforce minimum font sizes and line heights per accessibility policy.",
|
|
29963
29878
|
fixable: false,
|
|
@@ -29984,7 +29899,7 @@ var cssPolicyTypography = defineCSSRule({
|
|
|
29984
29899
|
d.startColumn,
|
|
29985
29900
|
cssPolicyTypography,
|
|
29986
29901
|
"fontTooSmall",
|
|
29987
|
-
resolveMessage(
|
|
29902
|
+
resolveMessage(messages125.fontTooSmall, {
|
|
29988
29903
|
value: d.value.trim(),
|
|
29989
29904
|
resolved: String(Math.round(px * 100) / 100),
|
|
29990
29905
|
context,
|
|
@@ -30011,7 +29926,7 @@ var cssPolicyTypography = defineCSSRule({
|
|
|
30011
29926
|
d.startColumn,
|
|
30012
29927
|
cssPolicyTypography,
|
|
30013
29928
|
"lineHeightTooSmall",
|
|
30014
|
-
resolveMessage(
|
|
29929
|
+
resolveMessage(messages125.lineHeightTooSmall, {
|
|
30015
29930
|
value: d.value.trim(),
|
|
30016
29931
|
context,
|
|
30017
29932
|
min: String(min),
|
|
@@ -30029,7 +29944,7 @@ var ZERO_S = /(^|\s|,)0s($|\s|,)/;
|
|
|
30029
29944
|
var TIME_VALUE_G = /([0-9]*\.?[0-9]+)(ms|s)/g;
|
|
30030
29945
|
var AMPERSAND_G = /&/g;
|
|
30031
29946
|
var WHITESPACE_G = /\s+/g;
|
|
30032
|
-
var
|
|
29947
|
+
var messages126 = {
|
|
30033
29948
|
missingReducedMotion: "Animated selector `{{selector}}` lacks prefers-reduced-motion override."
|
|
30034
29949
|
};
|
|
30035
29950
|
function isAnimationDecl(p) {
|
|
@@ -30133,7 +30048,7 @@ function normalizeSelector2(s) {
|
|
|
30133
30048
|
var cssRequireReducedMotionOverride = defineCSSRule({
|
|
30134
30049
|
id: "css-require-reduced-motion-override",
|
|
30135
30050
|
severity: "warn",
|
|
30136
|
-
messages:
|
|
30051
|
+
messages: messages126,
|
|
30137
30052
|
meta: {
|
|
30138
30053
|
description: "Require reduced-motion override for animated selectors.",
|
|
30139
30054
|
fixable: false,
|
|
@@ -30196,20 +30111,20 @@ var cssRequireReducedMotionOverride = defineCSSRule({
|
|
|
30196
30111
|
d.startColumn,
|
|
30197
30112
|
cssRequireReducedMotionOverride,
|
|
30198
30113
|
"missingReducedMotion",
|
|
30199
|
-
resolveMessage(
|
|
30114
|
+
resolveMessage(messages126.missingReducedMotion, { selector: r.selectorText })
|
|
30200
30115
|
);
|
|
30201
30116
|
}
|
|
30202
30117
|
}
|
|
30203
30118
|
});
|
|
30204
30119
|
|
|
30205
30120
|
// src/css/rules/structure/css-no-empty-rule.ts
|
|
30206
|
-
var
|
|
30121
|
+
var messages127 = {
|
|
30207
30122
|
emptyRule: "Empty rule `{{selector}}` should be removed."
|
|
30208
30123
|
};
|
|
30209
30124
|
var cssNoEmptyRule = defineCSSRule({
|
|
30210
30125
|
id: "css-no-empty-rule",
|
|
30211
30126
|
severity: "warn",
|
|
30212
|
-
messages:
|
|
30127
|
+
messages: messages127,
|
|
30213
30128
|
meta: {
|
|
30214
30129
|
description: "Disallow empty CSS rules.",
|
|
30215
30130
|
fixable: false,
|
|
@@ -30227,20 +30142,20 @@ var cssNoEmptyRule = defineCSSRule({
|
|
|
30227
30142
|
rule.startColumn,
|
|
30228
30143
|
cssNoEmptyRule,
|
|
30229
30144
|
"emptyRule",
|
|
30230
|
-
resolveMessage(
|
|
30145
|
+
resolveMessage(messages127.emptyRule, { selector: rule.selectorText })
|
|
30231
30146
|
);
|
|
30232
30147
|
}
|
|
30233
30148
|
}
|
|
30234
30149
|
});
|
|
30235
30150
|
|
|
30236
30151
|
// src/css/rules/structure/css-no-unknown-container-name.ts
|
|
30237
|
-
var
|
|
30152
|
+
var messages128 = {
|
|
30238
30153
|
unknownContainer: "Unknown container name `{{name}}` in @container query."
|
|
30239
30154
|
};
|
|
30240
30155
|
var cssNoUnknownContainerName = defineCSSRule({
|
|
30241
30156
|
id: "css-no-unknown-container-name",
|
|
30242
30157
|
severity: "error",
|
|
30243
|
-
messages:
|
|
30158
|
+
messages: messages128,
|
|
30244
30159
|
meta: {
|
|
30245
30160
|
description: "Disallow unknown named containers in @container queries.",
|
|
30246
30161
|
fixable: false,
|
|
@@ -30260,20 +30175,20 @@ var cssNoUnknownContainerName = defineCSSRule({
|
|
|
30260
30175
|
1,
|
|
30261
30176
|
cssNoUnknownContainerName,
|
|
30262
30177
|
"unknownContainer",
|
|
30263
|
-
resolveMessage(
|
|
30178
|
+
resolveMessage(messages128.unknownContainer, { name })
|
|
30264
30179
|
);
|
|
30265
30180
|
}
|
|
30266
30181
|
}
|
|
30267
30182
|
});
|
|
30268
30183
|
|
|
30269
30184
|
// src/css/rules/structure/css-no-unused-container-name.ts
|
|
30270
|
-
var
|
|
30185
|
+
var messages129 = {
|
|
30271
30186
|
unusedContainer: "Container name `{{name}}` is declared but never queried."
|
|
30272
30187
|
};
|
|
30273
30188
|
var cssNoUnusedContainerName = defineCSSRule({
|
|
30274
30189
|
id: "css-no-unused-container-name",
|
|
30275
30190
|
severity: "warn",
|
|
30276
|
-
messages:
|
|
30191
|
+
messages: messages129,
|
|
30277
30192
|
meta: {
|
|
30278
30193
|
description: "Disallow unused named containers.",
|
|
30279
30194
|
fixable: false,
|
|
@@ -30296,7 +30211,7 @@ var cssNoUnusedContainerName = defineCSSRule({
|
|
|
30296
30211
|
d.startColumn,
|
|
30297
30212
|
cssNoUnusedContainerName,
|
|
30298
30213
|
"unusedContainer",
|
|
30299
|
-
resolveMessage(
|
|
30214
|
+
resolveMessage(messages129.unusedContainer, { name })
|
|
30300
30215
|
);
|
|
30301
30216
|
}
|
|
30302
30217
|
}
|
|
@@ -30304,13 +30219,13 @@ var cssNoUnusedContainerName = defineCSSRule({
|
|
|
30304
30219
|
});
|
|
30305
30220
|
|
|
30306
30221
|
// src/css/rules/structure/layer-requirement-for-component-rules.ts
|
|
30307
|
-
var
|
|
30222
|
+
var messages130 = {
|
|
30308
30223
|
missingLayer: "Rule `{{selector}}` is not inside any @layer block while this file uses @layer. Place component rules inside an explicit layer."
|
|
30309
30224
|
};
|
|
30310
30225
|
var layerRequirementForComponentRules = defineCSSRule({
|
|
30311
30226
|
id: "layer-requirement-for-component-rules",
|
|
30312
30227
|
severity: "warn",
|
|
30313
|
-
messages:
|
|
30228
|
+
messages: messages130,
|
|
30314
30229
|
meta: {
|
|
30315
30230
|
description: "Require style rules to be inside @layer when the file defines layers.",
|
|
30316
30231
|
fixable: false,
|
|
@@ -30329,7 +30244,7 @@ var layerRequirementForComponentRules = defineCSSRule({
|
|
|
30329
30244
|
rule.startColumn,
|
|
30330
30245
|
layerRequirementForComponentRules,
|
|
30331
30246
|
"missingLayer",
|
|
30332
|
-
resolveMessage(
|
|
30247
|
+
resolveMessage(messages130.missingLayer, {
|
|
30333
30248
|
selector: rule.selectorText
|
|
30334
30249
|
})
|
|
30335
30250
|
);
|
|
@@ -30369,7 +30284,6 @@ var rules2 = [
|
|
|
30369
30284
|
selectorMaxAttributeAndUniversal,
|
|
30370
30285
|
selectorMaxSpecificity,
|
|
30371
30286
|
cssPolicyTypography,
|
|
30372
|
-
cssPolicyTouchTarget,
|
|
30373
30287
|
cssPolicySpacing,
|
|
30374
30288
|
cssPolicyContrast
|
|
30375
30289
|
];
|
|
@@ -30706,21 +30620,34 @@ function detectTailwindEntry(files) {
|
|
|
30706
30620
|
}
|
|
30707
30621
|
return themeOnlyFallback;
|
|
30708
30622
|
}
|
|
30709
|
-
async function resolveTailwindValidator(files) {
|
|
30623
|
+
async function resolveTailwindValidator(files, logger) {
|
|
30710
30624
|
const entry = detectTailwindEntry(files);
|
|
30711
|
-
if (!entry)
|
|
30625
|
+
if (!entry) {
|
|
30626
|
+
logger?.info('tailwind: no entry file detected (no @import "tailwindcss" or @theme block found in CSS files)');
|
|
30627
|
+
return null;
|
|
30628
|
+
}
|
|
30629
|
+
logger?.info(`tailwind: entry file detected: ${entry.path}`);
|
|
30712
30630
|
try {
|
|
30713
30631
|
const base = (0, import_node_path2.dirname)(entry.path);
|
|
30714
30632
|
const resolved = resolveTailwindNodePath(base);
|
|
30715
|
-
if (resolved === null)
|
|
30633
|
+
if (resolved === null) {
|
|
30634
|
+
logger?.warning(`tailwind: @tailwindcss/node not resolvable walking up from ${base}`);
|
|
30635
|
+
return null;
|
|
30636
|
+
}
|
|
30637
|
+
logger?.info(`tailwind: @tailwindcss/node resolved to ${resolved}`);
|
|
30716
30638
|
const mod = await import(resolved);
|
|
30717
|
-
if (typeof mod.__unstable__loadDesignSystem !== "function")
|
|
30639
|
+
if (typeof mod.__unstable__loadDesignSystem !== "function") {
|
|
30640
|
+
logger?.warning("tailwind: @tailwindcss/node module missing __unstable__loadDesignSystem export");
|
|
30641
|
+
return null;
|
|
30642
|
+
}
|
|
30718
30643
|
const design = await mod.__unstable__loadDesignSystem(
|
|
30719
30644
|
entry.content,
|
|
30720
30645
|
{ base }
|
|
30721
30646
|
);
|
|
30647
|
+
logger?.info("tailwind: design system loaded successfully");
|
|
30722
30648
|
return createLiveValidator(design);
|
|
30723
|
-
} catch {
|
|
30649
|
+
} catch (err) {
|
|
30650
|
+
logger?.warning(`tailwind: failed to load design system: ${err instanceof Error ? err.message : String(err)}`);
|
|
30724
30651
|
return null;
|
|
30725
30652
|
}
|
|
30726
30653
|
}
|
|
@@ -31740,6 +31667,9 @@ function readBaselineOffsetFacts(graph, node) {
|
|
|
31740
31667
|
function readElementRef(graph, node) {
|
|
31741
31668
|
return readElementRefById(graph, node.solidFile, node.elementId);
|
|
31742
31669
|
}
|
|
31670
|
+
function readHostElementRef(graph, node) {
|
|
31671
|
+
return graph.hostElementRefsByNode.get(node) ?? null;
|
|
31672
|
+
}
|
|
31743
31673
|
function readElementRefById(graph, solidFile, elementId) {
|
|
31744
31674
|
const refs = graph.elementRefsBySolidFileAndId.get(solidFile);
|
|
31745
31675
|
if (!refs) return null;
|
|
@@ -32805,7 +32735,7 @@ function publishLayoutPerfStatsForTest(stats) {
|
|
|
32805
32735
|
}
|
|
32806
32736
|
function maybeLogLayoutPerf(stats, log) {
|
|
32807
32737
|
if (process.env["SOLID_LINT_LAYOUT_PROFILE"] !== "1") return;
|
|
32808
|
-
if (!log || !log.
|
|
32738
|
+
if (!log || !log.isLevelEnabled(Level.Debug)) return;
|
|
32809
32739
|
const view = snapshotLayoutPerfStats(stats);
|
|
32810
32740
|
log.debug(
|
|
32811
32741
|
`[layout] elements=${view.elementsScanned} candidates=${view.selectorCandidatesChecked} compiledSelectors=${view.compiledSelectorCount} unsupportedSelectors=${view.selectorsRejectedUnsupported} conditionalSelectors=${view.selectorsGuardedConditional} ancestryChecks=${view.ancestryChecks} edges=${view.matchEdgesCreated} collected=${view.casesCollected} cases=${view.casesScored} rejectLowEvidence=${view.casesRejectedLowEvidence} rejectThreshold=${view.casesRejectedThreshold} rejectUndecidable=${view.casesRejectedUndecidable} rejectIdentifiability=${view.casesRejectedIdentifiability} undecidableInterval=${view.undecidableInterval} conditionalSignalRatio=${Math.round(view.conditionalSignalRatio * 1e3) / 1e3} conditionalSignals=${view.conditionalSignals} totalSignals=${view.totalSignals} cohortUnimodalFalse=${view.cohortUnimodalFalse} factorCoverageMean=${Math.round(view.factorCoverageMean * 1e3) / 1e3} posteriorWidthP95=${Math.round(view.posteriorWidthP95 * 1e3) / 1e3} uncertaintyEscalations=${view.uncertaintyEscalations} snapshots=${view.signalSnapshotsBuilt} snapshotHits=${view.signalSnapshotCacheHits} measurementIndexHits=${view.measurementIndexHits} contexts=${view.contextsClassified} diagnostics=${view.diagnosticsEmitted} selectorIndexMs=${Math.round(view.selectorIndexMs * 100) / 100} selectorMatchMs=${Math.round(view.selectorMatchMs * 100) / 100} cascadeBuildMs=${Math.round(view.cascadeBuildMs * 100) / 100} caseBuildMs=${Math.round(view.caseBuildMs * 100) / 100} scoringMs=${Math.round(view.scoringMs * 100) / 100} elapsedMs=${Math.round(view.elapsedMs * 100) / 100}`
|
|
@@ -32853,17 +32783,17 @@ function createLayoutComponentHostResolver(solids, moduleResolver, logger = noop
|
|
|
32853
32783
|
if (cached !== void 0) return cached;
|
|
32854
32784
|
const binding = resolveTagBinding(normalizedFile, tag);
|
|
32855
32785
|
if (binding === null) {
|
|
32856
|
-
if (logger.
|
|
32786
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] resolveHost(${tag}): binding=null`);
|
|
32857
32787
|
hostByTagCache.set(cacheKey, null);
|
|
32858
32788
|
return null;
|
|
32859
32789
|
}
|
|
32860
32790
|
if (binding.kind === "component") {
|
|
32861
|
-
if (logger.
|
|
32791
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] resolveHost(${tag}): component, tagName=${binding.host.descriptor.tagName}, attrs=[${[...binding.host.descriptor.staticAttributes.keys()]}]`);
|
|
32862
32792
|
hostByTagCache.set(cacheKey, binding.host);
|
|
32863
32793
|
return binding.host;
|
|
32864
32794
|
}
|
|
32865
32795
|
const host = binding.base ? binding.base.host : null;
|
|
32866
|
-
if (logger.
|
|
32796
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] resolveHost(${tag}): namespace, base=${host?.descriptor.tagName ?? "null"}`);
|
|
32867
32797
|
hostByTagCache.set(cacheKey, host);
|
|
32868
32798
|
return host;
|
|
32869
32799
|
},
|
|
@@ -32876,26 +32806,26 @@ function createLayoutComponentHostResolver(solids, moduleResolver, logger = noop
|
|
|
32876
32806
|
}
|
|
32877
32807
|
};
|
|
32878
32808
|
function resolveComponentHostEntry(entry) {
|
|
32879
|
-
if (entry.resolution === "resolved")
|
|
32880
|
-
|
|
32809
|
+
if (entry.resolution === "resolved") {
|
|
32810
|
+
return { descriptor: entry.descriptor, hostElementRef: entry.hostElementRef };
|
|
32811
|
+
}
|
|
32812
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] resolveComponentHostEntry: deferred innerTag=${entry.innerTag}, file=${entry.filePath}, attrs=[${[...entry.staticAttributes.keys()]}]`);
|
|
32881
32813
|
const innerBinding = resolveLocalIdentifierBinding(entry.filePath, entry.innerTag);
|
|
32882
|
-
if (logger.
|
|
32814
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] innerBinding=${innerBinding === null ? "null" : innerBinding.kind}`);
|
|
32883
32815
|
const innerHost = extractHostFromBinding(innerBinding);
|
|
32884
|
-
if (logger.
|
|
32885
|
-
let tagName = innerHost !== null ? innerHost.tagName : null;
|
|
32816
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] innerHost=${innerHost === null ? "null" : `tagName=${innerHost.descriptor.tagName}, attrs=[${[...innerHost.descriptor.staticAttributes.keys()]}]`}`);
|
|
32817
|
+
let tagName = innerHost !== null ? innerHost.descriptor.tagName : null;
|
|
32886
32818
|
if (tagName === null) {
|
|
32887
32819
|
tagName = resolveTagNameFromPolymorphicProp(entry.staticAttributes);
|
|
32888
|
-
if (logger.
|
|
32820
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] polymorphic fallback: tagName=${tagName}`);
|
|
32889
32821
|
}
|
|
32890
|
-
const staticAttributes = innerHost !== null ? mergeStaticAttributes(entry.staticAttributes, innerHost.staticAttributes) : entry.staticAttributes;
|
|
32891
|
-
const staticClassTokens = innerHost !== null ? mergeStaticClassTokens(entry.staticClassTokens, innerHost.staticClassTokens) : entry.staticClassTokens;
|
|
32892
|
-
const forwardsChildren = entry.forwardsChildren || innerHost !== null && innerHost.forwardsChildren;
|
|
32893
|
-
if (logger.
|
|
32822
|
+
const staticAttributes = innerHost !== null ? mergeStaticAttributes(entry.staticAttributes, innerHost.descriptor.staticAttributes) : entry.staticAttributes;
|
|
32823
|
+
const staticClassTokens = innerHost !== null ? mergeStaticClassTokens(entry.staticClassTokens, innerHost.descriptor.staticClassTokens) : entry.staticClassTokens;
|
|
32824
|
+
const forwardsChildren = entry.forwardsChildren || innerHost !== null && innerHost.descriptor.forwardsChildren;
|
|
32825
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] resolved: tagName=${tagName}, attrs=[${[...staticAttributes.keys()]}], classes=[${staticClassTokens}]`);
|
|
32894
32826
|
return {
|
|
32895
|
-
tagName,
|
|
32896
|
-
|
|
32897
|
-
staticClassTokens,
|
|
32898
|
-
forwardsChildren
|
|
32827
|
+
descriptor: { tagName, staticAttributes, staticClassTokens, forwardsChildren },
|
|
32828
|
+
hostElementRef: innerHost?.hostElementRef ?? null
|
|
32899
32829
|
};
|
|
32900
32830
|
}
|
|
32901
32831
|
function extractHostFromBinding(binding) {
|
|
@@ -32936,10 +32866,7 @@ function createLayoutComponentHostResolver(solids, moduleResolver, logger = noop
|
|
|
32936
32866
|
if (hostEntry) {
|
|
32937
32867
|
const resolved = resolveComponentHostEntry(hostEntry);
|
|
32938
32868
|
if (resolved !== null) {
|
|
32939
|
-
const binding = {
|
|
32940
|
-
kind: "component",
|
|
32941
|
-
host: resolved
|
|
32942
|
-
};
|
|
32869
|
+
const binding = { kind: "component", host: resolved };
|
|
32943
32870
|
localBindingCache.set(key, binding);
|
|
32944
32871
|
resolvingLocal.delete(key);
|
|
32945
32872
|
return binding;
|
|
@@ -33000,7 +32927,7 @@ function createLayoutComponentHostResolver(solids, moduleResolver, logger = noop
|
|
|
33000
32927
|
const baseExpression = toExpressionArgument(firstArg);
|
|
33001
32928
|
if (baseExpression === null) return null;
|
|
33002
32929
|
const baseBinding = resolveBindingFromExpression(filePath, baseExpression);
|
|
33003
|
-
if (logger.
|
|
32930
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] Object.assign base: ${baseBinding === null ? "null" : baseBinding.kind}${baseBinding?.kind === "component" ? `, tagName=${baseBinding.host.descriptor.tagName}` : ""}`);
|
|
33004
32931
|
let baseComponent = null;
|
|
33005
32932
|
const members = /* @__PURE__ */ new Map();
|
|
33006
32933
|
if (baseBinding && baseBinding.kind === "component") {
|
|
@@ -33026,7 +32953,7 @@ function createLayoutComponentHostResolver(solids, moduleResolver, logger = noop
|
|
|
33026
32953
|
if (!import_typescript126.default.isObjectLiteralExpression(argument)) continue;
|
|
33027
32954
|
appendObjectExpressionMembers(filePath, argument, members);
|
|
33028
32955
|
}
|
|
33029
|
-
if (logger.
|
|
32956
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] Object.assign result: base=${baseComponent === null ? "null" : `tagName=${baseComponent.host.descriptor.tagName}`}, members=[${[...members.keys()]}]`);
|
|
33030
32957
|
if (baseComponent === null && members.size === 0) return null;
|
|
33031
32958
|
return {
|
|
33032
32959
|
kind: "namespace",
|
|
@@ -33068,7 +32995,7 @@ function createLayoutComponentHostResolver(solids, moduleResolver, logger = noop
|
|
|
33068
32995
|
}
|
|
33069
32996
|
function resolveBindingFromImport(filePath, importBinding) {
|
|
33070
32997
|
const resolvedModule = moduleResolver.resolveSolid(filePath, importBinding.source) ?? resolveAndIndexExternalModule(filePath, importBinding.source);
|
|
33071
|
-
if (logger.
|
|
32998
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] resolveBindingFromImport: source=${importBinding.source}, kind=${importBinding.kind}, resolvedModule=${resolvedModule}`);
|
|
33072
32999
|
if (resolvedModule === null) return null;
|
|
33073
33000
|
const normalized = (0, import_node_path4.resolve)(resolvedModule);
|
|
33074
33001
|
if (importBinding.kind === "namespace") {
|
|
@@ -33077,7 +33004,7 @@ function createLayoutComponentHostResolver(solids, moduleResolver, logger = noop
|
|
|
33077
33004
|
const exportName = importBinding.kind === "default" ? "default" : importBinding.importedName;
|
|
33078
33005
|
if (exportName === null) return null;
|
|
33079
33006
|
const result = resolveExportBinding(normalized, exportName);
|
|
33080
|
-
if (logger.
|
|
33007
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[component-host] export ${exportName}: ${result === null ? "null" : result.kind}`);
|
|
33081
33008
|
return result;
|
|
33082
33009
|
}
|
|
33083
33010
|
function resolveAndIndexExternalModule(importerFile, importSource) {
|
|
@@ -33271,6 +33198,7 @@ function collectComponentHosts(graph) {
|
|
|
33271
33198
|
}
|
|
33272
33199
|
function resolveComponentHostEntryForFunction(graph, fn) {
|
|
33273
33200
|
let entry = null;
|
|
33201
|
+
let hostElementRefAgreed = true;
|
|
33274
33202
|
const bodyEntry = resolveHostEntryFromFunctionBody(graph, fn);
|
|
33275
33203
|
if (bodyEntry !== null) {
|
|
33276
33204
|
entry = bodyEntry;
|
|
@@ -33286,9 +33214,17 @@ function resolveComponentHostEntryForFunction(graph, fn) {
|
|
|
33286
33214
|
entry = returnEntry;
|
|
33287
33215
|
continue;
|
|
33288
33216
|
}
|
|
33289
|
-
if (areComponentHostEntriesEqual(entry, returnEntry))
|
|
33217
|
+
if (areComponentHostEntriesEqual(entry, returnEntry)) {
|
|
33218
|
+
if (hostElementRefAgreed && entry.resolution === "resolved" && returnEntry.resolution === "resolved" && entry.hostElementRef !== returnEntry.hostElementRef) {
|
|
33219
|
+
hostElementRefAgreed = false;
|
|
33220
|
+
}
|
|
33221
|
+
continue;
|
|
33222
|
+
}
|
|
33290
33223
|
return null;
|
|
33291
33224
|
}
|
|
33225
|
+
if (!hostElementRefAgreed && entry !== null && entry.resolution === "resolved") {
|
|
33226
|
+
return { resolution: "resolved", descriptor: entry.descriptor, hostElementRef: null };
|
|
33227
|
+
}
|
|
33292
33228
|
return entry;
|
|
33293
33229
|
}
|
|
33294
33230
|
function resolveHostEntryFromFunctionBody(graph, fn) {
|
|
@@ -33316,7 +33252,8 @@ function resolveHostEntryFromJSXElement(graph, node) {
|
|
|
33316
33252
|
staticAttributes: collectStaticAttributes(element),
|
|
33317
33253
|
staticClassTokens: getStaticClassTokensForElementEntity(graph, element),
|
|
33318
33254
|
forwardsChildren: detectChildrenForwarding(element)
|
|
33319
|
-
}
|
|
33255
|
+
},
|
|
33256
|
+
hostElementRef: { solid: graph, element }
|
|
33320
33257
|
};
|
|
33321
33258
|
}
|
|
33322
33259
|
if (isContextProviderTag(element.tag)) {
|
|
@@ -34026,7 +33963,7 @@ function matchesChain(matcher, node, index, perf, fileRootElements, logger) {
|
|
|
34026
33963
|
ancestor = ancestor.parentElementNode;
|
|
34027
33964
|
}
|
|
34028
33965
|
if (fileRootElements !== null) {
|
|
34029
|
-
if (logger.
|
|
33966
|
+
if (logger.isLevelEnabled(Level.Trace)) {
|
|
34030
33967
|
const compoundDesc = describeCompound(targetCompound);
|
|
34031
33968
|
logger.trace(`[selector-match] fallback: node=${node.key} tag=${node.tagName} checking ${fileRootElements.length} roots for compound=${compoundDesc}`);
|
|
34032
33969
|
}
|
|
@@ -34037,11 +33974,11 @@ function matchesChain(matcher, node, index, perf, fileRootElements, logger) {
|
|
|
34037
33974
|
if (root.solidFile !== node.solidFile) continue;
|
|
34038
33975
|
perf.ancestryChecks++;
|
|
34039
33976
|
const compoundResult = matchesCompound(root, targetCompound);
|
|
34040
|
-
if (logger.
|
|
33977
|
+
if (logger.isLevelEnabled(Level.Trace) && compoundResult === "no-match") {
|
|
34041
33978
|
logger.trace(`[selector-match] fallback MISS: root=${root.key} tag=${root.tagName} attrs=[${[...root.attributes.entries()].map(([k, v]) => `${k}=${v}`).join(",")}]`);
|
|
34042
33979
|
}
|
|
34043
33980
|
if (compoundResult !== "no-match") {
|
|
34044
|
-
if (logger.
|
|
33981
|
+
if (logger.isLevelEnabled(Level.Debug)) {
|
|
34045
33982
|
const compoundDesc = describeCompound(targetCompound);
|
|
34046
33983
|
logger.debug(`[selector-match] fallback HIT: node=${node.key} tag=${node.tagName} matched root=${root.key} tag=${root.tagName} compound=${compoundDesc} isFinal=${isFinal}`);
|
|
34047
33984
|
}
|
|
@@ -37243,7 +37180,7 @@ function appendMatchingEdgesFromSelectorIds(ctx, selectorIds, node, applies, app
|
|
|
37243
37180
|
};
|
|
37244
37181
|
applies.push(edge);
|
|
37245
37182
|
ctx.perf.matchEdgesCreated++;
|
|
37246
|
-
if (ctx.logger.
|
|
37183
|
+
if (ctx.logger.isLevelEnabled(Level.Trace)) {
|
|
37247
37184
|
ctx.logger.trace(
|
|
37248
37185
|
`[cascade] edge node=${node.key} selector=${selector.id} match=${matchResult} conditional=${edge.conditionalMatch} selector-raw=${selector.raw.slice(0, 80)}`
|
|
37249
37186
|
);
|
|
@@ -37686,7 +37623,7 @@ function getTextualContentState(element, memo, compositionMetaByElementId, logge
|
|
|
37686
37623
|
if (!child) continue;
|
|
37687
37624
|
if (child.kind === "expression") {
|
|
37688
37625
|
if (isStructuralExpression(child.node)) {
|
|
37689
|
-
if (logger.
|
|
37626
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id} \u2192 unknown (structural expression child)`);
|
|
37690
37627
|
memo.set(element.id, 2 /* Unknown */);
|
|
37691
37628
|
return 2 /* Unknown */;
|
|
37692
37629
|
}
|
|
@@ -37708,11 +37645,11 @@ function getTextualContentState(element, memo, compositionMetaByElementId, logge
|
|
|
37708
37645
|
if (!child.isDomElement) {
|
|
37709
37646
|
const childMeta = compositionMetaByElementId.get(child.id);
|
|
37710
37647
|
if (childMeta !== void 0 && isControlTag(childMeta.tagName)) {
|
|
37711
|
-
if (logger.
|
|
37648
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id}: non-DOM child ${child.tag}#${child.id} resolves to control tag=${childMeta.tagName}, skipping`);
|
|
37712
37649
|
continue;
|
|
37713
37650
|
}
|
|
37714
37651
|
if (childState !== 1 /* No */) {
|
|
37715
|
-
if (logger.
|
|
37652
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id}: non-DOM child ${child.tag ?? child.id}#${child.id} has state=${childState} \u2192 childHasUnknown`);
|
|
37716
37653
|
childHasUnknown = true;
|
|
37717
37654
|
}
|
|
37718
37655
|
continue;
|
|
@@ -37725,12 +37662,12 @@ function getTextualContentState(element, memo, compositionMetaByElementId, logge
|
|
|
37725
37662
|
if (childState === 3 /* DynamicText */) childHasDynamicText = true;
|
|
37726
37663
|
}
|
|
37727
37664
|
if (childHasUnknown) {
|
|
37728
|
-
if (logger.
|
|
37665
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id} \u2192 unknown (child has unknown)`);
|
|
37729
37666
|
memo.set(element.id, 2 /* Unknown */);
|
|
37730
37667
|
return 2 /* Unknown */;
|
|
37731
37668
|
}
|
|
37732
37669
|
if (hasTextOnlyExpression || childHasDynamicText) {
|
|
37733
|
-
if (logger.
|
|
37670
|
+
if (logger.isLevelEnabled(Level.Trace)) logger.trace(`[textual-content] element=${element.tagName ?? element.tag}#${element.id} \u2192 dynamic-text`);
|
|
37734
37671
|
memo.set(element.id, 3 /* DynamicText */);
|
|
37735
37672
|
return 3 /* DynamicText */;
|
|
37736
37673
|
}
|
|
@@ -37752,15 +37689,16 @@ function collectLayoutElementRecordsForSolid(solid, selectorRequirements, inline
|
|
|
37752
37689
|
const meta = compositionMetaByElementId.get(element.id);
|
|
37753
37690
|
if (!meta || !meta.participates) continue;
|
|
37754
37691
|
const localClassTokens = selectorRequirements.needsClassTokens ? getStaticClassTokensForElementEntity(solid, element) : EMPTY_STRING_LIST3;
|
|
37755
|
-
const classTokens = mergeClassTokens(localClassTokens, meta.
|
|
37692
|
+
const classTokens = mergeClassTokens(localClassTokens, meta.resolvedHost?.descriptor.staticClassTokens);
|
|
37756
37693
|
const classTokenSet = classTokens.length === 0 ? EMPTY_CLASS_TOKEN_SET : createClassTokenSet(classTokens);
|
|
37757
37694
|
const inlineStyleKeys = getStaticStyleKeysForElement(solid, element.id);
|
|
37758
37695
|
const localAttributes = selectorRequirements.needsAttributes ? collectStaticAttributes(element) : EMPTY_ATTRIBUTES2;
|
|
37759
|
-
const attributes = mergeAttributes(localAttributes, meta.
|
|
37696
|
+
const attributes = mergeAttributes(localAttributes, meta.resolvedHost?.descriptor.staticAttributes);
|
|
37760
37697
|
const selectorDispatchKeys = buildSelectorDispatchKeys(attributes, classTokens);
|
|
37761
37698
|
const inlineStyleValues = inlineStyleValuesByElementId.get(element.id) ?? EMPTY_INLINE_STYLE_VALUES;
|
|
37762
37699
|
const textualContent = getTextualContentState(element, textContentMemo, compositionMetaByElementId, logger);
|
|
37763
37700
|
const parentElementId = resolveComposedParentElementId(element, compositionMetaByElementId);
|
|
37701
|
+
const hostElementRef = meta.resolvedHost?.hostElementRef ?? null;
|
|
37764
37702
|
out.push({
|
|
37765
37703
|
element,
|
|
37766
37704
|
key: toLayoutElementKey(solid.file, element.id),
|
|
@@ -37773,7 +37711,8 @@ function collectLayoutElementRecordsForSolid(solid, selectorRequirements, inline
|
|
|
37773
37711
|
selectorDispatchKeys,
|
|
37774
37712
|
inlineStyleValues,
|
|
37775
37713
|
textualContent,
|
|
37776
|
-
parentElementId
|
|
37714
|
+
parentElementId,
|
|
37715
|
+
hostElementRef
|
|
37777
37716
|
});
|
|
37778
37717
|
}
|
|
37779
37718
|
return out;
|
|
@@ -37783,7 +37722,7 @@ function collectCompositionMetaByElementId(solid, componentHostResolver) {
|
|
|
37783
37722
|
for (let i = 0; i < solid.jsxElements.length; i++) {
|
|
37784
37723
|
const element = solid.jsxElements[i];
|
|
37785
37724
|
if (!element) continue;
|
|
37786
|
-
const
|
|
37725
|
+
const resolvedHost = resolveHostForElement(
|
|
37787
37726
|
componentHostResolver,
|
|
37788
37727
|
solid.file,
|
|
37789
37728
|
element
|
|
@@ -37792,30 +37731,30 @@ function collectCompositionMetaByElementId(solid, componentHostResolver) {
|
|
|
37792
37731
|
componentHostResolver,
|
|
37793
37732
|
solid.file,
|
|
37794
37733
|
element,
|
|
37795
|
-
|
|
37734
|
+
resolvedHost
|
|
37796
37735
|
);
|
|
37797
37736
|
const participates = element.tag !== null && !isTransparentPrimitive;
|
|
37798
|
-
const tag = resolveEffectiveTag(element,
|
|
37737
|
+
const tag = resolveEffectiveTag(element, resolvedHost?.descriptor ?? null);
|
|
37799
37738
|
const tagName = tag ? tag.toLowerCase() : null;
|
|
37800
37739
|
out.set(element.id, {
|
|
37801
37740
|
element,
|
|
37802
37741
|
participates,
|
|
37803
37742
|
tag,
|
|
37804
37743
|
tagName,
|
|
37805
|
-
|
|
37744
|
+
resolvedHost
|
|
37806
37745
|
});
|
|
37807
37746
|
}
|
|
37808
37747
|
return out;
|
|
37809
37748
|
}
|
|
37810
|
-
function
|
|
37749
|
+
function resolveHostForElement(componentHostResolver, solidFile, element) {
|
|
37811
37750
|
if (element.tag === null) return null;
|
|
37812
37751
|
if (element.isDomElement) return null;
|
|
37813
37752
|
return componentHostResolver.resolveHost(solidFile, element.tag);
|
|
37814
37753
|
}
|
|
37815
|
-
function resolveTransparentPrimitiveStatus(componentHostResolver, solidFile, element,
|
|
37754
|
+
function resolveTransparentPrimitiveStatus(componentHostResolver, solidFile, element, resolvedHost) {
|
|
37816
37755
|
if (element.tag === null) return false;
|
|
37817
37756
|
if (element.isDomElement) return false;
|
|
37818
|
-
if (
|
|
37757
|
+
if (resolvedHost !== null) return false;
|
|
37819
37758
|
return componentHostResolver.isTransparentPrimitive(solidFile, element.tag);
|
|
37820
37759
|
}
|
|
37821
37760
|
function resolveEffectiveTag(element, hostDescriptor) {
|
|
@@ -37933,6 +37872,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
|
|
|
37933
37872
|
const childrenByParentNodeMutable = /* @__PURE__ */ new Map();
|
|
37934
37873
|
const elementBySolidFileAndIdMutable = /* @__PURE__ */ new Map();
|
|
37935
37874
|
const elementRefsBySolidFileAndIdMutable = /* @__PURE__ */ new Map();
|
|
37875
|
+
const hostElementRefsByNodeMutable = /* @__PURE__ */ new Map();
|
|
37936
37876
|
const appliesByElementNodeMutable = /* @__PURE__ */ new Map();
|
|
37937
37877
|
const selectorsById = /* @__PURE__ */ new Map();
|
|
37938
37878
|
const monitoredDeclarationsBySelectorId = /* @__PURE__ */ new Map();
|
|
@@ -37969,7 +37909,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
|
|
|
37969
37909
|
const moduleResolver = createLayoutModuleResolver(solids, css);
|
|
37970
37910
|
const componentHostResolver = createLayoutComponentHostResolver(solids, moduleResolver, logger);
|
|
37971
37911
|
const cssScopeBySolidFile = collectCSSScopeBySolidFile(solids, css, moduleResolver);
|
|
37972
|
-
if (logger.
|
|
37912
|
+
if (logger.isLevelEnabled(Level.Trace)) {
|
|
37973
37913
|
for (const [solidFile, scopePaths] of cssScopeBySolidFile) {
|
|
37974
37914
|
if (scopePaths.length > 0) {
|
|
37975
37915
|
let names = "";
|
|
@@ -38064,6 +38004,9 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
|
|
|
38064
38004
|
isControl: isControlTag(record.tagName),
|
|
38065
38005
|
isReplaced: isReplacedTag(record.tagName)
|
|
38066
38006
|
};
|
|
38007
|
+
if (record.hostElementRef !== null) {
|
|
38008
|
+
hostElementRefsByNodeMutable.set(node, record.hostElementRef);
|
|
38009
|
+
}
|
|
38067
38010
|
elements.push(node);
|
|
38068
38011
|
elementById.set(record.element.id, node);
|
|
38069
38012
|
nodeByElementId.set(record.element.id, node);
|
|
@@ -38085,7 +38028,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
|
|
|
38085
38028
|
}
|
|
38086
38029
|
}
|
|
38087
38030
|
}
|
|
38088
|
-
if (logger.
|
|
38031
|
+
if (logger.isLevelEnabled(Level.Debug)) {
|
|
38089
38032
|
for (const [file, roots] of rootElementsByFile) {
|
|
38090
38033
|
const descs = roots.map((r) => `${r.key}(tag=${r.tagName}, attrs=[${[...r.attributes.entries()].map(([k, v]) => `${k}=${v}`).join(",")}])`);
|
|
38091
38034
|
logger.debug(`[build] rootElementsByFile file=${file} count=${roots.length}: ${descs.join(", ")}`);
|
|
@@ -38128,7 +38071,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
|
|
|
38128
38071
|
appliesByNode.set(node, edges);
|
|
38129
38072
|
}
|
|
38130
38073
|
perf.cascadeBuildMs = performance.now() - cascadeStartedAt;
|
|
38131
|
-
if (logger.
|
|
38074
|
+
if (logger.isLevelEnabled(Level.Trace)) {
|
|
38132
38075
|
for (let i = 0; i < elements.length; i++) {
|
|
38133
38076
|
const node = elements[i];
|
|
38134
38077
|
if (!node) continue;
|
|
@@ -38184,6 +38127,7 @@ function buildLayoutGraph(solids, css, logger = noopLogger) {
|
|
|
38184
38127
|
childrenByParentNode: childrenByParentNodeMutable,
|
|
38185
38128
|
elementBySolidFileAndId: elementBySolidFileAndIdMutable,
|
|
38186
38129
|
elementRefsBySolidFileAndId: elementRefsBySolidFileAndIdMutable,
|
|
38130
|
+
hostElementRefsByNode: hostElementRefsByNodeMutable,
|
|
38187
38131
|
appliesByNode,
|
|
38188
38132
|
selectorCandidatesByNode,
|
|
38189
38133
|
selectorsById,
|
|
@@ -38584,7 +38528,7 @@ function computeFlowParticipationFact(snapshot) {
|
|
|
38584
38528
|
}
|
|
38585
38529
|
function buildContextIndex(childrenByParentNode, snapshotByElementNode, perf, logger) {
|
|
38586
38530
|
const out = /* @__PURE__ */ new Map();
|
|
38587
|
-
const trace = logger.
|
|
38531
|
+
const trace = logger.isLevelEnabled(Level.Trace);
|
|
38588
38532
|
for (const [parent, children] of childrenByParentNode) {
|
|
38589
38533
|
if (children.length < 2) continue;
|
|
38590
38534
|
const snapshot = snapshotByElementNode.get(parent);
|
|
@@ -39403,7 +39347,7 @@ function runLayoutDetector(context, detector) {
|
|
|
39403
39347
|
result.evidence.posteriorLower,
|
|
39404
39348
|
result.evidence.posteriorUpper
|
|
39405
39349
|
);
|
|
39406
|
-
if (log.
|
|
39350
|
+
if (log.isLevelEnabled(Level.Debug)) {
|
|
39407
39351
|
log.debug(
|
|
39408
39352
|
`[${detector.id}] accept case=${i} severity=${result.evidence.severity.toFixed(2)} confidence=${result.evidence.confidence.toFixed(2)} posterior=[${result.evidence.posteriorLower.toFixed(3)},${result.evidence.posteriorUpper.toFixed(3)}] evidenceMass=${result.evidence.evidenceMass.toFixed(3)} context=${result.evidence.contextKind} offset=${result.evidence.estimatedOffsetPx?.toFixed(2) ?? "null"} topFactors=[${result.evidence.topFactors.join(",")}] causes=[${result.evidence.causes.join("; ")}]`
|
|
39409
39353
|
);
|
|
@@ -39412,7 +39356,7 @@ function runLayoutDetector(context, detector) {
|
|
|
39412
39356
|
continue;
|
|
39413
39357
|
}
|
|
39414
39358
|
recordPolicyMetrics(context, result.evidenceMass, result.posteriorLower, result.posteriorUpper);
|
|
39415
|
-
if (log.
|
|
39359
|
+
if (log.isLevelEnabled(Level.Debug)) {
|
|
39416
39360
|
log.debug(
|
|
39417
39361
|
`[${detector.id}] reject case=${i} reason=${result.reason} detail=${result.detail ?? "none"} posterior=[${result.posteriorLower.toFixed(3)},${result.posteriorUpper.toFixed(3)}] evidenceMass=${result.evidenceMass.toFixed(3)}`
|
|
39418
39362
|
);
|
|
@@ -39508,13 +39452,13 @@ function emitLayoutDiagnostic(layout, node, emit, ruleId, messageId, template, s
|
|
|
39508
39452
|
}
|
|
39509
39453
|
|
|
39510
39454
|
// src/cross-file/rules/undefined-css-class.ts
|
|
39511
|
-
var
|
|
39455
|
+
var messages131 = {
|
|
39512
39456
|
undefinedClass: "CSS class '{{className}}' is not defined in project CSS files"
|
|
39513
39457
|
};
|
|
39514
39458
|
var jsxNoUndefinedCssClass = defineCrossRule({
|
|
39515
39459
|
id: "jsx-no-undefined-css-class",
|
|
39516
39460
|
severity: "error",
|
|
39517
|
-
messages:
|
|
39461
|
+
messages: messages131,
|
|
39518
39462
|
meta: {
|
|
39519
39463
|
description: "Detect undefined CSS class names in JSX",
|
|
39520
39464
|
fixable: false,
|
|
@@ -39533,7 +39477,7 @@ var jsxNoUndefinedCssClass = defineCrossRule({
|
|
|
39533
39477
|
ref.solid.sourceFile,
|
|
39534
39478
|
jsxNoUndefinedCssClass.id,
|
|
39535
39479
|
"undefinedClass",
|
|
39536
|
-
resolveMessage(
|
|
39480
|
+
resolveMessage(messages131.undefinedClass, { className: item.className }),
|
|
39537
39481
|
"error"
|
|
39538
39482
|
));
|
|
39539
39483
|
}
|
|
@@ -39541,13 +39485,13 @@ var jsxNoUndefinedCssClass = defineCrossRule({
|
|
|
39541
39485
|
});
|
|
39542
39486
|
|
|
39543
39487
|
// src/cross-file/rules/unreferenced-css-class.ts
|
|
39544
|
-
var
|
|
39488
|
+
var messages132 = {
|
|
39545
39489
|
unreferencedClass: "CSS class '{{className}}' is defined but not referenced by static JSX class attributes"
|
|
39546
39490
|
};
|
|
39547
39491
|
var cssNoUnreferencedComponentClass = defineCrossRule({
|
|
39548
39492
|
id: "css-no-unreferenced-component-class",
|
|
39549
39493
|
severity: "warn",
|
|
39550
|
-
messages:
|
|
39494
|
+
messages: messages132,
|
|
39551
39495
|
meta: {
|
|
39552
39496
|
description: "Detect CSS classes that are never referenced by static JSX class attributes.",
|
|
39553
39497
|
fixable: false,
|
|
@@ -39571,7 +39515,7 @@ var cssNoUnreferencedComponentClass = defineCrossRule({
|
|
|
39571
39515
|
},
|
|
39572
39516
|
cssNoUnreferencedComponentClass.id,
|
|
39573
39517
|
"unreferencedClass",
|
|
39574
|
-
resolveMessage(
|
|
39518
|
+
resolveMessage(messages132.unreferencedClass, { className }),
|
|
39575
39519
|
"warn"
|
|
39576
39520
|
)
|
|
39577
39521
|
);
|
|
@@ -39580,13 +39524,13 @@ var cssNoUnreferencedComponentClass = defineCrossRule({
|
|
|
39580
39524
|
});
|
|
39581
39525
|
|
|
39582
39526
|
// src/cross-file/rules/jsx-no-duplicate-class-token-class-classlist.ts
|
|
39583
|
-
var
|
|
39527
|
+
var messages133 = {
|
|
39584
39528
|
duplicateClassToken: "Class token `{{name}}` appears in both class and classList."
|
|
39585
39529
|
};
|
|
39586
39530
|
var jsxNoDuplicateClassTokenClassClasslist = defineCrossRule({
|
|
39587
39531
|
id: "jsx-no-duplicate-class-token-class-classlist",
|
|
39588
39532
|
severity: "warn",
|
|
39589
|
-
messages:
|
|
39533
|
+
messages: messages133,
|
|
39590
39534
|
meta: {
|
|
39591
39535
|
description: "Disallow duplicate class tokens between class and classList on the same JSX element.",
|
|
39592
39536
|
fixable: false,
|
|
@@ -39628,7 +39572,7 @@ var jsxNoDuplicateClassTokenClassClasslist = defineCrossRule({
|
|
|
39628
39572
|
solid.sourceFile,
|
|
39629
39573
|
jsxNoDuplicateClassTokenClassClasslist.id,
|
|
39630
39574
|
"duplicateClassToken",
|
|
39631
|
-
resolveMessage(
|
|
39575
|
+
resolveMessage(messages133.duplicateClassToken, { name: token }),
|
|
39632
39576
|
"warn"
|
|
39633
39577
|
));
|
|
39634
39578
|
}
|
|
@@ -39639,13 +39583,13 @@ var jsxNoDuplicateClassTokenClassClasslist = defineCrossRule({
|
|
|
39639
39583
|
|
|
39640
39584
|
// src/cross-file/rules/jsx-classlist-static-keys.ts
|
|
39641
39585
|
var import_typescript128 = __toESM(require("typescript"), 1);
|
|
39642
|
-
var
|
|
39586
|
+
var messages134 = {
|
|
39643
39587
|
nonStaticKey: "classList key must be statically known for reliable class mapping."
|
|
39644
39588
|
};
|
|
39645
39589
|
var jsxClasslistStaticKeys = defineCrossRule({
|
|
39646
39590
|
id: "jsx-classlist-static-keys",
|
|
39647
39591
|
severity: "error",
|
|
39648
|
-
messages:
|
|
39592
|
+
messages: messages134,
|
|
39649
39593
|
meta: {
|
|
39650
39594
|
description: "Require classList keys to be static and non-computed.",
|
|
39651
39595
|
fixable: false,
|
|
@@ -39656,26 +39600,26 @@ var jsxClasslistStaticKeys = defineCrossRule({
|
|
|
39656
39600
|
forEachClassListPropertyAcross(solids, (solid, p) => {
|
|
39657
39601
|
if (import_typescript128.default.isSpreadAssignment(p)) return;
|
|
39658
39602
|
if (!import_typescript128.default.isPropertyAssignment(p)) {
|
|
39659
|
-
emit(createDiagnostic(solid.file, p, solid.sourceFile, jsxClasslistStaticKeys.id, "nonStaticKey", resolveMessage(
|
|
39603
|
+
emit(createDiagnostic(solid.file, p, solid.sourceFile, jsxClasslistStaticKeys.id, "nonStaticKey", resolveMessage(messages134.nonStaticKey), "error"));
|
|
39660
39604
|
return;
|
|
39661
39605
|
}
|
|
39662
39606
|
if (import_typescript128.default.isComputedPropertyName(p.name)) return;
|
|
39663
39607
|
if (import_typescript128.default.isIdentifier(p.name)) return;
|
|
39664
39608
|
if (import_typescript128.default.isStringLiteral(p.name)) return;
|
|
39665
|
-
emit(createDiagnostic(solid.file, p.name, solid.sourceFile, jsxClasslistStaticKeys.id, "nonStaticKey", resolveMessage(
|
|
39609
|
+
emit(createDiagnostic(solid.file, p.name, solid.sourceFile, jsxClasslistStaticKeys.id, "nonStaticKey", resolveMessage(messages134.nonStaticKey), "error"));
|
|
39666
39610
|
});
|
|
39667
39611
|
}
|
|
39668
39612
|
});
|
|
39669
39613
|
|
|
39670
39614
|
// src/cross-file/rules/jsx-classlist-no-constant-literals.ts
|
|
39671
39615
|
var import_typescript129 = __toESM(require("typescript"), 1);
|
|
39672
|
-
var
|
|
39616
|
+
var messages135 = {
|
|
39673
39617
|
constantEntry: "classList entry `{{name}}: {{value}}` is constant; move it to static class."
|
|
39674
39618
|
};
|
|
39675
39619
|
var jsxClasslistNoConstantLiterals = defineCrossRule({
|
|
39676
39620
|
id: "jsx-classlist-no-constant-literals",
|
|
39677
39621
|
severity: "warn",
|
|
39678
|
-
messages:
|
|
39622
|
+
messages: messages135,
|
|
39679
39623
|
meta: {
|
|
39680
39624
|
description: "Disallow classList entries with constant true/false values.",
|
|
39681
39625
|
fixable: false,
|
|
@@ -39695,7 +39639,7 @@ var jsxClasslistNoConstantLiterals = defineCrossRule({
|
|
|
39695
39639
|
solid.sourceFile,
|
|
39696
39640
|
jsxClasslistNoConstantLiterals.id,
|
|
39697
39641
|
"constantEntry",
|
|
39698
|
-
resolveMessage(
|
|
39642
|
+
resolveMessage(messages135.constantEntry, { name: n, value: val.kind === import_typescript129.default.SyntaxKind.TrueKeyword ? "true" : "false" }),
|
|
39699
39643
|
"warn"
|
|
39700
39644
|
));
|
|
39701
39645
|
});
|
|
@@ -39730,13 +39674,13 @@ function isDefinitelyNonBooleanType(solid, node) {
|
|
|
39730
39674
|
}
|
|
39731
39675
|
|
|
39732
39676
|
// src/cross-file/rules/jsx-classlist-boolean-values.ts
|
|
39733
|
-
var
|
|
39677
|
+
var messages136 = {
|
|
39734
39678
|
nonBooleanValue: "classList value for `{{name}}` must be boolean."
|
|
39735
39679
|
};
|
|
39736
39680
|
var jsxClasslistBooleanValues = defineCrossRule({
|
|
39737
39681
|
id: "jsx-classlist-boolean-values",
|
|
39738
39682
|
severity: "error",
|
|
39739
|
-
messages:
|
|
39683
|
+
messages: messages136,
|
|
39740
39684
|
meta: {
|
|
39741
39685
|
description: "Require classList values to be boolean-like expressions.",
|
|
39742
39686
|
fixable: false,
|
|
@@ -39750,25 +39694,25 @@ var jsxClasslistBooleanValues = defineCrossRule({
|
|
|
39750
39694
|
if (!n) return;
|
|
39751
39695
|
if (isBooleanish(p.initializer)) return;
|
|
39752
39696
|
if (isDefinitelyNonBoolean(p.initializer)) {
|
|
39753
|
-
emit(createDiagnostic(solid.file, p.initializer, solid.sourceFile, jsxClasslistBooleanValues.id, "nonBooleanValue", resolveMessage(
|
|
39697
|
+
emit(createDiagnostic(solid.file, p.initializer, solid.sourceFile, jsxClasslistBooleanValues.id, "nonBooleanValue", resolveMessage(messages136.nonBooleanValue, { name: n }), "error"));
|
|
39754
39698
|
return;
|
|
39755
39699
|
}
|
|
39756
39700
|
if (isBooleanType(solid, p.initializer)) return;
|
|
39757
39701
|
if (!isDefinitelyNonBooleanType(solid, p.initializer)) return;
|
|
39758
|
-
emit(createDiagnostic(solid.file, p.initializer, solid.sourceFile, jsxClasslistBooleanValues.id, "nonBooleanValue", resolveMessage(
|
|
39702
|
+
emit(createDiagnostic(solid.file, p.initializer, solid.sourceFile, jsxClasslistBooleanValues.id, "nonBooleanValue", resolveMessage(messages136.nonBooleanValue, { name: n }), "error"));
|
|
39759
39703
|
});
|
|
39760
39704
|
}
|
|
39761
39705
|
});
|
|
39762
39706
|
|
|
39763
39707
|
// src/cross-file/rules/jsx-classlist-no-accessor-reference.ts
|
|
39764
39708
|
var import_typescript131 = __toESM(require("typescript"), 1);
|
|
39765
|
-
var
|
|
39709
|
+
var messages137 = {
|
|
39766
39710
|
accessorReference: "Signal accessor `{{name}}` must be called in classList value (use {{name}}())."
|
|
39767
39711
|
};
|
|
39768
39712
|
var jsxClasslistNoAccessorReference = defineCrossRule({
|
|
39769
39713
|
id: "jsx-classlist-no-accessor-reference",
|
|
39770
39714
|
severity: "error",
|
|
39771
|
-
messages:
|
|
39715
|
+
messages: messages137,
|
|
39772
39716
|
meta: {
|
|
39773
39717
|
description: "Disallow passing accessor references directly as classList values.",
|
|
39774
39718
|
fixable: false,
|
|
@@ -39800,7 +39744,7 @@ var jsxClasslistNoAccessorReference = defineCrossRule({
|
|
|
39800
39744
|
solid.sourceFile,
|
|
39801
39745
|
jsxClasslistNoAccessorReference.id,
|
|
39802
39746
|
"accessorReference",
|
|
39803
|
-
resolveMessage(
|
|
39747
|
+
resolveMessage(messages137.accessorReference, { name: v.text }),
|
|
39804
39748
|
"error"
|
|
39805
39749
|
));
|
|
39806
39750
|
});
|
|
@@ -39809,13 +39753,13 @@ var jsxClasslistNoAccessorReference = defineCrossRule({
|
|
|
39809
39753
|
|
|
39810
39754
|
// src/cross-file/rules/jsx-style-kebab-case-keys.ts
|
|
39811
39755
|
var import_typescript132 = __toESM(require("typescript"), 1);
|
|
39812
|
-
var
|
|
39756
|
+
var messages138 = {
|
|
39813
39757
|
kebabStyleKey: "Style key `{{name}}` should be `{{kebab}}` in Solid style objects."
|
|
39814
39758
|
};
|
|
39815
39759
|
var jsxStyleKebabCaseKeys = defineCrossRule({
|
|
39816
39760
|
id: "jsx-style-kebab-case-keys",
|
|
39817
39761
|
severity: "error",
|
|
39818
|
-
messages:
|
|
39762
|
+
messages: messages138,
|
|
39819
39763
|
meta: {
|
|
39820
39764
|
description: "Require kebab-case keys in JSX style object literals.",
|
|
39821
39765
|
fixable: false,
|
|
@@ -39836,7 +39780,7 @@ var jsxStyleKebabCaseKeys = defineCrossRule({
|
|
|
39836
39780
|
solid.sourceFile,
|
|
39837
39781
|
jsxStyleKebabCaseKeys.id,
|
|
39838
39782
|
"kebabStyleKey",
|
|
39839
|
-
resolveMessage(
|
|
39783
|
+
resolveMessage(messages138.kebabStyleKey, { name: n, kebab }),
|
|
39840
39784
|
"error"
|
|
39841
39785
|
));
|
|
39842
39786
|
});
|
|
@@ -39845,13 +39789,13 @@ var jsxStyleKebabCaseKeys = defineCrossRule({
|
|
|
39845
39789
|
|
|
39846
39790
|
// src/cross-file/rules/jsx-style-no-function-values.ts
|
|
39847
39791
|
var import_typescript133 = __toESM(require("typescript"), 1);
|
|
39848
|
-
var
|
|
39792
|
+
var messages139 = {
|
|
39849
39793
|
functionStyleValue: "Style value for `{{name}}` is a function; pass computed value instead."
|
|
39850
39794
|
};
|
|
39851
39795
|
var jsxStyleNoFunctionValues = defineCrossRule({
|
|
39852
39796
|
id: "jsx-style-no-function-values",
|
|
39853
39797
|
severity: "error",
|
|
39854
|
-
messages:
|
|
39798
|
+
messages: messages139,
|
|
39855
39799
|
meta: {
|
|
39856
39800
|
description: "Disallow function values in JSX style objects.",
|
|
39857
39801
|
fixable: false,
|
|
@@ -39865,11 +39809,11 @@ var jsxStyleNoFunctionValues = defineCrossRule({
|
|
|
39865
39809
|
if (!n) return;
|
|
39866
39810
|
const v = p.initializer;
|
|
39867
39811
|
if (import_typescript133.default.isArrowFunction(v) || import_typescript133.default.isFunctionExpression(v)) {
|
|
39868
|
-
emit(createDiagnostic(solid.file, v, solid.sourceFile, jsxStyleNoFunctionValues.id, "functionStyleValue", resolveMessage(
|
|
39812
|
+
emit(createDiagnostic(solid.file, v, solid.sourceFile, jsxStyleNoFunctionValues.id, "functionStyleValue", resolveMessage(messages139.functionStyleValue, { name: n }), "error"));
|
|
39869
39813
|
return;
|
|
39870
39814
|
}
|
|
39871
39815
|
if (import_typescript133.default.isIdentifier(v) && solid.typeResolver.isCallableType(v)) {
|
|
39872
|
-
emit(createDiagnostic(solid.file, v, solid.sourceFile, jsxStyleNoFunctionValues.id, "functionStyleValue", resolveMessage(
|
|
39816
|
+
emit(createDiagnostic(solid.file, v, solid.sourceFile, jsxStyleNoFunctionValues.id, "functionStyleValue", resolveMessage(messages139.functionStyleValue, { name: n }), "error"));
|
|
39873
39817
|
}
|
|
39874
39818
|
});
|
|
39875
39819
|
}
|
|
@@ -39877,13 +39821,13 @@ var jsxStyleNoFunctionValues = defineCrossRule({
|
|
|
39877
39821
|
|
|
39878
39822
|
// src/cross-file/rules/jsx-style-no-unused-custom-prop.ts
|
|
39879
39823
|
var import_typescript134 = __toESM(require("typescript"), 1);
|
|
39880
|
-
var
|
|
39824
|
+
var messages140 = {
|
|
39881
39825
|
unusedInlineVar: "Inline custom property `{{name}}` is never read via var({{name}})."
|
|
39882
39826
|
};
|
|
39883
39827
|
var jsxStyleNoUnusedCustomProp = defineCrossRule({
|
|
39884
39828
|
id: "jsx-style-no-unused-custom-prop",
|
|
39885
39829
|
severity: "warn",
|
|
39886
|
-
messages:
|
|
39830
|
+
messages: messages140,
|
|
39887
39831
|
meta: {
|
|
39888
39832
|
description: "Detect inline style custom properties that are never consumed by CSS var() references.",
|
|
39889
39833
|
fixable: false,
|
|
@@ -39924,7 +39868,7 @@ var jsxStyleNoUnusedCustomProp = defineCrossRule({
|
|
|
39924
39868
|
solid.sourceFile,
|
|
39925
39869
|
jsxStyleNoUnusedCustomProp.id,
|
|
39926
39870
|
"unusedInlineVar",
|
|
39927
|
-
resolveMessage(
|
|
39871
|
+
resolveMessage(messages140.unusedInlineVar, { name: n }),
|
|
39928
39872
|
"warn"
|
|
39929
39873
|
));
|
|
39930
39874
|
}
|
|
@@ -39934,7 +39878,7 @@ var jsxStyleNoUnusedCustomProp = defineCrossRule({
|
|
|
39934
39878
|
|
|
39935
39879
|
// src/cross-file/rules/jsx-style-policy.ts
|
|
39936
39880
|
var import_typescript135 = __toESM(require("typescript"), 1);
|
|
39937
|
-
var
|
|
39881
|
+
var messages141 = {
|
|
39938
39882
|
fontTooSmall: "Inline style `{{prop}}: {{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for policy `{{policy}}`.",
|
|
39939
39883
|
lineHeightTooSmall: "Inline style `line-height: {{value}}` is below the minimum `{{min}}` for policy `{{policy}}`.",
|
|
39940
39884
|
heightTooSmall: "Inline style `{{prop}}: {{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for interactive elements in policy `{{policy}}`.",
|
|
@@ -39945,28 +39889,54 @@ var INLINE_TOUCH_TARGET_KEYS = /* @__PURE__ */ new Set([
|
|
|
39945
39889
|
"height",
|
|
39946
39890
|
"min-height",
|
|
39947
39891
|
"width",
|
|
39948
|
-
"min-width"
|
|
39949
|
-
|
|
39950
|
-
|
|
39951
|
-
|
|
39952
|
-
"
|
|
39953
|
-
"
|
|
39892
|
+
"min-width"
|
|
39893
|
+
]);
|
|
39894
|
+
var INTERACTIVE_HTML_TAGS = /* @__PURE__ */ new Set(["button", "a", "input", "select", "textarea", "label", "summary"]);
|
|
39895
|
+
var INTERACTIVE_ARIA_ROLES = /* @__PURE__ */ new Set([
|
|
39896
|
+
"button",
|
|
39897
|
+
"link",
|
|
39898
|
+
"checkbox",
|
|
39899
|
+
"radio",
|
|
39900
|
+
"combobox",
|
|
39901
|
+
"listbox",
|
|
39902
|
+
"menuitem",
|
|
39903
|
+
"menuitemcheckbox",
|
|
39904
|
+
"menuitemradio",
|
|
39905
|
+
"option",
|
|
39906
|
+
"switch",
|
|
39907
|
+
"tab"
|
|
39954
39908
|
]);
|
|
39909
|
+
function isInteractiveElement(solid, element, hostElementRef) {
|
|
39910
|
+
if (element.tagName !== null && INTERACTIVE_HTML_TAGS.has(element.tagName)) return true;
|
|
39911
|
+
const roleAttr = getJSXAttributeEntity(solid, element, "role");
|
|
39912
|
+
if (roleAttr !== null && roleAttr.valueNode !== null) {
|
|
39913
|
+
const role = getStaticStringFromJSXValue(roleAttr.valueNode);
|
|
39914
|
+
if (role !== null && INTERACTIVE_ARIA_ROLES.has(role)) return true;
|
|
39915
|
+
}
|
|
39916
|
+
if (hostElementRef !== null && hostElementRef.element.tagName !== null) {
|
|
39917
|
+
if (INTERACTIVE_HTML_TAGS.has(hostElementRef.element.tagName)) return true;
|
|
39918
|
+
}
|
|
39919
|
+
return false;
|
|
39920
|
+
}
|
|
39921
|
+
function readNodeHostElementRef(layout, solid, element) {
|
|
39922
|
+
const node = layout.elementBySolidFileAndId.get(solid.file)?.get(element.id) ?? null;
|
|
39923
|
+
return node !== null ? readHostElementRef(layout, node) : null;
|
|
39924
|
+
}
|
|
39955
39925
|
var jsxStylePolicy = defineCrossRule({
|
|
39956
39926
|
id: "jsx-style-policy",
|
|
39957
39927
|
severity: "warn",
|
|
39958
|
-
messages:
|
|
39928
|
+
messages: messages141,
|
|
39959
39929
|
meta: {
|
|
39960
39930
|
description: "Enforce accessibility policy thresholds on inline JSX style objects.",
|
|
39961
39931
|
fixable: false,
|
|
39962
39932
|
category: "css-jsx"
|
|
39963
39933
|
},
|
|
39964
39934
|
check(context, emit) {
|
|
39965
|
-
const { solids } = context;
|
|
39935
|
+
const { solids, layout } = context;
|
|
39966
39936
|
const policy = getActivePolicy();
|
|
39967
39937
|
if (policy === null) return;
|
|
39968
39938
|
const name = getActivePolicyName() ?? "";
|
|
39969
|
-
forEachStylePropertyAcross(solids, (solid, p) => {
|
|
39939
|
+
forEachStylePropertyAcross(solids, (solid, p, element) => {
|
|
39970
39940
|
if (!import_typescript135.default.isPropertyAssignment(p)) return;
|
|
39971
39941
|
const key = objectKeyName(p.name);
|
|
39972
39942
|
if (!key) return;
|
|
@@ -39982,7 +39952,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
39982
39952
|
solid.sourceFile,
|
|
39983
39953
|
jsxStylePolicy.id,
|
|
39984
39954
|
"fontTooSmall",
|
|
39985
|
-
resolveMessage(
|
|
39955
|
+
resolveMessage(messages141.fontTooSmall, {
|
|
39986
39956
|
prop: key,
|
|
39987
39957
|
value: strVal,
|
|
39988
39958
|
resolved: formatRounded(px),
|
|
@@ -40004,7 +39974,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40004
39974
|
solid.sourceFile,
|
|
40005
39975
|
jsxStylePolicy.id,
|
|
40006
39976
|
"lineHeightTooSmall",
|
|
40007
|
-
resolveMessage(
|
|
39977
|
+
resolveMessage(messages141.lineHeightTooSmall, {
|
|
40008
39978
|
value: String(lh),
|
|
40009
39979
|
min: String(policy.minLineHeight),
|
|
40010
39980
|
policy: name
|
|
@@ -40014,6 +39984,8 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40014
39984
|
return;
|
|
40015
39985
|
}
|
|
40016
39986
|
if (INLINE_TOUCH_TARGET_KEYS.has(normalizedKey)) {
|
|
39987
|
+
const hostRef = readNodeHostElementRef(layout, solid, element);
|
|
39988
|
+
if (!isInteractiveElement(solid, element, hostRef)) return;
|
|
40017
39989
|
const strVal = getStaticStringValue(p.initializer);
|
|
40018
39990
|
if (!strVal) return;
|
|
40019
39991
|
const px = parsePxValue(strVal);
|
|
@@ -40026,7 +39998,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40026
39998
|
solid.sourceFile,
|
|
40027
39999
|
jsxStylePolicy.id,
|
|
40028
40000
|
"heightTooSmall",
|
|
40029
|
-
resolveMessage(
|
|
40001
|
+
resolveMessage(messages141.heightTooSmall, {
|
|
40030
40002
|
prop: key,
|
|
40031
40003
|
value: strVal,
|
|
40032
40004
|
resolved: formatRounded(px),
|
|
@@ -40048,7 +40020,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40048
40020
|
solid.sourceFile,
|
|
40049
40021
|
jsxStylePolicy.id,
|
|
40050
40022
|
"letterSpacingTooSmall",
|
|
40051
|
-
resolveMessage(
|
|
40023
|
+
resolveMessage(messages141.letterSpacingTooSmall, {
|
|
40052
40024
|
value: strVal,
|
|
40053
40025
|
resolved: String(em),
|
|
40054
40026
|
min: String(policy.minLetterSpacing),
|
|
@@ -40069,7 +40041,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40069
40041
|
solid.sourceFile,
|
|
40070
40042
|
jsxStylePolicy.id,
|
|
40071
40043
|
"wordSpacingTooSmall",
|
|
40072
|
-
resolveMessage(
|
|
40044
|
+
resolveMessage(messages141.wordSpacingTooSmall, {
|
|
40073
40045
|
value: strVal,
|
|
40074
40046
|
resolved: String(em),
|
|
40075
40047
|
min: String(policy.minWordSpacing),
|
|
@@ -40083,7 +40055,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40083
40055
|
});
|
|
40084
40056
|
|
|
40085
40057
|
// src/cross-file/rules/css-layout-sibling-alignment-outlier.ts
|
|
40086
|
-
var
|
|
40058
|
+
var messages142 = {
|
|
40087
40059
|
misalignedSibling: "Vertically misaligned '{{subject}}' in '{{parent}}'.{{fix}}{{offsetClause}}"
|
|
40088
40060
|
};
|
|
40089
40061
|
var MIN_CONFIDENCE_THRESHOLD = 0.48;
|
|
@@ -40092,7 +40064,7 @@ var siblingAlignmentDetector = {
|
|
|
40092
40064
|
id: "sibling-alignment-outlier",
|
|
40093
40065
|
collect: collectAlignmentCases,
|
|
40094
40066
|
evaluate(input, context) {
|
|
40095
|
-
if (context.logger.
|
|
40067
|
+
if (context.logger.isLevelEnabled(Level.Trace)) {
|
|
40096
40068
|
const ctx = input.context;
|
|
40097
40069
|
context.logger.trace(
|
|
40098
40070
|
`[sibling-alignment] evaluate subject=${input.subject.elementKey} tag=${input.subject.tag} parent=${ctx.parentElementKey} parentTag=${ctx.parentTag} context.kind=${ctx.kind} certainty=${ctx.certainty} display=${ctx.parentDisplay} alignItems=${ctx.parentAlignItems} crossAxisIsBlockAxis=${ctx.crossAxisIsBlockAxis} crossAxisCertainty=${ctx.crossAxisIsBlockAxisCertainty} baseline=${ctx.baselineRelevance}`
|
|
@@ -40131,7 +40103,7 @@ var siblingAlignmentDetector = {
|
|
|
40131
40103
|
var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
40132
40104
|
id: "css-layout-sibling-alignment-outlier",
|
|
40133
40105
|
severity: "warn",
|
|
40134
|
-
messages:
|
|
40106
|
+
messages: messages142,
|
|
40135
40107
|
meta: {
|
|
40136
40108
|
description: "Detect vertical alignment outliers between sibling elements in shared layout containers.",
|
|
40137
40109
|
fixable: false,
|
|
@@ -40141,7 +40113,7 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
|
40141
40113
|
const log = context.logger;
|
|
40142
40114
|
const detections = runLayoutDetector(context, siblingAlignmentDetector);
|
|
40143
40115
|
const uniqueDetections = dedupeDetectionsBySubject(detections);
|
|
40144
|
-
if (log.
|
|
40116
|
+
if (log.isLevelEnabled(Level.Debug)) {
|
|
40145
40117
|
log.debug(
|
|
40146
40118
|
`[sibling-alignment] raw=${detections.length} deduped=${uniqueDetections.length}`
|
|
40147
40119
|
);
|
|
@@ -40155,7 +40127,7 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
|
40155
40127
|
const subjectId = detection.caseData.subject.elementId;
|
|
40156
40128
|
const logPrefix = `[sibling-alignment] <${subjectTag}> in <${parentTag}> (${subjectFile}#${subjectId})`;
|
|
40157
40129
|
if (detection.evidence.confidence < MIN_CONFIDENCE_THRESHOLD) {
|
|
40158
|
-
if (log.
|
|
40130
|
+
if (log.isLevelEnabled(Level.Debug)) {
|
|
40159
40131
|
log.debug(
|
|
40160
40132
|
`${logPrefix} SKIP: confidence=${detection.evidence.confidence.toFixed(2)} < threshold=${MIN_CONFIDENCE_THRESHOLD}`
|
|
40161
40133
|
);
|
|
@@ -40164,7 +40136,7 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
|
40164
40136
|
}
|
|
40165
40137
|
const estimatedOffset = detection.evidence.estimatedOffsetPx;
|
|
40166
40138
|
if (estimatedOffset !== null && Math.abs(estimatedOffset) < MIN_OFFSET_PX_THRESHOLD && !hasNonOffsetPrimaryEvidence(detection.evidence.topFactors)) {
|
|
40167
|
-
if (log.
|
|
40139
|
+
if (log.isLevelEnabled(Level.Debug)) {
|
|
40168
40140
|
log.debug(
|
|
40169
40141
|
`${logPrefix} SKIP: offset=${estimatedOffset.toFixed(2)}px < ${MIN_OFFSET_PX_THRESHOLD}px (no non-offset primary evidence, topFactors=[${detection.evidence.topFactors.join(",")}])`
|
|
40170
40142
|
);
|
|
@@ -40176,7 +40148,7 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
|
40176
40148
|
detection.caseData.cohort.parentElementKey,
|
|
40177
40149
|
detection.caseData.subject.solidFile
|
|
40178
40150
|
)) {
|
|
40179
|
-
if (log.
|
|
40151
|
+
if (log.isLevelEnabled(Level.Debug)) {
|
|
40180
40152
|
log.debug(`${logPrefix} SKIP: out-of-flow ancestor`);
|
|
40181
40153
|
}
|
|
40182
40154
|
continue;
|
|
@@ -40187,7 +40159,7 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
|
40187
40159
|
detection.caseData.subject.elementId
|
|
40188
40160
|
);
|
|
40189
40161
|
if (!subjectRef) {
|
|
40190
|
-
if (log.
|
|
40162
|
+
if (log.isLevelEnabled(Level.Debug)) {
|
|
40191
40163
|
log.debug(`${logPrefix} SKIP: no node ref`);
|
|
40192
40164
|
}
|
|
40193
40165
|
continue;
|
|
@@ -40203,7 +40175,7 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
|
40203
40175
|
const primaryFix = detection.evidence.primaryFix;
|
|
40204
40176
|
const firstChar = primaryFix.length > 0 ? primaryFix[0] : void 0;
|
|
40205
40177
|
const fix = firstChar !== void 0 ? ` ${firstChar.toUpperCase()}${primaryFix.slice(1)}.` : "";
|
|
40206
|
-
if (log.
|
|
40178
|
+
if (log.isLevelEnabled(Level.Debug)) {
|
|
40207
40179
|
log.debug(
|
|
40208
40180
|
`${logPrefix} EMIT: severity=${severity} confidence=${confidence} offset=${offset?.toFixed(2) ?? "null"} posterior=[${detection.evidence.posteriorLower.toFixed(3)},${detection.evidence.posteriorUpper.toFixed(3)}] evidenceMass=${detection.evidence.evidenceMass.toFixed(3)} topFactors=[${detection.evidence.topFactors.join(",")}] causes=[${causes}]`
|
|
40209
40181
|
);
|
|
@@ -40215,7 +40187,7 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
|
40215
40187
|
subjectRef.solid.sourceFile,
|
|
40216
40188
|
cssLayoutSiblingAlignmentOutlier.id,
|
|
40217
40189
|
"misalignedSibling",
|
|
40218
|
-
resolveMessage(
|
|
40190
|
+
resolveMessage(messages142.misalignedSibling, {
|
|
40219
40191
|
subject,
|
|
40220
40192
|
parent,
|
|
40221
40193
|
fix,
|
|
@@ -40299,13 +40271,13 @@ function hasNonOffsetPrimaryEvidence(topFactors) {
|
|
|
40299
40271
|
}
|
|
40300
40272
|
|
|
40301
40273
|
// src/cross-file/rules/css-layout-transition-layout-property.ts
|
|
40302
|
-
var
|
|
40274
|
+
var messages143 = {
|
|
40303
40275
|
transitionLayoutProperty: "Transition '{{property}}' in '{{declaration}}' animates layout-affecting geometry. Prefer transform/opacity to avoid CLS."
|
|
40304
40276
|
};
|
|
40305
40277
|
var cssLayoutTransitionLayoutProperty = defineCrossRule({
|
|
40306
40278
|
id: "css-layout-transition-layout-property",
|
|
40307
40279
|
severity: "warn",
|
|
40308
|
-
messages:
|
|
40280
|
+
messages: messages143,
|
|
40309
40281
|
meta: {
|
|
40310
40282
|
description: "Disallow transitions that animate layout-affecting geometry properties.",
|
|
40311
40283
|
fixable: false,
|
|
@@ -40329,7 +40301,7 @@ var cssLayoutTransitionLayoutProperty = defineCrossRule({
|
|
|
40329
40301
|
},
|
|
40330
40302
|
cssLayoutTransitionLayoutProperty.id,
|
|
40331
40303
|
"transitionLayoutProperty",
|
|
40332
|
-
resolveMessage(
|
|
40304
|
+
resolveMessage(messages143.transitionLayoutProperty, {
|
|
40333
40305
|
property: target,
|
|
40334
40306
|
declaration: declaration.property
|
|
40335
40307
|
}),
|
|
@@ -40344,13 +40316,13 @@ function findLayoutTransitionTarget(raw) {
|
|
|
40344
40316
|
}
|
|
40345
40317
|
|
|
40346
40318
|
// src/cross-file/rules/css-layout-animation-layout-property.ts
|
|
40347
|
-
var
|
|
40319
|
+
var messages144 = {
|
|
40348
40320
|
animationLayoutProperty: "Animation '{{animation}}' mutates layout-affecting '{{property}}', which can trigger CLS. Prefer transform/opacity or reserve geometry."
|
|
40349
40321
|
};
|
|
40350
40322
|
var cssLayoutAnimationLayoutProperty = defineCrossRule({
|
|
40351
40323
|
id: "css-layout-animation-layout-property",
|
|
40352
40324
|
severity: "warn",
|
|
40353
|
-
messages:
|
|
40325
|
+
messages: messages144,
|
|
40354
40326
|
meta: {
|
|
40355
40327
|
description: "Disallow keyframe animations that mutate layout-affecting properties and can trigger CLS.",
|
|
40356
40328
|
fixable: false,
|
|
@@ -40378,7 +40350,7 @@ var cssLayoutAnimationLayoutProperty = defineCrossRule({
|
|
|
40378
40350
|
},
|
|
40379
40351
|
cssLayoutAnimationLayoutProperty.id,
|
|
40380
40352
|
"animationLayoutProperty",
|
|
40381
|
-
resolveMessage(
|
|
40353
|
+
resolveMessage(messages144.animationLayoutProperty, {
|
|
40382
40354
|
animation: match.name,
|
|
40383
40355
|
property: match.property
|
|
40384
40356
|
}),
|
|
@@ -40448,13 +40420,13 @@ function firstRiskyAnimationName(names, riskyKeyframes) {
|
|
|
40448
40420
|
}
|
|
40449
40421
|
|
|
40450
40422
|
// src/cross-file/rules/css-layout-stateful-box-model-shift.ts
|
|
40451
|
-
var
|
|
40423
|
+
var messages145 = {
|
|
40452
40424
|
statefulBoxModelShift: "State selector '{{selector}}' changes layout-affecting '{{property}}'. Keep geometry stable across states to avoid CLS."
|
|
40453
40425
|
};
|
|
40454
40426
|
var cssLayoutStatefulBoxModelShift = defineCrossRule({
|
|
40455
40427
|
id: "css-layout-stateful-box-model-shift",
|
|
40456
40428
|
severity: "warn",
|
|
40457
|
-
messages:
|
|
40429
|
+
messages: messages145,
|
|
40458
40430
|
meta: {
|
|
40459
40431
|
description: "Disallow stateful selector changes that alter element geometry and trigger layout shifts.",
|
|
40460
40432
|
fixable: false,
|
|
@@ -40495,7 +40467,7 @@ var cssLayoutStatefulBoxModelShift = defineCrossRule({
|
|
|
40495
40467
|
},
|
|
40496
40468
|
cssLayoutStatefulBoxModelShift.id,
|
|
40497
40469
|
"statefulBoxModelShift",
|
|
40498
|
-
resolveMessage(
|
|
40470
|
+
resolveMessage(messages145.statefulBoxModelShift, {
|
|
40499
40471
|
selector: match.raw,
|
|
40500
40472
|
property: declaration.property
|
|
40501
40473
|
}),
|
|
@@ -40589,14 +40561,14 @@ function lookupBaseByProperty(baseValueIndex, selectorKeys) {
|
|
|
40589
40561
|
|
|
40590
40562
|
// src/cross-file/rules/css-layout-unsized-replaced-element.ts
|
|
40591
40563
|
var import_typescript136 = __toESM(require("typescript"), 1);
|
|
40592
|
-
var
|
|
40564
|
+
var messages146 = {
|
|
40593
40565
|
unsizedReplacedElement: "Replaced element '{{tag}}' has no stable reserved size (width/height or aspect-ratio with a dimension), which can cause CLS."
|
|
40594
40566
|
};
|
|
40595
40567
|
var REPLACED_MEDIA_TAGS = /* @__PURE__ */ new Set(["img", "video", "iframe", "canvas", "svg"]);
|
|
40596
40568
|
var cssLayoutUnsizedReplacedElement = defineCrossRule({
|
|
40597
40569
|
id: "css-layout-unsized-replaced-element",
|
|
40598
40570
|
severity: "warn",
|
|
40599
|
-
messages:
|
|
40571
|
+
messages: messages146,
|
|
40600
40572
|
meta: {
|
|
40601
40573
|
description: "Require stable reserved geometry for replaced media elements to prevent layout shifts.",
|
|
40602
40574
|
fixable: false,
|
|
@@ -40612,7 +40584,8 @@ var cssLayoutUnsizedReplacedElement = defineCrossRule({
|
|
|
40612
40584
|
const ref = readNodeRef(context.layout, node);
|
|
40613
40585
|
if (!ref) continue;
|
|
40614
40586
|
const reservedSpace = readReservedSpaceFact(context.layout, node);
|
|
40615
|
-
|
|
40587
|
+
const hostRef = readHostElementRef(context.layout, node);
|
|
40588
|
+
if (hasReservedSize(ref.solid, node.attributes, ref.element, reservedSpace, hostRef)) continue;
|
|
40616
40589
|
emit(
|
|
40617
40590
|
createDiagnostic(
|
|
40618
40591
|
ref.solid.file,
|
|
@@ -40620,22 +40593,24 @@ var cssLayoutUnsizedReplacedElement = defineCrossRule({
|
|
|
40620
40593
|
ref.solid.sourceFile,
|
|
40621
40594
|
cssLayoutUnsizedReplacedElement.id,
|
|
40622
40595
|
"unsizedReplacedElement",
|
|
40623
|
-
resolveMessage(
|
|
40596
|
+
resolveMessage(messages146.unsizedReplacedElement, { tag }),
|
|
40624
40597
|
"warn"
|
|
40625
40598
|
)
|
|
40626
40599
|
);
|
|
40627
40600
|
}
|
|
40628
40601
|
}
|
|
40629
40602
|
});
|
|
40630
|
-
function hasReservedSize(solid, attributes, element, reservedSpaceFact) {
|
|
40603
|
+
function hasReservedSize(solid, attributes, element, reservedSpaceFact, hostElementRef) {
|
|
40631
40604
|
if (reservedSpaceFact.hasReservedSpace) return true;
|
|
40632
40605
|
const attrWidth = parsePositiveLength(attributes.get("width"));
|
|
40633
40606
|
const attrHeight = parsePositiveLength(attributes.get("height"));
|
|
40634
40607
|
const jsxAttrWidth = readPositiveJsxAttribute(solid, element, "width");
|
|
40635
40608
|
const jsxAttrHeight = readPositiveJsxAttribute(solid, element, "height");
|
|
40636
|
-
|
|
40637
|
-
const
|
|
40638
|
-
|
|
40609
|
+
const hostJsxWidth = hostElementRef !== null ? readPositiveJsxAttribute(hostElementRef.solid, hostElementRef.element, "width") : false;
|
|
40610
|
+
const hostJsxHeight = hostElementRef !== null ? readPositiveJsxAttribute(hostElementRef.solid, hostElementRef.element, "height") : false;
|
|
40611
|
+
if (attrWidth && attrHeight || jsxAttrWidth && jsxAttrHeight || hostJsxWidth && hostJsxHeight) return true;
|
|
40612
|
+
const hasAnyWidth = attrWidth || jsxAttrWidth || hostJsxWidth || reservedSpaceFact.hasUsableInlineDimension;
|
|
40613
|
+
const hasAnyHeight = attrHeight || jsxAttrHeight || hostJsxHeight || reservedSpaceFact.hasUsableBlockDimension || reservedSpaceFact.hasContainIntrinsicSize;
|
|
40639
40614
|
if (reservedSpaceFact.hasUsableAspectRatio && (hasAnyWidth || hasAnyHeight)) return true;
|
|
40640
40615
|
if (reservedSpaceFact.hasContainIntrinsicSize && (hasAnyWidth || hasAnyHeight)) return true;
|
|
40641
40616
|
return false;
|
|
@@ -40671,7 +40646,7 @@ function readPositiveJsxAttribute(solid, element, name) {
|
|
|
40671
40646
|
}
|
|
40672
40647
|
|
|
40673
40648
|
// src/cross-file/rules/css-layout-dynamic-slot-no-reserved-space.ts
|
|
40674
|
-
var
|
|
40649
|
+
var messages147 = {
|
|
40675
40650
|
dynamicSlotNoReservedSpace: "Dynamic content container '{{tag}}' does not reserve block space (min-height/height/aspect-ratio/contain-intrinsic-size), which can cause CLS."
|
|
40676
40651
|
};
|
|
40677
40652
|
var INLINE_DISPLAYS = /* @__PURE__ */ new Set(["inline", "contents"]);
|
|
@@ -40693,7 +40668,7 @@ function hasOutOfFlowAncestor(layout, node) {
|
|
|
40693
40668
|
var cssLayoutDynamicSlotNoReservedSpace = defineCrossRule({
|
|
40694
40669
|
id: "css-layout-dynamic-slot-no-reserved-space",
|
|
40695
40670
|
severity: "warn",
|
|
40696
|
-
messages:
|
|
40671
|
+
messages: messages147,
|
|
40697
40672
|
meta: {
|
|
40698
40673
|
description: "Require reserved block space for dynamic content containers to avoid layout shifts.",
|
|
40699
40674
|
fixable: false,
|
|
@@ -40716,19 +40691,19 @@ var cssLayoutDynamicSlotNoReservedSpace = defineCrossRule({
|
|
|
40716
40691
|
const reservedSpace = readReservedSpaceFact(context.layout, node);
|
|
40717
40692
|
if (reservedSpace.hasReservedSpace) continue;
|
|
40718
40693
|
if (hasBlockAxisPadding(snapshot)) continue;
|
|
40719
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutDynamicSlotNoReservedSpace.id, "dynamicSlotNoReservedSpace",
|
|
40694
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutDynamicSlotNoReservedSpace.id, "dynamicSlotNoReservedSpace", messages147.dynamicSlotNoReservedSpace, cssLayoutDynamicSlotNoReservedSpace.severity)) continue;
|
|
40720
40695
|
}
|
|
40721
40696
|
}
|
|
40722
40697
|
});
|
|
40723
40698
|
|
|
40724
40699
|
// src/cross-file/rules/css-layout-scrollbar-gutter-instability.ts
|
|
40725
|
-
var
|
|
40700
|
+
var messages148 = {
|
|
40726
40701
|
missingScrollbarGutter: "Scrollable container '{{tag}}' uses overflow auto/scroll without `scrollbar-gutter: stable`, which can trigger CLS when scrollbars appear."
|
|
40727
40702
|
};
|
|
40728
40703
|
var cssLayoutScrollbarGutterInstability = defineCrossRule({
|
|
40729
40704
|
id: "css-layout-scrollbar-gutter-instability",
|
|
40730
40705
|
severity: "warn",
|
|
40731
|
-
messages:
|
|
40706
|
+
messages: messages148,
|
|
40732
40707
|
meta: {
|
|
40733
40708
|
description: "Require stable scrollbar gutters for scrollable containers to reduce layout shifts.",
|
|
40734
40709
|
fixable: false,
|
|
@@ -40747,19 +40722,19 @@ var cssLayoutScrollbarGutterInstability = defineCrossRule({
|
|
|
40747
40722
|
if (scrollbarWidth === "none") continue;
|
|
40748
40723
|
const gutter = readKnownNormalized(snapshot, "scrollbar-gutter");
|
|
40749
40724
|
if (gutter !== null && gutter.startsWith("stable")) continue;
|
|
40750
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutScrollbarGutterInstability.id, "missingScrollbarGutter",
|
|
40725
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutScrollbarGutterInstability.id, "missingScrollbarGutter", messages148.missingScrollbarGutter, cssLayoutScrollbarGutterInstability.severity)) continue;
|
|
40751
40726
|
}
|
|
40752
40727
|
}
|
|
40753
40728
|
});
|
|
40754
40729
|
|
|
40755
40730
|
// src/cross-file/rules/css-layout-overflow-anchor-instability.ts
|
|
40756
|
-
var
|
|
40731
|
+
var messages149 = {
|
|
40757
40732
|
unstableOverflowAnchor: "Element '{{tag}}' sets `overflow-anchor: none` on a {{context}} container; disabling scroll anchoring can amplify visible layout shifts."
|
|
40758
40733
|
};
|
|
40759
40734
|
var cssLayoutOverflowAnchorInstability = defineCrossRule({
|
|
40760
40735
|
id: "css-layout-overflow-anchor-instability",
|
|
40761
40736
|
severity: "warn",
|
|
40762
|
-
messages:
|
|
40737
|
+
messages: messages149,
|
|
40763
40738
|
meta: {
|
|
40764
40739
|
description: "Disallow overflow-anchor none on dynamic or scrollable containers prone to visible layout shifts.",
|
|
40765
40740
|
fixable: false,
|
|
@@ -40777,20 +40752,20 @@ var cssLayoutOverflowAnchorInstability = defineCrossRule({
|
|
|
40777
40752
|
const isDynamicContainer = isDynamicContainerLike(node);
|
|
40778
40753
|
if (!isScrollable && !isDynamicContainer) continue;
|
|
40779
40754
|
const containerContext = isScrollable ? "scrollable" : "dynamic";
|
|
40780
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutOverflowAnchorInstability.id, "unstableOverflowAnchor",
|
|
40755
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutOverflowAnchorInstability.id, "unstableOverflowAnchor", messages149.unstableOverflowAnchor, cssLayoutOverflowAnchorInstability.severity, { context: containerContext })) continue;
|
|
40781
40756
|
}
|
|
40782
40757
|
}
|
|
40783
40758
|
});
|
|
40784
40759
|
|
|
40785
40760
|
// src/cross-file/rules/css-layout-font-swap-instability.ts
|
|
40786
|
-
var
|
|
40761
|
+
var messages150 = {
|
|
40787
40762
|
unstableFontSwap: "`@font-face` for '{{family}}' uses `font-display: {{display}}` without metric overrides (for example `size-adjust`), which can cause CLS when the webfont swaps in."
|
|
40788
40763
|
};
|
|
40789
40764
|
var SWAP_DISPLAYS = /* @__PURE__ */ new Set(["swap", "fallback"]);
|
|
40790
40765
|
var cssLayoutFontSwapInstability = defineCrossRule({
|
|
40791
40766
|
id: "css-layout-font-swap-instability",
|
|
40792
40767
|
severity: "warn",
|
|
40793
|
-
messages:
|
|
40768
|
+
messages: messages150,
|
|
40794
40769
|
meta: {
|
|
40795
40770
|
description: "Require metric overrides for swapping webfonts to reduce layout shifts during font load.",
|
|
40796
40771
|
fixable: false,
|
|
@@ -40837,7 +40812,7 @@ var cssLayoutFontSwapInstability = defineCrossRule({
|
|
|
40837
40812
|
},
|
|
40838
40813
|
cssLayoutFontSwapInstability.id,
|
|
40839
40814
|
"unstableFontSwap",
|
|
40840
|
-
resolveMessage(
|
|
40815
|
+
resolveMessage(messages150.unstableFontSwap, {
|
|
40841
40816
|
family,
|
|
40842
40817
|
display: report.display
|
|
40843
40818
|
}),
|
|
@@ -40850,14 +40825,14 @@ var cssLayoutFontSwapInstability = defineCrossRule({
|
|
|
40850
40825
|
});
|
|
40851
40826
|
|
|
40852
40827
|
// src/cross-file/rules/css-layout-conditional-display-collapse.ts
|
|
40853
|
-
var
|
|
40828
|
+
var messages151 = {
|
|
40854
40829
|
conditionalDisplayCollapse: "Conditional display sets '{{display}}' on '{{tag}}' without stable reserved space, which can collapse/expand layout and cause CLS."
|
|
40855
40830
|
};
|
|
40856
40831
|
var COLLAPSING_DISPLAYS = /* @__PURE__ */ new Set(["none", "contents"]);
|
|
40857
40832
|
var cssLayoutConditionalDisplayCollapse = defineCrossRule({
|
|
40858
40833
|
id: "css-layout-conditional-display-collapse",
|
|
40859
40834
|
severity: "warn",
|
|
40860
|
-
messages:
|
|
40835
|
+
messages: messages151,
|
|
40861
40836
|
meta: {
|
|
40862
40837
|
description: "Disallow conditional display collapse in flow without reserved geometry.",
|
|
40863
40838
|
fixable: false,
|
|
@@ -40878,13 +40853,13 @@ var cssLayoutConditionalDisplayCollapse = defineCrossRule({
|
|
|
40878
40853
|
if (!isFlowRelevantBySiblingsOrText(node, snapshot.node.textualContent)) continue;
|
|
40879
40854
|
const reservedSpace = readReservedSpaceFact(context.layout, node);
|
|
40880
40855
|
if (reservedSpace.hasReservedSpace) continue;
|
|
40881
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalDisplayCollapse.id, "conditionalDisplayCollapse",
|
|
40856
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalDisplayCollapse.id, "conditionalDisplayCollapse", messages151.conditionalDisplayCollapse, cssLayoutConditionalDisplayCollapse.severity, { display })) continue;
|
|
40882
40857
|
}
|
|
40883
40858
|
}
|
|
40884
40859
|
});
|
|
40885
40860
|
|
|
40886
40861
|
// src/cross-file/rules/css-layout-conditional-white-space-wrap-shift.ts
|
|
40887
|
-
var
|
|
40862
|
+
var messages152 = {
|
|
40888
40863
|
conditionalWhiteSpaceShift: "Conditional white-space '{{whiteSpace}}' on '{{tag}}' can reflow text and shift siblings; keep wrapping behavior stable or reserve geometry."
|
|
40889
40864
|
};
|
|
40890
40865
|
var WRAP_SHIFT_VALUES = /* @__PURE__ */ new Set(["nowrap", "pre"]);
|
|
@@ -40893,7 +40868,7 @@ var BLOCK_SIZE_PROPERTIES = ["height", "min-height"];
|
|
|
40893
40868
|
var cssLayoutConditionalWhiteSpaceWrapShift = defineCrossRule({
|
|
40894
40869
|
id: "css-layout-conditional-white-space-wrap-shift",
|
|
40895
40870
|
severity: "warn",
|
|
40896
|
-
messages:
|
|
40871
|
+
messages: messages152,
|
|
40897
40872
|
meta: {
|
|
40898
40873
|
description: "Disallow conditional white-space wrapping mode toggles that can trigger CLS.",
|
|
40899
40874
|
fixable: false,
|
|
@@ -40916,7 +40891,7 @@ var cssLayoutConditionalWhiteSpaceWrapShift = defineCrossRule({
|
|
|
40916
40891
|
if (!flow.inFlow) continue;
|
|
40917
40892
|
if (!isFlowRelevantBySiblingsOrText(node, snapshot.node.textualContent)) continue;
|
|
40918
40893
|
if (hasStableTextShell(snapshot)) continue;
|
|
40919
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalWhiteSpaceWrapShift.id, "conditionalWhiteSpaceShift",
|
|
40894
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutConditionalWhiteSpaceWrapShift.id, "conditionalWhiteSpaceShift", messages152.conditionalWhiteSpaceShift, cssLayoutConditionalWhiteSpaceWrapShift.severity, { whiteSpace })) continue;
|
|
40920
40895
|
}
|
|
40921
40896
|
}
|
|
40922
40897
|
});
|
|
@@ -40938,13 +40913,13 @@ function hasWrapShiftDelta(delta) {
|
|
|
40938
40913
|
}
|
|
40939
40914
|
|
|
40940
40915
|
// src/cross-file/rules/css-layout-overflow-mode-toggle-instability.ts
|
|
40941
|
-
var
|
|
40916
|
+
var messages153 = {
|
|
40942
40917
|
overflowModeToggle: "Conditional overflow mode changes scrolling ('{{overflow}}') on '{{tag}}' without `scrollbar-gutter: stable`, which can trigger CLS."
|
|
40943
40918
|
};
|
|
40944
40919
|
var cssLayoutOverflowModeToggleInstability = defineCrossRule({
|
|
40945
40920
|
id: "css-layout-overflow-mode-toggle-instability",
|
|
40946
40921
|
severity: "warn",
|
|
40947
|
-
messages:
|
|
40922
|
+
messages: messages153,
|
|
40948
40923
|
meta: {
|
|
40949
40924
|
description: "Disallow conditional overflow mode switches that can introduce scrollbar-induced layout shifts.",
|
|
40950
40925
|
fixable: false,
|
|
@@ -40969,7 +40944,7 @@ var cssLayoutOverflowModeToggleInstability = defineCrossRule({
|
|
|
40969
40944
|
const gutter = readKnownNormalizedWithGuard(snapshot, "scrollbar-gutter");
|
|
40970
40945
|
if (gutter !== null && gutter.startsWith("stable")) continue;
|
|
40971
40946
|
const overflowValue = scrollFact.overflowY ?? scrollFact.overflow ?? "auto";
|
|
40972
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutOverflowModeToggleInstability.id, "overflowModeToggle",
|
|
40947
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutOverflowModeToggleInstability.id, "overflowModeToggle", messages153.overflowModeToggle, cssLayoutOverflowModeToggleInstability.severity, { overflow: overflowValue })) continue;
|
|
40973
40948
|
}
|
|
40974
40949
|
}
|
|
40975
40950
|
});
|
|
@@ -40989,7 +40964,7 @@ function hasAnyScrollValue(delta) {
|
|
|
40989
40964
|
}
|
|
40990
40965
|
|
|
40991
40966
|
// src/cross-file/rules/css-layout-box-sizing-toggle-with-chrome.ts
|
|
40992
|
-
var
|
|
40967
|
+
var messages154 = {
|
|
40993
40968
|
boxSizingToggleWithChrome: "Conditional `box-sizing` toggle on '{{tag}}' combines with non-zero padding/border, which can shift layout and trigger CLS."
|
|
40994
40969
|
};
|
|
40995
40970
|
var BOX_SIZING_VALUES = /* @__PURE__ */ new Set(["content-box", "border-box"]);
|
|
@@ -41006,7 +40981,7 @@ var CHROME_PROPERTIES = [
|
|
|
41006
40981
|
var cssLayoutBoxSizingToggleWithChrome = defineCrossRule({
|
|
41007
40982
|
id: "css-layout-box-sizing-toggle-with-chrome",
|
|
41008
40983
|
severity: "warn",
|
|
41009
|
-
messages:
|
|
40984
|
+
messages: messages154,
|
|
41010
40985
|
meta: {
|
|
41011
40986
|
description: "Disallow conditional box-sizing mode toggles when box chrome contributes to geometry shifts.",
|
|
41012
40987
|
fixable: false,
|
|
@@ -41024,7 +40999,7 @@ var cssLayoutBoxSizingToggleWithChrome = defineCrossRule({
|
|
|
41024
40999
|
if (!boxSizingDelta.hasConditional) continue;
|
|
41025
41000
|
if (!boxSizingDelta.hasDelta) continue;
|
|
41026
41001
|
if (!hasNonZeroChrome(snapshot)) continue;
|
|
41027
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutBoxSizingToggleWithChrome.id, "boxSizingToggleWithChrome",
|
|
41002
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutBoxSizingToggleWithChrome.id, "boxSizingToggleWithChrome", messages154.boxSizingToggleWithChrome, cssLayoutBoxSizingToggleWithChrome.severity)) continue;
|
|
41028
41003
|
}
|
|
41029
41004
|
}
|
|
41030
41005
|
});
|
|
@@ -41033,13 +41008,13 @@ function hasNonZeroChrome(snapshot) {
|
|
|
41033
41008
|
}
|
|
41034
41009
|
|
|
41035
41010
|
// src/cross-file/rules/css-layout-content-visibility-no-intrinsic-size.ts
|
|
41036
|
-
var
|
|
41011
|
+
var messages155 = {
|
|
41037
41012
|
missingIntrinsicSize: "`content-visibility: auto` on '{{tag}}' lacks intrinsic size reservation (`contain-intrinsic-size`/min-height/height/aspect-ratio), which can cause CLS."
|
|
41038
41013
|
};
|
|
41039
41014
|
var cssLayoutContentVisibilityNoIntrinsicSize = defineCrossRule({
|
|
41040
41015
|
id: "css-layout-content-visibility-no-intrinsic-size",
|
|
41041
41016
|
severity: "warn",
|
|
41042
|
-
messages:
|
|
41017
|
+
messages: messages155,
|
|
41043
41018
|
meta: {
|
|
41044
41019
|
description: "Require intrinsic size reservation when using content-visibility auto to avoid late layout shifts.",
|
|
41045
41020
|
fixable: false,
|
|
@@ -41054,19 +41029,19 @@ var cssLayoutContentVisibilityNoIntrinsicSize = defineCrossRule({
|
|
|
41054
41029
|
if (!isDeferredContainerLike(node, snapshot.node.textualContent)) continue;
|
|
41055
41030
|
const reservedSpace = readReservedSpaceFact(context.layout, node);
|
|
41056
41031
|
if (reservedSpace.hasReservedSpace) continue;
|
|
41057
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutContentVisibilityNoIntrinsicSize.id, "missingIntrinsicSize",
|
|
41032
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutContentVisibilityNoIntrinsicSize.id, "missingIntrinsicSize", messages155.missingIntrinsicSize, cssLayoutContentVisibilityNoIntrinsicSize.severity)) continue;
|
|
41058
41033
|
}
|
|
41059
41034
|
}
|
|
41060
41035
|
});
|
|
41061
41036
|
|
|
41062
41037
|
// src/cross-file/rules/css-layout-conditional-offset-shift.ts
|
|
41063
|
-
var
|
|
41038
|
+
var messages156 = {
|
|
41064
41039
|
conditionalOffsetShift: "Conditional style applies non-zero '{{property}}' offset ({{value}}), which can cause layout shifts when conditions toggle."
|
|
41065
41040
|
};
|
|
41066
41041
|
var cssLayoutConditionalOffsetShift = defineCrossRule({
|
|
41067
41042
|
id: "css-layout-conditional-offset-shift",
|
|
41068
41043
|
severity: "warn",
|
|
41069
|
-
messages:
|
|
41044
|
+
messages: messages156,
|
|
41070
41045
|
meta: {
|
|
41071
41046
|
description: "Disallow conditional non-zero block-axis offsets that can trigger layout shifts.",
|
|
41072
41047
|
fixable: false,
|
|
@@ -41092,7 +41067,7 @@ var cssLayoutConditionalOffsetShift = defineCrossRule({
|
|
|
41092
41067
|
ref.solid.sourceFile,
|
|
41093
41068
|
cssLayoutConditionalOffsetShift.id,
|
|
41094
41069
|
"conditionalOffsetShift",
|
|
41095
|
-
resolveMessage(
|
|
41070
|
+
resolveMessage(messages156.conditionalOffsetShift, {
|
|
41096
41071
|
property: match.property,
|
|
41097
41072
|
value: `${formatFixed(match.value)}px`
|
|
41098
41073
|
}),
|
|
@@ -41153,7 +41128,7 @@ function hasStableBaseline(baselineBySignal, property, expectedPx) {
|
|
|
41153
41128
|
|
|
41154
41129
|
// src/cross-file/rules/jsx-layout-unstable-style-toggle.ts
|
|
41155
41130
|
var import_typescript137 = __toESM(require("typescript"), 1);
|
|
41156
|
-
var
|
|
41131
|
+
var messages157 = {
|
|
41157
41132
|
unstableLayoutStyleToggle: "Dynamic style value for '{{property}}' can toggle layout geometry at runtime and cause CLS."
|
|
41158
41133
|
};
|
|
41159
41134
|
var PX_NUMBER_PROPERTIES = /* @__PURE__ */ new Set([
|
|
@@ -41176,7 +41151,7 @@ var POSITIONED_OFFSET_PROPERTIES = /* @__PURE__ */ new Set([
|
|
|
41176
41151
|
var jsxLayoutUnstableStyleToggle = defineCrossRule({
|
|
41177
41152
|
id: "jsx-layout-unstable-style-toggle",
|
|
41178
41153
|
severity: "warn",
|
|
41179
|
-
messages:
|
|
41154
|
+
messages: messages157,
|
|
41180
41155
|
meta: {
|
|
41181
41156
|
description: "Flag dynamic inline style values on layout-sensitive properties that can trigger CLS.",
|
|
41182
41157
|
fixable: false,
|
|
@@ -41198,7 +41173,7 @@ var jsxLayoutUnstableStyleToggle = defineCrossRule({
|
|
|
41198
41173
|
solid.sourceFile,
|
|
41199
41174
|
jsxLayoutUnstableStyleToggle.id,
|
|
41200
41175
|
"unstableLayoutStyleToggle",
|
|
41201
|
-
resolveMessage(
|
|
41176
|
+
resolveMessage(messages157.unstableLayoutStyleToggle, { property: normalized }),
|
|
41202
41177
|
"warn"
|
|
41203
41178
|
)
|
|
41204
41179
|
);
|
|
@@ -41315,14 +41290,14 @@ function unwrapTypeWrapper(node) {
|
|
|
41315
41290
|
|
|
41316
41291
|
// src/cross-file/rules/jsx-layout-classlist-geometry-toggle.ts
|
|
41317
41292
|
var import_typescript138 = __toESM(require("typescript"), 1);
|
|
41318
|
-
var
|
|
41293
|
+
var messages158 = {
|
|
41319
41294
|
classListGeometryToggle: "classList toggles '{{className}}', and matching CSS changes layout-affecting '{{property}}', which can cause CLS."
|
|
41320
41295
|
};
|
|
41321
41296
|
var OUT_OF_FLOW_POSITIONS2 = /* @__PURE__ */ new Set(["fixed", "absolute"]);
|
|
41322
41297
|
var jsxLayoutClasslistGeometryToggle = defineCrossRule({
|
|
41323
41298
|
id: "jsx-layout-classlist-geometry-toggle",
|
|
41324
41299
|
severity: "warn",
|
|
41325
|
-
messages:
|
|
41300
|
+
messages: messages158,
|
|
41326
41301
|
meta: {
|
|
41327
41302
|
description: "Flag classList-driven class toggles that map to layout-affecting CSS geometry changes.",
|
|
41328
41303
|
fixable: false,
|
|
@@ -41348,7 +41323,7 @@ var jsxLayoutClasslistGeometryToggle = defineCrossRule({
|
|
|
41348
41323
|
solid.sourceFile,
|
|
41349
41324
|
jsxLayoutClasslistGeometryToggle.id,
|
|
41350
41325
|
"classListGeometryToggle",
|
|
41351
|
-
resolveMessage(
|
|
41326
|
+
resolveMessage(messages158.classListGeometryToggle, {
|
|
41352
41327
|
className,
|
|
41353
41328
|
property
|
|
41354
41329
|
}),
|
|
@@ -41397,14 +41372,14 @@ function isDynamicallyToggleable(node) {
|
|
|
41397
41372
|
}
|
|
41398
41373
|
|
|
41399
41374
|
// src/cross-file/rules/jsx-layout-picture-source-ratio-consistency.ts
|
|
41400
|
-
var
|
|
41375
|
+
var messages159 = {
|
|
41401
41376
|
inconsistentPictureRatio: "`<picture>` source ratio {{sourceRatio}} differs from fallback img ratio {{imgRatio}}, which can cause reserved-space mismatch and CLS."
|
|
41402
41377
|
};
|
|
41403
41378
|
var RATIO_DELTA_THRESHOLD = 0.02;
|
|
41404
41379
|
var jsxLayoutPictureSourceRatioConsistency = defineCrossRule({
|
|
41405
41380
|
id: "jsx-layout-picture-source-ratio-consistency",
|
|
41406
41381
|
severity: "warn",
|
|
41407
|
-
messages:
|
|
41382
|
+
messages: messages159,
|
|
41408
41383
|
meta: {
|
|
41409
41384
|
description: "Require consistent intrinsic aspect ratios across <picture> sources and fallback image.",
|
|
41410
41385
|
fixable: false,
|
|
@@ -41427,7 +41402,7 @@ var jsxLayoutPictureSourceRatioConsistency = defineCrossRule({
|
|
|
41427
41402
|
solid.sourceFile,
|
|
41428
41403
|
jsxLayoutPictureSourceRatioConsistency.id,
|
|
41429
41404
|
"inconsistentPictureRatio",
|
|
41430
|
-
resolveMessage(
|
|
41405
|
+
resolveMessage(messages159.inconsistentPictureRatio, {
|
|
41431
41406
|
sourceRatio: mismatch.sourceRatio,
|
|
41432
41407
|
imgRatio: mismatch.imgRatio
|
|
41433
41408
|
}),
|
|
@@ -41497,13 +41472,13 @@ function formatRatio(value2) {
|
|
|
41497
41472
|
}
|
|
41498
41473
|
|
|
41499
41474
|
// src/cross-file/rules/jsx-layout-fill-image-parent-must-be-sized.ts
|
|
41500
|
-
var
|
|
41475
|
+
var messages160 = {
|
|
41501
41476
|
unsizedFillParent: "Fill-image component '{{component}}' is inside a parent without stable size/position; add parent sizing (height/min-height/aspect-ratio) and non-static position to avoid CLS."
|
|
41502
41477
|
};
|
|
41503
41478
|
var jsxLayoutFillImageParentMustBeSized = defineCrossRule({
|
|
41504
41479
|
id: "jsx-layout-fill-image-parent-must-be-sized",
|
|
41505
41480
|
severity: "warn",
|
|
41506
|
-
messages:
|
|
41481
|
+
messages: messages160,
|
|
41507
41482
|
meta: {
|
|
41508
41483
|
description: "Require stable parent size and positioning for fill-image component usage.",
|
|
41509
41484
|
fixable: false,
|
|
@@ -41533,7 +41508,7 @@ var jsxLayoutFillImageParentMustBeSized = defineCrossRule({
|
|
|
41533
41508
|
solid.sourceFile,
|
|
41534
41509
|
jsxLayoutFillImageParentMustBeSized.id,
|
|
41535
41510
|
"unsizedFillParent",
|
|
41536
|
-
resolveMessage(
|
|
41511
|
+
resolveMessage(messages160.unsizedFillParent, {
|
|
41537
41512
|
component: element.tag ?? "Image"
|
|
41538
41513
|
}),
|
|
41539
41514
|
"warn"
|
|
@@ -41544,6 +41519,210 @@ var jsxLayoutFillImageParentMustBeSized = defineCrossRule({
|
|
|
41544
41519
|
}
|
|
41545
41520
|
});
|
|
41546
41521
|
|
|
41522
|
+
// src/cross-file/rules/jsx-layout-policy-touch-target.ts
|
|
41523
|
+
var messages161 = {
|
|
41524
|
+
heightTooSmall: "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
|
|
41525
|
+
widthTooSmall: "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
|
|
41526
|
+
paddingTooSmall: "Horizontal padding `{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
|
|
41527
|
+
noReservedBlockSize: "Interactive element `<{{tag}}>` has no declared height (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold.",
|
|
41528
|
+
noReservedInlineSize: "Interactive element `<{{tag}}>` has no declared width (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold."
|
|
41529
|
+
};
|
|
41530
|
+
var INTERACTIVE_HTML_TAGS2 = /* @__PURE__ */ new Set(["button", "a", "input", "select", "textarea", "label", "summary"]);
|
|
41531
|
+
var INTERACTIVE_ARIA_ROLES2 = /* @__PURE__ */ new Set([
|
|
41532
|
+
"button",
|
|
41533
|
+
"link",
|
|
41534
|
+
"checkbox",
|
|
41535
|
+
"radio",
|
|
41536
|
+
"combobox",
|
|
41537
|
+
"listbox",
|
|
41538
|
+
"menuitem",
|
|
41539
|
+
"menuitemcheckbox",
|
|
41540
|
+
"menuitemradio",
|
|
41541
|
+
"option",
|
|
41542
|
+
"switch",
|
|
41543
|
+
"tab"
|
|
41544
|
+
]);
|
|
41545
|
+
function classifyInteractive(node, solid, element, layout) {
|
|
41546
|
+
const tag = node.tagName;
|
|
41547
|
+
if (tag !== null && INTERACTIVE_HTML_TAGS2.has(tag)) {
|
|
41548
|
+
if (tag === "input" || tag === "select" || tag === "textarea") return "input";
|
|
41549
|
+
return "button";
|
|
41550
|
+
}
|
|
41551
|
+
const roleAttr = getJSXAttributeEntity(solid, element, "role");
|
|
41552
|
+
if (roleAttr !== null && roleAttr.valueNode !== null) {
|
|
41553
|
+
const role = getStaticStringFromJSXValue(roleAttr.valueNode);
|
|
41554
|
+
if (role !== null && INTERACTIVE_ARIA_ROLES2.has(role)) return "button";
|
|
41555
|
+
}
|
|
41556
|
+
const hostRef = layout.hostElementRefsByNode.get(node) ?? null;
|
|
41557
|
+
if (hostRef !== null && hostRef.element.tagName !== null) {
|
|
41558
|
+
const hostTag = hostRef.element.tagName;
|
|
41559
|
+
if (INTERACTIVE_HTML_TAGS2.has(hostTag)) {
|
|
41560
|
+
if (hostTag === "input" || hostTag === "select" || hostTag === "textarea") return "input";
|
|
41561
|
+
return "button";
|
|
41562
|
+
}
|
|
41563
|
+
}
|
|
41564
|
+
return null;
|
|
41565
|
+
}
|
|
41566
|
+
function isVisuallyHidden(snapshot) {
|
|
41567
|
+
const position = readKnownNormalized(snapshot, "position");
|
|
41568
|
+
if (position !== "absolute" && position !== "fixed") return false;
|
|
41569
|
+
const node = snapshot.node;
|
|
41570
|
+
const opacityAttr = node.inlineStyleValues.get("opacity");
|
|
41571
|
+
if (opacityAttr === "0") return true;
|
|
41572
|
+
if (node.classTokenSet.has("opacity-0")) return true;
|
|
41573
|
+
return false;
|
|
41574
|
+
}
|
|
41575
|
+
var jsxLayoutPolicyTouchTarget = defineCrossRule({
|
|
41576
|
+
id: "jsx-layout-policy-touch-target",
|
|
41577
|
+
severity: "warn",
|
|
41578
|
+
messages: messages161,
|
|
41579
|
+
meta: {
|
|
41580
|
+
description: "Enforce minimum interactive element sizes per accessibility policy via resolved layout signals.",
|
|
41581
|
+
fixable: false,
|
|
41582
|
+
category: "css-a11y"
|
|
41583
|
+
},
|
|
41584
|
+
check(context, emit) {
|
|
41585
|
+
const policy = getActivePolicy();
|
|
41586
|
+
if (policy === null) return;
|
|
41587
|
+
const policyName = getActivePolicyName() ?? "";
|
|
41588
|
+
const { layout } = context;
|
|
41589
|
+
const elements = layout.elements;
|
|
41590
|
+
for (let i = 0; i < elements.length; i++) {
|
|
41591
|
+
const node = elements[i];
|
|
41592
|
+
if (!node) continue;
|
|
41593
|
+
const ref = readElementRef(layout, node);
|
|
41594
|
+
if (!ref) continue;
|
|
41595
|
+
const kind = classifyInteractive(node, ref.solid, ref.element, layout);
|
|
41596
|
+
if (kind === null) continue;
|
|
41597
|
+
const snapshot = collectSignalSnapshot(context, node);
|
|
41598
|
+
if (isVisuallyHidden(snapshot)) continue;
|
|
41599
|
+
const tag = node.tagName ?? node.tag ?? "element";
|
|
41600
|
+
checkDimension(
|
|
41601
|
+
snapshot,
|
|
41602
|
+
"height",
|
|
41603
|
+
kind === "button" ? policy.minButtonHeight : policy.minInputHeight,
|
|
41604
|
+
layout,
|
|
41605
|
+
node,
|
|
41606
|
+
emit,
|
|
41607
|
+
"heightTooSmall",
|
|
41608
|
+
messages161.heightTooSmall,
|
|
41609
|
+
tag,
|
|
41610
|
+
policyName
|
|
41611
|
+
);
|
|
41612
|
+
checkDimension(
|
|
41613
|
+
snapshot,
|
|
41614
|
+
"min-height",
|
|
41615
|
+
kind === "button" ? policy.minButtonHeight : policy.minInputHeight,
|
|
41616
|
+
layout,
|
|
41617
|
+
node,
|
|
41618
|
+
emit,
|
|
41619
|
+
"heightTooSmall",
|
|
41620
|
+
messages161.heightTooSmall,
|
|
41621
|
+
tag,
|
|
41622
|
+
policyName
|
|
41623
|
+
);
|
|
41624
|
+
checkDimension(
|
|
41625
|
+
snapshot,
|
|
41626
|
+
"width",
|
|
41627
|
+
kind === "button" ? policy.minButtonWidth : policy.minTouchTarget,
|
|
41628
|
+
layout,
|
|
41629
|
+
node,
|
|
41630
|
+
emit,
|
|
41631
|
+
"widthTooSmall",
|
|
41632
|
+
messages161.widthTooSmall,
|
|
41633
|
+
tag,
|
|
41634
|
+
policyName
|
|
41635
|
+
);
|
|
41636
|
+
checkDimension(
|
|
41637
|
+
snapshot,
|
|
41638
|
+
"min-width",
|
|
41639
|
+
kind === "button" ? policy.minButtonWidth : policy.minTouchTarget,
|
|
41640
|
+
layout,
|
|
41641
|
+
node,
|
|
41642
|
+
emit,
|
|
41643
|
+
"widthTooSmall",
|
|
41644
|
+
messages161.widthTooSmall,
|
|
41645
|
+
tag,
|
|
41646
|
+
policyName
|
|
41647
|
+
);
|
|
41648
|
+
if (kind === "button") {
|
|
41649
|
+
checkDimension(
|
|
41650
|
+
snapshot,
|
|
41651
|
+
"padding-left",
|
|
41652
|
+
policy.minButtonHorizontalPadding,
|
|
41653
|
+
layout,
|
|
41654
|
+
node,
|
|
41655
|
+
emit,
|
|
41656
|
+
"paddingTooSmall",
|
|
41657
|
+
messages161.paddingTooSmall,
|
|
41658
|
+
tag,
|
|
41659
|
+
policyName
|
|
41660
|
+
);
|
|
41661
|
+
checkDimension(
|
|
41662
|
+
snapshot,
|
|
41663
|
+
"padding-right",
|
|
41664
|
+
policy.minButtonHorizontalPadding,
|
|
41665
|
+
layout,
|
|
41666
|
+
node,
|
|
41667
|
+
emit,
|
|
41668
|
+
"paddingTooSmall",
|
|
41669
|
+
messages161.paddingTooSmall,
|
|
41670
|
+
tag,
|
|
41671
|
+
policyName
|
|
41672
|
+
);
|
|
41673
|
+
}
|
|
41674
|
+
const reservedSpace = readReservedSpaceFact(layout, node);
|
|
41675
|
+
const minBlock = kind === "button" ? policy.minButtonHeight : policy.minInputHeight;
|
|
41676
|
+
const minInline = kind === "button" ? policy.minButtonWidth : policy.minTouchTarget;
|
|
41677
|
+
if (!reservedSpace.hasUsableBlockDimension) {
|
|
41678
|
+
emitLayoutDiagnostic(
|
|
41679
|
+
layout,
|
|
41680
|
+
node,
|
|
41681
|
+
emit,
|
|
41682
|
+
jsxLayoutPolicyTouchTarget.id,
|
|
41683
|
+
"noReservedBlockSize",
|
|
41684
|
+
messages161.noReservedBlockSize,
|
|
41685
|
+
"warn",
|
|
41686
|
+
{ tag, min: String(minBlock), policy: policyName }
|
|
41687
|
+
);
|
|
41688
|
+
}
|
|
41689
|
+
if (!reservedSpace.hasUsableInlineDimension) {
|
|
41690
|
+
emitLayoutDiagnostic(
|
|
41691
|
+
layout,
|
|
41692
|
+
node,
|
|
41693
|
+
emit,
|
|
41694
|
+
jsxLayoutPolicyTouchTarget.id,
|
|
41695
|
+
"noReservedInlineSize",
|
|
41696
|
+
messages161.noReservedInlineSize,
|
|
41697
|
+
"warn",
|
|
41698
|
+
{ tag, min: String(minInline), policy: policyName }
|
|
41699
|
+
);
|
|
41700
|
+
}
|
|
41701
|
+
}
|
|
41702
|
+
}
|
|
41703
|
+
});
|
|
41704
|
+
function checkDimension(snapshot, signal, min, layout, node, emit, messageId, template, tag, policyName) {
|
|
41705
|
+
const px = readKnownPx(snapshot, signal);
|
|
41706
|
+
if (px === null) return;
|
|
41707
|
+
if (px >= min) return;
|
|
41708
|
+
emitLayoutDiagnostic(
|
|
41709
|
+
layout,
|
|
41710
|
+
node,
|
|
41711
|
+
emit,
|
|
41712
|
+
jsxLayoutPolicyTouchTarget.id,
|
|
41713
|
+
messageId,
|
|
41714
|
+
template,
|
|
41715
|
+
"warn",
|
|
41716
|
+
{
|
|
41717
|
+
signal,
|
|
41718
|
+
value: formatRounded(px),
|
|
41719
|
+
min: String(min),
|
|
41720
|
+
tag,
|
|
41721
|
+
policy: policyName
|
|
41722
|
+
}
|
|
41723
|
+
);
|
|
41724
|
+
}
|
|
41725
|
+
|
|
41547
41726
|
// src/cross-file/rules/index.ts
|
|
41548
41727
|
var rules3 = [
|
|
41549
41728
|
jsxNoUndefinedCssClass,
|
|
@@ -41575,7 +41754,8 @@ var rules3 = [
|
|
|
41575
41754
|
cssLayoutOverflowModeToggleInstability,
|
|
41576
41755
|
cssLayoutBoxSizingToggleWithChrome,
|
|
41577
41756
|
cssLayoutContentVisibilityNoIntrinsicSize,
|
|
41578
|
-
cssLayoutConditionalOffsetShift
|
|
41757
|
+
cssLayoutConditionalOffsetShift,
|
|
41758
|
+
jsxLayoutPolicyTouchTarget
|
|
41579
41759
|
];
|
|
41580
41760
|
|
|
41581
41761
|
// src/cross-file/plugin.ts
|
|
@@ -41877,19 +42057,6 @@ var RULES = [
|
|
|
41877
42057
|
"paragraphSpacingTooSmall": "Paragraph spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` ({{minMultiplier}}\xD7 font-size) for policy `{{policy}}`."
|
|
41878
42058
|
}
|
|
41879
42059
|
},
|
|
41880
|
-
{
|
|
41881
|
-
"id": "css-policy-touch-target",
|
|
41882
|
-
"severity": "warn",
|
|
41883
|
-
"description": "Enforce minimum interactive element sizes per accessibility policy.",
|
|
41884
|
-
"fixable": false,
|
|
41885
|
-
"category": "css-a11y",
|
|
41886
|
-
"plugin": "css",
|
|
41887
|
-
"messages": {
|
|
41888
|
-
"heightTooSmall": "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
|
|
41889
|
-
"widthTooSmall": "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
|
|
41890
|
-
"paddingTooSmall": "Horizontal padding `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`."
|
|
41891
|
-
}
|
|
41892
|
-
},
|
|
41893
42060
|
{
|
|
41894
42061
|
"id": "css-policy-typography",
|
|
41895
42062
|
"severity": "warn",
|
|
@@ -41913,6 +42080,21 @@ var RULES = [
|
|
|
41913
42080
|
"missingReducedMotion": "Animated selector `{{selector}}` lacks prefers-reduced-motion override."
|
|
41914
42081
|
}
|
|
41915
42082
|
},
|
|
42083
|
+
{
|
|
42084
|
+
"id": "jsx-layout-policy-touch-target",
|
|
42085
|
+
"severity": "warn",
|
|
42086
|
+
"description": "Enforce minimum interactive element sizes per accessibility policy via resolved layout signals.",
|
|
42087
|
+
"fixable": false,
|
|
42088
|
+
"category": "css-a11y",
|
|
42089
|
+
"plugin": "cross-file",
|
|
42090
|
+
"messages": {
|
|
42091
|
+
"heightTooSmall": "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
|
|
42092
|
+
"widthTooSmall": "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
|
|
42093
|
+
"paddingTooSmall": "Horizontal padding `{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.",
|
|
42094
|
+
"noReservedBlockSize": "Interactive element `<{{tag}}>` has no declared height (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold.",
|
|
42095
|
+
"noReservedInlineSize": "Interactive element `<{{tag}}>` has no declared width (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold."
|
|
42096
|
+
}
|
|
42097
|
+
},
|
|
41916
42098
|
{
|
|
41917
42099
|
"id": "css-no-discrete-transition",
|
|
41918
42100
|
"severity": "error",
|
|
@@ -43571,7 +43753,7 @@ var RULES = [
|
|
|
43571
43753
|
];
|
|
43572
43754
|
var RULES_BY_CATEGORY = {
|
|
43573
43755
|
"correctness": [{ "id": "avoid-conditional-spreads", "severity": "error", "description": "Disallow conditional spread operators that create empty objects. Patterns like `...(condition ? {...} : {})` are fragile and create unnecessary object creations.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "avoidConditionalSpread": "Avoid conditional spread with empty object fallback. Instead of `...(cond ? {...} : {})`, build the object first with conditional property assignment, then spread once.", "avoidLogicalAndSpread": "Avoid logical AND spread pattern. Instead of `...(cond && {...})`, use explicit conditional property assignment for clarity." } }, { "id": "avoid-non-null-assertions", "severity": "error", "description": "Disallow non-null assertion operator (`!`). Use optional chaining, nullish coalescing, or proper type narrowing instead.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "avoidNonNull": 'Avoid non-null assertion on "{{name}}". Non-null assertions bypass type safety. Use optional chaining (`?.`), nullish coalescing (`??`), or proper type narrowing instead.' } }, { "id": "avoid-object-assign", "severity": "error", "description": "Disallow Object.assign(). Prefer object spread syntax or structuredClone() for copying objects.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "avoidMerge": "Avoid Object.assign() for merging. Use object spread syntax { ...obj } instead.", "avoidMutation": "Avoid Object.assign() for mutation. Consider immutable patterns like { ...existing, ...props }." } }, { "id": "avoid-object-spread", "severity": "error", "description": "Disallow object spread operators that break Solid's fine-grained reactivity.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "avoidObjectCopy": "Avoid object spread for copying. Use direct property access.", "avoidObjectMerge": "Avoid object spread for merging. Use mergeProps() from 'solid-js'.", "avoidObjectUpdate": "Avoid object spread for updates. Use produce() or direct assignment.", "avoidJsxSpread": "Avoid JSX prop spreading. Use splitProps() to separate props.", "avoidRestDestructure": "Avoid rest destructuring. Use splitProps() from 'solid-js'.", "avoidPropsSpread": "Spreading props breaks reactivity. Use splitProps() to separate known props.", "avoidStoreSpread": "Spreading store creates a static snapshot. Access properties directly.", "avoidSignalSpread": "Spreading signal result captures current value. Wrap in createMemo().", "avoidClassListSpread": "Spreading in classList breaks reactivity. Wrap in createMemo().", "avoidStyleSpread": "Spreading in style breaks reactivity. Wrap in createMemo().", "unnecessarySplitProps": "Unnecessary splitProps with empty array. Remove it and use {{source}} directly." } }, { "id": "avoid-type-casting", "severity": "error", "description": "Disallow type casting methods that bypass TypeScript's type safety. Includes unnecessary casts, double assertions, casting to any, type predicates, and unsafe generic assertions.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "unnecessaryCast": `Unnecessary type assertion: "{{name}}" is already of type "{{exprType}}", which is assignable to "{{type}}". Remove the cast - it adds noise and suggests you don't understand the types.`, "doubleAssertion": 'Double assertion detected: "{{name}}" is cast through unknown/any to "{{type}}". This bypasses type safety. You are creating sloppy architecture.', "castToAny": 'Casting "{{name}}" to `any` disables all type checking. Use `unknown` with proper type guards, or fix the underlying type issue.', "castToUnknown": "Casting to `unknown` requires runtime type checks before use. You are creating sloppy architecture.", "simpleAssertion": 'Type assertion on "{{name}}" to "{{type}}" bypasses type checking. Why are you doing this? Do you EVEN need this? This is sloppy architecture.', "assertionInLoop": 'Type assertion on "{{name}}" inside a loop. Repeated casts to "{{type}}" without validation can mask type errors. Consider validating the type once before the loop.', "importAssertion": 'Type assertion on dynamic import to "{{type}}". Import types should be validated at runtime or use proper module type declarations.', "typePredicate": 'Type predicate function asserts "{{param}}" is "{{type}}". Why are you doing this? Do you EVEN need this? This is sloppy architecture.', "unsafeGeneric": 'Casting to generic type parameter "{{typeParam}}" without runtime validation. The function returns an unverified type. This is sloppy architecture.' } }, { "id": "avoid-unsafe-type-annotations", "severity": "error", "description": "Disallow `any` and `unknown` in value-level type annotation positions (parameters, returns, variables, properties)", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "anyParameter": "Parameter '{{name}}' is typed `any`{{inFunction}}. This disables type checking for all callers. Use a specific type, a generic, or `unknown` with proper type narrowing.", "anyReturn": "Function '{{name}}' returns `any`. This disables type checking for all callers. Use a specific return type.", "anyVariable": "Variable '{{name}}' is typed `any`. This disables all type checking on this variable. Use a specific type or `unknown` with type narrowing.", "anyProperty": "Property '{{name}}' is typed `any`. This disables type checking for all accesses. Use a specific type.", "unknownParameter": "Parameter '{{name}}' is typed `unknown`{{inFunction}}. Callers can pass anything and the function body requires type narrowing on every use. Use a specific type or a generic constraint.", "unknownReturn": "Function '{{name}}' returns `unknown`. Callers must narrow the return value before use. Use a specific return type or a generic.", "unknownVariable": "Variable '{{name}}' is typed `unknown`. Every use requires type narrowing. Use a specific type or parse the value at the boundary.", "unknownProperty": "Property '{{name}}' is typed `unknown`. Every access requires type narrowing. Use a specific type." } }, { "id": "event-handlers", "severity": "error", "description": "Enforce naming DOM element event handlers consistently and prevent Solid's analysis from misunderstanding whether a prop should be an event handler.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "detectedAttr": 'The "{{name}}" prop looks like an event handler but has a static value ({{staticValue}}), so Solid will treat it as an attribute instead of attaching an event listener. Use attr:{{name}} to make this explicit, or provide a function value.', "naming": 'The "{{name}}" prop is ambiguous. Solid cannot determine if this is an event handler or an attribute. Use {{handlerName}} for an event handler, or {{attrName}} for an attribute.', "capitalization": 'The "{{name}}" prop should be {{fixedName}} for Solid to recognize it as an event handler. Event handlers use camelCase with an uppercase letter after "on".', "nonstandard": 'The "{{name}}" prop uses a nonstandard event name. Use {{fixedName}} instead, which is the standard DOM event name that Solid recognizes.', "makeHandler": "Change {{name}} to {{handlerName}} (event handler).", "makeAttr": "Change {{name}} to {{attrName}} (attribute).", "spreadHandler": 'The "{{name}}" prop is being spread into JSX, which prevents Solid from attaching it as an event listener. Add it directly as a JSX attribute instead: {{name}}={...}.' } }, { "id": "missing-jsdoc-comments", "severity": "error", "description": "Require JSDoc comments on functions with appropriate tags for parameters, return values, and throws.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "missingJsdoc": "Function '{{name}}' is missing a JSDoc comment.", "missingParam": "JSDoc for '{{name}}' is missing @param tag for '{{param}}'.", "missingReturn": "JSDoc for '{{name}}' is missing @returns tag.", "missingThrows": "JSDoc for '{{name}}' is missing @throws tag.", "missingExample": "JSDoc for '{{name}}' is missing @example tag.", "missingClassJsdoc": "Class '{{name}}' is missing a JSDoc comment.", "missingPropertyJsdoc": "Property '{{name}}' is missing a JSDoc comment." } }, { "id": "no-ai-slop-comments", "severity": "error", "description": "Disallow comments containing specified forbidden words or phrases. Useful for enforcing comment style guidelines and detecting AI-generated boilerplate.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "forbiddenWord": "Comment contains forbidden word '{{word}}'." } }, { "id": "no-array-handlers", "severity": "error", "description": "Disallow array handlers in JSX event properties.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "noArrayHandlers": 'Passing an array to "{{handlerName}}" is type-unsafe. The array syntax `[handler, data]` passes data as the first argument, making the event object the second argument. Use a closure instead: `{{handlerName}}={() => handler(data)}`.' } }, { "id": "no-banner-comments", "severity": "error", "description": "Disallow banner-style comments with repeated separator characters.", "fixable": true, "category": "correctness", "plugin": "solid", "messages": { "banner": "Avoid banner-style comments with repeated separator characters. Use simple comments instead." } }, { "id": "no-destructure", "severity": "error", "description": "Disallow destructuring props in Solid components. Props must be accessed via property access (props.x) to preserve reactivity.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "noDestructure": "Destructuring component props breaks Solid's reactivity. Props are reactive getters, so `{ a }` captures the value at component creation time and won't update. Use `props.a` to access props reactively.", "noDestructureWithDefaults": "Destructuring component props breaks Solid's reactivity. For default values, use `mergeProps({ a: defaultValue }, props)` instead of `{ a = defaultValue }`.", "noDestructureWithRest": "Destructuring component props breaks Solid's reactivity. For rest patterns, use `splitProps(props, ['a', 'b'])` instead of `{ a, b, ...rest }`.", "noDestructureWithBoth": "Destructuring component props breaks Solid's reactivity. For default values with rest, use `splitProps(mergeProps({ a: defaultValue }, props), ['a'])` to combine both patterns." } }, { "id": "no-inline-imports", "severity": "error", "description": "Disallow inline type imports. Import types at the top of the file for clarity and maintainability.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "inlineImport": "Avoid inline imports. Import `{{specifier}}` at the top of the file instead." } }, { "id": "string-concat-in-loop", "severity": "error", "description": "Disallow string concatenation with += inside loops. Use array.push() and .join() instead.", "fixable": false, "category": "correctness", "plugin": "solid", "messages": { "stringConcatInLoop": "Avoid string concatenation with += inside loops. Use an array with .push() and .join() instead." } }],
|
|
43574
|
-
"css-a11y": [{ "id": "css-no-outline-none-without-focus-visible", "severity": "error", "description": "Disallow removing outline without explicit focus-visible replacement.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "missingFocusVisible": "Focus outline removed without matching `:focus-visible` replacement." } }, { "id": "css-policy-contrast", "severity": "warn", "description": "Enforce minimum contrast ratio between foreground and background colors per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "insufficientContrast": "Contrast ratio `{{ratio}}:1` between `{{fg}}` and `{{bg}}` is below the minimum `{{min}}:1` for `{{textSize}}` text in policy `{{policy}}`." } }, { "id": "css-policy-spacing", "severity": "warn", "description": "Enforce minimum letter-spacing, word-spacing, and paragraph spacing per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "letterSpacingTooSmall": "Letter spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`.", "wordSpacingTooSmall": "Word spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`.", "paragraphSpacingTooSmall": "Paragraph spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` ({{minMultiplier}}\xD7 font-size) for policy `{{policy}}`." } }, { "id": "css-policy-
|
|
43756
|
+
"css-a11y": [{ "id": "css-no-outline-none-without-focus-visible", "severity": "error", "description": "Disallow removing outline without explicit focus-visible replacement.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "missingFocusVisible": "Focus outline removed without matching `:focus-visible` replacement." } }, { "id": "css-policy-contrast", "severity": "warn", "description": "Enforce minimum contrast ratio between foreground and background colors per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "insufficientContrast": "Contrast ratio `{{ratio}}:1` between `{{fg}}` and `{{bg}}` is below the minimum `{{min}}:1` for `{{textSize}}` text in policy `{{policy}}`." } }, { "id": "css-policy-spacing", "severity": "warn", "description": "Enforce minimum letter-spacing, word-spacing, and paragraph spacing per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "letterSpacingTooSmall": "Letter spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`.", "wordSpacingTooSmall": "Word spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`.", "paragraphSpacingTooSmall": "Paragraph spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` ({{minMultiplier}}\xD7 font-size) for policy `{{policy}}`." } }, { "id": "css-policy-typography", "severity": "warn", "description": "Enforce minimum font sizes and line heights per accessibility policy.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "fontTooSmall": "Font size `{{value}}` ({{resolved}}px) is below the `{{context}}` minimum of `{{min}}px` for policy `{{policy}}`.", "lineHeightTooSmall": "Line height `{{value}}` is below the `{{context}}` minimum of `{{min}}` for policy `{{policy}}`." } }, { "id": "css-require-reduced-motion-override", "severity": "warn", "description": "Require reduced-motion override for animated selectors.", "fixable": false, "category": "css-a11y", "plugin": "css", "messages": { "missingReducedMotion": "Animated selector `{{selector}}` lacks prefers-reduced-motion override." } }, { "id": "jsx-layout-policy-touch-target", "severity": "warn", "description": "Enforce minimum interactive element sizes per accessibility policy via resolved layout signals.", "fixable": false, "category": "css-a11y", "plugin": "cross-file", "messages": { "heightTooSmall": "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.", "widthTooSmall": "`{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.", "paddingTooSmall": "Horizontal padding `{{signal}}` of `{{value}}px` is below the minimum `{{min}}px` for interactive element `<{{tag}}>` in policy `{{policy}}`.", "noReservedBlockSize": "Interactive element `<{{tag}}>` has no declared height (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold.", "noReservedInlineSize": "Interactive element `<{{tag}}>` has no declared width (minimum `{{min}}px` required by policy `{{policy}}`). The element is content-sized and may not meet the touch-target threshold." } }],
|
|
43575
43757
|
"css-animation": [{ "id": "css-no-discrete-transition", "severity": "error", "description": "Disallow transitions on discrete CSS properties.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "discreteTransition": "Property `{{property}}` is discrete and should not be transitioned." } }, { "id": "css-no-empty-keyframes", "severity": "error", "description": "Disallow empty @keyframes rules.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "emptyKeyframes": "@keyframes `{{name}}` has no effective keyframes." } }, { "id": "no-layout-property-animation", "severity": "warn", "description": "Disallow animating layout-affecting properties.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "avoidLayoutAnimation": "Avoid animating layout property `{{property}}`. Prefer transform or opacity to reduce layout thrashing." } }, { "id": "no-transition-all", "severity": "warn", "description": "Disallow transition: all and transition-property: all.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "avoidTransitionAll": "Avoid `transition: all`. Transition specific properties to reduce unnecessary style and paint work." } }, { "id": "no-unknown-animation-name", "severity": "error", "description": "Disallow animation names that do not match declared keyframes.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "unknownAnimationName": "Animation name `{{name}}` in `{{property}}` does not match any declared @keyframes." } }, { "id": "no-unused-keyframes", "severity": "warn", "description": "Disallow unused @keyframes declarations.", "fixable": false, "category": "css-animation", "plugin": "css", "messages": { "unusedKeyframes": "@keyframes `{{name}}` is never referenced by animation declarations." } }],
|
|
43576
43758
|
"css-cascade": [{ "id": "declaration-no-overridden-within-rule", "severity": "warn", "description": "Disallow duplicate declarations of the same property within a single rule block.", "fixable": false, "category": "css-cascade", "plugin": "css", "messages": { "overriddenWithinRule": "Declaration `{{property}}` is overridden later in the same rule. Keep one final declaration per property." } }, { "id": "media-query-overlap-conflict", "severity": "warn", "description": "Disallow conflicting declarations in partially overlapping media queries.", "fixable": false, "category": "css-cascade", "plugin": "css", "messages": { "mediaOverlapConflict": "Overlapping media queries set different `{{property}}` values for `{{selector}}` in the same overlap range." } }, { "id": "no-descending-specificity-conflict", "severity": "warn", "description": "Disallow lower-specificity selectors after higher-specificity selectors for the same property.", "fixable": false, "category": "css-cascade", "plugin": "css", "messages": { "descendingSpecificity": "Lower-specificity selector `{{laterSelector}}` appears after `{{earlierSelector}}` for `{{property}}`, creating brittle cascade behavior." } }, { "id": "no-layer-order-inversion", "severity": "warn", "description": "Disallow source-order assumptions that are inverted by layer precedence.", "fixable": false, "category": "css-cascade", "plugin": "css", "messages": { "layerOrderInversion": "Declaration for `{{property}}` in selector `{{selector}}` appears later but is overridden by an earlier declaration due to @layer precedence." } }, { "id": "no-redundant-override-pairs", "severity": "warn", "description": "Disallow declarations that are deterministically overridden in the same selector context.", "fixable": false, "category": "css-cascade", "plugin": "css", "messages": { "redundantOverride": "Declaration `{{property}}` is always overridden later by the same selector in the same cascade context." } }],
|
|
43577
43759
|
"css-jsx": [{ "id": "css-no-unreferenced-component-class", "severity": "warn", "description": "Detect CSS classes that are never referenced by static JSX class attributes.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "unreferencedClass": "CSS class '{{className}}' is defined but not referenced by static JSX class attributes" } }, { "id": "jsx-classlist-boolean-values", "severity": "error", "description": "Require classList values to be boolean-like expressions.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "nonBooleanValue": "classList value for `{{name}}` must be boolean." } }, { "id": "jsx-classlist-no-accessor-reference", "severity": "error", "description": "Disallow passing accessor references directly as classList values.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "accessorReference": "Signal accessor `{{name}}` must be called in classList value (use {{name}}())." } }, { "id": "jsx-classlist-no-constant-literals", "severity": "warn", "description": "Disallow classList entries with constant true/false values.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "constantEntry": "classList entry `{{name}}: {{value}}` is constant; move it to static class." } }, { "id": "jsx-classlist-static-keys", "severity": "error", "description": "Require classList keys to be static and non-computed.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "nonStaticKey": "classList key must be statically known for reliable class mapping." } }, { "id": "jsx-layout-classlist-geometry-toggle", "severity": "warn", "description": "Flag classList-driven class toggles that map to layout-affecting CSS geometry changes.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "classListGeometryToggle": "classList toggles '{{className}}', and matching CSS changes layout-affecting '{{property}}', which can cause CLS." } }, { "id": "jsx-layout-fill-image-parent-must-be-sized", "severity": "warn", "description": "Require stable parent size and positioning for fill-image component usage.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "unsizedFillParent": "Fill-image component '{{component}}' is inside a parent without stable size/position; add parent sizing (height/min-height/aspect-ratio) and non-static position to avoid CLS." } }, { "id": "jsx-layout-picture-source-ratio-consistency", "severity": "warn", "description": "Require consistent intrinsic aspect ratios across <picture> sources and fallback image.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "inconsistentPictureRatio": "`<picture>` source ratio {{sourceRatio}} differs from fallback img ratio {{imgRatio}}, which can cause reserved-space mismatch and CLS." } }, { "id": "jsx-layout-unstable-style-toggle", "severity": "warn", "description": "Flag dynamic inline style values on layout-sensitive properties that can trigger CLS.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "unstableLayoutStyleToggle": "Dynamic style value for '{{property}}' can toggle layout geometry at runtime and cause CLS." } }, { "id": "jsx-no-duplicate-class-token-class-classlist", "severity": "warn", "description": "Disallow duplicate class tokens between class and classList on the same JSX element.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "duplicateClassToken": "Class token `{{name}}` appears in both class and classList." } }, { "id": "jsx-no-undefined-css-class", "severity": "error", "description": "Detect undefined CSS class names in JSX", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "undefinedClass": "CSS class '{{className}}' is not defined in project CSS files" } }, { "id": "jsx-style-kebab-case-keys", "severity": "error", "description": "Require kebab-case keys in JSX style object literals.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "kebabStyleKey": "Style key `{{name}}` should be `{{kebab}}` in Solid style objects." } }, { "id": "jsx-style-no-function-values", "severity": "error", "description": "Disallow function values in JSX style objects.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "functionStyleValue": "Style value for `{{name}}` is a function; pass computed value instead." } }, { "id": "jsx-style-no-unused-custom-prop", "severity": "warn", "description": "Detect inline style custom properties that are never consumed by CSS var() references.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "unusedInlineVar": "Inline custom property `{{name}}` is never read via var({{name}})." } }, { "id": "jsx-style-policy", "severity": "warn", "description": "Enforce accessibility policy thresholds on inline JSX style objects.", "fixable": false, "category": "css-jsx", "plugin": "cross-file", "messages": { "fontTooSmall": "Inline style `{{prop}}: {{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for policy `{{policy}}`.", "lineHeightTooSmall": "Inline style `line-height: {{value}}` is below the minimum `{{min}}` for policy `{{policy}}`.", "heightTooSmall": "Inline style `{{prop}}: {{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for interactive elements in policy `{{policy}}`.", "letterSpacingTooSmall": "Inline style `letter-spacing: {{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`.", "wordSpacingTooSmall": "Inline style `word-spacing: {{value}}` ({{resolved}}em) is below the minimum `{{min}}em` for policy `{{policy}}`." } }],
|