fix(core): misc encapuslated mode fixes (#15075)

This commit is contained in:
Craigory Coppola 2023-02-17 18:31:39 -05:00 committed by GitHub
parent 6ff04d5a9b
commit 804cb95dff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 134 additions and 122 deletions

View File

@ -7,6 +7,7 @@ import {
checkFilesExist,
cleanupProject,
getPublishedVersion,
uniq,
} from '@nrwl/e2e/utils';
describe('encapsulated nx', () => {
@ -69,6 +70,18 @@ describe('encapsulated nx', () => {
new RegExp(`nx.*:.*${getPublishedVersion()}`)
);
});
it('should work with basic generators', () => {
updateJson<NxJsonConfiguration>('nx.json', (j) => {
j.installation.plugins ??= {};
j.installation.plugins['@nrwl/workspace'] = getPublishedVersion();
return j;
});
expect(() =>
runEncapsulatedNx(`g npm-package ${uniq('pkg')}`)
).not.toThrow();
expect(() => checkFilesExist());
});
});
function assertNoRootPackages() {

View File

@ -21,6 +21,7 @@ import { getLocalWorkspacePlugins } from '../utils/plugins/local-plugins';
import { printHelp } from '../utils/print-help';
import { workspaceRoot } from '../utils/workspace-root';
import { NxJsonConfiguration } from '../config/nx-json';
import { findInstalledPlugins } from '../utils/plugins/installed-plugins';
export interface GenerateOptions {
collectionName: string;
@ -50,14 +51,10 @@ async function promptForCollection(
interactive: boolean,
projectsConfiguration: ProjectsConfigurations
): Promise<string> {
const packageJson = readJsonFile(`${workspaceRoot}/package.json`);
const localPlugins = getLocalWorkspacePlugins(projectsConfiguration);
const installedCollections = Array.from(
new Set([
...Object.keys(packageJson.dependencies || {}),
...Object.keys(packageJson.devDependencies || {}),
])
new Set(findInstalledPlugins().map((x) => x.name))
);
const choicesMap = new Set<string>();

View File

@ -6,6 +6,7 @@ import { directoryExists, readJsonFile } from '../utils/fileutils';
import { PackageJson } from '../utils/package-json';
import * as parser from 'yargs-parser';
import { generateEncapsulatedNxSetup } from '../nx-init/encapsulated/add-nx-scripts';
import { prerelease } from 'semver';
export async function initHandler() {
const args = process.argv.slice(2).join(' ');
@ -16,7 +17,10 @@ export async function initHandler() {
},
}) as any as { encapsulated: boolean };
const version = process.env.NX_VERSION ?? 'latest';
const version =
process.env.NX_VERSION ?? prerelease(require('../../package.json').version)
? 'next'
: 'latest';
if (process.env.NX_VERSION) {
console.log(`Using version ${process.env.NX_VERSION}`);
}

View File

@ -3,7 +3,7 @@ import { output } from '../utils/output';
import {
fetchCommunityPlugins,
fetchCorePlugins,
getInstalledPluginsFromPackageJson,
getInstalledPluginsAndCapabilities,
listCommunityPlugins,
listCorePlugins,
listInstalledPlugins,
@ -49,7 +49,7 @@ export async function listHandler(args: ListArgs): Promise<void> {
const localPlugins = getLocalWorkspacePlugins(
readProjectsConfigurationFromProjectGraph(projectGraph)
);
const installedPlugins = getInstalledPluginsFromPackageJson(
const installedPlugins = getInstalledPluginsAndCapabilities(
workspaceRoot,
corePlugins,
communityPlugins

View File

@ -79,7 +79,7 @@ describe('report', () => {
);
const plugins = findInstalledCommunityPlugins();
expect(plugins).toEqual([
expect.objectContaining({ package: 'plugin-two', version: '2.0.0' }),
expect.objectContaining({ name: 'plugin-two', version: '2.0.0' }),
]);
});
@ -112,8 +112,8 @@ describe('report', () => {
);
const plugins = findInstalledCommunityPlugins();
expect(plugins).toEqual([
expect.objectContaining({ package: 'plugin-one', version: '1.0.0' }),
expect.objectContaining({ package: 'plugin-two', version: '2.0.0' }),
expect.objectContaining({ name: 'plugin-one', version: '1.0.0' }),
expect.objectContaining({ name: 'plugin-two', version: '2.0.0' }),
]);
});
@ -152,8 +152,8 @@ describe('report', () => {
const plugins = findInstalledCommunityPlugins();
expect(plugins).toEqual(
expect.arrayContaining([
expect.objectContaining({ package: 'plugin-one', version: '1.0.0' }),
expect.objectContaining({ package: 'plugin-two', version: '2.0.0' }),
expect.objectContaining({ name: 'plugin-one', version: '1.0.0' }),
expect.objectContaining({ name: 'plugin-two', version: '2.0.0' }),
])
);
});
@ -189,7 +189,7 @@ describe('report', () => {
},
})
);
const plugins = findInstalledCommunityPlugins().map((x) => x.package);
const plugins = findInstalledCommunityPlugins().map((x) => x.name);
expect(plugins).not.toContain('other-package');
});
});

