@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
- * through conditionals, `&&`, arrays, objects (keys and values) and `+`;
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 looks only at literals, with no scope or type information: a class built
24
- * in a variable outside these calls isn't seen.
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 call that makes a string a class, or undefined when it isn't one. */
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
  }
@@ -1,4 +1,5 @@
1
1
  {
2
+ "plugins": [],
2
3
  "jsPlugins": ["@botiverse/raft-ui/lint"],
3
4
  "rules": {
4
5
  "raft-ui/prefer-scroll-area": "error"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-ui",
3
- "version": "0.77.0",
3
+ "version": "0.78.0",
4
4
  "description": "React components and CSS foundations for the Raft Design System.",
5
5
  "keywords": [
6
6
  "agent-skills",