-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
105 lines (90 loc) Β· 4.23 KB
/
index.ts
File metadata and controls
105 lines (90 loc) Β· 4.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { PluginObj, NodePath, types as t, PluginPass } from '@babel/core'
import { loadConfig } from './config'
import { StyleObject } from './types/types'
import { checkStyleSheetExistence } from './utils/checkStyleSheetExistence'
import { extractRelativePath } from './utils/extractRelativePath'
import { generateStyleSheet } from './utils/generateStyleSheet'
import { initializePatterns } from './utils/initializePatterns'
import { ensureStyleSheetImport } from './utils/ensureStyleSheetImport'
import { ensureThemeImport } from './utils/ensureThemeImport'
import { addThemeHookToComponent } from './utils/addThemeHookToComponent'
import { checkIfComponentUsesThemeProvider } from './utils/checkIfComponentUsesThemeProvider'
import { processJSXElements } from './utils/processJSXElements'
import { hasThemeVariants, shouldUseAutoThemeVariants } from './utils/themeProcessor'
export default function (): PluginObj {
const config = loadConfig(process.cwd())
const prefix = config.prefix
const patterns = initializePatterns(config)
return {
name: 'babel-plugin-react-native-attributify',
visitor: {
Program(path: NodePath<t.Program>, state: PluginPass) {
if (patterns.length === 0) return
const filename = state.filename || 'unknown'
const projectRoot = process.cwd()
const relativePath = extractRelativePath(filename, projectRoot)
const styleSheetName = `${prefix}${relativePath.replace(/[^a-zA-Z0-9]/g, '_')}`
const styles: Record<string, StyleObject> = {}
if (checkStyleSheetExistence(path)) return
// Check if any JSX elements use theme variants
let hasThemeVariantsInFile = false
path.traverse({
JSXAttribute(attrPath) {
if (!t.isJSXIdentifier(attrPath.node.name)) return
let value: string | null = null
if (attrPath.node.value) {
if (t.isStringLiteral(attrPath.node.value)) {
value = attrPath.node.value.value
} else if (t.isJSXExpressionContainer(attrPath.node.value) &&
t.isStringLiteral(attrPath.node.value.expression)) {
value = attrPath.node.value.expression.value
}
}
if (value && (hasThemeVariants(value) || shouldUseAutoThemeVariants(value, config))) {
hasThemeVariantsInFile = true
}
}
})
// If theme variants are used, ensure useTheme is imported and add theme hook
if (hasThemeVariantsInFile) {
ensureThemeImport(path, t)
// Add useTheme hook call at the beginning of the component
// but skip components that use ThemeProvider
path.traverse({
FunctionDeclaration(funcPath) {
if (funcPath.node.id && funcPath.node.id.name &&
funcPath.node.id.name.match(/^[A-Z]/)) { // Component function
// Check if this component uses ThemeProvider
const usesThemeProvider = checkIfComponentUsesThemeProvider(funcPath, t)
if (!usesThemeProvider) {
addThemeHookToComponent(funcPath, t)
}
}
},
ArrowFunctionExpression(funcPath) {
const parent = funcPath.parent
if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id) &&
parent.id.name.match(/^[A-Z]/)) { // Component arrow function
// Check if this component uses ThemeProvider
const usesThemeProvider = checkIfComponentUsesThemeProvider(funcPath, t)
if (!usesThemeProvider) {
addThemeHookToComponent(funcPath, t)
}
}
}
})
}
processJSXElements(path, patterns, styles, styleSheetName, t, prefix, config)
if (Object.keys(styles).length > 0) {
const styleSheet = generateStyleSheet(styles, styleSheetName, t)
ensureStyleSheetImport(path, t)
const lastImportIndex = path.node.body.reduce((index, node, currentIndex) =>
t.isImportDeclaration(node) ? currentIndex : index,
-1
)
path.node.body.splice(lastImportIndex + 1, 0, styleSheet)
}
}
}
}
}