@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
package/src/graph.ts ADDED
@@ -0,0 +1,425 @@
1
+ import { createHash } from 'node:crypto'
2
+
3
+ import type {
4
+ AnalyzerCandidate,
5
+ CandidateFactory,
6
+ ExpressionReference,
7
+ HostModuleInput,
8
+ HostResolvedImport,
9
+ HostResolvedProject,
10
+ ProjectInput,
11
+ ResolvedModuleId,
12
+ SourceSpan,
13
+ SymbolDefinition,
14
+ SymbolResolver,
15
+ } from './contracts'
16
+ import { expressionReference, resolutionKey, resolvedModuleId, spanOf } from './contracts'
17
+ import { childNode, childNodes, identifierName, unwrapExpression } from './ast'
18
+ import { linkedBailout, localBailout, type BailoutReason } from './diagnostics'
19
+ import {
20
+ evaluateBinding,
21
+ evaluateBranches,
22
+ evaluateDynamicExpression,
23
+ evaluateExpression,
24
+ type BranchDecisionNode,
25
+ type DynamicEvaluation,
26
+ type EvaluationResult,
27
+ } from './evaluate'
28
+ import type { ElementIRResult } from './ir'
29
+ import { nodeAtSpan, normalizeElements } from './normalize'
30
+
31
+ interface ModuleRecord {
32
+ input: HostModuleInput
33
+ hash: string
34
+ }
35
+
36
+ interface CachedElements {
37
+ hash: string
38
+ result: ElementIRResult
39
+ }
40
+
41
+ export interface GraphInvalidation {
42
+ changed: boolean
43
+ id: ResolvedModuleId
44
+ previousHash: string | null
45
+ contentHash: string | null
46
+ invalidatedIds: ResolvedModuleId[]
47
+ }
48
+
49
+ function compareIds(left: ResolvedModuleId, right: ResolvedModuleId): number {
50
+ return compareCodeUnits(left, right)
51
+ }
52
+
53
+ function compareCodeUnits(left: string, right: string): number {
54
+ return left < right ? -1 : left > right ? 1 : 0
55
+ }
56
+
57
+ function stableImports(imports: readonly HostResolvedImport[]): HostResolvedImport[] {
58
+ return [...imports].sort((left, right) => {
59
+ const bySpecifier = compareCodeUnits(left.specifier, right.specifier)
60
+ return bySpecifier || compareCodeUnits(left.resolvedId, right.resolvedId)
61
+ })
62
+ }
63
+
64
+ export function moduleContentHash(input: HostModuleInput): string {
65
+ const hash = createHash('sha256')
66
+ hash.update(input.source)
67
+ hash.update('\0')
68
+ for (const dependency of stableImports(input.imports)) {
69
+ hash.update(dependency.specifier)
70
+ hash.update('\0')
71
+ hash.update(dependency.resolvedId)
72
+ hash.update('\0')
73
+ hash.update(dependency.external ? 'external' : 'internal')
74
+ hash.update('\0')
75
+ }
76
+ return hash.digest('hex')
77
+ }
78
+
79
+ /**
80
+ * Long-lived semantic graph. The host owns resolution and supplies every canonical id;
81
+ * this service owns parsing, linking, hashes, reverse edges, IR, and evaluation caches.
82
+ */
83
+ export class ProjectGraph implements SymbolResolver {
84
+ readonly #files = new Map<string, string>()
85
+ readonly #resolutions = new Map<string, string>()
86
+ readonly #modules = new Map<ResolvedModuleId, ModuleRecord>()
87
+ readonly #dependencies = new Map<ResolvedModuleId, Set<ResolvedModuleId>>()
88
+ readonly #dependents = new Map<ResolvedModuleId, Set<ResolvedModuleId>>()
89
+ readonly #elementCache = new Map<ResolvedModuleId, CachedElements>()
90
+ readonly #candidate: AnalyzerCandidate
91
+
92
+ constructor(factory: CandidateFactory, project: HostResolvedProject) {
93
+ for (const input of project.modules) this.#installHostRecord(input)
94
+ const candidateProject: ProjectInput = {
95
+ files: this.#files,
96
+ resolutions: this.#resolutions,
97
+ }
98
+ this.#candidate = factory.create(candidateProject)
99
+ this.#candidate.link()
100
+ }
101
+
102
+ moduleIds(): ResolvedModuleId[] {
103
+ return [...this.#modules.keys()].sort(compareIds)
104
+ }
105
+
106
+ contentHash(id: ResolvedModuleId): string | null {
107
+ return this.#modules.get(id)?.hash ?? null
108
+ }
109
+
110
+ sourceOf(id: ResolvedModuleId): string {
111
+ const source = this.#modules.get(id)?.input.source
112
+ if (source === undefined) throw new Error(`Unknown host module ${id}`)
113
+ return source
114
+ }
115
+
116
+ dependenciesOf(id: ResolvedModuleId): ResolvedModuleId[] {
117
+ return [...(this.#dependencies.get(id) ?? [])].sort(compareIds)
118
+ }
119
+
120
+ dependentsOf(id: ResolvedModuleId): ResolvedModuleId[] {
121
+ return [...(this.#dependents.get(id) ?? [])].sort(compareIds)
122
+ }
123
+
124
+ /** resolved ids a module imports that the graph does not own (package entries) */
125
+ externalImportsOf(id: ResolvedModuleId): ResolvedModuleId[] {
126
+ const record = this.#modules.get(id)
127
+ if (!record) return []
128
+ const ids = new Set<ResolvedModuleId>()
129
+ for (const dependency of record.input.imports) {
130
+ if (dependency.external) ids.add(dependency.resolvedId)
131
+ }
132
+ return [...ids].sort(compareIds)
133
+ }
134
+
135
+ affectedBy(id: ResolvedModuleId): ResolvedModuleId[] {
136
+ const affected = new Set<ResolvedModuleId>([id])
137
+ const queue = [id]
138
+ while (queue.length) {
139
+ const current = queue.shift()!
140
+ for (const dependent of this.#dependents.get(current) ?? []) {
141
+ if (affected.has(dependent)) continue
142
+ affected.add(dependent)
143
+ queue.push(dependent)
144
+ }
145
+ }
146
+ return [...affected].sort(compareIds)
147
+ }
148
+
149
+ parseCount(id: ResolvedModuleId): number {
150
+ return this.#candidate.parseCount(id)
151
+ }
152
+
153
+ resolveBinding(id: ResolvedModuleId, localName: string): SymbolDefinition | null {
154
+ const definition = this.#candidate.definitionOf(id, localName)
155
+ if (!definition) return null
156
+ return {
157
+ id: definition.id,
158
+ name: definition.name,
159
+ span: {
160
+ id: definition.id,
161
+ start: definition.start,
162
+ end: definition.end,
163
+ },
164
+ initializer: definition.initializer
165
+ ? expressionReference(definition.id, definition.initializer)
166
+ : null,
167
+ constant: definition.constant,
168
+ }
169
+ }
170
+
171
+ resolveReference(reference: ExpressionReference): SymbolDefinition | null {
172
+ const definition = this.#candidate.definitionAt(
173
+ reference.id,
174
+ reference.start,
175
+ reference.end
176
+ )
177
+ if (!definition) return null
178
+ return {
179
+ id: definition.id,
180
+ name: definition.name,
181
+ span: {
182
+ id: definition.id,
183
+ start: definition.start,
184
+ end: definition.end,
185
+ },
186
+ initializer: definition.initializer
187
+ ? expressionReference(definition.id, definition.initializer)
188
+ : null,
189
+ constant: definition.constant,
190
+ }
191
+ }
192
+
193
+ expressionNode(reference: ExpressionReference) {
194
+ if (!this.#modules.has(reference.id)) return null
195
+ return nodeAtSpan(
196
+ this.#candidate.programOf(reference.id),
197
+ reference.start,
198
+ reference.end
199
+ )
200
+ }
201
+
202
+ /**
203
+ * The members of a plain object literal, each as its own expression:
204
+ * `{ width: expr, height: 10 }`. Null for anything else, and for an object
205
+ * that spreads, computes a key, or defines a method or accessor.
206
+ */
207
+ objectMembers(
208
+ reference: ExpressionReference
209
+ ): { name: string; span: SourceSpan; value: ExpressionReference }[] | null {
210
+ const node = this.expressionNode(reference)
211
+ if (!node) return null
212
+ const object = unwrapExpression(node)
213
+ if (object.type !== 'ObjectExpression') return null
214
+ const members: { name: string; span: SourceSpan; value: ExpressionReference }[] = []
215
+ for (const property of childNodes(object, 'properties')) {
216
+ if (
217
+ (property.type !== 'Property' && property.type !== 'ObjectProperty') ||
218
+ property.computed === true ||
219
+ property.method === true ||
220
+ (property.kind !== undefined && property.kind !== 'init')
221
+ ) {
222
+ return null
223
+ }
224
+ const key = childNode(property, 'key')
225
+ const value = childNode(property, 'value')
226
+ if (!key || !value) return null
227
+ const name =
228
+ identifierName(key) ?? (typeof key.value === 'string' ? key.value : null)
229
+ if (name === null) return null
230
+ members.push({
231
+ name,
232
+ span: spanOf(reference.id, property),
233
+ value: expressionReference(reference.id, value),
234
+ })
235
+ }
236
+ return members
237
+ }
238
+
239
+ evaluate(reference: ExpressionReference): EvaluationResult {
240
+ return evaluateExpression(this, reference)
241
+ }
242
+
243
+ evaluateBranches(reference: ExpressionReference): BranchDecisionNode | null {
244
+ return evaluateBranches(this, reference)
245
+ }
246
+
247
+ evaluateDynamic(reference: ExpressionReference): DynamicEvaluation | null {
248
+ return evaluateDynamicExpression(this, reference)
249
+ }
250
+
251
+ evaluateBinding(id: ResolvedModuleId, localName: string): EvaluationResult {
252
+ return evaluateBinding(this, id, localName)
253
+ }
254
+
255
+ elementsOf(id: ResolvedModuleId): ElementIRResult {
256
+ const record = this.#modules.get(id)
257
+ if (!record) throw new Error(`Unknown host module ${id}`)
258
+ const cached = this.#elementCache.get(id)
259
+ if (cached?.hash === record.hash) return cached.result
260
+ const result = normalizeElements(this.#candidate, id, record.input.imports)
261
+ this.#elementCache.set(id, { hash: record.hash, result })
262
+ return result
263
+ }
264
+
265
+ diagnostics(): BailoutReason[] {
266
+ const diagnostics = this.#candidate
267
+ .diagnostics()
268
+ .map((diagnostic) =>
269
+ diagnostic.kind === 'parse'
270
+ ? localBailout(
271
+ 'local/parse-error',
272
+ { id: diagnostic.id, start: 0, end: 0 },
273
+ diagnostic.message
274
+ )
275
+ : linkedBailout(
276
+ 'linked/unresolved-binding',
277
+ { id: diagnostic.id, start: 0, end: 0 },
278
+ diagnostic.message
279
+ )
280
+ )
281
+ for (const [id, record] of this.#modules) {
282
+ for (const dependency of record.input.imports) {
283
+ if (dependency.external) continue
284
+ if (this.#modules.has(dependency.resolvedId)) continue
285
+ diagnostics.push(
286
+ linkedBailout(
287
+ 'linked/unresolved-import',
288
+ { id, start: 0, end: 0 },
289
+ `Host-resolved import ${dependency.specifier} points outside the project graph`,
290
+ dependency.resolvedId
291
+ )
292
+ )
293
+ }
294
+ }
295
+ return diagnostics.sort((left, right) => {
296
+ const byId = compareCodeUnits(left.span.id, right.span.id)
297
+ return (
298
+ byId ||
299
+ left.span.start - right.span.start ||
300
+ compareCodeUnits(left.code, right.code)
301
+ )
302
+ })
303
+ }
304
+
305
+ updateModule(input: HostModuleInput): GraphInvalidation {
306
+ const previous = this.#modules.get(input.id)
307
+ const nextHash = moduleContentHash(input)
308
+ if (previous?.hash === nextHash) {
309
+ return {
310
+ changed: false,
311
+ id: input.id,
312
+ previousHash: previous.hash,
313
+ contentHash: previous.hash,
314
+ invalidatedIds: [],
315
+ }
316
+ }
317
+
318
+ const invalidatedIds = this.affectedBy(input.id)
319
+ const previousHash = previous?.hash ?? null
320
+ if (previous) this.#candidate.removeFile(input.id)
321
+ this.#installHostRecord(input)
322
+ this.#candidate.addFile(input.id, input.source)
323
+ this.#candidate.link()
324
+ for (const id of invalidatedIds) this.#elementCache.delete(id)
325
+
326
+ return {
327
+ changed: true,
328
+ id: input.id,
329
+ previousHash,
330
+ contentHash: nextHash,
331
+ invalidatedIds,
332
+ }
333
+ }
334
+
335
+ removeModule(id: ResolvedModuleId): GraphInvalidation {
336
+ const previous = this.#modules.get(id)
337
+ if (!previous) {
338
+ return {
339
+ changed: false,
340
+ id,
341
+ previousHash: null,
342
+ contentHash: null,
343
+ invalidatedIds: [],
344
+ }
345
+ }
346
+ const invalidatedIds = this.affectedBy(id)
347
+ this.#candidate.removeFile(id)
348
+ this.#removeOutgoingEdges(id)
349
+ this.#removeResolutions(id)
350
+ this.#modules.delete(id)
351
+ this.#files.delete(id)
352
+ this.#candidate.link()
353
+ for (const invalidated of invalidatedIds) this.#elementCache.delete(invalidated)
354
+ return {
355
+ changed: true,
356
+ id,
357
+ previousHash: previous.hash,
358
+ contentHash: null,
359
+ invalidatedIds,
360
+ }
361
+ }
362
+
363
+ #installHostRecord(input: HostModuleInput): void {
364
+ resolvedModuleId(input.id)
365
+ const seenSpecifiers = new Set<string>()
366
+ for (const dependency of input.imports) {
367
+ resolvedModuleId(dependency.resolvedId)
368
+ if (seenSpecifiers.has(dependency.specifier)) {
369
+ throw new Error(
370
+ `Duplicate host resolution for ${input.id}:${dependency.specifier}`
371
+ )
372
+ }
373
+ seenSpecifiers.add(dependency.specifier)
374
+ }
375
+
376
+ this.#removeOutgoingEdges(input.id)
377
+ this.#removeResolutions(input.id)
378
+ const dependencies = new Set(
379
+ input.imports
380
+ .filter(({ external }) => !external)
381
+ .map(({ resolvedId }) => resolvedId)
382
+ )
383
+ this.#dependencies.set(input.id, dependencies)
384
+ for (const dependency of dependencies) {
385
+ let reverse = this.#dependents.get(dependency)
386
+ if (!reverse) {
387
+ reverse = new Set()
388
+ this.#dependents.set(dependency, reverse)
389
+ }
390
+ reverse.add(input.id)
391
+ }
392
+ for (const dependency of input.imports) {
393
+ if (dependency.external) continue
394
+ this.#resolutions.set(
395
+ resolutionKey(input.id, dependency.specifier),
396
+ dependency.resolvedId
397
+ )
398
+ }
399
+ const installedInput: HostModuleInput = {
400
+ ...input,
401
+ imports: input.imports.map((dependency) => ({ ...dependency })),
402
+ }
403
+ this.#files.set(installedInput.id, installedInput.source)
404
+ this.#modules.set(installedInput.id, {
405
+ input: installedInput,
406
+ hash: moduleContentHash(installedInput),
407
+ })
408
+ }
409
+
410
+ #removeOutgoingEdges(id: ResolvedModuleId): void {
411
+ for (const dependency of this.#dependencies.get(id) ?? []) {
412
+ const reverse = this.#dependents.get(dependency)
413
+ reverse?.delete(id)
414
+ if (reverse?.size === 0) this.#dependents.delete(dependency)
415
+ }
416
+ this.#dependencies.delete(id)
417
+ }
418
+
419
+ #removeResolutions(id: ResolvedModuleId): void {
420
+ const prefix = `${id}\0`
421
+ for (const key of this.#resolutions.keys()) {
422
+ if (key.startsWith(prefix)) this.#resolutions.delete(key)
423
+ }
424
+ }
425
+ }
package/src/hash.ts ADDED
@@ -0,0 +1,36 @@
1
+ import { createHash } from 'node:crypto'
2
+
3
+ /** The one content hash every compiler cache identity is built from. */
4
+ export function contentHash(value: string | Uint8Array): string {
5
+ return createHash('sha256').update(value).digest('hex')
6
+ }
7
+
8
+ function compareCodeUnits(left: string, right: string): number {
9
+ return left < right ? -1 : left > right ? 1 : 0
10
+ }
11
+
12
+ function stringify(value: unknown): string | undefined {
13
+ if (Array.isArray(value)) {
14
+ return `[${value.map((child) => stringify(child) ?? 'null').join(',')}]`
15
+ }
16
+ if (value && typeof value === 'object') {
17
+ const entries = Object.entries(value as Record<string, unknown>)
18
+ .sort(([left], [right]) => compareCodeUnits(left, right))
19
+ .map(([key, child]) => [key, stringify(child)] as const)
20
+ .filter((entry): entry is readonly [string, string] => entry[1] !== undefined)
21
+ return `{${entries
22
+ .map(([key, child]) => `${JSON.stringify(key)}:${child}`)
23
+ .join(',')}}`
24
+ }
25
+ return JSON.stringify(value)
26
+ }
27
+
28
+ /**
29
+ * Key-order-independent JSON. Cache identities are hashed from this, so two
30
+ * objects that differ only in property order must serialize identically.
31
+ */
32
+ export function stableStringify(value: unknown): string {
33
+ const serialized = stringify(value)
34
+ if (serialized === undefined) throw new Error('Cannot serialize undefined cache root')
35
+ return serialized
36
+ }
package/src/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ export * from './ast'
2
+ export * from './contracts'
3
+ export * from './diagnostics'
4
+ export * from './evaluate'
5
+ export * from './graph'
6
+ export * from './hash'
7
+ export * from './ir'
8
+ export * from './lower'
9
+ export * from './materialize'
10
+ export * from './output'
11
+ export * from './planCache'
12
+ export * from './session'
13
+ export { normalizeElements } from './normalize'
14
+ export * from './zero'
15
+ export { parseModuleAst, yukuFactory } from './yuku'
package/src/ir.ts ADDED
@@ -0,0 +1,156 @@
1
+ import type {
2
+ ExpressionReference,
3
+ HostResolvedImport,
4
+ ResolvedModuleId,
5
+ SourceSpan,
6
+ SymbolDefinition,
7
+ } from './contracts'
8
+ import type { BailoutReason } from './diagnostics'
9
+
10
+ export type ElementForm = 'jsx' | 'jsx-runtime' | 'create-element'
11
+
12
+ export type StaticValue = string | number | boolean | null
13
+
14
+ export type ElementValue =
15
+ | { kind: 'static'; value: StaticValue; span: SourceSpan }
16
+ | ExpressionReference
17
+ | DOMStyleProgramIR
18
+
19
+ export interface DOMStyleProgramIR {
20
+ kind: 'dom-style'
21
+ span: SourceSpan
22
+ items: {
23
+ condition: ExpressionReference | null
24
+ value: ExpressionReference
25
+ }[]
26
+ }
27
+
28
+ export interface ComponentImportProvenance {
29
+ specifier: string
30
+ importedName: string
31
+ resolvedId: ResolvedModuleId
32
+ external: boolean
33
+ }
34
+
35
+ export interface ElementComponentIR {
36
+ kind: 'binding' | 'intrinsic'
37
+ name: string
38
+ span: SourceSpan
39
+ closingSpan: SourceSpan | null
40
+ definition: SymbolDefinition | null
41
+ provenance: ComponentImportProvenance | null
42
+ }
43
+
44
+ export interface ElementPropIR {
45
+ kind: 'prop'
46
+ name: string
47
+ span: SourceSpan
48
+ value: ElementValue
49
+ }
50
+
51
+ export interface ElementSpreadIR {
52
+ kind: 'spread'
53
+ span: SourceSpan
54
+ value: ExpressionReference
55
+ }
56
+
57
+ export interface ElementChildIR {
58
+ kind: 'child'
59
+ span: SourceSpan
60
+ value:
61
+ | ElementValue
62
+ | { kind: 'element'; span: SourceSpan }
63
+ | { kind: 'empty'; span: SourceSpan }
64
+ }
65
+
66
+ /**
67
+ * Entries remain in source order. Consumers must apply duplicate props in this order;
68
+ * the last statically evaluated value wins exactly as it does at runtime.
69
+ */
70
+ export type ElementEntryIR = ElementPropIR | ElementSpreadIR | ElementChildIR
71
+
72
+ export interface ElementIRBase {
73
+ kind: 'element'
74
+ form: ElementForm
75
+ id: ResolvedModuleId
76
+ span: SourceSpan
77
+ /** JSX opening element or compiled props argument, used for punctuation-safe edits. */
78
+ propsSpan: SourceSpan | null
79
+ component: ElementComponentIR
80
+ }
81
+
82
+ export interface CompleteElementIR extends ElementIRBase {
83
+ complete: true
84
+ entries: ElementEntryIR[]
85
+ bailouts: readonly []
86
+ }
87
+
88
+ export interface IncompleteElementIR extends ElementIRBase {
89
+ complete: false
90
+ /** Preserved only for diagnostics; incomplete entries must never be optimized. */
91
+ bailedEntries: ElementEntryIR[]
92
+ bailouts: readonly [BailoutReason, ...BailoutReason[]]
93
+ }
94
+
95
+ export type ElementIR = CompleteElementIR | IncompleteElementIR
96
+
97
+ export interface StyledDefinitionIRBase {
98
+ kind: 'styled-definition'
99
+ id: ResolvedModuleId
100
+ name: string
101
+ span: SourceSpan
102
+ definitionSpan: SourceSpan
103
+ factory: ComponentImportProvenance
104
+ base: ElementComponentIR
105
+ baseClassName: ElementValue | null
106
+ options: ExpressionReference
107
+ /** the object form's third argument, `styled(Base, options, staticConfig)` */
108
+ staticConfig: ExpressionReference | null
109
+ }
110
+
111
+ export interface CompleteStyledDefinitionIR extends StyledDefinitionIRBase {
112
+ complete: true
113
+ bailouts: readonly []
114
+ }
115
+
116
+ export interface IncompleteStyledDefinitionIR extends StyledDefinitionIRBase {
117
+ complete: false
118
+ bailouts: readonly [BailoutReason, ...BailoutReason[]]
119
+ }
120
+
121
+ export type StyledDefinitionIR = CompleteStyledDefinitionIR | IncompleteStyledDefinitionIR
122
+
123
+ /** A compile-time style handle declared by a Tamagui style frontend. */
124
+ export interface DOMStyleDefinitionIR {
125
+ kind: 'dom-style-definition'
126
+ id: ResolvedModuleId
127
+ name: string
128
+ /** Span of the complete style(...) call. */
129
+ span: SourceSpan
130
+ definitionSpan: SourceSpan
131
+ factory: ComponentImportProvenance
132
+ value: ExpressionReference
133
+ }
134
+
135
+ export interface ElementIRResult {
136
+ elements: ElementIR[]
137
+ styledDefinitions: StyledDefinitionIR[]
138
+ domStyleDefinitions: DOMStyleDefinitionIR[]
139
+ bailouts: BailoutReason[]
140
+ }
141
+
142
+ export function hostImportProvenance(
143
+ imports: readonly HostResolvedImport[],
144
+ specifier: string,
145
+ importedName: string
146
+ ): ComponentImportProvenance | null {
147
+ const resolution = imports.find((candidate) => candidate.specifier === specifier)
148
+ return resolution
149
+ ? {
150
+ specifier,
151
+ importedName,
152
+ resolvedId: resolution.resolvedId,
153
+ external: resolution.external === true,
154
+ }
155
+ : null
156
+ }