@tamagui/codemod-flat-values 3.0.0-beta.1312.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 +20 -0
- package/dist/expressions.mjs +1 -1
- package/dist/expressions.mjs.map +1 -1
- package/dist/grammar.mjs +7 -4
- package/dist/grammar.mjs.map +1 -1
- package/dist/index.mjs +320 -6
- package/dist/index.mjs.map +1 -1
- package/dist/provenance.mjs +10 -1
- package/dist/provenance.mjs.map +1 -1
- package/package.json +5 -5
- package/src/expressions.ts +1 -1
- package/src/grammar.ts +11 -4
- package/src/index.ts +454 -5
- package/src/provenance.ts +11 -0
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
|
package/dist/expressions.mjs
CHANGED
package/dist/expressions.mjs.map
CHANGED
|
@@ -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":"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
|
|
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
|
|
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(
|
|
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] =
|
|
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,
|
|
23
|
+
return convertLegacyConditionProp$1(propName, normalizeV3Payload(value), options);
|
|
21
24
|
}
|
|
22
25
|
const styleProps = /* @__PURE__ */ new Set([
|
|
23
26
|
...grammarEntries.map((entry) => entry.prop),
|
package/dist/grammar.mjs.map
CHANGED
|
@@ -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
|
|
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();
|