@botiverse/raft-ui 0.77.0 → 0.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## [0.78.0](https://github.com/botiverse/raft-design/compare/v0.77.1...v0.78.0) (2026-11-08)
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
|
|
5
|
+
- **`raft-ui/prefer-scroll-area` also reads class lists kept in a const and a `className` property**: a native-scrolling class in a `const` of the same file is reported when the const is used as a class there (`const log = "max-h-64 overflow-auto"`, then `className={log}` or `cn(log)`), wherever in the file it is declared; and so is one in a `className`, `class` or `*ClassName` property, such as a recipe slot's option (`content({ className: "overflow-y-auto" })`). The escape comment works on them as before. ([#1078](https://github.com/botiverse/raft-design/pull/1078))
|
|
6
|
+
- Why: apps keep many class lists in consts; Stamp's port of the rule found four native scrollers this way that the rule missed (Kirby, #proj-raft-ui:#209).
|
|
7
|
+
- Affects: upgrading can report scrollers the rule didn't see before; fix them, or mark an allowed exception. The Raft app gets one, the onboarding form panel (`ONBOARDING_CREATE_FORM_PANEL_CLASS`); its desktop app none. A string in a const that is never used as a class is still not reported.
|
|
8
|
+
- Docs: https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
|
|
9
|
+
|
|
10
|
+
## [0.77.1](https://github.com/botiverse/raft-design/compare/v0.77.0...v0.77.1) (2026-11-08)
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **The lint plugin's recommended config keeps the app's own Oxlint plugins**: `recommended.json` sets `"plugins": []`. ([#1076](https://github.com/botiverse/raft-design/pull/1076))
|
|
15
|
+
- Why: an extended config without `plugins` counts as Oxlint's default plugins, so extending it turned `unicorn` back on in an app that lists its plugins without it (36 new warnings in slock, Kirby, #proj-raft-ui:#209). Same on Oxlint 1.85, 1.87 and 1.91.
|
|
16
|
+
- Affects: an app that extends `recommended.json` keeps exactly the `plugins` it lists; an app that lists none keeps Oxlint's defaults, as before. The ESLint config is unchanged.
|
|
17
|
+
- Docs: https://raft-design.botiverse.build/about/use-raft-ui/#lint-rules
|
|
18
|
+
|
|
1
19
|
## [0.77.0](https://github.com/botiverse/raft-design/compare/v0.76.1...v0.77.0) (2026-11-08)
|
|
2
20
|
|
|
3
21
|
### Changed
|
|
@@ -9,8 +9,13 @@
|
|
|
9
9
|
* set to auto or scroll, with any variant prefix, `!`, or as an arbitrary
|
|
10
10
|
* property) in a string or template literal that is a class: the value of a
|
|
11
11
|
* `className`, `class` or `*ClassName` JSX attribute, or an argument of
|
|
12
|
-
* `cn`, `clsx`, `cx`, `tv`, `cva`, `twMerge`, `twJoin` or `classNames`,
|
|
13
|
-
*
|
|
12
|
+
* `cn`, `clsx`, `cx`, `tv`, `cva`, `twMerge`, `twJoin` or `classNames`, or
|
|
13
|
+
* a `className`, `class` or `*ClassName` property (a recipe slot's option,
|
|
14
|
+
* `content({ className: "…" })`), through conditionals, `&&`, arrays,
|
|
15
|
+
* objects (keys and values) and `+`;
|
|
16
|
+
* - such a string in a `const` of the same file that is used as a class
|
|
17
|
+
* (`const log = "max-h-64 …"`, then `className={log}` or
|
|
18
|
+
* `cn(log)`), wherever in the file it is declared;
|
|
14
19
|
* - an inline `style={{ overflow | overflowX | overflowY: "auto" | "scroll" }}`.
|
|
15
20
|
*
|
|
16
21
|
* Allowed with a `raft-ui: native-scroll <reason>` comment, line or block, on
|
|
@@ -20,8 +25,9 @@
|
|
|
20
25
|
* still reported. For an inline style, the line of its property. A comment
|
|
21
26
|
* without a reason doesn't count.
|
|
22
27
|
*
|
|
23
|
-
* It
|
|
24
|
-
* in a
|
|
28
|
+
* It reads the file as written, with no scope or type information: a const is
|
|
29
|
+
* matched by its name in the same file, and a class in a `let`, an import, a
|
|
30
|
+
* member (`styles.log`) or a function's return isn't seen.
|
|
25
31
|
*/
|
|
26
32
|
export interface Position {
|
|
27
33
|
line: number;
|
|
@@ -34,6 +34,7 @@ const PASS_THROUGH = /* @__PURE__ */ new Set([
|
|
|
34
34
|
"TSNonNullExpression",
|
|
35
35
|
"TSSatisfiesExpression"
|
|
36
36
|
]);
|
|
37
|
+
const INIT_THROUGH = /* @__PURE__ */ new Set([...PASS_THROUGH, "CallExpression"]);
|
|
37
38
|
const ESCAPE = /\braft-ui:\s*native-scroll\s+\S/;
|
|
38
39
|
/** Whether a class string has a native-scrolling class in it. */
|
|
39
40
|
function hasNativeScrollClass(value) {
|
|
@@ -44,7 +45,7 @@ function attributeName(node) {
|
|
|
44
45
|
const name = node.name;
|
|
45
46
|
return name?.type === "JSXIdentifier" ? name.name : void 0;
|
|
46
47
|
}
|
|
47
|
-
/** The attribute or
|
|
48
|
+
/** The attribute, call or property that makes a node a class, or undefined when it isn't one. */
|
|
48
49
|
function classContext(node) {
|
|
49
50
|
let child = node;
|
|
50
51
|
let parent = node.parent;
|
|
@@ -57,11 +58,28 @@ function classContext(node) {
|
|
|
57
58
|
const callee = parent.callee;
|
|
58
59
|
return callee.type === "Identifier" && CLASS_FUNCTIONS.has(callee.name) && parent.arguments.includes(child) ? parent : void 0;
|
|
59
60
|
}
|
|
61
|
+
if (parent.type === "Property" && parent.value === child) {
|
|
62
|
+
const key = propertyKey(parent);
|
|
63
|
+
if (key !== void 0 && CLASS_ATTRIBUTE.test(key)) return parent;
|
|
64
|
+
}
|
|
60
65
|
if (!PASS_THROUGH.has(parent.type)) return void 0;
|
|
61
66
|
child = parent;
|
|
62
67
|
parent = parent.parent;
|
|
63
68
|
}
|
|
64
69
|
}
|
|
70
|
+
/** The `const` declarator whose initializer holds `node` (through conditionals, arrays, calls), if any. */
|
|
71
|
+
function constDeclarator(node) {
|
|
72
|
+
let parent = node.parent;
|
|
73
|
+
while (parent) {
|
|
74
|
+
if (parent.type === "VariableDeclarator") {
|
|
75
|
+
const id = parent.id;
|
|
76
|
+
const declaration = parent.parent;
|
|
77
|
+
return id?.type === "Identifier" && declaration?.kind === "const" ? parent : void 0;
|
|
78
|
+
}
|
|
79
|
+
if (!INIT_THROUGH.has(parent.type)) return void 0;
|
|
80
|
+
parent = parent.parent;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
65
83
|
function stringValue(node) {
|
|
66
84
|
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
67
85
|
if (node.type === "TemplateLiteral" && node.expressions.length === 0) {
|
|
@@ -127,9 +145,15 @@ const preferScrollArea = {
|
|
|
127
145
|
messageId: "preferScrollArea"
|
|
128
146
|
});
|
|
129
147
|
}
|
|
148
|
+
const constStrings = /* @__PURE__ */ new Map();
|
|
149
|
+
const classNames = /* @__PURE__ */ new Set();
|
|
130
150
|
function checkClassString(node, value) {
|
|
131
151
|
if (!hasNativeScrollClass(value)) return;
|
|
132
|
-
if (classContext(node)) report(node, node);
|
|
152
|
+
if (classContext(node)) return report(node, node);
|
|
153
|
+
const declarator = constDeclarator(node);
|
|
154
|
+
if (!declarator) return;
|
|
155
|
+
const name = declarator.id.name;
|
|
156
|
+
constStrings.set(name, [...constStrings.get(name) ?? [], node]);
|
|
133
157
|
}
|
|
134
158
|
return {
|
|
135
159
|
Literal(node) {
|
|
@@ -138,6 +162,11 @@ const preferScrollArea = {
|
|
|
138
162
|
TemplateLiteral(node) {
|
|
139
163
|
checkClassString(node, node.quasis.map((quasi) => quasi.value.raw).join(" "));
|
|
140
164
|
},
|
|
165
|
+
Identifier(node) {
|
|
166
|
+
const parent = node.parent;
|
|
167
|
+
if (parent?.type === "Property" && parent.key === node && !parent.computed && !parent.shorthand) return;
|
|
168
|
+
if (classContext(node)) classNames.add(node.name);
|
|
169
|
+
},
|
|
141
170
|
JSXAttribute(node) {
|
|
142
171
|
if (attributeName(node) !== "style") return;
|
|
143
172
|
const value = node.value;
|
|
@@ -149,6 +178,9 @@ const preferScrollArea = {
|
|
|
149
178
|
if (key === void 0 || !NATIVE_SCROLL_STYLE_KEYS.has(key)) continue;
|
|
150
179
|
for (const literal of styleValueStrings(property.value, [])) if (NATIVE_SCROLL_STYLE_VALUES.has(stringValue(literal).trim())) report(literal, property);
|
|
151
180
|
}
|
|
181
|
+
},
|
|
182
|
+
"Program:exit"() {
|
|
183
|
+
for (const name of classNames) for (const node of constStrings.get(name) ?? []) report(node, node);
|
|
152
184
|
}
|
|
153
185
|
};
|
|
154
186
|
}
|