Skip to content

Commit

Permalink
feat(core): support inference in generators
Browse files Browse the repository at this point in the history
  • Loading branch information
AgentEnder committed Jul 21, 2023
1 parent 56fe9ee commit cd0d320
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 163 deletions.
165 changes: 4 additions & 161 deletions packages/nx/src/config/workspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ import {
} from '../project-graph/utils/find-project-for-path';
import { ProjectGraphExternalNode } from './project-graph';
import { readNxJson } from './nx-json';
import { getNxCoreProjectPlugin } from '../plugins/nx-projects';
import { getGlobPatternsFromPackageManagerWorkspaces } from '../plugins/nx-projects/package-manager-workspaces';
import { combineGlobPatterns } from '../utils/glob';

export class Workspaces {
private cachedProjectsConfig: ProjectsConfigurations;
Expand Down Expand Up @@ -220,75 +223,6 @@ export async function getGlobPatternsFromPluginsAsync(
);
}

/**
* Get the package.json globs from package manager workspaces
*/
export function getGlobPatternsFromPackageManagerWorkspaces(
root: string,
readJson: <T extends Object = any>(f: string) => T = <T extends Object>(f) =>
readJsonFile<T>(join(root, f))
): string[] {
try {
const patterns: string[] = [];
const packageJson = readJson<PackageJson>('package.json');
patterns.push(
...normalizePatterns(
Array.isArray(packageJson.workspaces)
? packageJson.workspaces
: packageJson.workspaces?.packages ?? []
)
);

if (existsSync(join(root, 'pnpm-workspace.yaml'))) {
try {
const { packages } = readYamlFile<{ packages: string[] }>(
join(root, 'pnpm-workspace.yaml')
);
patterns.push(...normalizePatterns(packages || []));
} catch (e: unknown) {
output.warn({
title: `${NX_PREFIX} Unable to parse pnpm-workspace.yaml`,
bodyLines: [e.toString()],
});
}
}

if (existsSync(join(root, 'lerna.json'))) {
try {
const { packages } = readJson('lerna.json');
patterns.push(
...normalizePatterns(packages?.length > 0 ? packages : ['packages/*'])
);
} catch (e: unknown) {
output.warn({
title: `${NX_PREFIX} Unable to parse lerna.json`,
bodyLines: [e.toString()],
});
}
}

// Merge patterns from workspaces definitions
// TODO(@AgentEnder): update logic after better way to determine root project inclusion
// Include the root project
return packageJson.nx ? patterns.concat('package.json') : patterns;
} catch (e) {
return [];
}
}

function normalizePatterns(patterns: string[]): string[] {
return patterns.map((pattern) =>
removeRelativePath(
pattern.endsWith('/package.json')
? pattern
: joinPathFragments(pattern, 'package.json')
)
);
}

function removeRelativePath(pattern: string): string {
return pattern.startsWith('./') ? pattern.substring(2) : pattern;
}

