Skip to content

Commit 31fa37f

Browse files
committed
[snackui] many native fixes, fix evaluating constants for native, Pressable for buttons
1 parent bf4ff73 commit 31fa37f

18 files changed

+157
-190
lines changed

jest.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
module.exports = {
2+
watchman: false,
23
testMatch: ['**/tests/*.[jt]s?(x)'],
34
transform: {
45
'^.+\\.[jt]sx?$': 'esbuild-jest',

packages/babel-plugin/src/index.ts

+28-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
process.env.SNACKUI_COMPILE_PROCESS = '1'
22

3+
import { basename } from 'path'
4+
35
import generator from '@babel/generator'
46
import { declare } from '@babel/helper-plugin-utils'
57
import template from '@babel/template'
68
import { Visitor } from '@babel/traverse'
79
import * as t from '@babel/types'
8-
import { SnackOptions, createExtractor, literalToAst } from '@snackui/static'
10+
import { SnackOptions, createExtractor, isSimpleSpread, literalToAst } from '@snackui/static'
911

1012
const importNativeView = template(`
1113
import { View as __ReactNativeView, Text as __ReactNativeText } from 'react-native';
@@ -47,8 +49,16 @@ export default declare(function snackBabelPlugin(
4749

4850
const shouldPrintDebug = firstComment?.trim() === 'debug'
4951

50-
function addSheetStyle(style: any) {
51-
const key = `${Object.keys(sheetStyles).length}`
52+
function addSheetStyle(style: any, node: t.JSXOpeningElement) {
53+
const styleIndex = `${Object.keys(sheetStyles).length}`
54+
let key = styleIndex
55+
if (process.env.NODE_ENV === 'development') {
56+
const lineNumbers = node.loc
57+
? node.loc.start.line +
58+
(node.loc.start.line !== node.loc.end.line ? `-${node.loc.end.line}` : '')
59+
: ''
60+
key = `${styleIndex}:${basename(sourceFileName)}:${lineNumbers}`
61+
}
5262
sheetStyles[key] = style
5363
return readStyleExpr(key)
5464
}
@@ -91,26 +101,35 @@ export default declare(function snackBabelPlugin(
91101
onExtractTag(props) {
92102
assertValidTag(props.node)
93103

94-
const baseStyleExpr = addSheetStyle(props.viewStyles)
104+
const baseStyleExpr = addSheetStyle(props.viewStyles, props.node)
95105
const stylesExpr = t.arrayExpression([baseStyleExpr])
96106

107+
let finalAttrs: (t.JSXAttribute | t.JSXSpreadAttribute)[] = []
108+
97109
for (const attr of props.attrs) {
98110
switch (attr.type) {
99111
case 'ternary':
100-
const cons = addSheetStyle(attr.value.consequent)
101-
const alt = addSheetStyle(attr.value.alternate)
112+
const cons = addSheetStyle(attr.value.consequent, props.node)
113+
const alt = addSheetStyle(attr.value.alternate, props.node)
102114
stylesExpr.elements.push(t.conditionalExpression(attr.value.test, cons, alt))
103115
break
104116
case 'attr':
105117
if (t.isJSXSpreadAttribute(attr.value)) {
106-
stylesExpr.elements.push(
107-
t.memberExpression(attr.value.argument, t.identifier('style'))
108-
)
118+
if (isSimpleSpread(attr.value)) {
119+
stylesExpr.elements.push(
120+
t.memberExpression(attr.value.argument, t.identifier('style'))
121+
)
122+
} else {
123+
finalAttrs.push(attr.value)
124+
}
125+
} else {
126+
finalAttrs.push(attr.value)
109127
}
110128
break
111129
}
112130
}
113131

132+
props.node.attributes = finalAttrs
114133
props.node.attributes.push(
115134
t.jsxAttribute(t.jsxIdentifier('style'), t.jsxExpressionContainer(stylesExpr))
116135
)

packages/babel-plugin/types.d.ts.map

+1-1
Original file line numberDiff line numberDiff line change

packages/snackui-static/src/extractor/buildClassName.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import { ClassNameObject } from '../types'
44

55
export function buildClassName(
66
classNameObjects: ClassNameObject[]
7-
): t.Expression | t.StringLiteral {
8-
// @ts-expect-error
7+
): t.Expression | t.StringLiteral | null {
98
return classNameObjects.reduce<t.Expression | null>((acc, val) => {
109
if (acc == null) {
1110
if (

packages/snackui-static/src/extractor/createExtractor.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ export function createExtractor() {
209209
' ⚠️ SnackUI: warning! no themesFile option given, themes will fallback'
210210
)
211211
}
212-
console.log(' attemptEval staticNamespace', { staticNamespace })
212+
console.log(' attemptEval staticNamespace', staticNamespace)
213213
}
214214

215215
// called when evaluateAstNode encounters a dynamic-looking prop

packages/snackui-static/src/extractor/extractToClassNames.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,11 @@ export function extractToClassNames(
201201
if (finalClassNames.length) {
202202
// inserts the _cn variable and uses it for className
203203
const names = buildClassName(finalClassNames)
204-
const nameExpr = hoistClassNames(jsxPath, existingHoists, names)
204+
const nameExpr = names ? hoistClassNames(jsxPath, existingHoists, names) : null
205205
let expr = nameExpr
206206

207207
// if has some spreads, use concat helper
208-
if (!t.isIdentifier(nameExpr)) {
208+
if (nameExpr && !t.isIdentifier(nameExpr)) {
209209
ensureImportingConcat(programPath)
210210
const simpleSpreads = attrs.filter(
211211
(x) => t.isJSXSpreadAttribute(x.value) && isSimpleSpread(x.value)

packages/snackui-static/src/extractor/getPropValueFromAttributes.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import generate from '@babel/generator'
22
import * as t from '@babel/types'
3-
import invariant from 'invariant'
43

54
import { accessSafe } from './accessSafe'
65

packages/snackui-static/src/extractor/getStaticBindingsForScope.ts

+62-76
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import path from 'path'
1+
import { dirname, extname, resolve } from 'path'
22

3+
import { Binding, NodePath } from '@babel/traverse'
34
import * as t from '@babel/types'
45
import { defaultMediaQueries } from '@snackui/node'
56
import { existsSync } from 'fs-extra'
@@ -12,50 +13,73 @@ const snackuiConstants = {
1213
defaultMediaQueries,
1314
}
1415

15-
interface Binding {
16-
identifier: any
17-
scope: any
18-
path: any
19-
// this list is incomplete
20-
kind: 'module' | 'let' | 'const' | 'var' | 'param' | 'hoisted' | 'local'
16+
const isLocalImport = (path: string) => path.startsWith('.') || path.startsWith('/')
2117

22-
constantViolations: any[]
23-
constant: boolean
24-
25-
referencePaths: any[] // NodePath[]
26-
referenced: boolean
27-
references: number
28-
29-
hasDeoptedValue: boolean
30-
hasValue: boolean
31-
value: any
18+
function resolveImportPath(sourceFileName: string, path: string) {
19+
const sourceDir = dirname(sourceFileName)
20+
if (isLocalImport(path)) {
21+
if (extname(path) === '') {
22+
path += '.js'
23+
}
24+
return resolve(sourceDir, path)
25+
}
26+
return path
3227
}
3328

34-
// const fileCache: {
35-
// [key: string]: {
36-
// mtimeMs: number
37-
// path: string
38-
// src: Object
39-
// }
40-
// } = {}
41-
42-
// const clearCache = debounce(() => {
43-
// console.log('clear cache')
44-
// Object.keys(require.cache).forEach((id) => {
45-
// delete require.cache[id]
46-
// })
47-
// }, 2000)
29+
function importModule(path: string) {
30+
const filenames = [path.replace('.js', '.tsx'), path.replace('.js', '.ts'), path]
31+
for (const file of filenames) {
32+
if (existsSync(file)) {
33+
// TODO we can clear this when we see updates on it later on
34+
return require(file)
35+
}
36+
}
37+
return null
38+
}
4839

4940
export function getStaticBindingsForScope(
50-
scope: any,
41+
scope: NodePath<t.JSXElement>['scope'],
5142
whitelist: string[] = [],
5243
sourceFileName: string,
5344
bindingCache: Record<string, string | null>,
5445
shouldPrintDebug: boolean
5546
): Record<string, any> {
56-
const bindings: Record<string, Binding> = scope.getAllBindings()
47+
const bindings: Record<string, Binding> = scope.getAllBindings() as any
5748
const ret: Record<string, any> = {}
58-
const sourceDir = path.dirname(sourceFileName)
49+
50+
const program = scope.getProgramParent().block as t.Program
51+
52+
// on react native at least it doesnt find some bindings? not sure why
53+
// lets add in whitelisted imports if they exist
54+
for (const node of program.body) {
55+
if (t.isImportDeclaration(node)) {
56+
const importPath = node.source.value
57+
if (!node.specifiers.length) continue
58+
if (!isLocalImport(importPath)) {
59+
if (importPath === 'snackui') {
60+
Object.assign(ret, snackuiConstants)
61+
}
62+
continue
63+
}
64+
const moduleName = resolveImportPath(sourceFileName, importPath)
65+
const isOnWhitelist = whitelist.some((test) => moduleName.endsWith(test))
66+
if (!isOnWhitelist) continue
67+
const src = importModule(moduleName)
68+
if (!src) continue
69+
for (const specifier of node.specifiers) {
70+
if (t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported)) {
71+
if (typeof src[specifier.imported.name] !== 'undefined') {
72+
const val = src[specifier.local.name]
73+
ret[specifier.local.name] = val
74+
}
75+
}
76+
}
77+
}
78+
}
79+
80+
if (shouldPrintDebug) {
81+
console.log('import bindings', ret)
82+
}
5983

6084
if (!bindingCache) {
6185
throw new Error('bindingCache is a required param')
@@ -66,61 +90,23 @@ export function getStaticBindingsForScope(
6690

6791
// check to see if the item is a module
6892
const sourceModule = getSourceModule(k, binding)
93+
6994
if (sourceModule) {
7095
if (!sourceModule.sourceModule) {
7196
continue
7297
}
7398

74-
let moduleName = sourceModule.sourceModule
75-
// if modulePath is an absolute or relative path
76-
if (moduleName.startsWith('.') || moduleName.startsWith('/')) {
77-
// if moduleName doesn't end with an extension, add .js
78-
if (path.extname(moduleName) === '') {
79-
moduleName += '.js'
80-
}
81-
// get absolute path
82-
moduleName = path.resolve(sourceDir, moduleName)
83-
}
84-
85-
if (moduleName === 'snackui') {
86-
return snackuiConstants
87-
}
88-
99+
const moduleName = resolveImportPath(sourceFileName, sourceModule.sourceModule)
89100
const isOnWhitelist = whitelist.some((test) => moduleName.endsWith(test))
90101

91102
// TODO we could cache this at the file level.. and check if its been touched since
92103

93104
if (isOnWhitelist) {
94-
let src: any
95-
// const info = fileCache[moduleName]
96-
// if (info && statSync(info.path).mtimeMs === info.mtimeMs) {
97-
// src = info.src
98-
// } else {
99-
const filenames = [
100-
moduleName.replace('.js', '.tsx'),
101-
moduleName.replace('.js', '.ts'),
102-
moduleName,
103-
]
104-
// let mtimeMs = 0
105-
// let path = ''
106-
for (const file of filenames) {
107-
if (existsSync(file)) {
108-
// path = file
109-
src = require(file)
110-
// mtimeMs = statSync(file).mtimeMs
111-
break
112-
}
113-
}
114-
if (src === undefined) {
105+
const src = importModule(moduleName)
106+
if (!src) {
115107
console.log(`⚠️ missing file ${moduleName}?`)
116108
return {}
117109
}
118-
// fileCache[moduleName] = {
119-
// path,
120-
// src,
121-
// mtimeMs,
122-
// }
123-
// }
124110
if (sourceModule.destructured) {
125111
if (sourceModule.imported) {
126112
ret[k] = src[sourceModule.imported]

packages/snackui-static/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export { createExtractor } from './extractor/createExtractor'
66
export { literalToAst } from './extractor/literalToAst'
77
export * from './constants'
88
export * from './extractor/extractToClassNames'
9+
export * from './extractor/extractHelpers'

0 commit comments

Comments
 (0)