@tamagui/codemod-flat-values 3.0.0-beta.1313.1 → 3.0.0-beta.1341.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
@@ -11,9 +11,28 @@ Run it from the root of the project you are migrating. Paths, the report and the
11
11
  npx @tamagui/codemod-flat-values ./src # dry run
12
12
  npx @tamagui/codemod-flat-values --json report.json ./src # machine readable
13
13
  npx @tamagui/codemod-flat-values --write ./src # apply safe conversions
14
+ npx @tamagui/codemod-flat-values --source-semantics v2-pixels --write ./src
15
+ npx @tamagui/codemod-flat-values --source-semantics v2-pixels --line-height-only --write ./src
14
16
  npx @tamagui/codemod-flat-values --help
15
17
  ```
16
18
 
19
+ The default `--source-semantics v3-ratios` describes current V3 source: numeric
20
+ Tamagui `lineHeight` values are ratios. Select `v2-pixels` when migrating V2
21
+ source. It changes every proven numeric Tamagui line height to an explicit px
22
+ string, so `lineHeight={1.5}` becomes `lineHeight="1.5px"` and a rerun makes no
23
+ further change. Numeric font configuration, raw React Native styles, and Restyle
24
+ styles retain their existing pixel contract and are not rewritten. A local React
25
+ wrapper that renders Tamagui JSX, including an imported local alias, is reported
26
+ as `ambiguous-wrapper-line-height` and never rewritten: the wrapper may transform
27
+ units. Already-migrated strings stay authored.
28
+
29
+ The migration follows Tamagui import and factory provenance through JSX,
30
+ `styled()` configs and variants, `styled.dynamic`, component `style()` and
31
+ `resolve()` calls, inline style objects, and inline style arrays. It reports a
32
+ shared style object rather than changing it when that object may also reach a raw
33
+ native or Restyle host. Untyped dynamic values and extracted token `.val` values
34
+ are also reported because their units cannot be recovered from the expression.
35
+
17
36
  Inside this repository the same two runs over the pinned corpus are
18
37
  `bun run dry-run` and `bun run write` from `code/core/codemod-flat-values`. That
19
38
  corpus is `code/kitchen-sink/src/usecases` plus the canonical `Button.tsx` skin,
@@ -214,6 +233,7 @@ A flag means a human decides. Every code the tool can emit:
214
233
  | `functional-variant-type-bodies` | different type-key bodies cannot be combined into safe `typeof` branches |
215
234
  | `functional-variant-unsupported`, `functional-variant-unsupported-extras`, `functional-variant-styled-import` | the callback, env access, or `styled` import does not have a provable automatic rewrite |
216
235
  | `emitted-program-mismatch`, `emitted-value-invalid` | the printer failed its own re-parse; this is a codemod bug |
236
+ | `ambiguous-wrapper-line-height` | a local React wrapper renders Tamagui JSX, so numeric `lineHeight` / `lh` or an inline style may still be V2 pixels, but the wrapper may transform units |
217
237
  | plus any code from the shared converter | `unsupported-legacy-value`, `legacy-condition-object`, `ambiguous-legacy-group`, `legacy-composite-shorthand` |
218
238
 
219
239
  ## Configuration warning codes
@@ -152,7 +152,7 @@ function runtimeType(expression) {
152
152
  literals.push(String(part.getLiteralValue()));
153
153
  continue;
154
154
  }
155
- if (part.isString()) {
155
+ if (part.isString() || part.isTemplateLiteral()) {
156
156
  strings++;
157
157
  continue;
158
158
  }
@@ -1 +1 @@
1
- {"version":3,"file":"expressions.js","names":[],"sources":["expressions.js"],"sourcesContent":["import { Node, SyntaxKind } from \"ts-morph\";\nimport { flatStringValue } from \"./grammar\";\nfunction unwrapExpression(expression) {\n let current = expression;\n while (Node.isParenthesizedExpression(current) || Node.isAsExpression(current) || Node.isTypeAssertion(current) || Node.isNonNullExpression(current)) {\n current = current.getExpression();\n }\n return current;\n}\nfunction numericValue(expression) {\n const current = unwrapExpression(expression);\n if (Node.isNumericLiteral(current)) return Number(current.getText());\n if (Node.isPrefixUnaryExpression(current)) {\n const operand = current.getOperand();\n if (!Node.isNumericLiteral(operand)) return null;\n const value = Number(operand.getText());\n const operator = current.getOperatorToken();\n if (operator === SyntaxKind.MinusToken) return -value;\n if (operator === SyntaxKind.PlusToken) return value;\n }\n return null;\n}\nfunction staticLeafValue(expression) {\n const current = unwrapExpression(expression);\n if (Node.isStringLiteral(current) || Node.isNoSubstitutionTemplateLiteral(current)) {\n return { found: true, value: current.getLiteralValue() };\n }\n const number = numericValue(current);\n if (number !== null) return { found: true, value: number };\n if (current.getKind() === SyntaxKind.TrueKeyword) return { found: true, value: true };\n if (current.getKind() === SyntaxKind.FalseKeyword) return { found: true, value: false };\n if (current.getKind() === SyntaxKind.NullKeyword) return { found: true, value: null };\n return null;\n}\nfunction literalText(value, registry) {\n if (!value.includes(\"$\")) return { text: value, error: null };\n const flat = flatStringValue(value, registry);\n if (flat.text === null) {\n return {\n text: value,\n error: {\n code: flat.error?.code ?? \"unsupported-legacy-value\",\n message: flat.error?.message ?? `\"${value}\" has no flat spelling`\n }\n };\n }\n return { text: flat.text, error: null };\n}\nfunction templateTree(template, registry) {\n const head = literalText(template.getHead().getLiteralText(), registry);\n let error = head.error;\n let text = `\\`${head.text}`;\n const strings = [template.getHead().getLiteralText()];\n for (const span of template.getTemplateSpans()) {\n const literal = span.getLiteral().getLiteralText();\n const tail = literalText(literal, registry);\n error ??= tail.error;\n strings.push(literal);\n text += `\\${${span.getExpression().getText().trim()}}${tail.text}`;\n }\n return { kind: \"string\", text: `${text}\\``, strings, error };\n}\nfunction literalTree(expression, registry) {\n const current = unwrapExpression(expression);\n if (Node.isStringLiteral(current) || Node.isNoSubstitutionTemplateLiteral(current)) {\n const value = current.getLiteralValue();\n const literal = literalText(value, registry);\n return {\n kind: \"string\",\n text: JSON.stringify(literal.text),\n strings: [value],\n error: literal.error\n };\n }\n if (Node.isTemplateExpression(current)) return templateTree(current, registry);\n const number = numericValue(current);\n if (number !== null) {\n return { kind: \"number\", text: String(number), strings: [], error: null };\n }\n if (current.getKind() === SyntaxKind.UndefinedKeyword || current.getKind() === SyntaxKind.NullKeyword || Node.isIdentifier(current) && current.getText() === \"undefined\") {\n return { kind: \"nullish\", text: current.getText(), strings: [], error: null };\n }\n if (Node.isConditionalExpression(current)) {\n const whenTrue = literalTree(current.getWhenTrue(), registry);\n const whenFalse = literalTree(current.getWhenFalse(), registry);\n if (!whenTrue || !whenFalse) return null;\n if (whenTrue.kind !== whenFalse.kind && whenTrue.kind !== \"nullish\" && whenFalse.kind !== \"nullish\") {\n return null;\n }\n return {\n kind: whenTrue.kind === \"nullish\" ? whenFalse.kind : whenTrue.kind,\n text: `${current.getCondition().getText().trim()} ? ${whenTrue.text} : ${whenFalse.text}`,\n strings: [...whenTrue.strings, ...whenFalse.strings],\n error: whenTrue.error ?? whenFalse.error\n };\n }\n return null;\n}\nfunction runtimeType(expression) {\n const type = unwrapExpression(expression).getType();\n const parts = type.isUnion() ? type.getUnionTypes() : [type];\n const literals = [];\n let numbers = 0;\n let strings = 0;\n let known = 0;\n for (const part of parts) {\n if (part.isUndefined() || part.isNull()) continue;\n known++;\n if (part.isNumber() || part.isNumberLiteral()) {\n numbers++;\n continue;\n }\n if (part.isStringLiteral()) {\n strings++;\n literals.push(String(part.getLiteralValue()));\n continue;\n }\n if (part.isString()) {\n strings++;\n continue;\n }\n return { kind: \"unknown\", literals: null };\n }\n if (!known) return { kind: \"unknown\", literals: null };\n if (numbers === known) return { kind: \"number\", literals: null };\n if (strings === known) {\n return { kind: \"string\", literals: literals.length === known ? literals : null };\n }\n return { kind: \"unknown\", literals: null };\n}\nfunction compact(text) {\n return text.replace(/\\s+/g, \" \").trim();\n}\nexport {\n compact,\n literalTree,\n numericValue,\n runtimeType,\n staticLeafValue,\n unwrapExpression\n};\n//# sourceMappingURL=expressions.js.map\n"],"mappings":";;;;AAEA,SAAS,iBAAiB,YAAY;CACpC,IAAI,UAAU;CACd,OAAO,KAAK,0BAA0B,OAAO,KAAK,KAAK,eAAe,OAAO,KAAK,KAAK,gBAAgB,OAAO,KAAK,KAAK,oBAAoB,OAAO,GAAG;EACpJ,UAAU,QAAQ,cAAc;CAClC;CACA,OAAO;AACT;AACA,SAAS,aAAa,YAAY;CAChC,MAAM,UAAU,iBAAiB,UAAU;CAC3C,IAAI,KAAK,iBAAiB,OAAO,GAAG,OAAO,OAAO,QAAQ,QAAQ,CAAC;CACnE,IAAI,KAAK,wBAAwB,OAAO,GAAG;EACzC,MAAM,UAAU,QAAQ,WAAW;EACnC,IAAI,CAAC,KAAK,iBAAiB,OAAO,GAAG,OAAO;EAC5C,MAAM,QAAQ,OAAO,QAAQ,QAAQ,CAAC;EACtC,MAAM,WAAW,QAAQ,iBAAiB;EAC1C,IAAI,aAAa,WAAW,YAAY,OAAO,CAAC;EAChD,IAAI,aAAa,WAAW,WAAW,OAAO;CAChD;CACA,OAAO;AACT;AACA,SAAS,gBAAgB,YAAY;CACnC,MAAM,UAAU,iBAAiB,UAAU;CAC3C,IAAI,KAAK,gBAAgB,OAAO,KAAK,KAAK,gCAAgC,OAAO,GAAG;EAClF,OAAO;GAAE,OAAO;GAAM,OAAO,QAAQ,gBAAgB;EAAE;CACzD;CACA,MAAM,SAAS,aAAa,OAAO;CACnC,IAAI,WAAW,MAAM,OAAO;EAAE,OAAO;EAAM,OAAO;CAAO;CACzD,IAAI,QAAQ,QAAQ,MAAM,WAAW,aAAa,OAAO;EAAE,OAAO;EAAM,OAAO;CAAK;CACpF,IAAI,QAAQ,QAAQ,MAAM,WAAW,cAAc,OAAO;EAAE,OAAO;EAAM,OAAO;CAAM;CACtF,IAAI,QAAQ,QAAQ,MAAM,WAAW,aAAa,OAAO;EAAE,OAAO;EAAM,OAAO;CAAK;CACpF,OAAO;AACT;AACA,SAAS,YAAY,OAAO,UAAU;CACpC,IAAI,CAAC,MAAM,SAAS,GAAG,GAAG,OAAO;EAAE,MAAM;EAAO,OAAO;CAAK;CAC5D,MAAM,OAAO,gBAAgB,OAAO,QAAQ;CAC5C,IAAI,KAAK,SAAS,MAAM;EACtB,OAAO;GACL,MAAM;GACN,OAAO;IACL,MAAM,KAAK,OAAO,QAAQ;IAC1B,SAAS,KAAK,OAAO,WAAW,IAAI,MAAM;GAC5C;EACF;CACF;CACA,OAAO;EAAE,MAAM,KAAK;EAAM,OAAO;CAAK;AACxC;AACA,SAAS,aAAa,UAAU,UAAU;CACxC,MAAM,OAAO,YAAY,SAAS,QAAQ,CAAC,CAAC,eAAe,GAAG,QAAQ;CACtE,IAAI,QAAQ,KAAK;CACjB,IAAI,OAAO,KAAK,KAAK;CACrB,MAAM,UAAU,CAAC,SAAS,QAAQ,CAAC,CAAC,eAAe,CAAC;CACpD,KAAK,MAAM,QAAQ,SAAS,iBAAiB,GAAG;EAC9C,MAAM,UAAU,KAAK,WAAW,CAAC,CAAC,eAAe;EACjD,MAAM,OAAO,YAAY,SAAS,QAAQ;EAC1C,UAAU,KAAK;EACf,QAAQ,KAAK,OAAO;EACpB,QAAQ,MAAM,KAAK,cAAc,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK;CAC9D;CACA,OAAO;EAAE,MAAM;EAAU,MAAM,GAAG,KAAK;EAAK;EAAS;CAAM;AAC7D;AACA,SAAS,YAAY,YAAY,UAAU;CACzC,MAAM,UAAU,iBAAiB,UAAU;CAC3C,IAAI,KAAK,gBAAgB,OAAO,KAAK,KAAK,gCAAgC,OAAO,GAAG;EAClF,MAAM,QAAQ,QAAQ,gBAAgB;EACtC,MAAM,UAAU,YAAY,OAAO,QAAQ;EAC3C,OAAO;GACL,MAAM;GACN,MAAM,KAAK,UAAU,QAAQ,IAAI;GACjC,SAAS,CAAC,KAAK;GACf,OAAO,QAAQ;EACjB;CACF;CACA,IAAI,KAAK,qBAAqB,OAAO,GAAG,OAAO,aAAa,SAAS,QAAQ;CAC7E,MAAM,SAAS,aAAa,OAAO;CACnC,IAAI,WAAW,MAAM;EACnB,OAAO;GAAE,MAAM;GAAU,MAAM,OAAO,MAAM;GAAG,SAAS,CAAC;GAAG,OAAO;EAAK;CAC1E;CACA,IAAI,QAAQ,QAAQ,MAAM,WAAW,oBAAoB,QAAQ,QAAQ,MAAM,WAAW,eAAe,KAAK,aAAa,OAAO,KAAK,QAAQ,QAAQ,MAAM,aAAa;EACxK,OAAO;GAAE,MAAM;GAAW,MAAM,QAAQ,QAAQ;GAAG,SAAS,CAAC;GAAG,OAAO;EAAK;CAC9E;CACA,IAAI,KAAK,wBAAwB,OAAO,GAAG;EACzC,MAAM,WAAW,YAAY,QAAQ,YAAY,GAAG,QAAQ;EAC5D,MAAM,YAAY,YAAY,QAAQ,aAAa,GAAG,QAAQ;EAC9D,IAAI,CAAC,YAAY,CAAC,WAAW,OAAO;EACpC,IAAI,SAAS,SAAS,UAAU,QAAQ,SAAS,SAAS,aAAa,UAAU,SAAS,WAAW;GACnG,OAAO;EACT;EACA,OAAO;GACL,MAAM,SAAS,SAAS,YAAY,UAAU,OAAO,SAAS;GAC9D,MAAM,GAAG,QAAQ,aAAa,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,KAAK,SAAS,KAAK,KAAK,UAAU;GACnF,SAAS,CAAC,GAAG,SAAS,SAAS,GAAG,UAAU,OAAO;GACnD,OAAO,SAAS,SAAS,UAAU;EACrC;CACF;CACA,OAAO;AACT;AACA,SAAS,YAAY,YAAY;CAC/B,MAAM,OAAO,iBAAiB,UAAU,CAAC,CAAC,QAAQ;CAClD,MAAM,QAAQ,KAAK,QAAQ,IAAI,KAAK,cAAc,IAAI,CAAC,IAAI;CAC3D,MAAM,WAAW,CAAC;CAClB,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI,QAAQ;CACZ,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,KAAK,YAAY,KAAK,KAAK,OAAO,GAAG;EACzC;EACA,IAAI,KAAK,SAAS,KAAK,KAAK,gBAAgB,GAAG;GAC7C;GACA;EACF;EACA,IAAI,KAAK,gBAAgB,GAAG;GAC1B;GACA,SAAS,KAAK,OAAO,KAAK,gBAAgB,CAAC,CAAC;GAC5C;EACF;EACA,IAAI,KAAK,SAAS,GAAG;GACnB;GACA;EACF;EACA,OAAO;GAAE,MAAM;GAAW,UAAU;EAAK;CAC3C;CACA,IAAI,CAAC,OAAO,OAAO;EAAE,MAAM;EAAW,UAAU;CAAK;CACrD,IAAI,YAAY,OAAO,OAAO;EAAE,MAAM;EAAU,UAAU;CAAK;CAC/D,IAAI,YAAY,OAAO;EACrB,OAAO;GAAE,MAAM;GAAU,UAAU,SAAS,WAAW,QAAQ,WAAW;EAAK;CACjF;CACA,OAAO;EAAE,MAAM;EAAW,UAAU;CAAK;AAC3C;AACA,SAAS,QAAQ,MAAM;CACrB,OAAO,KAAK,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;AACxC"}
1
+ {"version":3,"file":"expressions.js","names":[],"sources":["expressions.js"],"sourcesContent":["import { Node, SyntaxKind } from \"ts-morph\";\nimport { flatStringValue } from \"./grammar\";\nfunction unwrapExpression(expression) {\n let current = expression;\n while (Node.isParenthesizedExpression(current) || Node.isAsExpression(current) || Node.isTypeAssertion(current) || Node.isNonNullExpression(current)) {\n current = current.getExpression();\n }\n return current;\n}\nfunction numericValue(expression) {\n const current = unwrapExpression(expression);\n if (Node.isNumericLiteral(current)) return Number(current.getText());\n if (Node.isPrefixUnaryExpression(current)) {\n const operand = current.getOperand();\n if (!Node.isNumericLiteral(operand)) return null;\n const value = Number(operand.getText());\n const operator = current.getOperatorToken();\n if (operator === SyntaxKind.MinusToken) return -value;\n if (operator === SyntaxKind.PlusToken) return value;\n }\n return null;\n}\nfunction staticLeafValue(expression) {\n const current = unwrapExpression(expression);\n if (Node.isStringLiteral(current) || Node.isNoSubstitutionTemplateLiteral(current)) {\n return { found: true, value: current.getLiteralValue() };\n }\n const number = numericValue(current);\n if (number !== null) return { found: true, value: number };\n if (current.getKind() === SyntaxKind.TrueKeyword) return { found: true, value: true };\n if (current.getKind() === SyntaxKind.FalseKeyword) return { found: true, value: false };\n if (current.getKind() === SyntaxKind.NullKeyword) return { found: true, value: null };\n return null;\n}\nfunction literalText(value, registry) {\n if (!value.includes(\"$\")) return { text: value, error: null };\n const flat = flatStringValue(value, registry);\n if (flat.text === null) {\n return {\n text: value,\n error: {\n code: flat.error?.code ?? \"unsupported-legacy-value\",\n message: flat.error?.message ?? `\"${value}\" has no flat spelling`\n }\n };\n }\n return { text: flat.text, error: null };\n}\nfunction templateTree(template, registry) {\n const head = literalText(template.getHead().getLiteralText(), registry);\n let error = head.error;\n let text = `\\`${head.text}`;\n const strings = [template.getHead().getLiteralText()];\n for (const span of template.getTemplateSpans()) {\n const literal = span.getLiteral().getLiteralText();\n const tail = literalText(literal, registry);\n error ??= tail.error;\n strings.push(literal);\n text += `\\${${span.getExpression().getText().trim()}}${tail.text}`;\n }\n return { kind: \"string\", text: `${text}\\``, strings, error };\n}\nfunction literalTree(expression, registry) {\n const current = unwrapExpression(expression);\n if (Node.isStringLiteral(current) || Node.isNoSubstitutionTemplateLiteral(current)) {\n const value = current.getLiteralValue();\n const literal = literalText(value, registry);\n return {\n kind: \"string\",\n text: JSON.stringify(literal.text),\n strings: [value],\n error: literal.error\n };\n }\n if (Node.isTemplateExpression(current)) return templateTree(current, registry);\n const number = numericValue(current);\n if (number !== null) {\n return { kind: \"number\", text: String(number), strings: [], error: null };\n }\n if (current.getKind() === SyntaxKind.UndefinedKeyword || current.getKind() === SyntaxKind.NullKeyword || Node.isIdentifier(current) && current.getText() === \"undefined\") {\n return { kind: \"nullish\", text: current.getText(), strings: [], error: null };\n }\n if (Node.isConditionalExpression(current)) {\n const whenTrue = literalTree(current.getWhenTrue(), registry);\n const whenFalse = literalTree(current.getWhenFalse(), registry);\n if (!whenTrue || !whenFalse) return null;\n if (whenTrue.kind !== whenFalse.kind && whenTrue.kind !== \"nullish\" && whenFalse.kind !== \"nullish\") {\n return null;\n }\n return {\n kind: whenTrue.kind === \"nullish\" ? whenFalse.kind : whenTrue.kind,\n text: `${current.getCondition().getText().trim()} ? ${whenTrue.text} : ${whenFalse.text}`,\n strings: [...whenTrue.strings, ...whenFalse.strings],\n error: whenTrue.error ?? whenFalse.error\n };\n }\n return null;\n}\nfunction runtimeType(expression) {\n const type = unwrapExpression(expression).getType();\n const parts = type.isUnion() ? type.getUnionTypes() : [type];\n const literals = [];\n let numbers = 0;\n let strings = 0;\n let known = 0;\n for (const part of parts) {\n if (part.isUndefined() || part.isNull()) continue;\n known++;\n if (part.isNumber() || part.isNumberLiteral()) {\n numbers++;\n continue;\n }\n if (part.isStringLiteral()) {\n strings++;\n literals.push(String(part.getLiteralValue()));\n continue;\n }\n if (part.isString() || part.isTemplateLiteral()) {\n strings++;\n continue;\n }\n return { kind: \"unknown\", literals: null };\n }\n if (!known) return { kind: \"unknown\", literals: null };\n if (numbers === known) return { kind: \"number\", literals: null };\n if (strings === known) {\n return { kind: \"string\", literals: literals.length === known ? literals : null };\n }\n return { kind: \"unknown\", literals: null };\n}\nfunction compact(text) {\n return text.replace(/\\s+/g, \" \").trim();\n}\nexport {\n compact,\n literalTree,\n numericValue,\n runtimeType,\n staticLeafValue,\n unwrapExpression\n};\n//# sourceMappingURL=expressions.js.map\n"],"mappings":"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"}
package/dist/grammar.mjs CHANGED
@@ -6,18 +6,21 @@ import { convertLegacyConditionProp as convertLegacyConditionProp$1, pseudoToMod
6
6
 
7
7
  const grammar = styleGrammarTooling;
8
8
  const { assessFlatConversion, createModifierRegistry, defaultMediaKeys, evaluateProgram, expandToLonghands, grammarEntries, grammarPlatformNames, legacyPartComposite, mergeProgramValues, parseValue, programEligibility, standaloneValueProps, parseTransformString } = grammar;
9
- function renameBuiltInTokens(value) {
9
+ function normalizeV3Payload(value, prop = "") {
10
+ if (typeof value === "number" && Number.isFinite(value) && (prop === "lineHeight" || prop === "lh")) {
11
+ return String(value);
12
+ }
10
13
  if (typeof value === "string") return replaceV6BuiltInTokens(value);
11
- if (Array.isArray(value)) return value.map(renameBuiltInTokens);
14
+ if (Array.isArray(value)) return value.map((item) => normalizeV3Payload(item, prop));
12
15
  if (value === null || typeof value !== "object") return value;
13
16
  const renamed = {};
14
17
  for (const key in value) {
15
- renamed[key] = renameBuiltInTokens(value[key]);
18
+ renamed[key] = normalizeV3Payload(value[key], key);
16
19
  }
17
20
  return renamed;
18
21
  }
19
22
  function convertLegacyConditionProp(propName, value, options) {
20
- return convertLegacyConditionProp$1(propName, renameBuiltInTokens(value), options);
23
+ return convertLegacyConditionProp$1(propName, normalizeV3Payload(value), options);
21
24
  }
22
25
  const styleProps = /* @__PURE__ */ new Set([
23
26
  ...grammarEntries.map((entry) => entry.prop),
@@ -1 +1 @@
1
- {"version":3,"file":"grammar.js","names":["convertLegacyConditionPropLocal"],"sources":["grammar.js"],"sourcesContent":["import { stylePropsAll } from \"@tamagui/helpers\";\nimport { shorthands } from \"@tamagui/shorthands/v6\";\nimport * as styleGrammarTooling from \"@tamagui/style-grammar/tooling\";\nimport { replaceV6BuiltInTokens } from \"./builtInNames\";\nimport {\n convertLegacyConditionProp as convertLegacyConditionPropLocal,\n pseudoToModifier\n} from \"./legacyConditions\";\nconst grammar = styleGrammarTooling;\nconst {\n assessFlatConversion,\n createModifierRegistry,\n defaultMediaKeys,\n evaluateProgram,\n expandToLonghands,\n grammarEntries,\n grammarPlatformNames,\n legacyPartComposite,\n mergeProgramValues,\n parseValue,\n programEligibility,\n standaloneValueProps,\n parseTransformString\n} = grammar;\nfunction renameBuiltInTokens(value) {\n if (typeof value === \"string\") return replaceV6BuiltInTokens(value);\n if (Array.isArray(value)) return value.map(renameBuiltInTokens);\n if (value === null || typeof value !== \"object\") return value;\n const renamed = {};\n for (const key in value) {\n renamed[key] = renameBuiltInTokens(value[key]);\n }\n return renamed;\n}\nfunction convertLegacyConditionProp(propName, value, options) {\n return convertLegacyConditionPropLocal(propName, renameBuiltInTokens(value), options);\n}\nconst styleProps = /* @__PURE__ */ new Set([\n ...grammarEntries.map((entry) => entry.prop),\n ...Object.keys(standaloneValueProps),\n ...Object.keys(stylePropsAll),\n ...Object.keys(shorthands),\n ...Object.values(shorthands)\n]);\nconst tokenVariantProps = /* @__PURE__ */ new Set([\n \"size\",\n \"elevation\",\n \"iconSize\"\n]);\nconst codemodMediaNames = [\n ...defaultMediaKeys,\n \"motionReduce\",\n \"motionSafe\"\n];\nfunction resolveProp(prop) {\n return shorthands[prop] ?? prop;\n}\nconst payloadProbeCondition = \"$platform-web\";\nfunction sharedPayload(prop, value, registry) {\n const converted = convertLegacyConditionProp(\n payloadProbeCondition,\n { [prop]: value },\n { registry }\n );\n if (converted === null) {\n throw new Error(\n `the payload probe condition \"${payloadProbeCondition}\" is unregistered`\n );\n }\n return {\n payload: converted.contributions[0]?.clause.payload ?? null,\n errors: converted.errors\n };\n}\nconst stringProbeProp = \"color\";\nfunction flatStringValue(value, registry) {\n const probe = sharedPayload(stringProbeProp, value, registry);\n return { text: probe.payload, error: probe.errors[0] ?? null };\n}\nconst unitSuffixes = /* @__PURE__ */ new Map();\nfunction unitSuffix(prop, registry) {\n const cached = unitSuffixes.get(prop);\n if (cached !== void 0) return cached;\n const probe = sharedPayload(prop, 1, registry);\n const suffix = probe.payload !== null && probe.payload.startsWith(\"1\") ? probe.payload.slice(1) : \"\";\n unitSuffixes.set(prop, suffix);\n return suffix;\n}\nconst printProgram = grammar.formatParsedValue;\nexport {\n assessFlatConversion,\n codemodMediaNames,\n convertLegacyConditionProp,\n createModifierRegistry,\n defaultMediaKeys,\n evaluateProgram,\n expandToLonghands,\n flatStringValue,\n grammarEntries,\n grammarPlatformNames,\n legacyPartComposite,\n mergeProgramValues,\n parseTransformString,\n parseValue,\n printProgram,\n programEligibility,\n pseudoToModifier,\n resolveProp,\n sharedPayload,\n shorthands,\n standaloneValueProps,\n styleProps,\n tokenVariantProps,\n unitSuffix\n};\n//# sourceMappingURL=grammar.js.map\n"],"mappings":";;;;;;;AAQA,MAAM,UAAU;AAChB,MAAM,EACJ,sBACA,wBACA,kBACA,iBACA,mBACA,gBACA,sBACA,qBACA,oBACA,YACA,oBACA,sBACA,yBACE;AACJ,SAAS,oBAAoB,OAAO;CAClC,IAAI,OAAO,UAAU,UAAU,OAAO,uBAAuB,KAAK;CAClE,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,IAAI,mBAAmB;CAC9D,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,MAAM,UAAU,CAAC;CACjB,KAAK,MAAM,OAAO,OAAO;EACvB,QAAQ,OAAO,oBAAoB,MAAM,IAAI;CAC/C;CACA,OAAO;AACT;AACA,SAAS,2BAA2B,UAAU,OAAO,SAAS;CAC5D,OAAOA,6BAAgC,UAAU,oBAAoB,KAAK,GAAG,OAAO;AACtF;AACA,MAAM,6BAA6B,IAAI,IAAI;CACzC,GAAG,eAAe,KAAK,UAAU,MAAM,IAAI;CAC3C,GAAG,OAAO,KAAK,oBAAoB;CACnC,GAAG,OAAO,KAAK,aAAa;CAC5B,GAAG,OAAO,KAAK,UAAU;CACzB,GAAG,OAAO,OAAO,UAAU;AAC7B,CAAC;AACD,MAAM,oCAAoC,IAAI,IAAI;CAChD;CACA;CACA;AACF,CAAC;AACD,MAAM,oBAAoB;CACxB,GAAG;CACH;CACA;AACF;AACA,SAAS,YAAY,MAAM;CACzB,OAAO,WAAW,SAAS;AAC7B;AACA,MAAM,wBAAwB;AAC9B,SAAS,cAAc,MAAM,OAAO,UAAU;CAC5C,MAAM,YAAY,2BAChB,uBACA,GAAG,OAAO,MAAM,GAChB,EAAE,SAAS,CACb;CACA,IAAI,cAAc,MAAM;EACtB,MAAM,IAAI,MACR,gCAAgC,sBAAsB,kBACxD;CACF;CACA,OAAO;EACL,SAAS,UAAU,cAAc,EAAE,EAAE,OAAO,WAAW;EACvD,QAAQ,UAAU;CACpB;AACF;AACA,MAAM,kBAAkB;AACxB,SAAS,gBAAgB,OAAO,UAAU;CACxC,MAAM,QAAQ,cAAc,iBAAiB,OAAO,QAAQ;CAC5D,OAAO;EAAE,MAAM,MAAM;EAAS,OAAO,MAAM,OAAO,MAAM;CAAK;AAC/D;AACA,MAAM,+BAA+B,IAAI,IAAI;AAC7C,SAAS,WAAW,MAAM,UAAU;CAClC,MAAM,SAAS,aAAa,IAAI,IAAI;CACpC,IAAI,WAAW,KAAK,GAAG,OAAO;CAC9B,MAAM,QAAQ,cAAc,MAAM,GAAG,QAAQ;CAC7C,MAAM,SAAS,MAAM,YAAY,QAAQ,MAAM,QAAQ,WAAW,GAAG,IAAI,MAAM,QAAQ,MAAM,CAAC,IAAI;CAClG,aAAa,IAAI,MAAM,MAAM;CAC7B,OAAO;AACT;AACA,MAAM,eAAe,QAAQ"}
1
+ {"version":3,"file":"grammar.js","names":["convertLegacyConditionPropLocal"],"sources":["grammar.js"],"sourcesContent":["import { stylePropsAll } from \"@tamagui/helpers\";\nimport { shorthands } from \"@tamagui/shorthands/v6\";\nimport * as styleGrammarTooling from \"@tamagui/style-grammar/tooling\";\nimport { replaceV6BuiltInTokens } from \"./builtInNames\";\nimport {\n convertLegacyConditionProp as convertLegacyConditionPropLocal,\n pseudoToModifier\n} from \"./legacyConditions\";\nconst grammar = styleGrammarTooling;\nconst {\n assessFlatConversion,\n createModifierRegistry,\n defaultMediaKeys,\n evaluateProgram,\n expandToLonghands,\n grammarEntries,\n grammarPlatformNames,\n legacyPartComposite,\n mergeProgramValues,\n parseValue,\n programEligibility,\n standaloneValueProps,\n parseTransformString\n} = grammar;\nfunction normalizeV3Payload(value, prop = \"\") {\n if (typeof value === \"number\" && Number.isFinite(value) && (prop === \"lineHeight\" || prop === \"lh\")) {\n return String(value);\n }\n if (typeof value === \"string\") return replaceV6BuiltInTokens(value);\n if (Array.isArray(value)) return value.map((item) => normalizeV3Payload(item, prop));\n if (value === null || typeof value !== \"object\") return value;\n const renamed = {};\n for (const key in value) {\n renamed[key] = normalizeV3Payload(value[key], key);\n }\n return renamed;\n}\nfunction convertLegacyConditionProp(propName, value, options) {\n return convertLegacyConditionPropLocal(propName, normalizeV3Payload(value), options);\n}\nconst styleProps = /* @__PURE__ */ new Set([\n ...grammarEntries.map((entry) => entry.prop),\n ...Object.keys(standaloneValueProps),\n ...Object.keys(stylePropsAll),\n ...Object.keys(shorthands),\n ...Object.values(shorthands)\n]);\nconst tokenVariantProps = /* @__PURE__ */ new Set([\n \"size\",\n \"elevation\",\n \"iconSize\"\n]);\nconst codemodMediaNames = [\n ...defaultMediaKeys,\n \"motionReduce\",\n \"motionSafe\"\n];\nfunction resolveProp(prop) {\n return shorthands[prop] ?? prop;\n}\nconst payloadProbeCondition = \"$platform-web\";\nfunction sharedPayload(prop, value, registry) {\n const converted = convertLegacyConditionProp(\n payloadProbeCondition,\n { [prop]: value },\n { registry }\n );\n if (converted === null) {\n throw new Error(\n `the payload probe condition \"${payloadProbeCondition}\" is unregistered`\n );\n }\n return {\n payload: converted.contributions[0]?.clause.payload ?? null,\n errors: converted.errors\n };\n}\nconst stringProbeProp = \"color\";\nfunction flatStringValue(value, registry) {\n const probe = sharedPayload(stringProbeProp, value, registry);\n return { text: probe.payload, error: probe.errors[0] ?? null };\n}\nconst unitSuffixes = /* @__PURE__ */ new Map();\nfunction unitSuffix(prop, registry) {\n const cached = unitSuffixes.get(prop);\n if (cached !== void 0) return cached;\n const probe = sharedPayload(prop, 1, registry);\n const suffix = probe.payload !== null && probe.payload.startsWith(\"1\") ? probe.payload.slice(1) : \"\";\n unitSuffixes.set(prop, suffix);\n return suffix;\n}\nconst printProgram = grammar.formatParsedValue;\nexport {\n assessFlatConversion,\n codemodMediaNames,\n convertLegacyConditionProp,\n createModifierRegistry,\n defaultMediaKeys,\n evaluateProgram,\n expandToLonghands,\n flatStringValue,\n grammarEntries,\n grammarPlatformNames,\n legacyPartComposite,\n mergeProgramValues,\n parseTransformString,\n parseValue,\n printProgram,\n programEligibility,\n pseudoToModifier,\n resolveProp,\n sharedPayload,\n shorthands,\n standaloneValueProps,\n styleProps,\n tokenVariantProps,\n unitSuffix\n};\n//# sourceMappingURL=grammar.js.map\n"],"mappings":";;;;;;;AAQA,MAAM,UAAU;AAChB,MAAM,EACJ,sBACA,wBACA,kBACA,iBACA,mBACA,gBACA,sBACA,qBACA,oBACA,YACA,oBACA,sBACA,yBACE;AACJ,SAAS,mBAAmB,OAAO,OAAO,IAAI;CAC5C,IAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,MAAM,SAAS,gBAAgB,SAAS,OAAO;EACnG,OAAO,OAAO,KAAK;CACrB;CACA,IAAI,OAAO,UAAU,UAAU,OAAO,uBAAuB,KAAK;CAClE,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,KAAK,SAAS,mBAAmB,MAAM,IAAI,CAAC;CACnF,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,MAAM,UAAU,CAAC;CACjB,KAAK,MAAM,OAAO,OAAO;EACvB,QAAQ,OAAO,mBAAmB,MAAM,MAAM,GAAG;CACnD;CACA,OAAO;AACT;AACA,SAAS,2BAA2B,UAAU,OAAO,SAAS;CAC5D,OAAOA,6BAAgC,UAAU,mBAAmB,KAAK,GAAG,OAAO;AACrF;AACA,MAAM,6BAA6B,IAAI,IAAI;CACzC,GAAG,eAAe,KAAK,UAAU,MAAM,IAAI;CAC3C,GAAG,OAAO,KAAK,oBAAoB;CACnC,GAAG,OAAO,KAAK,aAAa;CAC5B,GAAG,OAAO,KAAK,UAAU;CACzB,GAAG,OAAO,OAAO,UAAU;AAC7B,CAAC;AACD,MAAM,oCAAoC,IAAI,IAAI;CAChD;CACA;CACA;AACF,CAAC;AACD,MAAM,oBAAoB;CACxB,GAAG;CACH;CACA;AACF;AACA,SAAS,YAAY,MAAM;CACzB,OAAO,WAAW,SAAS;AAC7B;AACA,MAAM,wBAAwB;AAC9B,SAAS,cAAc,MAAM,OAAO,UAAU;CAC5C,MAAM,YAAY,2BAChB,uBACA,GAAG,OAAO,MAAM,GAChB,EAAE,SAAS,CACb;CACA,IAAI,cAAc,MAAM;EACtB,MAAM,IAAI,MACR,gCAAgC,sBAAsB,kBACxD;CACF;CACA,OAAO;EACL,SAAS,UAAU,cAAc,EAAE,EAAE,OAAO,WAAW;EACvD,QAAQ,UAAU;CACpB;AACF;AACA,MAAM,kBAAkB;AACxB,SAAS,gBAAgB,OAAO,UAAU;CACxC,MAAM,QAAQ,cAAc,iBAAiB,OAAO,QAAQ;CAC5D,OAAO;EAAE,MAAM,MAAM;EAAS,OAAO,MAAM,OAAO,MAAM;CAAK;AAC/D;AACA,MAAM,+BAA+B,IAAI,IAAI;AAC7C,SAAS,WAAW,MAAM,UAAU;CAClC,MAAM,SAAS,aAAa,IAAI,IAAI;CACpC,IAAI,WAAW,KAAK,GAAG,OAAO;CAC9B,MAAM,QAAQ,cAAc,MAAM,GAAG,QAAQ;CAC7C,MAAM,SAAS,MAAM,YAAY,QAAQ,MAAM,QAAQ,WAAW,GAAG,IAAI,MAAM,QAAQ,MAAM,CAAC,IAAI;CAClG,aAAa,IAAI,MAAM,MAAM;CAC7B,OAAO;AACT;AACA,MAAM,eAAe,QAAQ"}
package/dist/index.mjs CHANGED
@@ -5,14 +5,294 @@ import { stylePropsTextOnly } from "@tamagui/helpers";
5
5
  import { IndentationText, ModuleKind, ModuleResolutionKind, Node, Project, ScriptTarget, SyntaxKind, ts } from "ts-morph";
6
6
  import { planContainers } from "./containers.mjs";
7
7
  import { convertJsxSite, convertStyleObject } from "./convert.mjs";
8
- import { compact, unwrapExpression } from "./expressions.mjs";
8
+ import { compact, numericValue, runtimeType, unwrapExpression } from "./expressions.mjs";
9
9
  import { addFunctionalVariantTypeImports, convertFunctionalVariants } from "./functionalVariants.mjs";
10
10
  import { codemodMediaNames, createModifierRegistry, grammarPlatformNames, shorthands } from "./grammar.mjs";
11
11
  import { createProvenance } from "./provenance.mjs";
12
12
  import { renderReport } from "./report.mjs";
13
13
  import { convertSheetFrames } from "./sheetAnatomy.mjs";
14
14
  import { convertTransitions } from "./transition.mjs";
15
+ import { isLegacyConditionName } from "./legacyNames.mjs";
15
16
 
17
+ function propertyName(node) {
18
+ if (Node.isIdentifier(node) || Node.isStringLiteral(node) || Node.isNumericLiteral(node)) {
19
+ return node.getText().replace(/^['"]|['"]$/g, "");
20
+ }
21
+ return null;
22
+ }
23
+ function isLineHeightName(name) {
24
+ return name === "lineHeight" || name === "lh";
25
+ }
26
+ function extractedTokenValue(expression) {
27
+ const nodes = [expression, ...expression.getDescendants()];
28
+ return nodes.some((node) => Node.isPropertyAccessExpression(node) && node.getName() === "val" || Node.isCallExpression(node) && Node.isIdentifier(node.getExpression()) && node.getExpression().getText() === "getVariableValue");
29
+ }
30
+ function pixelReplacement(expression) {
31
+ const current = unwrapExpression(expression);
32
+ const number = numericValue(current);
33
+ if (number !== null) return {
34
+ text: JSON.stringify(`${number}px`),
35
+ dynamic: false
36
+ };
37
+ const type = runtimeType(current);
38
+ if (type.kind === "string") return null;
39
+ if (extractedTokenValue(current)) {
40
+ return { flag: {
41
+ code: "ambiguous-line-height-token-value",
42
+ detail: `lineHeight value "${compact(current.getText())}" extracts a token value whose units are no longer visible; replace it with an explicit ratio or px length by hand`
43
+ } };
44
+ }
45
+ if (type.kind === "number") {
46
+ return {
47
+ text: `\`\${${current.getText()}}px\``,
48
+ dynamic: true
49
+ };
50
+ }
51
+ return { flag: {
52
+ code: "ambiguous-line-height-expression",
53
+ detail: `lineHeight value "${compact(current.getText())}" has no provable numeric type; replace old pixel semantics with an explicit px length by hand`
54
+ } };
55
+ }
56
+ function declarationContainsNumericLineHeight(expression) {
57
+ const symbol = expression.getSymbol();
58
+ if (!symbol) return false;
59
+ for (const declaration of symbol.getDeclarations()) {
60
+ for (const property of [...Node.isPropertyAssignment(declaration) ? [declaration] : [], ...declaration.getDescendantsOfKind(SyntaxKind.PropertyAssignment)]) {
61
+ if (!isLineHeightName(propertyName(property.getNameNode()))) continue;
62
+ const initializer = property.getInitializer();
63
+ if (!initializer) continue;
64
+ const value = unwrapExpression(initializer);
65
+ if (numericValue(value) !== null || runtimeType(value).kind === "number") return true;
66
+ }
67
+ }
68
+ return false;
69
+ }
70
+ function lineHeightValueIsUnmigrated(expression) {
71
+ const current = unwrapExpression(expression);
72
+ if (Node.isObjectLiteralExpression(current)) {
73
+ return current.getProperties().some((property) => {
74
+ if (!Node.isPropertyAssignment(property)) return false;
75
+ const initializer = property.getInitializer();
76
+ return initializer !== void 0 && lineHeightValueIsUnmigrated(initializer);
77
+ });
78
+ }
79
+ if (Node.isConditionalExpression(current)) {
80
+ return lineHeightValueIsUnmigrated(current.getWhenTrue()) || lineHeightValueIsUnmigrated(current.getWhenFalse());
81
+ }
82
+ return pixelReplacement(current) !== null;
83
+ }
84
+ function replaceWithin(source, sourceStart, edits) {
85
+ let output = source;
86
+ for (const edit of [...edits].sort((left, right) => right.start - left.start)) {
87
+ output = output.slice(0, edit.start - sourceStart) + edit.text + output.slice(edit.end - sourceStart);
88
+ }
89
+ return output;
90
+ }
91
+ function migrateLegacyLineHeights(sourceFile, provenance2) {
92
+ const edits = [];
93
+ const claimed = /* @__PURE__ */ new Set();
94
+ const reports = [];
95
+ const scanLineHeightExpression = (expression, contextEdits, flags) => {
96
+ const current = unwrapExpression(expression);
97
+ if (Node.isObjectLiteralExpression(current)) {
98
+ for (const property of current.getProperties()) {
99
+ if (!Node.isPropertyAssignment(property)) continue;
100
+ const initializer = property.getInitializer();
101
+ if (initializer) scanLineHeightExpression(initializer, contextEdits, flags);
102
+ }
103
+ return;
104
+ }
105
+ if (Node.isConditionalExpression(current)) {
106
+ scanLineHeightExpression(current.getWhenTrue(), contextEdits, flags);
107
+ scanLineHeightExpression(current.getWhenFalse(), contextEdits, flags);
108
+ return;
109
+ }
110
+ if (claimed.has(current.getStart())) return;
111
+ const replacement = pixelReplacement(current);
112
+ if (!replacement) return;
113
+ claimed.add(current.getStart());
114
+ if ("flag" in replacement) {
115
+ flags.push(replacement.flag);
116
+ return;
117
+ }
118
+ const edit = {
119
+ start: current.getStart(),
120
+ end: current.getEnd(),
121
+ text: replacement.text,
122
+ dynamic: replacement.dynamic
123
+ };
124
+ edits.push(edit);
125
+ contextEdits.push(edit);
126
+ };
127
+ const scanProperty = (property, contextEdits, flags) => {
128
+ if (!isLineHeightName(propertyName(property.getNameNode()))) return;
129
+ const initializer = property.getInitializer();
130
+ if (initializer) scanLineHeightExpression(initializer, contextEdits, flags);
131
+ };
132
+ const scanObject = (object, contextEdits, flags) => {
133
+ for (const property of object.getDescendantsOfKind(SyntaxKind.PropertyAssignment)) {
134
+ scanProperty(property, contextEdits, flags);
135
+ }
136
+ for (const property of object.getProperties()) {
137
+ if (Node.isPropertyAssignment(property)) scanProperty(property, contextEdits, flags);
138
+ }
139
+ };
140
+ const addReport = (node, kind, label, contextEdits, flags) => {
141
+ if (!contextEdits.length && !flags.length) return;
142
+ const before = compact(node.getText());
143
+ reports.push({
144
+ kind,
145
+ label,
146
+ line: sourceFile.getLineAndColumnAtPos(node.getStart()).line,
147
+ before,
148
+ after: compact(replaceWithin(node.getText(), node.getStart(), contextEdits)),
149
+ programs: contextEdits.map((edit) => ({
150
+ name: "lineHeight",
151
+ value: edit.text,
152
+ dynamic: edit.dynamic
153
+ })),
154
+ assessments: [],
155
+ assessmentVerdict: "clean",
156
+ warnings: [],
157
+ flags,
158
+ inventory: [],
159
+ pending: [],
160
+ notes: [contextEdits.length ? "numeric lineHeight used V2 pixel semantics and now carries an explicit px unit" : "lineHeight was left authored for a manual unit decision"],
161
+ legacyLeft: flags.length
162
+ });
163
+ };
164
+ const scanJsx = (opening) => {
165
+ const contextEdits = [];
166
+ const flags = [];
167
+ for (const attribute of opening.getAttributes()) {
168
+ if (Node.isJsxSpreadAttribute(attribute)) {
169
+ const expression2 = unwrapExpression(attribute.getExpression());
170
+ if (Node.isObjectLiteralExpression(expression2)) {
171
+ scanObject(expression2, contextEdits, flags);
172
+ } else if (declarationContainsNumericLineHeight(expression2)) {
173
+ flags.push({
174
+ code: "ambiguous-shared-line-height-style",
175
+ detail: `spread "${compact(expression2.getText())}" contains numeric lineHeight and may also be consumed outside Tamagui; migrate it at this Tamagui boundary by hand`
176
+ });
177
+ }
178
+ continue;
179
+ }
180
+ if (!Node.isJsxAttribute(attribute)) continue;
181
+ const nameNode = attribute.getNameNode();
182
+ const name = Node.isIdentifier(nameNode) ? nameNode.getText() : null;
183
+ const initializer = attribute.getInitializer();
184
+ if (isLineHeightName(name)) {
185
+ if (!Node.isJsxExpression(initializer)) continue;
186
+ const expression2 = initializer.getExpression();
187
+ if (!expression2) continue;
188
+ const current2 = unwrapExpression(expression2);
189
+ const number = numericValue(current2);
190
+ if (number !== null && !claimed.has(current2.getStart())) {
191
+ claimed.add(current2.getStart());
192
+ const edit = {
193
+ start: initializer.getStart(),
194
+ end: initializer.getEnd(),
195
+ text: JSON.stringify(`${number}px`),
196
+ dynamic: false
197
+ };
198
+ edits.push(edit);
199
+ contextEdits.push(edit);
200
+ } else {
201
+ scanLineHeightExpression(current2, contextEdits, flags);
202
+ }
203
+ continue;
204
+ }
205
+ if (isLegacyConditionName(name || "") && Node.isJsxExpression(initializer)) {
206
+ const expression2 = initializer.getExpression();
207
+ if (expression2) {
208
+ const current2 = unwrapExpression(expression2);
209
+ for (const object of [...Node.isObjectLiteralExpression(current2) ? [current2] : [], ...current2.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression)]) {
210
+ scanObject(object, contextEdits, flags);
211
+ }
212
+ }
213
+ continue;
214
+ }
215
+ if (name !== "style" || !Node.isJsxExpression(initializer)) continue;
216
+ const expression = initializer.getExpression();
217
+ if (!expression) continue;
218
+ const current = unwrapExpression(expression);
219
+ const objects = [...Node.isObjectLiteralExpression(current) ? [current] : [], ...current.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression)];
220
+ if (objects.length) {
221
+ for (const object of objects) scanObject(object, contextEdits, flags);
222
+ } else if (declarationContainsNumericLineHeight(current)) {
223
+ flags.push({
224
+ code: "ambiguous-shared-line-height-style",
225
+ detail: `style "${compact(current.getText())}" contains numeric lineHeight and may also be consumed by React Native or Restyle; migrate it at this Tamagui boundary by hand`
226
+ });
227
+ }
228
+ }
229
+ addReport(opening, "jsx", `<${opening.getTagNameNode().getText()}> lineHeight`, contextEdits, flags);
230
+ };
231
+ const scanCall = (call, label) => {
232
+ const contextEdits = [];
233
+ const flags = [];
234
+ for (const argument of call.getArguments()) {
235
+ for (const object of [...Node.isObjectLiteralExpression(argument) ? [argument] : [], ...argument.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression)]) {
236
+ scanObject(object, contextEdits, flags);
237
+ }
238
+ }
239
+ addReport(call, "styled", label, contextEdits, flags);
240
+ };
241
+ const wrapperCache = /* @__PURE__ */ new Map();
242
+ for (const opening of [...sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement), ...sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)]) {
243
+ if (provenance2.isTamaguiElement(opening)) {
244
+ scanJsx(opening);
245
+ continue;
246
+ }
247
+ const hasLegacyValue = opening.getAttributes().some((attribute) => {
248
+ if (!Node.isJsxAttribute(attribute)) return false;
249
+ const name = propertyName(attribute.getNameNode());
250
+ const initializer = attribute.getInitializer();
251
+ if (!initializer || !Node.isJsxExpression(initializer)) return false;
252
+ const expression = initializer.getExpression();
253
+ if (!expression) return false;
254
+ if (isLineHeightName(name)) return lineHeightValueIsUnmigrated(expression);
255
+ if (name !== "style") return false;
256
+ const current = unwrapExpression(expression);
257
+ const properties = current.getDescendantsOfKind(SyntaxKind.PropertyAssignment);
258
+ return properties.length ? properties.some((property) => {
259
+ const value = property.getInitializer();
260
+ return isLineHeightName(propertyName(property.getNameNode())) && value !== void 0 && lineHeightValueIsUnmigrated(value);
261
+ }) : declarationContainsNumericLineHeight(current);
262
+ });
263
+ if (!hasLegacyValue) continue;
264
+ const tag = opening.getTagNameNode();
265
+ const symbol = tag.getSymbol();
266
+ if (!symbol) continue;
267
+ const declarations = (symbol.getAliasedSymbol() ?? symbol).getDeclarations();
268
+ const isLocalWrapper = declarations.some((declaration) => {
269
+ const cached = wrapperCache.get(declaration);
270
+ if (cached !== void 0) return cached;
271
+ const file = declaration.getSourceFile();
272
+ const filePath = file.getFilePath();
273
+ const local = !file.isDeclarationFile() && !filePath.includes("/node_modules/") && !relative(projectRoot, filePath).startsWith("..");
274
+ const wrapsTamagui = local && [...declaration.getDescendantsOfKind(SyntaxKind.JsxOpeningElement), ...declaration.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)].some((child) => provenance2.isTamaguiElement(child));
275
+ wrapperCache.set(declaration, wrapsTamagui);
276
+ return wrapsTamagui;
277
+ });
278
+ if (!isLocalWrapper) continue;
279
+ addReport(opening, "jsx", `<${tag.getText()}> lineHeight`, [], [{
280
+ code: "ambiguous-wrapper-line-height",
281
+ detail: `local wrapper <${tag.getText()}> renders Tamagui JSX, so numeric lineHeight may still be V2 pixels, but the wrapper may transform units; migrate this call site by hand`
282
+ }]);
283
+ }
284
+ for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
285
+ if (provenance2.isTamaguiStyledCall(call)) {
286
+ scanCall(call, `styled(${compact(call.getArguments()[0]?.getText() ?? "unknown")}, \u2026) lineHeight`);
287
+ } else if (provenance2.isTamaguiStyleCall(call)) {
288
+ scanCall(call, `${compact(call.getExpression().getText())}(\u2026) lineHeight`);
289
+ }
290
+ }
291
+ if (edits.length) {
292
+ sourceFile.replaceWithText(replaceWithin(sourceFile.getFullText(), 0, edits));
293
+ }
294
+ return reports;
295
+ }
16
296
  const projectRoot = process.cwd();