export function globForProjectFiles(
root: string,
Expand Down Expand Up @@ -389,59 +323,6 @@ export function globForProjectFiles(
return projectGlobCache;
}

const combineGlobPatterns = (patterns: string[]) =>
patterns.length > 1
? '{' + patterns.join(',') + '}'
: patterns.length === 1
? patterns[0]
: '';

function buildProjectConfigurationFromPackageJson(
path: string,
packageJson: { name: string },
nxJson: NxJsonConfiguration
): ProjectConfiguration & { name: string } {
const normalizedPath = path.split('\\').join('/');
const directory = dirname(normalizedPath);

if (!packageJson.name && directory === '.') {
throw new Error(
'Nx requires the root package.json to specify a name if it is being used as an Nx project.'
);
}

let name = packageJson.name ?? toProjectName(normalizedPath);
if (nxJson?.npmScope) {
const npmPrefix = `@${nxJson.npmScope}/`;
if (name.startsWith(npmPrefix)) {
name = name.replace(npmPrefix, '');
}
}
const projectType =
nxJson?.workspaceLayout?.appsDir != nxJson?.workspaceLayout?.libsDir &&
nxJson?.workspaceLayout?.appsDir &&
directory.startsWith(nxJson.workspaceLayout.appsDir)
? 'application'
: 'library';

return {
root: directory,
sourceRoot: directory,
name,
projectType,
};
}

export function inferProjectFromNonStandardFile(
file: string
): ProjectConfiguration & { name: string } {
const directory = dirname(file).split('\\').join('/');

return {
name: toProjectName(file),
root: directory,
};
}

function mergeProjectConfigurationIntoWorkspace(
// projectName -> ProjectConfiguration
Expand Down Expand Up @@ -538,45 +419,7 @@ export function buildProjectsConfigurationsFromProjectPaths(
).reverse();

// We push the nx core node builder onto the end, s.t. it overwrites any user specified behavior
const globPatternsFromPackageManagerWorkspaces =
getGlobPatternsFromPackageManagerWorkspaces(root, readJson);
const nxCorePlugin: NxPluginV2 = {
name: 'nx-core-build-nodes',
processProjectNodes: {
// Load projects from pnpm / npm workspaces
...(globPatternsFromPackageManagerWorkspaces.length
? {
[combineGlobPatterns(globPatternsFromPackageManagerWorkspaces)]: (
pkgJsonPath
) => {
const json = readJson<PackageJson>(pkgJsonPath);
return {
projectNodes: {
[json.name]: buildProjectConfigurationFromPackageJson(
pkgJsonPath,
json,
nxJson
),
},
};
},
}
: {}),
// Load projects from project.json files. These will be read second, since
// they are listed last in the plugin, so they will overwrite things from the package.json
// based projects.
'{project.json,**/project.json}': (file) => {
const json = readJson<ProjectConfiguration>(file);
json.name ??= toProjectName(file);
json.root ??= dirname(file).split('\\').join('/');
return {
projectNodes: {
[json.name]: json,
},
};
},
},
};
const nxCorePlugin: NxPluginV2 = getNxCoreProjectPlugin(root, readJson, nxJson);
plugins.push(nxCorePlugin);

// We iterate over plugins first - this ensures that plugins specified first take precedence.
Expand Down
13 changes: 11 additions & 2 deletions packages/nx/src/generators/utils/project-configuration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { basename, join, relative } from 'path';
import { basename, dirname, join, relative } from 'path';
import {
ProjectConfiguration,
ProjectsConfigurations,
Expand Down Expand Up @@ -184,7 +184,7 @@ function readAndCombineAllProjectConfigurations(tree: Tree): {

const globbedFiles = globForProjectFiles(
tree.root,
getGlobPatternsFromPlugins(nxJson, getNxRequirePaths(tree.root), tree.root),
[],
nxJson
).map(normalizePath);
const createdFiles = findCreatedProjectFiles(tree);
Expand All @@ -193,6 +193,15 @@ function readAndCombineAllProjectConfigurations(tree: Tree): {
(r) => deletedFiles.indexOf(r) === -1
);

return projectFiles.reduce((projects, projectFile) => {
if (basename(projectFile) === 'project.json') {
const json = readJson(tree, projectFile);
json.root = dirname(projectFile);
projects[json.name] = json;
}
return projects;
}, {} as Record<string, ProjectConfiguration>)

return buildProjectsConfigurationsFromProjectPaths(
nxJson,
projectFiles,
Expand Down
60 changes: 60 additions & 0 deletions packages/nx/src/plugins/nx-projects/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { NxJsonConfiguration } from '../../config/nx-json';
import { toProjectName } from '../../config/workspaces';
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
import { NxPluginV2 } from '../../utils/nx-plugin';
import { getGlobPatternsFromPackageManagerWorkspaces } from './package-manager-workspaces';
import { combineGlobPatterns } from '../../utils/glob';
import { PackageJson } from '../../utils/package-json';
import { dirname } from 'node:path';

export function getNxCoreProjectPlugin(
root: string,
readJson: <T extends Object>(string: any) => T,
nxJson: NxJsonConfiguration<string[] | '*'>
) {
const globPatternsFromPackageManagerWorkspaces =
getGlobPatternsFromPackageManagerWorkspaces(root, readJson);
const nxCorePlugin: NxPluginV2 = {
name: 'nx-core-build-nodes',
processProjectNodes: {
// Load projects from pnpm / npm workspaces
...(globPatternsFromPackageManagerWorkspaces.length
? {
[combineGlobPatterns(globPatternsFromPackageManagerWorkspaces)]: (
pkgJsonPath
) => {
const json = readJson<PackageJson>(pkgJsonPath);
json.name
return {
projectNodes: {
[json.name]: buildProjectConfigurationFromPackageJson(
pkgJsonPath,
json,
nxJson
),
},
};
},
}
: {}),
// Load projects from project.json files. These will be read second, since
// they are listed last in the plugin, so they will overwrite things from the package.json
// based projects.
'{project.json,**/project.json}': (file) => {
const json = readJson<ProjectConfiguration>(file);
json.name ??= toProjectName(file);
json.root ??= dirname(file).split('\\').join('/');
return {
projectNodes: {
[json.name]: json,
},
};
},
},
};
return nxCorePlugin;
}
function buildProjectConfigurationFromPackageJson(pkgJsonPath: string, json: PackageJson, nxJson: NxJsonConfiguration<string[] | "*">): ProjectConfiguration {
throw new Error('Function not implemented.');
}

Loading

0 comments on commit cd0d320

Please sign in to comment.