format import chalk from 'chalk' ; import { Express } from 'express' ; import http from 'http' ; import path from 'path' ; import fs from 'fs-extra' ; import socketio from 'socket.io' ; import pFilter from 'p-filter' ; import { homedir } from 'os' ; const uiSourceDirs = [ 'flipper-ui-browser' , 'flipper-ui-core' , 'flipper-plugin' , 'flipper-common' , ] ; export async function getPluginSourceFolders ( ) { const pluginFolders = [ ] ; if ( process . env . FLIPPER_NO_DEFAULT_PLUGINS ) { console . log ( '🥫  Skipping default plugins because "--no-default-plugins" flag provided' ) ; return pluginFolders ; } const flipperConfigPath = path . join ( homedir ( ) , '.flipper' , 'config.json' ) ; if ( await fs . pathExists ( flipperConfigPath ) ) { const config = await fs . readJson ( flipperConfigPath ) ; if ( config . pluginPaths ) { pluginFolders . push ( ... config . pluginPaths ) ; } } pluginFolders . push ( path . resolve ( __dirname , '..' , '..' , 'plugins' , 'public' ) ) ; pluginFolders . push ( path . resolve ( __dirname , '..' , '..' , 'plugins' , 'fb' ) ) ; return pFilter ( pluginFolders , ( p ) => fs . pathExists ( p ) ) ; } export async function startWebServerDev ( app , server , socket , rootDir ) { const Metro = electronRequire ( 'metro' ) ; const MetroResolver = electronRequire ( 'metro-resolver' ) ; const { getWatchFolders } = electronRequire ( 'flipper-pkg-lib' ) ; const babelTransformationsDir = path . resolve ( rootDir , 'babel-transformer' , 'lib' ) ; const electronRequires = path . join ( babelTransformationsDir , 'electron-requires.js' ) ; const stubModules = new Set < string > ( electronRequire ( electronRequires ) . BUILTINS ) ; if ( ! stubModules . size ) { throw new Error ( 'Failed to load list of Node builtins' ) ; } const watchFolders = await dedupeFolders ( [ ... ( await Promise . all ( uiSourceDirs . map ( ( dir ) => getWatchFolders ( path . resolve ( rootDir , dir ) ) ) ) ) . flat ( ) , ... ( await getPluginSourceFolders ( ) ) , ] ) ; const baseConfig = await Metro . loadConfig ( ) ; const config = Object . assign ( { } , baseConfig , { projectRoot : rootDir , watchFolders , transformer : { ... baseConfig . transformer , babelTransformerPath : path . join ( babelTransformationsDir , 'transform-browser' ) , } , resolver : { ... baseConfig . resolver , resolverMainFields : [ 'flipperBundlerEntry' , 'browser' , 'module' , 'main' ] , blacklistRE : [ /\.native\.js$/ ] , sourceExts : [ 'js' , 'jsx' , 'ts' , 'tsx' , 'json' , 'mjs' , 'cjs' ] , resolveRequest ( context , moduleName , ... rest ) { if ( moduleName === 'flipper' ) { return MetroResolver . resolve ( context , 'flipper-ui-core' , ... rest ) ; } if ( stubModules . has ( moduleName ) ) { console . warn ( `Found a reference to built-in module '${ moduleName }', which will be stubbed out. Referer: ${ context . originModulePath }` ) ; return { type : 'empty' , } ; } return MetroResolver . resolve ( { ... context , resolveRequest : null , } , moduleName , ... rest ) ; } , } , watch : true , } ) ; const connectMiddleware = await Metro . createConnectMiddleware ( config ) ; app . use ( connectMiddleware . middleware ) ; connectMiddleware . attachHmrServer ( server ) ; app . use ( function ( err , _req , _res , next ) { console . error ( chalk . red ( '\n\nCompile error in client bundle\n' ) , err ) ; socket . local . emit ( 'hasErrors' , err . toString ( ) ) ; next ( ) ; } ) ; console . log ( 'DEV webserver started.' ) ; } async function dedupeFolders ( paths ) { return pFilter ( paths . filter ( ( value , index , self ) => self . indexOf ( value ) === index ) , ( f ) => fs . pathExists ( f ) ) ; }