17
297
  const defaultReportPath = resolve(projectRoot, "tamagui-flat-values-report.md");
18
298
  const ignoreMarker = ".tamagui-flat-values-ignore";
@@ -220,12 +500,22 @@ function typeAwareHost(node) {
220
500
  accepts: (property) => !(property in stylePropsTextOnly) && accepts(property)
221
501
  };
222
502
  }
223
- function inspectFile(sourceFile, registry, provenance2, write2) {
503
+ function inspectFile(sourceFile, registry, provenance2, sourceSemantics2, lineHeightOnly2, write2) {
504
+ const lineHeightSites = sourceSemantics2 === "v2-pixels" ? migrateLegacyLineHeights(sourceFile, provenance2) : [];
505
+ if (lineHeightOnly2) {
506
+ return {
507
+ file: relative(projectRoot, sourceFile.getFilePath()),
508
+ sites: lineHeightSites,
509
+ functionalVariants: [],
510
+ sheetFrames: [],
511
+ transitions: []
512
+ };
513
+ }
224
514
  const sheetFrames = convertSheetFrames(sourceFile, provenance2, write2);
225
515
  const transitions = convertTransitions(sourceFile, provenance2, write2);
226
516
  const containers = planContainers(sourceFile, registry);
227
517
  const targets = conversionTargets(sourceFile.getFilePath());
228
- const sites = [];
518
+ const sites = [...lineHeightSites];
229
519
  const functionalVariants = [];
230
520
  const requiredTypeImports = [];
231
521
  const styledCalls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).filter((call) => provenance2.isTamaguiStyledCall(call)).sort((left, right) => right.getStart() - left.getStart());
@@ -265,6 +555,10 @@ const usage = `Converts Tamagui style syntax to V3 flat property values and repo
265
555
  --report <path> where to write the Markdown report (default: ${relative(projectRoot, defaultReportPath)})
266
556
  --json <path> also write the machine-readable report
267
557
  --write rewrite every statically safe conversion in place
558
+ --source-semantics <v2-pixels|v3-ratios>
559
+ interpret numeric Tamagui lineHeight from that source contract
560
+ (default: v3-ratios)
561
+ --line-height-only apply and report only the selected lineHeight source migration
268
562
  --help print this
269
563
 
270
564
  Run it from your project root, which is where paths and the tsconfig resolve from.
@@ -274,6 +568,8 @@ function parseArguments(argv) {
274
568
  let reportPath2 = defaultReportPath;
275
569
  let jsonPath2 = null;
276
570
  let write2 = false;
571
+ let sourceSemantics2 = "v3-ratios";
572
+ let lineHeightOnly2 = false;
277
573
  for (let index = 0; index < argv.length; index++) {
278
574
  const argument = argv[index];
279
575
  if (argument === "--help" || argument === "-h") {
@@ -284,6 +580,22 @@ function parseArguments(argv) {
284
580
  write2 = true;
285
581
  continue;
286
582
  }
583
+ if (argument === "--line-height-only") {
584
+ lineHeightOnly2 = true;
585
+ continue;
586
+ }
587
+ if (argument === "--source-semantics") {
588
+ const next = argv[index + 1];
589
+ if (next !== "v2-pixels" && next !== "v3-ratios") {
590
+ console.error(`${argument} requires v2-pixels or v3-ratios
591
+
592
+ ${usage}`);
593
+ process.exit(2);
594
+ }
595
+ sourceSemantics2 = next;
596
+ index++;
597
+ continue;
598
+ }
287
599
  if (argument === "--report" || argument === "--json") {
288
600
  const next = argv[index + 1];
289
601
  if (!next) {
@@ -315,10 +627,12 @@ ${usage}`);
315
627
  reportPath: reportPath2,
