@drskillissue/ganko 0.2.71 → 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-SVX3WRFV.js → chunk-MJKIL7DJ.js} +341 -264
- 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 +322 -258
- package/dist/eslint-plugin.cjs.map +1 -1
- package/dist/eslint-plugin.js +1 -1
- package/dist/index.cjs +356 -277
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- 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 -1
- package/dist/chunk-5OEDGKHL.js.map +0 -1
- package/dist/chunk-SVX3WRFV.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -29835,148 +29835,8 @@ function checkParagraphSpacing(graph, emit, policy, name) {
|
|
|
29835
29835
|
}
|
|
29836
29836
|
}
|
|
29837
29837
|
|
|
29838
|
-
// src/css/rules/a11y/css-policy-touch-target.ts
|
|
29839
|
-
var messages125 = {
|
|
29840
|
-
heightTooSmall: "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
|
|
29841
|
-
widthTooSmall: "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
|
|
29842
|
-
paddingTooSmall: "Horizontal padding `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`."
|
|
29843
|
-
};
|
|
29844
|
-
function classifyRule(rule) {
|
|
29845
|
-
if (rule.elementKinds.has("button")) return "button";
|
|
29846
|
-
if (rule.elementKinds.has("input")) return "input";
|
|
29847
|
-
return null;
|
|
29848
|
-
}
|
|
29849
|
-
var HEIGHT_PROPERTIES = /* @__PURE__ */ new Set(["height", "min-height"]);
|
|
29850
|
-
var WIDTH_PROPERTIES = /* @__PURE__ */ new Set(["width", "min-width"]);
|
|
29851
|
-
var HPADDING_PROPERTIES = /* @__PURE__ */ new Set(["padding-left", "padding-right", "padding-inline", "padding-inline-start", "padding-inline-end"]);
|
|
29852
|
-
var cssPolicyTouchTarget = defineCSSRule({
|
|
29853
|
-
id: "css-policy-touch-target",
|
|
29854
|
-
severity: "warn",
|
|
29855
|
-
messages: messages125,
|
|
29856
|
-
meta: {
|
|
29857
|
-
description: "Enforce minimum interactive element sizes per accessibility policy.",
|
|
29858
|
-
fixable: false,
|
|
29859
|
-
category: "css-a11y"
|
|
29860
|
-
},
|
|
29861
|
-
options: {},
|
|
29862
|
-
check(graph, emit) {
|
|
29863
|
-
const policy = getActivePolicy();
|
|
29864
|
-
if (policy === null) return;
|
|
29865
|
-
const name = getActivePolicyName() ?? "";
|
|
29866
|
-
const decls = graph.declarationsForProperties(
|
|
29867
|
-
"height",
|
|
29868
|
-
"min-height",
|
|
29869
|
-
"width",
|
|
29870
|
-
"min-width",
|
|
29871
|
-
"padding-left",
|
|
29872
|
-
"padding-right",
|
|
29873
|
-
"padding-inline",
|
|
29874
|
-
"padding-inline-start",
|
|
29875
|
-
"padding-inline-end"
|
|
29876
|
-
);
|
|
29877
|
-
for (let i = 0; i < decls.length; i++) {
|
|
29878
|
-
const d = decls[i];
|
|
29879
|
-
if (!d) continue;
|
|
29880
|
-
if (!d.rule) continue;
|
|
29881
|
-
const prop = d.property.toLowerCase();
|
|
29882
|
-
const kind = classifyRule(d.rule);
|
|
29883
|
-
if (!kind) continue;
|
|
29884
|
-
if (isVisuallyHiddenInput(d.rule)) continue;
|
|
29885
|
-
const px = parsePxValue(d.value);
|
|
29886
|
-
if (px === null) continue;
|
|
29887
|
-
if (HEIGHT_PROPERTIES.has(prop)) {
|
|
29888
|
-
const min = kind === "button" ? policy.minButtonHeight : policy.minInputHeight;
|
|
29889
|
-
if (px >= min) continue;
|
|
29890
|
-
emitCSSDiagnostic(
|
|
29891
|
-
emit,
|
|
29892
|
-
d.file.path,
|
|
29893
|
-
d.startLine,
|
|
29894
|
-
d.startColumn,
|
|
29895
|
-
cssPolicyTouchTarget,
|
|
29896
|
-
"heightTooSmall",
|
|
29897
|
-
resolveMessage(messages125.heightTooSmall, {
|
|
29898
|
-
property: d.property,
|
|
29899
|
-
value: d.value.trim(),
|
|
29900
|
-
resolved: String(Math.round(px * 100) / 100),
|
|
29901
|
-
min: String(min),
|
|
29902
|
-
element: kind,
|
|
29903
|
-
policy: name
|
|
29904
|
-
})
|
|
29905
|
-
);
|
|
29906
|
-
continue;
|
|
29907
|
-
}
|
|
29908
|
-
if (WIDTH_PROPERTIES.has(prop)) {
|
|
29909
|
-
const min = kind === "button" ? policy.minButtonWidth : policy.minTouchTarget;
|
|
29910
|
-
if (px >= min) continue;
|
|
29911
|
-
emitCSSDiagnostic(
|
|
29912
|
-
emit,
|
|
29913
|
-
d.file.path,
|
|
29914
|
-
d.startLine,
|
|
29915
|
-
d.startColumn,
|
|
29916
|
-
cssPolicyTouchTarget,
|
|
29917
|
-
"widthTooSmall",
|
|
29918
|
-
resolveMessage(messages125.widthTooSmall, {
|
|
29919
|
-
property: d.property,
|
|
29920
|
-
value: d.value.trim(),
|
|
29921
|
-
resolved: String(Math.round(px * 100) / 100),
|
|
29922
|
-
min: String(min),
|
|
29923
|
-
element: kind,
|
|
29924
|
-
policy: name
|
|
29925
|
-
})
|
|
29926
|
-
);
|
|
29927
|
-
continue;
|
|
29928
|
-
}
|
|
29929
|
-
if (kind === "button" && HPADDING_PROPERTIES.has(prop)) {
|
|
29930
|
-
if (px >= policy.minButtonHorizontalPadding) continue;
|
|
29931
|
-
emitCSSDiagnostic(
|
|
29932
|
-
emit,
|
|
29933
|
-
d.file.path,
|
|
29934
|
-
d.startLine,
|
|
29935
|
-
d.startColumn,
|
|
29936
|
-
cssPolicyTouchTarget,
|
|
29937
|
-
"paddingTooSmall",
|
|
29938
|
-
resolveMessage(messages125.paddingTooSmall, {
|
|
29939
|
-
value: d.value.trim(),
|
|
29940
|
-
resolved: String(Math.round(px * 100) / 100),
|
|
29941
|
-
min: String(policy.minButtonHorizontalPadding),
|
|
29942
|
-
element: kind,
|
|
29943
|
-
policy: name
|
|
29944
|
-
})
|
|
29945
|
-
);
|
|
29946
|
-
}
|
|
29947
|
-
}
|
|
29948
|
-
}
|
|
29949
|
-
});
|
|
29950
|
-
function isVisuallyHiddenInput(rule) {
|
|
29951
|
-
if (!hasPositionAbsoluteOrFixed(rule)) return false;
|
|
29952
|
-
if (!hasOpacityZero(rule)) return false;
|
|
29953
|
-
return true;
|
|
29954
|
-
}
|
|
29955
|
-
function hasPositionAbsoluteOrFixed(rule) {
|
|
29956
|
-
const positionDecls = rule.declarationIndex.get("position");
|
|
29957
|
-
if (!positionDecls || positionDecls.length === 0) return false;
|
|
29958
|
-
for (let i = 0; i < positionDecls.length; i++) {
|
|
29959
|
-
const decl = positionDecls[i];
|
|
29960
|
-
if (!decl) continue;
|
|
29961
|
-
const v = decl.value.trim().toLowerCase();
|
|
29962
|
-
if (v === "absolute" || v === "fixed") return true;
|
|
29963
|
-
}
|
|
29964
|
-
return false;
|
|
29965
|
-
}
|
|
29966
|
-
function hasOpacityZero(rule) {
|
|
29967
|
-
const opacityDecls = rule.declarationIndex.get("opacity");
|
|
29968
|
-
if (!opacityDecls || opacityDecls.length === 0) return false;
|
|
29969
|
-
for (let i = 0; i < opacityDecls.length; i++) {
|
|
29970
|
-
const decl = opacityDecls[i];
|
|
29971
|
-
if (!decl) continue;
|
|
29972
|
-
const v = decl.value.trim();
|
|
29973
|
-
if (v === "0") return true;
|
|
29974
|
-
}
|
|
29975
|
-
return false;
|
|
29976
|
-
}
|
|
29977
|
-
|
|
29978
29838
|
// src/css/rules/a11y/css-policy-typography.ts
|
|
29979
|
-
var
|
|
29839
|
+
var messages125 = {
|
|
29980
29840
|
fontTooSmall: "Font size `{{value}}` ({{resolved}}px) is below the `{{context}}` minimum of `{{min}}px` for policy `{{policy}}`.",
|
|
29981
29841
|
lineHeightTooSmall: "Line height `{{value}}` is below the `{{context}}` minimum of `{{min}}` for policy `{{policy}}`."
|
|
29982
29842
|
};
|
|
@@ -30012,7 +29872,7 @@ function resolveLineHeightContext(d, p) {
|
|
|
30012
29872
|
var cssPolicyTypography = defineCSSRule({
|
|
30013
29873
|
id: "css-policy-typography",
|
|
30014
29874
|
severity: "warn",
|
|
30015
|
-
messages:
|
|
29875
|
+
messages: messages125,
|
|
30016
29876
|
meta: {
|
|
30017
29877
|
description: "Enforce minimum font sizes and line heights per accessibility policy.",
|
|
30018
29878
|
fixable: false,
|
|
@@ -30039,7 +29899,7 @@ var cssPolicyTypography = defineCSSRule({
|
|
|
30039
29899
|
d.startColumn,
|
|
30040
29900
|
cssPolicyTypography,
|
|
30041
29901
|
"fontTooSmall",
|
|
30042
|
-
resolveMessage(
|
|
29902
|
+
resolveMessage(messages125.fontTooSmall, {
|
|
30043
29903
|
value: d.value.trim(),
|
|
30044
29904
|
resolved: String(Math.round(px * 100) / 100),
|
|
30045
29905
|
context,
|
|
@@ -30066,7 +29926,7 @@ var cssPolicyTypography = defineCSSRule({
|
|
|
30066
29926
|
d.startColumn,
|
|
30067
29927
|
cssPolicyTypography,
|
|
30068
29928
|
"lineHeightTooSmall",
|
|
30069
|
-
resolveMessage(
|
|
29929
|
+
resolveMessage(messages125.lineHeightTooSmall, {
|
|
30070
29930
|
value: d.value.trim(),
|
|
30071
29931
|
context,
|
|
30072
29932
|
min: String(min),
|
|
@@ -30084,7 +29944,7 @@ var ZERO_S = /(^|\s|,)0s($|\s|,)/;
|
|
|
30084
29944
|
var TIME_VALUE_G = /([0-9]*\.?[0-9]+)(ms|s)/g;
|
|
30085
29945
|
var AMPERSAND_G = /&/g;
|
|
30086
29946
|
var WHITESPACE_G = /\s+/g;
|
|
30087
|
-
var
|
|
29947
|
+
var messages126 = {
|
|
30088
29948
|
missingReducedMotion: "Animated selector `{{selector}}` lacks prefers-reduced-motion override."
|
|
30089
29949
|
};
|
|
30090
29950
|
function isAnimationDecl(p) {
|
|
@@ -30188,7 +30048,7 @@ function normalizeSelector2(s) {
|
|
|
30188
30048
|
var cssRequireReducedMotionOverride = defineCSSRule({
|
|
30189
30049
|
id: "css-require-reduced-motion-override",
|
|
30190
30050
|
severity: "warn",
|
|
30191
|
-
messages:
|
|
30051
|
+
messages: messages126,
|
|
30192
30052
|
meta: {
|
|
30193
30053
|
description: "Require reduced-motion override for animated selectors.",
|
|
30194
30054
|
fixable: false,
|
|
@@ -30251,20 +30111,20 @@ var cssRequireReducedMotionOverride = defineCSSRule({
|
|
|
30251
30111
|
d.startColumn,
|
|
30252
30112
|
cssRequireReducedMotionOverride,
|
|
30253
30113
|
"missingReducedMotion",
|
|
30254
|
-
resolveMessage(
|
|
30114
|
+
resolveMessage(messages126.missingReducedMotion, { selector: r.selectorText })
|
|
30255
30115
|
);
|
|
30256
30116
|
}
|
|
30257
30117
|
}
|
|
30258
30118
|
});
|
|
30259
30119
|
|
|
30260
30120
|
// src/css/rules/structure/css-no-empty-rule.ts
|
|
30261
|
-
var
|
|
30121
|
+
var messages127 = {
|
|
30262
30122
|
emptyRule: "Empty rule `{{selector}}` should be removed."
|
|
30263
30123
|
};
|
|
30264
30124
|
var cssNoEmptyRule = defineCSSRule({
|
|
30265
30125
|
id: "css-no-empty-rule",
|
|
30266
30126
|
severity: "warn",
|
|
30267
|
-
messages:
|
|
30127
|
+
messages: messages127,
|
|
30268
30128
|
meta: {
|
|
30269
30129
|
description: "Disallow empty CSS rules.",
|
|
30270
30130
|
fixable: false,
|
|
@@ -30282,20 +30142,20 @@ var cssNoEmptyRule = defineCSSRule({
|
|
|
30282
30142
|
rule.startColumn,
|
|
30283
30143
|
cssNoEmptyRule,
|
|
30284
30144
|
"emptyRule",
|
|
30285
|
-
resolveMessage(
|
|
30145
|
+
resolveMessage(messages127.emptyRule, { selector: rule.selectorText })
|
|
30286
30146
|
);
|
|
30287
30147
|
}
|
|
30288
30148
|
}
|
|
30289
30149
|
});
|
|
30290
30150
|
|
|
30291
30151
|
// src/css/rules/structure/css-no-unknown-container-name.ts
|
|
30292
|
-
var
|
|
30152
|
+
var messages128 = {
|
|
30293
30153
|
unknownContainer: "Unknown container name `{{name}}` in @container query."
|
|
30294
30154
|
};
|
|
30295
30155
|
var cssNoUnknownContainerName = defineCSSRule({
|
|
30296
30156
|
id: "css-no-unknown-container-name",
|
|
30297
30157
|
severity: "error",
|
|
30298
|
-
messages:
|
|
30158
|
+
messages: messages128,
|
|
30299
30159
|
meta: {
|
|
30300
30160
|
description: "Disallow unknown named containers in @container queries.",
|
|
30301
30161
|
fixable: false,
|
|
@@ -30315,20 +30175,20 @@ var cssNoUnknownContainerName = defineCSSRule({
|
|
|
30315
30175
|
1,
|
|
30316
30176
|
cssNoUnknownContainerName,
|
|
30317
30177
|
"unknownContainer",
|
|
30318
|
-
resolveMessage(
|
|
30178
|
+
resolveMessage(messages128.unknownContainer, { name })
|
|
30319
30179
|
);
|
|
30320
30180
|
}
|
|
30321
30181
|
}
|
|
30322
30182
|
});
|
|
30323
30183
|
|
|
30324
30184
|
// src/css/rules/structure/css-no-unused-container-name.ts
|
|
30325
|
-
var
|
|
30185
|
+
var messages129 = {
|
|
30326
30186
|
unusedContainer: "Container name `{{name}}` is declared but never queried."
|
|
30327
30187
|
};
|
|
30328
30188
|
var cssNoUnusedContainerName = defineCSSRule({
|
|
30329
30189
|
id: "css-no-unused-container-name",
|
|
30330
30190
|
severity: "warn",
|
|
30331
|
-
messages:
|
|
30191
|
+
messages: messages129,
|
|
30332
30192
|
meta: {
|
|
30333
30193
|
description: "Disallow unused named containers.",
|
|
30334
30194
|
fixable: false,
|
|
@@ -30351,7 +30211,7 @@ var cssNoUnusedContainerName = defineCSSRule({
|
|
|
30351
30211
|
d.startColumn,
|
|
30352
30212
|
cssNoUnusedContainerName,
|
|
30353
30213
|
"unusedContainer",
|
|
30354
|
-
resolveMessage(
|
|
30214
|
+
resolveMessage(messages129.unusedContainer, { name })
|
|
30355
30215
|
);
|
|
30356
30216
|
}
|
|
30357
30217
|
}
|
|
@@ -30359,13 +30219,13 @@ var cssNoUnusedContainerName = defineCSSRule({
|
|
|
30359
30219
|
});
|
|
30360
30220
|
|
|
30361
30221
|
// src/css/rules/structure/layer-requirement-for-component-rules.ts
|
|
30362
|
-
var
|
|
30222
|
+
var messages130 = {
|
|
30363
30223
|
missingLayer: "Rule `{{selector}}` is not inside any @layer block while this file uses @layer. Place component rules inside an explicit layer."
|
|
30364
30224
|
};
|
|
30365
30225
|
var layerRequirementForComponentRules = defineCSSRule({
|
|
30366
30226
|
id: "layer-requirement-for-component-rules",
|
|
30367
30227
|
severity: "warn",
|
|
30368
|
-
messages:
|
|
30228
|
+
messages: messages130,
|
|
30369
30229
|
meta: {
|
|
30370
30230
|
description: "Require style rules to be inside @layer when the file defines layers.",
|
|
30371
30231
|
fixable: false,
|
|
@@ -30384,7 +30244,7 @@ var layerRequirementForComponentRules = defineCSSRule({
|
|
|
30384
30244
|
rule.startColumn,
|
|
30385
30245
|
layerRequirementForComponentRules,
|
|
30386
30246
|
"missingLayer",
|
|
30387
|
-
resolveMessage(
|
|
30247
|
+
resolveMessage(messages130.missingLayer, {
|
|
30388
30248
|
selector: rule.selectorText
|
|
30389
30249
|
})
|
|
30390
30250
|
);
|
|
@@ -30424,7 +30284,6 @@ var rules2 = [
|
|
|
30424
30284
|
selectorMaxAttributeAndUniversal,
|
|
30425
30285
|
selectorMaxSpecificity,
|
|
30426
30286
|
cssPolicyTypography,
|
|
30427
|
-
cssPolicyTouchTarget,
|
|
30428
30287
|
cssPolicySpacing,
|
|
30429
30288
|
cssPolicyContrast
|
|
30430
30289
|
];
|
|
@@ -30761,21 +30620,34 @@ function detectTailwindEntry(files) {
|
|
|
30761
30620
|
}
|
|
30762
30621
|
return themeOnlyFallback;
|
|
30763
30622
|
}
|
|
30764
|
-
async function resolveTailwindValidator(files) {
|
|
30623
|
+
async function resolveTailwindValidator(files, logger) {
|
|
30765
30624
|
const entry = detectTailwindEntry(files);
|
|
30766
|
-
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}`);
|
|
30767
30630
|
try {
|
|
30768
30631
|
const base = (0, import_node_path2.dirname)(entry.path);
|
|
30769
30632
|
const resolved = resolveTailwindNodePath(base);
|
|
30770
|
-
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}`);
|
|
30771
30638
|
const mod = await import(resolved);
|
|
30772
|
-
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
|
+
}
|
|
30773
30643
|
const design = await mod.__unstable__loadDesignSystem(
|
|
30774
30644
|
entry.content,
|
|
30775
30645
|
{ base }
|
|
30776
30646
|
);
|
|
30647
|
+
logger?.info("tailwind: design system loaded successfully");
|
|
30777
30648
|
return createLiveValidator(design);
|
|
30778
|
-
} catch {
|
|
30649
|
+
} catch (err) {
|
|
30650
|
+
logger?.warning(`tailwind: failed to load design system: ${err instanceof Error ? err.message : String(err)}`);
|
|
30779
30651
|
return null;
|
|
30780
30652
|
}
|
|
30781
30653
|
}
|
|
@@ -39580,13 +39452,13 @@ function emitLayoutDiagnostic(layout, node, emit, ruleId, messageId, template, s
|
|
|
39580
39452
|
}
|
|
39581
39453
|
|
|
39582
39454
|
// src/cross-file/rules/undefined-css-class.ts
|
|
39583
|
-
var
|
|
39455
|
+
var messages131 = {
|
|
39584
39456
|
undefinedClass: "CSS class '{{className}}' is not defined in project CSS files"
|
|
39585
39457
|
};
|
|
39586
39458
|
var jsxNoUndefinedCssClass = defineCrossRule({
|
|
39587
39459
|
id: "jsx-no-undefined-css-class",
|
|
39588
39460
|
severity: "error",
|
|
39589
|
-
messages:
|
|
39461
|
+
messages: messages131,
|
|
39590
39462
|
meta: {
|
|
39591
39463
|
description: "Detect undefined CSS class names in JSX",
|
|
39592
39464
|
fixable: false,
|
|
@@ -39605,7 +39477,7 @@ var jsxNoUndefinedCssClass = defineCrossRule({
|
|
|
39605
39477
|
ref.solid.sourceFile,
|
|
39606
39478
|
jsxNoUndefinedCssClass.id,
|
|
39607
39479
|
"undefinedClass",
|
|
39608
|
-
resolveMessage(
|
|
39480
|
+
resolveMessage(messages131.undefinedClass, { className: item.className }),
|
|
39609
39481
|
"error"
|
|
39610
39482
|
));
|
|
39611
39483
|
}
|
|
@@ -39613,13 +39485,13 @@ var jsxNoUndefinedCssClass = defineCrossRule({
|
|
|
39613
39485
|
});
|
|
39614
39486
|
|
|
39615
39487
|
// src/cross-file/rules/unreferenced-css-class.ts
|
|
39616
|
-
var
|
|
39488
|
+
var messages132 = {
|
|
39617
39489
|
unreferencedClass: "CSS class '{{className}}' is defined but not referenced by static JSX class attributes"
|
|
39618
39490
|
};
|
|
39619
39491
|
var cssNoUnreferencedComponentClass = defineCrossRule({
|
|
39620
39492
|
id: "css-no-unreferenced-component-class",
|
|
39621
39493
|
severity: "warn",
|
|
39622
|
-
messages:
|
|
39494
|
+
messages: messages132,
|
|
39623
39495
|
meta: {
|
|
39624
39496
|
description: "Detect CSS classes that are never referenced by static JSX class attributes.",
|
|
39625
39497
|
fixable: false,
|
|
@@ -39643,7 +39515,7 @@ var cssNoUnreferencedComponentClass = defineCrossRule({
|
|
|
39643
39515
|
},
|
|
39644
39516
|
cssNoUnreferencedComponentClass.id,
|
|
39645
39517
|
"unreferencedClass",
|
|
39646
|
-
resolveMessage(
|
|
39518
|
+
resolveMessage(messages132.unreferencedClass, { className }),
|
|
39647
39519
|
"warn"
|
|
39648
39520
|
)
|
|
39649
39521
|
);
|
|
@@ -39652,13 +39524,13 @@ var cssNoUnreferencedComponentClass = defineCrossRule({
|
|
|
39652
39524
|
});
|
|
39653
39525
|
|
|
39654
39526
|
// src/cross-file/rules/jsx-no-duplicate-class-token-class-classlist.ts
|
|
39655
|
-
var
|
|
39527
|
+
var messages133 = {
|
|
39656
39528
|
duplicateClassToken: "Class token `{{name}}` appears in both class and classList."
|
|
39657
39529
|
};
|
|
39658
39530
|
var jsxNoDuplicateClassTokenClassClasslist = defineCrossRule({
|
|
39659
39531
|
id: "jsx-no-duplicate-class-token-class-classlist",
|
|
39660
39532
|
severity: "warn",
|
|
39661
|
-
messages:
|
|
39533
|
+
messages: messages133,
|
|
39662
39534
|
meta: {
|
|
39663
39535
|
description: "Disallow duplicate class tokens between class and classList on the same JSX element.",
|
|
39664
39536
|
fixable: false,
|
|
@@ -39700,7 +39572,7 @@ var jsxNoDuplicateClassTokenClassClasslist = defineCrossRule({
|
|
|
39700
39572
|
solid.sourceFile,
|
|
39701
39573
|
jsxNoDuplicateClassTokenClassClasslist.id,
|
|
39702
39574
|
"duplicateClassToken",
|
|
39703
|
-
resolveMessage(
|
|
39575
|
+
resolveMessage(messages133.duplicateClassToken, { name: token }),
|
|
39704
39576
|
"warn"
|
|
39705
39577
|
));
|
|
39706
39578
|
}
|
|
@@ -39711,13 +39583,13 @@ var jsxNoDuplicateClassTokenClassClasslist = defineCrossRule({
|
|
|
39711
39583
|
|
|
39712
39584
|
// src/cross-file/rules/jsx-classlist-static-keys.ts
|
|
39713
39585
|
var import_typescript128 = __toESM(require("typescript"), 1);
|
|
39714
|
-
var
|
|
39586
|
+
var messages134 = {
|
|
39715
39587
|
nonStaticKey: "classList key must be statically known for reliable class mapping."
|
|
39716
39588
|
};
|
|
39717
39589
|
var jsxClasslistStaticKeys = defineCrossRule({
|
|
39718
39590
|
id: "jsx-classlist-static-keys",
|
|
39719
39591
|
severity: "error",
|
|
39720
|
-
messages:
|
|
39592
|
+
messages: messages134,
|
|
39721
39593
|
meta: {
|
|
39722
39594
|
description: "Require classList keys to be static and non-computed.",
|
|
39723
39595
|
fixable: false,
|
|
@@ -39728,26 +39600,26 @@ var jsxClasslistStaticKeys = defineCrossRule({
|
|
|
39728
39600
|
forEachClassListPropertyAcross(solids, (solid, p) => {
|
|
39729
39601
|
if (import_typescript128.default.isSpreadAssignment(p)) return;
|
|
39730
39602
|
if (!import_typescript128.default.isPropertyAssignment(p)) {
|
|
39731
|
-
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"));
|
|
39732
39604
|
return;
|
|
39733
39605
|
}
|
|
39734
39606
|
if (import_typescript128.default.isComputedPropertyName(p.name)) return;
|
|
39735
39607
|
if (import_typescript128.default.isIdentifier(p.name)) return;
|
|
39736
39608
|
if (import_typescript128.default.isStringLiteral(p.name)) return;
|
|
39737
|
-
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"));
|
|
39738
39610
|
});
|
|
39739
39611
|
}
|
|
39740
39612
|
});
|
|
39741
39613
|
|
|
39742
39614
|
// src/cross-file/rules/jsx-classlist-no-constant-literals.ts
|
|
39743
39615
|
var import_typescript129 = __toESM(require("typescript"), 1);
|
|
39744
|
-
var
|
|
39616
|
+
var messages135 = {
|
|
39745
39617
|
constantEntry: "classList entry `{{name}}: {{value}}` is constant; move it to static class."
|
|
39746
39618
|
};
|
|
39747
39619
|
var jsxClasslistNoConstantLiterals = defineCrossRule({
|
|
39748
39620
|
id: "jsx-classlist-no-constant-literals",
|
|
39749
39621
|
severity: "warn",
|
|
39750
|
-
messages:
|
|
39622
|
+
messages: messages135,
|
|
39751
39623
|
meta: {
|
|
39752
39624
|
description: "Disallow classList entries with constant true/false values.",
|
|
39753
39625
|
fixable: false,
|
|
@@ -39767,7 +39639,7 @@ var jsxClasslistNoConstantLiterals = defineCrossRule({
|
|
|
39767
39639
|
solid.sourceFile,
|
|
39768
39640
|
jsxClasslistNoConstantLiterals.id,
|
|
39769
39641
|
"constantEntry",
|
|
39770
|
-
resolveMessage(
|
|
39642
|
+
resolveMessage(messages135.constantEntry, { name: n, value: val.kind === import_typescript129.default.SyntaxKind.TrueKeyword ? "true" : "false" }),
|
|
39771
39643
|
"warn"
|
|
39772
39644
|
));
|
|
39773
39645
|
});
|
|
@@ -39802,13 +39674,13 @@ function isDefinitelyNonBooleanType(solid, node) {
|
|
|
39802
39674
|
}
|
|
39803
39675
|
|
|
39804
39676
|
// src/cross-file/rules/jsx-classlist-boolean-values.ts
|
|
39805
|
-
var
|
|
39677
|
+
var messages136 = {
|
|
39806
39678
|
nonBooleanValue: "classList value for `{{name}}` must be boolean."
|
|
39807
39679
|
};
|
|
39808
39680
|
var jsxClasslistBooleanValues = defineCrossRule({
|
|
39809
39681
|
id: "jsx-classlist-boolean-values",
|
|
39810
39682
|
severity: "error",
|
|
39811
|
-
messages:
|
|
39683
|
+
messages: messages136,
|
|
39812
39684
|
meta: {
|
|
39813
39685
|
description: "Require classList values to be boolean-like expressions.",
|
|
39814
39686
|
fixable: false,
|
|
@@ -39822,25 +39694,25 @@ var jsxClasslistBooleanValues = defineCrossRule({
|
|
|
39822
39694
|
if (!n) return;
|
|
39823
39695
|
if (isBooleanish(p.initializer)) return;
|
|
39824
39696
|
if (isDefinitelyNonBoolean(p.initializer)) {
|
|
39825
|
-
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"));
|
|
39826
39698
|
return;
|
|
39827
39699
|
}
|
|
39828
39700
|
if (isBooleanType(solid, p.initializer)) return;
|
|
39829
39701
|
if (!isDefinitelyNonBooleanType(solid, p.initializer)) return;
|
|
39830
|
-
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"));
|
|
39831
39703
|
});
|
|
39832
39704
|
}
|
|
39833
39705
|
});
|
|
39834
39706
|
|
|
39835
39707
|
// src/cross-file/rules/jsx-classlist-no-accessor-reference.ts
|
|
39836
39708
|
var import_typescript131 = __toESM(require("typescript"), 1);
|
|
39837
|
-
var
|
|
39709
|
+
var messages137 = {
|
|
39838
39710
|
accessorReference: "Signal accessor `{{name}}` must be called in classList value (use {{name}}())."
|
|
39839
39711
|
};
|
|
39840
39712
|
var jsxClasslistNoAccessorReference = defineCrossRule({
|
|
39841
39713
|
id: "jsx-classlist-no-accessor-reference",
|
|
39842
39714
|
severity: "error",
|
|
39843
|
-
messages:
|
|
39715
|
+
messages: messages137,
|
|
39844
39716
|
meta: {
|
|
39845
39717
|
description: "Disallow passing accessor references directly as classList values.",
|
|
39846
39718
|
fixable: false,
|
|
@@ -39872,7 +39744,7 @@ var jsxClasslistNoAccessorReference = defineCrossRule({
|
|
|
39872
39744
|
solid.sourceFile,
|
|
39873
39745
|
jsxClasslistNoAccessorReference.id,
|
|
39874
39746
|
"accessorReference",
|
|
39875
|
-
resolveMessage(
|
|
39747
|
+
resolveMessage(messages137.accessorReference, { name: v.text }),
|
|
39876
39748
|
"error"
|
|
39877
39749
|
));
|
|
39878
39750
|
});
|
|
@@ -39881,13 +39753,13 @@ var jsxClasslistNoAccessorReference = defineCrossRule({
|
|
|
39881
39753
|
|
|
39882
39754
|
// src/cross-file/rules/jsx-style-kebab-case-keys.ts
|
|
39883
39755
|
var import_typescript132 = __toESM(require("typescript"), 1);
|
|
39884
|
-
var
|
|
39756
|
+
var messages138 = {
|
|
39885
39757
|
kebabStyleKey: "Style key `{{name}}` should be `{{kebab}}` in Solid style objects."
|
|
39886
39758
|
};
|
|
39887
39759
|
var jsxStyleKebabCaseKeys = defineCrossRule({
|
|
39888
39760
|
id: "jsx-style-kebab-case-keys",
|
|
39889
39761
|
severity: "error",
|
|
39890
|
-
messages:
|
|
39762
|
+
messages: messages138,
|
|
39891
39763
|
meta: {
|
|
39892
39764
|
description: "Require kebab-case keys in JSX style object literals.",
|
|
39893
39765
|
fixable: false,
|
|
@@ -39908,7 +39780,7 @@ var jsxStyleKebabCaseKeys = defineCrossRule({
|
|
|
39908
39780
|
solid.sourceFile,
|
|
39909
39781
|
jsxStyleKebabCaseKeys.id,
|
|
39910
39782
|
"kebabStyleKey",
|
|
39911
|
-
resolveMessage(
|
|
39783
|
+
resolveMessage(messages138.kebabStyleKey, { name: n, kebab }),
|
|
39912
39784
|
"error"
|
|
39913
39785
|
));
|
|
39914
39786
|
});
|
|
@@ -39917,13 +39789,13 @@ var jsxStyleKebabCaseKeys = defineCrossRule({
|
|
|
39917
39789
|
|
|
39918
39790
|
// src/cross-file/rules/jsx-style-no-function-values.ts
|
|
39919
39791
|
var import_typescript133 = __toESM(require("typescript"), 1);
|
|
39920
|
-
var
|
|
39792
|
+
var messages139 = {
|
|
39921
39793
|
functionStyleValue: "Style value for `{{name}}` is a function; pass computed value instead."
|
|
39922
39794
|
};
|
|
39923
39795
|
var jsxStyleNoFunctionValues = defineCrossRule({
|
|
39924
39796
|
id: "jsx-style-no-function-values",
|
|
39925
39797
|
severity: "error",
|
|
39926
|
-
messages:
|
|
39798
|
+
messages: messages139,
|
|
39927
39799
|
meta: {
|
|
39928
39800
|
description: "Disallow function values in JSX style objects.",
|
|
39929
39801
|
fixable: false,
|
|
@@ -39937,11 +39809,11 @@ var jsxStyleNoFunctionValues = defineCrossRule({
|
|
|
39937
39809
|
if (!n) return;
|
|
39938
39810
|
const v = p.initializer;
|
|
39939
39811
|
if (import_typescript133.default.isArrowFunction(v) || import_typescript133.default.isFunctionExpression(v)) {
|
|
39940
|
-
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"));
|
|
39941
39813
|
return;
|
|
39942
39814
|
}
|
|
39943
39815
|
if (import_typescript133.default.isIdentifier(v) && solid.typeResolver.isCallableType(v)) {
|
|
39944
|
-
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"));
|
|
39945
39817
|
}
|
|
39946
39818
|
});
|
|
39947
39819
|
}
|
|
@@ -39949,13 +39821,13 @@ var jsxStyleNoFunctionValues = defineCrossRule({
|
|
|
39949
39821
|
|
|
39950
39822
|
// src/cross-file/rules/jsx-style-no-unused-custom-prop.ts
|
|
39951
39823
|
var import_typescript134 = __toESM(require("typescript"), 1);
|
|
39952
|
-
var
|
|
39824
|
+
var messages140 = {
|
|
39953
39825
|
unusedInlineVar: "Inline custom property `{{name}}` is never read via var({{name}})."
|
|
39954
39826
|
};
|
|
39955
39827
|
var jsxStyleNoUnusedCustomProp = defineCrossRule({
|
|
39956
39828
|
id: "jsx-style-no-unused-custom-prop",
|
|
39957
39829
|
severity: "warn",
|
|
39958
|
-
messages:
|
|
39830
|
+
messages: messages140,
|
|
39959
39831
|
meta: {
|
|
39960
39832
|
description: "Detect inline style custom properties that are never consumed by CSS var() references.",
|
|
39961
39833
|
fixable: false,
|
|
@@ -39996,7 +39868,7 @@ var jsxStyleNoUnusedCustomProp = defineCrossRule({
|
|
|
39996
39868
|
solid.sourceFile,
|
|
39997
39869
|
jsxStyleNoUnusedCustomProp.id,
|
|
39998
39870
|
"unusedInlineVar",
|
|
39999
|
-
resolveMessage(
|
|
39871
|
+
resolveMessage(messages140.unusedInlineVar, { name: n }),
|
|
40000
39872
|
"warn"
|
|
40001
39873
|
));
|
|
40002
39874
|
}
|
|
@@ -40006,7 +39878,7 @@ var jsxStyleNoUnusedCustomProp = defineCrossRule({
|
|
|
40006
39878
|
|
|
40007
39879
|
// src/cross-file/rules/jsx-style-policy.ts
|
|
40008
39880
|
var import_typescript135 = __toESM(require("typescript"), 1);
|
|
40009
|
-
var
|
|
39881
|
+
var messages141 = {
|
|
40010
39882
|
fontTooSmall: "Inline style `{{prop}}: {{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for policy `{{policy}}`.",
|
|
40011
39883
|
lineHeightTooSmall: "Inline style `line-height: {{value}}` is below the minimum `{{min}}` for policy `{{policy}}`.",
|
|
40012
39884
|
heightTooSmall: "Inline style `{{prop}}: {{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for interactive elements in policy `{{policy}}`.",
|
|
@@ -40053,7 +39925,7 @@ function readNodeHostElementRef(layout, solid, element) {
|
|
|
40053
39925
|
var jsxStylePolicy = defineCrossRule({
|
|
40054
39926
|
id: "jsx-style-policy",
|
|
40055
39927
|
severity: "warn",
|
|
40056
|
-
messages:
|
|
39928
|
+
messages: messages141,
|
|
40057
39929
|
meta: {
|
|
40058
39930
|
description: "Enforce accessibility policy thresholds on inline JSX style objects.",
|
|
40059
39931
|
fixable: false,
|
|
@@ -40080,7 +39952,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40080
39952
|
solid.sourceFile,
|
|
40081
39953
|
jsxStylePolicy.id,
|
|
40082
39954
|
"fontTooSmall",
|
|
40083
|
-
resolveMessage(
|
|
39955
|
+
resolveMessage(messages141.fontTooSmall, {
|
|
40084
39956
|
prop: key,
|
|
40085
39957
|
value: strVal,
|
|
40086
39958
|
resolved: formatRounded(px),
|
|
@@ -40102,7 +39974,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40102
39974
|
solid.sourceFile,
|
|
40103
39975
|
jsxStylePolicy.id,
|
|
40104
39976
|
"lineHeightTooSmall",
|
|
40105
|
-
resolveMessage(
|
|
39977
|
+
resolveMessage(messages141.lineHeightTooSmall, {
|
|
40106
39978
|
value: String(lh),
|
|
40107
39979
|
min: String(policy.minLineHeight),
|
|
40108
39980
|
policy: name
|
|
@@ -40126,7 +39998,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40126
39998
|
solid.sourceFile,
|
|
40127
39999
|
jsxStylePolicy.id,
|
|
40128
40000
|
"heightTooSmall",
|
|
40129
|
-
resolveMessage(
|
|
40001
|
+
resolveMessage(messages141.heightTooSmall, {
|
|
40130
40002
|
prop: key,
|
|
40131
40003
|
value: strVal,
|
|
40132
40004
|
resolved: formatRounded(px),
|
|
@@ -40148,7 +40020,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40148
40020
|
solid.sourceFile,
|
|
40149
40021
|
jsxStylePolicy.id,
|
|
40150
40022
|
"letterSpacingTooSmall",
|
|
40151
|
-
resolveMessage(
|
|
40023
|
+
resolveMessage(messages141.letterSpacingTooSmall, {
|
|
40152
40024
|
value: strVal,
|
|
40153
40025
|
resolved: String(em),
|
|
40154
40026
|
min: String(policy.minLetterSpacing),
|
|
@@ -40169,7 +40041,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40169
40041
|
solid.sourceFile,
|
|
40170
40042
|
jsxStylePolicy.id,
|
|
40171
40043
|
"wordSpacingTooSmall",
|
|
40172
|
-
resolveMessage(
|
|
40044
|
+
resolveMessage(messages141.wordSpacingTooSmall, {
|
|
40173
40045
|
value: strVal,
|
|
40174
40046
|
resolved: String(em),
|
|
40175
40047
|
min: String(policy.minWordSpacing),
|
|
@@ -40183,7 +40055,7 @@ var jsxStylePolicy = defineCrossRule({
|
|
|
40183
40055
|
});
|
|
40184
40056
|
|
|
40185
40057
|
// src/cross-file/rules/css-layout-sibling-alignment-outlier.ts
|
|
40186
|
-
var
|
|
40058
|
+
var messages142 = {
|
|
40187
40059
|
misalignedSibling: "Vertically misaligned '{{subject}}' in '{{parent}}'.{{fix}}{{offsetClause}}"
|
|
40188
40060
|
};
|
|
40189
40061
|
var MIN_CONFIDENCE_THRESHOLD = 0.48;
|
|
@@ -40231,7 +40103,7 @@ var siblingAlignmentDetector = {
|
|
|
40231
40103
|
var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
40232
40104
|
id: "css-layout-sibling-alignment-outlier",
|
|
40233
40105
|
severity: "warn",
|
|
40234
|
-
messages:
|
|
40106
|
+
messages: messages142,
|
|
40235
40107
|
meta: {
|
|
40236
40108
|
description: "Detect vertical alignment outliers between sibling elements in shared layout containers.",
|
|
40237
40109
|
fixable: false,
|
|
@@ -40315,7 +40187,7 @@ var cssLayoutSiblingAlignmentOutlier = defineCrossRule({
|
|
|
40315
40187
|
subjectRef.solid.sourceFile,
|
|
40316
40188
|
cssLayoutSiblingAlignmentOutlier.id,
|
|
40317
40189
|
"misalignedSibling",
|
|
40318
|
-
resolveMessage(
|
|
40190
|
+
resolveMessage(messages142.misalignedSibling, {
|
|
40319
40191
|
subject,
|
|
40320
40192
|
parent,
|
|
40321
40193
|
fix,
|
|
@@ -40399,13 +40271,13 @@ function hasNonOffsetPrimaryEvidence(topFactors) {
|
|
|
40399
40271
|
}
|
|
40400
40272
|
|
|
40401
40273
|
// src/cross-file/rules/css-layout-transition-layout-property.ts
|
|
40402
|
-
var
|
|
40274
|
+
var messages143 = {
|
|
40403
40275
|
transitionLayoutProperty: "Transition '{{property}}' in '{{declaration}}' animates layout-affecting geometry. Prefer transform/opacity to avoid CLS."
|
|
40404
40276
|
};
|
|
40405
40277
|
var cssLayoutTransitionLayoutProperty = defineCrossRule({
|
|
40406
40278
|
id: "css-layout-transition-layout-property",
|
|
40407
40279
|
severity: "warn",
|
|
40408
|
-
messages:
|
|
40280
|
+
messages: messages143,
|
|
40409
40281
|
meta: {
|
|
40410
40282
|
description: "Disallow transitions that animate layout-affecting geometry properties.",
|
|
40411
40283
|
fixable: false,
|
|
@@ -40429,7 +40301,7 @@ var cssLayoutTransitionLayoutProperty = defineCrossRule({
|
|
|
40429
40301
|
},
|
|
40430
40302
|
cssLayoutTransitionLayoutProperty.id,
|
|
40431
40303
|
"transitionLayoutProperty",
|
|
40432
|
-
resolveMessage(
|
|
40304
|
+
resolveMessage(messages143.transitionLayoutProperty, {
|
|
40433
40305
|
property: target,
|
|
40434
40306
|
declaration: declaration.property
|
|
40435
40307
|
}),
|
|
@@ -40444,13 +40316,13 @@ function findLayoutTransitionTarget(raw) {
|
|
|
40444
40316
|
}
|
|
40445
40317
|
|
|
40446
40318
|
// src/cross-file/rules/css-layout-animation-layout-property.ts
|
|
40447
|
-
var
|
|
40319
|
+
var messages144 = {
|
|
40448
40320
|
animationLayoutProperty: "Animation '{{animation}}' mutates layout-affecting '{{property}}', which can trigger CLS. Prefer transform/opacity or reserve geometry."
|
|
40449
40321
|
};
|
|
40450
40322
|
var cssLayoutAnimationLayoutProperty = defineCrossRule({
|
|
40451
40323
|
id: "css-layout-animation-layout-property",
|
|
40452
40324
|
severity: "warn",
|
|
40453
|
-
messages:
|
|
40325
|
+
messages: messages144,
|
|
40454
40326
|
meta: {
|
|
40455
40327
|
description: "Disallow keyframe animations that mutate layout-affecting properties and can trigger CLS.",
|
|
40456
40328
|
fixable: false,
|
|
@@ -40478,7 +40350,7 @@ var cssLayoutAnimationLayoutProperty = defineCrossRule({
|
|
|
40478
40350
|
},
|
|
40479
40351
|
cssLayoutAnimationLayoutProperty.id,
|
|
40480
40352
|
"animationLayoutProperty",
|
|
40481
|
-
resolveMessage(
|
|
40353
|
+
resolveMessage(messages144.animationLayoutProperty, {
|
|
40482
40354
|
animation: match.name,
|
|
40483
40355
|
property: match.property
|
|
40484
40356
|
}),
|
|
@@ -40548,13 +40420,13 @@ function firstRiskyAnimationName(names, riskyKeyframes) {
|
|
|
40548
40420
|
}
|
|
40549
40421
|
|
|
40550
40422
|
// src/cross-file/rules/css-layout-stateful-box-model-shift.ts
|
|
40551
|
-
var
|
|
40423
|
+
var messages145 = {
|
|
40552
40424
|
statefulBoxModelShift: "State selector '{{selector}}' changes layout-affecting '{{property}}'. Keep geometry stable across states to avoid CLS."
|
|
40553
40425
|
};
|
|
40554
40426
|
var cssLayoutStatefulBoxModelShift = defineCrossRule({
|
|
40555
40427
|
id: "css-layout-stateful-box-model-shift",
|
|
40556
40428
|
severity: "warn",
|
|
40557
|
-
messages:
|
|
40429
|
+
messages: messages145,
|
|
40558
40430
|
meta: {
|
|
40559
40431
|
description: "Disallow stateful selector changes that alter element geometry and trigger layout shifts.",
|
|
40560
40432
|
fixable: false,
|
|
@@ -40595,7 +40467,7 @@ var cssLayoutStatefulBoxModelShift = defineCrossRule({
|
|
|
40595
40467
|
},
|
|
40596
40468
|
cssLayoutStatefulBoxModelShift.id,
|
|
40597
40469
|
"statefulBoxModelShift",
|
|
40598
|
-
resolveMessage(
|
|
40470
|
+
resolveMessage(messages145.statefulBoxModelShift, {
|
|
40599
40471
|
selector: match.raw,
|
|
40600
40472
|
property: declaration.property
|
|
40601
40473
|
}),
|
|
@@ -40689,14 +40561,14 @@ function lookupBaseByProperty(baseValueIndex, selectorKeys) {
|
|
|
40689
40561
|
|
|
40690
40562
|
// src/cross-file/rules/css-layout-unsized-replaced-element.ts
|
|
40691
40563
|
var import_typescript136 = __toESM(require("typescript"), 1);
|
|
40692
|
-
var
|
|
40564
|
+
var messages146 = {
|
|
40693
40565
|
unsizedReplacedElement: "Replaced element '{{tag}}' has no stable reserved size (width/height or aspect-ratio with a dimension), which can cause CLS."
|
|
40694
40566
|
};
|
|
40695
40567
|
var REPLACED_MEDIA_TAGS = /* @__PURE__ */ new Set(["img", "video", "iframe", "canvas", "svg"]);
|
|
40696
40568
|
var cssLayoutUnsizedReplacedElement = defineCrossRule({
|
|
40697
40569
|
id: "css-layout-unsized-replaced-element",
|
|
40698
40570
|
severity: "warn",
|
|
40699
|
-
messages:
|
|
40571
|
+
messages: messages146,
|
|
40700
40572
|
meta: {
|
|
40701
40573
|
description: "Require stable reserved geometry for replaced media elements to prevent layout shifts.",
|
|
40702
40574
|
fixable: false,
|
|
@@ -40721,7 +40593,7 @@ var cssLayoutUnsizedReplacedElement = defineCrossRule({
|
|
|
40721
40593
|
ref.solid.sourceFile,
|
|
40722
40594
|
cssLayoutUnsizedReplacedElement.id,
|
|
40723
40595
|
"unsizedReplacedElement",
|
|
40724
|
-
resolveMessage(
|
|
40596
|
+
resolveMessage(messages146.unsizedReplacedElement, { tag }),
|
|
40725
40597
|
"warn"
|
|
40726
40598
|
)
|
|
40727
40599
|
);
|
|
@@ -40774,7 +40646,7 @@ function readPositiveJsxAttribute(solid, element, name) {
|
|
|
40774
40646
|
}
|
|
40775
40647
|
|
|
40776
40648
|
// src/cross-file/rules/css-layout-dynamic-slot-no-reserved-space.ts
|
|
40777
|
-
var
|
|
40649
|
+
var messages147 = {
|
|
40778
40650
|
dynamicSlotNoReservedSpace: "Dynamic content container '{{tag}}' does not reserve block space (min-height/height/aspect-ratio/contain-intrinsic-size), which can cause CLS."
|
|
40779
40651
|
};
|
|
40780
40652
|
var INLINE_DISPLAYS = /* @__PURE__ */ new Set(["inline", "contents"]);
|
|
@@ -40796,7 +40668,7 @@ function hasOutOfFlowAncestor(layout, node) {
|
|
|
40796
40668
|
var cssLayoutDynamicSlotNoReservedSpace = defineCrossRule({
|
|
40797
40669
|
id: "css-layout-dynamic-slot-no-reserved-space",
|
|
40798
40670
|
severity: "warn",
|
|
40799
|
-
messages:
|
|
40671
|
+
messages: messages147,
|
|
40800
40672
|
meta: {
|
|
40801
40673
|
description: "Require reserved block space for dynamic content containers to avoid layout shifts.",
|
|
40802
40674
|
fixable: false,
|
|
@@ -40819,19 +40691,19 @@ var cssLayoutDynamicSlotNoReservedSpace = defineCrossRule({
|
|
|
40819
40691
|
const reservedSpace = readReservedSpaceFact(context.layout, node);
|
|
40820
40692
|
if (reservedSpace.hasReservedSpace) continue;
|
|
40821
40693
|
if (hasBlockAxisPadding(snapshot)) continue;
|
|
40822
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutDynamicSlotNoReservedSpace.id, "dynamicSlotNoReservedSpace",
|
|
40694
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutDynamicSlotNoReservedSpace.id, "dynamicSlotNoReservedSpace", messages147.dynamicSlotNoReservedSpace, cssLayoutDynamicSlotNoReservedSpace.severity)) continue;
|
|
40823
40695
|
}
|
|
40824
40696
|
}
|
|
40825
40697
|
});
|
|
40826
40698
|
|
|
40827
40699
|
// src/cross-file/rules/css-layout-scrollbar-gutter-instability.ts
|
|
40828
|
-
var
|
|
40700
|
+
var messages148 = {
|
|
40829
40701
|
missingScrollbarGutter: "Scrollable container '{{tag}}' uses overflow auto/scroll without `scrollbar-gutter: stable`, which can trigger CLS when scrollbars appear."
|
|
40830
40702
|
};
|
|
40831
40703
|
var cssLayoutScrollbarGutterInstability = defineCrossRule({
|
|
40832
40704
|
id: "css-layout-scrollbar-gutter-instability",
|
|
40833
40705
|
severity: "warn",
|
|
40834
|
-
messages:
|
|
40706
|
+
messages: messages148,
|
|
40835
40707
|
meta: {
|
|
40836
40708
|
description: "Require stable scrollbar gutters for scrollable containers to reduce layout shifts.",
|
|
40837
40709
|
fixable: false,
|
|
@@ -40850,19 +40722,19 @@ var cssLayoutScrollbarGutterInstability = defineCrossRule({
|
|
|
40850
40722
|
if (scrollbarWidth === "none") continue;
|
|
40851
40723
|
const gutter = readKnownNormalized(snapshot, "scrollbar-gutter");
|
|
40852
40724
|
if (gutter !== null && gutter.startsWith("stable")) continue;
|
|
40853
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutScrollbarGutterInstability.id, "missingScrollbarGutter",
|
|
40725
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutScrollbarGutterInstability.id, "missingScrollbarGutter", messages148.missingScrollbarGutter, cssLayoutScrollbarGutterInstability.severity)) continue;
|
|
40854
40726
|
}
|
|
40855
40727
|
}
|
|
40856
40728
|
});
|
|
40857
40729
|
|
|
40858
40730
|
// src/cross-file/rules/css-layout-overflow-anchor-instability.ts
|
|
40859
|
-
var
|
|
40731
|
+
var messages149 = {
|
|
40860
40732
|
unstableOverflowAnchor: "Element '{{tag}}' sets `overflow-anchor: none` on a {{context}} container; disabling scroll anchoring can amplify visible layout shifts."
|
|
40861
40733
|
};
|
|
40862
40734
|
var cssLayoutOverflowAnchorInstability = defineCrossRule({
|
|
40863
40735
|
id: "css-layout-overflow-anchor-instability",
|
|
40864
40736
|
severity: "warn",
|
|
40865
|
-
messages:
|
|
40737
|
+
messages: messages149,
|
|
40866
40738
|
meta: {
|
|
40867
40739
|
description: "Disallow overflow-anchor none on dynamic or scrollable containers prone to visible layout shifts.",
|
|
40868
40740
|
fixable: false,
|
|
@@ -40880,20 +40752,20 @@ var cssLayoutOverflowAnchorInstability = defineCrossRule({
|
|
|
40880
40752
|
const isDynamicContainer = isDynamicContainerLike(node);
|
|
40881
40753
|
if (!isScrollable && !isDynamicContainer) continue;
|
|
40882
40754
|
const containerContext = isScrollable ? "scrollable" : "dynamic";
|
|
40883
|
-
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;
|
|
40884
40756
|
}
|
|
40885
40757
|
}
|
|
40886
40758
|
});
|
|
40887
40759
|
|
|
40888
40760
|
// src/cross-file/rules/css-layout-font-swap-instability.ts
|
|
40889
|
-
var
|
|
40761
|
+
var messages150 = {
|
|
40890
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."
|
|
40891
40763
|
};
|
|
40892
40764
|
var SWAP_DISPLAYS = /* @__PURE__ */ new Set(["swap", "fallback"]);
|
|
40893
40765
|
var cssLayoutFontSwapInstability = defineCrossRule({
|
|
40894
40766
|
id: "css-layout-font-swap-instability",
|
|
40895
40767
|
severity: "warn",
|
|
40896
|
-
messages:
|
|
40768
|
+
messages: messages150,
|
|
40897
40769
|
meta: {
|
|
40898
40770
|
description: "Require metric overrides for swapping webfonts to reduce layout shifts during font load.",
|
|
40899
40771
|
fixable: false,
|
|
@@ -40940,7 +40812,7 @@ var cssLayoutFontSwapInstability = defineCrossRule({
|
|
|
40940
40812
|
},
|
|
40941
40813
|
cssLayoutFontSwapInstability.id,
|
|
40942
40814
|
"unstableFontSwap",
|
|
40943
|
-
resolveMessage(
|
|
40815
|
+
resolveMessage(messages150.unstableFontSwap, {
|
|
40944
40816
|
family,
|
|
40945
40817
|
display: report.display
|
|
40946
40818
|
}),
|
|
@@ -40953,14 +40825,14 @@ var cssLayoutFontSwapInstability = defineCrossRule({
|
|
|
40953
40825
|
});
|
|
40954
40826
|
|
|
40955
40827
|
// src/cross-file/rules/css-layout-conditional-display-collapse.ts
|
|
40956
|
-
var
|
|
40828
|
+
var messages151 = {
|
|
40957
40829
|
conditionalDisplayCollapse: "Conditional display sets '{{display}}' on '{{tag}}' without stable reserved space, which can collapse/expand layout and cause CLS."
|
|
40958
40830
|
};
|
|
40959
40831
|
var COLLAPSING_DISPLAYS = /* @__PURE__ */ new Set(["none", "contents"]);
|
|
40960
40832
|
var cssLayoutConditionalDisplayCollapse = defineCrossRule({
|
|
40961
40833
|
id: "css-layout-conditional-display-collapse",
|
|
40962
40834
|
severity: "warn",
|
|
40963
|
-
messages:
|
|
40835
|
+
messages: messages151,
|
|
40964
40836
|
meta: {
|
|
40965
40837
|
description: "Disallow conditional display collapse in flow without reserved geometry.",
|
|
40966
40838
|
fixable: false,
|
|
@@ -40981,13 +40853,13 @@ var cssLayoutConditionalDisplayCollapse = defineCrossRule({
|
|
|
40981
40853
|
if (!isFlowRelevantBySiblingsOrText(node, snapshot.node.textualContent)) continue;
|
|
40982
40854
|
const reservedSpace = readReservedSpaceFact(context.layout, node);
|
|
40983
40855
|
if (reservedSpace.hasReservedSpace) continue;
|
|
40984
|
-
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;
|
|
40985
40857
|
}
|
|
40986
40858
|
}
|
|
40987
40859
|
});
|
|
40988
40860
|
|
|
40989
40861
|
// src/cross-file/rules/css-layout-conditional-white-space-wrap-shift.ts
|
|
40990
|
-
var
|
|
40862
|
+
var messages152 = {
|
|
40991
40863
|
conditionalWhiteSpaceShift: "Conditional white-space '{{whiteSpace}}' on '{{tag}}' can reflow text and shift siblings; keep wrapping behavior stable or reserve geometry."
|
|
40992
40864
|
};
|
|
40993
40865
|
var WRAP_SHIFT_VALUES = /* @__PURE__ */ new Set(["nowrap", "pre"]);
|
|
@@ -40996,7 +40868,7 @@ var BLOCK_SIZE_PROPERTIES = ["height", "min-height"];
|
|
|
40996
40868
|
var cssLayoutConditionalWhiteSpaceWrapShift = defineCrossRule({
|
|
40997
40869
|
id: "css-layout-conditional-white-space-wrap-shift",
|
|
40998
40870
|
severity: "warn",
|
|
40999
|
-
messages:
|
|
40871
|
+
messages: messages152,
|
|
41000
40872
|
meta: {
|
|
41001
40873
|
description: "Disallow conditional white-space wrapping mode toggles that can trigger CLS.",
|
|
41002
40874
|
fixable: false,
|
|
@@ -41019,7 +40891,7 @@ var cssLayoutConditionalWhiteSpaceWrapShift = defineCrossRule({
|
|
|
41019
40891
|
if (!flow.inFlow) continue;
|
|
41020
40892
|
if (!isFlowRelevantBySiblingsOrText(node, snapshot.node.textualContent)) continue;
|
|
41021
40893
|
if (hasStableTextShell(snapshot)) continue;
|
|
41022
|
-
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;
|
|
41023
40895
|
}
|
|
41024
40896
|
}
|
|
41025
40897
|
});
|
|
@@ -41041,13 +40913,13 @@ function hasWrapShiftDelta(delta) {
|
|
|
41041
40913
|
}
|
|
41042
40914
|
|
|
41043
40915
|
// src/cross-file/rules/css-layout-overflow-mode-toggle-instability.ts
|
|
41044
|
-
var
|
|
40916
|
+
var messages153 = {
|
|
41045
40917
|
overflowModeToggle: "Conditional overflow mode changes scrolling ('{{overflow}}') on '{{tag}}' without `scrollbar-gutter: stable`, which can trigger CLS."
|
|
41046
40918
|
};
|
|
41047
40919
|
var cssLayoutOverflowModeToggleInstability = defineCrossRule({
|
|
41048
40920
|
id: "css-layout-overflow-mode-toggle-instability",
|
|
41049
40921
|
severity: "warn",
|
|
41050
|
-
messages:
|
|
40922
|
+
messages: messages153,
|
|
41051
40923
|
meta: {
|
|
41052
40924
|
description: "Disallow conditional overflow mode switches that can introduce scrollbar-induced layout shifts.",
|
|
41053
40925
|
fixable: false,
|
|
@@ -41072,7 +40944,7 @@ var cssLayoutOverflowModeToggleInstability = defineCrossRule({
|
|
|
41072
40944
|
const gutter = readKnownNormalizedWithGuard(snapshot, "scrollbar-gutter");
|
|
41073
40945
|
if (gutter !== null && gutter.startsWith("stable")) continue;
|
|
41074
40946
|
const overflowValue = scrollFact.overflowY ?? scrollFact.overflow ?? "auto";
|
|
41075
|
-
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;
|
|
41076
40948
|
}
|
|
41077
40949
|
}
|
|
41078
40950
|
});
|
|
@@ -41092,7 +40964,7 @@ function hasAnyScrollValue(delta) {
|
|
|
41092
40964
|
}
|
|
41093
40965
|
|
|
41094
40966
|
// src/cross-file/rules/css-layout-box-sizing-toggle-with-chrome.ts
|
|
41095
|
-
var
|
|
40967
|
+
var messages154 = {
|
|
41096
40968
|
boxSizingToggleWithChrome: "Conditional `box-sizing` toggle on '{{tag}}' combines with non-zero padding/border, which can shift layout and trigger CLS."
|
|
41097
40969
|
};
|
|
41098
40970
|
var BOX_SIZING_VALUES = /* @__PURE__ */ new Set(["content-box", "border-box"]);
|
|
@@ -41109,7 +40981,7 @@ var CHROME_PROPERTIES = [
|
|
|
41109
40981
|
var cssLayoutBoxSizingToggleWithChrome = defineCrossRule({
|
|
41110
40982
|
id: "css-layout-box-sizing-toggle-with-chrome",
|
|
41111
40983
|
severity: "warn",
|
|
41112
|
-
messages:
|
|
40984
|
+
messages: messages154,
|
|
41113
40985
|
meta: {
|
|
41114
40986
|
description: "Disallow conditional box-sizing mode toggles when box chrome contributes to geometry shifts.",
|
|
41115
40987
|
fixable: false,
|
|
@@ -41127,7 +40999,7 @@ var cssLayoutBoxSizingToggleWithChrome = defineCrossRule({
|
|
|
41127
40999
|
if (!boxSizingDelta.hasConditional) continue;
|
|
41128
41000
|
if (!boxSizingDelta.hasDelta) continue;
|
|
41129
41001
|
if (!hasNonZeroChrome(snapshot)) continue;
|
|
41130
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutBoxSizingToggleWithChrome.id, "boxSizingToggleWithChrome",
|
|
41002
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutBoxSizingToggleWithChrome.id, "boxSizingToggleWithChrome", messages154.boxSizingToggleWithChrome, cssLayoutBoxSizingToggleWithChrome.severity)) continue;
|
|
41131
41003
|
}
|
|
41132
41004
|
}
|
|
41133
41005
|
});
|
|
@@ -41136,13 +41008,13 @@ function hasNonZeroChrome(snapshot) {
|
|
|
41136
41008
|
}
|
|
41137
41009
|
|
|
41138
41010
|
// src/cross-file/rules/css-layout-content-visibility-no-intrinsic-size.ts
|
|
41139
|
-
var
|
|
41011
|
+
var messages155 = {
|
|
41140
41012
|
missingIntrinsicSize: "`content-visibility: auto` on '{{tag}}' lacks intrinsic size reservation (`contain-intrinsic-size`/min-height/height/aspect-ratio), which can cause CLS."
|
|
41141
41013
|
};
|
|
41142
41014
|
var cssLayoutContentVisibilityNoIntrinsicSize = defineCrossRule({
|
|
41143
41015
|
id: "css-layout-content-visibility-no-intrinsic-size",
|
|
41144
41016
|
severity: "warn",
|
|
41145
|
-
messages:
|
|
41017
|
+
messages: messages155,
|
|
41146
41018
|
meta: {
|
|
41147
41019
|
description: "Require intrinsic size reservation when using content-visibility auto to avoid late layout shifts.",
|
|
41148
41020
|
fixable: false,
|
|
@@ -41157,19 +41029,19 @@ var cssLayoutContentVisibilityNoIntrinsicSize = defineCrossRule({
|
|
|
41157
41029
|
if (!isDeferredContainerLike(node, snapshot.node.textualContent)) continue;
|
|
41158
41030
|
const reservedSpace = readReservedSpaceFact(context.layout, node);
|
|
41159
41031
|
if (reservedSpace.hasReservedSpace) continue;
|
|
41160
|
-
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutContentVisibilityNoIntrinsicSize.id, "missingIntrinsicSize",
|
|
41032
|
+
if (!emitLayoutDiagnostic(context.layout, node, emit, cssLayoutContentVisibilityNoIntrinsicSize.id, "missingIntrinsicSize", messages155.missingIntrinsicSize, cssLayoutContentVisibilityNoIntrinsicSize.severity)) continue;
|
|
41161
41033
|
}
|
|
41162
41034
|
}
|
|
41163
41035
|
});
|
|
41164
41036
|
|
|
41165
41037
|
// src/cross-file/rules/css-layout-conditional-offset-shift.ts
|
|
41166
|
-
var
|
|
41038
|
+
var messages156 = {
|
|
41167
41039
|
conditionalOffsetShift: "Conditional style applies non-zero '{{property}}' offset ({{value}}), which can cause layout shifts when conditions toggle."
|
|
41168
41040
|
};
|
|
41169
41041
|
var cssLayoutConditionalOffsetShift = defineCrossRule({
|
|
41170
41042
|
id: "css-layout-conditional-offset-shift",
|
|
41171
41043
|
severity: "warn",
|
|
41172
|
-
messages:
|
|
41044
|
+
messages: messages156,
|
|
41173
41045
|
meta: {
|
|
41174
41046
|
description: "Disallow conditional non-zero block-axis offsets that can trigger layout shifts.",
|
|
41175
41047
|
fixable: false,
|
|
@@ -41195,7 +41067,7 @@ var cssLayoutConditionalOffsetShift = defineCrossRule({
|
|
|
41195
41067
|
ref.solid.sourceFile,
|
|
41196
41068
|
cssLayoutConditionalOffsetShift.id,
|
|
41197
41069
|
"conditionalOffsetShift",
|
|
41198
|
-
resolveMessage(
|
|
41070
|
+
resolveMessage(messages156.conditionalOffsetShift, {
|
|
41199
41071
|
property: match.property,
|
|
41200
41072
|
value: `${formatFixed(match.value)}px`
|
|
41201
41073
|
}),
|
|
@@ -41256,7 +41128,7 @@ function hasStableBaseline(baselineBySignal, property, expectedPx) {
|
|
|
41256
41128
|
|
|
41257
41129
|
// src/cross-file/rules/jsx-layout-unstable-style-toggle.ts
|
|
41258
41130
|
var import_typescript137 = __toESM(require("typescript"), 1);
|
|
41259
|
-
var
|
|
41131
|
+
var messages157 = {
|
|
41260
41132
|
unstableLayoutStyleToggle: "Dynamic style value for '{{property}}' can toggle layout geometry at runtime and cause CLS."
|
|
41261
41133
|
};
|
|
41262
41134
|
var PX_NUMBER_PROPERTIES = /* @__PURE__ */ new Set([
|
|
@@ -41279,7 +41151,7 @@ var POSITIONED_OFFSET_PROPERTIES = /* @__PURE__ */ new Set([
|
|
|
41279
41151
|
var jsxLayoutUnstableStyleToggle = defineCrossRule({
|
|
41280
41152
|
id: "jsx-layout-unstable-style-toggle",
|
|
41281
41153
|
severity: "warn",
|
|
41282
|
-
messages:
|
|
41154
|
+
messages: messages157,
|
|
41283
41155
|
meta: {
|
|
41284
41156
|
description: "Flag dynamic inline style values on layout-sensitive properties that can trigger CLS.",
|
|
41285
41157
|
fixable: false,
|
|
@@ -41301,7 +41173,7 @@ var jsxLayoutUnstableStyleToggle = defineCrossRule({
|
|
|
41301
41173
|
solid.sourceFile,
|
|
41302
41174
|
jsxLayoutUnstableStyleToggle.id,
|
|
41303
41175
|
"unstableLayoutStyleToggle",
|
|
41304
|
-
resolveMessage(
|
|
41176
|
+
resolveMessage(messages157.unstableLayoutStyleToggle, { property: normalized }),
|
|
41305
41177
|
"warn"
|
|
41306
41178
|
)
|
|
41307
41179
|
);
|
|
@@ -41418,14 +41290,14 @@ function unwrapTypeWrapper(node) {
|
|
|
41418
41290
|
|
|
41419
41291
|
// src/cross-file/rules/jsx-layout-classlist-geometry-toggle.ts
|
|
41420
41292
|
var import_typescript138 = __toESM(require("typescript"), 1);
|
|
41421
|
-
var
|
|
41293
|
+
var messages158 = {
|
|
41422
41294
|
classListGeometryToggle: "classList toggles '{{className}}', and matching CSS changes layout-affecting '{{property}}', which can cause CLS."
|
|
41423
41295
|
};
|
|
41424
41296
|
var OUT_OF_FLOW_POSITIONS2 = /* @__PURE__ */ new Set(["fixed", "absolute"]);
|
|
41425
41297
|
var jsxLayoutClasslistGeometryToggle = defineCrossRule({
|
|
41426
41298
|
id: "jsx-layout-classlist-geometry-toggle",
|
|
41427
41299
|
severity: "warn",
|
|
41428
|
-
messages:
|
|
41300
|
+
messages: messages158,
|
|
41429
41301
|
meta: {
|
|
41430
41302
|
description: "Flag classList-driven class toggles that map to layout-affecting CSS geometry changes.",
|
|
41431
41303
|
fixable: false,
|
|
@@ -41451,7 +41323,7 @@ var jsxLayoutClasslistGeometryToggle = defineCrossRule({
|
|
|
41451
41323
|
solid.sourceFile,
|
|
41452
41324
|
jsxLayoutClasslistGeometryToggle.id,
|
|
41453
41325
|
"classListGeometryToggle",
|
|
41454
|
-
resolveMessage(
|
|
41326
|
+
resolveMessage(messages158.classListGeometryToggle, {
|
|
41455
41327
|
className,
|
|
41456
41328
|
property
|
|
41457
41329
|
}),
|
|
@@ -41500,14 +41372,14 @@ function isDynamicallyToggleable(node) {
|
|
|
41500
41372
|
}
|
|
41501
41373
|
|
|
41502
41374
|
// src/cross-file/rules/jsx-layout-picture-source-ratio-consistency.ts
|
|
41503
|
-
var
|
|
41375
|
+
var messages159 = {
|
|
41504
41376
|
inconsistentPictureRatio: "`<picture>` source ratio {{sourceRatio}} differs from fallback img ratio {{imgRatio}}, which can cause reserved-space mismatch and CLS."
|
|
41505
41377
|
};
|
|
41506
41378
|
var RATIO_DELTA_THRESHOLD = 0.02;
|
|
41507
41379
|
var jsxLayoutPictureSourceRatioConsistency = defineCrossRule({
|
|
41508
41380
|
id: "jsx-layout-picture-source-ratio-consistency",
|
|
41509
41381
|
severity: "warn",
|
|
41510
|
-
messages:
|
|
41382
|
+
messages: messages159,
|
|
41511
41383
|
meta: {
|
|
41512
41384
|
description: "Require consistent intrinsic aspect ratios across <picture> sources and fallback image.",
|
|
41513
41385
|
fixable: false,
|
|
@@ -41530,7 +41402,7 @@ var jsxLayoutPictureSourceRatioConsistency = defineCrossRule({
|
|
|
41530
41402
|
solid.sourceFile,
|
|
41531
41403
|
jsxLayoutPictureSourceRatioConsistency.id,
|
|
41532
41404
|
"inconsistentPictureRatio",
|
|
41533
|
-
resolveMessage(
|
|
41405
|
+
resolveMessage(messages159.inconsistentPictureRatio, {
|
|
41534
41406
|
sourceRatio: mismatch.sourceRatio,
|
|
41535
41407
|
imgRatio: mismatch.imgRatio
|
|
41536
41408
|
}),
|
|
@@ -41600,13 +41472,13 @@ function formatRatio(value2) {
|
|
|
41600
41472
|
}
|
|
41601
41473
|
|
|
41602
41474
|
// src/cross-file/rules/jsx-layout-fill-image-parent-must-be-sized.ts
|
|
41603
|
-
var
|
|
41475
|
+
var messages160 = {
|
|
41604
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."
|
|
41605
41477
|
};
|
|
41606
41478
|
var jsxLayoutFillImageParentMustBeSized = defineCrossRule({
|
|
41607
41479
|
id: "jsx-layout-fill-image-parent-must-be-sized",
|
|
41608
41480
|
severity: "warn",
|
|
41609
|
-
messages:
|
|
41481
|
+
messages: messages160,
|
|
41610
41482
|
meta: {
|
|
41611
41483
|
description: "Require stable parent size and positioning for fill-image component usage.",
|
|
41612
41484
|
fixable: false,
|
|
@@ -41636,7 +41508,7 @@ var jsxLayoutFillImageParentMustBeSized = defineCrossRule({
|
|
|
41636
41508
|
solid.sourceFile,
|
|
41637
41509
|
jsxLayoutFillImageParentMustBeSized.id,
|
|
41638
41510
|
"unsizedFillParent",
|
|
41639
|
-
resolveMessage(
|
|
41511
|
+
resolveMessage(messages160.unsizedFillParent, {
|
|
41640
41512
|
component: element.tag ?? "Image"
|
|
41641
41513
|
}),
|
|
41642
41514
|
"warn"
|
|
@@ -41647,6 +41519,210 @@ var jsxLayoutFillImageParentMustBeSized = defineCrossRule({
|
|
|
41647
41519
|
}
|
|
41648
41520
|
});
|
|
41649
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
|
+
|
|
41650
41726
|
// src/cross-file/rules/index.ts
|
|
41651
41727
|
var rules3 = [
|
|
41652
41728
|
jsxNoUndefinedCssClass,
|
|
@@ -41678,7 +41754,8 @@ var rules3 = [
|
|
|
41678
41754
|
cssLayoutOverflowModeToggleInstability,
|
|
41679
41755
|
cssLayoutBoxSizingToggleWithChrome,
|
|
41680
41756
|
cssLayoutContentVisibilityNoIntrinsicSize,
|
|
41681
|
-
cssLayoutConditionalOffsetShift
|
|
41757
|
+
cssLayoutConditionalOffsetShift,
|
|
41758
|
+
jsxLayoutPolicyTouchTarget
|
|
41682
41759
|
];
|
|
41683
41760
|
|
|
41684
41761
|
// src/cross-file/plugin.ts
|
|
@@ -41980,19 +42057,6 @@ var RULES = [
|
|
|
41980
42057
|
"paragraphSpacingTooSmall": "Paragraph spacing `{{value}}` ({{resolved}}em) is below the minimum `{{min}}em` ({{minMultiplier}}\xD7 font-size) for policy `{{policy}}`."
|
|
41981
42058
|
}
|
|
41982
42059
|
},
|
|
41983
|
-
{
|
|
41984
|
-
"id": "css-policy-touch-target",
|
|
41985
|
-
"severity": "warn",
|
|
41986
|
-
"description": "Enforce minimum interactive element sizes per accessibility policy.",
|
|
41987
|
-
"fixable": false,
|
|
41988
|
-
"category": "css-a11y",
|
|
41989
|
-
"plugin": "css",
|
|
41990
|
-
"messages": {
|
|
41991
|
-
"heightTooSmall": "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
|
|
41992
|
-
"widthTooSmall": "`{{property}}` of `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`.",
|
|
41993
|
-
"paddingTooSmall": "Horizontal padding `{{value}}` ({{resolved}}px) is below the minimum `{{min}}px` for `{{element}}` elements in policy `{{policy}}`."
|
|
41994
|
-
}
|
|
41995
|
-
},
|
|
41996
42060
|
{
|
|
41997
42061
|
"id": "css-policy-typography",
|
|
41998
42062
|
"severity": "warn",
|
|
@@ -42016,6 +42080,21 @@ var RULES = [
|
|
|
42016
42080
|
"missingReducedMotion": "Animated selector `{{selector}}` lacks prefers-reduced-motion override."
|
|
42017
42081
|
}
|
|
42018
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
|
+
},
|
|
42019
42098
|
{
|
|
42020
42099
|
"id": "css-no-discrete-transition",
|
|
42021
42100
|
"severity": "error",
|
|
@@ -43674,7 +43753,7 @@ var RULES = [
|
|
|
43674
43753
|
];
|
|
43675
43754
|
var RULES_BY_CATEGORY = {
|
|
43676
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." } }],
|
|
43677
|
-
"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." } }],
|
|
43678
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." } }],
|
|
43679
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." } }],
|
|
43680
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}}`." } }],
|