Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): support adding plugins to non-js nx installations
Browse files Browse the repository at this point in the history
leosvelperez committed Jan 16, 2024
1 parent 45673b2 commit 2d7591b
Showing 6 changed files with 142 additions and 37 deletions.
6 changes: 6 additions & 0 deletions packages/nx/migrations.json
Original file line number Diff line number Diff line change
@@ -82,6 +82,12 @@
"version": "17.3.0-beta.3",
"description": "Explicitly opt-out of git operations in nx release",
"implementation": "./src/migrations/update-17-3-0/nx-release-git-operations-explicit-opt-out"
},
"17.3.0-update-nx-wrapper": {
"cli": "nx",
"version": "17.3.0-beta.4",
"description": "Updates the nx wrapper.",
"implementation": "./src/migrations/update-17-3-0/update-nxw"
}
}
}
67 changes: 57 additions & 10 deletions packages/nx/src/command-line/add/add.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { exec } from 'child_process';
import { existsSync } from 'fs';
import * as ora from 'ora';
import { join, relative } from 'path';
import { isAngularPluginInstalled } from '../../adapter/angular-json';
import type { GeneratorsJsonEntry } from '../../config/misc-interfaces';
import { readNxJson } from '../../config/nx-json';
import { writeJsonFile } from '../../utils/fileutils';
import { logger } from '../../utils/logger';
import { output } from '../../utils/output';
import {
@@ -10,7 +14,7 @@ import {
} from '../../utils/package-manager';
import { handleErrors } from '../../utils/params';
import { getPluginCapabilities } from '../../utils/plugins';
import { workspaceRoot } from '../../utils/workspace-root';
import { workspaceRoot, workspaceRootInner } from '../../utils/workspace-root';
import type { AddOptions } from './command-object';

export function addHandler(args: AddOptions): Promise<void> {
@@ -42,21 +46,61 @@ async function installPackage(
const spinner = ora(`Installing ${pkgName}@${version}...`);
spinner.start();

await new Promise<void>((resolve) =>
exec(
`${packageManagerCommands.addDev} ${pkgName}@${version}`,
(error, stdout) => {
if (existsSync('package.json')) {
await new Promise<void>((resolve) =>
exec(
`${packageManagerCommands.addDev} ${pkgName}@${version}`,
(error, stdout) => {
if (error) {
spinner.fail();
output.addNewline();
logger.log(stdout);
output.error({
title: `Failed to install ${pkgName}. Please check the error above for more details.`,
});
process.exit(1);
}

return resolve();
}
)
);
} else {
const nxJson = readNxJson();
nxJson.installation.plugins ??= {};
nxJson.installation.plugins[pkgName] = version;
writeJsonFile('nx.json', nxJson);

const cwd = process.cwd();
const offsetFromRoot = relative(cwd, workspaceRootInner(cwd, null));

let nxExecutable: string;
if (process.platform === 'win32') {
nxExecutable = '.\\' + join(`${offsetFromRoot}`, 'nx.bat');
} else {
nxExecutable = './' + join(`${offsetFromRoot}`, 'nx');
}

await new Promise<void>((resolve) =>
exec(`${nxExecutable}`, (error, _stdout, stderr) => {
if (error) {
// revert adding the plugin to nx.json
nxJson.installation.plugins[pkgName] = undefined;
writeJsonFile('nx.json', nxJson);

spinner.fail();
output.addNewline();
logger.log(stdout);
logger.log(stderr);
output.error({
title: `Failed to install ${pkgName}. Please check the error above for more details.`,
});
process.exit(1);
}

return resolve();
}
)
);
})
);
}

spinner.succeed();
}
@@ -92,7 +136,10 @@ async function initializePlugin(
if (error) {
spinner.fail();
output.addNewline();
logger.log(stdout);
logger.error(stdout);
output.error({
title: `Failed to initialize ${pkgName}. Please check the error above for more details.`,
});
process.exit(1);
}

76 changes: 58 additions & 18 deletions packages/nx/src/command-line/init/implementation/dot-nx/nxw.ts
Original file line number Diff line number Diff line change
@@ -17,10 +17,17 @@ import type { PackageJson } from '../../../../utils/package-json';
const installationPath = path.join(__dirname, 'installation', 'package.json');

function matchesCurrentNxInstall(
currentInstallation: PackageJson,
nxJsonInstallation: NxJsonConfiguration['installation']
) {
if (
!currentInstallation.devDependencies ||
!Object.keys(currentInstallation.devDependencies).length
) {
return false;
}

try {
const currentInstallation: PackageJson = require(installationPath);
if (
currentInstallation.devDependencies['nx'] !==
nxJsonInstallation.version ||
@@ -52,37 +59,70 @@ function ensureDir(p: string) {
}
}

function getCurrentInstallation(): PackageJson {
try {
return require(installationPath);
} catch {
return {
name: 'nx-installation',
version: '0.0.0',
devDependencies: {},
};
}
}

function performInstallation(
currentInstallation: PackageJson,
nxJson: NxJsonConfiguration
) {
fs.writeFileSync(
installationPath,
JSON.stringify({
...currentInstallation,
devDependencies: {
nx: nxJson.installation.version,
...nxJson.installation.plugins,
},
})
);

try {
cp.execSync('npm i', {
cwd: path.dirname(installationPath),
stdio: 'inherit',
});
} catch (e) {
// revert possible changes to the current installation
fs.writeFileSync(installationPath, JSON.stringify(currentInstallation));
// rethrow
throw e;
}
}

function ensureUpToDateInstallation() {
const nxJsonPath = path.join(__dirname, '..', 'nx.json');

let nxJson: NxJsonConfiguration;

try {
nxJson = require(nxJsonPath);
if (!nxJson.installation) {
console.error(
'[NX]: The "installation" entry in the "nx.json" file is required when running the nx wrapper. See https://nx.dev/recipes/installation/install-non-javascript'
);
process.exit(1);
}
} catch {
console.error(
'[NX]: nx.json is required when running the nx wrapper. See https://nx.dev/more-concepts/nx-and-the-wrapper'
'[NX]: The "nx.json" file is required when running the nx wrapper. See https://nx.dev/recipes/installation/install-non-javascript'
);
process.exit(1);
}

try {
ensureDir(path.join(__dirname, 'installation'));
if (!matchesCurrentNxInstall(nxJson.installation)) {
fs.writeFileSync(
installationPath,
JSON.stringify({
name: 'nx-installation',
devDependencies: {
nx: nxJson.installation.version,
...nxJson.installation.plugins,
},
})
);
cp.execSync('npm i', {
cwd: path.dirname(installationPath),
stdio: 'inherit',
});
const currentInstallation = getCurrentInstallation();
if (!matchesCurrentNxInstall(currentInstallation, nxJson.installation)) {
performInstallation(currentInstallation, nxJson);
}
} catch (e: unknown) {
const messageLines = [
11 changes: 2 additions & 9 deletions packages/nx/src/migrations/update-15-8-2/update-nxw.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import {
getNxWrapperContents,
nxWrapperPath,
} from '../../command-line/init/implementation/dot-nx/add-nx-scripts';
import { normalizePath } from '../../utils/path';
import { Tree } from '../../generators/tree';
import { updateNxw } from '../../utils/update-nxw';

export default async function (tree: Tree) {
const wrapperPath = normalizePath(nxWrapperPath());
if (tree.exists(wrapperPath)) {
tree.write(wrapperPath, getNxWrapperContents());
}
updateNxw(tree);
}
6 changes: 6 additions & 0 deletions packages/nx/src/migrations/update-17-3-0/update-nxw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Tree } from '../../generators/tree';
import { updateNxw } from '../../utils/update-nxw';

export default async function (tree: Tree) {
updateNxw(tree);
}
13 changes: 13 additions & 0 deletions packages/nx/src/utils/update-nxw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {
getNxWrapperContents,
nxWrapperPath,
} from '../command-line/init/implementation/dot-nx/add-nx-scripts';
import type { Tree } from '../generators/tree';
import { normalizePath } from '../utils/path';

export function updateNxw(tree: Tree) {
const wrapperPath = normalizePath(nxWrapperPath());
if (tree.exists(wrapperPath)) {
tree.write(wrapperPath, getNxWrapperContents());
}
}

0 comments on commit 2d7591b

Please sign in to comment.