feat(core): allow skipping lockfile for affected (#23509)

This commit is contained in:
Craigory Coppola 2024-06-04 18:15:00 -04:00 committed by GitHub
parent 43ddd72aa2
commit cda799b8a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 76 additions and 23 deletions

View File

@ -40,6 +40,7 @@ export interface NrwlJsPluginConfig {
analyzeSourceFiles?: boolean;
analyzePackageJson?: boolean;
analyzeLockfile?: boolean;
projectsAffectedByDependencyUpdates?: 'all' | 'auto' | string[];
}
interface NxInstallationConfiguration {

View File

@ -1,10 +1,25 @@
import { readNxJson } from '../../../../config/configuration';
import { TouchedProjectLocator } from '../../../../project-graph/affected/affected-project-graph-models';
import { WholeFileChange } from '../../../../project-graph/file-utils';
import { JsonChange } from '../../../../utils/json-diff';
import { jsPluginConfig as readJsPluginConfig } from '../../utils/config';
import { findMatchingProjects } from '../../../../utils/find-matching-projects';
export const getTouchedProjectsFromLockFile: TouchedProjectLocator<
WholeFileChange | JsonChange
> = (fileChanges, projectGraphNodes): string[] => {
const nxJson = readNxJson();
const { projectsAffectedByDependencyUpdates } = readJsPluginConfig(nxJson);
if (projectsAffectedByDependencyUpdates === 'auto') {
return [];
} else if (Array.isArray(projectsAffectedByDependencyUpdates)) {
return findMatchingProjects(
projectsAffectedByDependencyUpdates,
projectGraphNodes
);
}
const lockFiles = [
'package-lock.json',
'yarn.lock',

View File

@ -13,6 +13,8 @@ import {
ProjectGraphExternalNode,
ProjectGraphProjectNode,
} from '../../../../config/project-graph';
import { NxJsonConfiguration } from '../../../../config/nx-json';
import { getPackageNameFromImportPath } from '../../../../utils/get-package-name-from-import-path';
export const getTouchedNpmPackages: TouchedProjectLocator<
WholeFileChange | JsonChange
@ -20,6 +22,8 @@ export const getTouchedNpmPackages: TouchedProjectLocator<
const packageJsonChange = touchedFiles.find((f) => f.file === 'package.json');
if (!packageJsonChange) return [];
const globalPackages = new Set(getGlobalPackages(nxJson.plugins));
let touched = [];
const changes = packageJsonChange.getChanges();
@ -59,6 +63,12 @@ export const getTouchedNpmPackages: TouchedProjectLocator<
touched.push(implementationNpmPackage.name);
}
}
if ('packageName' in npmPackage.data) {
if (globalPackages.has(npmPackage.data.packageName)) {
return Object.keys(projectGraph.nodes);
}
}
}
} else if (isWholeFileChange(c)) {
// Whole file was touched, so all npm packages are touched.
@ -76,3 +86,11 @@ export const getTouchedNpmPackages: TouchedProjectLocator<
}
return touched;
};
function getGlobalPackages(plugins: NxJsonConfiguration['plugins']) {
return (plugins ?? [])
.map((p) =>
getPackageNameFromImportPath(typeof p === 'string' ? p : p.plugin)
)
.concat('nx');
}

View File

