@tamagui/static 2.0.0-rc.3 → 2.0.0-rc.30

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 (140) hide show
  1. package/dist/checkDeps.cjs +164 -31
  2. package/dist/exports.cjs +3 -0
  3. package/dist/extractor/bundle.cjs +72 -35
  4. package/dist/extractor/bundleConfig.cjs +219 -35
  5. package/dist/extractor/createExtractor.cjs +170 -28
  6. package/dist/extractor/detectModuleFormat.cjs +49 -0
  7. package/dist/extractor/esbuildTsconfigPaths.cjs +3 -1
  8. package/dist/extractor/extractToClassNames.cjs +7 -5
  9. package/dist/extractor/extractToNative.cjs +7 -8
  10. package/dist/extractor/loadTamagui.cjs +1 -1
  11. package/dist/getPragmaOptions.cjs +7 -3
  12. package/dist/index.js +26 -16
  13. package/dist/registerRequire.cjs +23 -14
  14. package/package.json +26 -22
  15. package/src/checkDeps.ts +305 -68
  16. package/src/exports.ts +1 -0
  17. package/src/extractor/bundle.ts +140 -37
  18. package/src/extractor/bundleConfig.ts +435 -61
  19. package/src/extractor/createExtractor.ts +261 -41
  20. package/src/extractor/detectModuleFormat.ts +42 -0
  21. package/src/extractor/esbuildTsconfigPaths.ts +6 -1
  22. package/src/extractor/extractToClassNames.ts +15 -9
  23. package/src/extractor/extractToNative.ts +32 -25
  24. package/src/extractor/loadTamagui.ts +2 -2
  25. package/src/getPragmaOptions.ts +6 -1
  26. package/src/registerRequire.ts +40 -8
  27. package/types/checkDeps.d.ts.map +1 -1
  28. package/types/exports.d.ts +1 -0
  29. package/types/exports.d.ts.map +1 -1
  30. package/types/extractor/bundle.d.ts +83 -1
  31. package/types/extractor/bundle.d.ts.map +1 -1
  32. package/types/extractor/bundleConfig.d.ts +15 -2
  33. package/types/extractor/bundleConfig.d.ts.map +1 -1
  34. package/types/extractor/createExtractor.d.ts.map +1 -1
  35. package/types/extractor/detectModuleFormat.d.ts +5 -0
  36. package/types/extractor/detectModuleFormat.d.ts.map +1 -0
  37. package/types/extractor/esbuildTsconfigPaths.d.ts +8 -0
  38. package/types/extractor/esbuildTsconfigPaths.d.ts.map +1 -1
  39. package/types/extractor/extractToClassNames.d.ts.map +1 -1
  40. package/types/extractor/extractToNative.d.ts.map +1 -1
  41. package/types/getPragmaOptions.d.ts.map +1 -1
  42. package/types/registerRequire.d.ts.map +1 -1
  43. package/dist/check-dep-versions.js +0 -389
  44. package/dist/check-dep-versions.js.map +0 -6
  45. package/dist/checkDeps.js +0 -60
  46. package/dist/checkDeps.js.map +0 -6
  47. package/dist/constants.js +0 -34
  48. package/dist/constants.js.map +0 -6
  49. package/dist/exports.js +0 -34
  50. package/dist/exports.js.map +0 -6
  51. package/dist/extractor/accessSafe.js +0 -47
  52. package/dist/extractor/accessSafe.js.map +0 -6
  53. package/dist/extractor/babelParse.js +0 -59
  54. package/dist/extractor/babelParse.js.map +0 -6
  55. package/dist/extractor/buildClassName.js +0 -72
  56. package/dist/extractor/buildClassName.js.map +0 -6
  57. package/dist/extractor/bundle.js +0 -135
  58. package/dist/extractor/bundle.js.map +0 -6
  59. package/dist/extractor/bundleConfig.js +0 -352
  60. package/dist/extractor/bundleConfig.js.map +0 -6
  61. package/dist/extractor/concatClassName.js +0 -69
  62. package/dist/extractor/concatClassName.js.map +0 -6
  63. package/dist/extractor/createEvaluator.js +0 -66
  64. package/dist/extractor/createEvaluator.js.map +0 -6
  65. package/dist/extractor/createExtractor.js +0 -1215
  66. package/dist/extractor/createExtractor.js.map +0 -6
  67. package/dist/extractor/createLogger.js +0 -32
  68. package/dist/extractor/createLogger.js.map +0 -6
  69. package/dist/extractor/ensureImportingConcat.js +0 -50
  70. package/dist/extractor/ensureImportingConcat.js.map +0 -6
  71. package/dist/extractor/errors.js +0 -22
  72. package/dist/extractor/errors.js.map +0 -6
  73. package/dist/extractor/esbuildAliasPlugin.js +0 -36
  74. package/dist/extractor/esbuildAliasPlugin.js.map +0 -6
  75. package/dist/extractor/esbuildTsconfigPaths.js +0 -79
  76. package/dist/extractor/esbuildTsconfigPaths.js.map +0 -6
  77. package/dist/extractor/evaluateAstNode.js +0 -99
  78. package/dist/extractor/evaluateAstNode.js.map +0 -6
  79. package/dist/extractor/extractHelpers.js +0 -108
  80. package/dist/extractor/extractHelpers.js.map +0 -6
  81. package/dist/extractor/extractMediaStyle.js +0 -123
  82. package/dist/extractor/extractMediaStyle.js.map +0 -6
  83. package/dist/extractor/extractToClassNames.js +0 -351
  84. package/dist/extractor/extractToClassNames.js.map +0 -6
  85. package/dist/extractor/extractToNative.js +0 -286
  86. package/dist/extractor/extractToNative.js.map +0 -6
  87. package/dist/extractor/findTopmostFunction.js +0 -32
  88. package/dist/extractor/findTopmostFunction.js.map +0 -6
  89. package/dist/extractor/generatedUid.js +0 -42
  90. package/dist/extractor/generatedUid.js.map +0 -6
  91. package/dist/extractor/getPrefixLogs.js +0 -24
  92. package/dist/extractor/getPrefixLogs.js.map +0 -6
  93. package/dist/extractor/getPropValueFromAttributes.js +0 -65
  94. package/dist/extractor/getPropValueFromAttributes.js.map +0 -6
  95. package/dist/extractor/getSourceModule.js +0 -62
  96. package/dist/extractor/getSourceModule.js.map +0 -6
  97. package/dist/extractor/getStaticBindingsForScope.js +0 -145
  98. package/dist/extractor/getStaticBindingsForScope.js.map +0 -6
  99. package/dist/extractor/getTamaguiConfigPathFromOptionsConfig.js +0 -32
  100. package/dist/extractor/getTamaguiConfigPathFromOptionsConfig.js.map +0 -6
  101. package/dist/extractor/hoistClassNames.js +0 -63
  102. package/dist/extractor/hoistClassNames.js.map +0 -6
  103. package/dist/extractor/literalToAst.js +0 -90
  104. package/dist/extractor/literalToAst.js.map +0 -6
  105. package/dist/extractor/loadFile.js +0 -14
  106. package/dist/extractor/loadFile.js.map +0 -6
  107. package/dist/extractor/loadTamagui.js +0 -306
  108. package/dist/extractor/loadTamagui.js.map +0 -6
  109. package/dist/extractor/logLines.js +0 -30
  110. package/dist/extractor/logLines.js.map +0 -6
  111. package/dist/extractor/normalizeTernaries.js +0 -61
  112. package/dist/extractor/normalizeTernaries.js.map +0 -6
  113. package/dist/extractor/propsToFontFamilyCache.js +0 -33
  114. package/dist/extractor/propsToFontFamilyCache.js.map +0 -6
  115. package/dist/extractor/regenerateConfig.js +0 -123
  116. package/dist/extractor/regenerateConfig.js.map +0 -6
  117. package/dist/extractor/removeUnusedHooks.js +0 -72
  118. package/dist/extractor/removeUnusedHooks.js.map +0 -6
  119. package/dist/extractor/timer.js +0 -38
  120. package/dist/extractor/timer.js.map +0 -6
  121. package/dist/extractor/validHTMLAttributes.js +0 -72
  122. package/dist/extractor/validHTMLAttributes.js.map +0 -6
  123. package/dist/extractor/watchTamaguiConfig.js +0 -57
  124. package/dist/extractor/watchTamaguiConfig.js.map +0 -6
  125. package/dist/getPragmaOptions.js +0 -46
  126. package/dist/getPragmaOptions.js.map +0 -6
  127. package/dist/helpers/memoize.js +0 -33
  128. package/dist/helpers/memoize.js.map +0 -6
  129. package/dist/helpers/requireTamaguiCore.js +0 -30
  130. package/dist/helpers/requireTamaguiCore.js.map +0 -6
  131. package/dist/registerRequire.js +0 -100
  132. package/dist/registerRequire.js.map +0 -6
  133. package/dist/server.js +0 -58
  134. package/dist/server.js.map +0 -6
  135. package/dist/setup.js +0 -1
  136. package/dist/setup.js.map +0 -6
  137. package/dist/types.js +0 -14
  138. package/dist/types.js.map +0 -6
  139. package/dist/worker.js +0 -72
  140. package/dist/worker.js.map +0 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamagui/static",
