@tamagui/codemod-flat-values 0.0.0-bootstrap.0 → 3.0.0-beta.643.1

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 CHANGED
@@ -1 +1,211 @@
1
- # @tamagui/codemod-flat-values
1
+ # Flat-values codemod
2
+
3
+ Converts existing Tamagui style syntax to V3 flat property values. Dry-run mode
4
+ reports every conversion; `--write` applies every flat-eligible rewrite in one
5
+ transaction and records host or target assessments for manual review.
6
+
7
+ Run it from the root of the project you are migrating. Paths, the report and the
8
+ `tsconfig.json` it reads the type information from all resolve from there.
9
+
10
+ ```sh
11
+ npx @tamagui/codemod-flat-values ./src # dry run
12
+ npx @tamagui/codemod-flat-values --json report.json ./src # machine readable
13
+ npx @tamagui/codemod-flat-values --write ./src # apply safe conversions
14
+ npx @tamagui/codemod-flat-values --help
15
+ ```
16
+
17
+ Inside this repository the same two runs over the pinned corpus are
18
+ `bun run dry-run` and `bun run write` from `code/core/codemod-flat-values`. That
19
+ corpus is `code/kitchen-sink/src/usecases` plus the canonical `Button.tsx` skin,
20
+ and its acceptance test asserts the migrated corpus has no V1 conversion sites.
21
+ Focused fixtures still parse every emitted program back through
22
+ `@tamagui/style-grammar`.
23
+
24
+ A run with no positional argument exits 2, and so does a positional argument that
25
+ matches no file, so a typo in a migration path can never read as a clean corpus.
26
+
27
+ A directory containing `.tamagui-flat-values-ignore` is excluded recursively from
28
+ both dry-run and write mode. Use the marker only for intentionally pinned legacy
29
+ fixtures, such as the V2 side of a version comparison; application source should be
30
+ migrated rather than excluded.
31
+
32
+ ## What it converts
33
+
34
+ A conversion site is one style object: a JSX attribute list, a `styled()` config, or
35
+ a single variant branch. Everything comes from `@tamagui/style-grammar` — the value
36
+ parser, the legacy condition converter, the clause merge, and the property/unit
37
+ tables — so a converted program is spelled exactly the way the runtime and the
38
+ compiler read it back.
39
+
40
+ Only Tamagui bindings convert. A JSX tag or a `styled` callee has to resolve, through
41
+ module specifiers, to an import from `tamagui`/`@tamagui/*`, a re-export chain ending
42
+ in one, or a value built by a Tamagui factory. Emotion's `styled`, a local `styled`
43
+ helper, a `react-native` component, and an intrinsic tag are all left alone —
44
+ `hoverStyle` and `$token` mean nothing to them.
45
+
46
+ - Condition objects become clauses on the longhand's program:
47
+ `bg="$surface" hoverStyle={{ bg: '$surfaceHover' }}` becomes
48
+ `bg="surface hover:surfaceHover"`.
49
+ - v1 platform conditions (`$web`, `$ios`, …) become platform modifiers, and v1
50
+ camelCase media keys resolve to their V6 kebab-case names. The V6 built-in theme
51
+ names use one explicit rename table, so `$backgroundHover` becomes
52
+ `background-hover`. User-defined names keep their authored spelling.
53
+ `$backgroundActive` is the one non-mechanical migration: v3 removed it, so it
54
+ becomes `background-press`, matching the corrected active-state component defaults.
55
+ - Group conditions split the way V3 splits them: `$group-card-hover` becomes
56
+ `group-hover/card:`, and a container size (`$group-card-maxMd`) becomes
57
+ `@max-md/card:` plus `container containerName="card"` on the element that declares
58
+ the group. That element has to be proven, never inferred: a JSX ancestor declaring
59
+ the group takes the container silently, a single declaration elsewhere in the file
60
+ takes it under `unproven-container-group`, and anything else stays authored
61
+ (`ambiguous-container-group`, `container-group-not-declared`). Declaring a
62
+ container changes containment, so it never lands on a `group` this pass cannot tie
63
+ to the condition.
64
+ - A base value only folds into a program when a clause would otherwise need a second
65
+ attribute of the same name. `opacity={0.5}` plus `enterStyle={{ opacity: 0 }}`
66
+ becomes `opacity="0.5 enter:0"`, while a numeric or dynamic value nothing conditions
67
+ on is left exactly as authored.
68
+ - A dynamic value converts when its units and token spellings are provable: a tree of
69
+ literals is rewritten in place (`active ? '$red10' : '$blue10'`), and an expression
70
+ whose type the checker can prove interpolates into the program
71
+ (`` backgroundColor={`${GREEN} disabled:${GREY}`} ``).
72
+ - Variant and compound-variant branches convert as their own style objects, so a
73
+ branch contributes at its own position in the forward pass. No base is invented for
74
+ a condition-only branch: with clause-level merge (decision 21)
75
+ `exitStyle={{ x: 10 }}` is exactly `x="exit:10px"`, which keeps whatever base the
76
+ styled component, variant, or call site defined — the same thing the v1 prop did.
77
+
78
+ Every program the report suggests is parsed back with the real value parser and merged
79
+ with the real clause merge before it is printed. A program that does not read back
80
+ identically is reported (`emitted-program-mismatch`, `emitted-value-invalid`) instead
81
+ of suggested.
82
+
83
+ The codemod preserves palette-step names such as `blue10` and `red10`. It does
84
+ not evaluate the application's runtime config, so it cannot know whether a
85
+ custom config still defines them or which absolute shade preserves the design.
86
+ Every preserved v5 palette name appears as a non-blocking
87
+ `legacy-palette-token` configuration warning in the Markdown and JSON reports.
88
+ Write mode still applies the safe syntax conversion, so resolve every warning
89
+ before running the converted application. When moving to `@tamagui/config/v6`,
90
+ migrate each name manually to an absolute token such as `blue-500`, or enter a
91
+ color theme and use its adaptive `colorN` ramp. The v6 defaults do not define
92
+ the old palette-step names, and a missing color is dropped without a runtime
93
+ warning.
94
+
95
+ ## Conversion assessment
96
+
97
+ Syntax alone cannot make a conversion safe. Before suggesting a program, the codemod
98
+ asks the shared conversion contract whether the property can carry clauses, whether
99
+ every clause evaluates on every target implied by the file name, and whether the
100
+ component's TypeScript prop type accepts the style.
101
+
102
+ - **clean** means all three checks passed. A shared `.tsx` file must work on web and
103
+ native; `.web.tsx` and `.native.tsx` require only their named target.
104
+ - **needs-relocation** means the syntax is valid but the authored target or host cannot
105
+ evaluate it. Write mode still removes the V1 syntax because V3 has no legacy runtime;
106
+ the report prints the required hand edit. For example, a component-tier web state in
107
+ a shared file moves to `.web.tsx`, while text-only styles on a View move to Text or
108
+ `html.*`.
109
+ - **unknown-host** means the component is provably Tamagui but its TypeScript type does
110
+ not expose enough component identity and style-prop information to verify the host.
111
+ Write mode converts the site and leaves the host check in the report for review.
112
+ - **ineligible** means the property has no flat clause spelling. Transform and React
113
+ Native shadow part props stay authored, with a remedy that points to the `transform`,
114
+ `boxShadow`, or `textShadow` composite.
115
+
116
+ The type-aware host lookup is shared with `@tamagui/language-service`; the codemod does
117
+ not import or evaluate an app's runtime config.
118
+
119
+ Ordering is never traded for a bigger diff. A program merges only when every
120
+ contribution still beats and loses to the same things it did, so an opaque spread or
121
+ an unconverted condition object between two contributions leaves the later ones
122
+ authored (`condition-order-not-preservable`, `base-order-not-preservable`). Spreading
123
+ an object literal writes its members out, and a nested spread or an unreadable key
124
+ inside it stays exactly where it was authored and orders the merge like any other
125
+ spread. An unconvertible nested condition counts every longhand under it, at any
126
+ depth, as something its member can still set.
127
+
128
+ ## Apply mode
129
+
130
+ Clause-free tokens now become base-only programs: `p="$4"` becomes `p="4"` and
131
+ resolves config-first through the same flat engine as a value with modifiers.
132
+ For `x` and `y`, this also applies the v3 category cutover: the legacy `$4`
133
+ resolved through the size scale, while flat `4` resolves through the space scale.
134
+ If a custom config gives those scales different values, the rendered offset changes
135
+ by design and those rows need review during migration.
136
+
137
+ All legacy runtime gates are closed. Apply mode is the migration path; there is no
138
+ runtime compatibility setting or fallback parser.
139
+
140
+ ## Structured native values
141
+
142
+ Unconditional React Native objects and arrays keep their authored representation and
143
+ do not appear as migration work. When a condition needs the same property in one
144
+ string program, the codemod converts only the structures with an explicit,
145
+ round-trippable CSS spelling:
146
+
147
+ - a static `transform` array becomes an ordered CSS transform function list;
148
+ - a static `fontVariant` string array becomes a space-separated token list;
149
+ - one static React Native `linear-gradient` object becomes `linear-gradient(...)`.
150
+
151
+ Dynamic and Animated transform entries stay authored under
152
+ `structured-transform-dynamic`. Matrix arrays stay authored under
153
+ `structured-transform-matrix`, because React Native's matrix array and portable CSS
154
+ matrix forms do not have the same shape. A dynamic gradient gets
155
+ `structured-background-image-dynamic`; unsupported structured properties use a
156
+ property-specific `structured-<property>` code.
157
+
158
+ Plain `shadowOffset` and `textShadowOffset` objects also stay natural and are not
159
+ inventory. If a condition targets one, the shared eligibility contract reports it as
160
+ ineligible and points to the complete `boxShadow` or `textShadow` composite. The
161
+ codemod never guesses the missing color, radius, opacity, or sibling offset.
162
+
163
+ Transition arrays, per-property objects, and dynamic references remain authored
164
+ because they are supported animation-driver configuration, not flat-value
165
+ migration work. An open string type that could still hold a `$token` at runtime
166
+ is listed separately as `dynamic-string-value`, which only a human can confirm.
167
+
168
+ ## Flag codes
169
+
170
+ A flag means a human decides. Every code the tool can emit:
171
+
172
+ | code | meaning |
173
+ | --- | --- |
174
+ | `legacy-token-dot-path` | `$1.5` and friends need one flat token name |
175
+ | `legacy-numeric-composite-token` | a numeric token embedded in a composite needs its resolved CSS value |
176
+ | `legacy-token-constant` | the token lives in a module constant (`const RADIUS = '$6'`), so the constant is what migrates |
177
+ | `unproven-container-group`, `ambiguous-container-group`, `container-group-not-declared` | the element that has to declare the query container is not provable from this file |
178
+ | `value-reparses-as-program` | the converted string would read back as something other than one base value |
179
+ | `legacy-group-presence` | `$group-card` with no state or size styles every descendant unconditionally and has no flat spelling |
180
+ | `unknown-legacy-condition` | the condition name is not a registered spelling (a media key missing from the V6 defaults, for one) |
181
+ | `unregistered-legacy-condition` | a registered shape whose parameter is not registered |
182
+ | `non-style-condition-entry` | a conditional non-style prop (`numberOfLines`) has no flat target |
183
+ | `dynamic-legacy-condition` | the condition object is built at runtime (spread, computed key, non-literal) |
184
+ | `unprovable-dynamic-value` | a condition needs the base value, whose type cannot be proven |
185
+ | `dynamic-condition-value` | a value inside the condition object is not statically known |
186
+ | `non-css-style-value` | a condition needs a base that is `true`, `false`, or `null` |
187
+ | `empty-style-value` | a condition needs a base that is always nullish |
188
+ | `structured-transform-*` | a transform array is dynamic or has no faithful static function-list spelling; matrix arrays are intentionally included |
189
+ | `structured-font-variant-*`, `structured-background-image-*` | a font-variant or gradient structure is dynamic, empty, layered, or unsupported |
190
+ | `structured-<property>` | a condition needs an object or array with no verified CSS-shaped migration rule |
191
+ | `condition-order-not-preservable`, `base-order-not-preservable` | merging would move a value past something that can also set it |
192
+ | `computed-property` | a computed key hides the affected style property |
193
+ | `emitted-program-mismatch`, `emitted-value-invalid` | the printer failed its own re-parse; this is a codemod bug |
194
+ | plus any code from the shared converter | `unsupported-legacy-value`, `legacy-condition-object`, `ambiguous-legacy-group`, `legacy-composite-shorthand` |
195
+
196
+ ## Configuration warning codes
197
+
198
+ | code | meaning |
199
+ | --- | --- |
200
+ | `legacy-palette-token` | the conversion preserves a v5 palette-step name that the v6 defaults do not define; choose an absolute palette token or an adaptive `colorN` value |
201
+
202
+ `unsupported-legacy-value` covers the token-context refusals: a `$` mixed with quoted
203
+ or unquoted `url()` content is literal CSS the resolver never reads as a token
204
+ candidate, so the value is reported rather than rewritten. Base values and clause
205
+ payloads go through the same shared converter, so they always agree on that.
206
+
207
+ ## Remaining manual migration
208
+
209
+ The report counts the files whose sites all converted: those have no legacy condition
210
+ object left. V3 has no compatibility setting, so the report names every file that
211
+ still needs a hand edit after `--write`.
@@ -0,0 +1,13 @@
1
+ import { v6ThemeNameReplacements } from "@tamagui/style-grammar/tooling";
2
+
3
+ const v6CodemodBuiltInNameReplacements = {
4
+ ...v6ThemeNameReplacements,
5
+ backgroundActive: "background-press"
6
+ };
7
+ const builtInTokenPattern = new RegExp(`\\$(${Object.keys(v6CodemodBuiltInNameReplacements).join("|")})(?![\\w-])`, "g");
8
+ function replaceV6BuiltInTokens(value) {
9
+ return value.replace(builtInTokenPattern, (_, name) => `$${v6CodemodBuiltInNameReplacements[name]}`);
10
+ }
11
+
12
+ export { replaceV6BuiltInTokens, v6CodemodBuiltInNameReplacements };
13
+ //# sourceMappingURL=builtInNames.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtInNames.js","names":[],"sources":["builtInNames.js"],"sourcesContent":["import { v6ThemeNameReplacements } from \"@tamagui/style-grammar/tooling\";\nconst v6CodemodBuiltInNameReplacements = {\n ...v6ThemeNameReplacements,\n // v3 removed backgroundActive after its component defaults had already\n // stopped resolving; press is the corrected active-state default\n backgroundActive: \"background-press\"\n};\nconst builtInTokenPattern = new RegExp(\n `\\\\$(${Object.keys(v6CodemodBuiltInNameReplacements).join(\"|\")})(?![\\\\w-])`,\n \"g\"\n);\nfunction replaceV6BuiltInTokens(value) {\n return value.replace(\n builtInTokenPattern,\n (_, name) => `$${v6CodemodBuiltInNameReplacements[name]}`\n );\n}\nexport {\n replaceV6BuiltInTokens,\n v6CodemodBuiltInNameReplacements\n};\n//# sourceMappingURL=builtInNames.js.map\n"],"mappings":";;;AACA,MAAM,mCAAmC;CACvC,GAAG;CAGH,kBAAkB;AACpB;AACA,MAAM,sBAAsB,IAAI,OAC9B,OAAO,OAAO,KAAK,gCAAgC,EAAE,KAAK,GAAG,EAAE,cAC/D,GACF;AACA,SAAS,uBAAuB,OAAO;CACrC,OAAO,MAAM,QACX,sBACC,GAAG,SAAS,IAAI,iCAAiC,OACpD;AACF"}
@@ -0,0 +1,141 @@
1
+ import { Node, SyntaxKind } from "ts-morph";
2
+ import { isLegacyConditionName, resolveLegacyName } from "./legacyNames.mjs";
3
+
4
+ const emptyPlan = {
5
+ targets: /* @__PURE__ */ new Map(),
6
+ unresolved: /* @__PURE__ */ new Map()
7
+ };
8
+ function declaredGroup(declaration) {
9
+ if (Node.isJsxAttribute(declaration)) {
10
+ const initializer2 = declaration.getInitializer();
11
+ if (initializer2 === void 0) return "";
12
+ if (Node.isStringLiteral(initializer2)) return initializer2.getLiteralValue();
13
+ if (!Node.isJsxExpression(initializer2)) return null;
14
+ const expression = initializer2.getExpression();
15
+ if (expression === void 0) return null;
16
+ if (Node.isStringLiteral(expression) || Node.isNoSubstitutionTemplateLiteral(expression)) {
17
+ return expression.getLiteralValue();
18
+ }
19
+ return expression.getKind() === SyntaxKind.TrueKeyword ? "" : null;
20
+ }
21
+ const initializer = declaration.getInitializer();
22
+ if (initializer === void 0) return null;
23
+ if (Node.isStringLiteral(initializer) || Node.isNoSubstitutionTemplateLiteral(initializer)) {
24
+ return initializer.getLiteralValue();
25
+ }
26
+ return initializer.getKind() === SyntaxKind.TrueKeyword ? "" : null;
27
+ }
28
+ function groupAttribute(opening) {
29
+ for (const attribute of opening.getAttributes()) {
30
+ if (!Node.isJsxAttribute(attribute)) continue;
31
+ const name = attribute.getNameNode();
32
+ if (Node.isIdentifier(name) && name.getText() === "group") return attribute;
33
+ }
34
+ return null;
35
+ }
36
+ function matches(consumerGroup, declaration) {
37
+ if (declaration === null) return true;
38
+ return consumerGroup === "" || consumerGroup === declaration;
39
+ }
40
+ function containerConsumers(sourceFile, registry) {
41
+ const consumers = [];
42
+ const add = (node, name) => {
43
+ if (!isLegacyConditionName(name)) return;
44
+ const resolution = resolveLegacyName(name, registry);
45
+ if (!resolution.ok || resolution.resolved.container === null) return;
46
+ consumers.push({
47
+ node,
48
+ group: resolution.resolved.container.group ?? ""
49
+ });
50
+ };
51
+ for (const attribute of sourceFile.getDescendantsOfKind(SyntaxKind.JsxAttribute)) {
52
+ const name = attribute.getNameNode();
53
+ if (Node.isIdentifier(name)) add(attribute, name.getText());
54
+ }
55
+ for (const property of sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAssignment)) {
56
+ const name = property.getNameNode();
57
+ if (Node.isComputedPropertyName(name)) continue;
58
+ add(property, name.getText().replace(/^['"]|['"]$/g, ""));
59
+ }
60
+ return consumers;
61
+ }
62
+ function declarations(sourceFile) {
63
+ const found = [];
64
+ for (const attribute of sourceFile.getDescendantsOfKind(SyntaxKind.JsxAttribute)) {
65
+ const name = attribute.getNameNode();
66
+ if (Node.isIdentifier(name) && name.getText() === "group") found.push(attribute);
67
+ }
68
+ for (const property of sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAssignment)) {
69
+ const name = property.getNameNode();
70
+ if (Node.isComputedPropertyName(name)) continue;
71
+ if (name.getText().replace(/^['"]|['"]$/g, "") === "group") found.push(property);
72
+ }
73
+ return found;
74
+ }
75
+ function provenAncestor(consumer) {
76
+ const owner = consumer.node.getFirstAncestor((node) => Node.isJsxOpeningElement(node) || Node.isJsxSelfClosingElement(node));
77
+ if (owner === void 0) return null;
78
+ for (const ancestor of owner.getAncestors()) {
79
+ if (!Node.isJsxElement(ancestor)) continue;
80
+ const declaration = groupAttribute(ancestor.getOpeningElement());
81
+ if (declaration === null) continue;
82
+ const group = declaredGroup(declaration);
83
+ if (group === null) return "ambiguous";
84
+ if (matches(consumer.group, group)) return { declaration };
85
+ }
86
+ return null;
87
+ }
88
+ function planContainers(sourceFile, registry) {
89
+ const consumers = containerConsumers(sourceFile, registry);
90
+ if (!consumers.length) return emptyPlan;
91
+ const targets = /* @__PURE__ */ new Map();
92
+ const unresolved = /* @__PURE__ */ new Map();
93
+ const all = declarations(sourceFile);
94
+ const target = (declaration, consumer, flag) => {
95
+ const existing = targets.get(declaration);
96
+ if (existing) {
97
+ existing.named ||= consumer.group !== "";
98
+ existing.flag ??= flag;
99
+ return;
100
+ }
101
+ targets.set(declaration, {
102
+ group: declaredGroup(declaration) ?? consumer.group,
103
+ named: consumer.group !== "",
104
+ flag
105
+ });
106
+ };
107
+ for (const consumer of consumers) {
108
+ const label = consumer.group === "" ? "the nearest group" : `group "${consumer.group}"`;
109
+ const proven = provenAncestor(consumer);
110
+ if (proven === "ambiguous") {
111
+ unresolved.set(consumer.node, {
112
+ code: "ambiguous-container-group",
113
+ detail: `a JSX ancestor declares a group name this pass cannot read, so the element that has to declare the container for ${label} is not provable; add "container" by hand`
114
+ });
115
+ continue;
116
+ }
117
+ if (proven !== null) {
118
+ target(proven.declaration, consumer, null);
119
+ continue;
120
+ }
121
+ const candidates = all.filter((declaration) => matches(consumer.group, declaredGroup(declaration)));
122
+ if (candidates.length === 1) {
123
+ target(candidates[0], consumer, {
124
+ code: "unproven-container-group",
125
+ detail: `a legacy container-size condition targets ${label} and this is the only declaration of it in the file, but no JSX ancestry proves it wraps that element; confirm the container belongs here`
126
+ });
127
+ continue;
128
+ }
129
+ unresolved.set(consumer.node, {
130
+ code: candidates.length ? "ambiguous-container-group" : "container-group-not-declared",
131
+ detail: candidates.length ? `${candidates.length} declarations of ${label} are in this file and no JSX ancestry picks one, so the element that has to declare the container is not provable; add "container" by hand` : `${label} is not declared in this file, so the "@\u2026" query this condition becomes has no container to match; add "container" to the element declaring the group`
132
+ });
133
+ }
134
+ return {
135
+ targets,
136
+ unresolved
137
+ };
138
+ }
139
+
140
+ export { planContainers };
141
+ //# sourceMappingURL=containers.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"containers.js","names":[],"sources":["containers.js"],"sourcesContent":["import {\n Node,\n SyntaxKind\n} from \"ts-morph\";\nimport { isLegacyConditionName, resolveLegacyName } from \"./legacyNames\";\nconst emptyPlan = {\n targets: /* @__PURE__ */ new Map(),\n unresolved: /* @__PURE__ */ new Map()\n};\nfunction declaredGroup(declaration) {\n if (Node.isJsxAttribute(declaration)) {\n const initializer2 = declaration.getInitializer();\n if (initializer2 === void 0) return \"\";\n if (Node.isStringLiteral(initializer2)) return initializer2.getLiteralValue();\n if (!Node.isJsxExpression(initializer2)) return null;\n const expression = initializer2.getExpression();\n if (expression === void 0) return null;\n if (Node.isStringLiteral(expression) || Node.isNoSubstitutionTemplateLiteral(expression)) {\n return expression.getLiteralValue();\n }\n return expression.getKind() === SyntaxKind.TrueKeyword ? \"\" : null;\n }\n const initializer = declaration.getInitializer();\n if (initializer === void 0) return null;\n if (Node.isStringLiteral(initializer) || Node.isNoSubstitutionTemplateLiteral(initializer)) {\n return initializer.getLiteralValue();\n }\n return initializer.getKind() === SyntaxKind.TrueKeyword ? \"\" : null;\n}\nfunction groupAttribute(opening) {\n for (const attribute of opening.getAttributes()) {\n if (!Node.isJsxAttribute(attribute)) continue;\n const name = attribute.getNameNode();\n if (Node.isIdentifier(name) && name.getText() === \"group\") return attribute;\n }\n return null;\n}\nfunction matches(consumerGroup, declaration) {\n if (declaration === null) return true;\n return consumerGroup === \"\" || consumerGroup === declaration;\n}\nfunction containerConsumers(sourceFile, registry) {\n const consumers = [];\n const add = (node, name) => {\n if (!isLegacyConditionName(name)) return;\n const resolution = resolveLegacyName(name, registry);\n if (!resolution.ok || resolution.resolved.container === null) return;\n consumers.push({ node, group: resolution.resolved.container.group ?? \"\" });\n };\n for (const attribute of sourceFile.getDescendantsOfKind(SyntaxKind.JsxAttribute)) {\n const name = attribute.getNameNode();\n if (Node.isIdentifier(name)) add(attribute, name.getText());\n }\n for (const property of sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAssignment)) {\n const name = property.getNameNode();\n if (Node.isComputedPropertyName(name)) continue;\n add(property, name.getText().replace(/^['\"]|['\"]$/g, \"\"));\n }\n return consumers;\n}\nfunction declarations(sourceFile) {\n const found = [];\n for (const attribute of sourceFile.getDescendantsOfKind(SyntaxKind.JsxAttribute)) {\n const name = attribute.getNameNode();\n if (Node.isIdentifier(name) && name.getText() === \"group\") found.push(attribute);\n }\n for (const property of sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAssignment)) {\n const name = property.getNameNode();\n if (Node.isComputedPropertyName(name)) continue;\n if (name.getText().replace(/^['\"]|['\"]$/g, \"\") === \"group\") found.push(property);\n }\n return found;\n}\nfunction provenAncestor(consumer) {\n const owner = consumer.node.getFirstAncestor(\n (node) => Node.isJsxOpeningElement(node) || Node.isJsxSelfClosingElement(node)\n );\n if (owner === void 0) return null;\n for (const ancestor of owner.getAncestors()) {\n if (!Node.isJsxElement(ancestor)) continue;\n const declaration = groupAttribute(ancestor.getOpeningElement());\n if (declaration === null) continue;\n const group = declaredGroup(declaration);\n if (group === null) return \"ambiguous\";\n if (matches(consumer.group, group)) return { declaration };\n }\n return null;\n}\nfunction planContainers(sourceFile, registry) {\n const consumers = containerConsumers(sourceFile, registry);\n if (!consumers.length) return emptyPlan;\n const targets = /* @__PURE__ */ new Map();\n const unresolved = /* @__PURE__ */ new Map();\n const all = declarations(sourceFile);\n const target = (declaration, consumer, flag) => {\n const existing = targets.get(declaration);\n if (existing) {\n existing.named ||= consumer.group !== \"\";\n existing.flag ??= flag;\n return;\n }\n targets.set(declaration, {\n group: declaredGroup(declaration) ?? consumer.group,\n named: consumer.group !== \"\",\n flag\n });\n };\n for (const consumer of consumers) {\n const label = consumer.group === \"\" ? \"the nearest group\" : `group \"${consumer.group}\"`;\n const proven = provenAncestor(consumer);\n if (proven === \"ambiguous\") {\n unresolved.set(consumer.node, {\n code: \"ambiguous-container-group\",\n detail: `a JSX ancestor declares a group name this pass cannot read, so the element that has to declare the container for ${label} is not provable; add \"container\" by hand`\n });\n continue;\n }\n if (proven !== null) {\n target(proven.declaration, consumer, null);\n continue;\n }\n const candidates = all.filter(\n (declaration) => matches(consumer.group, declaredGroup(declaration))\n );\n if (candidates.length === 1) {\n target(candidates[0], consumer, {\n code: \"unproven-container-group\",\n detail: `a legacy container-size condition targets ${label} and this is the only declaration of it in the file, but no JSX ancestry proves it wraps that element; confirm the container belongs here`\n });\n continue;\n }\n unresolved.set(consumer.node, {\n code: candidates.length ? \"ambiguous-container-group\" : \"container-group-not-declared\",\n detail: candidates.length ? `${candidates.length} declarations of ${label} are in this file and no JSX ancestry picks one, so the element that has to declare the container is not provable; add \"container\" by hand` : `${label} is not declared in this file, so the \"@\\u2026\" query this condition becomes has no container to match; add \"container\" to the element declaring the group`\n });\n }\n return { targets, unresolved };\n}\nexport {\n planContainers\n};\n//# sourceMappingURL=containers.js.map\n"],"mappings":"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"}