@tamagui/compiler-core 0.0.0-bootstrap.0 → 3.0.0-beta.1093.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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +57 -1
  3. package/dist/cjs/ast.cjs +108 -0
  4. package/dist/cjs/contracts.cjs +61 -0
  5. package/dist/cjs/diagnostics.cjs +68 -0
  6. package/dist/cjs/evaluate.cjs +602 -0
  7. package/dist/cjs/graph.cjs +335 -0
  8. package/dist/cjs/hash.cjs +48 -0
  9. package/dist/cjs/index.cjs +43 -0
  10. package/dist/cjs/ir.cjs +32 -0
  11. package/dist/cjs/lower.cjs +232 -0
  12. package/dist/cjs/materialize.cjs +222 -0
  13. package/dist/cjs/normalize.cjs +628 -0
  14. package/dist/cjs/output.cjs +148 -0
  15. package/dist/cjs/planCache.cjs +262 -0
  16. package/dist/cjs/session.cjs +127 -0
  17. package/dist/cjs/yuku.cjs +166 -0
  18. package/dist/cjs/zero.cjs +389 -0
  19. package/dist/esm/ast.mjs +79 -0
  20. package/dist/esm/ast.mjs.map +1 -0
  21. package/dist/esm/contracts.mjs +34 -0
  22. package/dist/esm/contracts.mjs.map +1 -0
  23. package/dist/esm/diagnostics.mjs +44 -0
  24. package/dist/esm/diagnostics.mjs.map +1 -0
  25. package/dist/esm/evaluate.mjs +576 -0
  26. package/dist/esm/evaluate.mjs.map +1 -0
  27. package/dist/esm/graph.mjs +313 -0
  28. package/dist/esm/graph.mjs.map +1 -0
  29. package/dist/esm/hash.mjs +26 -0
  30. package/dist/esm/hash.mjs.map +1 -0
  31. package/dist/esm/index.mjs +30 -0
  32. package/dist/esm/ir.mjs +12 -0
  33. package/dist/esm/ir.mjs.map +1 -0
  34. package/dist/esm/lower.mjs +210 -0
  35. package/dist/esm/lower.mjs.map +1 -0
  36. package/dist/esm/materialize.mjs +203 -0
  37. package/dist/esm/materialize.mjs.map +1 -0
  38. package/dist/esm/normalize.mjs +604 -0
  39. package/dist/esm/normalize.mjs.map +1 -0
  40. package/dist/esm/output.mjs +119 -0
  41. package/dist/esm/output.mjs.map +1 -0
  42. package/dist/esm/planCache.mjs +232 -0
  43. package/dist/esm/planCache.mjs.map +1 -0
  44. package/dist/esm/session.mjs +108 -0
  45. package/dist/esm/session.mjs.map +1 -0
  46. package/dist/esm/yuku.mjs +144 -0
  47. package/dist/esm/yuku.mjs.map +1 -0
  48. package/dist/esm/zero.mjs +354 -0
  49. package/dist/esm/zero.mjs.map +1 -0
  50. package/package.json +42 -4
  51. package/src/ast.ts +106 -0
  52. package/src/contracts.ts +150 -0
  53. package/src/diagnostics.ts +96 -0
  54. package/src/evaluate.ts +875 -0
  55. package/src/graph.ts +425 -0
  56. package/src/hash.ts +36 -0
  57. package/src/index.ts +15 -0
  58. package/src/ir.ts +156 -0
  59. package/src/lower.ts +435 -0
  60. package/src/materialize.ts +383 -0
  61. package/src/normalize.ts +834 -0
  62. package/src/output.ts +175 -0
  63. package/src/planCache.ts +362 -0
  64. package/src/session.ts +192 -0
  65. package/src/yuku.ts +197 -0
  66. package/src/zero.ts +609 -0
  67. package/types/ast.d.ts +11 -0
  68. package/types/ast.d.ts.map +1 -0
  69. package/types/contracts.d.ts +99 -0
  70. package/types/contracts.d.ts.map +1 -0
  71. package/types/diagnostics.d.ts +31 -0
  72. package/types/diagnostics.d.ts.map +1 -0
  73. package/types/evaluate.d.ts +48 -0
  74. package/types/evaluate.d.ts.map +1 -0
  75. package/types/graph.d.ts +51 -0
  76. package/types/graph.d.ts.map +1 -0
  77. package/types/hash.d.ts +8 -0
  78. package/types/hash.d.ts.map +1 -0
  79. package/types/index.d.ts +16 -0
  80. package/types/index.d.ts.map +1 -0
  81. package/types/ir.d.ts +120 -0
  82. package/types/ir.d.ts.map +1 -0
  83. package/types/lower.d.ts +99 -0
  84. package/types/lower.d.ts.map +1 -0
  85. package/types/materialize.d.ts +121 -0
  86. package/types/materialize.d.ts.map +1 -0
  87. package/types/normalize.d.ts +7 -0
  88. package/types/normalize.d.ts.map +1 -0
  89. package/types/output.d.ts +32 -0
  90. package/types/output.d.ts.map +1 -0
  91. package/types/planCache.d.ts +123 -0
  92. package/types/planCache.d.ts.map +1 -0
  93. package/types/session.d.ts +52 -0
  94. package/types/session.d.ts.map +1 -0
  95. package/types/yuku.d.ts +4 -0
  96. package/types/yuku.d.ts.map +1 -0
  97. package/types/zero.d.ts +99 -0
  98. package/types/zero.d.ts.map +1 -0