3
- "version": "2.0.0-rc.3",
3
+ "version": "2.0.0-rc.30",
4
4
  "gitHead": "a49cc7ea6b93ba384e77a4880ae48ac4a5635c14",
5
5
  "license": "MIT",
6
6
  "source": "src/index.ts",
@@ -16,15 +16,19 @@
16
16
  "./package.json": "./package.json",
17
17
  ".": {
18
18
  "types": "./types/index.d.ts",
19
- "require": "./dist/index.cjs",
20
- "module": "./dist/index.cjs",
21
- "import": "./dist/index.cjs"
19
+ "default": "./dist/index.cjs"
22
20
  },
23
21
  "./worker": {
24
22
  "types": "./types/worker.d.ts",
25
- "require": "./dist/worker.cjs",
26
- "module": "./dist/worker.cjs",
27
- "import": "./dist/worker.cjs"
23
+ "default": "./dist/worker.cjs"
24
+ },
25
+ "./checkDeps": {
26
+ "types": "./types/checkDeps.d.ts",
27
+ "default": "./dist/checkDeps.cjs"
28
+ },
29
+ "./loadTamagui": {
30
+ "types": "./types/extractor/loadTamagui.d.ts",
31
+ "default": "./dist/extractor/loadTamagui.cjs"
28
32
  }
