"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateWithSchemaFileAsync = validateWithSchemaFileAsync; exports.validateWithSchema = validateWithSchema; exports.validateLowLatencyAsync = validateLowLatencyAsync; exports.validateWithNetworkAsync = validateWithNetworkAsync; exports.getExpoSdkStatus = getExpoSdkStatus; exports.EXPO_SDK_NOT_IMPORTED = exports.EXPO_SDK_NOT_INSTALLED = exports.EXPO_SDK_INSTALLED_AND_IMPORTED = exports.FATAL = exports.ERROR = exports.WARNING = exports.NO_ISSUES = void 0; function _lodash() { const data = _interopRequireDefault(require("lodash")); _lodash = function () { return data; }; return data; } function _semver() { const data = _interopRequireDefault(require("semver")); _semver = function () { return data; }; return data; } function _fsExtra() { const data = _interopRequireDefault(require("fs-extra")); _fsExtra = function () { return data; }; return data; } function _getenv() { const data = _interopRequireDefault(require("getenv")); _getenv = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _spawnAsync() { const data = _interopRequireDefault(require("@expo/spawn-async")); _spawnAsync = function () { return data; }; return data; } function ConfigUtils() { const data = _interopRequireWildcard(require("@expo/config")); ConfigUtils = function () { return data; }; return data; } function _schemer() { const data = _interopRequireWildcard(require("@expo/schemer")); _schemer = function () { return data; }; return data; } function ExpSchema() { const data = _interopRequireWildcard(require("./ExpSchema")); ExpSchema = function () { return data; }; return data; } function ProjectUtils() { const data = _interopRequireWildcard(require("./ProjectUtils")); ProjectUtils = function () { return data; }; return data; } function _Config() { const data = _interopRequireDefault(require("../Config")); _Config = function () { return data; }; return data; } function Versions() { const data = _interopRequireWildcard(require("../Versions")); Versions = function () { return data; }; return data; } function Watchman() { const data = _interopRequireWildcard(require("../Watchman")); Watchman = function () { return data; }; return data; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const NO_ISSUES = 0; exports.NO_ISSUES = NO_ISSUES; const WARNING = 1; exports.WARNING = WARNING; const ERROR = 2; exports.ERROR = ERROR; const FATAL = 3; exports.FATAL = FATAL; const MIN_WATCHMAN_VERSION = '4.6.0'; const MIN_NPM_VERSION = '3.0.0'; const CORRECT_NPM_VERSION = 'latest'; const WARN_NPM_VERSION_RANGES = ['>= 5.0.0 < 5.7.0']; const BAD_NPM_VERSION_RANGES = ['>= 5.0.0 <= 5.0.3']; function _isNpmVersionWithinRanges(npmVersion, ranges) { return _lodash().default.some(ranges, range => _semver().default.satisfies(npmVersion, range)); } async function _checkNpmVersionAsync(projectRoot) { try { try { let yarnVersionResponse = await (0, _spawnAsync().default)('yarnpkg', ['--version']); if (yarnVersionResponse.status === 0) { return NO_ISSUES; } } catch (e) {} let npmVersionResponse = await (0, _spawnAsync().default)('npm', ['--version']); let npmVersion = _lodash().default.trim(npmVersionResponse.stdout); if (_semver().default.lt(npmVersion, MIN_NPM_VERSION) || _isNpmVersionWithinRanges(npmVersion, BAD_NPM_VERSION_RANGES)) { ProjectUtils().logError(projectRoot, 'expo', `Error: You are using npm version ${npmVersion}. We recommend the latest version ${CORRECT_NPM_VERSION}. To install it, run 'npm i -g npm@${CORRECT_NPM_VERSION}'.`, 'doctor-npm-version'); return WARNING; } else if (_isNpmVersionWithinRanges(npmVersion, WARN_NPM_VERSION_RANGES)) { ProjectUtils().logWarning(projectRoot, 'expo', `Warning: You are using npm version ${npmVersion}. There may be bugs in this version, use it at your own risk. We recommend version ${CORRECT_NPM_VERSION}.`, 'doctor-npm-version'); } else { ProjectUtils().clearNotification(projectRoot, 'doctor-npm-version'); } } catch (e) { ProjectUtils().logWarning(projectRoot, 'expo', `Warning: Could not determine npm version. Make sure your version is >= ${MIN_NPM_VERSION} - we recommend ${CORRECT_NPM_VERSION}.`, 'doctor-npm-version'); return WARNING; } return NO_ISSUES; } async function _checkWatchmanVersionAsync(projectRoot) { // There's no point in checking any of this stuff if watchman isn't supported on this platform if (!Watchman().isPlatformSupported()) { ProjectUtils().clearNotification(projectRoot, 'doctor-watchman-version'); return; } let watchmanVersion = await Watchman().unblockAndGetVersionAsync(projectRoot); // If we can't get the watchman version, `getVersionAsync` will return `null` if (!watchmanVersion) { // watchman is probably just not installed ProjectUtils().clearNotification(projectRoot, 'doctor-watchman-version'); return; } if (_semver().default.lt(watchmanVersion, MIN_WATCHMAN_VERSION)) { let warningMessage = `Warning: You are using an old version of watchman (v${watchmanVersion}). This may cause problems for you.\n\nWe recommend that you either uninstall watchman (and XDE will try to use a copy it is bundled with) or upgrade watchman to a newer version, at least v${MIN_WATCHMAN_VERSION}.`; // Add a note about homebrew if the user is on a Mac if (process.platform === 'darwin') { warningMessage += `\n\nIf you are using homebrew, try:\nbrew uninstall watchman; brew install watchman`; } ProjectUtils().logWarning(projectRoot, 'expo', warningMessage, 'doctor-watchman-version'); } else { ProjectUtils().clearNotification(projectRoot, 'doctor-watchman-version'); } } async function validateWithSchemaFileAsync(projectRoot, schemaPath) { let { exp } = await ProjectUtils().readConfigJsonAsync(projectRoot); let schema = JSON.parse((await _fsExtra().default.readFile(schemaPath, 'utf8'))); return validateWithSchema(projectRoot, exp, schema.schema, 'exp.json', 'UNVERSIONED', true); } async function validateWithSchema(projectRoot, exp, schema, configName, sdkVersion, validateAssets) { let schemaErrorMessage; let assetsErrorMessage; let validator = new (_schemer().default)(schema, { rootDir: projectRoot }); // Validate the schema itself try { await validator.validateSchemaAsync(exp); } catch (e) { if (e instanceof _schemer().SchemerError) { schemaErrorMessage = `Error: Problem${e.errors.length > 1 ? 's' : ''} validating fields in ${configName}. See https://docs.expo.io/versions/v${sdkVersion}/workflow/configuration/`; schemaErrorMessage += e.errors.map(formatValidationError).join(''); } } if (validateAssets) { try { await validator.validateAssetsAsync(exp); } catch (e) { if (e instanceof _schemer().SchemerError) { assetsErrorMessage = `Error: Problem${e.errors.length > 1 ? '' : 's'} validating asset fields in ${configName}. See ${_Config().default.helpUrl}`; assetsErrorMessage += e.errors.map(formatValidationError).join(''); } } } return { schemaErrorMessage, assetsErrorMessage }; } function formatValidationError(validationError) { return `\n • ${validationError.fieldPath ? 'Field: ' + validationError.fieldPath + ' - ' : ''}${validationError.message}.`; } async function _validateExpJsonAsync(exp, pkg, projectRoot, allowNetwork) { if (!exp || !pkg) { // readConfigJsonAsync already logged an error return FATAL; } try { await _checkWatchmanVersionAsync(projectRoot); } catch (e) { ProjectUtils().logWarning(projectRoot, 'expo', `Warning: Problem checking watchman version. ${e.message}.`, 'doctor-problem-checking-watchman-version'); } ProjectUtils().clearNotification(projectRoot, 'doctor-problem-checking-watchman-version'); const expJsonExists = await ConfigUtils().fileExistsAsync(_path().default.join(projectRoot, 'exp.json')); const appJsonExists = await ConfigUtils().fileExistsAsync(_path().default.join(projectRoot, 'app.json')); if (expJsonExists && appJsonExists) { ProjectUtils().logWarning(projectRoot, 'expo', `Warning: Both app.json and exp.json exist in this directory. Only one should exist for a single project.`, 'doctor-both-app-and-exp-json'); return WARNING; } ProjectUtils().clearNotification(projectRoot, 'doctor-both-app-and-exp-json'); let sdkVersion = exp.sdkVersion; const configName = await ConfigUtils().configFilenameAsync(projectRoot); // Warn if sdkVersion is UNVERSIONED if (sdkVersion === 'UNVERSIONED' && !process.env.EXPO_SKIP_MANIFEST_VALIDATION_TOKEN) { ProjectUtils().logError(projectRoot, 'expo', `Error: Using unversioned Expo SDK. Do not publish until you set sdkVersion in ${configName}`, 'doctor-unversioned'); return ERROR; } ProjectUtils().clearNotification(projectRoot, 'doctor-unversioned'); let sdkVersions = await Versions().sdkVersionsAsync(); if (!sdkVersions) { ProjectUtils().logError(projectRoot, 'expo', `Error: Couldn't connect to SDK versions server`, 'doctor-versions-endpoint-failed'); return ERROR; } ProjectUtils().clearNotification(projectRoot, 'doctor-versions-endpoint-failed'); if (!sdkVersion || !sdkVersions[sdkVersion]) { ProjectUtils().logError(projectRoot, 'expo', `Error: Invalid sdkVersion. Valid options are ${_lodash().default.keys(sdkVersions).join(', ')}`, 'doctor-invalid-sdk-version'); return ERROR; } ProjectUtils().clearNotification(projectRoot, 'doctor-invalid-sdk-version'); // Skip validation if the correct token is set in env if (sdkVersion !== 'UNVERSIONED') { try { let schema = await ExpSchema().getSchemaAsync(sdkVersion); let { schemaErrorMessage, assetsErrorMessage } = await validateWithSchema(projectRoot, exp, schema, configName, sdkVersion, allowNetwork); if (schemaErrorMessage) { ProjectUtils().logError(projectRoot, 'expo', schemaErrorMessage, 'doctor-schema-validation'); } else { ProjectUtils().clearNotification(projectRoot, 'doctor-schema-validation'); } if (assetsErrorMessage) { ProjectUtils().logError(projectRoot, 'expo', assetsErrorMessage, `doctor-validate-asset-fields`); } else { ProjectUtils().clearNotification(projectRoot, `doctor-validate-asset-fields`); } ProjectUtils().clearNotification(projectRoot, 'doctor-schema-validation-exception'); if (schemaErrorMessage || assetsErrorMessage) return ERROR; } catch (e) { ProjectUtils().logWarning(projectRoot, 'expo', `Warning: Problem validating ${configName}: ${e.message}.`, 'doctor-schema-validation-exception'); } } const reactNativeIssue = await _validateReactNativeVersionAsync(exp, pkg, projectRoot, sdkVersions, sdkVersion); if (reactNativeIssue !== NO_ISSUES) { return reactNativeIssue; } // TODO: Check any native module versions here return NO_ISSUES; } async function _validateReactNativeVersionAsync(exp, pkg, projectRoot, sdkVersions, sdkVersion) { if (_Config().default.validation.reactNativeVersionWarnings) { let reactNative = pkg.dependencies ? pkg.dependencies['react-native'] : null; // react-native is required if (!reactNative) { ProjectUtils().logError(projectRoot, 'expo', `Error: Can't find react-native in package.json dependencies`, 'doctor-no-react-native-in-package-json'); return ERROR; } ProjectUtils().clearNotification(projectRoot, 'doctor-no-react-native-in-package-json'); if (!exp.isDetached) { return NO_ISSUES; // (TODO-2017-07-20): Validate the react-native version if it uses // officially published package rather than Expo fork. Expo fork of // react-native was required before CRNA. We now only run the react-native // validation of the version if we are using the fork. We should probably // validate the version here as well such that it matches with the // react-native version compatible with the selected SDK. } // Expo fork of react-native is required if (!/expo\/react-native/.test(reactNative)) { ProjectUtils().logWarning(projectRoot, 'expo', `Warning: Not using the Expo fork of react-native. See ${_Config().default.helpUrl}.`, 'doctor-not-using-expo-fork'); return WARNING; } ProjectUtils().clearNotification(projectRoot, 'doctor-not-using-expo-fork'); try { let reactNativeTag = reactNative.match(/sdk-\d+\.\d+\.\d+/)[0]; let sdkVersionObject = sdkVersions[sdkVersion]; // TODO: Want to be smarter about this. Maybe warn if there's a newer version. if (_semver().default.major(Versions().parseSdkVersionFromTag(reactNativeTag)) !== _semver().default.major(Versions().parseSdkVersionFromTag(sdkVersionObject['expoReactNativeTag']))) { ProjectUtils().logWarning(projectRoot, 'expo', `Warning: Invalid version of react-native for sdkVersion ${sdkVersion}. Use github:expo/react-native#${sdkVersionObject['expoReactNativeTag']}`, 'doctor-invalid-version-of-react-native'); return WARNING; } ProjectUtils().clearNotification(projectRoot, 'doctor-invalid-version-of-react-native'); ProjectUtils().clearNotification(projectRoot, 'doctor-malformed-version-of-react-native'); } catch (e) { ProjectUtils().logWarning(projectRoot, 'expo', `Warning: ${reactNative} is not a valid version. Version must be in the form of sdk-x.y.z. Please update your package.json file.`, 'doctor-malformed-version-of-react-native'); return WARNING; } } return NO_ISSUES; } async function _validateNodeModulesAsync(projectRoot) { let { exp } = await ConfigUtils().readConfigJsonAsync(projectRoot); let nodeModulesPath = projectRoot; if (exp.nodeModulesPath) { nodeModulesPath = _path().default.resolve(projectRoot, exp.nodeModulesPath); } // Check to make sure node_modules exists at all try { let result = _fsExtra().default.statSync(_path().default.join(nodeModulesPath, 'node_modules')); if (!result.isDirectory()) { ProjectUtils().logError(projectRoot, 'expo', `Error: node_modules directory is missing. Please run \`npm install\` in your project directory.`, 'doctor-node-modules-missing'); return FATAL; } ProjectUtils().clearNotification(projectRoot, 'doctor-node-modules-missing'); } catch (e) { ProjectUtils().logError(projectRoot, 'expo', `Error: node_modules directory is missing. Please run \`npm install\` in your project directory.`, 'doctor-node-modules-missing'); return FATAL; } // Check to make sure react native is installed try { ConfigUtils().resolveModule('react-native/local-cli/cli.js', projectRoot, exp); ProjectUtils().clearNotification(projectRoot, 'doctor-react-native-not-installed'); } catch (e) { if (e.code === 'MODULE_NOT_FOUND') { ProjectUtils().logError(projectRoot, 'expo', `Error: React Native is not installed. Please run \`npm install\` in your project directory.`, 'doctor-react-native-not-installed'); return FATAL; } else { throw e; } } return NO_ISSUES; } async function validateLowLatencyAsync(projectRoot) { return validateAsync(projectRoot, false); } async function validateWithNetworkAsync(projectRoot) { return validateAsync(projectRoot, true); } async function validateAsync(projectRoot, allowNetwork) { if (_getenv().default.boolish('EXPO_NO_DOCTOR', false)) { return NO_ISSUES; } let { exp, pkg } = await ConfigUtils().readConfigJsonAsync(projectRoot); let status = await _checkNpmVersionAsync(projectRoot); if (status === FATAL) { return status; } const expStatus = await _validateExpJsonAsync(exp, pkg, projectRoot, allowNetwork); if (expStatus === FATAL) { return expStatus; } status = Math.max(status, expStatus); if (exp && !exp.ignoreNodeModulesValidation) { let nodeModulesStatus = await _validateNodeModulesAsync(projectRoot); if (nodeModulesStatus > status) { return nodeModulesStatus; } } return status; } const EXPO_SDK_INSTALLED_AND_IMPORTED = 0; exports.EXPO_SDK_INSTALLED_AND_IMPORTED = EXPO_SDK_INSTALLED_AND_IMPORTED; const EXPO_SDK_NOT_INSTALLED = 1; exports.EXPO_SDK_NOT_INSTALLED = EXPO_SDK_NOT_INSTALLED; const EXPO_SDK_NOT_IMPORTED = 2; exports.EXPO_SDK_NOT_IMPORTED = EXPO_SDK_NOT_IMPORTED; async function getExpoSdkStatus(projectRoot) { let { pkg } = await ConfigUtils().readConfigJsonAsync(projectRoot); try { let sdkPkg; if (pkg.dependencies['exponent']) { sdkPkg = 'exponent'; } else if (pkg.dependencies['expo']) { sdkPkg = 'expo'; } else { return EXPO_SDK_NOT_INSTALLED; } let mainFilePath = _path().default.join(projectRoot, pkg.main); let mainFile = await _fsExtra().default.readFile(mainFilePath, 'utf8'); // TODO: support separate .ios.js and .android.js files if (mainFile.includes(`from '${sdkPkg}'`) || mainFile.includes(`require('${sdkPkg}')`)) { return EXPO_SDK_INSTALLED_AND_IMPORTED; } else { return EXPO_SDK_NOT_IMPORTED; } } catch (e) { return EXPO_SDK_NOT_IMPORTED; } } //# sourceMappingURL=../__sourcemaps__/project/Doctor.js.map