@@ -0,0 +1,834 @@
1
+ import {
2
+ childNode,
3
+ childNodes,
4
+ findAstNode,
5
+ identifierName,
6
+ isAstNode,
7
+ walkAst,
8
+ } from './ast'
9
+ import {
10
+ expressionReference,
11
+ resolvedModuleId,
12
+ spanOf,
13
+ type AnalyzerCandidate,
14
+ type AstNode,
15
+ type DefinitionSite,
16
+ type HostResolvedImport,
17
+ type ResolvedModuleId,
18
+ type SymbolDefinition,
19
+ } from './contracts'
20
+ import { linkedBailout, localBailout, type BailoutReason } from './diagnostics'
21
+ import type {
22
+ ElementChildIR,
23
+ ElementComponentIR,
24
+ ElementEntryIR,
25
+ ElementIR,
26
+ ElementIRBase,
27
+ ElementIRResult,
28
+ ElementPropIR,
29
+ ElementSpreadIR,
30
+ ElementValue,
31
+ DOMStyleDefinitionIR,
32
+ DOMStyleProgramIR,
33
+ StaticValue,
34
+ StyledDefinitionIR,
35
+ } from './ir'
36
+ import { hostImportProvenance } from './ir'
37
+
38
+ function finalizeElement(
39
+ base: ElementIRBase,
40
+ entries: ElementEntryIR[],
41
+ bailouts: BailoutReason[]
42
+ ): ElementIR {
43
+ if (bailouts.length === 0) {
44
+ return { ...base, complete: true, entries, bailouts: [] }
45
+ }
46
+ return {
47
+ ...base,
48
+ complete: false,
49
+ bailedEntries: entries,
50
+ bailouts: bailouts as [BailoutReason, ...BailoutReason[]],
51
+ }
52
+ }
53
+
54
+ export function definitionFromDeclaration(
55
+ id: string,
56
+ name: string,
57
+ declaration: AstNode,
58
+ parentOf: (node: AstNode) => AstNode | null
59
+ ): DefinitionSite {
60
+ let identifier = declaration
61
+ let initializer: AstNode | null = null
62
+ let constant = false
63
+
64
+ if (declaration.type === 'VariableDeclarator') {
65
+ identifier = childNode(declaration, 'id') ?? declaration
66
+ initializer = childNode(declaration, 'init')
67
+ const parent = parentOf(declaration)
68
+ constant = parent?.type === 'VariableDeclaration' && parent.kind === 'const'
69
+ } else if (declaration.type === 'Identifier') {
70
+ const declarator = parentOf(declaration)
71
+ if (
72
+ declarator?.type === 'VariableDeclarator' &&
73
+ childNode(declarator, 'id')?.start === declaration.start
74
+ ) {
75
+ initializer = childNode(declarator, 'init')
76
+ const parent = parentOf(declarator)
77
+ constant = parent?.type === 'VariableDeclaration' && parent.kind === 'const'
78
+ }
79
+ }
80
+
81
+ return {
82
+ id: resolvedModuleId(id),
83
+ name,
84
+ start: identifier.start,
85
+ end: identifier.end,
86
+ initializer,
87
+ constant,
88
+ }
89
+ }
90
+
91
+ interface ImportBinding {
92
+ source: string
93
+ imported: string
94
+ }
95
+
96
+ function importBinding(program: AstNode, localName: string): ImportBinding | null {
97
+ for (const statement of childNodes(program, 'body')) {
98
+ if (statement.type !== 'ImportDeclaration') continue
99
+ const source = childNode(statement, 'source')
100
+ const sourceValue = source && typeof source.value === 'string' ? source.value : null
101
+ if (!sourceValue) continue
102
+
103
+ for (const specifier of childNodes(statement, 'specifiers')) {
104
+ if (identifierName(childNode(specifier, 'local')) !== localName) continue
105
+ if (specifier.type === 'ImportDefaultSpecifier') {
106
+ return { source: sourceValue, imported: 'default' }
107
+ }
108
+ if (specifier.type === 'ImportNamespaceSpecifier') {
109
+ return { source: sourceValue, imported: '*' }
110
+ }
111
+ const imported = childNode(specifier, 'imported')
112
+ const importedName =
113
+ identifierName(imported) ??
114
+ (typeof imported?.value === 'string' ? imported.value : null)
115
+ return importedName ? { source: sourceValue, imported: importedName } : null
116
+ }
117
+ }
118
+ return null
119
+ }
120
+
121
+ function asStaticValue(node: AstNode | null): StaticValue | undefined {
122
+ if (!node) return undefined
123
+ if (node.type === 'NullLiteral') return null
124
+ if (node.type === 'Literal' || node.type.endsWith('Literal')) {
125
+ const value = node.value
126
+ if (
127
+ typeof value === 'string' ||
128
+ typeof value === 'number' ||
129
+ typeof value === 'boolean' ||
130
+ value === null
131
+ ) {
132
+ return value
133
+ }
134
+ }
135
+ return undefined
136
+ }
137
+
138
+ function elementValue(id: ResolvedModuleId, node: AstNode): ElementValue {
139
+ const staticValue = asStaticValue(node)
140
+ return staticValue === undefined
141
+ ? expressionReference(id, node)
142
+ : { kind: 'static', value: staticValue, span: spanOf(id, node) }
143
+ }
144
+
145
+ function symbolDefinition(definition: DefinitionSite | null): SymbolDefinition | null {
146
+ if (!definition) return null
147
+ return {
148
+ id: definition.id,
149
+ name: definition.name,
150
+ span: {
151
+ id: definition.id,
152
+ start: definition.start,
153
+ end: definition.end,
154
+ },
155
+ initializer: definition.initializer
156
+ ? expressionReference(definition.id, definition.initializer)
157
+ : null,
158
+ constant: definition.constant,
159
+ }
160
+ }
161
+
162
+ function componentFromNode(
163
+ candidate: AnalyzerCandidate,
164
+ id: ResolvedModuleId,
165
+ program: AstNode,
166
+ imports: readonly HostResolvedImport[],
167
+ node: AstNode,
168
+ bailouts: BailoutReason[]
169
+ ): ElementComponentIR | null {
170
+ const literal = asStaticValue(node)
171
+ if (typeof literal === 'string') {
172
+ return {
173
+ kind: 'intrinsic',
174
+ name: literal,
175
+ span: spanOf(id, node),
176
+ closingSpan: null,
177
+ definition: null,
178
+ provenance: null,
179
+ }
180
+ }
181
+
182
+ if (
183
+ (node.type === 'MemberExpression' || node.type === 'JSXMemberExpression') &&
184
+ node.computed !== true
185
+ ) {
186
+ const objectName = identifierName(childNode(node, 'object'))
187
+ const memberName = identifierName(childNode(node, 'property'))
188
+ const binding = objectName ? importBinding(program, objectName) : null
189
+ if (memberName && binding?.imported === 'html') {
190
+ const provenance = hostImportProvenance(imports, binding.source, binding.imported)
191
+ if (provenance) {
192
+ return {
193
+ kind: 'binding',
194
+ name: memberName,
195
+ span: spanOf(id, node),
196
+ closingSpan: null,
197
+ definition: null,
198
+ provenance,
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ const name = identifierName(node)
205
+ if (!name) {
206
+ bailouts.push(
207
+ localBailout(
208
+ 'local/unsupported-element-name',
209
+ spanOf(id, node),
210
+ `Element target ${node.type} is not a stable identifier or intrinsic tag`
211
+ )
212
+ )
213
+ return null
214
+ }
215
+
216
+ const intrinsic = /^[a-z]/.test(name)
217
+ const binding = intrinsic ? null : importBinding(program, name)
218
+ const provenance = binding
219
+ ? hostImportProvenance(imports, binding.source, binding.imported)
220
+ : null
221
+ const definition = intrinsic ? null : symbolDefinition(candidate.definitionOf(id, name))
222
+ if (!intrinsic && !definition && !provenance) {
223
+ bailouts.push(
224
+ linkedBailout(
225
+ 'linked/unresolved-component-binding',
226
+ spanOf(id, node),
227
+ `Component binding ${name} has no linked definition`
228
+ )
229
+ )
230
+ }
231
+ return {
232
+ kind: intrinsic ? 'intrinsic' : 'binding',
233
+ name,
234
+ span: spanOf(id, node),
235
+ closingSpan: null,
236
+ definition,
237
+ provenance,
238
+ }
239
+ }
240
+
241
+ function jsxComponentNode(opening: AstNode): AstNode | null {
242
+ return childNode(opening, 'name')
243
+ }
244
+
245
+ function jsxEntries(
246
+ id: ResolvedModuleId,
247
+ element: AstNode,
248
+ opening: AstNode,
249
+ bailouts: BailoutReason[]
250
+ ): ElementEntryIR[] {
251
+ const entries: ElementEntryIR[] = []
252
+ for (const attribute of childNodes(opening, 'attributes')) {
253
+ if (attribute.type === 'JSXSpreadAttribute') {
254
+ const argument = childNode(attribute, 'argument')
255
+ if (argument) {
256
+ entries.push({
257
+ kind: 'spread',
258
+ span: spanOf(id, attribute),
259
+ value: expressionReference(id, argument),
260
+ })
261
+ }
262
+ continue
263
+ }
264
+ if (attribute.type !== 'JSXAttribute') continue
265
+ const nameNode = childNode(attribute, 'name')
266
+ const name = identifierName(nameNode)
267
+ if (!name) {
268
+ bailouts.push(
269
+ localBailout(
270
+ 'local/unsupported-prop-key',
271
+ spanOf(id, attribute),
272
+ 'Namespaced JSX attributes are not statically supported'
273
+ )
274
+ )
275
+ continue
276
+ }
277
+
278
+ const rawValue = childNode(attribute, 'value')
279
+ let value: ElementValue
280
+ if (!rawValue) {
281
+ value = { kind: 'static', value: true, span: spanOf(id, attribute) }
282
+ } else if (rawValue.type === 'JSXExpressionContainer') {
283
+ const expression = childNode(rawValue, 'expression')
284
+ if (!expression || expression.type === 'JSXEmptyExpression') {
285
+ bailouts.push(
286
+ localBailout(
287
+ 'local/unsupported-expression',
288
+ spanOf(id, rawValue),
289
+ `JSX prop ${name} has no expression`
290
+ )
291
+ )
292
+ continue
293
+ }
294
+ value = elementValue(id, expression)
295
+ } else {
296
+ value = elementValue(id, rawValue)
297
+ }
298
+ entries.push({ kind: 'prop', name, span: spanOf(id, attribute), value })
299
+ }
300
+
301
+ for (const child of childNodes(element, 'children')) {
302
+ let value: ElementChildIR['value']
303
+ if (child.type === 'JSXText') {
304
+ if (typeof child.value !== 'string' || child.value.trim() === '') continue
305
+ value = {
306
+ kind: 'static',
307
+ value: child.value,
308
+ span: spanOf(id, child),
309
+ }
310
+ } else if (child.type === 'JSXExpressionContainer') {
311
+ const expression = childNode(child, 'expression')
312
+ value =
313
+ !expression || expression.type === 'JSXEmptyExpression'
314
+ ? { kind: 'empty', span: spanOf(id, child) }
315
+ : elementValue(id, expression)
316
+ } else if (child.type === 'JSXElement' || child.type === 'JSXFragment') {
317
+ value = { kind: 'element', span: spanOf(id, child) }
318
+ } else {
319
+ bailouts.push(
320
+ localBailout(
321
+ 'local/unsupported-child',
322
+ spanOf(id, child),
323
+ `JSX child ${child.type} cannot be normalized`
324
+ )
325
+ )
326
+ continue
327
+ }
328
+ entries.push({ kind: 'child', span: spanOf(id, child), value })
329
+ }
330
+
331
+ return entries.sort((left, right) => left.span.start - right.span.start)
332
+ }
333
+
334
+ function propertyName(property: AstNode): string | null {
335
+ if (property.computed === true) return null
336
+ const key = childNode(property, 'key')
337
+ const name = identifierName(key)
338
+ if (name) return name
339
+ const literal = asStaticValue(key)
340
+ return typeof literal === 'string' || typeof literal === 'number'
341
+ ? String(literal)
342
+ : null
343
+ }
344
+
345
+ function runtimeChildEntries(
346
+ id: ResolvedModuleId,
347
+ program: AstNode,
348
+ node: AstNode,
349
+ bailouts: BailoutReason[]
350
+ ): ElementChildIR[] {
351
+ const rawNodes = node.type === 'ArrayExpression' ? node.elements : null
352
+ if (
353
+ Array.isArray(rawNodes) &&
354
+ rawNodes.some((child) => child === null || !isAstNode(child))
355
+ ) {
356
+ bailouts.push(
357
+ localBailout(
358
+ 'local/unsupported-child',
359
+ spanOf(id, node),
360
+ 'Sparse compiled children cannot be normalized without changing order'
361
+ )
362
+ )
363
+ return []
364
+ }
365
+ const nodes = Array.isArray(rawNodes) ? rawNodes.filter(isAstNode) : [node]
366
+ return nodes.flatMap((child): ElementChildIR[] => {
367
+ if (child.type === 'SpreadElement') {
368
+ bailouts.push(
369
+ localBailout(
370
+ 'local/unsupported-child',
371
+ spanOf(id, child),
372
+ 'Spread children cannot be statically ordered'
373
+ )
374
+ )
375
+ return []
376
+ }
377
+ if (child.type === 'CallExpression' && runtimeCall(program, child)) {
378
+ return [
379
+ {
380
+ kind: 'child',
381
+ span: spanOf(id, child),
382
+ value: { kind: 'element', span: spanOf(id, child) },
383
+ },
384
+ ]
385
+ }
386
+ return [{ kind: 'child', span: spanOf(id, child), value: elementValue(id, child) }]
387
+ })
388
+ }
389
+
390
+ function objectEntries(
391
+ id: ResolvedModuleId,
392
+ program: AstNode,
393
+ object: AstNode,
394
+ bailouts: BailoutReason[]
395
+ ): ElementEntryIR[] {
396
+ const entries: ElementEntryIR[] = []
397
+ for (const property of childNodes(object, 'properties')) {
398
+ if (property.type === 'SpreadElement') {
399
+ const argument = childNode(property, 'argument')
400
+ if (argument) {
401
+ const spread: ElementSpreadIR = {
402
+ kind: 'spread',
403
+ span: spanOf(id, property),
404
+ value: expressionReference(id, argument),
405
+ }
406
+ entries.push(spread)
407
+ }
408
+ continue
409
+ }
410
+ if (property.type !== 'Property' && property.type !== 'ObjectProperty') {
411
+ bailouts.push(
412
+ localBailout(
413
+ 'local/unsupported-prop-key',
414
+ spanOf(id, property),
415
+ `Runtime props entry ${property.type} is not a data property`
416
+ )
417
+ )
418
+ continue
419
+ }
420
+ const name = propertyName(property)
421
+ const valueNode = childNode(property, 'value')
422
+ if (!name || !valueNode) {
423
+ bailouts.push(
424
+ localBailout(
425
+ 'local/unsupported-prop-key',
426
+ spanOf(id, property),
427
+ 'Computed runtime prop keys are not statically supported'
428
+ )
429
+ )
430
+ continue
431
+ }
432
+ if (name === 'children') {
433
+ entries.push(...runtimeChildEntries(id, program, valueNode, bailouts))
434
+ continue
435
+ }
436
+ const prop: ElementPropIR = {
437
+ kind: 'prop',
438
+ name,
439
+ span: spanOf(id, property),
440
+ value: elementValue(id, valueNode),
441
+ }
442
+ entries.push(prop)
443
+ }
444
+ return entries
445
+ }
446
+
447
+ type RuntimeCall = { form: 'jsx-runtime' | 'create-element' }
448
+
449
+ function runtimeCall(program: AstNode, call: AstNode): RuntimeCall | null {
450
+ const callee = childNode(call, 'callee')
451
+ if (!callee) return null
452
+ const directName = identifierName(callee)
453
+ if (directName) {
454
+ const provenance = importBinding(program, directName)
455
+ if (
456
+ provenance?.source === 'react/jsx-runtime' &&
457
+ (provenance.imported === 'jsx' || provenance.imported === 'jsxs')
458
+ ) {
459
+ return { form: 'jsx-runtime' }
460
+ }
461
+ if (provenance?.source === 'react' && provenance.imported === 'createElement') {
462
+ return { form: 'create-element' }
463
+ }
464
+ }
465
+
466
+ if (callee.type !== 'MemberExpression' || callee.computed === true) return null
467
+ const objectName = identifierName(childNode(callee, 'object'))
468
+ const propertyName = identifierName(childNode(callee, 'property'))
469
+ if (propertyName !== 'createElement' || !objectName) return null
470
+ const provenance = importBinding(program, objectName)
471
+ return provenance?.source === 'react' &&
472
+ (provenance.imported === 'default' || provenance.imported === '*')
473
+ ? { form: 'create-element' }
474
+ : null
475
+ }
476
+
477
+ function normalizeRuntimeElement(
478
+ candidate: AnalyzerCandidate,
479
+ id: ResolvedModuleId,
480
+ program: AstNode,
481
+ imports: readonly HostResolvedImport[],
482
+ call: AstNode,
483
+ runtime: RuntimeCall,
484
+ bailouts: BailoutReason[]
485
+ ): ElementIR | null {
486
+ const bailoutStart = bailouts.length
487
+ const args = childNodes(call, 'arguments')
488
+ const target = args[0]
489
+ if (!target) {
490
+ bailouts.push(
491
+ localBailout(
492
+ 'local/invalid-element-call',
493
+ spanOf(id, call),
494
+ `${runtime.form} call has no element target`
495
+ )
496
+ )
497
+ return null
498
+ }
499
+ const component = componentFromNode(candidate, id, program, imports, target, bailouts)
500
+ if (!component) return null
501
+
502
+ const entries: ElementEntryIR[] = []
503
+ const props = args[1]
504
+ if (props && asStaticValue(props) !== null) {
505
+ if (props.type === 'ObjectExpression') {
506
+ entries.push(...objectEntries(id, program, props, bailouts))
507
+ } else {
508
+ entries.push({
509
+ kind: 'spread',
510
+ span: spanOf(id, props),
511
+ value: expressionReference(id, props),
512
+ })
513
+ }
514
+ }
515
+ if (runtime.form === 'create-element') {
516
+ for (const child of args.slice(2)) {
517
+ entries.push(...runtimeChildEntries(id, program, child, bailouts))
518
+ }
519
+ }
520
+
521
+ return finalizeElement(
522
+ {
523
+ kind: 'element',
524
+ form: runtime.form,
525
+ id,
526
+ span: spanOf(id, call),
527
+ propsSpan: props ? spanOf(id, props) : null,
528
+ component,
529
+ },
530
+ entries.sort((left, right) => left.span.start - right.span.start),
531
+ bailouts.slice(bailoutStart)
532
+ )
533
+ }
534
+
535
+ function styledDefinitions(
536
+ candidate: AnalyzerCandidate,
537
+ id: ResolvedModuleId,
538
+ program: AstNode,
539
+ imports: readonly HostResolvedImport[],
540
+ bailouts: BailoutReason[]
541
+ ): StyledDefinitionIR[] {
542
+ const definitions: StyledDefinitionIR[] = []
543
+ walkAst(program, (node) => {
544
+ if (node.type !== 'VariableDeclarator') return
545
+ const identifier = childNode(node, 'id')
546
+ const name = identifierName(identifier)
547
+ const call = childNode(node, 'init')
548
+ if (!name || !identifier || call?.type !== 'CallExpression') return
549
+ const callee = childNode(call, 'callee')
550
+ const factoryName = callee && identifierName(callee)
551
+ if (!factoryName) return
552
+ const binding = importBinding(program, factoryName)
553
+ if (!binding || binding.imported !== 'styled') return
554
+ const factory = hostImportProvenance(imports, binding.source, binding.imported)
555
+ if (!factory) return
556
+
557
+ const definitionBailouts: BailoutReason[] = []
558
+ const args = childNodes(call, 'arguments')
559
+ const baseNode = args[0]
560
+ // `styled(Base, options, staticConfig?)` is the object form; the class
561
+ // string frontend form is `styled(Base, 'classes', options)`
562
+ const objectForm = args[1]?.type === 'ObjectExpression'
563
+ const optionsNode = objectForm ? args[1] : args.length >= 3 ? args[2] : args[1]
564
+ if (!baseNode || !optionsNode) {
565
+ definitionBailouts.push(
566
+ localBailout(
567
+ 'local/unsupported-styled-definition',
568
+ spanOf(id, call),
569
+ `styled definition ${name} must provide a base and options object`
570
+ )
571
+ )
572
+ bailouts.push(...definitionBailouts)
573
+ return
574
+ }
575
+ const base = componentFromNode(
576
+ candidate,
577
+ id,
578
+ program,
579
+ imports,
580
+ baseNode,
581
+ definitionBailouts
582
+ )
583
+ if (!base) {
584
+ bailouts.push(...definitionBailouts)
585
+ return
586
+ }
587
+ const baseClassNode = objectForm ? null : args.length >= 3 ? args[1] : null
588
+ const staticConfigNode = objectForm && args.length >= 3 ? args[2] : null
589
+ const baseDefinition = {
590
+ kind: 'styled-definition' as const,
591
+ id,
592
+ name,
593
+ span: spanOf(id, call),
594
+ definitionSpan: spanOf(id, identifier),
595
+ factory,
596
+ base,
597
+ baseClassName: baseClassNode ? elementValue(id, baseClassNode) : null,
598
+ options: expressionReference(id, optionsNode),
599
+ staticConfig: staticConfigNode ? expressionReference(id, staticConfigNode) : null,
600
+ }
601
+ definitions.push(
602
+ definitionBailouts.length === 0
603
+ ? { ...baseDefinition, complete: true, bailouts: [] }
604
+ : {
605
+ ...baseDefinition,
606
+ complete: false,
607
+ bailouts: definitionBailouts as [BailoutReason, ...BailoutReason[]],
608
+ }
609
+ )
610
+ bailouts.push(...definitionBailouts)
611
+ })
612
+ return definitions.sort((left, right) => left.span.start - right.span.start)
613
+ }
614
+
615
+ const DOM_STYLE_FRONTENDS = new Set([
616
+ 'tamagui/dom',
617
+ '@tamagui/core/dom',
618
+ '@tamagui/tailwind',
619
+ ])
620
+
621
+ const STYLE_FRONTENDS = new Set([
622
+ ...DOM_STYLE_FRONTENDS,
623
+ 'tamagui',
624
+ '@tamagui/core',
625
+ '@tamagui/web',
626
+ ])
627
+
628
+ function domStyleDefinitions(
629
+ id: ResolvedModuleId,
630
+ program: AstNode,
631
+ imports: readonly HostResolvedImport[]
632
+ ): DOMStyleDefinitionIR[] {
633
+ const definitions: DOMStyleDefinitionIR[] = []
634
+ walkAst(program, (node) => {
635
+ if (node.type !== 'VariableDeclarator') return
636
+ const identifier = childNode(node, 'id')
637
+ const name = identifierName(identifier)
638
+ const call = childNode(node, 'init')
639
+ if (!name || !identifier || call?.type !== 'CallExpression') return
640
+ const callee = childNode(call, 'callee')
641
+ const factoryName = callee && identifierName(callee)
642
+ if (!factoryName) return
643
+ const binding = importBinding(program, factoryName)
644
+ if (
645
+ !binding ||
646
+ binding.imported !== 'style' ||
647
+ !STYLE_FRONTENDS.has(binding.source)
648
+ ) {
649
+ return
650
+ }
651
+ const factory = hostImportProvenance(imports, binding.source, binding.imported)
652
+ const value = childNodes(call, 'arguments')[0]
653
+ if (!factory || !value) return
654
+ definitions.push({
655
+ kind: 'dom-style-definition',
656
+ id,
657
+ name,
658
+ span: spanOf(id, call),
659
+ definitionSpan: spanOf(id, identifier),
660
+ factory,
661
+ value: expressionReference(id, value),
662
+ })
663
+ })
664
+ return definitions.sort((left, right) => left.span.start - right.span.start)
665
+ }
666
+
667
+ function domStyleArgument(
668
+ candidate: AnalyzerCandidate,
669
+ id: ResolvedModuleId,
670
+ program: AstNode,
671
+ node: AstNode
672
+ ): AstNode | null {
673
+ let call = node
674
+ if (node.type === 'Identifier') {
675
+ const name = identifierName(node)
676
+ const definition = name ? candidate.definitionOf(id, name) : null
677
+ if (!definition?.constant || !definition.initializer) return null
678
+ call = definition.initializer
679
+ }
680
+ if (call.type !== 'CallExpression') return null
681
+ const callee = childNode(call, 'callee')
682
+ const factoryName = callee && identifierName(callee)
683
+ if (!factoryName) return null
684
+ const binding = importBinding(program, factoryName)
685
+ if (!binding || binding.imported !== 'style' || !STYLE_FRONTENDS.has(binding.source)) {
686
+ return null
687
+ }
688
+ return childNodes(call, 'arguments')[0] ?? null
689
+ }
690
+
691
+ function domStyleProgram(
692
+ candidate: AnalyzerCandidate,
693
+ id: ResolvedModuleId,
694
+ program: AstNode,
695
+ node: AstNode
696
+ ): DOMStyleProgramIR | null {
697
+ if (node.type !== 'ArrayExpression' || !Array.isArray(node.elements)) {
698
+ const argument = domStyleArgument(candidate, id, program, node)
699
+ return argument
700
+ ? {
701
+ kind: 'dom-style',
702
+ span: spanOf(id, node),
703
+ items: [{ condition: null, value: expressionReference(id, argument) }],
704
+ }
705
+ : null
706
+ }
707
+ const items: DOMStyleProgramIR['items'] = []
708
+ for (const rawItem of node.elements) {
709
+ if (!isAstNode(rawItem)) return null
710
+ let item = rawItem
711
+ let condition: DOMStyleProgramIR['items'][number]['condition'] = null
712
+ if (item.type === 'LogicalExpression' && item.operator === '&&') {
713
+ const left = childNode(item, 'left')
714
+ const right = childNode(item, 'right')
715
+ if (!left || !right) return null
716
+ condition = expressionReference(id, left)
717
+ item = right
718
+ }
719
+ const argument = domStyleArgument(candidate, id, program, item)
720
+ if (!argument) {
721
+ const staticValue = asStaticValue(item)
722
+ if (staticValue === false || staticValue === null) continue
723
+ return null
724
+ }
725
+ items.push({ condition, value: expressionReference(id, argument) })
726
+ }
727
+ return { kind: 'dom-style', span: spanOf(id, node), items }
728
+ }
729
+
730
+ export function normalizeElements(
731
+ candidate: AnalyzerCandidate,
732
+ rawId: string,
733
+ imports: readonly HostResolvedImport[] = []
734
+ ): ElementIRResult {
735
+ const id = resolvedModuleId(rawId)
736
+ const program = candidate.programOf(id)
737
+ const elements: ElementIR[] = []
738
+ const bailouts: BailoutReason[] = []
739
+
740
+ walkAst(program, (node) => {
741
+ if (node.type === 'JSXElement') {
742
+ const bailoutStart = bailouts.length
743
+ const opening = childNode(node, 'openingElement')
744
+ const target = opening && jsxComponentNode(opening)
745
+ if (!opening || !target) return
746
+ const component = componentFromNode(
747
+ candidate,
748
+ id,
749
+ program,
750
+ imports,
751
+ target,
752
+ bailouts
753
+ )
754
+ if (!component) return
755
+ const closingElement = childNode(node, 'closingElement')
756
+ const closingName = closingElement && childNode(closingElement, 'name')
757
+ if (closingName) component.closingSpan = spanOf(id, closingName)
758
+ const entries = jsxEntries(id, node, opening, bailouts)
759
+ for (const entry of entries) {
760
+ if (entry.kind !== 'prop' || entry.name !== 'style') continue
761
+ const raw =
762
+ entry.value.kind === 'expression'
763
+ ? nodeAtSpan(program, entry.value.start, entry.value.end)
764
+ : null
765
+ const styleProgram = raw && domStyleProgram(candidate, id, program, raw)
766
+ const argument = raw && domStyleArgument(candidate, id, program, raw)
767
+ if (styleProgram) entry.value = styleProgram
768
+ else if (argument) entry.value = expressionReference(id, argument)
769
+ }
770
+ elements.push(
771
+ finalizeElement(
772
+ {
773
+ kind: 'element',
774
+ form: 'jsx',
775
+ id,
776
+ span: spanOf(id, node),
777
+ propsSpan: spanOf(id, opening),
778
+ component,
779
+ },
780
+ entries,
781
+ bailouts.slice(bailoutStart)
782
+ )
783
+ )
784
+ return
785
+ }
786
+
787
+ if (node.type !== 'CallExpression') return
788
+ const runtime = runtimeCall(program, node)
789
+ if (!runtime) return
790
+ const element = normalizeRuntimeElement(
791
+ candidate,
792
+ id,
793
+ program,
794
+ imports,
795
+ node,
796
+ runtime,
797
+ bailouts
798
+ )
799
+ if (element) {
800
+ const entries = element.complete ? element.entries : element.bailedEntries
801
+ for (const entry of entries) {
802
+ if (entry.kind !== 'prop' || entry.name !== 'style') continue
803
+ const raw =
804
+ entry.value.kind === 'expression'
805
+ ? nodeAtSpan(program, entry.value.start, entry.value.end)
806
+ : null
807
+ const styleProgram = raw && domStyleProgram(candidate, id, program, raw)
808
+ const argument = raw && domStyleArgument(candidate, id, program, raw)
809
+ if (styleProgram) entry.value = styleProgram
810
+ else if (argument) entry.value = expressionReference(id, argument)
811
+ }
812
+ elements.push(element)
813
+ }
814
+ })
815
+
816
+ return {
817
+ elements: elements.sort((left, right) => left.span.start - right.span.start),
818
+ styledDefinitions: styledDefinitions(candidate, id, program, imports, bailouts),
819
+ domStyleDefinitions: domStyleDefinitions(id, program, imports),
820
+ bailouts: bailouts.sort((left, right) => left.span.start - right.span.start),
821
+ }
822
+ }
823
+
824
+ export function nodeAtSpan(program: AstNode, start: number, end: number): AstNode | null {
825
+ return findAstNode(program, (node) => node.start === start && node.end === end, {
826
+ start,
827
+ end,
828
+ })
829
+ }
830
+
831
+ export function asAstNode(value: unknown, label: string): AstNode {
832
+ if (!isAstNode(value)) throw new Error(`${label} is not an AST node`)
833
+ return value
834
+ }