29
33
  },
30
34
  "publishConfig": {
@@ -46,19 +50,19 @@
46
50
  "@babel/template": "^7.25.0",
47
51
  "@babel/traverse": "^7.25.4",
48
52
  "@babel/types": "^7.25.4",
49
- "@tamagui/cli-color": "2.0.0-rc.3",
50
- "@tamagui/config-default": "2.0.0-rc.3",
51
- "@tamagui/core": "2.0.0-rc.3",
52
- "@tamagui/fake-react-native": "2.0.0-rc.3",
53
- "@tamagui/generate-themes": "2.0.0-rc.3",
54
- "@tamagui/helpers": "2.0.0-rc.3",
55
- "@tamagui/helpers-node": "2.0.0-rc.3",
56
- "@tamagui/proxy-worm": "2.0.0-rc.3",
57
- "@tamagui/react-native-web-internals": "2.0.0-rc.3",
58
- "@tamagui/react-native-web-lite": "2.0.0-rc.3",
59
- "@tamagui/shorthands": "2.0.0-rc.3",
60
- "@tamagui/types": "2.0.0-rc.3",
61
- "@tamagui/web": "2.0.0-rc.3",
53
+ "@tamagui/cli-color": "2.0.0-rc.30",
54
+ "@tamagui/config-default": "2.0.0-rc.30",
55
+ "@tamagui/core": "2.0.0-rc.30",
56
+ "@tamagui/fake-react-native": "2.0.0-rc.30",
57
+ "@tamagui/generate-themes": "2.0.0-rc.30",
58
+ "@tamagui/helpers": "2.0.0-rc.30",
59
+ "@tamagui/helpers-node": "2.0.0-rc.30",
60
+ "@tamagui/proxy-worm": "2.0.0-rc.30",
61
+ "@tamagui/react-native-web-internals": "2.0.0-rc.30",
62
+ "@tamagui/react-native-web-lite": "2.0.0-rc.30",
63
+ "@tamagui/shorthands": "2.0.0-rc.30",
64
+ "@tamagui/types": "2.0.0-rc.30",
65
+ "@tamagui/web": "2.0.0-rc.30",
62
66
  "babel-literal-to-ast": "^2.1.0",
63
67
  "browserslist": "^4.28.1",
64
68
  "check-dependency-version-consistency": "^4.1.0",
@@ -75,7 +79,7 @@
75
79
  },
