@nadicodeai/ui 12.1.36 → 12.1.38

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.
@@ -7,6 +7,8 @@ import { confirmDestructiveAction } from "./rules/confirm-destructive-action.js"
7
7
  import { drawerOwnsItsEdges } from "./rules/drawer-owns-its-edges.js";
8
8
  import { iconControlAccessibleName } from "./rules/icon-control-accessible-name.js";
9
9
  import { noAppFeatureComponent } from "./rules/no-app-feature-component.js";
10
+ import { noBarePageNavigation } from "./rules/no-bare-page-navigation.js";
11
+ import { noStoreCast } from "./rules/no-store-cast.js";
10
12
  import { noElevatedEdge } from "./rules/no-elevated-edge.js";
11
13
  import { noExistenceOnlyAssertion } from "./rules/no-existence-only-assertion.js";
12
14
  import { noNestedOverlay } from "./rules/no-nested-overlay.js";
@@ -119,6 +121,8 @@ const plugin = {
119
121
  "drawer-owns-its-edges": drawerOwnsItsEdges,
120
122
  "icon-control-accessible-name": iconControlAccessibleName,
121
123
  "no-app-feature-component": noAppFeatureComponent,
124
+ "no-bare-page-navigation": noBarePageNavigation,
125
+ "no-store-cast": noStoreCast,
122
126
  "no-elevated-edge": noElevatedEdge,
123
127
  "no-existence-only-assertion": noExistenceOnlyAssertion,
124
128
  "no-deprecated-zod-api": noDeprecatedZodApi,
@@ -0,0 +1,46 @@
1
+ /*
2
+ * The Portal's browser suite navigates through `tests/support/hydration.ts`.
3
+ * `page.goto` and `page.reload` resolve while React is still hydrating, and the
4
+ * interaction a spec dispatches next lands on server markup the shell is about
5
+ * to replace: #2173 lost six open menus that way, #2776 clicked "Create team"
6
+ * on a page that had not taken over yet, and #2837 read a prefetch as a
7
+ * refresh. `back` and `forward` are outside the rule, because they restore a
8
+ * tree the client already owns rather than loading a document.
9
+ */
10
+
11
+ const NAVIGATIONS = new Map([
12
+ ["goto", "bareGoto"],
13
+ ["reload", "bareReload"],
14
+ ]);
15
+
16
+ function propertyName(member) {
17
+ if (member.computed) return member.property.type === "Literal" ? member.property.value : null;
18
+ return member.property.type === "Identifier" ? member.property.name : null;
19
+ }
20
+
21
+ export const noBarePageNavigation = {
22
+ meta: {
23
+ type: "problem",
24
+ docs: {
25
+ description:
26
+ "Disallow a bare page.goto or page.reload in the Portal's browser suite; navigate through the helper that waits for the shell to hydrate",
27
+ },
28
+ schema: [],
29
+ messages: {
30
+ bareGoto:
31
+ "`goto()` resolves before React hydrates the page, so the next click or keypress lands on server markup the shell is about to replace (#2173, #2776, #2837). Open the page with `gotoPortal` from `tests/support/hydration`, which waits for the shell's `data-hydrated`, or with `gotoOutsideShell` for an address the dashboard shell never renders, such as login, signup or an app's consent page.",
32
+ bareReload:
33
+ "`reload()` resolves before React hydrates the page, so the next click or keypress lands on server markup the shell is about to replace (#2776). Reload with `reloadPortal` from `tests/support/hydration`, which waits for the shell's `data-hydrated`.",
34
+ },
35
+ },
36
+
37
+ create(context) {
38
+ return {
39
+ CallExpression(node) {
40
+ if (node.callee.type !== "MemberExpression") return;
41
+ const messageId = NAVIGATIONS.get(propertyName(node.callee));
42
+ if (messageId) context.report({ node, messageId });
43
+ },
44
+ };
45
+ },
46
+ };
@@ -0,0 +1,46 @@
1
+ /*
2
+ * A test hands the subject the store methods it calls, as an object typed by
3
+ * the subject's own parameter. `as PortalStore` (or `as unknown as
4
+ * PortalStore`) turns a partial object into the whole 227-method store: the
5
+ * compiler then checks nothing about the methods the fake does implement, and a
6
+ * subject that grows a call reaches `undefined` at run time instead of failing
7
+ * to compile. Narrow the subject's parameter to the capability it uses
8
+ * (`Pick<SomePersistence, "a" | "b">`), then build the fake as that type.
9
+ */
10
+
11
+ export const noStoreCast = {
12
+ meta: {
13
+ type: "problem",
14
+ docs: {
15
+ description:
16
+ "Disallow casting a partial object to the whole store in a test; type the subject by the store methods it calls and build the fake as that type",
17
+ },
18
+ schema: [
19
+ {
20
+ type: "object",
21
+ properties: { types: { type: "array", items: { type: "string" } } },
22
+ additionalProperties: false,
23
+ },
24
+ ],
25
+ messages: {
26
+ storeCast:
27
+ '`as {{name}}` hides which store methods this fake implements, so the compiler checks none of them. Type the subject\'s store parameter as the methods it calls (`Pick<SomePersistence, "a" | "b">`) and build the fake as `Parameters<typeof subject>[0]`.',
28
+ },
29
+ },
30
+
31
+ create(context) {
32
+ const names = new Set(context.options[0]?.types ?? ["PortalStore"]);
33
+ return {
34
+ TSAsExpression(node) {
35
+ const type = node.typeAnnotation;
36
+ if (
37
+ type.type === "TSTypeReference" &&
38
+ type.typeName.type === "Identifier" &&
39
+ names.has(type.typeName.name)
40
+ ) {
41
+ context.report({ node, messageId: "storeCast", data: { name: type.typeName.name } });
42
+ }
43
+ },
44
+ };
45
+ },
46
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/ui",
3
- "version": "12.1.36",
3
+ "version": "12.1.38",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -77,7 +77,7 @@
77
77
  "@assistant-ui/react": "0.15.21",
78
78
  "@assistant-ui/react-markdown": "0.14.16",
79
79
  "@base-ui/react": "^1.8.0",
80
- "@nadicodeai/design-system": "12.1.36",
80
+ "@nadicodeai/design-system": "12.1.38",
81
81
  "class-variance-authority": "^0.7.1",
82
82
  "clsx": "^2.1.1",
83
83
  "cmdk": "^1.1.1",