Skip to content

Commit

Permalink
Merge branch 'master' into nx-forward-more-args
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkMcCulloh authored Jul 5, 2023
2 parents 99801e0 + fc58ef4 commit 1b101c9
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 23 deletions.
10 changes: 5 additions & 5 deletions docs/shared/core-features/automate-updating-dependencies.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# Automate Updating Dependencies

Keeping a codebase updated with the latest changes in your framework of choice and tooling can be challenging. Not to mention that "tooling maintenance work" is usually hard to squeeze into your feature sprint.
Keeping a codebase updated with the latest changes in your framework of choice can be challenging. Not to mention that "tooling maintenance work" is usually hard to squeeze into your feature sprint.

The `nx migrate` helps by automating the process of updating:
The `nx migrate` command helps by automating the process of updating:

- package versions in your `package.json`
- configuration files (e.g. your Jest, ESLint or Nx config)
- your source code (e.g. fixing breaking changes or migrating to new best practices)

## How does it work?

Nx knows where its configuration files are and can therefore make sure they match the expected format or can alternatively adjust them. This automated update process, commonly referred to as "migration," becomes even more powerful when you leverage [Nx plugins](/packages). Nx plugins, which are NPM packages with a range of capabilities (code generation, task automation...), offer targeted updates based on their specific areas of responsibility.
Nx knows where its configuration files are and can therefore make sure they match the expected format. This automated update process, commonly referred to as "migration," becomes even more powerful when you leverage [Nx plugins](/packages). Nx plugins, which are NPM packages with a range of capabilities (code generation, task automation...), offer targeted updates based on their specific areas of responsibility.

For example, the [Nx ESLint plugin](/packages/linter) excels at configuring linting in your workspace. With its understanding of the configuration file locations, this plugin can provide precise migration scripts to update ESLint packages in your `package.json` and corresponding configuration files in your workspace when a new version is released.

Updating happens in three steps:

- The installed dependencies are updated including the `package.json` (and `node_modules`).
- The source code in the repo is updated to match the new versions of packages according to set of instructions specified in `migrations.json` file.
- Optionally remove the `migrations.json` file or keep it to re-run it in different Git branches
- The source code in the repo is updated to match the new versions of packages according to the set of instructions specified in `migrations.json` file.
- Optionally remove the `migrations.json` file or keep it to re-run the migration in different Git branches

### Step 1: Updating dependencies and generating migrations

Expand Down
13 changes: 9 additions & 4 deletions packages/eslint-plugin/src/rules/dependency-checks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { satisfies } from 'semver';
import { getHelperDependenciesFromProjectGraph } from '@nx/js';
import {
getAllDependencies,
getPackageJson,
removePackageJsonFromFileMap,
} from '../utils/package-json-utils';
import { JSONLiteral } from 'jsonc-eslint-parser/lib/parser/ast';
Expand Down Expand Up @@ -126,13 +127,17 @@ export default createESLintRule<Options, MessageIds>({
projectGraph
);

const rootPackageJson = getPackageJson(join(workspaceRoot, 'package.json'));

// find all dependencies for the project
const npmDeps = findProjectsNpmDependencies(
sourceProject,
projectGraph,
buildTarget,
rootPackageJson,
{
helperDependencies: helperDependencies.map((dep) => dep.target),
isProduction: true,
},
removePackageJsonFromFileMap(projectFileMap)
);
Expand All @@ -148,12 +153,12 @@ export default createESLintRule<Options, MessageIds>({
'package.json'
);

globalThis.projPackageJsonDeps ??= getAllDependencies(projPackageJsonPath);
globalThis.projPackageJsonDeps ??= getAllDependencies(
getPackageJson(projPackageJsonPath)
);
const projPackageJsonDeps: Record<string, string> =
globalThis.projPackageJsonDeps;
const rootPackageJsonDeps = getAllDependencies(
join(workspaceRoot, 'package.json')
);
const rootPackageJsonDeps = getAllDependencies(rootPackageJson);

function validateMissingDependencies(node: AST.JSONProperty) {
if (!checkMissingDependencies) {
Expand Down
22 changes: 14 additions & 8 deletions packages/eslint-plugin/src/utils/package-json-utils.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { ProjectFileMap, readJsonFile } from '@nx/devkit';
import { existsSync } from 'fs';
import { PackageJson } from 'nx/src/utils/package-json';

export function getAllDependencies(path: string): Record<string, string> {
export function getAllDependencies(
packageJson: PackageJson
): Record<string, string> {
return {
...packageJson.dependencies,
...packageJson.devDependencies,
...packageJson.peerDependencies,
};
}

export function getPackageJson(path: string): PackageJson {
if (existsSync(path)) {
const packageJson = readJsonFile(path);
return {
...packageJson.dependencies,
...packageJson.devDependencies,
...packageJson.peerDependencies,
};
return readJsonFile(path);
}
return {};
return {} as PackageJson;
}

export function removePackageJsonFromFileMap(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ describe('updatePackageJson', () => {
const fileMap = {
'@org/lib1': [
{
file: 'test.ts',
file: 'libs/lib1/src/test.ts',
hash: '',
deps: ['npm:external1', 'npm:external2'],
},
Expand Down
26 changes: 21 additions & 5 deletions packages/nx/src/plugins/js/package-json/create-package-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,19 @@ export function createPackageJson(
const projectNode = graph.nodes[projectName];
const isLibrary = projectNode.type === 'lib';

const rootPackageJson = readJsonFile(
`${options.root || workspaceRoot}/package.json`
);

const npmDeps = findProjectsNpmDependencies(
projectNode,
graph,
options.target,
{ helperDependencies: options.helperDependencies },
rootPackageJson,
{
helperDependencies: options.helperDependencies,
isProduction: options.isProduction,
},
fileMap
);

Expand Down Expand Up @@ -92,9 +100,6 @@ export function createPackageJson(
);
};

const rootPackageJson = readJsonFile(
`${options.root || workspaceRoot}/package.json`
);
Object.entries(npmDeps.dependencies).forEach(([packageName, version]) => {
if (
rootPackageJson.devDependencies?.[packageName] &&
Expand Down Expand Up @@ -179,9 +184,11 @@ export function findProjectsNpmDependencies(
projectNode: ProjectGraphProjectNode,
graph: ProjectGraph,
target: string,
rootPackageJson: PackageJson,
options: {
helperDependencies?: string[];
ignoredDependencies?: string[];
isProduction?: boolean;
},
fileMap?: ProjectFileMap
): NpmDeps {
Expand All @@ -208,13 +215,22 @@ export function findProjectsNpmDependencies(
recursivelyCollectPeerDependencies(dep, graph, npmDeps, seen);
});

// if it's production, we want to ignore all found devDependencies
const ignoredDependencies =
options.isProduction && rootPackageJson.devDependencies
? [
...(options.ignoredDependencies || []),
...Object.keys(rootPackageJson.devDependencies),
]
: options.ignoredDependencies || [];

findAllNpmDeps(
fileMap,
projectNode,
graph,
npmDeps,
seen,
options.ignoredDependencies || [],
ignoredDependencies,
dependencyInputs,
selfInputs
);
Expand Down

0 comments on commit 1b101c9

Please sign in to comment.