@tamagui/codemod-flat-values 3.0.0-beta.922.1 → 3.0.0

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/src/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
3
3
  import { dirname, relative, resolve } from 'node:path'
4
- import { resolveTamaguiHost } from '@tamagui/language-service/host'
5
4
  import { stylePropsTextOnly } from '@tamagui/helpers'
6
5
  import {
7
6
  IndentationText,
@@ -12,13 +11,17 @@ import {
12
11
  ScriptTarget,
13
12
  SyntaxKind,
14
13
  ts,
14
+ type CallExpression,
15
15
  type Expression,
16
+ type JsxOpeningElement,
17
+ type JsxSelfClosingElement,
16
18
  type ObjectLiteralExpression,
19
+ type PropertyAssignment,
17
20
  type SourceFile,
18
21
  } from 'ts-morph'
19
22
  import { planContainers, type ContainerPlan } from './containers'
20
23
  import { convertJsxSite, convertStyleObject, type SiteReport } from './convert'
21
- import { compact, unwrapExpression } from './expressions'
24
+ import { compact, numericValue, runtimeType, unwrapExpression } from './expressions'
22
25
  import {
23
26
  addFunctionalVariantTypeImports,
24
27
  convertFunctionalVariants,
@@ -37,8 +40,409 @@ import {
37
40
  import { createProvenance } from './provenance'
38
41
  import { renderReport, type FileReport } from './report'
39
42
  import { convertSheetFrames } from './sheetAnatomy'
43
+ import { convertTransitions } from './transition'
44
+ import { isLegacyConditionName } from './legacyNames'
40
45
 
41
46
  type Provenance = ReturnType<typeof createProvenance>
47
+ type SourceSemantics = 'v2-pixels' | 'v3-ratios'
48
+
49
+ interface SourceEdit {
50
+ start: number
51
+ end: number
52
+ text: string
53
+ dynamic: boolean
54
+ }
55
+
56
+ function propertyName(node: Node): string | null {
57
+ if (
58
+ Node.isIdentifier(node) ||
59
+ Node.isStringLiteral(node) ||
60
+ Node.isNumericLiteral(node)
61
+ ) {
62
+ return node.getText().replace(/^['"]|['"]$/g, '')
63
+ }
64
+ return null
65
+ }
66
+
67
+ function isLineHeightName(name: string | null): boolean {
68
+ return name === 'lineHeight' || name === 'lh'
69
+ }
70
+
71
+ function extractedTokenValue(expression: Expression): boolean {
72
+ const nodes = [expression, ...expression.getDescendants()]
73
+ return nodes.some(
74
+ (node) =>
75
+ (Node.isPropertyAccessExpression(node) && node.getName() === 'val') ||
76
+ (Node.isCallExpression(node) &&
77
+ Node.isIdentifier(node.getExpression()) &&
78
+ node.getExpression().getText() === 'getVariableValue')
79
+ )
80
+ }
81
+
82
+ function pixelReplacement(
83
+ expression: Expression
84
+ ): { text: string; dynamic: boolean } | { flag: SiteReport['flags'][number] } | null {
85
+ const current = unwrapExpression(expression)
86
+ const number = numericValue(current)
87
+ if (number !== null) return { text: JSON.stringify(`${number}px`), dynamic: false }
88
+ const type = runtimeType(current)
89
+ if (type.kind === 'string') return null
90
+
91
+ if (extractedTokenValue(current)) {
92
+ return {
93
+ flag: {
94
+ code: 'ambiguous-line-height-token-value',
95
+ 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`,
96
+ },
97
+ }
98
+ }
99
+
100
+ if (type.kind === 'number') {
101
+ return { text: `\`\${${current.getText()}}px\``, dynamic: true }
102
+ }
103
+ return {
104
+ flag: {
105
+ code: 'ambiguous-line-height-expression',
106
+ detail: `lineHeight value "${compact(current.getText())}" has no provable numeric type; replace old pixel semantics with an explicit px length by hand`,
107
+ },
108
+ }
109
+ }
110
+
111
+ function declarationContainsNumericLineHeight(expression: Expression): boolean {
112
+ const symbol = expression.getSymbol()
113
+ if (!symbol) return false
114
+ for (const declaration of symbol.getDeclarations()) {
115
+ for (const property of [
116
+ ...(Node.isPropertyAssignment(declaration) ? [declaration] : []),
117
+ ...declaration.getDescendantsOfKind(SyntaxKind.PropertyAssignment),
118
+ ]) {
119
+ if (!isLineHeightName(propertyName(property.getNameNode()))) continue
120
+ const initializer = property.getInitializer()
121
+ if (!initializer) continue
122
+ const value = unwrapExpression(initializer)
123
+ if (numericValue(value) !== null || runtimeType(value).kind === 'number')
124
+ return true
125
+ }
126
+ }
127
+ return false
128
+ }
129
+
130
+ function lineHeightValueIsUnmigrated(expression: Expression): boolean {
131
+ const current = unwrapExpression(expression)
132
+ if (Node.isObjectLiteralExpression(current)) {
133
+ return current.getProperties().some((property) => {
134
+ if (!Node.isPropertyAssignment(property)) return false
135
+ const initializer = property.getInitializer()
136
+ return initializer !== undefined && lineHeightValueIsUnmigrated(initializer)
137
+ })
138
+ }
139
+ if (Node.isConditionalExpression(current)) {
140
+ return (
141
+ lineHeightValueIsUnmigrated(current.getWhenTrue()) ||
142
+ lineHeightValueIsUnmigrated(current.getWhenFalse())
143
+ )
144
+ }
145
+ return pixelReplacement(current) !== null
146
+ }
147
+
148
+ function replaceWithin(
149
+ source: string,
150
+ sourceStart: number,
151
+ edits: readonly SourceEdit[]
152
+ ): string {
153
+ let output = source
154
+ for (const edit of [...edits].sort((left, right) => right.start - left.start)) {
155
+ output =
156
+ output.slice(0, edit.start - sourceStart) +
157
+ edit.text +
158
+ output.slice(edit.end - sourceStart)
159
+ }
160
+ return output
161
+ }
162
+
163
+ function migrateLegacyLineHeights(
164
+ sourceFile: SourceFile,
165
+ provenance: Provenance
166
+ ): SiteReport[] {
167
+ const edits: SourceEdit[] = []
168
+ const claimed = new Set<number>()
169
+ const reports: SiteReport[] = []
170
+
171
+ const scanLineHeightExpression = (
172
+ expression: Expression,
173
+ contextEdits: SourceEdit[],
174
+ flags: SiteReport['flags']
175
+ ) => {
176
+ const current = unwrapExpression(expression)
177
+ if (Node.isObjectLiteralExpression(current)) {
178
+ for (const property of current.getProperties()) {
179
+ if (!Node.isPropertyAssignment(property)) continue
180
+ const initializer = property.getInitializer()
181
+ if (initializer) scanLineHeightExpression(initializer, contextEdits, flags)
182
+ }
183
+ return
184
+ }
185
+ if (Node.isConditionalExpression(current)) {
186
+ scanLineHeightExpression(current.getWhenTrue(), contextEdits, flags)
187
+ scanLineHeightExpression(current.getWhenFalse(), contextEdits, flags)
188
+ return
189
+ }
190
+ if (claimed.has(current.getStart())) return
191
+ const replacement = pixelReplacement(current)
192
+ if (!replacement) return
193
+ claimed.add(current.getStart())
194
+ if ('flag' in replacement) {
195
+ flags.push(replacement.flag)
196
+ return
197
+ }
198
+ const edit = {
199
+ start: current.getStart(),
200
+ end: current.getEnd(),
201
+ text: replacement.text,
202
+ dynamic: replacement.dynamic,
203
+ }
204
+ edits.push(edit)
205
+ contextEdits.push(edit)
206
+ }
207
+
208
+ const scanProperty = (
209
+ property: PropertyAssignment,
210
+ contextEdits: SourceEdit[],
211
+ flags: SiteReport['flags']
212
+ ) => {
213
+ if (!isLineHeightName(propertyName(property.getNameNode()))) return
214
+ const initializer = property.getInitializer()
215
+ if (initializer) scanLineHeightExpression(initializer, contextEdits, flags)
216
+ }
217
+
218
+ const scanObject = (
219
+ object: ObjectLiteralExpression,
220
+ contextEdits: SourceEdit[],
221
+ flags: SiteReport['flags']
222
+ ) => {
223
+ for (const property of object.getDescendantsOfKind(SyntaxKind.PropertyAssignment)) {
224
+ scanProperty(property, contextEdits, flags)
225
+ }
226
+ for (const property of object.getProperties()) {
227
+ if (Node.isPropertyAssignment(property)) scanProperty(property, contextEdits, flags)
228
+ }
229
+ }
230
+
231
+ const addReport = (
232
+ node: Node,
233
+ kind: SiteReport['kind'],
234
+ label: string,
235
+ contextEdits: SourceEdit[],
236
+ flags: SiteReport['flags']
237
+ ) => {
238
+ if (!contextEdits.length && !flags.length) return
239
+ const before = compact(node.getText())
240
+ reports.push({
241
+ kind,
242
+ label,
243
+ line: sourceFile.getLineAndColumnAtPos(node.getStart()).line,
244
+ before,
245
+ after: compact(replaceWithin(node.getText(), node.getStart(), contextEdits)),
246
+ programs: contextEdits.map((edit) => ({
247
+ name: 'lineHeight',
248
+ value: edit.text,
249
+ dynamic: edit.dynamic,
250
+ })),
251
+ assessments: [],
252
+ assessmentVerdict: 'clean',
253
+ warnings: [],
254
+ flags,
255
+ inventory: [],
256
+ pending: [],
257
+ notes: [
258
+ contextEdits.length
259
+ ? 'numeric lineHeight used V2 pixel semantics and now carries an explicit px unit'
260
+ : 'lineHeight was left authored for a manual unit decision',
261
+ ],
262
+ legacyLeft: flags.length,
263
+ })
264
+ }
265
+
266
+ const scanJsx = (opening: JsxOpeningElement | JsxSelfClosingElement) => {
267
+ const contextEdits: SourceEdit[] = []
268
+ const flags: SiteReport['flags'] = []
269
+ for (const attribute of opening.getAttributes()) {
270
+ if (Node.isJsxSpreadAttribute(attribute)) {
271
+ const expression = unwrapExpression(attribute.getExpression())
272
+ if (Node.isObjectLiteralExpression(expression)) {
273
+ scanObject(expression, contextEdits, flags)
274
+ } else if (declarationContainsNumericLineHeight(expression)) {
275
+ flags.push({
276
+ code: 'ambiguous-shared-line-height-style',
277
+ detail: `spread "${compact(expression.getText())}" contains numeric lineHeight and may also be consumed outside Tamagui; migrate it at this Tamagui boundary by hand`,
278
+ })
279
+ }
280
+ continue
281
+ }
282
+ if (!Node.isJsxAttribute(attribute)) continue
283
+ const nameNode = attribute.getNameNode()
284
+ const name = Node.isIdentifier(nameNode) ? nameNode.getText() : null
285
+ const initializer = attribute.getInitializer()
286
+ if (isLineHeightName(name)) {
287
+ if (!Node.isJsxExpression(initializer)) continue
288
+ const expression = initializer.getExpression()
289
+ if (!expression) continue
290
+ const current = unwrapExpression(expression)
291
+ const number = numericValue(current)
292
+ if (number !== null && !claimed.has(current.getStart())) {
293
+ claimed.add(current.getStart())
294
+ const edit = {
295
+ start: initializer.getStart(),
296
+ end: initializer.getEnd(),
297
+ text: JSON.stringify(`${number}px`),
298
+ dynamic: false,
299
+ }
300
+ edits.push(edit)
301
+ contextEdits.push(edit)
302
+ } else {
303
+ scanLineHeightExpression(current, contextEdits, flags)
304
+ }
305
+ continue
306
+ }
307
+ if (isLegacyConditionName(name || '') && Node.isJsxExpression(initializer)) {
308
+ const expression = initializer.getExpression()
309
+ if (expression) {
310
+ const current = unwrapExpression(expression)
311
+ for (const object of [
312
+ ...(Node.isObjectLiteralExpression(current) ? [current] : []),
313
+ ...current.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression),
314
+ ]) {
315
+ scanObject(object, contextEdits, flags)
316
+ }
317
+ }
318
+ continue
319
+ }
320
+ if (name !== 'style' || !Node.isJsxExpression(initializer)) continue
321
+ const expression = initializer.getExpression()
322
+ if (!expression) continue
323
+ const current = unwrapExpression(expression)
324
+ const objects = [
325
+ ...(Node.isObjectLiteralExpression(current) ? [current] : []),
326
+ ...current.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression),
327
+ ]
328
+ if (objects.length) {
329
+ for (const object of objects) scanObject(object, contextEdits, flags)
330
+ } else if (declarationContainsNumericLineHeight(current)) {
331
+ flags.push({
332
+ code: 'ambiguous-shared-line-height-style',
333
+ 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`,
334
+ })
335
+ }
336
+ }
337
+ addReport(
338
+ opening,
339
+ 'jsx',
340
+ `<${opening.getTagNameNode().getText()}> lineHeight`,
341
+ contextEdits,
342
+ flags
343
+ )
344
+ }
345
+
346
+ const scanCall = (call: CallExpression, label: string) => {
347
+ const contextEdits: SourceEdit[] = []
348
+ const flags: SiteReport['flags'] = []
349
+ for (const argument of call.getArguments()) {
350
+ for (const object of [
351
+ ...(Node.isObjectLiteralExpression(argument) ? [argument] : []),
352
+ ...argument.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression),
353
+ ]) {
354
+ scanObject(object, contextEdits, flags)
355
+ }
356
+ }
357
+ addReport(call, 'styled', label, contextEdits, flags)
358
+ }
359
+
360
+ const wrapperCache = new Map<Node, boolean>()
361
+ for (const opening of [
362
+ ...sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement),
363
+ ...sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),
364
+ ]) {
365
+ if (provenance.isTamaguiElement(opening)) {
366
+ scanJsx(opening)
367
+ continue
368
+ }
369
+ const hasLegacyValue = opening.getAttributes().some((attribute) => {
370
+ if (!Node.isJsxAttribute(attribute)) return false
371
+ const name = propertyName(attribute.getNameNode())
372
+ const initializer = attribute.getInitializer()
373
+ if (!initializer || !Node.isJsxExpression(initializer)) return false
374
+ const expression = initializer.getExpression()
375
+ if (!expression) return false
376
+ if (isLineHeightName(name)) return lineHeightValueIsUnmigrated(expression)
377
+ if (name !== 'style') return false
378
+ const current = unwrapExpression(expression)
379
+ const properties = current.getDescendantsOfKind(SyntaxKind.PropertyAssignment)
380
+ return properties.length
381
+ ? properties.some((property) => {
382
+ const value = property.getInitializer()
383
+ return (
384
+ isLineHeightName(propertyName(property.getNameNode())) &&
385
+ value !== undefined &&
386
+ lineHeightValueIsUnmigrated(value)
387
+ )
388
+ })
389
+ : declarationContainsNumericLineHeight(current)
390
+ })
391
+ if (!hasLegacyValue) continue
392
+
393
+ const tag = opening.getTagNameNode()
394
+ const symbol = tag.getSymbol()
395
+ if (!symbol) continue
396
+ const declarations = (symbol.getAliasedSymbol() ?? symbol).getDeclarations()
397
+ const isLocalWrapper = declarations.some((declaration) => {
398
+ const cached = wrapperCache.get(declaration)
399
+ if (cached !== undefined) return cached
400
+ const file = declaration.getSourceFile()
401
+ const filePath = file.getFilePath()
402
+ const local =
403
+ !file.isDeclarationFile() &&
404
+ !filePath.includes('/node_modules/') &&
405
+ !relative(projectRoot, filePath).startsWith('..')
406
+ const wrapsTamagui =
407
+ local &&
408
+ [
409
+ ...declaration.getDescendantsOfKind(SyntaxKind.JsxOpeningElement),
410
+ ...declaration.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement),
411
+ ].some((child) => provenance.isTamaguiElement(child))
412
+ wrapperCache.set(declaration, wrapsTamagui)
413
+ return wrapsTamagui
414
+ })
415
+ if (!isLocalWrapper) continue
416
+ addReport(
417
+ opening,
418
+ 'jsx',
419
+ `<${tag.getText()}> lineHeight`,
420
+ [],
421
+ [
422
+ {
423
+ code: 'ambiguous-wrapper-line-height',
424
+ 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`,
425
+ },
426
+ ]
427
+ )
428
+ }
429
+
430
+ for (const call of sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
431
+ if (provenance.isTamaguiStyledCall(call)) {
432
+ scanCall(
433
+ call,
434
+ `styled(${compact(call.getArguments()[0]?.getText() ?? 'unknown')}, …) lineHeight`
435
+ )
436
+ } else if (provenance.isTamaguiStyleCall(call)) {
437
+ scanCall(call, `${compact(call.getExpression().getText())}(…) lineHeight`)
438
+ }
439
+ }
440
+
441
+ if (edits.length) {
442
+ sourceFile.replaceWithText(replaceWithin(sourceFile.getFullText(), 0, edits))
443
+ }
444
+ return reports
445
+ }
42
446
 
43
447
  // every path is resolved against the directory the codemod is invoked from, so it
44
448
  // migrates the project you are standing in whether that is an app or this repo
@@ -99,7 +503,7 @@ function collectFiles(inputs: readonly string[]): {
99
503
  jsx: 4,
100
504
  target: ScriptTarget.ES2020,
101
505
  module: ModuleKind.ESNext,
102
- moduleResolution: ModuleResolutionKind.NodeJs,
506
+ moduleResolution: ModuleResolutionKind.Bundler,
103
507
  skipLibCheck: true,
104
508
  strictNullChecks: true,
105
509
  baseUrl: projectRoot,
@@ -324,12 +728,23 @@ for (const [shorthand, longhand] of Object.entries(shorthands)) {
324
728
  }
325
729
 
326
730
  function typeAwareHost(node: Node): HostView | undefined {
327
- const checker = node.getProject().getTypeChecker().compilerObject
328
- const host = resolveTamaguiHost(
329
- checker as unknown as Parameters<typeof resolveTamaguiHost>[0],
330
- node.compilerNode as unknown as Parameters<typeof resolveTamaguiHost>[1]
331
- )
332
- if (!host) return host
731
+ const checker = node.getProject().getTypeChecker()
732
+ const componentType = checker.getTypeAtLocation(node)
733
+ if (!componentType.getProperty('staticConfig')) return undefined
734
+
735
+ const propsTypes = componentType
736
+ .getCallSignatures()
737
+ .flatMap((signature) => signature.getParameters().slice(0, 1))
738
+ .map((props) => checker.getTypeOfSymbolAtLocation(props, node))
739
+ if (propsTypes.length === 0) return undefined
740
+
741
+ const host: HostView = {
742
+ displayName: node.getText(),
743
+ accepts: (property) =>
744
+ propsTypes.some(
745
+ (propsType) => propsType.getApparentType().getProperty(property) !== undefined
746
+ ),
747
+ }
333
748
 
334
749
  // The conversion resolves an authored shorthand to its longhand before asking
335
750
  // the host, and `onlyAllowShorthands: true` omits exactly those longhands from
@@ -357,14 +772,32 @@ function inspectFile(
357
772
  sourceFile: SourceFile,
358
773
  registry: ModifierRegistryView,
359
774
  provenance: Provenance,
775
+ sourceSemantics: SourceSemantics,
776
+ lineHeightOnly: boolean,
360
777
  write: boolean
361
778
  ): FileReport {
779
+ const lineHeightSites =
780
+ sourceSemantics === 'v2-pixels'
781
+ ? migrateLegacyLineHeights(sourceFile, provenance)
782
+ : []
783
+ if (lineHeightOnly) {
784
+ return {
785
+ file: relative(projectRoot, sourceFile.getFilePath()),
786
+ sites: lineHeightSites,
787
+ functionalVariants: [],
788
+ sheetFrames: [],
789
+ transitions: [],
790
+ }
791
+ }
362
792
  // the anatomy rewrite runs first so the Background it adds, and the surface
363
793
  // props it moves there, go through the flat-value conversion below
364
794
  const sheetFrames = convertSheetFrames(sourceFile, provenance, write)
795
+ // the transition respelling touches one value, so it runs on its own rather
796
+ // than turning every element that has a v2 transition into a style site
797
+ const transitions = convertTransitions(sourceFile, provenance, write)
365
798
  const containers = planContainers(sourceFile, registry)
366
799
  const targets = conversionTargets(sourceFile.getFilePath())
367
- const sites: SiteReport[] = []
800
+ const sites: SiteReport[] = [...lineHeightSites]
368
801
  const functionalVariants: FunctionalVariantReport[] = []
369
802
  const requiredTypeImports: RequiredTypeImport[] = []
370
803
  const styledCalls = sourceFile
@@ -427,6 +860,7 @@ function inspectFile(
427
860
  sites,
428
861
  functionalVariants,
429
862
  sheetFrames,
863
+ transitions,
430
864
  }
431
865
  }
432
866
 
@@ -440,6 +874,10 @@ const usage = `Converts Tamagui style syntax to V3 flat property values and repo
440
874
  )})
441
875
  --json <path> also write the machine-readable report
442
876
  --write rewrite every statically safe conversion in place
877
+ --source-semantics <v2-pixels|v3-ratios>
878
+ interpret numeric Tamagui lineHeight from that source contract
879
+ (default: v3-ratios)
880
+ --line-height-only apply and report only the selected lineHeight source migration
443
881
  --help print this
444
882
 
445
883
  Run it from your project root, which is where paths and the tsconfig resolve from.
@@ -450,11 +888,15 @@ function parseArguments(argv: readonly string[]): {
450
888
  jsonPath: string | null
451
889
  inputs: string[]
452
890
  write: boolean
891
+ sourceSemantics: SourceSemantics
892
+ lineHeightOnly: boolean
453
893
  } {
454
894
  const inputs: string[] = []
455
895
  let reportPath = defaultReportPath
456
896
  let jsonPath: string | null = null
457
897
  let write = false
898
+ let sourceSemantics: SourceSemantics = 'v3-ratios'
899
+ let lineHeightOnly = false
458
900
 
459
901
  for (let index = 0; index < argv.length; index++) {
460
902
  const argument = argv[index]
@@ -466,6 +908,20 @@ function parseArguments(argv: readonly string[]): {
466
908
  write = true
467
909
  continue
468
910
  }
911
+ if (argument === '--line-height-only') {
912
+ lineHeightOnly = true
913
+ continue
914
+ }
915
+ if (argument === '--source-semantics') {
916
+ const next = argv[index + 1]
917
+ if (next !== 'v2-pixels' && next !== 'v3-ratios') {
918
+ console.error(`${argument} requires v2-pixels or v3-ratios\n\n${usage}`)
919
+ process.exit(2)
920
+ }
921
+ sourceSemantics = next
922
+ index++
923
+ continue
924
+ }
469
925
  if (argument === '--report' || argument === '--json') {
470
926
  const next = argv[index + 1]
471
927
  if (!next) {
@@ -493,10 +949,11 @@ function parseArguments(argv: readonly string[]): {
493
949
  process.exit(2)
494
950
  }
495
951
 
496
- return { reportPath, jsonPath, inputs, write }
952
+ return { reportPath, jsonPath, inputs, write, sourceSemantics, lineHeightOnly }
497
953
  }
498
954
 
499
- const { reportPath, jsonPath, inputs, write } = parseArguments(process.argv.slice(2))
955
+ const { reportPath, jsonPath, inputs, write, sourceSemantics, lineHeightOnly } =
956
+ parseArguments(process.argv.slice(2))
500
957
  const { sourceFiles, ignoredFiles } = collectFiles(inputs)
501
958
  for (const sourceFile of sourceFiles) {
502
959
  const diagnostics = (
@@ -520,7 +977,14 @@ const modifierRegistry = createModifierRegistry({
520
977
  })
521
978
  const provenance = createProvenance()
522
979
  const files = sourceFiles.map((sourceFile) =>
523
- inspectFile(sourceFile, modifierRegistry.registry, provenance, write)
980
+ inspectFile(
981
+ sourceFile,
982
+ modifierRegistry.registry,
983
+ provenance,
984
+ sourceSemantics,
985
+ lineHeightOnly,
986
+ write
987
+ )
524
988
  )
525
989
  if (write) {
526
990
  for (const sourceFile of sourceFiles) {
@@ -580,5 +1044,5 @@ if (write) {
580
1044
  console.log(`wrote ${reportPath}`)
581
1045
  if (write) console.log(`rewrote ${written} source files`)
582
1046
  console.log(
583
- `${summary.sites} style sites: ${summary.clean - summary.waiting} clean, ${summary.needsRelocation} need relocation, ${summary.unknownHost} unknown host, ${summary.ineligible} ineligible, ${summary.waiting} waiting on runtime support, ${summary.flagged} syntax-flagged; ${summary.functionalVariantSites} functional variants: ${summary.functionalVariantConverted} automatic, ${summary.functionalVariantFlagged} flagged; ${summary.sheetFrames} Sheet.Frame sites: ${summary.sheetFramesFlagged} need review; ${summary.ignoredFiles} source files ignored`
1047
+ `${summary.sites} style sites: ${summary.clean - summary.waiting} clean, ${summary.needsRelocation} need relocation, ${summary.unknownHost} unknown host, ${summary.ineligible} ineligible, ${summary.waiting} waiting on runtime support, ${summary.flagged} syntax-flagged; ${summary.functionalVariantSites} functional variants: ${summary.functionalVariantConverted} automatic, ${summary.functionalVariantFlagged} flagged; ${summary.sheetFrames} Sheet.Frame sites: ${summary.sheetFramesFlagged} need review; ${summary.transitions} v2 transitions: ${summary.transitionsFlagged} need review; ${summary.ignoredFiles} source files ignored`
584
1048
  )
package/src/provenance.ts CHANGED
@@ -28,6 +28,7 @@ function rootIdentifier(node: Node): Identifier | null {
28
28
  export function createProvenance(): {
29
29
  isTamaguiElement: (opening: JsxOpeningElement | JsxSelfClosingElement) => boolean
30
30
  isTamaguiStyledCall: (call: CallExpression) => boolean
31
+ isTamaguiStyleCall: (call: CallExpression) => boolean
31
32
  } {
32
33
  const resolved = new Map<Node, boolean>()
33
34
  const active = new Set<Node>()
@@ -177,6 +178,15 @@ export function createProvenance(): {
177
178
  return fromIdentifier(callee)
178
179
  }
179
180
 
181
+ const isTamaguiStyleCall = (call: CallExpression): boolean => {
182
+ const callee = call.getExpression()
183
+ if (!Node.isPropertyAccessExpression(callee)) return false
184
+ const method = callee.getName()
185
+ if (method !== 'style' && method !== 'resolve' && method !== 'dynamic') return false
186
+ const owner = rootIdentifier(callee.getExpression())
187
+ return owner !== null && fromIdentifier(owner)
188
+ }
189
+
180
190
  /**
181
191
  * A value re-bound from Tamagui: an alias (`const Sheet = SheetRaw as ...`), a
182
192
  * member of one, or the result of a Tamagui factory (`styled`,
@@ -206,5 +216,6 @@ export function createProvenance(): {
206
216
  return fromIdentifier(tag)
207
217
  },
208
218
  isTamaguiStyledCall,
219
+ isTamaguiStyleCall,
209
220
  }
210
221
  }