Skip to content

Commit

Permalink
fix(core): update ensurePackage util with workaround for bad module c…
Browse files Browse the repository at this point in the history
…ache
  • Loading branch information
jaysoo committed Feb 3, 2023
1 parent b85f9de commit b71c118
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 10 deletions.
10 changes: 10 additions & 0 deletions packages/devkit/src/utils/package-json.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,16 @@ describe('ensurePackage', () => {
tree = createTree();
});

it('should return successfully when package is present', async () => {
writeJson(tree, 'package.json', {});

await expect(
ensurePackage(tree, '@nrwl/devkit', '>=15.0.0', {
throwOnMissing: true,
})
).resolves.toBeUndefined(); // return void
});

it('should throw when dependencies are missing', async () => {
writeJson(tree, 'package.json', {});

Expand Down
34 changes: 24 additions & 10 deletions packages/devkit/src/utils/package-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { GeneratorCallback } from 'nx/src/config/misc-interfaces';
import { clean, coerce, gt, satisfies } from 'semver';
import { getPackageManagerCommand } from 'nx/src/utils/package-manager';
import { execSync } from 'child_process';
import { readModulePackageJson } from 'nx/src/utils/package-json';
import { createRequire } from 'module';

const NON_SEMVER_TAGS = {
'*': 2,
Expand Down Expand Up @@ -344,19 +344,14 @@ export async function ensurePackage(
throwOnMissing?: boolean;
} = {}
): Promise<void> {
let version: string;

// Read package and version from root package.json file.
const dev = options.dev ?? true;
const throwOnMissing = options.throwOnMissing ?? !!process.env.NX_DRY_RUN; // NX_DRY_RUN is set in `packages/nx/src/command-line/generate.ts`
const pmc = getPackageManagerCommand();

// Try to resolve the actual version from resolved module.
try {
version = readModulePackageJson(pkg).packageJson.version;
} catch {
// ignore
}
// Try to read the installed package version.
// NOTE: Avoid `require.resolve` or `require` here, because doing so may pollute module cache and the package cannot be imported later.
let version = getPackageVersion(pkg);

// Otherwise try to read in from package.json. This is needed for E2E tests to pass.
if (!version) {
Expand All @@ -365,7 +360,15 @@ export async function ensurePackage(
version = packageJson[field]?.[pkg];
}

if (!satisfies(version, requiredVersion)) {
if (
// Special case: When running Nx unit tests, the version read from package.json is "0.0.1".
!(
pkg.startsWith('@nrwl/') &&
(version === '0.0.1' || requiredVersion === '0.0.1')
) &&
// Normal case
!satisfies(version, requiredVersion, { includePrerelease: true })
) {
const installCmd = `${
dev ? pmc.addDev : pmc.add
} ${pkg}@${requiredVersion}`;
Expand All @@ -381,3 +384,14 @@ export async function ensurePackage(
}
}
}

function getPackageVersion(pkg: string): undefined | string {
try {
// Create a sand-boxed `require` otherwise the module/file cache may be polluted,
// and importing packages such as `typescript` may fail even though they are installed.
const require = createRequire(__filename);
return require(`${pkg}/package.json`).version;
} catch (e) {
return null;
}
}

0 comments on commit b71c118

Please sign in to comment.