Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
[doctor] check for sdkVersion in Expo config (#4732)
Browse files Browse the repository at this point in the history
* [doctor] check for expo.sdkVersion

* use @expo/config SDK installed version resolution to compare with Expo config

* remove bad mock stuff
  • Loading branch information
keith-kurak committed Jul 20, 2023
1 parent 4cd9960 commit b1852a8
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 1 deletion.
20 changes: 20 additions & 0 deletions packages/expo-doctor/__mocks__/resolve-from.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = require(require.resolve('resolve-from'));

module.exports.silent = (fromDirectory, request) => {
const fs = require('fs');
const path = require('path');
try {
fromDirectory = fs.realpathSync(fromDirectory);
} catch (error) {
if (error.code === 'ENOENT') {
fromDirectory = path.resolve(fromDirectory);
} else {
return;
}
}

const outputPath = path.join(fromDirectory, 'node_modules', request);
if (fs.existsSync(outputPath)) {
return outputPath;
}
};
1 change: 1 addition & 0 deletions packages/expo-doctor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@expo/spawn-async": "^1.7.0",
"chalk": "^4.0.0",
"ora": "3.4.0",
"resolve-from": "^5.0.0",
"semver": "7.3.2"
}
}
55 changes: 55 additions & 0 deletions packages/expo-doctor/src/checks/ExpoConfigCommonIssueCheck.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import JsonFile from '@expo/json-file';
import resolveFrom from 'resolve-from';

import { DoctorCheck, DoctorCheckParams, DoctorCheckResult } from './checks.types';

export class ExpoConfigCommonIssueCheck implements DoctorCheck {
description = 'Check Expo config for common issues';

sdkVersionRange = '*';

async runAsync({ projectRoot, exp }: DoctorCheckParams): Promise<DoctorCheckResult> {
const issues: string[] = [];
let advice;

// compare SDK version in package.json with installed expo package version
// If these don't match, it almost certainly means the user specified expo.sdkVersion in their app.json/ app.config.js
const expoSDKVersionFromPackageJson = getExpoSDKVersionFromPackage(projectRoot);

if (expoSDKVersionFromPackageJson !== exp.sdkVersion) {
issues.push(
"It appears that expo.sdkVersion is defined in your app.json/ app.config.js. This can cause 'expo install' to install dependency versions for the wrong SDK. SDK version is determined by the version of the expo package installed in your project."
);
advice = 'Remove expo.sdkVersion from your app.json/ app.config.js.';
}

return {
isSuccessful: issues.length === 0,
issues,
advice,
};
}
}

/**
* Resolve the Expo SDK Version either from the input Expo config or from the installed
* version of the `expo` package.
* Adapted from https://github.com/expo/expo/blob/main/packages/%40expo/config/src/getExpoSDKVersion.ts
*/
function getExpoSDKVersionFromPackage(projectRoot: string): string | undefined {
const packageJsonPath = resolveFrom.silent(projectRoot, 'expo/package.json');
if (!packageJsonPath) {
// (probably) technically impossible - if this happens, `getConfig` throws and Doctor crashes
return undefined;
}
const expoPackageJson = JsonFile.read(packageJsonPath, { json5: true });
const { version: packageVersion } = expoPackageJson;

if (!(typeof packageVersion === 'string')) {
// This is technically impossible.
return undefined;
}

const majorVersion = packageVersion.split('.').shift();
return `${majorVersion}.0.0`;
}
2 changes: 1 addition & 1 deletion packages/expo-doctor/src/checks/ExpoConfigSchemaCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { validateWithSchemaAsync } from '../utils/schema';
import { DoctorCheck, DoctorCheckParams, DoctorCheckResult } from './checks.types';

export class ExpoConfigSchemaCheck implements DoctorCheck {
description = 'Check Expo config (app.json/ app.config.js)';
description = 'Check Expo config (app.json/ app.config.js) schema';

sdkVersionRange = '*';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { vol } from 'memfs';

import { ExpoConfigCommonIssueCheck } from '../ExpoConfigCommonIssueCheck';

jest.mock('resolve-from');
jest.mock('fs');

const projectRoot = '/tmp/project';

// required by runAsync
const additionalProjectProps = {
exp: {
name: 'name',
slug: 'slug',
sdkVersion: '49.0.0',
},
projectRoot,
};

describe('runAsync', () => {
it('returns result with isSuccessful = true if Expo config SDK version matches installed version', async () => {
vol.fromJSON({
[projectRoot + '/node_modules/expo/package.json']: `{
"version": "49.0.0"
}`,
});

const check = new ExpoConfigCommonIssueCheck();
const result = await check.runAsync({
pkg: { name: 'name', version: '1.0.0' },
...additionalProjectProps,
});
expect(result.isSuccessful).toBeTruthy();
});

it('returns result with isSuccessful = false if Expo config SDK version does not match installed version', async () => {
vol.fromJSON({
[projectRoot + '/node_modules/expo/package.json']: `{
"version": "48.0.0"
}`,
});

const check = new ExpoConfigCommonIssueCheck();
const result = await check.runAsync({
pkg: { name: 'name', version: '1.0.0' },
...additionalProjectProps,
exp: {
name: 'name',
sdkVersion: '49.0.0',
slug: 'slug',
},
});
expect(result.isSuccessful).toBeFalsy();
});
});
2 changes: 2 additions & 0 deletions packages/expo-doctor/src/doctor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getConfig } from '@expo/config';
import chalk from 'chalk';
import semver from 'semver';

import { ExpoConfigCommonIssueCheck } from './checks/ExpoConfigCommonIssueCheck';
import { DirectPackageInstallCheck } from './checks/DirectPackageInstallCheck';

Check warning on line 6 in packages/expo-doctor/src/doctor.ts

View workflow job for this annotation

GitHub Actions / Build with Node 14

`./checks/DirectPackageInstallCheck` import should occur before import of `./checks/ExpoConfigCommonIssueCheck`

Check warning on line 6 in packages/expo-doctor/src/doctor.ts

View workflow job for this annotation

GitHub Actions / Build with Node 16

`./checks/DirectPackageInstallCheck` import should occur before import of `./checks/ExpoConfigCommonIssueCheck`

Check warning on line 6 in packages/expo-doctor/src/doctor.ts

View workflow job for this annotation

GitHub Actions / Build with Node 16

`./checks/DirectPackageInstallCheck` import should occur before import of `./checks/ExpoConfigCommonIssueCheck`
import { ExpoConfigSchemaCheck } from './checks/ExpoConfigSchemaCheck';
import { GlobalPackageInstalledCheck } from './checks/GlobalPackageInstalledCheck';
Expand Down Expand Up @@ -140,6 +141,7 @@ export async function actionAsync(projectRoot: string) {
new SupportPackageVersionCheck(),
new InstalledDependencyVersionCheck(),
new ExpoConfigSchemaCheck(),
new ExpoConfigCommonIssueCheck(),
new DirectPackageInstallCheck(),
new PackageJsonCheck(),
new ProjectSetupCheck(),
Expand Down

0 comments on commit b1852a8

Please sign in to comment.