@ -17,7 +17,7 @@ import {
getRootTsConfigFileName,
resolveModuleByImport,
} from '../../utils/typescript';
import { getPackageNameFromImportPath } from '../../../../utils/get-package-name-from-import-path';
/**
* The key is a combination of the package name and the workspace relative directory
* containing the file importing it e.g. `lodash__packages/my-lib`, the value is the
@ -36,7 +36,7 @@ const builtInModuleSet = new Set<string>([
]);
export function isBuiltinModuleImport(importExpr: string): boolean {
const packageName = parsePackageNameFromImportExpression(importExpr);
const packageName = getPackageNameFromImportPath(importExpr);
return builtInModuleSet.has(packageName);
}
@ -153,7 +153,7 @@ export class TargetProjectLocator {
importExpr: string,
fromFilePath: string
): string | null {
const packageName = parsePackageNameFromImportExpression(importExpr);
const packageName = getPackageNameFromImportPath(importExpr);
let fullFilePath = fromFilePath;
let workspaceRelativeFilePath = fromFilePath;
@ -362,15 +362,3 @@ export class TargetProjectLocator {
}
}
}
function parsePackageNameFromImportExpression(
importExpression: string
): string {
// Check if the package is scoped
if (importExpression.startsWith('@')) {
// For scoped packages, the package name is up to the second '/'
return importExpression.split('/').slice(0, 2).join('/');
}
// For unscoped packages, the package name is up to the first '/'
return importExpression.split('/')[0];
}

View File

@ -29,6 +29,7 @@ export function jsPluginConfig(
analyzePackageJson: true,
analyzeSourceFiles: true,
analyzeLockfile,
projectsAffectedByDependencyUpdates: 'all',
...nxJsonConfig,
};
}
@ -38,6 +39,7 @@ export function jsPluginConfig(
analyzeLockfile: false,
analyzePackageJson: false,
analyzeSourceFiles: false,
projectsAffectedByDependencyUpdates: 'all',
};
}
@ -69,12 +71,14 @@ export function jsPluginConfig(
analyzePackageJson: true,
analyzeLockfile,
analyzeSourceFiles: true,
projectsAffectedByDependencyUpdates: 'all',
};
} else {
return {
analyzePackageJson: true,
analyzeLockfile,
analyzeSourceFiles: false,
projectsAffectedByDependencyUpdates: 'all',
};
}
}

View File

@ -60,10 +60,10 @@ function filterAffectedProjects(
};
const reversed = reverse(graph);
ctx.touchedProjects.forEach((p) => {
addAffectedNodes(p, reversed, result, []);
addAffectedNodes(p, reversed, result, new Set());
});
ctx.touchedProjects.forEach((p) => {
addAffectedDependencies(p, reversed, result, []);
addAffectedDependencies(p, reversed, result, new Set());
});
return result;
}
@ -72,15 +72,15 @@ function addAffectedNodes(
startingProject: string,
reversed: ProjectGraph,
result: ProjectGraph,
visited: string[]
visited: Set<string>
): void {
if (visited.indexOf(startingProject) > -1) return;
if (visited.has(startingProject)) return;
const reversedNode = reversed.nodes[startingProject];
const reversedExternalNode = reversed.externalNodes[startingProject];
if (!reversedNode && !reversedExternalNode) {
throw new Error(`Invalid project name is detected: "${startingProject}"`);
}
visited.push(startingProject);
visited.add(startingProject);
if (reversedNode) {
result.nodes[startingProject] = reversedNode;
result.dependencies[startingProject] = [];
@ -96,10 +96,10 @@ function addAffectedDependencies(
startingProject: string,
reversed: ProjectGraph,
result: ProjectGraph,
visited: string[]
visited: Set<string>
): void {
if (visited.indexOf(startingProject) > -1) return;
visited.push(startingProject);
if (visited.has(startingProject)) return;
visited.add(startingProject);
if (reversed.dependencies[startingProject]) {
reversed.dependencies[startingProject].forEach(({ target }) =>
addAffectedDependencies(target, reversed, result, visited)

View File

@ -0,0 +1,13 @@
import { getPackageNameFromImportPath } from './get-package-name-from-import-path';
describe('getPackageNameFromImportPath', () => {
it.each([
['@nx/workspace', '@nx/workspace'],
['@nx/workspace/plugin', '@nx/workspace'],
['@nx/workspace/other', '@nx/workspace'],
['nx/plugin', 'nx'],
['nx', 'nx'],
])('should return %s for %s', (input, expected) => {
expect(getPackageNameFromImportPath(input)).toEqual(expected);
});
});

View File

@ -0,0 +1,14 @@
//# Converts import paths to package names.
//# e.g. - `@nx/workspace` -> `@nx/workspace`
//# - `@nx/workspace/plugin` -> `@nx/workspace`
//# - `@nx/workspace/other` -> `@nx/workspace`
//# - `nx/plugin` -> `nx`
export function getPackageNameFromImportPath(importExpression: string) {
// Check if the package is scoped
if (importExpression.startsWith('@')) {
// For scoped packages, the package name is up to the second '/'
return importExpression.split('/').slice(0, 2).join('/');
}
// For unscoped packages, the package name is up to the first '/'
return importExpression.split('/')[0];
}