View File

@ -20,6 +20,7 @@ import {
} from '../project-graph/project-graph';
import { gt, valid } from 'semver';
import { NxJsonConfiguration } from '../config/nx-json';
import { findInstalledPlugins } from '../utils/plugins/installed-plugins';
const nxPackageJson = readJsonFile<typeof import('../../package.json')>(
join(__dirname, '../../package.json')
@ -79,11 +80,11 @@ export async function reportHandler() {
if (communityPlugins.length) {
bodyLines.push(LINE_SEPARATOR);
padding = Math.max(...communityPlugins.map((x) => x.package.length)) + 1;
padding = Math.max(...communityPlugins.map((x) => x.name.length)) + 1;
bodyLines.push('Community plugins:');
communityPlugins.forEach((p) => {
bodyLines.push(
`${chalk.green(p.package.padEnd(padding))}: ${chalk.bold(p.version)}`
`${chalk.green(p.name.padEnd(padding))}: ${chalk.bold(p.version)}`
);
});
}
@ -129,9 +130,7 @@ export interface ReportData {
pm: PackageManager;
pmVersion: string;
localPlugins: string[];
communityPlugins: (PackageJson & {
package: string;
})[];
communityPlugins: PackageJson[];
packageVersionsWeCareAbout: {
package: string;
version: string;
@ -249,47 +248,15 @@ export function findMisalignedPackagesForPackage(
: undefined;
}
export function findInstalledCommunityPlugins(): (PackageJson & {
package: string;
})[] {
const packageJsonDeps = getDependenciesFromPackageJson();
const nxJsonDeps = getDependenciesFromNxJson();
const deps = packageJsonDeps.concat(nxJsonDeps);
return deps.reduce(
(arr: any[], nextDep: string): { project: string; version: string }[] => {
if (
patternsWeIgnoreInCommunityReport.some((pattern) =>
export function findInstalledCommunityPlugins(): PackageJson[] {
const installedPlugins = findInstalledPlugins();
return installedPlugins.filter(
(dep) =>
!patternsWeIgnoreInCommunityReport.some((pattern) =>
typeof pattern === 'string'
? pattern === nextDep
: pattern.test(nextDep)
? pattern === dep.name
: pattern.test(dep.name)
)
) {
return arr;
}
try {
const depPackageJson: Partial<PackageJson> =
readPackageJson(nextDep) || {};
if (
[
'ng-update',
'nx-migrations',
'schematics',
'generators',
'builders',
'executors',
].some((field) => field in depPackageJson)
) {
arr.push({ package: nextDep, ...depPackageJson });
return arr;
} else {
return arr;
}
} catch {
console.warn(`Error parsing packageJson for ${nextDep}`);
return arr;
}
},
[]
);
}
export function findInstalledPackagesWeCareAbout() {
@ -301,25 +268,3 @@ export function findInstalledPackagesWeCareAbout() {
return acc;
}, [] as { package: string; version: string }[]);
}
function getDependenciesFromPackageJson(
packageJsonPath = 'package.json'
): string[] {
try {
const { dependencies, devDependencies } = readJsonFile(
join(workspaceRoot, packageJsonPath)
);
return Object.keys({ ...dependencies, ...devDependencies });
} catch {}
return [];
}
function getDependenciesFromNxJson(): string[] {
const { installation } = readJsonFile<NxJsonConfiguration>(
join(workspaceRoot, 'nx.json')
);
if (!installation) {
return [];
}
return ['nx', ...Object.keys(installation.plugins || {})];
}

View File

@ -1,6 +1,5 @@
import { execSync } from 'child_process';
import { readFileSync, constants as FsConstants } from 'fs';
import { stripIndent } from 'nx/src/utils/logger';
import * as path from 'path';
import { valid } from 'semver';
import { NxJsonConfiguration } from '../../config/nx-json';
@ -20,22 +19,18 @@ const NODE_MISSING_ERR =
const NPM_MISSING_ERR =
'Nx requires npm to be available. To install NodeJS and NPM, see: https://nodejs.org/en/download/ .';
const BATCH_SCRIPT_CONTENTS = stripIndent(
`set path_to_root=%~dp0
const BATCH_SCRIPT_CONTENTS = `set path_to_root=%~dp0
WHERE node >nul 2>nul
IF %ERRORLEVEL% NEQ 0 (ECHO ${NODE_MISSING_ERR}; EXIT 1)
WHERE npm >nul 2>nul
IF %ERRORLEVEL% NEQ 0 (ECHO ${NPM_MISSING_ERR}; EXIT 1)
node ${path.win32.join('%path_to_root%', nxWrapperPath(path.win32))} %*`
);
node ${path.win32.join('%path_to_root%', nxWrapperPath(path.win32))} %*`;
const SHELL_SCRIPT_CONTENTS = stripIndent(
`#!/bin/bash
const SHELL_SCRIPT_CONTENTS = `#!/bin/bash
command -v node >/dev/null 2>&1 || { echo >&2 "${NODE_MISSING_ERR}"; exit 1; }
command -v npm >/dev/null 2>&1 || { echo >&2 "${NPM_MISSING_ERR}"; exit 1; }
path_to_root=$(dirname $BASH_SOURCE)
node ${path.posix.join('$path_to_root', nxWrapperPath(path.posix))} $@`
);
node ${path.posix.join('$path_to_root', nxWrapperPath(path.posix))} $@`;
export function generateEncapsulatedNxSetup(version?: string) {
const host = new FsTree(process.cwd(), false);
@ -81,9 +76,10 @@ export function updateGitIgnore(host: Tree) {
}
function getNodeScriptContents() {
// Read nxw.js, but remove any empty comments or comments that start with `INTERNAL: `
// Read nxw.js, but remove any empty comments or comments that start with `//#: `
// This removes the sourceMapUrl since it is invalid, as well as any internal comments.
return readFileSync(path.join(__dirname, 'nxw.js'), 'utf-8').replace(
/(\/\/ INTERNAL: .*)|(\/\/\w*)$/gm,
/(\/\/# .*)|(\/\/\w*)$/gm,
''
);
}

View File

@ -2,9 +2,9 @@
// that your local installation matches nx.json.
// See: https://nx.dev/more-concepts/encapsulated-nx-and-the-wrapper for more info.
//
// INTERNAL: The contents of this file are executed before packages are installed.
// INTERNAL: As such, we should not import anything from nx, other @nrwl packages,
// INTERNAL: or any other npm packages. Only import node builtins.
//# The contents of this file are executed before packages are installed.
//# As such, we should not import anything from nx, other @nrwl packages,
//# or any other npm packages. Only import node builtins.
const fs: typeof import('fs') = require('fs');
const path: typeof import('path') = require('path');
@ -19,24 +19,23 @@ function matchesCurrentNxInstall(
nxJsonInstallation: NxJsonConfiguration['installation']
) {
try {
const currentInstallation: PackageJson = JSON.parse(
fs.readFileSync(installationPath, 'utf-8')
);
const currentInstallation: PackageJson = require(installationPath);
if (
currentInstallation.dependencies['nx'] !== nxJsonInstallation.version ||
JSON.parse(
fs.readFileSync(
path.join(installationPath, 'node_modules', 'nx', 'package.json'),
'utf-8'
)
).version !== nxJsonInstallation.version
currentInstallation.devDependencies['nx'] !==
nxJsonInstallation.version ||
require(path.join(
path.dirname(installationPath),
'node_modules',
'nx',
'package.json'
)).version !== nxJsonInstallation.version
) {
return false;
}
for (const [plugin, desiredVersion] of Object.entries(
nxJsonInstallation.plugins || {}
)) {
if (currentInstallation.dependencies[plugin] !== desiredVersion) {
if (currentInstallation.devDependencies[plugin] !== desiredVersion) {
return false;
}
}
@ -58,7 +57,7 @@ function ensureUpToDateInstallation() {
let nxJson: NxJsonConfiguration;
try {
nxJson = JSON.parse(fs.readFileSync(nxJsonPath, 'utf-8'));
nxJson = require(nxJsonPath);
} catch {
console.error(
'[NX]: nx.json is required when running in encapsulated mode. Run `npx nx init --encapsulated` to restore it.'

View File

@ -4,7 +4,7 @@ export {
} from './community-plugins';
export { fetchCorePlugins, listCorePlugins } from './core-plugins';
export {
getInstalledPluginsFromPackageJson,
getInstalledPluginsAndCapabilities,
listInstalledPlugins,
} from './installed-plugins';
export {

View File

@ -4,20 +4,78 @@ import type { CommunityPlugin, CorePlugin, PluginCapabilities } from './models';
import { getPluginCapabilities } from './plugin-capabilities';
import { hasElements } from './shared';
import { readJsonFile } from '../fileutils';
import { readModulePackageJson } from '../package-json';
import { PackageJson, readModulePackageJson } from '../package-json';
import { workspaceRoot } from '../workspace-root';
import { join } from 'path';
import { NxJsonConfiguration } from '../../config/nx-json';
export function getInstalledPluginsFromPackageJson(
export function findInstalledPlugins(): PackageJson[] {
const packageJsonDeps = getDependenciesFromPackageJson();
const nxJsonDeps = getDependenciesFromNxJson();
const deps = packageJsonDeps.concat(nxJsonDeps);
const result: PackageJson[] = [];
for (const dep of deps) {
const pluginPackageJson = getNxPluginPackageJsonOrNull(dep);
if (pluginPackageJson) {
result.push(pluginPackageJson);
}
}
return result;
}
function getNxPluginPackageJsonOrNull(pkg: string): PackageJson | null {
try {
const { packageJson } = readModulePackageJson(pkg, [
workspaceRoot,
join(workspaceRoot, '.nx', ' installation'),
]);
return packageJson &&
[
'ng-update',
'nx-migrations',
'schematics',
'generators',
'builders',
'executors',
].some((field) => field in packageJson)
? packageJson
: null;
} catch {
return null;
}
}
function getDependenciesFromPackageJson(
packageJsonPath = 'package.json'
): string[] {
try {
const { dependencies, devDependencies } = readJsonFile(
join(workspaceRoot, packageJsonPath)
);
return Object.keys({ ...dependencies, ...devDependencies });
} catch {}
return [];
}
function getDependenciesFromNxJson(): string[] {
const { installation } = readJsonFile<NxJsonConfiguration>(
join(workspaceRoot, 'nx.json')
);
if (!installation) {
return [];
}
return ['nx', ...Object.keys(installation.plugins || {})];
}
export function getInstalledPluginsAndCapabilities(
workspaceRoot: string,
corePlugins: CorePlugin[],
communityPlugins: CommunityPlugin[] = []
): Map<string, PluginCapabilities> {
const packageJson = readJsonFile(`${workspaceRoot}/package.json`);
const plugins = new Set([
...corePlugins.map((p) => p.name),
...communityPlugins.map((p) => p.name),
...Object.keys(packageJson.dependencies || {}),
...Object.keys(packageJson.devDependencies || {}),
...findInstalledPlugins().map((p) => p.name),
]);
return new Map(