1import { 2 AppJSONConfig, 3 ExpoConfig, 4 getConfig, 5 getProjectConfigDescriptionWithPaths, 6 ProjectConfig, 7} from '@expo/config'; 8import chalk from 'chalk'; 9 10import * as Log from '../../../log'; 11import { env } from '../../../utils/env'; 12import { PrerequisiteCommandError, ProjectPrerequisite } from '../Prerequisite'; 13import { ensureDependenciesAsync } from '../dependencies/ensureDependenciesAsync'; 14 15/** Ensure the project has the required web support settings. */ 16export class WebSupportProjectPrerequisite extends ProjectPrerequisite { 17 /** Ensure a project that hasn't explicitly disabled web support has all the required packages for running in the browser. */ 18 async assertImplementation(): Promise<void> { 19 if (env.EXPO_NO_WEB_SETUP) { 20 Log.warn('Skipping web setup: EXPO_NO_WEB_SETUP is enabled.'); 21 return; 22 } 23 Log.debug('Ensuring web support is setup'); 24 25 const result = await this._shouldSetupWebSupportAsync(); 26 27 // Ensure web packages are installed 28 await this._ensureWebDependenciesInstalledAsync({ exp: result.exp }); 29 } 30 31 /** Exposed for testing. */ 32 async _shouldSetupWebSupportAsync(): Promise<ProjectConfig> { 33 const config = getConfig(this.projectRoot); 34 35 // Detect if the 'web' string is purposefully missing from the platforms array. 36 if (isWebPlatformExcluded(config.rootConfig)) { 37 // Get exact config description with paths. 38 const configName = getProjectConfigDescriptionWithPaths(this.projectRoot, config); 39 throw new PrerequisiteCommandError( 40 'WEB_SUPPORT', 41 chalk`Skipping web setup: {bold "web"} is not included in the project ${configName} {bold "platforms"} array.` 42 ); 43 } 44 45 return config; 46 } 47 48 /** Exposed for testing. */ 49 async _ensureWebDependenciesInstalledAsync({ exp }: { exp: ExpoConfig }): Promise<boolean> { 50 try { 51 return await ensureDependenciesAsync(this.projectRoot, { 52 exp, 53 installMessage: `It looks like you're trying to use web support but don't have the required dependencies installed.`, 54 warningMessage: chalk`If you're not using web, please remove the {bold "web"} string from the platforms array in the project Expo config.`, 55 requiredPackages: [ 56 // use react-native-web/package.json to skip node module cache issues when the user installs 57 // the package and attempts to resolve the module in the same process. 58 { file: 'react-native-web/package.json', pkg: 'react-native-web' }, 59 { file: 'react-dom/package.json', pkg: 'react-dom' }, 60 { file: '@expo/webpack-config/package.json', pkg: '@expo/webpack-config' }, 61 ], 62 }); 63 } catch (error) { 64 // Reset the cached check so we can re-run the check if the user re-runs the command by pressing 'w' in the Terminal UI. 65 this.resetAssertion(); 66 throw error; 67 } 68 } 69} 70 71/** Return `true` if the `web` platform is purposefully excluded from the project Expo config. */ 72export function isWebPlatformExcluded(rootConfig: AppJSONConfig): boolean { 73 // Detect if the 'web' string is purposefully missing from the platforms array. 74 const isWebExcluded = 75 Array.isArray(rootConfig.expo?.platforms) && 76 !!rootConfig.expo?.platforms.length && 77 !rootConfig.expo?.platforms.includes('web'); 78 return isWebExcluded; 79} 80