1
- import path from 'path'
1
+ import { dirname , extname , resolve } from 'path'
2
2
3
+ import { Binding , NodePath } from '@babel/traverse'
3
4
import * as t from '@babel/types'
4
5
import { defaultMediaQueries } from '@snackui/node'
5
6
import { existsSync } from 'fs-extra'
@@ -12,50 +13,73 @@ const snackuiConstants = {
12
13
defaultMediaQueries,
13
14
}
14
15
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 ( '/' )
21
17
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
32
27
}
33
28
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
+ }
48
39
49
40
export function getStaticBindingsForScope (
50
- scope : any ,
41
+ scope : NodePath < t . JSXElement > [ 'scope' ] ,
51
42
whitelist : string [ ] = [ ] ,
52
43
sourceFileName : string ,
53
44
bindingCache : Record < string , string | null > ,
54
45
shouldPrintDebug : boolean
55
46
) : Record < string , any > {
56
- const bindings : Record < string , Binding > = scope . getAllBindings ( )
47
+ const bindings : Record < string , Binding > = scope . getAllBindings ( ) as any
57
48
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
+ }
59
83
60
84
if ( ! bindingCache ) {
61
85
throw new Error ( 'bindingCache is a required param' )
@@ -66,61 +90,23 @@ export function getStaticBindingsForScope(
66
90
67
91
// check to see if the item is a module
68
92
const sourceModule = getSourceModule ( k , binding )
93
+
69
94
if ( sourceModule ) {
70
95
if ( ! sourceModule . sourceModule ) {
71
96
continue
72
97
}
73
98
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 )
89
100
const isOnWhitelist = whitelist . some ( ( test ) => moduleName . endsWith ( test ) )
90
101
91
102
// TODO we could cache this at the file level.. and check if its been touched since
92
103
93
104
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 ) {
115
107
console . log ( `⚠️ missing file ${ moduleName } ?` )
116
108
return { }
117
109
}
118
- // fileCache[moduleName] = {
119
- // path,
120
- // src,
121
- // mtimeMs,
122
- // }
123
- // }
124
110
if ( sourceModule . destructured ) {
125
111
if ( sourceModule . imported ) {
126
112
ret [ k ] = src [ sourceModule . imported ]
0 commit comments