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 // This never seems to work when prompting, installing, and running -- instead just inform the user to run the install command and try again. 53 skipPrompt: true, 54 exp, 55 installMessage: `It looks like you're trying to use web support but don't have the required dependencies installed.`, 56 warningMessage: chalk`If you're not using web, please ensure you remove the {bold "web"} string from the platforms array in the project Expo config.`, 57 requiredPackages: [ 58 // use react-native-web/package.json to skip node module cache issues when the user installs 59 // the package and attempts to resolve the module in the same process. 60 { file: 'react-native-web/package.json', pkg: 'react-native-web', version: '~0.17.1' }, 61 { file: 'react-dom/package.json', pkg: 'react-dom', version: '^17.0.1' }, 62 // `webpack` and `webpack-dev-server` should be installed in the `@expo/webpack-config` 63 { 64 file: '@expo/webpack-config/package.json', 65 pkg: '@expo/webpack-config', 66 version: '~0.16.2', 67 dev: true, 68 }, 69 ], 70 }); 71 } catch (error) { 72 // 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. 73 this.resetAssertion(); 74 throw error; 75 } 76 } 77} 78 79/** Return `true` if the `web` platform is purposefully excluded from the project Expo config. */ 80export function isWebPlatformExcluded(rootConfig: AppJSONConfig): boolean { 81 // Detect if the 'web' string is purposefully missing from the platforms array. 82 const isWebExcluded = 83 Array.isArray(rootConfig.expo?.platforms) && 84 !!rootConfig.expo?.platforms.length && 85 !rootConfig.expo?.platforms.includes('web'); 86 return isWebExcluded; 87} 88