Skip to content

Commit

Permalink
fix: fix Project Graph generation when creating projects in nested fo…
Browse files Browse the repository at this point in the history
…lders
  • Loading branch information
tinesoft committed Feb 18, 2024
1 parent d704e1d commit 32b8fbf
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 91 deletions.
2 changes: 1 addition & 1 deletion packages/common-jvm/src/lib/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createProjectGraphAsync, logger, ProjectGraph, readCachedProjectGraph, Tree } from '@nx/devkit';
import { createProjectGraphAsync, joinPathFragments, logger, ProjectGraph, readCachedProjectGraph, Tree } from '@nx/devkit';
import { execSync } from 'child_process';
import { fileExists } from '@nx/workspace/src/utilities/fileutils';

Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/lib/workspace/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface PackageInfo {
*/
export interface WorkspacePackageInfoConfiguration {
projects: {
[projectName: string]: PackageInfo;
[projectRoot: string]: PackageInfo;
};

packages: {
Expand Down
122 changes: 63 additions & 59 deletions packages/common/src/lib/workspace/project-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import {
import { minimatch } from 'minimatch';

import { PackageInfo, WorkspacePackageInfoConfiguration } from './models';
import { getNameAndRoot } from './utils';
import { getNameAndRoot, getProjectRootFromFile } from './utils';

export function getPackageInfosForNxProjects(
function getPackageInfosForNxProjects(
pluginName: string,
projectFilter: (project: ProjectConfiguration) => boolean,
getPackageInfo: (project: ProjectConfiguration) => PackageInfo,
Expand All @@ -38,7 +38,7 @@ export function getPackageInfosForNxProjects(
try {
const pkgInfo = getPackageInfo(project);

workspacePackageInfo.projects[projectName] = pkgInfo;
workspacePackageInfo.projects[project.root] = pkgInfo;
workspacePackageInfo.packages[pkgInfo.packageId] = projectName;
} catch (e) {
if (process.env['NX_VERBOSE_LOGGING'] === 'true') {
Expand All @@ -53,62 +53,49 @@ export function getPackageInfosForNxProjects(
return workspacePackageInfo;
}

export function addDependenciesForProject(
function getDependenciesForProject(
pluginName: string,
rootProjectFolder: string,
rootProjectName: string,
rootPkgInfo: PackageInfo,
builder: ProjectGraphBuilder,
workspace: WorkspacePackageInfoConfiguration
): void {

const dependencies = getDependenciesForProject(pluginName, rootProjectFolder, rootProjectName, rootPkgInfo, workspace);
for (const dep of dependencies) {
builder.addDependency(dep.source, dep.target, dep.type, dep.source);
}
}


export function getDependenciesForProject(
pluginName: string,
rootProjectFolder: string,
rootProjectName: string,
rootPkgInfo: PackageInfo,
filePath: string,
sourceProjectName: string,
workspace: WorkspacePackageInfoConfiguration
): RawProjectGraphDependency[] {
if (process.env['NX_VERBOSE_LOGGING'] === 'true') {
logger.debug(
`[${pluginName}]: Getting dependencies for project '${rootProjectName}'...`
`[${pluginName}]: Getting dependencies for project '${sourceProjectName}'...`
);
}

const dependencies: RawProjectGraphDependency[] = [];

rootPkgInfo.dependencies?.forEach((depPkgInfo) => {
const depProjectName = workspace.packages[depPkgInfo.packageId];
const sourceProjectRoot = getProjectRootFromFile(filePath);
const sourcePkgInfo = workspace.projects[sourceProjectRoot];

if (depProjectName) {

sourcePkgInfo.dependencies?.forEach((depPkgInfo) => {
const targetProjectName = workspace.packages[depPkgInfo.packageId];

if (targetProjectName) {
dependencies.push(
{
source: rootProjectName,
target: depProjectName,
source: sourceProjectName,
target: targetProjectName,
type: DependencyType.static,
sourceFile: joinPathFragments(rootProjectFolder, rootPkgInfo.packageFile),
sourceFile: joinPathFragments(sourceProjectRoot, sourcePkgInfo.packageFile),
}
);
}
});

rootPkgInfo.modules?.forEach((moduleId) => {
sourcePkgInfo.modules?.forEach((moduleId) => {
const depProjectName = workspace.packages[moduleId];

if (depProjectName) {
dependencies.push(
{
source: rootProjectName,
source: sourceProjectName,
target: depProjectName,
type: DependencyType.static,
sourceFile: joinPathFragments(rootProjectFolder, rootPkgInfo.packageFile)
sourceFile: joinPathFragments(sourceProjectRoot, sourcePkgInfo.packageFile)
}
);
}
Expand All @@ -117,41 +104,53 @@ export function getDependenciesForProject(
return dependencies;
}

// Project Graph V1

export function getProjectGraph(
pluginName: string,
projectFilter: (project: { root: string }) => boolean,
getPackageInfo: (project: { root: string }) => PackageInfo,
graph: ProjectGraph,
context: ProjectGraphProcessorContext
ctx: ProjectGraphProcessorContext
): ProjectGraph {
const builder = new ProjectGraphBuilder(graph);
if (process.env['NX_VERBOSE_LOGGING'] === 'true') {
logger.debug(
`[${pluginName}]: Looking related projects inside the workspace...`
);
}
const workspace = getPackageInfosForNxProjects(
pluginName,
projectFilter,
getPackageInfo,
context.projectsConfigurations
);
Object.entries(workspace.projects).forEach(([projectName, pkgInfo]) => {
addDependenciesForProject(
pluginName,
graph.nodes[projectName].data.root,
projectName,
pkgInfo,
builder,
workspace
);
});
let workspace = undefined;
let dependencies: RawProjectGraphDependency[] = [];

for (const source in ctx.filesToProcess) {
const changed = ctx.filesToProcess[source];
for (const file of changed) {
// we only create the workspace map once and only if changed file is of interest
workspace ??= getPackageInfosForNxProjects(
pluginName,
projectFilter,
getPackageInfo,
ctx.projectsConfigurations
);

dependencies = dependencies.concat(
getDependenciesForProject(pluginName, file.file, source, workspace),
);
}
}

for (const dep of dependencies) {
builder.addDependency(dep.source, dep.target, dep.type, dep.source);
}

return builder.getUpdatedProjectGraph();

}

// Project Graph V2

export const createNodesFor = (projectFiles: string[], pluginName: string) =>

export const createNodesFor = (projectFiles: string[], projectFilter: (project: { root: string }) => boolean, pluginName: string) =>
[
`**/{${projectFiles.join(',')}}` as string,
(
Expand All @@ -160,6 +159,10 @@ export const createNodesFor = (projectFiles: string[], pluginName: string) =>
context: CreateNodesContext,
): CreateNodesResult => {

if (!projectFilter({ root: getProjectRootFromFile(file) })) {
return {}; // back off if the file/project does not match the criteria
}

const { root, name } = getNameAndRoot(file);

return {
Expand All @@ -185,12 +188,7 @@ export const createDependenciesIf = (pluginName: string,
`[${pluginName}]: Looking related projects inside the workspace...`
);
}
const workspace = getPackageInfosForNxProjects(
pluginName,
projectFilter,
getPackageInfo,
{ projects: ctx.projects }
);
let workspace = undefined;

let dependencies: RawProjectGraphDependency[] = [];

Expand All @@ -199,10 +197,16 @@ export const createDependenciesIf = (pluginName: string,
const changed = ctx.filesToProcess.projectFileMap[source];
for (const file of changed) {
if (minimatch(file.file, projectFilePattern)) {
const { root, name } = getNameAndRoot(file.file);
// we only create the workspace map once and only if changed file is of interest
workspace ??= getPackageInfosForNxProjects(
pluginName,
projectFilter,
getPackageInfo,
{ projects: ctx.projects }
);

dependencies = dependencies.concat(
getDependenciesForProject(pluginName, root, name, workspace.projects[name], workspace),
getDependenciesForProject(pluginName, file.file, source, workspace),
);
}
}
Expand Down
14 changes: 12 additions & 2 deletions packages/common/src/lib/workspace/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { basename, dirname, join, resolve } from 'path';
import { basename, dirname, isAbsolute, join, relative, resolve } from 'path';

import { workspaceRoot } from '@nx/devkit';
import { normalizePath, workspaceRoot } from '@nx/devkit';
import { readFileSync } from 'fs';

export function getProjectRoot(project: { root: string }) {
Expand Down Expand Up @@ -38,4 +38,14 @@ export function getCurrentAndParentFolder(cwd: string) {
const parentFolder = dirname(cwd);

return { currentFolder, parentFolder };
}


export function getProjectRootFromFile(filePath: string){

const absoluteFilePath = isAbsolute(filePath) ? filePath : resolve(workspaceRoot, filePath);

const projectRootFilePath = relative(workspaceRoot, absoluteFilePath);

return normalizePath(dirname(projectRootFilePath));
}
36 changes: 13 additions & 23 deletions packages/common/testing/e2e/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const isWin = process.platform === 'win32';
* Creates a test project with create-nx-workspace and installs the plugin
* @returns The directory where the test project was created
*/
export function createTestProject(pkgManager='npm', projectName='test-project', workspaceVersion: 'latest' | 'local' = 'local') {
export function createTestProject(pkgManager: PackageManager = 'npm', projectName = 'test-project', workspaceVersion: 'latest' | 'local' = 'local') {
const projectDirectory = join(process.cwd(), 'tmp', projectName);

// Ensure projectDirectory is empty
Expand All @@ -23,27 +23,17 @@ export function createTestProject(pkgManager='npm', projectName='test-project',
recursive: true,
});

const nxVersion = workspaceVersion === 'local' ? readLocalNxWorkspaceVersion(): 'latest';
let createCommand;
switch (pkgManager) {
case "pnpm":
createCommand = 'pnpm dlx create-nx-workspace'
break;
case "npm":
case "yarn":
createCommand = 'npx --yes create-nx-workspace';
break;
default:
throw new Error(`Unsupported package manager: ${pkgManager}`);
}
const nxVersion = workspaceVersion === 'local' ? readLocalNxWorkspaceVersion() : 'latest';

execSync(
`${createCommand}@${nxVersion} ${projectName} --preset apps --nxCloud=skip --no-interactive --pm ${pkgManager}`,
`${getPackageManagerCommand(pkgManager).dlx} create-nx-workspace@${nxVersion} ${projectName} --preset apps --nxCloud=skip --no-interactive --pm ${pkgManager}`,
{
cwd: dirname(projectDirectory),
stdio: 'inherit',
env: process.env,
}
);

console.log(`Created test project in "${projectDirectory}"`);

return projectDirectory;
Expand All @@ -53,8 +43,8 @@ export function createTestProject(pkgManager='npm', projectName='test-project',
* Creates a test project with create-nx-workspace and installs the plugin
* @returns The directory where the test project was created
*/
export function createCLITestProject(createPkgName:string, extraArgs = '', createPkgVersion='0.0.0-e2e', pkgManager: PackageManager='npm', projectName = 'test-project') {
export function createCLITestProject(createPkgName: string, extraArgs = '', createPkgVersion = '0.0.0-e2e', pkgManager: PackageManager = 'npm', projectName = 'test-project') {

const projectDirectory = join(process.cwd(), 'tmp', projectName);

// Ensure projectDirectory is empty
Expand All @@ -66,7 +56,7 @@ export function createCLITestProject(createPkgName:string, extraArgs = '', creat
recursive: true,
});

execSync(`${getPackageManagerCommand(pkgManager).exec} --yes ${createPkgName}@${createPkgVersion} ${projectName} ${extraArgs}`, {
execSync(`${getPackageManagerCommand(pkgManager).dlx} ${createPkgName}@${createPkgVersion} ${projectName} ${extraArgs}`, {
cwd: dirname(projectDirectory),
stdio: 'inherit',
env: process.env,
Expand Down Expand Up @@ -127,7 +117,7 @@ export async function runNxCommandAsync(command: string, pkgManagerExec = 'npx',
return runNxCommand(command, pkgManagerExec, opts);
}

export function readJson<T extends object = any>(path: string, options?: JsonParseOptions): T{
export function readJson<T extends object = any>(path: string, options?: JsonParseOptions): T {
return parseJson<T>(readFile(path), options);
}

Expand All @@ -136,26 +126,26 @@ export function readFile(path: string): string {
return readFileSync(filePath, 'utf-8');
}

export function getLatestNxWorkspaceVersion():string{
export function getLatestNxWorkspaceVersion(): string {
return getPackageLatestNpmVersion("nx");
}

export function readLocalNxWorkspaceVersion():string{
export function readLocalNxWorkspaceVersion(): string {
const pkgJsonPath = joinPathFragments(workspaceRoot, 'package.json');
if (!existsSync(pkgJsonPath)) {
throw new Error(
'Could not find root package.json to determine dependency versions.'
);
}

return readJsonFile(pkgJsonPath).devDependencies["nx"];
}

/**
* Checks if local Nx version matches latest feature version of Nx
* @returns `true` if on same feature version, `false` otherwise
*/
export function isLocalNxMatchingLatestFeatureVersion(){
export function isLocalNxMatchingLatestFeatureVersion() {
const localNxVersion = readLocalNxWorkspaceVersion().split('.');
const latestNxVersion = getLatestNxWorkspaceVersion().split('.');

Expand Down
3 changes: 2 additions & 1 deletion packages/nx-flutter/src/graph/create-nodes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createNodesFor } from "@nxrocks/common";
import { NX_FLUTTER_PKG } from "../index";
import { isFlutterProject } from "../utils/flutter-utils";

// wrapped into a () to avoid the 'Cannot access 'NX_FLUTTER_PKG' before initialization'
export const createNodesFn = () => createNodesFor(['pubspec.yaml'], NX_FLUTTER_PKG);
export const createNodesFn = () => createNodesFor(['pubspec.yaml'], isFlutterProject, NX_FLUTTER_PKG);
3 changes: 2 additions & 1 deletion packages/nx-ktor/src/graph/create-nodes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { JVM_PROJECT_FILES, createNodesFor } from "@nxrocks/common-jvm";
import { NX_KTOR_PKG } from "../index";
import { isKtorProject } from "../utils/ktor-utils";

// wrapped into a () to avoid the 'Cannot access 'NX_KTOR_PKG' before initialization'
export const createNodesFn = () => createNodesFor(JVM_PROJECT_FILES, NX_KTOR_PKG);
export const createNodesFn = () => createNodesFor(JVM_PROJECT_FILES, isKtorProject, NX_KTOR_PKG);
3 changes: 2 additions & 1 deletion packages/nx-micronaut/src/graph/create-nodes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { JVM_PROJECT_FILES, createNodesFor } from "@nxrocks/common-jvm";
import { NX_MICRONAUT_PKG } from "../index";
import { isMicronautProject } from "../utils/micronaut-utils";

// wrapped into a () to avoid the 'Cannot access 'NX_MICRONAUT_PKG' before initialization'
export const createNodesFn = () => createNodesFor(JVM_PROJECT_FILES, NX_MICRONAUT_PKG);
export const createNodesFn = () => createNodesFor(JVM_PROJECT_FILES, isMicronautProject, NX_MICRONAUT_PKG);
3 changes: 2 additions & 1 deletion packages/nx-quarkus/src/graph/create-nodes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { JVM_PROJECT_FILES, createNodesFor } from "@nxrocks/common-jvm";
import { NX_QUARKUS_PKG } from "../index";
import { isQuarkusProject } from "../utils/quarkus-utils";

// wrapped into a () to avoid the 'Cannot access 'NX_QUARKUS_PKG' before initialization'
export const createNodesFn = () => createNodesFor(JVM_PROJECT_FILES, NX_QUARKUS_PKG);
export const createNodesFn = () => createNodesFor(JVM_PROJECT_FILES, isQuarkusProject, NX_QUARKUS_PKG);
Loading

0 comments on commit 32b8fbf

Please sign in to comment.