@mittwald/flow-codemods 1.1.46 → 1.1.48

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.
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Renames the `defaultOpen` prop to `isDefaultOpen` on `Popover`,
5
+ * `ContextualHelp` and `ContextMenu`.
6
+ *
7
+ * The scope is deliberately narrow, and here that is not only about same-named
8
+ * components from other packages. `defaultOpen` is react-aria's own prop and
9
+ * Flow passes it straight through on `Select`, `MenuTrigger`, `Tooltip`,
10
+ * `TooltipTrigger`, `DialogTrigger`, `DatePicker` and `DateRangePicker`, where
11
+ * it did not change — renaming it there would remove a working prop.
12
+ *
13
+ * Only JSX elements that resolve to one of the three — imported (named or as a
14
+ * namespace) from `@mittwald/flow-react-components` or
15
+ * `@mittwald/flow-remote-react-components`, including their subpath entries —
16
+ * are touched.
17
+ *
18
+ * An element that already carries `isDefaultOpen` keeps it and only loses the
19
+ * stale `defaultOpen`, which mirrors the runtime: `isDefaultOpen` wins.
20
+ *
21
+ * The other two changes of this migration are not here. `isOpen` and
22
+ * `onOpenChange` changed behaviour, not spelling: whether a given `isOpen` is
23
+ * meant to control the popover, and whether an `onOpenChange` handler performed
24
+ * or suppressed the close, is not decidable from the source. `apply` asks for
25
+ * those by hand.
26
+ */
27
+ const popoverOpenStatePropsTransform = (fileInfo, { j }) => {
28
+ const flowPackages = [
29
+ "@mittwald/flow-react-components",
30
+ "@mittwald/flow-remote-react-components",
31
+ ];
32
+ const affectedComponents = new Set([
33
+ "Popover",
34
+ "ContextualHelp",
35
+ "ContextMenu",
36
+ ]);
37
+ const isFlowImport = (source) => flowPackages.some((pkg) => source === pkg || source.startsWith(`${pkg}/`));
38
+ const root = j(fileInfo.source, { parser: "tsx" });
39
+ // Local JSX identifier -> canonical component name (resolves `as` aliases).
40
+ const localToComponent = new Map();
41
+ // Local names of `import * as Flow` namespace imports from a Flow package.
42
+ const flowNamespaces = new Set();
43
+ root
44
+ .find(j.ImportDeclaration)
45
+ .filter((path) => isFlowImport(String(path.node.source.value)))
46
+ .forEach((path) => {
47
+ for (const specifier of path.node.specifiers ?? []) {
48
+ if (specifier.type === "ImportSpecifier" &&
49
+ specifier.imported.type === "Identifier" &&
50
+ affectedComponents.has(specifier.imported.name)) {
51
+ localToComponent.set(String(specifier.local?.name ?? specifier.imported.name), String(specifier.imported.name));
52
+ }
53
+ else if (specifier.type === "ImportNamespaceSpecifier" &&
54
+ specifier.local) {
55
+ flowNamespaces.add(String(specifier.local.name));
56
+ }
57
+ }
58
+ });
59
+ if (localToComponent.size === 0 && flowNamespaces.size === 0) {
60
+ return fileInfo.source;
61
+ }
62
+ let changed = false;
63
+ root.find(j.JSXOpeningElement).forEach((path) => {
64
+ const name = path.node.name;
65
+ let isAffected = false;
66
+ if (name.type === "JSXIdentifier") {
67
+ isAffected = localToComponent.has(name.name);
68
+ }
69
+ else if (name.type === "JSXMemberExpression" &&
70
+ name.object.type === "JSXIdentifier" &&
71
+ name.property.type === "JSXIdentifier") {
72
+ isAffected =
73
+ flowNamespaces.has(name.object.name) &&
74
+ affectedComponents.has(name.property.name);
75
+ }
76
+ if (!isAffected) {
77
+ return;
78
+ }
79
+ const attributes = path.node.attributes ?? [];
80
+ const isNamed = (attribute, key) => attribute.type === "JSXAttribute" &&
81
+ attribute.name.type === "JSXIdentifier" &&
82
+ attribute.name.name === key;
83
+ if (!attributes.some((attribute) => isNamed(attribute, "defaultOpen"))) {
84
+ return;
85
+ }
86
+ changed = true;
87
+ if (attributes.some((attribute) => isNamed(attribute, "isDefaultOpen"))) {
88
+ path.node.attributes = attributes.filter((attribute) => !isNamed(attribute, "defaultOpen"));
89
+ return;
90
+ }
91
+ for (const attribute of attributes) {
92
+ if (isNamed(attribute, "defaultOpen") &&
93
+ attribute.type === "JSXAttribute" &&
94
+ attribute.name.type === "JSXIdentifier") {
95
+ attribute.name.name = "isDefaultOpen";
96
+ }
97
+ }
98
+ });
99
+ return changed ? root.toSource() : fileInfo.source;
100
+ };
101
+ exports.default = popoverOpenStatePropsTransform;
@@ -3,6 +3,15 @@
3
3
  // Source: packages/codemods/src/migrations/<id>/entry.md
4
4
  /** Every migration, newest first. Bodies live in `src/migrations`. */
5
5
  export const migrations = [
6
+ {
7
+ id: "popover-open-state-props",
8
+ since: "1.1.47",
9
+ title: "Popover: `defaultOpen` renamed, `isOpen` and `onOpenChange` now work",
10
+ kind: "migration",
11
+ action: "codemod",
12
+ remotePackage: true,
13
+ apply: "Rename `defaultOpen` to `isDefaultOpen` on `Popover`, `ContextualHelp` and `ContextMenu` — a codemod does it, scoped to those three. Leave `defaultOpen` alone on `Select`, `MenuTrigger`, `Tooltip`, `TooltipTrigger`, `DialogTrigger`, `DatePicker` and `DateRangePicker`, where it is react-aria's own prop and unchanged. Then check every `isOpen` and `onOpenChange` passed to those three by hand, because both changed behaviour and neither is mechanically decidable. `isOpen` used to be ignored and now controls the popover: a value that is not kept up to date through `onOpenChange` keeps the popover closed. `onOpenChange` used to take over the open state and now only reports it, so a handler that performed the close itself — `onOpenChange={(open) => controller.setOpen(open)}` — can drop that call, and a handler that relied on the prop to *suppress* the close no longer does; use the controller's `onClose` for that.",
14
+ },
6
15
  {
7
16
  id: "tabler-icons-no-longer-transitive",
8
17
  since: "1.1.40",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/flow-codemods",
3
- "version": "1.1.46",
3
+ "version": "1.1.48",
4
4
  "type": "module",
5
5
  "description": "Codemods and an upgrade CLI for consumers of Flow, mittwald's design system",
6
6
  "homepage": "https://flow.mittwald.de",
@@ -38,5 +38,5 @@
38
38
  "vitest": "^4.1.11",
39
39
  "yaml": "^2.8.1"
40
40
  },
41
- "gitHead": "6e6d23d148abd4884fd8f955e0c26641265a6854"
41
+ "gitHead": "29caa832765bca544d6fae2e4c05e8b3dedcdcb3"
42
42
  }