@tamagui/static 2.0.0-rc.3 → 2.0.0-rc.31
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/dist/checkDeps.cjs +164 -31
- package/dist/exports.cjs +3 -0
- package/dist/extractor/bundle.cjs +72 -35
- package/dist/extractor/bundleConfig.cjs +219 -35
- package/dist/extractor/createExtractor.cjs +170 -28
- package/dist/extractor/detectModuleFormat.cjs +49 -0
- package/dist/extractor/esbuildTsconfigPaths.cjs +3 -1
- package/dist/extractor/extractToClassNames.cjs +7 -5
- package/dist/extractor/extractToNative.cjs +7 -8
- package/dist/extractor/loadTamagui.cjs +1 -1
- package/dist/getPragmaOptions.cjs +7 -3
- package/dist/registerRequire.cjs +23 -14
- package/package.json +26 -22
- package/src/checkDeps.ts +305 -68
- package/src/exports.ts +1 -0
- package/src/extractor/bundle.ts +140 -37
- package/src/extractor/bundleConfig.ts +435 -61
- package/src/extractor/createExtractor.ts +261 -41
- package/src/extractor/detectModuleFormat.ts +42 -0
- package/src/extractor/esbuildTsconfigPaths.ts +6 -1
- package/src/extractor/extractToClassNames.ts +15 -9
- package/src/extractor/extractToNative.ts +32 -25
- package/src/extractor/loadTamagui.ts +2 -2
- package/src/getPragmaOptions.ts +6 -1
- package/src/registerRequire.ts +40 -8
- package/types/checkDeps.d.ts.map +1 -1
- package/types/exports.d.ts +1 -0
- package/types/exports.d.ts.map +1 -1
- package/types/extractor/bundle.d.ts +83 -1
- package/types/extractor/bundle.d.ts.map +1 -1
- package/types/extractor/bundleConfig.d.ts +15 -2
- package/types/extractor/bundleConfig.d.ts.map +1 -1
- package/types/extractor/createExtractor.d.ts.map +1 -1
- package/types/extractor/detectModuleFormat.d.ts +5 -0
- package/types/extractor/detectModuleFormat.d.ts.map +1 -0
- package/types/extractor/esbuildTsconfigPaths.d.ts +8 -0
- package/types/extractor/esbuildTsconfigPaths.d.ts.map +1 -1
- package/types/extractor/extractToClassNames.d.ts.map +1 -1
- package/types/extractor/extractToNative.d.ts.map +1 -1
- package/types/getPragmaOptions.d.ts.map +1 -1
- package/types/registerRequire.d.ts.map +1 -1
- package/dist/check-dep-versions.js +0 -389
- package/dist/check-dep-versions.js.map +0 -6
- package/dist/checkDeps.js +0 -60
- package/dist/checkDeps.js.map +0 -6
- package/dist/constants.js +0 -34
- package/dist/constants.js.map +0 -6
- package/dist/exports.js +0 -34
- package/dist/exports.js.map +0 -6
- package/dist/extractor/accessSafe.js +0 -47
- package/dist/extractor/accessSafe.js.map +0 -6
- package/dist/extractor/babelParse.js +0 -59
- package/dist/extractor/babelParse.js.map +0 -6
- package/dist/extractor/buildClassName.js +0 -72
- package/dist/extractor/buildClassName.js.map +0 -6
- package/dist/extractor/bundle.js +0 -135
- package/dist/extractor/bundle.js.map +0 -6
- package/dist/extractor/bundleConfig.js +0 -352
- package/dist/extractor/bundleConfig.js.map +0 -6
- package/dist/extractor/concatClassName.js +0 -69
- package/dist/extractor/concatClassName.js.map +0 -6
- package/dist/extractor/createEvaluator.js +0 -66
- package/dist/extractor/createEvaluator.js.map +0 -6
- package/dist/extractor/createExtractor.js +0 -1215
- package/dist/extractor/createExtractor.js.map +0 -6
- package/dist/extractor/createLogger.js +0 -32
- package/dist/extractor/createLogger.js.map +0 -6
- package/dist/extractor/ensureImportingConcat.js +0 -50
- package/dist/extractor/ensureImportingConcat.js.map +0 -6
- package/dist/extractor/errors.js +0 -22
- package/dist/extractor/errors.js.map +0 -6
- package/dist/extractor/esbuildAliasPlugin.js +0 -36
- package/dist/extractor/esbuildAliasPlugin.js.map +0 -6
- package/dist/extractor/esbuildTsconfigPaths.js +0 -79
- package/dist/extractor/esbuildTsconfigPaths.js.map +0 -6
- package/dist/extractor/evaluateAstNode.js +0 -99
- package/dist/extractor/evaluateAstNode.js.map +0 -6
- package/dist/extractor/extractHelpers.js +0 -108
- package/dist/extractor/extractHelpers.js.map +0 -6
- package/dist/extractor/extractMediaStyle.js +0 -123
- package/dist/extractor/extractMediaStyle.js.map +0 -6
- package/dist/extractor/extractToClassNames.js +0 -351
- package/dist/extractor/extractToClassNames.js.map +0 -6
- package/dist/extractor/extractToNative.js +0 -286
- package/dist/extractor/extractToNative.js.map +0 -6
- package/dist/extractor/findTopmostFunction.js +0 -32
- package/dist/extractor/findTopmostFunction.js.map +0 -6
- package/dist/extractor/generatedUid.js +0 -42
- package/dist/extractor/generatedUid.js.map +0 -6
- package/dist/extractor/getPrefixLogs.js +0 -24
- package/dist/extractor/getPrefixLogs.js.map +0 -6
- package/dist/extractor/getPropValueFromAttributes.js +0 -65
- package/dist/extractor/getPropValueFromAttributes.js.map +0 -6
- package/dist/extractor/getSourceModule.js +0 -62
- package/dist/extractor/getSourceModule.js.map +0 -6
- package/dist/extractor/getStaticBindingsForScope.js +0 -145
- package/dist/extractor/getStaticBindingsForScope.js.map +0 -6
- package/dist/extractor/getTamaguiConfigPathFromOptionsConfig.js +0 -32
- package/dist/extractor/getTamaguiConfigPathFromOptionsConfig.js.map +0 -6
- package/dist/extractor/hoistClassNames.js +0 -63
- package/dist/extractor/hoistClassNames.js.map +0 -6
- package/dist/extractor/literalToAst.js +0 -90
- package/dist/extractor/literalToAst.js.map +0 -6
- package/dist/extractor/loadFile.js +0 -14
- package/dist/extractor/loadFile.js.map +0 -6
- package/dist/extractor/loadTamagui.js +0 -306
- package/dist/extractor/loadTamagui.js.map +0 -6
- package/dist/extractor/logLines.js +0 -30
- package/dist/extractor/logLines.js.map +0 -6
- package/dist/extractor/normalizeTernaries.js +0 -61
- package/dist/extractor/normalizeTernaries.js.map +0 -6
- package/dist/extractor/propsToFontFamilyCache.js +0 -33
- package/dist/extractor/propsToFontFamilyCache.js.map +0 -6
- package/dist/extractor/regenerateConfig.js +0 -123
- package/dist/extractor/regenerateConfig.js.map +0 -6
- package/dist/extractor/removeUnusedHooks.js +0 -72
- package/dist/extractor/removeUnusedHooks.js.map +0 -6
- package/dist/extractor/timer.js +0 -38
- package/dist/extractor/timer.js.map +0 -6
- package/dist/extractor/validHTMLAttributes.js +0 -72
- package/dist/extractor/validHTMLAttributes.js.map +0 -6
- package/dist/extractor/watchTamaguiConfig.js +0 -57
- package/dist/extractor/watchTamaguiConfig.js.map +0 -6
- package/dist/getPragmaOptions.js +0 -46
- package/dist/getPragmaOptions.js.map +0 -6
- package/dist/helpers/memoize.js +0 -33
- package/dist/helpers/memoize.js.map +0 -6
- package/dist/helpers/requireTamaguiCore.js +0 -30
- package/dist/helpers/requireTamaguiCore.js.map +0 -6
- package/dist/index.js +0 -30
- package/dist/index.js.map +0 -6
- package/dist/registerRequire.js +0 -100
- package/dist/registerRequire.js.map +0 -6
- package/dist/server.js +0 -58
- package/dist/server.js.map +0 -6
- package/dist/setup.js +0 -1
- package/dist/setup.js.map +0 -6
- package/dist/types.js +0 -14
- package/dist/types.js.map +0 -6
- package/dist/worker.js +0 -72
- package/dist/worker.js.map +0 -6
|
@@ -12,7 +12,9 @@ import {
|
|
|
12
12
|
type StaticConfig,
|
|
13
13
|
type TamaguiComponentState,
|
|
14
14
|
} from '@tamagui/web'
|
|
15
|
-
import {
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
16
|
+
import { basename, dirname, resolve, relative } from 'node:path'
|
|
17
|
+
import { nodeModuleNameResolver, sys } from 'typescript'
|
|
16
18
|
import type { ViewStyle } from 'react-native'
|
|
17
19
|
|
|
18
20
|
import { FAILED_EVAL } from '../constants'
|
|
@@ -26,7 +28,7 @@ import type {
|
|
|
26
28
|
TamaguiOptionsWithFileInfo,
|
|
27
29
|
Ternary,
|
|
28
30
|
} from '../types'
|
|
29
|
-
import type { TamaguiProjectInfo } from './bundleConfig'
|
|
31
|
+
import type { LoadedComponents, TamaguiProjectInfo } from './bundleConfig'
|
|
30
32
|
import { createEvaluator, createSafeEvaluator } from './createEvaluator'
|
|
31
33
|
import { evaluateAstNode } from './evaluateAstNode'
|
|
32
34
|
import {
|
|
@@ -49,6 +51,7 @@ import { setPropsToFontFamily } from './propsToFontFamilyCache'
|
|
|
49
51
|
import { timer } from './timer'
|
|
50
52
|
import { validHTMLAttributes } from './validHTMLAttributes'
|
|
51
53
|
import { BailOptimizationError } from './errors'
|
|
54
|
+
import { loadCompilerOptionsFromTsconfig } from './esbuildTsconfigPaths'
|
|
52
55
|
|
|
53
56
|
const UNTOUCHED_PROPS = {
|
|
54
57
|
key: true,
|
|
@@ -85,6 +88,29 @@ export function createExtractor(
|
|
|
85
88
|
onPressIn: 'onMouseDown',
|
|
86
89
|
onPressOut: 'onMouseUp',
|
|
87
90
|
}),
|
|
91
|
+
...(platform === 'native' && {
|
|
92
|
+
// native view props that should pass through without preventing flattening
|
|
93
|
+
testID: 'testID',
|
|
94
|
+
nativeID: 'nativeID',
|
|
95
|
+
accessibilityLabel: 'accessibilityLabel',
|
|
96
|
+
accessibilityHint: 'accessibilityHint',
|
|
97
|
+
accessibilityRole: 'accessibilityRole',
|
|
98
|
+
accessibilityState: 'accessibilityState',
|
|
99
|
+
accessibilityValue: 'accessibilityValue',
|
|
100
|
+
accessibilityActions: 'accessibilityActions',
|
|
101
|
+
accessibilityLabelledBy: 'accessibilityLabelledBy',
|
|
102
|
+
accessibilityLiveRegion: 'accessibilityLiveRegion',
|
|
103
|
+
accessibilityElementsHidden: 'accessibilityElementsHidden',
|
|
104
|
+
accessibilityViewIsModal: 'accessibilityViewIsModal',
|
|
105
|
+
importantForAccessibility: 'importantForAccessibility',
|
|
106
|
+
collapsable: 'collapsable',
|
|
107
|
+
needsOffscreenAlphaCompositing: 'needsOffscreenAlphaCompositing',
|
|
108
|
+
removeClippedSubviews: 'removeClippedSubviews',
|
|
109
|
+
renderToHardwareTextureAndroid: 'renderToHardwareTextureAndroid',
|
|
110
|
+
shouldRasterizeIOS: 'shouldRasterizeIOS',
|
|
111
|
+
hitSlop: 'hitSlop',
|
|
112
|
+
pointerEvents: 'pointerEvents',
|
|
113
|
+
}),
|
|
88
114
|
}
|
|
89
115
|
|
|
90
116
|
const componentState: TamaguiComponentState = {
|
|
@@ -113,6 +139,83 @@ export function createExtractor(
|
|
|
113
139
|
|
|
114
140
|
let projectInfo: TamaguiProjectInfo | null = null
|
|
115
141
|
|
|
142
|
+
// cache of dynamically discovered styled components, keyed by absolute file path
|
|
143
|
+
// persists across files within the same worker/extractor instance
|
|
144
|
+
const dynamicComponentCache = new Map<string, LoadedComponents>()
|
|
145
|
+
const dynamicLoadingInProgress = new Set<string>()
|
|
146
|
+
|
|
147
|
+
// lazily loaded tsconfig compiler options for path alias resolution
|
|
148
|
+
let _compilerOptions: any = null
|
|
149
|
+
function getCompilerOptions() {
|
|
150
|
+
if (!_compilerOptions) {
|
|
151
|
+
try {
|
|
152
|
+
_compilerOptions = loadCompilerOptionsFromTsconfig()
|
|
153
|
+
} catch {
|
|
154
|
+
_compilerOptions = {}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return _compilerOptions
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function resolveImportPath(fromFile: string, importPath: string): string | null {
|
|
161
|
+
if (importPath.startsWith('.')) {
|
|
162
|
+
// relative path resolution
|
|
163
|
+
const dir = dirname(fromFile)
|
|
164
|
+
const base = resolve(dir, importPath)
|
|
165
|
+
const extensions = ['.tsx', '.ts', '.jsx', '.js']
|
|
166
|
+
for (const ext of extensions) {
|
|
167
|
+
const full = base + ext
|
|
168
|
+
if (existsSync(full)) return full
|
|
169
|
+
}
|
|
170
|
+
// try index files
|
|
171
|
+
for (const ext of extensions) {
|
|
172
|
+
const full = resolve(base, `index${ext}`)
|
|
173
|
+
if (existsSync(full)) return full
|
|
174
|
+
}
|
|
175
|
+
return null
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// tsconfig path alias resolution (e.g. ~/foo, @/bar)
|
|
179
|
+
const compilerOptions = getCompilerOptions()
|
|
180
|
+
if (compilerOptions.paths) {
|
|
181
|
+
try {
|
|
182
|
+
const { resolvedModule } = nodeModuleNameResolver(
|
|
183
|
+
importPath,
|
|
184
|
+
fromFile,
|
|
185
|
+
compilerOptions,
|
|
186
|
+
sys
|
|
187
|
+
)
|
|
188
|
+
if (
|
|
189
|
+
resolvedModule &&
|
|
190
|
+
!resolvedModule.resolvedFileName.endsWith('.d.ts') &&
|
|
191
|
+
!resolvedModule.isExternalLibraryImport
|
|
192
|
+
) {
|
|
193
|
+
return resolvedModule.resolvedFileName
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
// fallback - tsconfig resolution failed
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return null
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const styledCheckCache = new Map<string, boolean>()
|
|
204
|
+
|
|
205
|
+
function mightHaveStyledComponents(filePath: string): boolean {
|
|
206
|
+
const cached = styledCheckCache.get(filePath)
|
|
207
|
+
if (cached !== undefined) return cached
|
|
208
|
+
try {
|
|
209
|
+
const content = readFileSync(filePath, 'utf-8')
|
|
210
|
+
const result = content.includes('styled(')
|
|
211
|
+
styledCheckCache.set(filePath, result)
|
|
212
|
+
return result
|
|
213
|
+
} catch {
|
|
214
|
+
styledCheckCache.set(filePath, false)
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
116
219
|
// we load tamagui delayed because we need to set some global/env stuff before importing
|
|
117
220
|
// otherwise we'd import `rnw` and cause it to evaluate react-native-web which causes errors
|
|
118
221
|
|
|
@@ -178,6 +281,12 @@ export function createExtractor(
|
|
|
178
281
|
...restProps
|
|
179
282
|
} = options
|
|
180
283
|
|
|
284
|
+
// invalidate dynamic cache for this file on re-parse (HMR)
|
|
285
|
+
if (sourcePath && dynamicComponentCache.has(sourcePath)) {
|
|
286
|
+
dynamicComponentCache.delete(sourcePath)
|
|
287
|
+
styledCheckCache.delete(sourcePath)
|
|
288
|
+
}
|
|
289
|
+
|
|
181
290
|
if (sourcePath.includes('.tamagui-dynamic-eval')) {
|
|
182
291
|
return null
|
|
183
292
|
}
|
|
@@ -375,12 +484,12 @@ export function createExtractor(
|
|
|
375
484
|
logger.info(` - import via ${moduleName} ${valid}`)
|
|
376
485
|
}
|
|
377
486
|
|
|
378
|
-
if (extractStyledDefinitions) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
487
|
+
if (extractStyledDefinitions && enableDynamicEvaluation) {
|
|
488
|
+
// check all imports for `styled`, not just valid packages
|
|
489
|
+
// styled( is basically guaranteed to be tamagui regardless of source
|
|
490
|
+
if (node.specifiers.some((specifier) => specifier.local.name === 'styled')) {
|
|
491
|
+
doesUseValidImport = true
|
|
492
|
+
// don't break - need to collect all import declarations for the styled() handler
|
|
384
493
|
}
|
|
385
494
|
}
|
|
386
495
|
|
|
@@ -400,7 +509,7 @@ export function createExtractor(
|
|
|
400
509
|
}
|
|
401
510
|
if (isValidComponent) {
|
|
402
511
|
doesUseValidImport = true
|
|
403
|
-
break
|
|
512
|
+
if (!(extractStyledDefinitions && enableDynamicEvaluation)) break
|
|
404
513
|
}
|
|
405
514
|
}
|
|
406
515
|
}
|
|
@@ -411,6 +520,35 @@ export function createExtractor(
|
|
|
411
520
|
)
|
|
412
521
|
}
|
|
413
522
|
|
|
523
|
+
if (
|
|
524
|
+
!doesUseValidImport &&
|
|
525
|
+
extractStyledDefinitions &&
|
|
526
|
+
enableDynamicEvaluation &&
|
|
527
|
+
sourcePath
|
|
528
|
+
) {
|
|
529
|
+
// check if any local import is in the dynamic cache or has styled components
|
|
530
|
+
for (const bodyPath of body) {
|
|
531
|
+
if (bodyPath.type !== 'ImportDeclaration') continue
|
|
532
|
+
const node = (
|
|
533
|
+
'node' in bodyPath ? bodyPath.node : bodyPath
|
|
534
|
+
) as t.ImportDeclaration
|
|
535
|
+
const moduleName = node.source.value
|
|
536
|
+
|
|
537
|
+
const resolved = resolveImportPath(sourcePath, moduleName)
|
|
538
|
+
if (!resolved) continue
|
|
539
|
+
|
|
540
|
+
if (dynamicComponentCache.has(resolved)) {
|
|
541
|
+
doesUseValidImport = true
|
|
542
|
+
break
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (mightHaveStyledComponents(resolved)) {
|
|
546
|
+
doesUseValidImport = true
|
|
547
|
+
break
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
414
552
|
if (!doesUseValidImport) {
|
|
415
553
|
return null
|
|
416
554
|
}
|
|
@@ -484,6 +622,10 @@ export function createExtractor(
|
|
|
484
622
|
? path.parent.id.name
|
|
485
623
|
: 'unknown'
|
|
486
624
|
|
|
625
|
+
if (shouldPrintDebug) {
|
|
626
|
+
logger.info(` [styled] Found styled(${variableName})`)
|
|
627
|
+
}
|
|
628
|
+
|
|
487
629
|
const parentNode = path.node.arguments[0]
|
|
488
630
|
|
|
489
631
|
if (!t.isIdentifier(parentNode)) {
|
|
@@ -496,10 +638,12 @@ export function createExtractor(
|
|
|
496
638
|
return
|
|
497
639
|
}
|
|
498
640
|
|
|
499
|
-
|
|
641
|
+
// look up by parent first (e.g. View in `styled(View, {...})`), then by self
|
|
642
|
+
let Component =
|
|
643
|
+
getValidImportedComponent(parentName) || getValidImportedComponent(variableName)
|
|
500
644
|
|
|
501
645
|
if (!Component) {
|
|
502
|
-
if (enableDynamicEvaluation
|
|
646
|
+
if (!enableDynamicEvaluation) {
|
|
503
647
|
return
|
|
504
648
|
}
|
|
505
649
|
|
|
@@ -583,6 +727,7 @@ export function createExtractor(
|
|
|
583
727
|
// for now dont parse variants, spreads, etc
|
|
584
728
|
const skipped = new Set<t.ObjectProperty | t.SpreadElement | t.ObjectMethod>()
|
|
585
729
|
const styles = {}
|
|
730
|
+
const staticDefaultProps = {}
|
|
586
731
|
|
|
587
732
|
// Generate scope object at this level
|
|
588
733
|
const staticNamespace = getStaticBindingsForScope(
|
|
@@ -604,6 +749,19 @@ export function createExtractor(
|
|
|
604
749
|
const attemptEvalSafe = createSafeEvaluator(attemptEval)
|
|
605
750
|
|
|
606
751
|
for (const property of definition.properties) {
|
|
752
|
+
if (
|
|
753
|
+
t.isObjectProperty(property) &&
|
|
754
|
+
(t.isIdentifier(property.key) || t.isStringLiteral(property.key))
|
|
755
|
+
) {
|
|
756
|
+
const key = t.isIdentifier(property.key)
|
|
757
|
+
? property.key.name
|
|
758
|
+
: property.key.value
|
|
759
|
+
const defaultPropValue = attemptEvalSafe(property.value)
|
|
760
|
+
if (defaultPropValue !== FAILED_EVAL) {
|
|
761
|
+
staticDefaultProps[key] = defaultPropValue
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
607
765
|
if (
|
|
608
766
|
!t.isObjectProperty(property) ||
|
|
609
767
|
!t.isIdentifier(property.key) ||
|
|
@@ -667,23 +825,9 @@ export function createExtractor(
|
|
|
667
825
|
)
|
|
668
826
|
}
|
|
669
827
|
|
|
670
|
-
//
|
|
671
|
-
//
|
|
672
|
-
|
|
673
|
-
if (
|
|
674
|
-
skipped.has(prop) ||
|
|
675
|
-
!t.isObjectProperty(prop) ||
|
|
676
|
-
!t.isIdentifier(prop.key)
|
|
677
|
-
) {
|
|
678
|
-
return prop
|
|
679
|
-
}
|
|
680
|
-
const key = prop.key.name
|
|
681
|
-
const value = classNames[key]
|
|
682
|
-
if (value) {
|
|
683
|
-
return t.objectProperty(t.stringLiteral(key), t.stringLiteral(value))
|
|
684
|
-
}
|
|
685
|
-
return prop
|
|
686
|
-
})
|
|
828
|
+
// don't replace definition values with class name strings -
|
|
829
|
+
// the runtime needs real values for animations, context, and group styles.
|
|
830
|
+
// we only emit the CSS rules so they're available if the runtime uses classNames.
|
|
687
831
|
|
|
688
832
|
if (out.rulesToInsert) {
|
|
689
833
|
for (const key in out.rulesToInsert) {
|
|
@@ -697,6 +841,34 @@ export function createExtractor(
|
|
|
697
841
|
|
|
698
842
|
res.styled++
|
|
699
843
|
|
|
844
|
+
// register so JSX handler can find this component (same-file and cross-file)
|
|
845
|
+
if (extractStyledDefinitions && enableDynamicEvaluation && Component) {
|
|
846
|
+
const dynamicStaticConfig = {
|
|
847
|
+
...Component.staticConfig,
|
|
848
|
+
defaultProps: {
|
|
849
|
+
...Component.staticConfig.defaultProps,
|
|
850
|
+
...staticDefaultProps,
|
|
851
|
+
},
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// add to allLoadedComponents with '' so getValidComponent matches when moduleName is ''
|
|
855
|
+
// (same-file styled components have '' as moduleName in JSX handler)
|
|
856
|
+
propsWithFileInfo.allLoadedComponents.push({
|
|
857
|
+
moduleName: '',
|
|
858
|
+
nameToInfo: { [variableName]: { staticConfig: dynamicStaticConfig } },
|
|
859
|
+
})
|
|
860
|
+
|
|
861
|
+
// also cache by file path so other files importing from this path can find it
|
|
862
|
+
if (sourcePath) {
|
|
863
|
+
let existing = dynamicComponentCache.get(sourcePath)
|
|
864
|
+
if (!existing) {
|
|
865
|
+
existing = { moduleName: sourcePath, nameToInfo: {} }
|
|
866
|
+
dynamicComponentCache.set(sourcePath, existing)
|
|
867
|
+
}
|
|
868
|
+
existing.nameToInfo[variableName] = { staticConfig: dynamicStaticConfig }
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
700
872
|
if (shouldPrintDebug) {
|
|
701
873
|
logger.info(`Extracted styled(${variableName})`)
|
|
702
874
|
}
|
|
@@ -725,22 +897,77 @@ export function createExtractor(
|
|
|
725
897
|
// validate its a proper import from tamagui (or internally inside tamagui)
|
|
726
898
|
const binding = traversePath.scope.getBinding(node.name.name)
|
|
727
899
|
let moduleName = ''
|
|
900
|
+
let dynamicComponent: { staticConfig: any } | null = null
|
|
728
901
|
|
|
729
902
|
if (binding) {
|
|
730
903
|
if (t.isImportDeclaration(binding.path.parent)) {
|
|
731
904
|
moduleName = binding.path.parent.source.value
|
|
732
905
|
if (!isValidImport(propsWithFileInfo, moduleName, binding.identifier.name)) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
)
|
|
906
|
+
// fallback: try dynamic component cache for local imports (relative or tsconfig alias)
|
|
907
|
+
if (enableDynamicEvaluation && sourcePath) {
|
|
908
|
+
const resolved = resolveImportPath(sourcePath, moduleName)
|
|
909
|
+
if (resolved) {
|
|
910
|
+
// check cache first
|
|
911
|
+
const cached = dynamicComponentCache.get(resolved)
|
|
912
|
+
if (cached?.nameToInfo[binding.identifier.name]) {
|
|
913
|
+
dynamicComponent = cached.nameToInfo[binding.identifier.name]
|
|
914
|
+
} else if (
|
|
915
|
+
!dynamicLoadingInProgress.has(resolved) &&
|
|
916
|
+
mightHaveStyledComponents(resolved)
|
|
917
|
+
) {
|
|
918
|
+
// proactively load the file
|
|
919
|
+
dynamicLoadingInProgress.add(resolved)
|
|
920
|
+
try {
|
|
921
|
+
const out = loadTamaguiSync({
|
|
922
|
+
forceExports: true,
|
|
923
|
+
components: [resolved],
|
|
924
|
+
})
|
|
925
|
+
if (out?.components) {
|
|
926
|
+
for (const comp of out.components) {
|
|
927
|
+
// merge into cache
|
|
928
|
+
let existing = dynamicComponentCache.get(resolved)
|
|
929
|
+
if (!existing) {
|
|
930
|
+
existing = { moduleName: resolved, nameToInfo: {} }
|
|
931
|
+
dynamicComponentCache.set(resolved, existing)
|
|
932
|
+
}
|
|
933
|
+
Object.assign(existing.nameToInfo, comp.nameToInfo)
|
|
934
|
+
// also add to allLoadedComponents so getValidComponent works
|
|
935
|
+
propsWithFileInfo.allLoadedComponents.push({
|
|
936
|
+
moduleName: resolved,
|
|
937
|
+
nameToInfo: comp.nameToInfo,
|
|
938
|
+
})
|
|
939
|
+
}
|
|
940
|
+
const cachedNow = dynamicComponentCache.get(resolved)
|
|
941
|
+
if (cachedNow?.nameToInfo[binding.identifier.name]) {
|
|
942
|
+
dynamicComponent = cachedNow.nameToInfo[binding.identifier.name]
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
} catch (err) {
|
|
946
|
+
if (shouldPrintDebug) {
|
|
947
|
+
logger.info(` - Failed to dynamically load ${resolved}: ${err}`)
|
|
948
|
+
}
|
|
949
|
+
} finally {
|
|
950
|
+
dynamicLoadingInProgress.delete(resolved)
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
if (!dynamicComponent) {
|
|
957
|
+
if (shouldPrintDebug) {
|
|
958
|
+
logger.info(
|
|
959
|
+
` - Binding in component ${componentName} not valid import: "${binding.identifier.name}" isn't in ${moduleName}\n`
|
|
960
|
+
)
|
|
961
|
+
}
|
|
962
|
+
return
|
|
737
963
|
}
|
|
738
|
-
return
|
|
739
964
|
}
|
|
740
965
|
}
|
|
741
966
|
}
|
|
742
967
|
|
|
743
|
-
const component =
|
|
968
|
+
const component =
|
|
969
|
+
dynamicComponent ||
|
|
970
|
+
getValidComponent(propsWithFileInfo, moduleName, node.name.name)
|
|
744
971
|
if (!component || !component.staticConfig) {
|
|
745
972
|
if (shouldPrintDebug) {
|
|
746
973
|
logger.info(`\n - No Tamagui conf for: ${node.name.name}\n`)
|
|
@@ -832,13 +1059,6 @@ export function createExtractor(
|
|
|
832
1059
|
const isTextView = staticConfig.isText || false
|
|
833
1060
|
const validStyles = staticConfig?.validStyles ?? {}
|
|
834
1061
|
|
|
835
|
-
if (process.env.NODE_ENV === 'production') {
|
|
836
|
-
if (isTextView) {
|
|
837
|
-
// temporarily disabled - need to fix css nesting dix
|
|
838
|
-
return
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
|
|
842
1062
|
// find render="a" render="main" etc dom indicators
|
|
843
1063
|
let tagName = defaultProps.render ?? (isTextView ? 'span' : 'div')
|
|
844
1064
|
traversePath
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, extname, join } from 'node:path'
|
|
3
|
+
|
|
4
|
+
type ModuleFormat = 'cjs' | 'esm'
|
|
5
|
+
|
|
6
|
+
// cache per directory to avoid repeated fs reads
|
|
7
|
+
const formatCache = new Map<string, ModuleFormat>()
|
|
8
|
+
|
|
9
|
+
export function detectModuleFormat(filePath: string): ModuleFormat {
|
|
10
|
+
const ext = extname(filePath)
|
|
11
|
+
|
|
12
|
+
// definitive by extension
|
|
13
|
+
if (ext === '.mjs') return 'esm'
|
|
14
|
+
if (ext === '.cjs') return 'cjs'
|
|
15
|
+
|
|
16
|
+
// walk up to find nearest package.json with "type" field
|
|
17
|
+
let dir = dirname(filePath)
|
|
18
|
+
while (true) {
|
|
19
|
+
if (formatCache.has(dir)) {
|
|
20
|
+
return formatCache.get(dir)!
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8'))
|
|
25
|
+
const format: ModuleFormat = pkg.type === 'module' ? 'esm' : 'cjs'
|
|
26
|
+
formatCache.set(dir, format)
|
|
27
|
+
return format
|
|
28
|
+
} catch {
|
|
29
|
+
// no package.json or malformed, keep walking
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const parent = dirname(dir)
|
|
33
|
+
if (parent === dir) break
|
|
34
|
+
dir = parent
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return 'cjs'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function clearFormatCache() {
|
|
41
|
+
formatCache.clear()
|
|
42
|
+
}
|
|
@@ -25,6 +25,11 @@ export function TsconfigPathsPlugin(): Plugin {
|
|
|
25
25
|
name,
|
|
26
26
|
setup({ onResolve }) {
|
|
27
27
|
onResolve({ filter: /.*/ }, (args) => {
|
|
28
|
+
// skip @tamagui packages - they should be externalized, not resolved via tsconfig
|
|
29
|
+
if (args.path.startsWith('@tamagui/')) {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
|
|
28
33
|
const paths = compilerOptions.paths || {}
|
|
29
34
|
const hasMatchingPath = Object.keys(paths).some((p) =>
|
|
30
35
|
new RegExp(p.replace('*', '\\w*')).test(args.path)
|
|
@@ -59,7 +64,7 @@ export function TsconfigPathsPlugin(): Plugin {
|
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
|
|
62
|
-
function loadCompilerOptionsFromTsconfig(tsconfig?: Tsconfig | string) {
|
|
67
|
+
export function loadCompilerOptionsFromTsconfig(tsconfig?: Tsconfig | string) {
|
|
63
68
|
if (!tsconfig) {
|
|
64
69
|
const configPath =
|
|
65
70
|
findConfigFile(process.cwd(), sys.fileExists, 'tsconfig.json') ||
|
|
@@ -107,7 +107,7 @@ export async function extractToClassNames({
|
|
|
107
107
|
sourcePath,
|
|
108
108
|
extractStyledDefinitions: true,
|
|
109
109
|
onStyledDefinitionRule(identifier, rules) {
|
|
110
|
-
const css = rules.join('
|
|
110
|
+
const css = rules.join('\n')
|
|
111
111
|
if (shouldPrintDebug) {
|
|
112
112
|
console.info(`adding styled() rule: .${identifier} ${css}`)
|
|
113
113
|
}
|
|
@@ -343,11 +343,18 @@ export async function extractToClassNames({
|
|
|
343
343
|
baseClassNameStr = `font_${baseFontFamily}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
346
|
+
// add is_View or is_Text base class matching runtime behavior
|
|
347
|
+
const baseTypeClass = staticConfig.isText ? 'is_Text' : 'is_View'
|
|
348
|
+
baseClassNameStr = `${baseTypeClass}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
|
349
|
+
|
|
350
|
+
// add component name class (skip 'Text' since is_Text already covers it)
|
|
351
|
+
const componentNameFinal = staticConfig.componentName
|
|
352
|
+
let base =
|
|
353
|
+
componentNameFinal && componentNameFinal !== 'Text'
|
|
354
|
+
? t.stringLiteral(
|
|
355
|
+
`is_${componentNameFinal}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
|
356
|
+
)
|
|
357
|
+
: t.stringLiteral(baseClassNameStr || '')
|
|
351
358
|
|
|
352
359
|
attrClassName = attrClassName as t.Expression | null // actual typescript bug, flatMap doesn't update from never
|
|
353
360
|
|
|
@@ -616,10 +623,9 @@ function hoistClassName(path: NodePath<t.JSXElement>, str: string) {
|
|
|
616
623
|
function cleanupClassName(inStr: string) {
|
|
617
624
|
const out = new Set<string>()
|
|
618
625
|
for (const part of inStr.split(' ')) {
|
|
619
|
-
if (part === ' ') continue
|
|
626
|
+
if (!part || part === ' ') continue
|
|
620
627
|
if (part === 'font_') continue
|
|
621
628
|
out.add(part)
|
|
622
629
|
}
|
|
623
|
-
|
|
624
|
-
return [...out].join(' ') + ' '
|
|
630
|
+
return [...out].join(' ')
|
|
625
631
|
}
|
|
@@ -22,13 +22,7 @@ const importStyleSheet = template(`
|
|
|
22
22
|
const __ReactNativeStyleSheet = require('react-native').StyleSheet;
|
|
23
23
|
`)
|
|
24
24
|
|
|
25
|
-
const importWithStyle = template(`
|
|
26
|
-
const __withStableStyle = require('@tamagui/core')._withStableStyle;
|
|
27
|
-
`)
|
|
28
|
-
|
|
29
|
-
const importReactUseMemo = template(`
|
|
30
|
-
const __ReactUseMemo = require('react').useMemo;
|
|
31
|
-
`)
|
|
25
|
+
const importWithStyle = template.ast(`import { _withStableStyle } from '@tamagui/core';`)
|
|
32
26
|
|
|
33
27
|
const extractor = createExtractor({ platform: 'native' })
|
|
34
28
|
|
|
@@ -161,6 +155,33 @@ export function getBabelParseDefinition(options: TamaguiOptions) {
|
|
|
161
155
|
'cursor',
|
|
162
156
|
'contain',
|
|
163
157
|
]),
|
|
158
|
+
// native props that should pass through without preventing extraction
|
|
159
|
+
inlineProps: new Set([
|
|
160
|
+
'testID',
|
|
161
|
+
'nativeID',
|
|
162
|
+
'accessibilityLabel',
|
|
163
|
+
'accessibilityHint',
|
|
164
|
+
'accessibilityRole',
|
|
165
|
+
'accessibilityState',
|
|
166
|
+
'accessibilityValue',
|
|
167
|
+
'accessibilityActions',
|
|
168
|
+
'accessibilityLabelledBy',
|
|
169
|
+
'accessibilityLiveRegion',
|
|
170
|
+
'accessibilityElementsHidden',
|
|
171
|
+
'accessibilityViewIsModal',
|
|
172
|
+
'importantForAccessibility',
|
|
173
|
+
'onAccessibilityAction',
|
|
174
|
+
'onAccessibilityEscape',
|
|
175
|
+
'onAccessibilityTap',
|
|
176
|
+
'onMagicTap',
|
|
177
|
+
'collapsable',
|
|
178
|
+
'needsOffscreenAlphaCompositing',
|
|
179
|
+
'removeClippedSubviews',
|
|
180
|
+
'renderToHardwareTextureAndroid',
|
|
181
|
+
'shouldRasterizeIOS',
|
|
182
|
+
'hitSlop',
|
|
183
|
+
'pointerEvents',
|
|
184
|
+
]),
|
|
164
185
|
shouldPrintDebug,
|
|
165
186
|
...finalOptions,
|
|
166
187
|
// disable extracting variables as no native concept of them (only theme values)
|
|
@@ -306,8 +327,7 @@ export function getBabelParseDefinition(options: TamaguiOptions) {
|
|
|
306
327
|
hasDynamicStyle
|
|
307
328
|
) {
|
|
308
329
|
if (!hasImportedViewWrapper) {
|
|
309
|
-
root.unshiftContainer('body', importWithStyle
|
|
310
|
-
root.unshiftContainer('body', importReactUseMemo())
|
|
330
|
+
root.unshiftContainer('body', importWithStyle)
|
|
311
331
|
hasImportedViewWrapper = true
|
|
312
332
|
}
|
|
313
333
|
|
|
@@ -323,25 +343,12 @@ export function getBabelParseDefinition(options: TamaguiOptions) {
|
|
|
323
343
|
t.variableDeclaration('const', [
|
|
324
344
|
t.variableDeclarator(
|
|
325
345
|
WrapperIdentifier,
|
|
326
|
-
t.callExpression(t.identifier('
|
|
346
|
+
t.callExpression(t.identifier('_withStableStyle'), [
|
|
327
347
|
t.identifier(name),
|
|
328
348
|
t.arrowFunctionExpression(
|
|
329
349
|
[t.identifier('theme'), t.identifier('_expressions')],
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
t.callExpression(t.identifier('__ReactUseMemo'), [
|
|
333
|
-
t.arrowFunctionExpression(
|
|
334
|
-
[],
|
|
335
|
-
t.blockStatement([
|
|
336
|
-
t.returnStatement(
|
|
337
|
-
t.arrayExpression([...hocStylesExpr.elements])
|
|
338
|
-
),
|
|
339
|
-
])
|
|
340
|
-
),
|
|
341
|
-
t.identifier('_expressions'),
|
|
342
|
-
])
|
|
343
|
-
),
|
|
344
|
-
])
|
|
350
|
+
// return styles directly - no useMemo, theme changes must trigger style recalc
|
|
351
|
+
t.arrayExpression([...hocStylesExpr.elements])
|
|
345
352
|
),
|
|
346
353
|
])
|
|
347
354
|
),
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
getBundledConfig,
|
|
16
16
|
getLoadedConfig,
|
|
17
17
|
hasBundledConfigChanged,
|
|
18
|
-
|
|
18
|
+
loadComponentsSync,
|
|
19
19
|
writeTamaguiCSS,
|
|
20
20
|
} from './bundleConfig'
|
|
21
21
|
import { getTamaguiConfigPathFromOptionsConfig } from './getTamaguiConfigPathFromOptionsConfig'
|
|
@@ -288,7 +288,7 @@ export function loadTamaguiSync({
|
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
// components
|
|
291
|
-
const components =
|
|
291
|
+
const components = loadComponentsSync(props, forceExports)
|
|
292
292
|
if (!components) {
|
|
293
293
|
throw new Error(`No components loaded`)
|
|
294
294
|
}
|
package/src/getPragmaOptions.ts
CHANGED
|
@@ -7,8 +7,13 @@ export function getPragmaOptions({ source, path }: { source: string; path: strin
|
|
|
7
7
|
|
|
8
8
|
let pragma = ''
|
|
9
9
|
for (const line of firstLines.split('\n')) {
|
|
10
|
+
const trimmed = line.trim()
|
|
11
|
+
// only look at leading comments/empty lines, stop at first real code
|
|
12
|
+
if (trimmed && !trimmed.startsWith('//') && !trimmed.startsWith('/*')) {
|
|
13
|
+
break
|
|
14
|
+
}
|
|
10
15
|
pragma =
|
|
11
|
-
|
|
16
|
+
trimmed
|
|
12
17
|
.match(/(\/\/|\/\*)\s?!?\s?(tamagui-ignore|debug|debug-verbose)(\n|\s|$).*/)?.[2]
|
|
13
18
|
.trim() || ''
|
|
14
19
|
if (pragma) {
|