import { dirname , extname , resolve } from 'path' ; import { Binding , NodePath } from '@babel/traverse' ; import * as t from '@babel/types' ; import { existsSync } from 'fs-extra' ; import { evaluateAstNode } from './evaluateAstNode' ; import { getSourceModule } from './getSourceModule' ; const isLocalImport = ( path ) => path . startsWith ( '.' ) || path . startsWith ( '/' ) ; function resolveImportPath ( sourcePath , path ) { const sourceDir = dirname ( sourcePath ) ; if ( isLocalImport ( path ) ) { if ( extname ( path ) === '' ) { path += '.js' ; } return resolve ( sourceDir , path ) ; } return path ; } function importModule ( path ) { const filenames = [ path . replace ( '.js' , '.tsx' ) , path . replace ( '.js' , '.ts' ) , path ] ; for ( const file of filenames ) { if ( existsSync ( file ) ) { return require ( file ) ; } } return null ; } export function getStaticBindingsForScope ( scope , whitelist = [ ] , sourcePath , bindingCache , shouldPrintDebug ) { const bindings = scope . getAllBindings ( ) as any ; const ret = { } ; if ( shouldPrintDebug ) { console . log ( '  ' , Object . keys ( bindings ) . length , 'variables in scope' ) ; } const program = scope . getProgramParent ( ) . block as t . Program ; for ( const node of program . body ) { if ( t . isImportDeclaration ( node ) ) { const importPath = node . source . value ; if ( ! node . specifiers . length ) continue ; if ( ! isLocalImport ( importPath ) ) { continue ; } const moduleName = resolveImportPath ( sourcePath , importPath ) ; const isOnWhitelist = whitelist . some ( ( test ) => moduleName . endsWith ( test ) ) ; if ( ! isOnWhitelist ) continue ; const src = importModule ( moduleName ) ; if ( ! src ) continue ; for ( const specifier of node . specifiers ) { if ( t . isImportSpecifier ( specifier ) && t . isIdentifier ( specifier . imported ) ) { if ( typeof src [ specifier . imported . name ] !== 'undefined' ) { const val = src [ specifier . local . name ] ; ret [ specifier . local . name ] = val ; } } } } } if ( ! bindingCache ) { throw new Error ( 'bindingCache is a required param' ) ; } for ( const k in bindings ) { const binding = bindings [ k ] ; const sourceModule = getSourceModule ( k , binding ) ; if ( sourceModule ) { if ( ! sourceModule . sourceModule ) { continue ; } const moduleName = resolveImportPath ( sourcePath , sourceModule . sourceModule ) ; const isOnWhitelist = whitelist . some ( ( test ) => moduleName . endsWith ( test ) ) ; if ( isOnWhitelist ) { const src = importModule ( moduleName ) ; if ( ! src ) { console . log ( `⚠️ missing file ${ moduleName } via ${ sourcePath } import ${ sourceModule . sourceModule }?` ) ; return { } ; } if ( sourceModule . destructured ) { if ( sourceModule . imported ) { ret [ k ] = src [ sourceModule . imported ] ; } } else { ret [ k ] = src ; } } continue ; } const { parent , parentPath } = binding . path ; if ( ! t . isVariableDeclaration ( parent ) || parent . kind !== 'const' ) { continue ; } const dec = parent . declarations . find ( ( d ) => t . isIdentifier ( d . id ) && d . id . name === k ) ; if ( ! dec || ! dec . init ) { continue ; } if ( typeof dec . id . start !== 'number' || typeof dec . id . end !== 'number' ) { console . error ( 'dec.id.start/end is not a number' ) ; continue ; } if ( ! t . isIdentifier ( dec . id ) ) { console . error ( 'dec is not an identifier' ) ; continue ; } const cacheKey = `${ dec . id . name }_${ dec . id . start }-${ dec . id . end }` ; if ( bindingCache . hasOwnProperty ( cacheKey ) ) { ret [ k ] = bindingCache [ cacheKey ] ; continue ; } if ( bindingCache . hasOwnProperty ( cacheKey ) ) { ret [ k ] = bindingCache [ cacheKey ] ; continue ; } try { ret [ k ] = evaluateAstNode ( dec . init , undefined , shouldPrintDebug ) ; bindingCache [ cacheKey ] = ret [ k ] ; continue ; } catch ( e ) { } } return ret ; }