You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
413 lines
19 KiB
413 lines
19 KiB
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.WARNING = exports.NO_ISSUES = exports.FATAL = exports.ERROR = void 0;
|
|
exports.validateExpoServersAsync = validateExpoServersAsync;
|
|
exports.validateWithNetworkAsync = validateWithNetworkAsync;
|
|
exports.validateWithoutNetworkAsync = validateWithoutNetworkAsync;
|
|
function _config() {
|
|
const data = require("@expo/config");
|
|
_config = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _schemer() {
|
|
const data = _interopRequireWildcard(require("@expo/schemer"));
|
|
_schemer = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _spawnAsync() {
|
|
const data = _interopRequireDefault(require("@expo/spawn-async"));
|
|
_spawnAsync = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _child_process() {
|
|
const data = require("child_process");
|
|
_child_process = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _getenv() {
|
|
const data = _interopRequireDefault(require("getenv"));
|
|
_getenv = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _isReachable() {
|
|
const data = _interopRequireDefault(require("is-reachable"));
|
|
_isReachable = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _memoize() {
|
|
const data = _interopRequireDefault(require("lodash/memoize"));
|
|
_memoize = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _resolveFrom() {
|
|
const data = _interopRequireDefault(require("resolve-from"));
|
|
_resolveFrom = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _semver() {
|
|
const data = _interopRequireDefault(require("semver"));
|
|
_semver = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _internal2() {
|
|
const data = require("../internal");
|
|
_internal2 = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _TerminalLink() {
|
|
const data = require("../logs/TerminalLink");
|
|
_TerminalLink = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _profileMethod() {
|
|
const data = require("../utils/profileMethod");
|
|
_profileMethod = function () {
|
|
return data;
|
|
};
|
|
return data;
|
|
}
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
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'];
|
|
const EXPO_NO_DOCTOR = _getenv().default.boolish('EXPO_NO_DOCTOR', false);
|
|
function _isNpmVersionWithinRanges(npmVersion, ranges) {
|
|
return ranges.some(range => _semver().default.satisfies(npmVersion, range));
|
|
}
|
|
async function _checkNpmVersionAsync(projectRoot) {
|
|
try {
|
|
try {
|
|
const yarnVersionResponse = await (0, _spawnAsync().default)('yarnpkg', ['--version']);
|
|
if (yarnVersionResponse.status === 0) {
|
|
return NO_ISSUES;
|
|
}
|
|
} catch {}
|
|
const npmVersion = (0, _child_process().execSync)('npm --version', {
|
|
stdio: 'pipe'
|
|
}).toString().trim();
|
|
if (_semver().default.lt(npmVersion, MIN_NPM_VERSION) || _isNpmVersionWithinRanges(npmVersion, BAD_NPM_VERSION_RANGES)) {
|
|
_internal2().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)) {
|
|
_internal2().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 {
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-npm-version');
|
|
}
|
|
} catch {
|
|
_internal2().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 (!_internal2().Watchman.isPlatformSupported()) {
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-watchman-version');
|
|
return;
|
|
}
|
|
const watchmanVersion = await _internal2().Watchman.unblockAndGetVersionAsync(projectRoot);
|
|
|
|
// If we can't get the watchman version, `getVersionAsync` will return `null`
|
|
if (!watchmanVersion) {
|
|
// watchman is probably just not installed
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-watchman-version');
|
|
return;
|
|
}
|
|
|
|
// Watchman versioning through homebrew is changed from semver to date since "2021.05.31.00"
|
|
// see: https://github.com/Homebrew/homebrew-core/commit/d299c2867503cb6ad8d90792343993c80e745071
|
|
const validSemver = !!_semver().default.valid(watchmanVersion) && _semver().default.gte(watchmanVersion, MIN_WATCHMAN_VERSION);
|
|
|
|
// This new format is used later than the MIN_WATCHMAN_VERSION, we assume/estimate if the version is correct here.
|
|
const validNewVersion = !validSemver && /[0-9]{4}\.[0-9]{2}\.[0-9]{2}\.[0-9]{2}/.test(watchmanVersion);
|
|
if (!validSemver && !validNewVersion) {
|
|
let warningMessage = `Warning: You are using an old version of watchman (v${watchmanVersion}).\n\nIt is recommend to always use the latest version, or 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`;
|
|
}
|
|
_internal2().ProjectUtils.logWarning(projectRoot, 'expo', warningMessage, 'doctor-watchman-version');
|
|
return;
|
|
}
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-watchman-version');
|
|
}
|
|
async function validateWithSchema(projectRoot, {
|
|
// Extract internal from the config object.
|
|
_internal,
|
|
...exp
|
|
}, schema, configName, validateAssets) {
|
|
let schemaErrorMessage;
|
|
let assetsErrorMessage;
|
|
const 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}. ${(0, _TerminalLink().learnMore)('https://docs.expo.dev/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}. ${(0, _TerminalLink().learnMore)('https://docs.expo.dev/')}`;
|
|
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, skipSDKVersionRequirement) {
|
|
if (!exp || !pkg) {
|
|
// getConfig already logged an error
|
|
return FATAL;
|
|
}
|
|
try {
|
|
await _checkWatchmanVersionAsync(projectRoot);
|
|
} catch (e) {
|
|
_internal2().ProjectUtils.logWarning(projectRoot, 'expo', `Warning: Problem checking watchman version. ${e.message}.`, 'doctor-problem-checking-watchman-version');
|
|
}
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-problem-checking-watchman-version');
|
|
const sdkVersion = exp.sdkVersion;
|
|
const configName = (0, _config().configFilename)(projectRoot);
|
|
|
|
// Warn if sdkVersion is UNVERSIONED
|
|
if (sdkVersion === 'UNVERSIONED' && !process.env.EXPO_SKIP_MANIFEST_VALIDATION_TOKEN) {
|
|
_internal2().ProjectUtils.logError(projectRoot, 'expo', `Error: Using unversioned Expo SDK. Do not publish until you set sdkVersion in ${configName}`, 'doctor-unversioned');
|
|
return ERROR;
|
|
}
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-unversioned');
|
|
const sdkVersions = await _internal2().Versions.sdkVersionsAsync();
|
|
if (!sdkVersions) {
|
|
_internal2().ProjectUtils.logError(projectRoot, 'expo', `Error: Couldn't connect to SDK versions server`, 'doctor-versions-endpoint-failed');
|
|
return ERROR;
|
|
}
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-versions-endpoint-failed');
|
|
if (!skipSDKVersionRequirement && (!sdkVersion || !sdkVersions[sdkVersion])) {
|
|
_internal2().ProjectUtils.logError(projectRoot, 'expo', `Error: Invalid sdkVersion. Valid options are ${Object.keys(sdkVersions).join(', ')}`, 'doctor-invalid-sdk-version');
|
|
return ERROR;
|
|
}
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-invalid-sdk-version');
|
|
|
|
// Skip validation if the correct token is set in env
|
|
if (sdkVersion && sdkVersion !== 'UNVERSIONED') {
|
|
try {
|
|
const schema = await _internal2().ExpSchema.getSchemaAsync(sdkVersion);
|
|
const {
|
|
schemaErrorMessage,
|
|
assetsErrorMessage
|
|
} = await validateWithSchema(projectRoot, exp, schema, configName, allowNetwork);
|
|
if (schemaErrorMessage) {
|
|
_internal2().ProjectUtils.logError(projectRoot, 'expo', schemaErrorMessage, 'doctor-schema-validation');
|
|
} else {
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-schema-validation');
|
|
}
|
|
if (assetsErrorMessage) {
|
|
_internal2().ProjectUtils.logError(projectRoot, 'expo', assetsErrorMessage, `doctor-validate-asset-fields`);
|
|
} else {
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, `doctor-validate-asset-fields`);
|
|
}
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-schema-validation-exception');
|
|
if (schemaErrorMessage || assetsErrorMessage) return ERROR;
|
|
} catch (e) {
|
|
_internal2().ProjectUtils.logWarning(projectRoot, 'expo', `Warning: Problem validating ${configName}: ${e.message}.`, 'doctor-schema-validation-exception');
|
|
}
|
|
}
|
|
if (sdkVersion) {
|
|
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;
|
|
}
|
|
const resolveReactNativeVersionFromProjectRoot = (0, _memoize().default)(projectRoot => {
|
|
try {
|
|
return require((0, _resolveFrom().default)(projectRoot, 'react-native/package.json')).version;
|
|
} catch {}
|
|
return null;
|
|
});
|
|
async function _validateReactNativeVersionAsync(exp, pkg, projectRoot, sdkVersions, sdkVersion) {
|
|
var _pkg$dependencies, _pkg$devDependencies, _pkg$peerDependencies, _pkg$dependencies2;
|
|
let reactNative = null;
|
|
if ((_pkg$dependencies = pkg.dependencies) !== null && _pkg$dependencies !== void 0 && _pkg$dependencies['react-native']) {
|
|
reactNative = pkg.dependencies['react-native'];
|
|
} else if ((_pkg$devDependencies = pkg.devDependencies) !== null && _pkg$devDependencies !== void 0 && _pkg$devDependencies['react-native']) {
|
|
reactNative = pkg.devDependencies['react-native'];
|
|
} else if ((_pkg$peerDependencies = pkg.peerDependencies) !== null && _pkg$peerDependencies !== void 0 && _pkg$peerDependencies['react-native']) {
|
|
reactNative = pkg.peerDependencies['react-native'];
|
|
}
|
|
|
|
// react-native is required
|
|
if (!reactNative) {
|
|
_internal2().ProjectUtils.logError(projectRoot, 'expo', `Error: Can't find react-native in package.json dependencies`, 'doctor-no-react-native-in-package-json');
|
|
return ERROR;
|
|
}
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-no-react-native-in-package-json');
|
|
if (_internal2().Versions.gteSdkVersion(exp, '41.0.0') && (_pkg$dependencies2 = pkg.dependencies) !== null && _pkg$dependencies2 !== void 0 && _pkg$dependencies2['@react-native-community/async-storage']) {
|
|
_internal2().ProjectUtils.logWarning(projectRoot, 'expo', `\n@react-native-community/async-storage has been renamed. To upgrade:\n- remove @react-native-community/async-storage from package.json\n- run "expo install @react-native-async-storage/async-storage"\n- update your imports manually, or run "npx expo-codemod sdk41-async-storage './**/*'".\n`, 'doctor-legacy-async-storage');
|
|
return WARNING;
|
|
} else {
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-legacy-async-storage');
|
|
}
|
|
const isExpoForkedReactNative = /expo\/react-native/.test(reactNative);
|
|
if (!isExpoForkedReactNative) {
|
|
// Expo fork of react-native is required for deprecated `isDetached` project
|
|
if (exp.isDetached) {
|
|
_internal2().ProjectUtils.logWarning(projectRoot, 'expo', `Warning: Not using the Expo fork of react-native (${reactNative}). ${(0, _TerminalLink().learnMore)('https://docs.expo.dev/')}`, 'doctor-not-using-expo-fork');
|
|
return WARNING;
|
|
}
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-not-using-expo-fork');
|
|
|
|
// Get the exact version as late as possible
|
|
// `semver` will match `semver.satisfies('~1.0.0', '~1.0.0')` as `false` even though it's technically allowed,
|
|
// to combat this, attempt to get the exact installed version.
|
|
const exactReactNativeVersion = (0, _profileMethod().profileMethod)(resolveReactNativeVersionFromProjectRoot)(projectRoot);
|
|
reactNative = exactReactNativeVersion || reactNative;
|
|
|
|
// Check react-native version satisfies with sdk's `facebookReactNativeVersion`
|
|
const {
|
|
facebookReactNativeVersion
|
|
} = sdkVersions[sdkVersion];
|
|
const anyReactNativePatchVersion = `~${facebookReactNativeVersion}`;
|
|
if (!_semver().default.satisfies(reactNative, anyReactNativePatchVersion)) {
|
|
_internal2().ProjectUtils.logWarning(projectRoot, 'expo', `Warning: Invalid version react-native@${reactNative} for expo sdkVersion ${sdkVersion}. Use react-native@${facebookReactNativeVersion}`, 'doctor-invalid-version-of-react-native');
|
|
return WARNING;
|
|
}
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-invalid-version-of-react-native');
|
|
} else {
|
|
// If `isExpoForkedReactNative === true`
|
|
try {
|
|
const reactNativeTag = reactNative.match(/sdk-\d+\.\d+\.\d+/)[0];
|
|
const sdkVersionObject = sdkVersions[sdkVersion];
|
|
|
|
// TODO: Want to be smarter about this. Maybe warn if there's a newer version.
|
|
if (_semver().default.major(_internal2().Versions.parseSdkVersionFromTag(reactNativeTag)) !== _semver().default.major(_internal2().Versions.parseSdkVersionFromTag(sdkVersionObject['expoReactNativeTag']))) {
|
|
_internal2().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;
|
|
}
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-invalid-version-of-react-native');
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-malformed-version-of-react-native');
|
|
} catch {
|
|
_internal2().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) {
|
|
// Check to make sure react-native is installed
|
|
|
|
if (_resolveFrom().default.silent(projectRoot, 'react-native/local-cli/cli.js')) {
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-react-native-not-installed');
|
|
} else {
|
|
_internal2().ProjectUtils.logError(projectRoot, 'expo', `Error: react-native is not installed. Please run \`npm install\` or \`yarn\` in your project directory.`, 'doctor-react-native-not-installed');
|
|
return FATAL;
|
|
}
|
|
return NO_ISSUES;
|
|
}
|
|
async function validateWithoutNetworkAsync(projectRoot, options = {}) {
|
|
return validateAsync(projectRoot, false, options.skipSDKVersionRequirement);
|
|
}
|
|
async function validateWithNetworkAsync(projectRoot, options = {}) {
|
|
return validateAsync(projectRoot, true, options.skipSDKVersionRequirement);
|
|
}
|
|
async function validateAsync(projectRoot, allowNetwork, skipSDKVersionRequirement) {
|
|
if (EXPO_NO_DOCTOR) {
|
|
return NO_ISSUES;
|
|
}
|
|
const {
|
|
exp,
|
|
pkg
|
|
} = (0, _config().getConfig)(projectRoot, {
|
|
strict: true,
|
|
skipSDKVersionRequirement
|
|
});
|
|
_internal2().ProjectUtils.clearNotification(projectRoot, 'doctor-config-json-not-read');
|
|
let status = await _checkNpmVersionAsync(projectRoot);
|
|
if (status === FATAL) {
|
|
return status;
|
|
}
|
|
const expStatus = await _validateExpJsonAsync(exp, pkg, projectRoot, allowNetwork, skipSDKVersionRequirement);
|
|
if (expStatus === FATAL) {
|
|
return expStatus;
|
|
}
|
|
status = Math.max(status, expStatus);
|
|
const nodeModulesStatus = await _validateNodeModulesAsync(projectRoot);
|
|
if (nodeModulesStatus > status) {
|
|
return nodeModulesStatus;
|
|
}
|
|
return status;
|
|
}
|
|
async function validateExpoServersAsync(projectRoot) {
|
|
const domains = ['expo.io', 'expo.fyi', 'expo.dev', 'static.expo.dev', 'exp.host'];
|
|
const attempts = await Promise.all(domains.map(async domain => ({
|
|
domain,
|
|
reachable: await (0, _isReachable().default)(domain)
|
|
})));
|
|
const failures = attempts.filter(attempt => !attempt.reachable);
|
|
if (failures.length) {
|
|
failures.forEach(failure => {
|
|
_internal2().ProjectUtils.logWarning(projectRoot, 'expo', `Warning: could not reach \`${failure.domain}\`.`, `doctor-server-dashboard-not-reachable-${failure.domain}`);
|
|
});
|
|
console.log();
|
|
_internal2().ProjectUtils.logWarning(projectRoot, 'expo', `We couldn't reach some of our domains, this might cause issues on our website or services.\nPlease check your network configuration and try to access these domains in your browser.`, 'doctor-server-dashboard-not-reachable');
|
|
console.log();
|
|
return WARNING;
|
|
}
|
|
return NO_ISSUES;
|
|
}
|
|
//# sourceMappingURL=Doctor.js.map
|