316
628
  jsonPath: jsonPath2,
317
629
  inputs: inputs2,
318
- write: write2
630
+ write: write2,
631
+ sourceSemantics: sourceSemantics2,
632
+ lineHeightOnly: lineHeightOnly2
319
633
  };
320
634
  }
321
- const { reportPath, jsonPath, inputs, write } = parseArguments(process.argv.slice(2));
635
+ const { reportPath, jsonPath, inputs, write, sourceSemantics, lineHeightOnly } = parseArguments(process.argv.slice(2));
322
636
  const { sourceFiles, ignoredFiles } = collectFiles(inputs);
323
637
  for (const sourceFile of sourceFiles) {
324
638
  const diagnostics = sourceFile.compilerNode.parseDiagnostics;
@@ -333,7 +647,7 @@ const modifierRegistry = createModifierRegistry({
333
647
  themeNames: themeNames(sourceFiles)
334
648
  });
335
649
  const provenance = createProvenance();
336
- const files = sourceFiles.map((sourceFile) => inspectFile(sourceFile, modifierRegistry.registry, provenance, write));
650
+ const files = sourceFiles.map((sourceFile) => inspectFile(sourceFile, modifierRegistry.registry, provenance, sourceSemantics, lineHeightOnly, write));
337
651
  if (write) {
338
652
  for (const sourceFile of sourceFiles) {
339
653
  const filePath = sourceFile.getFilePath();