76
80
  "devDependencies": {
77
81
  "@babel/plugin-syntax-typescript": "^7.25.4",
78
- "@tamagui/build": "2.0.0-rc.3",
82
+ "@tamagui/build": "2.0.0-rc.30",
79
83
  "@types/babel__core": "^7.20.5",
80
84
  "@types/find-root": "^1.1.2",
81
85
  "@types/node": "^22.1.0",
@@ -84,7 +88,7 @@
84
88
  "esbuild-loader": "^4.2.2",
85
89
  "null-loader": "^4.0.1",
86
90
  "react": ">=19",
87
- "react-native": "0.81.5",
91
+ "react-native": "0.83.2",
88
92
  "typescript": "~5.9.2"
89
93
  },
90
94
  "peerDependencies": {
package/src/checkDeps.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { existsSync, readFileSync } from 'node:fs'
2
- import { join } from 'node:path'
1
+ import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs'
2
+ import { join, relative } from 'node:path'
3
3
  import { CDVC } from './check-dep-versions'
4
4
 
5
5
  export enum DEPENDENCY_TYPE {
@@ -21,113 +21,350 @@ export type Options = {
21
21
  ignorePathPattern?: readonly string[]
22
22
  }
23
23
 
24
- type PackageJson = {
25
- dependencies?: Record<string, string>
26
- devDependencies?: Record<string, string>
27
- optionalDependencies?: Record<string, string>
28
- }
24
+ // critical packages that must not be duplicated at runtime
25
+ const CRITICAL_PACKAGES = ['@tamagui/web', '@tamagui/core', 'tamagui']
29
26
 
30
27
  /**
31
- * Checks if @tamagui/* packages within a single package.json have mismatched versions.
32
- * Returns a summary of mismatches or empty string if all versions match.
28
+ * Walks node_modules to find duplicate physical copies of critical tamagui packages.
29
+ * Detects nested node_modules that would cause multiple runtime instances.
33
30
  */
34
- function checkTamaguiPackageVersions(root: string): string {
35
- const packageJsonPath = join(root, 'package.json')
36
- if (!existsSync(packageJsonPath)) {
37
- return ''
31
+ function checkDuplicateInstalls(root: string): string {
32
+ const nodeModules = join(root, 'node_modules')
33
+ if (!existsSync(nodeModules)) return ''
34
+
35
+ const duplicates = new Map<string, string[]>()
36
+
37
+ for (const pkg of CRITICAL_PACKAGES) {
38
+ const locations = findAllInstances(nodeModules, pkg)
39
+ if (locations.length > 1) {
40
+ // resolve symlinks to find truly distinct copies
41
+ const realPaths = new Set<string>()
42
+ const distinctLocations: string[] = []
43
+ for (const loc of locations) {
44
+ try {
45
+ const real = realpathSync(loc)
46
+ if (!realPaths.has(real)) {
47
+ realPaths.add(real)
48
+ distinctLocations.push(relative(root, loc))
49
+ }
50
+ } catch {
51
+ distinctLocations.push(relative(root, loc))
52
+ }
53
+ }
54
+ if (distinctLocations.length > 1) {
55
+ duplicates.set(pkg, distinctLocations)
56
+ }
57
+ }
38
58
  }
39
59
 
40
- const packageJson: PackageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
60
+ if (duplicates.size === 0) return ''
41
61
 
42
- // Collect all @tamagui/* dependencies and their versions
43
- const tamaguiDeps: { name: string; version: string }[] = []
62
+ const lines: string[] = [
63
+ 'Found duplicate tamagui installations in node_modules:',
64
+ '',
65
+ 'This causes multiple runtime instances, which breaks theme/config detection.',
66
+ '',
67
+ ]
44
68
 
45
- const allDeps = {
46
- ...packageJson.dependencies,
47
- ...packageJson.devDependencies,
48
- ...packageJson.optionalDependencies,
69
+ for (const [pkg, locations] of duplicates) {
70
+ // read versions from each location
71
+ lines.push(` ${pkg}:`)
72
+ for (const loc of locations) {
73
+ const pkgJsonPath = join(root, loc, 'package.json')
74
+ let version = '?'
75
+ try {
76
+ version = JSON.parse(readFileSync(pkgJsonPath, 'utf8')).version
77
+ } catch {}
78
+ lines.push(` ${version} at ${loc}`)
79
+ }
80
+ lines.push('')
49
81
  }
50
82
 
51
- for (const [name, version] of Object.entries(allDeps)) {
52
- if (name === 'tamagui' || name.startsWith('@tamagui/')) {
53
- tamaguiDeps.push({ name, version })
54
- }
83
+ lines.push("Fix: run your package manager's dedupe command:")
84
+ lines.push(' bun install (bun auto-dedupes)')
85
+ lines.push(' npx yarn-deduplicate && yarn install')
86
+ lines.push(' npm dedupe')
87
+ lines.push('')
88
+ lines.push("If that doesn't help, delete node_modules and lockfile, then reinstall.")
89
+
90
+ return lines.join('\n')
91
+ }
92
+
93
+ /**
94
+ * Recursively find all instances of a package in node_modules.
95
+ * Handles both scoped (@tamagui/web) and unscoped (tamagui) packages.
96
+ */
97
+ function findAllInstances(
98
+ nodeModulesDir: string,
99
+ packageName: string,
100
+ found: string[] = [],
101
+ depth = 0
102
+ ): string[] {
103
+ // don't go too deep, typical hoisting issues show up within a few levels
104
+ if (depth > 4 || !existsSync(nodeModulesDir)) return found
105
+
106
+ const pkgDir = join(nodeModulesDir, ...packageName.split('/'))
107
+ if (existsSync(join(pkgDir, 'package.json'))) {
108
+ found.push(pkgDir)
55
109
  }
56
110
 
57
- if (tamaguiDeps.length <= 1) {
111
+ // scan nested node_modules inside direct children
112
+ try {
113
+ const entries = readdirSync(nodeModulesDir)
114
+ for (const entry of entries) {
115
+ if (entry.startsWith('.')) continue
116
+
117
+ if (entry.startsWith('@')) {
118
+ // scoped packages have another level
119
+ const scopeDir = join(nodeModulesDir, entry)
120
+ try {
121
+ const scopeEntries = readdirSync(scopeDir)
122
+ for (const scopeEntry of scopeEntries) {
123
+ const nested = join(scopeDir, scopeEntry, 'node_modules')
124
+ if (existsSync(nested)) {
125
+ findAllInstances(nested, packageName, found, depth + 1)
126
+ }
127
+ }
128
+ } catch {}
129
+ } else {
130
+ const nested = join(nodeModulesDir, entry, 'node_modules')
131
+ if (existsSync(nested)) {
132
+ findAllInstances(nested, packageName, found, depth + 1)
133
+ }
134
+ }
135
+ }
136
+ } catch {}
137
+
138
+ return found
139
+ }
140
+
141
+ /**
142
+ * Checks lockfile for multiple resolved versions of tamagui packages.
143
+ * Supports bun.lock, yarn.lock, and package-lock.json.
144
+ */
145
+ function checkLockfileDuplicates(root: string): string {
146
+ const bunLock = join(root, 'bun.lock')
147
+ const yarnLock = join(root, 'yarn.lock')
148
+ const npmLock = join(root, 'package-lock.json')
149
+
150
+ if (existsSync(bunLock)) return checkBunLockDuplicates(bunLock)
151
+ if (existsSync(yarnLock)) return checkYarnLockDuplicates(yarnLock)
152
+ if (existsSync(npmLock)) return checkNpmLockDuplicates(npmLock)
153
+
154
+ return ''
155
+ }
156
+
157
+ function checkBunLockDuplicates(lockPath: string): string {
158
+ try {
159
+ const content = readFileSync(lockPath, 'utf8')
160
+ const duplicates = new Map<string, Set<string>>()
161
+ const criticalSet = new Set(CRITICAL_PACKAGES)
162
+
163
+ // match patterns like "@tamagui/web@version" or "tamagui@version" in resolved entries
164
+ // bun.lock format: "package@version": ["resolved-url", ...]
165
+ const packagePattern = /["'](@tamagui\/[\w-]+|tamagui)@([^"'\s,]+)["']/g
166
+ let match: RegExpExecArray | null
167
+ while ((match = packagePattern.exec(content)) !== null) {
168
+ const name = match[1]
169
+ const version = match[2]
170
+ if (version.startsWith('workspace:')) continue
171
+ // only flag critical packages — leaf packages can safely differ
172
+ if (!criticalSet.has(name)) continue
173
+ if (!duplicates.has(name)) duplicates.set(name, new Set())
174
+ duplicates.get(name)!.add(version)
175
+ }
176
+
177
+ return formatLockfileDuplicates(duplicates, 'bun.lock')
178
+ } catch {
58
179
  return ''
59
180
  }
181
+ }
60
182
 
61
- // Normalize versions by removing prefixes like ^, ~, >=, etc.
62
- const normalizeVersion = (v: string): string => {
63
- // Handle workspace: protocol
64
- if (v.startsWith('workspace:')) {
65
- return v
183
+ function checkYarnLockDuplicates(lockPath: string): string {
184
+ try {
185
+ const content = readFileSync(lockPath, 'utf8')
186
+ const duplicates = new Map<string, Set<string>>()
187
+ const criticalSet = new Set(CRITICAL_PACKAGES)
188
+
189
+ // yarn.lock format:
190
+ // "@tamagui/web@^1.0.0":
191
+ // version "1.0.1"
192
+ const entryPattern = /^"?(@tamagui\/[\w-]+|tamagui)@[^":\n]+[":]?\s*$/gm
193
+ const versionPattern = /^\s+version\s+"([^"]+)"/gm
194
+
195
+ let entryMatch: RegExpExecArray | null
196
+ while ((entryMatch = entryPattern.exec(content)) !== null) {
197
+ const name = entryMatch[1]
198
+ if (!criticalSet.has(name)) continue
199
+ versionPattern.lastIndex = entryMatch.index
200
+ const verMatch = versionPattern.exec(content)
201
+ if (verMatch) {
202
+ if (!duplicates.has(name)) duplicates.set(name, new Set())
203
+ duplicates.get(name)!.add(verMatch[1])
204
+ }
66
205
  }
67
- return v.replace(/^[\^~>=<]+/, '')
206
+
207
+ return formatLockfileDuplicates(duplicates, 'yarn.lock')
208
+ } catch {
209
+ return ''
68
210
  }
211
+ }
212
+
213
+ function checkNpmLockDuplicates(lockPath: string): string {
214
+ try {
215
+ const lock = JSON.parse(readFileSync(lockPath, 'utf8'))
216
+ const duplicates = new Map<string, Set<string>>()
217
+ const criticalSet = new Set(CRITICAL_PACKAGES)
69
218
 
70
- // Group by normalized version
71
- const versionGroups = new Map<string, string[]>()
72
- for (const dep of tamaguiDeps) {
73
- const normalized = normalizeVersion(dep.version)
74
- if (!versionGroups.has(normalized)) {
75
- versionGroups.set(normalized, [])
219
+ // package-lock.json v2/v3 uses "packages" map with path keys
220
+ const packages = lock.packages || {}
221
+ for (const [path, info] of Object.entries(packages) as [string, any][]) {
222
+ if (!path) continue // skip root
223
+ const name = info.name || path.split('node_modules/').pop()
224
+ if (!name) continue
225
+ if (!criticalSet.has(name)) continue
226
+ const version = info.version
227
+ if (version) {
228
+ if (!duplicates.has(name)) duplicates.set(name, new Set())
229
+ duplicates.get(name)!.add(version)
230
+ }
76
231
  }
77
- versionGroups.get(normalized)!.push(`${dep.name}@${dep.version}`)
78
- }
79
232
 
80
- // If all packages have the same normalized version, no mismatch
81
- if (versionGroups.size <= 1) {
233
+ return formatLockfileDuplicates(duplicates, 'package-lock.json')
234
+ } catch {
82
235
  return ''
83
236
  }
237
+ }
84
238
 
85
- // Build mismatch summary
86
- const lines: string[] = [
87
- 'Found mismatched @tamagui/* package versions in package.json:',
88
- '',
89
- ]
90
-
91
- for (const [version, packages] of versionGroups) {
92
- lines.push(` ${version}:`)
93
- for (const pkg of packages.sort()) {
94
- lines.push(` - ${pkg}`)
239
+ function formatLockfileDuplicates(
240
+ duplicates: Map<string, Set<string>>,
241
+ lockfileName: string
242
+ ): string {
243
+ // filter to only packages with multiple versions
244
+ const multiVersion = new Map<string, string[]>()
245
+ for (const [name, versions] of duplicates) {
246
+ if (versions.size > 1) {
247
+ multiVersion.set(name, [...versions].sort())
95
248
  }
96
249
  }
97
250
 
251
+ if (multiVersion.size === 0) return ''
252
+
253
+ const lines: string[] = [`Found multiple resolved versions in ${lockfileName}:`, '']
254
+
255
+ for (const [name, versions] of multiVersion) {
256
+ lines.push(` ${name}: ${versions.join(', ')}`)
257
+ }
258
+
98
259
  lines.push('')
99
260
  lines.push(
100
- 'All @tamagui/* packages should use the same version to avoid runtime issues.'
261
+ 'Multiple versions cause duplicate runtime instances, breaking config/theme detection.'
101
262
  )
102
- lines.push('Run `npx tamagui upgrade` to sync all packages to the latest version.')
263
+ lines.push('Fix: ensure all tamagui packages use the same version range, then dedupe.')
103
264
 
104
265
  return lines.join('\n')
105
266
  }
106
267
 
268
+ /**
269
+ * Checks that a tamagui config file exists in common locations.
270
+ */
271
+ function checkConfigExists(root: string): string {
272
+ const configNames = [
273
+ 'tamagui.config.ts',
274
+ 'tamagui.config.tsx',
275
+ 'tamagui.config.js',
276
+ 'tamagui.config.mjs',
277
+ 'tamagui.config.cjs',
278
+ ]
279
+
280
+ const searchDirs = [root, join(root, 'src'), join(root, 'app'), join(root, 'config')]
281
+
282
+ for (const dir of searchDirs) {
283
+ for (const name of configNames) {
284
+ if (existsSync(join(dir, name))) {
285
+ return ''
286
+ }
287
+ }
288
+ }
289
+
290
+ // check if tamagui.build.ts references a config path
291
+ const buildConfigNames = [
292
+ 'tamagui.build.ts',
293
+ 'tamagui.build.js',
294
+ 'tamagui.build.mjs',
295
+ 'tamagui.build.cjs',
296
+ ]
297
+ for (const name of buildConfigNames) {
298
+ const buildPath = join(root, name)
299
+ if (existsSync(buildPath)) {
300
+ try {
301
+ const content = readFileSync(buildPath, 'utf8')
302
+ const match = content.match(/config\s*:\s*['"`]([^'"`]+)['"`]/)
303
+ if (match) {
304
+ const configPath = join(root, match[1])
305
+ if (existsSync(configPath)) return ''
306
+ }
307
+ } catch {}
308
+ }
309
+ }
310
+
311
+ // also check if there's a tamagui config referenced in package.json
312
+ const pkgJsonPath = join(root, 'package.json')
313
+ if (existsSync(pkgJsonPath)) {
314
+ try {
315
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8'))
316
+ if (pkg.tamagui?.config) {
317
+ const configPath = join(root, pkg.tamagui.config)
318
+ if (existsSync(configPath)) return ''
319
+ }
320
+ } catch {}
321
+ }
322
+
323
+ // check if this is a monorepo root (has workspaces) - skip config check for root
324
+ if (existsSync(pkgJsonPath)) {
325
+ try {
326
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8'))
327
+ if (pkg.workspaces) return ''
328
+ } catch {}
329
+ }
330
+
331
+ return [
332
+ 'No tamagui.config file found.',
333
+ '',
334
+ 'Tamagui requires a config file (e.g. tamagui.config.ts) that calls createTamagui().',
335
+ 'Without it, components will throw "Can\'t find Tamagui configuration" at runtime.',
336
+ '',
337
+ 'See: https://tamagui.dev/docs/core/configuration',
338
+ ].join('\n')
339
+ }
340
+
107
341
  export async function checkDeps(root: string) {
108
- // Check for @tamagui/* version mismatches within the same package.json
109
- const tamaguiMismatchSummary = checkTamaguiPackageVersions(root)
342
+ const issues: string[] = []
110
343
 
111
- // Check for dependency version mismatches across workspace packages
344
+ // 1. check for dependency version mismatches across workspace packages
112
345
  const workspaceMismatchSummary = new CDVC(root).toMismatchSummary()
346
+ if (workspaceMismatchSummary) issues.push(workspaceMismatchSummary)
347
+
348
+ // 2. check lockfile for duplicate resolved versions of critical packages
349
+ const lockfileSummary = checkLockfileDuplicates(root)
350
+ if (lockfileSummary) issues.push(lockfileSummary)
113
351
 
114
- const hasTamaguiMismatch = Boolean(tamaguiMismatchSummary)
115
- const hasWorkspaceMismatch = Boolean(workspaceMismatchSummary)
352
+ // 3. check for duplicate physical installations in node_modules
353
+ const duplicatesSummary = checkDuplicateInstalls(root)
354
+ if (duplicatesSummary) issues.push(duplicatesSummary)
116
355
 
117
- if (!hasTamaguiMismatch && !hasWorkspaceMismatch) {
356
+ // 4. check that a config file exists
357
+ const configSummary = checkConfigExists(root)
358
+ if (configSummary) issues.push(configSummary)
359
+
360
+ if (issues.length === 0) {
118
361
  console.info(`Tamagui dependencies look good ✅`)
119
362
  process.exit(0)
120
363
  }
121
364
 
122
- if (hasTamaguiMismatch) {
123
- console.error(tamaguiMismatchSummary)
124
- }
125
-
126
- if (hasWorkspaceMismatch) {
127
- if (hasTamaguiMismatch) {
128
- console.error('') // Add spacing between error sections
129
- }
130
- console.error(workspaceMismatchSummary)
365
+ for (let i = 0; i < issues.length; i++) {
366
+ if (i > 0) console.error('')
367
+ console.error(issues[i])
131
368
  }
132
369
 
133
370
  process.exit(1)
package/src/exports.ts CHANGED
@@ -11,4 +11,5 @@ export * from './extractor/loadTamagui'
11
11
  export * from './extractor/watchTamaguiConfig'
12
12
  export * from './extractor/createLogger'
13
13
  export * from './registerRequire'
14
+ export { detectModuleFormat, clearFormatCache } from './extractor/detectModuleFormat'
14
15
  export * from './getPragmaOptions'