feat(core): allow skipping lockfile for affected (#23509)
This commit is contained in:
parent
43ddd72aa2
commit
cda799b8a6
@ -40,6 +40,7 @@ export interface NrwlJsPluginConfig {
|
|||||||
analyzeSourceFiles?: boolean;
|
analyzeSourceFiles?: boolean;
|
||||||
analyzePackageJson?: boolean;
|
analyzePackageJson?: boolean;
|
||||||
analyzeLockfile?: boolean;
|
analyzeLockfile?: boolean;
|
||||||
|
projectsAffectedByDependencyUpdates?: 'all' | 'auto' | string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NxInstallationConfiguration {
|
interface NxInstallationConfiguration {
|
||||||
|
|||||||
@ -1,10 +1,25 @@
|
|||||||
|
import { readNxJson } from '../../../../config/configuration';
|
||||||
import { TouchedProjectLocator } from '../../../../project-graph/affected/affected-project-graph-models';
|
import { TouchedProjectLocator } from '../../../../project-graph/affected/affected-project-graph-models';
|
||||||
import { WholeFileChange } from '../../../../project-graph/file-utils';
|
import { WholeFileChange } from '../../../../project-graph/file-utils';
|
||||||
import { JsonChange } from '../../../../utils/json-diff';
|
import { JsonChange } from '../../../../utils/json-diff';
|
||||||
|
import { jsPluginConfig as readJsPluginConfig } from '../../utils/config';
|
||||||
|
import { findMatchingProjects } from '../../../../utils/find-matching-projects';
|
||||||
|
|
||||||
export const getTouchedProjectsFromLockFile: TouchedProjectLocator<
|
export const getTouchedProjectsFromLockFile: TouchedProjectLocator<
|
||||||
WholeFileChange | JsonChange
|
WholeFileChange | JsonChange
|
||||||
> = (fileChanges, projectGraphNodes): string[] => {
|
> = (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 = [
|
const lockFiles = [
|
||||||
'package-lock.json',
|
'package-lock.json',
|
||||||
'yarn.lock',
|
'yarn.lock',
|
||||||
|
|||||||
@ -13,6 +13,8 @@ import {
|
|||||||
ProjectGraphExternalNode,
|
ProjectGraphExternalNode,
|
||||||
ProjectGraphProjectNode,
|
ProjectGraphProjectNode,
|
||||||
} from '../../../../config/project-graph';
|
} from '../../../../config/project-graph';
|
||||||
|
import { NxJsonConfiguration } from '../../../../config/nx-json';
|
||||||
|
import { getPackageNameFromImportPath } from '../../../../utils/get-package-name-from-import-path';
|
||||||
|
|
||||||
export const getTouchedNpmPackages: TouchedProjectLocator<
|
export const getTouchedNpmPackages: TouchedProjectLocator<
|
||||||
WholeFileChange | JsonChange
|
WholeFileChange | JsonChange
|
||||||
@ -20,6 +22,8 @@ export const getTouchedNpmPackages: TouchedProjectLocator<
|
|||||||
const packageJsonChange = touchedFiles.find((f) => f.file === 'package.json');
|
const packageJsonChange = touchedFiles.find((f) => f.file === 'package.json');
|
||||||
if (!packageJsonChange) return [];
|
if (!packageJsonChange) return [];
|
||||||
|
|
||||||
|
const globalPackages = new Set(getGlobalPackages(nxJson.plugins));
|
||||||
|
|
||||||
let touched = [];
|
let touched = [];
|
||||||
const changes = packageJsonChange.getChanges();
|
const changes = packageJsonChange.getChanges();
|
||||||
|
|
||||||
@ -59,6 +63,12 @@ export const getTouchedNpmPackages: TouchedProjectLocator<
|
|||||||
touched.push(implementationNpmPackage.name);
|
touched.push(implementationNpmPackage.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('packageName' in npmPackage.data) {
|
||||||
|
if (globalPackages.has(npmPackage.data.packageName)) {
|
||||||
|
return Object.keys(projectGraph.nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (isWholeFileChange(c)) {
|
} else if (isWholeFileChange(c)) {
|
||||||
// Whole file was touched, so all npm packages are touched.
|
// Whole file was touched, so all npm packages are touched.
|
||||||
@ -76,3 +86,11 @@ export const getTouchedNpmPackages: TouchedProjectLocator<
|
|||||||
}
|
}
|
||||||
return touched;
|
return touched;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getGlobalPackages(plugins: NxJsonConfiguration['plugins']) {
|
||||||
|
return (plugins ?? [])
|
||||||
|
.map((p) =>
|
||||||
|
getPackageNameFromImportPath(typeof p === 'string' ? p : p.plugin)
|
||||||
|
)
|
||||||
|
.concat('nx');
|
||||||
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import {
|
|||||||
getRootTsConfigFileName,
|
getRootTsConfigFileName,
|
||||||
resolveModuleByImport,
|
resolveModuleByImport,
|
||||||
} from '../../utils/typescript';
|
} 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
|
* 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
|
* 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 {
|
export function isBuiltinModuleImport(importExpr: string): boolean {
|
||||||
const packageName = parsePackageNameFromImportExpression(importExpr);
|
const packageName = getPackageNameFromImportPath(importExpr);
|
||||||
return builtInModuleSet.has(packageName);
|
return builtInModuleSet.has(packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ export class TargetProjectLocator {
|
|||||||
importExpr: string,
|
importExpr: string,
|
||||||
fromFilePath: string
|
fromFilePath: string
|
||||||
): string | null {
|
): string | null {
|
||||||
const packageName = parsePackageNameFromImportExpression(importExpr);
|
const packageName = getPackageNameFromImportPath(importExpr);
|
||||||
|
|
||||||
let fullFilePath = fromFilePath;
|
let fullFilePath = fromFilePath;
|
||||||
let workspaceRelativeFilePath = 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];
|
|
||||||
}
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ export function jsPluginConfig(
|
|||||||
analyzePackageJson: true,
|
analyzePackageJson: true,
|
||||||
analyzeSourceFiles: true,
|
analyzeSourceFiles: true,
|
||||||
analyzeLockfile,
|
analyzeLockfile,
|
||||||
|
projectsAffectedByDependencyUpdates: 'all',
|
||||||
...nxJsonConfig,
|
...nxJsonConfig,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -38,6 +39,7 @@ export function jsPluginConfig(
|
|||||||
analyzeLockfile: false,
|
analyzeLockfile: false,
|
||||||
analyzePackageJson: false,
|
analyzePackageJson: false,
|
||||||
analyzeSourceFiles: false,
|
analyzeSourceFiles: false,
|
||||||
|
projectsAffectedByDependencyUpdates: 'all',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,12 +71,14 @@ export function jsPluginConfig(
|
|||||||
analyzePackageJson: true,
|
analyzePackageJson: true,
|
||||||
analyzeLockfile,
|
analyzeLockfile,
|
||||||
analyzeSourceFiles: true,
|
analyzeSourceFiles: true,
|
||||||
|
projectsAffectedByDependencyUpdates: 'all',
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
analyzePackageJson: true,
|
analyzePackageJson: true,
|
||||||
analyzeLockfile,
|
analyzeLockfile,
|
||||||
analyzeSourceFiles: false,
|
analyzeSourceFiles: false,
|
||||||
|
projectsAffectedByDependencyUpdates: 'all',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,10 +60,10 @@ function filterAffectedProjects(
|
|||||||
};
|
};
|
||||||
const reversed = reverse(graph);
|
const reversed = reverse(graph);
|
||||||
ctx.touchedProjects.forEach((p) => {
|
ctx.touchedProjects.forEach((p) => {
|
||||||
addAffectedNodes(p, reversed, result, []);
|
addAffectedNodes(p, reversed, result, new Set());
|
||||||
});
|
});
|
||||||
ctx.touchedProjects.forEach((p) => {
|
ctx.touchedProjects.forEach((p) => {
|
||||||
addAffectedDependencies(p, reversed, result, []);
|
addAffectedDependencies(p, reversed, result, new Set());
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -72,15 +72,15 @@ function addAffectedNodes(
|
|||||||
startingProject: string,
|
startingProject: string,
|
||||||
reversed: ProjectGraph,
|
reversed: ProjectGraph,
|
||||||
result: ProjectGraph,
|
result: ProjectGraph,
|
||||||
visited: string[]
|
visited: Set<string>
|
||||||
): void {
|
): void {
|
||||||
if (visited.indexOf(startingProject) > -1) return;
|
if (visited.has(startingProject)) return;
|
||||||
const reversedNode = reversed.nodes[startingProject];
|
const reversedNode = reversed.nodes[startingProject];
|
||||||
const reversedExternalNode = reversed.externalNodes[startingProject];
|
const reversedExternalNode = reversed.externalNodes[startingProject];
|
||||||
if (!reversedNode && !reversedExternalNode) {
|
if (!reversedNode && !reversedExternalNode) {
|
||||||
throw new Error(`Invalid project name is detected: "${startingProject}"`);
|
throw new Error(`Invalid project name is detected: "${startingProject}"`);
|
||||||
}
|
}
|
||||||
visited.push(startingProject);
|
visited.add(startingProject);
|
||||||
if (reversedNode) {
|
if (reversedNode) {
|
||||||
result.nodes[startingProject] = reversedNode;
|
result.nodes[startingProject] = reversedNode;
|
||||||
result.dependencies[startingProject] = [];
|
result.dependencies[startingProject] = [];
|
||||||
@ -96,10 +96,10 @@ function addAffectedDependencies(
|
|||||||
startingProject: string,
|
startingProject: string,
|
||||||
reversed: ProjectGraph,
|
reversed: ProjectGraph,
|
||||||
result: ProjectGraph,
|
result: ProjectGraph,
|
||||||
visited: string[]
|
visited: Set<string>
|
||||||
): void {
|
): void {
|
||||||
if (visited.indexOf(startingProject) > -1) return;
|
if (visited.has(startingProject)) return;
|
||||||
visited.push(startingProject);
|
visited.add(startingProject);
|
||||||
if (reversed.dependencies[startingProject]) {
|
if (reversed.dependencies[startingProject]) {
|
||||||
reversed.dependencies[startingProject].forEach(({ target }) =>
|
reversed.dependencies[startingProject].forEach(({ target }) =>
|
||||||
addAffectedDependencies(target, reversed, result, visited)
|
addAffectedDependencies(target, reversed, result, visited)
|
||||||
|
|||||||
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
14
packages/nx/src/utils/get-package-name-from-import-path.ts
Normal file
14
packages/nx/src/utils/get-package-name-from-import-path.ts
Normal 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];
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user