feat(core): make createNodes async (#20195)
Co-authored-by: Jonathan Cammisuli <jon@cammisuli.ca>
This commit is contained in:
parent
6475c41ec8
commit
206247f9ad
9
docs/generated/devkit/CreateNodesAsync.md
Normal file
9
docs/generated/devkit/CreateNodesAsync.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Type alias: CreateNodesAsync<T\>
|
||||
|
||||
Ƭ **CreateNodesAsync**<`T`\>: readonly [projectFilePattern: string, createNodesFunction: CreateNodesFunctionAsync<T\>]
|
||||
|
||||
#### Type parameters
|
||||
|
||||
| Name | Type |
|
||||
| :--- | :-------- |
|
||||
| `T` | `unknown` |
|
||||
@ -1,6 +1,6 @@
|
||||
# Type alias: CreateNodesFunction<T\>
|
||||
|
||||
Ƭ **CreateNodesFunction**<`T`\>: (`projectConfigurationFile`: `string`, `options`: `T` \| `undefined`, `context`: [`CreateNodesContext`](../../devkit/documents/CreateNodesContext)) => { `externalNodes?`: `Record`<`string`, [`ProjectGraphExternalNode`](../../devkit/documents/ProjectGraphExternalNode)\> ; `projects?`: `Record`<`string`, `Optional`<[`ProjectConfiguration`](../../devkit/documents/ProjectConfiguration), `"root"`\>\> }
|
||||
Ƭ **CreateNodesFunction**<`T`\>: (`projectConfigurationFile`: `string`, `options`: `T` \| `undefined`, `context`: [`CreateNodesContext`](../../devkit/documents/CreateNodesContext)) => [`CreateNodesResult`](../../devkit/documents/CreateNodesResult)
|
||||
|
||||
#### Type parameters
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
#### Type declaration
|
||||
|
||||
▸ (`projectConfigurationFile`, `options`, `context`): `Object`
|
||||
▸ (`projectConfigurationFile`, `options`, `context`): [`CreateNodesResult`](../../devkit/documents/CreateNodesResult)
|
||||
|
||||
A function which parses a configuration file into a set of nodes.
|
||||
Used for creating nodes for the [ProjectGraph](../../devkit/documents/ProjectGraph)
|
||||
@ -25,9 +25,4 @@ Used for creating nodes for the [ProjectGraph](../../devkit/documents/ProjectGra
|
||||
|
||||
##### Returns
|
||||
|
||||
`Object`
|
||||
|
||||
| Name | Type | Description |
|
||||
| :--------------- | :---------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------- |
|
||||
| `externalNodes?` | `Record`<`string`, [`ProjectGraphExternalNode`](../../devkit/documents/ProjectGraphExternalNode)\> | A map of external node name -> external node. External nodes do not have a root, so the key is their name. |
|
||||
| `projects?` | `Record`<`string`, `Optional`<[`ProjectConfiguration`](../../devkit/documents/ProjectConfiguration), `"root"`\>\> | A map of project root -> project configuration |
|
||||
[`CreateNodesResult`](../../devkit/documents/CreateNodesResult)
|
||||
|
||||
24
docs/generated/devkit/CreateNodesResult.md
Normal file
24
docs/generated/devkit/CreateNodesResult.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Interface: CreateNodesResult
|
||||
|
||||
## Table of contents
|
||||
|
||||
### Properties
|
||||
|
||||
- [externalNodes](../../devkit/documents/CreateNodesResult#externalnodes): Record<string, ProjectGraphExternalNode>
|
||||
- [projects](../../devkit/documents/CreateNodesResult#projects): Record<string, Optional<ProjectConfiguration, "root">>
|
||||
|
||||
## Properties
|
||||
|
||||
### externalNodes
|
||||
|
||||
• `Optional` **externalNodes**: `Record`<`string`, [`ProjectGraphExternalNode`](../../devkit/documents/ProjectGraphExternalNode)\>
|
||||
|
||||
A map of external node name -> external node. External nodes do not have a root, so the key is their name.
|
||||
|
||||
---
|
||||
|
||||
### projects
|
||||
|
||||
• `Optional` **projects**: `Record`<`string`, `Optional`<[`ProjectConfiguration`](../../devkit/documents/ProjectConfiguration), `"root"`\>\>
|
||||
|
||||
A map of project root -> project configuration
|
||||
@ -1,19 +1,20 @@
|
||||
# Type alias: NxPluginV2<T\>
|
||||
# Type alias: NxPluginV2<TOptions, TCreateNodes\>
|
||||
|
||||
Ƭ **NxPluginV2**<`T`\>: `Object`
|
||||
Ƭ **NxPluginV2**<`TOptions`, `TCreateNodes`\>: `Object`
|
||||
|
||||
A plugin for Nx which creates nodes and dependencies for the [ProjectGraph](../../devkit/documents/ProjectGraph)
|
||||
|
||||
#### Type parameters
|
||||
|
||||
| Name | Type |
|
||||
| :--- | :-------- |
|
||||
| `T` | `unknown` |
|
||||
| Name | Type |
|
||||
| :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `TOptions` | `unknown` |
|
||||
| `TCreateNodes` | extends [`CreateNodes`](../../devkit/documents/CreateNodes)<`TOptions`\> \| [`CreateNodesAsync`](../../devkit/documents/CreateNodesAsync)<`TOptions`\> = [`CreateNodes`](../../devkit/documents/CreateNodes)<`TOptions`\> \| [`CreateNodesAsync`](../../devkit/documents/CreateNodesAsync)<`TOptions`\> |
|
||||
|
||||
#### Type declaration
|
||||
|
||||
| Name | Type | Description |
|
||||
| :-------------------- | :---------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `createDependencies?` | [`CreateDependencies`](../../devkit/documents/CreateDependencies)<`T`\> | Provides a function to analyze files to create dependencies for the [ProjectGraph](../../devkit/documents/ProjectGraph) |
|
||||
| `createNodes?` | [`CreateNodes`](../../devkit/documents/CreateNodes)<`T`\> | Provides a file pattern and function that retrieves configuration info from those files. e.g. { '\*_/_.csproj': buildProjectsFromCsProjFile } |
|
||||
| `name` | `string` | - |
|
||||
| Name | Type | Description |
|
||||
| :-------------------- | :----------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `createDependencies?` | [`CreateDependencies`](../../devkit/documents/CreateDependencies)<`TOptions`\> | Provides a function to analyze files to create dependencies for the [ProjectGraph](../../devkit/documents/ProjectGraph) |
|
||||
| `createNodes?` | `TCreateNodes` | Provides a file pattern and function that retrieves configuration info from those files. e.g. { '\*_/_.csproj': buildProjectsFromCsProjFile } |
|
||||
| `name` | `string` | - |
|
||||
|
||||
@ -25,6 +25,7 @@ It only uses language primitives and immutable objects
|
||||
|
||||
- [CreateDependenciesContext](../../devkit/documents/CreateDependenciesContext)
|
||||
- [CreateNodesContext](../../devkit/documents/CreateNodesContext)
|
||||
- [CreateNodesResult](../../devkit/documents/CreateNodesResult)
|
||||
- [DefaultTasksRunnerOptions](../../devkit/documents/DefaultTasksRunnerOptions)
|
||||
- [ExecutorContext](../../devkit/documents/ExecutorContext)
|
||||
- [ExecutorsJson](../../devkit/documents/ExecutorsJson)
|
||||
@ -64,6 +65,7 @@ It only uses language primitives and immutable objects
|
||||
|
||||
- [CreateDependencies](../../devkit/documents/CreateDependencies)
|
||||
- [CreateNodes](../../devkit/documents/CreateNodes)
|
||||
- [CreateNodesAsync](../../devkit/documents/CreateNodesAsync)
|
||||
- [CreateNodesFunction](../../devkit/documents/CreateNodesFunction)
|
||||
- [CustomHasher](../../devkit/documents/CustomHasher)
|
||||
- [DynamicDependency](../../devkit/documents/DynamicDependency)
|
||||
|
||||
@ -25,6 +25,7 @@ It only uses language primitives and immutable objects
|
||||
|
||||
- [CreateDependenciesContext](../../devkit/documents/CreateDependenciesContext)
|
||||
- [CreateNodesContext](../../devkit/documents/CreateNodesContext)
|
||||
- [CreateNodesResult](../../devkit/documents/CreateNodesResult)
|
||||
- [DefaultTasksRunnerOptions](../../devkit/documents/DefaultTasksRunnerOptions)
|
||||
- [ExecutorContext](../../devkit/documents/ExecutorContext)
|
||||
- [ExecutorsJson](../../devkit/documents/ExecutorsJson)
|
||||
@ -64,6 +65,7 @@ It only uses language primitives and immutable objects
|
||||
|
||||
- [CreateDependencies](../../devkit/documents/CreateDependencies)
|
||||
- [CreateNodes](../../devkit/documents/CreateNodes)
|
||||
- [CreateNodesAsync](../../devkit/documents/CreateNodesAsync)
|
||||
- [CreateNodesFunction](../../devkit/documents/CreateNodesFunction)
|
||||
- [CustomHasher](../../devkit/documents/CustomHasher)
|
||||
- [DynamicDependency](../../devkit/documents/DynamicDependency)
|
||||
|
||||
@ -203,7 +203,8 @@ function startDevRemotes(
|
||||
const { schema } = getExecutorInformation(
|
||||
collection,
|
||||
executor,
|
||||
workspaceRoot
|
||||
workspaceRoot,
|
||||
workspaceProjects
|
||||
);
|
||||
if (
|
||||
(options.verbose && schema.additionalProperties) ||
|
||||
@ -223,6 +224,7 @@ function startDevRemotes(
|
||||
target: 'serve',
|
||||
configuration: context.target.configuration,
|
||||
runOptions,
|
||||
projects: workspaceProjects,
|
||||
},
|
||||
options.verbose
|
||||
).then((obs) => {
|
||||
@ -279,8 +281,7 @@ export function executeModuleFederationDevServerBuilder(
|
||||
configurationName: context.target.configuration,
|
||||
cwd: context.currentDirectory,
|
||||
isVerbose: options.verbose,
|
||||
projectsConfigurations:
|
||||
readProjectsConfigurationFromProjectGraph(projectGraph),
|
||||
projectsConfigurations: { projects: workspaceProjects, version: 2 },
|
||||
nxJsonConfiguration: readNxJson(),
|
||||
}
|
||||
)
|
||||
|
||||
@ -98,7 +98,8 @@ export function executeModuleFederationDevSSRBuilder(
|
||||
const { schema } = getExecutorInformation(
|
||||
collection,
|
||||
executor,
|
||||
workspaceRoot
|
||||
workspaceRoot,
|
||||
workspaceProjects
|
||||
);
|
||||
|
||||
if (schema.additionalProperties || 'verbose' in schema.properties) {
|
||||
@ -141,6 +142,7 @@ export function executeModuleFederationDevSSRBuilder(
|
||||
target,
|
||||
configuration: context.target.configuration,
|
||||
runOptions,
|
||||
projects: workspaceProjects,
|
||||
},
|
||||
options.verbose
|
||||
).then((obs) =>
|
||||
|
||||
@ -194,7 +194,8 @@ ${e.message || e}`);
|
||||
const { schema } = getExecutorInformation(
|
||||
collection,
|
||||
executor,
|
||||
context.root
|
||||
context.root,
|
||||
context.projectsConfigurations.projects
|
||||
);
|
||||
|
||||
// NOTE: schema won't have a default since readTargetOptions would have
|
||||
|
||||
@ -38,7 +38,12 @@ export function readTargetOptions<T = any>(
|
||||
const ws = new Workspaces(context.root);
|
||||
const [nodeModule, executorName] = targetConfiguration.executor.split(':');
|
||||
const { schema } = getExecutorInformation
|
||||
? getExecutorInformation(nodeModule, executorName, context.root)
|
||||
? getExecutorInformation(
|
||||
nodeModule,
|
||||
executorName,
|
||||
context.root,
|
||||
context.projectsConfigurations?.projects ?? context.workspace.projects
|
||||
)
|
||||
: // TODO(v18): remove readExecutor. This is to be backwards compatible with Nx 16.5 and below.
|
||||
(ws as any).readExecutor(nodeModule, executorName);
|
||||
|
||||
|
||||
@ -35,7 +35,10 @@ import {
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
} from '../generators/utils/project-configuration';
|
||||
import { createProjectGraphAsync } from '../project-graph/project-graph';
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
readProjectsConfigurationFromProjectGraph,
|
||||
} from '../project-graph/project-graph';
|
||||
import { readJsonFile } from '../utils/fileutils';
|
||||
import { getNxRequirePaths } from '../utils/installation-directory';
|
||||
import { parseJson } from '../utils/json';
|
||||
@ -79,7 +82,8 @@ export async function createBuilderContext(
|
||||
);
|
||||
const architectHost = await getWrappedWorkspaceNodeModulesArchitectHost(
|
||||
workspace,
|
||||
context.root
|
||||
context.root,
|
||||
context.projectsConfigurations.projects
|
||||
);
|
||||
|
||||
const registry = new schema.CoreSchemaRegistry();
|
||||
@ -156,6 +160,7 @@ export async function scheduleTarget(
|
||||
target: string;
|
||||
configuration: string;
|
||||
runOptions: any;
|
||||
projects: Record<string, ProjectConfiguration>;
|
||||
},
|
||||
verbose: boolean
|
||||
): Promise<Observable<import('@angular-devkit/architect').BuilderOutput>> {
|
||||
@ -177,7 +182,8 @@ export async function scheduleTarget(
|
||||
|
||||
const architectHost = await getWrappedWorkspaceNodeModulesArchitectHost(
|
||||
workspace,
|
||||
root
|
||||
root,
|
||||
opts.projects
|
||||
);
|
||||
const architect: Architect = new Architect(architectHost, registry);
|
||||
const run = await architect.scheduleTarget(
|
||||
@ -204,7 +210,8 @@ export async function scheduleTarget(
|
||||
}
|
||||
|
||||
function createNodeModulesEngineHost(
|
||||
resolvePaths: string[]
|
||||
resolvePaths: string[],
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
): import('@angular-devkit/schematics/tools').NodeModulesEngineHost {
|
||||
const NodeModulesEngineHost = require('@angular-devkit/schematics/tools')
|
||||
.NodeModulesEngineHost as typeof import('@angular-devkit/schematics/tools').NodeModulesEngineHost;
|
||||
@ -226,7 +233,7 @@ function createNodeModulesEngineHost(
|
||||
const {
|
||||
json: { generators, schematics },
|
||||
path: packageJsonPath,
|
||||
} = readPluginPackageJson(name, paths);
|
||||
} = readPluginPackageJson(name, projects, paths);
|
||||
|
||||
if (!schematics && !generators) {
|
||||
throw new Error(
|
||||
@ -255,7 +262,8 @@ function createNodeModulesEngineHost(
|
||||
function createWorkflow(
|
||||
fsHost: virtualFs.Host<Stats>,
|
||||
root: string,
|
||||
opts: any
|
||||
opts: any,
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
): import('@angular-devkit/schematics/tools').NodeWorkflow {
|
||||
const NodeWorkflow: typeof import('@angular-devkit/schematics/tools').NodeWorkflow =
|
||||
require('@angular-devkit/schematics/tools').NodeWorkflow;
|
||||
@ -269,7 +277,7 @@ function createWorkflow(
|
||||
),
|
||||
resolvePaths: [process.cwd(), root],
|
||||
engineHostCreator: (options) =>
|
||||
createNodeModulesEngineHost(options.resolvePaths),
|
||||
createNodeModulesEngineHost(options.resolvePaths, projects),
|
||||
});
|
||||
workflow.registry.addPostTransform(schema.transforms.addUndefinedDefaults);
|
||||
workflow.engineHost.registerOptionsTransform(
|
||||
@ -676,6 +684,7 @@ function findMatchingFileChange(host: Tree, path: Path) {
|
||||
export async function generate(
|
||||
root: string,
|
||||
opts: GenerateOptions,
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
verbose: boolean
|
||||
) {
|
||||
const logger = getLogger(verbose);
|
||||
@ -687,7 +696,7 @@ export async function generate(
|
||||
`ng-cli generator: ${opts.collectionName}:${opts.generatorName}`
|
||||
)
|
||||
);
|
||||
const workflow = createWorkflow(fsHost, root, opts);
|
||||
const workflow = createWorkflow(fsHost, root, opts, projects);
|
||||
const collection = getCollection(workflow, opts.collectionName);
|
||||
const schematic = collection.createSchematic(opts.generatorName, true);
|
||||
return (
|
||||
@ -769,6 +778,7 @@ export async function runMigration(
|
||||
root: string,
|
||||
packageName: string,
|
||||
migrationName: string,
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
isVerbose: boolean
|
||||
) {
|
||||
const logger = getLogger(isVerbose);
|
||||
@ -780,7 +790,7 @@ export async function runMigration(
|
||||
`ng-cli migration: ${packageName}:${migrationName}`
|
||||
)
|
||||
);
|
||||
const workflow = createWorkflow(fsHost, root, {});
|
||||
const workflow = createWorkflow(fsHost, root, {}, projects);
|
||||
const collection = resolveMigrationsCollection(packageName);
|
||||
|
||||
const record = { loggingQueue: [] as string[], error: false };
|
||||
@ -889,6 +899,9 @@ export function wrapAngularDevkitSchematic(
|
||||
require('./compat');
|
||||
|
||||
return async (host: Tree, generatorOptions: { [k: string]: any }) => {
|
||||
const graph = await createProjectGraphAsync();
|
||||
const { projects } = readProjectsConfigurationFromProjectGraph(graph);
|
||||
|
||||
if (
|
||||
mockedSchematics &&
|
||||
mockedSchematics[`${collectionName}:${generatorName}`]
|
||||
@ -950,7 +963,7 @@ export function wrapAngularDevkitSchematic(
|
||||
defaults: false,
|
||||
quiet: false,
|
||||
};
|
||||
const workflow = createWorkflow(fsHost, host.root, options);
|
||||
const workflow = createWorkflow(fsHost, host.root, options, projects);
|
||||
|
||||
// used for testing
|
||||
if (collectionResolutionOverrides) {
|
||||
@ -1053,14 +1066,19 @@ function saveProjectsConfigurationsInWrappedSchematic(
|
||||
|
||||
async function getWrappedWorkspaceNodeModulesArchitectHost(
|
||||
workspace: workspaces.WorkspaceDefinition,
|
||||
root: string
|
||||
root: string,
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
) {
|
||||
const {
|
||||
WorkspaceNodeModulesArchitectHost: AngularWorkspaceNodeModulesArchitectHost,
|
||||
} = await import('@angular-devkit/architect/node');
|
||||
|
||||
class WrappedWorkspaceNodeModulesArchitectHost extends AngularWorkspaceNodeModulesArchitectHost {
|
||||
constructor(private workspace, private root) {
|
||||
constructor(
|
||||
private workspace,
|
||||
private root: string,
|
||||
private projects: Record<string, ProjectConfiguration>
|
||||
) {
|
||||
super(workspace, root);
|
||||
}
|
||||
|
||||
@ -1090,6 +1108,7 @@ async function getWrappedWorkspaceNodeModulesArchitectHost(
|
||||
const { json: packageJson, path: packageJsonPath } =
|
||||
readPluginPackageJson(
|
||||
nodeModule,
|
||||
this.projects,
|
||||
this.root ? [this.root, __dirname] : [__dirname]
|
||||
);
|
||||
const executorsFile = packageJson.executors ?? packageJson.builders;
|
||||
@ -1170,5 +1189,9 @@ async function getWrappedWorkspaceNodeModulesArchitectHost(
|
||||
}
|
||||
}
|
||||
|
||||
return new WrappedWorkspaceNodeModulesArchitectHost(workspace, root);
|
||||
return new WrappedWorkspaceNodeModulesArchitectHost(
|
||||
workspace,
|
||||
root,
|
||||
projects
|
||||
);
|
||||
}
|
||||
|
||||
@ -71,7 +71,12 @@ async function promptForCollection(
|
||||
resolvedCollectionName,
|
||||
normalizedGeneratorName,
|
||||
generatorConfiguration: { ['x-deprecated']: deprecated, hidden },
|
||||
} = getGeneratorInformation(collectionName, generatorName, workspaceRoot);
|
||||
} = getGeneratorInformation(
|
||||
collectionName,
|
||||
generatorName,
|
||||
workspaceRoot,
|
||||
projectsConfiguration.projects
|
||||
);
|
||||
if (hidden) {
|
||||
continue;
|
||||
}
|
||||
@ -96,7 +101,12 @@ async function promptForCollection(
|
||||
resolvedCollectionName,
|
||||
normalizedGeneratorName,
|
||||
generatorConfiguration: { ['x-deprecated']: deprecated, hidden },
|
||||
} = getGeneratorInformation(name, generatorName, workspaceRoot);
|
||||
} = getGeneratorInformation(
|
||||
name,
|
||||
generatorName,
|
||||
workspaceRoot,
|
||||
projectsConfiguration.projects
|
||||
);
|
||||
if (hidden) {
|
||||
continue;
|
||||
}
|
||||
@ -158,7 +168,12 @@ async function promptForCollection(
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
getGeneratorInformation(value, generatorName, workspaceRoot);
|
||||
getGeneratorInformation(
|
||||
value,
|
||||
generatorName,
|
||||
workspaceRoot,
|
||||
projectsConfiguration.projects
|
||||
);
|
||||
return true;
|
||||
} catch {
|
||||
logger.error(`\nCould not find ${value}:${generatorName}`);
|
||||
@ -316,7 +331,8 @@ export async function generate(cwd: string, args: { [k: string]: any }) {
|
||||
} = getGeneratorInformation(
|
||||
opts.collectionName,
|
||||
opts.generatorName,
|
||||
workspaceRoot
|
||||
workspaceRoot,
|
||||
projectsConfigurations.projects
|
||||
);
|
||||
|
||||
if (deprecated) {
|
||||
@ -360,7 +376,8 @@ export async function generate(cwd: string, args: { [k: string]: any }) {
|
||||
getGeneratorInformation(
|
||||
opts.collectionName,
|
||||
normalizedGeneratorName,
|
||||
workspaceRoot
|
||||
workspaceRoot,
|
||||
projectsConfigurations.projects
|
||||
).isNxGenerator
|
||||
) {
|
||||
const host = new FsTree(
|
||||
@ -400,6 +417,7 @@ export async function generate(cwd: string, args: { [k: string]: any }) {
|
||||
...opts,
|
||||
generatorOptions: combinedOpts,
|
||||
},
|
||||
projectsConfigurations.projects,
|
||||
verbose
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
GeneratorsJson,
|
||||
GeneratorsJsonEntry,
|
||||
} from '../../config/misc-interfaces';
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
import {
|
||||
getImplementationFactory,
|
||||
resolveSchema,
|
||||
@ -14,7 +15,8 @@ import { readPluginPackageJson } from '../../utils/nx-plugin';
|
||||
export function getGeneratorInformation(
|
||||
collectionName: string,
|
||||
generatorName: string,
|
||||
root: string | null
|
||||
root: string | null,
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
): {
|
||||
resolvedCollectionName: string;
|
||||
normalizedGeneratorName: string;
|
||||
@ -30,7 +32,7 @@ export function getGeneratorInformation(
|
||||
generatorsJson,
|
||||
resolvedCollectionName,
|
||||
normalizedGeneratorName,
|
||||
} = readGeneratorsJson(collectionName, generatorName, root);
|
||||
} = readGeneratorsJson(collectionName, generatorName, root, projects);
|
||||
const generatorsDir = dirname(generatorsFilePath);
|
||||
const generatorConfig =
|
||||
generatorsJson.generators?.[normalizedGeneratorName] ||
|
||||
@ -71,7 +73,8 @@ export function getGeneratorInformation(
|
||||
export function readGeneratorsJson(
|
||||
collectionName: string,
|
||||
generator: string,
|
||||
root: string | null
|
||||
root: string | null,
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
): {
|
||||
generatorsFilePath: string;
|
||||
generatorsJson: GeneratorsJson;
|
||||
@ -86,6 +89,7 @@ export function readGeneratorsJson(
|
||||
} else {
|
||||
const { json: packageJson, path: packageJsonPath } = readPluginPackageJson(
|
||||
collectionName,
|
||||
projects,
|
||||
root ? [root, __dirname] : [__dirname]
|
||||
);
|
||||
const generatorsFile = packageJson.generators ?? packageJson.schematics;
|
||||
@ -109,7 +113,7 @@ export function readGeneratorsJson(
|
||||
if (!normalizedGeneratorName) {
|
||||
for (let parent of generatorsJson.extends || []) {
|
||||
try {
|
||||
return readGeneratorsJson(parent, generator, root);
|
||||
return readGeneratorsJson(parent, generator, root, projects);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
|
||||
@ -31,19 +31,19 @@ export interface ListArgs {
|
||||
*
|
||||
*/
|
||||
export async function listHandler(args: ListArgs): Promise<void> {
|
||||
if (args.plugin) {
|
||||
await listPluginCapabilities(args.plugin);
|
||||
} else {
|
||||
const nxJson = readNxJson();
|
||||
const corePlugins = fetchCorePlugins();
|
||||
const projectGraph = await createProjectGraphAsync({ exitOnError: true });
|
||||
const nxJson = readNxJson();
|
||||
const projectGraph = await createProjectGraphAsync({ exitOnError: true });
|
||||
const projects = readProjectsConfigurationFromProjectGraph(projectGraph);
|
||||
|
||||
const localPlugins = await getLocalWorkspacePlugins(
|
||||
readProjectsConfigurationFromProjectGraph(projectGraph),
|
||||
nxJson
|
||||
);
|
||||
if (args.plugin) {
|
||||
await listPluginCapabilities(args.plugin, projects.projects);
|
||||
} else {
|
||||
const corePlugins = fetchCorePlugins();
|
||||
|
||||
const localPlugins = await getLocalWorkspacePlugins(projects, nxJson);
|
||||
const installedPlugins = await getInstalledPluginsAndCapabilities(
|
||||
workspaceRoot
|
||||
workspaceRoot,
|
||||
projects.projects
|
||||
);
|
||||
|
||||
if (localPlugins.size) {
|
||||
|
||||
@ -62,6 +62,10 @@ import { readNxJson } from '../../config/configuration';
|
||||
import { runNxSync } from '../../utils/child-process';
|
||||
import { daemonClient } from '../../daemon/client/client';
|
||||
import { isNxCloudUsed } from '../../utils/nx-cloud-utils';
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
readProjectsConfigurationFromProjectGraph,
|
||||
} from '../../project-graph/project-graph';
|
||||
|
||||
export interface ResolvedMigrationConfiguration extends MigrationsJson {
|
||||
packageGroup?: ArrayPackageGroup;
|
||||
@ -1404,6 +1408,9 @@ export async function executeMigrations(
|
||||
root,
|
||||
m.package,
|
||||
m.name,
|
||||
readProjectsConfigurationFromProjectGraph(
|
||||
await createProjectGraphAsync()
|
||||
).projects,
|
||||
isVerbose
|
||||
);
|
||||
|
||||
|
||||
@ -16,7 +16,12 @@ export async function newWorkspace(cwd: string, args: { [k: string]: any }) {
|
||||
async () => {
|
||||
const isInteractive = args.interactive;
|
||||
const { normalizedGeneratorName, schema, implementationFactory } =
|
||||
getGeneratorInformation('@nx/workspace/generators.json', 'new', null);
|
||||
getGeneratorInformation(
|
||||
'@nx/workspace/generators.json',
|
||||
'new',
|
||||
null,
|
||||
{}
|
||||
);
|
||||
removeSpecialFlags(args);
|
||||
const combinedOpts = await combineOptionsForGenerator(
|
||||
args,
|
||||
|
||||
@ -51,6 +51,7 @@ export interface ReleaseVersionGeneratorSchema {
|
||||
|
||||
export async function versionHandler(args: VersionOptions): Promise<void> {
|
||||
const projectGraph = await createProjectGraphAsync({ exitOnError: true });
|
||||
const { projects } = readProjectsConfigurationFromProjectGraph(projectGraph);
|
||||
const nxJson = readNxJson();
|
||||
|
||||
if (args.verbose) {
|
||||
@ -98,6 +99,7 @@ export async function versionHandler(args: VersionOptions): Promise<void> {
|
||||
releaseGroup.version.generator
|
||||
),
|
||||
configGeneratorOptions: releaseGroup.version.generatorOptions,
|
||||
projects,
|
||||
});
|
||||
|
||||
const releaseGroupProjectNames = Array.from(
|
||||
@ -133,6 +135,7 @@ export async function versionHandler(args: VersionOptions): Promise<void> {
|
||||
releaseGroup.version.generator
|
||||
),
|
||||
configGeneratorOptions: releaseGroup.version.generatorOptions,
|
||||
projects,
|
||||
});
|
||||
|
||||
await runVersionOnProjects(
|
||||
@ -260,9 +263,15 @@ function resolveGeneratorData({
|
||||
collectionName,
|
||||
generatorName,
|
||||
configGeneratorOptions,
|
||||
projects,
|
||||
}): GeneratorData {
|
||||
const { normalizedGeneratorName, schema, implementationFactory } =
|
||||
getGeneratorInformation(collectionName, generatorName, workspaceRoot);
|
||||
getGeneratorInformation(
|
||||
collectionName,
|
||||
generatorName,
|
||||
workspaceRoot,
|
||||
projects
|
||||
);
|
||||
|
||||
return {
|
||||
collectionName,
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
resolveSchema,
|
||||
} from '../../config/schema-utils';
|
||||
import { getNxRequirePaths } from '../../utils/installation-directory';
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
|
||||
export function normalizeExecutorSchema(
|
||||
schema: Partial<ExecutorConfig['schema']>
|
||||
@ -40,7 +41,8 @@ const cachedExecutorInformation = {};
|
||||
export function getExecutorInformation(
|
||||
nodeModule: string,
|
||||
executor: string,
|
||||
root: string
|
||||
root: string,
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
): ExecutorConfig & { isNgCompat: boolean; isNxExecutor: boolean } {
|
||||
try {
|
||||
const key = cacheKey(nodeModule, executor, root);
|
||||
@ -49,7 +51,8 @@ export function getExecutorInformation(
|
||||
const { executorsFilePath, executorConfig, isNgCompat } = readExecutorJson(
|
||||
nodeModule,
|
||||
executor,
|
||||
root
|
||||
root,
|
||||
projects
|
||||
);
|
||||
const executorsDir = dirname(executorsFilePath);
|
||||
const schemaPath = resolveSchema(executorConfig.schema, executorsDir);
|
||||
@ -95,7 +98,8 @@ export function getExecutorInformation(
|
||||
function readExecutorJson(
|
||||
nodeModule: string,
|
||||
executor: string,
|
||||
root: string
|
||||
root: string,
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
): {
|
||||
executorsFilePath: string;
|
||||
executorConfig: {
|
||||
@ -108,6 +112,7 @@ function readExecutorJson(
|
||||
} {
|
||||
const { json: packageJson, path: packageJsonPath } = readPluginPackageJson(
|
||||
nodeModule,
|
||||
projects,
|
||||
root
|
||||
? [root, __dirname, process.cwd(), ...getNxRequirePaths()]
|
||||
: [__dirname, process.cwd(), ...getNxRequirePaths()]
|
||||
|
||||
@ -96,7 +96,8 @@ async function parseExecutorAndTarget(
|
||||
const { schema, implementationFactory } = getExecutorInformation(
|
||||
nodeModule,
|
||||
executor,
|
||||
root
|
||||
root,
|
||||
projectsConfigurations.projects
|
||||
);
|
||||
|
||||
return { executor, implementationFactory, nodeModule, schema, targetConfig };
|
||||
@ -154,7 +155,14 @@ async function runExecutorInternal<T extends { success: boolean }>(
|
||||
isVerbose
|
||||
);
|
||||
|
||||
if (getExecutorInformation(nodeModule, executor, root).isNxExecutor) {
|
||||
if (
|
||||
getExecutorInformation(
|
||||
nodeModule,
|
||||
executor,
|
||||
root,
|
||||
projectsConfigurations.projects
|
||||
).isNxExecutor
|
||||
) {
|
||||
const implementation = implementationFactory() as Executor<any>;
|
||||
const r = implementation(combinedOptions, {
|
||||
root,
|
||||
@ -190,6 +198,7 @@ async function runExecutorInternal<T extends { success: boolean }>(
|
||||
target,
|
||||
configuration,
|
||||
runOptions: combinedOptions,
|
||||
projects: projectsConfigurations.projects,
|
||||
},
|
||||
isVerbose
|
||||
);
|
||||
|
||||
@ -60,30 +60,25 @@ describe('Workspaces', () => {
|
||||
}),
|
||||
});
|
||||
|
||||
withEnvironmentVariables(
|
||||
const { projectNodes } = await withEnvironmentVariables(
|
||||
{
|
||||
NX_WORKSPACE_ROOT: fs.tempDir,
|
||||
},
|
||||
async () => {
|
||||
const resolved = await retrieveProjectConfigurations(
|
||||
fs.tempDir,
|
||||
readNxJson(fs.tempDir)
|
||||
);
|
||||
expect(resolved.projectNodes['my-package']).toEqual({
|
||||
name: 'my-package',
|
||||
root: 'packages/my-package',
|
||||
sourceRoot: 'packages/my-package',
|
||||
projectType: 'library',
|
||||
targets: {
|
||||
'nx-release-publish': {
|
||||
dependsOn: ['^nx-release-publish'],
|
||||
executor: '@nx/js:release-publish',
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
() => retrieveProjectConfigurations(fs.tempDir, readNxJson(fs.tempDir))
|
||||
);
|
||||
expect(projectNodes['my-package']).toEqual({
|
||||
name: 'my-package',
|
||||
root: 'packages/my-package',
|
||||
sourceRoot: 'packages/my-package',
|
||||
projectType: 'library',
|
||||
targets: {
|
||||
'nx-release-publish': {
|
||||
dependsOn: ['^nx-release-publish'],
|
||||
executor: '@nx/js:release-publish',
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { dirname } from 'path';
|
||||
import {
|
||||
readCachedProjectGraph,
|
||||
readProjectsConfigurationFromProjectGraph,
|
||||
} from '../project-graph/project-graph';
|
||||
|
||||
import type { NxJsonConfiguration } from './nx-json';
|
||||
import { readNxJson } from './nx-json';
|
||||
import { ProjectsConfigurations } from './workspace-json-project-json';
|
||||
import { retrieveProjectConfigurationsSync } from '../project-graph/utils/retrieve-workspace-files';
|
||||
|
||||
// TODO(v18): remove this class
|
||||
/**
|
||||
@ -19,9 +22,7 @@ export class Workspaces {
|
||||
const nxJson = readNxJson(this.root);
|
||||
|
||||
return {
|
||||
version: 2,
|
||||
projects: retrieveProjectConfigurationsSync(this.root, nxJson)
|
||||
.projectNodes,
|
||||
...readProjectsConfigurationFromProjectGraph(readCachedProjectGraph()),
|
||||
...nxJson,
|
||||
};
|
||||
}
|
||||
|
||||
@ -51,7 +51,9 @@ export type {
|
||||
NxPluginV2,
|
||||
ProjectTargetConfigurator,
|
||||
CreateNodes,
|
||||
CreateNodesAsync,
|
||||
CreateNodesFunction,
|
||||
CreateNodesResult,
|
||||
CreateNodesContext,
|
||||
CreateDependencies,
|
||||
CreateDependenciesContext,
|
||||
|
||||
4
packages/nx/src/native/index.d.ts
vendored
4
packages/nx/src/native/index.d.ts
vendored
@ -134,9 +134,9 @@ export class Watcher {
|
||||
export class WorkspaceContext {
|
||||
workspaceRoot: string
|
||||
constructor(workspaceRoot: string)
|
||||
getWorkspaceFiles(globs: Array<string>, parseConfigurations: (arg0: Array<string>) => Record<string, string>): NxWorkspaceFiles
|
||||
getWorkspaceFiles(globs: Array<string>, parseConfigurations: (arg0: Array<string>) => Promise<Record<string, string>>): Promise<NxWorkspaceFiles>
|
||||
glob(globs: Array<string>): Array<string>
|
||||
getProjectConfigurations(globs: Array<string>, parseConfigurations: (arg0: Array<string>) => Record<string, string>): Record<string, string>
|
||||
getProjectConfigurations(globs: Array<string>, parseConfigurations: (arg0: Array<string>) => Promise<Record<string, string>>): Promise<Record<string, string>>
|
||||
incrementalUpdate(updatedFiles: Array<string>, deletedFiles: Array<string>): Record<string, string>
|
||||
allFileData(): Array<FileData>
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import { readJsonFile } from '../../utils/fileutils';
|
||||
|
||||
describe('workspace files', () => {
|
||||
function createParseConfigurationsFunction(tempDir: string) {
|
||||
return (filenames: string[]) => {
|
||||
return async (filenames: string[]) => {
|
||||
const res = {};
|
||||
for (const filename of filenames) {
|
||||
const json = readJsonFile(join(tempDir, filename));
|
||||
@ -51,7 +51,7 @@ describe('workspace files', () => {
|
||||
let globs = ['project.json', '**/project.json', 'libs/*/package.json'];
|
||||
|
||||
const context = new WorkspaceContext(fs.tempDir);
|
||||
let { projectFileMap, globalFiles } = context.getWorkspaceFiles(
|
||||
let { projectFileMap, globalFiles } = await context.getWorkspaceFiles(
|
||||
globs,
|
||||
createParseConfigurationsFunction(fs.tempDir)
|
||||
);
|
||||
@ -148,7 +148,7 @@ describe('workspace files', () => {
|
||||
const context = new WorkspaceContext(fs.tempDir);
|
||||
|
||||
const globs = ['project.json', '**/project.json', '**/package.json'];
|
||||
const { globalFiles, projectFileMap } = context.getWorkspaceFiles(
|
||||
const { globalFiles, projectFileMap } = await context.getWorkspaceFiles(
|
||||
globs,
|
||||
createParseConfigurationsFunction(fs.tempDir)
|
||||
);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[napi(object)]
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FileData {
|
||||
pub file: String,
|
||||
pub hash: String,
|
||||
@ -17,7 +17,7 @@ impl PartialEq<Self> for FileData {
|
||||
|
||||
impl PartialOrd<Self> for FileData {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.file.partial_cmp(&other.file)
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use crate::native::glob::build_glob_set;
|
||||
use crate::native::utils::path::Normalize;
|
||||
use std::collections::HashMap;
|
||||
use napi::bindgen_prelude::Promise;
|
||||
|
||||
use crate::native::workspace::errors::{InternalWorkspaceErrors, WorkspaceErrors};
|
||||
use rayon::prelude::*;
|
||||
@ -29,9 +30,9 @@ pub(super) fn get_project_configurations<ConfigurationParser>(
|
||||
globs: Vec<String>,
|
||||
files: Option<&[(PathBuf, String)]>,
|
||||
parse_configurations: ConfigurationParser,
|
||||
) -> napi::Result<HashMap<String, String>>
|
||||
) -> napi::Result<Promise<HashMap<String, String>>>
|
||||
where
|
||||
ConfigurationParser: Fn(Vec<String>) -> napi::Result<HashMap<String, String>>,
|
||||
ConfigurationParser: Fn(Vec<String>) -> napi::Result<Promise<HashMap<String, String>>>,
|
||||
{
|
||||
let config_paths = glob_files(globs, files).map_err(anyhow::Error::from)?;
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ use std::collections::HashMap;
|
||||
|
||||
use crate::native::types::FileData;
|
||||
use crate::native::utils::path::Normalize;
|
||||
use napi::bindgen_prelude::*;
|
||||
use parking_lot::lock_api::MutexGuard;
|
||||
use parking_lot::{Condvar, Mutex, RawMutex};
|
||||
use rayon::prelude::*;
|
||||
@ -15,7 +16,6 @@ use xxhash_rust::xxh3;
|
||||
|
||||
use crate::native::walker::nx_walker;
|
||||
use crate::native::workspace::errors::WorkspaceErrors;
|
||||
use crate::native::workspace::workspace_files::NxWorkspaceFiles;
|
||||
use crate::native::workspace::{config_files, workspace_files};
|
||||
|
||||
#[napi]
|
||||
@ -148,20 +148,23 @@ impl WorkspaceContext {
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
#[napi(ts_return_type = "Promise<NxWorkspaceFiles>")]
|
||||
pub fn get_workspace_files<ConfigurationParser>(
|
||||
&self,
|
||||
env: Env,
|
||||
globs: Vec<String>,
|
||||
parse_configurations: ConfigurationParser,
|
||||
) -> napi::Result<NxWorkspaceFiles, WorkspaceErrors>
|
||||
) -> anyhow::Result<Option<Object>>
|
||||
where
|
||||
ConfigurationParser: Fn(Vec<String>) -> napi::Result<HashMap<String, String>>,
|
||||
ConfigurationParser: Fn(Vec<String>) -> napi::Result<Promise<HashMap<String, String>>>,
|
||||
{
|
||||
workspace_files::get_files(
|
||||
env,
|
||||
globs,
|
||||
parse_configurations,
|
||||
self.files_worker.get_files().as_deref(),
|
||||
)
|
||||
.map_err(anyhow::Error::from)
|
||||
}
|
||||
|
||||
#[napi]
|
||||
@ -169,20 +172,25 @@ impl WorkspaceContext {
|
||||
config_files::glob_files(globs, self.files_worker.get_files().as_deref())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
#[napi(ts_return_type = "Promise<Record<string, string>>")]
|
||||
pub fn get_project_configurations<ConfigurationParser>(
|
||||
&self,
|
||||
env: Env,
|
||||
globs: Vec<String>,
|
||||
parse_configurations: ConfigurationParser,
|
||||
) -> napi::Result<HashMap<String, String>>
|
||||
) -> napi::Result<Object>
|
||||
where
|
||||
ConfigurationParser: Fn(Vec<String>) -> napi::Result<HashMap<String, String>>,
|
||||
ConfigurationParser: Fn(Vec<String>) -> napi::Result<Promise<HashMap<String, String>>>,
|
||||
{
|
||||
config_files::get_project_configurations(
|
||||
let promise = config_files::get_project_configurations(
|
||||
globs,
|
||||
self.files_worker.get_files().as_deref(),
|
||||
parse_configurations,
|
||||
)
|
||||
)?;
|
||||
env.spawn_future(async move {
|
||||
let result = promise.await?;
|
||||
Ok(result)
|
||||
})
|
||||
}
|
||||
|
||||
#[napi]
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
use napi::bindgen_prelude::{Object, Promise};
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use napi::Env;
|
||||
use rayon::prelude::*;
|
||||
use tracing::trace;
|
||||
|
||||
@ -18,74 +20,83 @@ pub struct NxWorkspaceFiles {
|
||||
}
|
||||
|
||||
pub(super) fn get_files<ConfigurationParser>(
|
||||
env: Env,
|
||||
globs: Vec<String>,
|
||||
parse_configurations: ConfigurationParser,
|
||||
file_data: Option<&[(PathBuf, String)]>,
|
||||
) -> napi::Result<NxWorkspaceFiles, WorkspaceErrors>
|
||||
) -> napi::Result<Option<Object>>
|
||||
where
|
||||
ConfigurationParser: Fn(Vec<String>) -> napi::Result<HashMap<String, String>>,
|
||||
ConfigurationParser: Fn(Vec<String>) -> napi::Result<Promise<HashMap<String, String>>>,
|
||||
{
|
||||
let Some(file_data) = file_data else {
|
||||
return Ok(Default::default());
|
||||
};
|
||||
|
||||
trace!("{globs:?}");
|
||||
let root_map = transform_root_map(
|
||||
config_files::get_project_configurations(globs, Some(file_data), parse_configurations)
|
||||
.map_err(|e| InternalWorkspaceErrors::ParseError(e.to_string()))?,
|
||||
);
|
||||
let file_data = file_data.to_vec();
|
||||
let promise =
|
||||
config_files::get_project_configurations(globs, Some(&file_data), parse_configurations)?;
|
||||
|
||||
trace!(?root_map);
|
||||
let result = env.spawn_future(async move {
|
||||
let parsed_graph_nodes = promise.await?;
|
||||
|
||||
let file_locations = file_data
|
||||
.into_par_iter()
|
||||
.map(|(file_path, hash)| {
|
||||
let mut parent = file_path.parent().unwrap_or_else(|| Path::new("."));
|
||||
let root_map = transform_root_map(
|
||||
parsed_graph_nodes
|
||||
);
|
||||
|
||||
while root_map.get(parent).is_none() && parent != Path::new(".") {
|
||||
parent = parent.parent().unwrap_or_else(|| Path::new("."));
|
||||
}
|
||||
trace!(?root_map);
|
||||
|
||||
let file_data = FileData {
|
||||
file: file_path.to_normalized_string(),
|
||||
hash: hash.clone(),
|
||||
};
|
||||
let file_locations = file_data
|
||||
.into_par_iter()
|
||||
.map(|(file_path, hash)| {
|
||||
let mut parent = file_path.parent().unwrap_or_else(|| Path::new("."));
|
||||
|
||||
match root_map.get(parent) {
|
||||
Some(project_name) => (FileLocation::Project(project_name.into()), file_data),
|
||||
None => (FileLocation::Global, file_data),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<(FileLocation, FileData)>>();
|
||||
|
||||
let mut project_file_map: HashMap<String, Vec<FileData>> = HashMap::with_capacity(
|
||||
file_locations
|
||||
.iter()
|
||||
.filter(|&f| f.0 != FileLocation::Global)
|
||||
.count(),
|
||||
);
|
||||
let mut global_files: Vec<FileData> = Vec::with_capacity(
|
||||
file_locations
|
||||
.iter()
|
||||
.filter(|&f| f.0 == FileLocation::Global)
|
||||
.count(),
|
||||
);
|
||||
for (file_location, file_data) in file_locations {
|
||||
match file_location {
|
||||
FileLocation::Global => global_files.push(file_data),
|
||||
FileLocation::Project(project_name) => match project_file_map.get_mut(&project_name) {
|
||||
None => {
|
||||
project_file_map.insert(project_name.clone(), vec![file_data]);
|
||||
while root_map.get(parent).is_none() && parent != Path::new(".") {
|
||||
parent = parent.parent().unwrap_or_else(|| Path::new("."));
|
||||
}
|
||||
Some(project_files) => project_files.push(file_data),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(NxWorkspaceFiles {
|
||||
project_file_map,
|
||||
global_files,
|
||||
})
|
||||
let file_data = FileData {
|
||||
file: file_path.to_normalized_string(),
|
||||
hash: hash.clone(),
|
||||
};
|
||||
|
||||
match root_map.get(parent) {
|
||||
Some(project_name) => (FileLocation::Project(project_name.into()), file_data),
|
||||
None => (FileLocation::Global, file_data),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<(FileLocation, FileData)>>();
|
||||
|
||||
let mut project_file_map: HashMap<String, Vec<FileData>> = HashMap::with_capacity(
|
||||
file_locations
|
||||
.iter()
|
||||
.filter(|&f| f.0 != FileLocation::Global)
|
||||
.count(),
|
||||
);
|
||||
let mut global_files: Vec<FileData> = Vec::with_capacity(
|
||||
file_locations
|
||||
.iter()
|
||||
.filter(|&f| f.0 == FileLocation::Global)
|
||||
.count(),
|
||||
);
|
||||
for (file_location, file_data) in file_locations {
|
||||
match file_location {
|
||||
FileLocation::Global => global_files.push(file_data),
|
||||
FileLocation::Project(project_name) => match project_file_map.get_mut(&project_name) {
|
||||
None => {
|
||||
project_file_map.insert(project_name.clone(), vec![file_data]);
|
||||
}
|
||||
Some(project_files) => project_files.push(file_data),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(NxWorkspaceFiles {
|
||||
project_file_map,
|
||||
global_files,
|
||||
})
|
||||
})?;
|
||||
Ok(Some(result))
|
||||
}
|
||||
|
||||
fn transform_root_map(root_map: HashMap<String, String>) -> hashbrown::HashMap<PathBuf, String> {
|
||||
|
||||
@ -3,22 +3,18 @@ import * as memfs from 'memfs';
|
||||
import '../../../internal-testing-utils/mock-fs';
|
||||
|
||||
import { CreatePackageJsonProjectsNextToProjectJson } from './package-json-next-to-project-json';
|
||||
import {
|
||||
CreateNodesContext,
|
||||
CreateNodesFunction,
|
||||
} from '../../../utils/nx-plugin';
|
||||
import { CreateNodesContext } from '../../../utils/nx-plugin';
|
||||
const { createNodes } = CreatePackageJsonProjectsNextToProjectJson;
|
||||
|
||||
describe('nx project.json plugin', () => {
|
||||
let context: CreateNodesContext;
|
||||
let createNodesFunction: CreateNodesFunction;
|
||||
let createNodesFunction = createNodes[1];
|
||||
|
||||
beforeEach(() => {
|
||||
context = {
|
||||
nxJsonConfiguration: {},
|
||||
workspaceRoot: '/root',
|
||||
};
|
||||
createNodesFunction = createNodes[1];
|
||||
});
|
||||
|
||||
it('should build projects from project.json', () => {
|
||||
|
||||
@ -3,9 +3,9 @@ import { dirname, join } from 'node:path';
|
||||
import { ProjectConfiguration } from '../../../config/workspace-json-project-json';
|
||||
import { toProjectName } from '../../../config/workspaces';
|
||||
import { readJsonFile } from '../../../utils/fileutils';
|
||||
import { NxPluginV2 } from '../../../utils/nx-plugin';
|
||||
import { CreateNodes, NxPluginV2 } from '../../../utils/nx-plugin';
|
||||
|
||||
export const CreateProjectJsonProjectsPlugin: NxPluginV2 = {
|
||||
export const CreateProjectJsonProjectsPlugin: NxPluginV2<void, CreateNodes> = {
|
||||
name: 'nx-core-build-project-json-nodes',
|
||||
createNodes: [
|
||||
'{project.json,**/project.json}',
|
||||
|
||||
@ -22,10 +22,8 @@ import {
|
||||
import { getRootTsConfigPath } from '../plugins/js/utils/typescript';
|
||||
import {
|
||||
FileMap,
|
||||
ProjectFileMap,
|
||||
ProjectGraph,
|
||||
ProjectGraphExternalNode,
|
||||
ProjectGraphProcessorContext,
|
||||
} from '../config/project-graph';
|
||||
import { readJsonFile } from '../utils/fileutils';
|
||||
import { NxJsonConfiguration } from '../config/nx-json';
|
||||
@ -34,6 +32,7 @@ import { ProjectConfiguration } from '../config/workspace-json-project-json';
|
||||
import { readNxJson } from '../config/configuration';
|
||||
import { existsSync } from 'fs';
|
||||
import { PackageJson } from '../utils/package-json';
|
||||
import { getNxRequirePaths } from '../utils/installation-directory';
|
||||
|
||||
let storedFileMap: FileMap | null = null;
|
||||
let storedAllWorkspaceFiles: FileData[] | null = null;
|
||||
@ -231,7 +230,12 @@ async function updateProjectGraphWithPlugins(
|
||||
context: CreateDependenciesContext,
|
||||
initProjectGraph: ProjectGraph
|
||||
) {
|
||||
const plugins = await loadNxPlugins(context.nxJsonConfiguration?.plugins);
|
||||
const plugins = await loadNxPlugins(
|
||||
context.nxJsonConfiguration?.plugins,
|
||||
getNxRequirePaths(),
|
||||
context.workspaceRoot,
|
||||
context.projects
|
||||
);
|
||||
let graph = initProjectGraph;
|
||||
for (const { plugin } of plugins) {
|
||||
try {
|
||||
|
||||
@ -14,7 +14,10 @@ import {
|
||||
} from './project-graph';
|
||||
import { toOldFormat } from '../adapter/angular-json';
|
||||
import { getIgnoreObject } from '../utils/ignore';
|
||||
import { retrieveProjectConfigurationsSync } from './utils/retrieve-workspace-files';
|
||||
import { retrieveProjectConfigurationPathsWithoutPluginInference } from './utils/retrieve-workspace-files';
|
||||
import { buildProjectsConfigurationsFromProjectPathsAndPlugins } from './utils/project-configuration-utils';
|
||||
import { NxJsonConfiguration } from '../config/nx-json';
|
||||
import { getDefaultPluginsSync } from '../utils/nx-plugin.deprecated';
|
||||
|
||||
export interface Change {
|
||||
type: string;
|
||||
@ -139,7 +142,7 @@ export function readWorkspaceConfig(opts: {
|
||||
} catch {
|
||||
configuration = {
|
||||
version: 2,
|
||||
projects: retrieveProjectConfigurationsSync(root, nxJson).projectNodes,
|
||||
projects: getProjectsSyncNoInference(root, nxJson).projects,
|
||||
};
|
||||
}
|
||||
if (opts.format === 'angularCli') {
|
||||
@ -164,3 +167,14 @@ export function readPackageJson(): any {
|
||||
export { FileData };
|
||||
// TODO(17): Remove these exports
|
||||
export { readNxJson, workspaceLayout } from '../config/configuration';
|
||||
|
||||
function getProjectsSyncNoInference(root: string, nxJson: NxJsonConfiguration) {
|
||||
const paths = retrieveProjectConfigurationPathsWithoutPluginInference(root);
|
||||
return buildProjectsConfigurationsFromProjectPathsAndPlugins(
|
||||
nxJson,
|
||||
paths,
|
||||
getDefaultPluginsSync(root),
|
||||
root,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,8 +5,9 @@ import {
|
||||
TargetConfiguration,
|
||||
} from '../../config/workspace-json-project-json';
|
||||
import { NX_PREFIX } from '../../utils/logger';
|
||||
import { LoadedNxPlugin } from '../../utils/nx-plugin';
|
||||
import { CreateNodesResult, LoadedNxPlugin } from '../../utils/nx-plugin';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import { output } from '../../utils/output';
|
||||
|
||||
import minimatch = require('minimatch');
|
||||
|
||||
@ -88,18 +89,48 @@ export function mergeProjectConfigurationIntoRootMap(
|
||||
);
|
||||
}
|
||||
|
||||
type ConfigurationResult = {
|
||||
projects: Record<string, ProjectConfiguration>;
|
||||
externalNodes: Record<string, ProjectGraphExternalNode>;
|
||||
rootMap: Record<string, string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* ** DO NOT USE ** - Please use without the `skipAsync` parameter.
|
||||
* @deprecated
|
||||
* @todo(@agentender): Remove in Nx 18 alongside the removal of its usage.
|
||||
*/
|
||||
export function buildProjectsConfigurationsFromProjectPathsAndPlugins(
|
||||
nxJson: NxJsonConfiguration,
|
||||
projectFiles: string[], // making this parameter allows devkit to pick up newly created projects
|
||||
plugins: LoadedNxPlugin[],
|
||||
root: string = workspaceRoot
|
||||
): {
|
||||
projects: Record<string, ProjectConfiguration>;
|
||||
externalNodes: Record<string, ProjectGraphExternalNode>;
|
||||
rootMap: Record<string, string>;
|
||||
} {
|
||||
const projectRootMap: Map<string, ProjectConfiguration> = new Map();
|
||||
const externalNodes: Record<string, ProjectGraphExternalNode> = {};
|
||||
root: string,
|
||||
skipAsync: true
|
||||
): ConfigurationResult;
|
||||
|
||||
/**
|
||||
* Transforms a list of project paths into a map of project configurations.
|
||||
*
|
||||
* @param nxJson The NxJson configuration
|
||||
* @param projectFiles A list of files identified as projects
|
||||
* @param plugins The plugins that should be used to infer project configuration
|
||||
* @param root The workspace root
|
||||
*/
|
||||
export function buildProjectsConfigurationsFromProjectPathsAndPlugins(
|
||||
nxJson: NxJsonConfiguration,
|
||||
projectFiles: string[], // making this parameter allows devkit to pick up newly created projects
|
||||
plugins: LoadedNxPlugin[],
|
||||
root: string,
|
||||
skipAsync?: false
|
||||
): Promise<ConfigurationResult>;
|
||||
export function buildProjectsConfigurationsFromProjectPathsAndPlugins(
|
||||
nxJson: NxJsonConfiguration,
|
||||
projectFiles: string[], // making this parameter allows devkit to pick up newly created projects
|
||||
plugins: LoadedNxPlugin[],
|
||||
root: string = workspaceRoot,
|
||||
skipAsync: boolean = false
|
||||
): ConfigurationResult | Promise<ConfigurationResult> {
|
||||
const results: Array<CreateNodesResult | Promise<CreateNodesResult>> = [];
|
||||
|
||||
// We iterate over plugins first - this ensures that plugins specified first take precedence.
|
||||
for (const { plugin, options } of plugins) {
|
||||
@ -109,31 +140,53 @@ export function buildProjectsConfigurationsFromProjectPathsAndPlugins(
|
||||
}
|
||||
for (const file of projectFiles) {
|
||||
if (minimatch(file, pattern, { dot: true })) {
|
||||
const { projects: projectNodes, externalNodes: pluginExternalNodes } =
|
||||
results.push(
|
||||
createNodes(file, options, {
|
||||
nxJsonConfiguration: nxJson,
|
||||
workspaceRoot: root,
|
||||
});
|
||||
for (const node in projectNodes) {
|
||||
mergeProjectConfigurationIntoRootMap(projectRootMap, {
|
||||
// If root is specified in config, that will overwrite this.
|
||||
// Specifying it here though allows plugins to return something like
|
||||
// {
|
||||
// projects: {
|
||||
// [root]: { targets: buildTargetsFromFile(f) }
|
||||
// }
|
||||
// }
|
||||
// Otherwise, the root would have to be specified in the config as well
|
||||
// which would be a bit redundant.
|
||||
root: node,
|
||||
...projectNodes[node],
|
||||
});
|
||||
}
|
||||
Object.assign(externalNodes, pluginExternalNodes);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return skipAsync
|
||||
? combineSyncConfigurationResults(results)
|
||||
: combineAsyncConfigurationResults(results);
|
||||
}
|
||||
|
||||
function combineSyncConfigurationResults(
|
||||
results: (CreateNodesResult | Promise<CreateNodesResult>)[]
|
||||
): ConfigurationResult {
|
||||
const projectRootMap: Map<string, ProjectConfiguration> = new Map();
|
||||
const externalNodes: Record<string, ProjectGraphExternalNode> = {};
|
||||
|
||||
let warned = false;
|
||||
for (const result of results) {
|
||||
if (typeof result === 'object' && 'then' in result) {
|
||||
if (!warned) {
|
||||
output.warn({
|
||||
title: 'One or more plugins in this workspace are async.',
|
||||
bodyLines: [
|
||||
'Configuration from these plugins will not be visible to readWorkspaceConfig or readWorkspaceConfiguration. If you are using these methods, consider reading project info from the graph with createProjectGraphAsync instead.',
|
||||
'If you are not using one of these methods, please open an issue at http://github.com/nrwl/nx',
|
||||
],
|
||||
});
|
||||
warned = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const { projects: projectNodes, externalNodes: pluginExternalNodes } =
|
||||
result;
|
||||
for (const node in projectNodes) {
|
||||
mergeProjectConfigurationIntoRootMap(projectRootMap, {
|
||||
root: node,
|
||||
...projectNodes[node],
|
||||
});
|
||||
}
|
||||
Object.assign(externalNodes, pluginExternalNodes);
|
||||
}
|
||||
|
||||
const rootMap = createRootMap(projectRootMap);
|
||||
|
||||
return {
|
||||
@ -143,6 +196,12 @@ export function buildProjectsConfigurationsFromProjectPathsAndPlugins(
|
||||
};
|
||||
}
|
||||
|
||||
function combineAsyncConfigurationResults(
|
||||
results: Array<CreateNodesResult | Promise<CreateNodesResult>>
|
||||
): Promise<ConfigurationResult> {
|
||||
return Promise.all(results).then((r) => combineSyncConfigurationResults(r));
|
||||
}
|
||||
|
||||
export function readProjectConfigurationsFromRootMap(
|
||||
projectRootMap: Map<string, ProjectConfiguration>
|
||||
) {
|
||||
|
||||
@ -21,13 +21,7 @@ import {
|
||||
getNxPackageJsonWorkspacesPlugin,
|
||||
} from '../../../plugins/package-json-workspaces';
|
||||
import { buildProjectsConfigurationsFromProjectPathsAndPlugins } from './project-configuration-utils';
|
||||
import {
|
||||
LoadedNxPlugin,
|
||||
loadNxPlugins,
|
||||
loadNxPluginsSync,
|
||||
NxPluginV2,
|
||||
unregisterPluginTSTranspiler,
|
||||
} from '../../utils/nx-plugin';
|
||||
import { LoadedNxPlugin, loadNxPlugins } from '../../utils/nx-plugin';
|
||||
import { CreateProjectJsonProjectsPlugin } from '../../plugins/project-json/build-nodes/project-json';
|
||||
import {
|
||||
globWithWorkspaceContext,
|
||||
@ -63,11 +57,11 @@ export async function retrieveWorkspaceFiles(
|
||||
let projects: Record<string, ProjectConfiguration>;
|
||||
let externalNodes: Record<string, ProjectGraphExternalNode>;
|
||||
|
||||
const { projectFileMap, globalFiles } = getNxWorkspaceFilesFromContext(
|
||||
const { projectFileMap, globalFiles } = (await getNxWorkspaceFilesFromContext(
|
||||
workspaceRoot,
|
||||
globs,
|
||||
(configs: string[]) => {
|
||||
const projectConfigurations = createProjectConfigurations(
|
||||
async (configs: string[]) => {
|
||||
const projectConfigurations = await createProjectConfigurations(
|
||||
workspaceRoot,
|
||||
nxJson,
|
||||
configs,
|
||||
@ -79,7 +73,7 @@ export async function retrieveWorkspaceFiles(
|
||||
externalNodes = projectConfigurations.externalNodes;
|
||||
return projectConfigurations.rootMap;
|
||||
}
|
||||
) as NxWorkspaceFiles;
|
||||
)) as NxWorkspaceFiles;
|
||||
performance.mark('get-workspace-files:end');
|
||||
performance.measure(
|
||||
'get-workspace-files',
|
||||
@ -148,49 +142,30 @@ export async function retrieveProjectConfigurationsWithAngularProjects(
|
||||
return _retrieveProjectConfigurations(workspaceRoot, nxJson, plugins, globs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link retrieveProjectConfigurations} instead.
|
||||
*/
|
||||
export function retrieveProjectConfigurationsSync(
|
||||
workspaceRoot: string,
|
||||
nxJson: NxJsonConfiguration
|
||||
): {
|
||||
externalNodes: Record<string, ProjectGraphExternalNode>;
|
||||
projectNodes: Record<string, ProjectConfiguration>;
|
||||
} {
|
||||
const plugins = loadNxPluginsSync(
|
||||
nxJson?.plugins ?? [],
|
||||
getNxRequirePaths(workspaceRoot),
|
||||
workspaceRoot
|
||||
);
|
||||
|
||||
const globs = configurationGlobs(workspaceRoot, plugins);
|
||||
return _retrieveProjectConfigurations(workspaceRoot, nxJson, plugins, globs);
|
||||
}
|
||||
|
||||
function _retrieveProjectConfigurations(
|
||||
workspaceRoot: string,
|
||||
nxJson: NxJsonConfiguration,
|
||||
plugins: LoadedNxPlugin[],
|
||||
globs: string[]
|
||||
): {
|
||||
): Promise<{
|
||||
externalNodes: Record<string, ProjectGraphExternalNode>;
|
||||
projectNodes: Record<string, ProjectConfiguration>;
|
||||
} {
|
||||
}> {
|
||||
let result: {
|
||||
externalNodes: Record<string, ProjectGraphExternalNode>;
|
||||
projectNodes: Record<string, ProjectConfiguration>;
|
||||
};
|
||||
getProjectConfigurationsFromContext(
|
||||
return getProjectConfigurationsFromContext(
|
||||
workspaceRoot,
|
||||
globs,
|
||||
(configs: string[]) => {
|
||||
const { projects, externalNodes, rootMap } = createProjectConfigurations(
|
||||
workspaceRoot,
|
||||
nxJson,
|
||||
configs,
|
||||
plugins
|
||||
);
|
||||
async (configs: string[]) => {
|
||||
const { projects, externalNodes, rootMap } =
|
||||
await createProjectConfigurations(
|
||||
workspaceRoot,
|
||||
nxJson,
|
||||
configs,
|
||||
plugins
|
||||
);
|
||||
|
||||
result = {
|
||||
projectNodes: projects,
|
||||
@ -199,8 +174,7 @@ function _retrieveProjectConfigurations(
|
||||
|
||||
return rootMap;
|
||||
}
|
||||
);
|
||||
return result;
|
||||
).then(() => result);
|
||||
}
|
||||
|
||||
export async function retrieveProjectConfigurationPaths(
|
||||
@ -226,9 +200,9 @@ const projectsWithoutPluginCache = new Map<
|
||||
>();
|
||||
|
||||
// TODO: This function is called way too often, it should be optimized without this cache
|
||||
export function retrieveProjectConfigurationsWithoutPluginInference(
|
||||
export async function retrieveProjectConfigurationsWithoutPluginInference(
|
||||
root: string
|
||||
): Record<string, ProjectConfiguration> {
|
||||
): Promise<Record<string, ProjectConfiguration>> {
|
||||
const nxJson = readNxJson(root);
|
||||
const projectGlobPatterns = configurationGlobsWithoutPlugins(root);
|
||||
const cacheKey = root + ',' + projectGlobPatterns.join(',');
|
||||
@ -238,11 +212,11 @@ export function retrieveProjectConfigurationsWithoutPluginInference(
|
||||
}
|
||||
|
||||
let projects: Record<string, ProjectConfiguration>;
|
||||
getProjectConfigurationsFromContext(
|
||||
await getProjectConfigurationsFromContext(
|
||||
root,
|
||||
projectGlobPatterns,
|
||||
(configs: string[]) => {
|
||||
const projectConfigurations = createProjectConfigurations(
|
||||
async (configs: string[]) => {
|
||||
const projectConfigurations = await createProjectConfigurations(
|
||||
root,
|
||||
nxJson,
|
||||
configs,
|
||||
@ -280,20 +254,20 @@ function buildAllWorkspaceFiles(
|
||||
return fileData;
|
||||
}
|
||||
|
||||
export function createProjectConfigurations(
|
||||
export async function createProjectConfigurations(
|
||||
workspaceRoot: string,
|
||||
nxJson: NxJsonConfiguration,
|
||||
configFiles: string[],
|
||||
plugins: LoadedNxPlugin[]
|
||||
): {
|
||||
): Promise<{
|
||||
projects: Record<string, ProjectConfiguration>;
|
||||
externalNodes: Record<string, ProjectGraphExternalNode>;
|
||||
rootMap: Record<string, string>;
|
||||
} {
|
||||
}> {
|
||||
performance.mark('build-project-configs:start');
|
||||
|
||||
const { projects, externalNodes, rootMap } =
|
||||
buildProjectsConfigurationsFromProjectPathsAndPlugins(
|
||||
await buildProjectsConfigurationsFromProjectPathsAndPlugins(
|
||||
nxJson,
|
||||
configFiles,
|
||||
plugins,
|
||||
|
||||
@ -16,10 +16,19 @@ import {
|
||||
import { readNxJson } from '../../config/configuration';
|
||||
import { isAsyncIterator } from '../../utils/async-iterator';
|
||||
import { getExecutorInformation } from '../../command-line/run/executor-utils';
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
|
||||
function getBatchExecutor(executorName: string) {
|
||||
function getBatchExecutor(
|
||||
executorName: string,
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
) {
|
||||
const [nodeModule, exportName] = executorName.split(':');
|
||||
return getExecutorInformation(nodeModule, exportName, workspaceRoot);
|
||||
return getExecutorInformation(
|
||||
nodeModule,
|
||||
exportName,
|
||||
workspaceRoot,
|
||||
projects
|
||||
);
|
||||
}
|
||||
|
||||
async function runTasks(
|
||||
@ -32,7 +41,10 @@ async function runTasks(
|
||||
const projectsConfigurations =
|
||||
readProjectsConfigurationFromProjectGraph(projectGraph);
|
||||
const nxJsonConfiguration = readNxJson();
|
||||
const batchExecutor = getBatchExecutor(executorName);
|
||||
const batchExecutor = getBatchExecutor(
|
||||
executorName,
|
||||
projectsConfigurations.projects
|
||||
);
|
||||
const tasks = Object.values(batchTaskGraph.tasks);
|
||||
const context: ExecutorContext = {
|
||||
root: workspaceRoot,
|
||||
|
||||
@ -10,6 +10,7 @@ import { serializeOverridesIntoCommandLine } from '../utils/serialize-overrides-
|
||||
import { splitByColons } from '../utils/split-target';
|
||||
import { getExecutorInformation } from '../command-line/run/executor-utils';
|
||||
import { CustomHasher } from '../config/misc-interfaces';
|
||||
import { readProjectsConfigurationFromProjectGraph } from '../project-graph/project-graph';
|
||||
|
||||
export function getCommandAsString(execCommand: string, task: Task) {
|
||||
const args = getPrintableCommandArgsForTask(task);
|
||||
@ -262,7 +263,12 @@ export async function getExecutorForTask(
|
||||
const executor = await getExecutorNameForTask(task, projectGraph);
|
||||
const [nodeModule, executorName] = executor.split(':');
|
||||
|
||||
return getExecutorInformation(nodeModule, executorName, workspaceRoot);
|
||||
return getExecutorInformation(
|
||||
nodeModule,
|
||||
executorName,
|
||||
workspaceRoot,
|
||||
readProjectsConfigurationFromProjectGraph(projectGraph).projects
|
||||
);
|
||||
}
|
||||
|
||||
export async function getCustomHasher(
|
||||
|
||||
@ -200,8 +200,8 @@ describe.each([
|
||||
iterations
|
||||
);
|
||||
const directTime = time(() => minimatch.match(items, pattern), iterations);
|
||||
// Using minimatch directly takes at least twice as long than using the cache.
|
||||
expect(directTime / cacheTime).toBeGreaterThan(2);
|
||||
// Using minimatch directly is slower than using the cache.
|
||||
expect(directTime / cacheTime).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
it(`should be comparable to using minimatch a single time (${pattern})`, () => {
|
||||
|
||||
@ -1,5 +1,25 @@
|
||||
import { getNxPackageJsonWorkspacesPlugin } from '../../plugins/package-json-workspaces';
|
||||
import {
|
||||
NxAngularJsonPlugin,
|
||||
shouldMergeAngularProjects,
|
||||
} from '../adapter/angular-json';
|
||||
import { NxJsonConfiguration, PluginConfiguration } from '../config/nx-json';
|
||||
import { ProjectGraphProcessor } from '../config/project-graph';
|
||||
import { TargetConfiguration } from '../config/workspace-json-project-json';
|
||||
import {
|
||||
ProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
} from '../config/workspace-json-project-json';
|
||||
import { CreateProjectJsonProjectsPlugin } from '../plugins/project-json/build-nodes/project-json';
|
||||
import { retrieveProjectConfigurationsWithoutPluginInference } from '../project-graph/utils/retrieve-workspace-files';
|
||||
import { getNxRequirePaths } from './installation-directory';
|
||||
import {
|
||||
ensurePluginIsV2,
|
||||
getPluginPathAndName,
|
||||
LoadedNxPlugin,
|
||||
nxPluginCache,
|
||||
NxPluginV2,
|
||||
} from './nx-plugin';
|
||||
import { workspaceRoot } from './workspace-root';
|
||||
|
||||
/**
|
||||
* @deprecated Add targets to the projects in a {@link CreateNodes} function instead. This will be removed in Nx 18
|
||||
@ -30,3 +50,17 @@ export type NxPluginV1 = {
|
||||
*/
|
||||
projectFilePatterns?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* @todo(@agentender) v18: Remove this fn when we remove readWorkspaceConfig
|
||||
*/
|
||||
export function getDefaultPluginsSync(root: string): LoadedNxPlugin[] {
|
||||
const plugins: NxPluginV2[] = [require('../plugins/js')];
|
||||
|
||||
if (shouldMergeAngularProjects(root, false)) {
|
||||
plugins.push(require('../adapter/angular-json').NxAngularJsonPlugin);
|
||||
}
|
||||
return plugins.map((p) => ({
|
||||
plugin: p,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -27,20 +27,26 @@ import { normalizePath } from './path';
|
||||
import { dirname, join } from 'path';
|
||||
import { getNxRequirePaths } from './installation-directory';
|
||||
import { readTsConfig } from '../plugins/js/utils/typescript';
|
||||
import { NxJsonConfiguration, PluginConfiguration } from '../config/nx-json';
|
||||
import {
|
||||
NxJsonConfiguration,
|
||||
PluginConfiguration,
|
||||
readNxJson,
|
||||
} from '../config/nx-json';
|
||||
|
||||
import type * as ts from 'typescript';
|
||||
import { retrieveProjectConfigurationsWithoutPluginInference } from '../project-graph/utils/retrieve-workspace-files';
|
||||
import { NxPluginV1 } from './nx-plugin.deprecated';
|
||||
import { RawProjectGraphDependency } from '../project-graph/project-graph-builder';
|
||||
import { combineGlobPatterns } from './globs';
|
||||
import {
|
||||
NxAngularJsonPlugin,
|
||||
shouldMergeAngularProjects,
|
||||
} from '../adapter/angular-json';
|
||||
import { shouldMergeAngularProjects } from '../adapter/angular-json';
|
||||
import { getNxPackageJsonWorkspacesPlugin } from '../../plugins/package-json-workspaces';
|
||||
import { CreateProjectJsonProjectsPlugin } from '../plugins/project-json/build-nodes/project-json';
|
||||
import { CreatePackageJsonProjectsNextToProjectJson } from '../plugins/project-json/build-nodes/package-json-next-to-project-json';
|
||||
import {
|
||||
mergeProjectConfigurationIntoRootMap,
|
||||
readProjectConfigurationsFromRootMap,
|
||||
} from '../project-graph/utils/project-configuration-utils';
|
||||
import { globWithWorkspaceContext } from './workspace-context';
|
||||
import { retrieveProjectConfigurationsWithoutPluginInference } from '../project-graph/utils/retrieve-workspace-files';
|
||||
|
||||
/**
|
||||
* Context for {@link CreateNodesFunction}
|
||||
@ -58,7 +64,19 @@ export type CreateNodesFunction<T = unknown> = (
|
||||
projectConfigurationFile: string,
|
||||
options: T | undefined,
|
||||
context: CreateNodesContext
|
||||
) => {
|
||||
) => CreateNodesResult;
|
||||
|
||||
/**
|
||||
* A function which parses a configuration file into a set of nodes.
|
||||
* Used for creating nodes for the {@link ProjectGraph}
|
||||
*/
|
||||
export type CreateNodesFunctionAsync<T = unknown> = (
|
||||
projectConfigurationFile: string,
|
||||
options: T | undefined,
|
||||
context: CreateNodesContext
|
||||
) => Promise<CreateNodesResult>;
|
||||
|
||||
export interface CreateNodesResult {
|
||||
/**
|
||||
* A map of project root -> project configuration
|
||||
*/
|
||||
@ -68,7 +86,7 @@ export type CreateNodesFunction<T = unknown> = (
|
||||
* A map of external node name -> external node. External nodes do not have a root, so the key is their name.
|
||||
*/
|
||||
externalNodes?: Record<string, ProjectGraphExternalNode>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A pair of file patterns and {@link CreateNodesFunction}
|
||||
@ -78,6 +96,11 @@ export type CreateNodes<T = unknown> = readonly [
|
||||
createNodesFunction: CreateNodesFunction<T>
|
||||
];
|
||||
|
||||
export type CreateNodesAsync<T = unknown> = readonly [
|
||||
projectFilePattern: string,
|
||||
createNodesFunction: CreateNodesFunctionAsync<T>
|
||||
];
|
||||
|
||||
/**
|
||||
* Context for {@link CreateDependencies}
|
||||
*/
|
||||
@ -122,20 +145,25 @@ export type CreateDependencies<T = unknown> = (
|
||||
/**
|
||||
* A plugin for Nx which creates nodes and dependencies for the {@link ProjectGraph}
|
||||
*/
|
||||
export type NxPluginV2<T = unknown> = {
|
||||
export type NxPluginV2<
|
||||
TOptions = unknown,
|
||||
TCreateNodes extends CreateNodes<TOptions> | CreateNodesAsync<TOptions> =
|
||||
| CreateNodes<TOptions>
|
||||
| CreateNodesAsync<TOptions>
|
||||
> = {
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Provides a file pattern and function that retrieves configuration info from
|
||||
* those files. e.g. { '**\/*.csproj': buildProjectsFromCsProjFile }
|
||||
*/
|
||||
createNodes?: CreateNodes<T>;
|
||||
createNodes?: TCreateNodes;
|
||||
|
||||
// Todo(@AgentEnder): This shouldn't be a full processor, since its only responsible for defining edges between projects. What do we want the API to be?
|
||||
/**
|
||||
* Provides a function to analyze files to create dependencies for the {@link ProjectGraph}
|
||||
*/
|
||||
createDependencies?: CreateDependencies<T>;
|
||||
createDependencies?: CreateDependencies<TOptions>;
|
||||
};
|
||||
|
||||
export * from './nx-plugin.deprecated';
|
||||
@ -154,11 +182,12 @@ export type LoadedNxPlugin = {
|
||||
// holding resolved nx plugin objects.
|
||||
// Allows loadNxPlugins to be called multiple times w/o
|
||||
// executing resolution mulitple times.
|
||||
let nxPluginCache: Map<string, LoadedNxPlugin['plugin']> = new Map();
|
||||
export const nxPluginCache: Map<string, LoadedNxPlugin['plugin']> = new Map();
|
||||
|
||||
function getPluginPathAndName(
|
||||
export function getPluginPathAndName(
|
||||
moduleName: string,
|
||||
paths: string[],
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
root: string
|
||||
) {
|
||||
let pluginPath: string;
|
||||
@ -168,7 +197,12 @@ function getPluginPathAndName(
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.code === 'MODULE_NOT_FOUND') {
|
||||
const plugin = resolveLocalNxPlugin(moduleName, root);
|
||||
const plugin = resolveLocalNxPlugin(
|
||||
moduleName,
|
||||
readNxJson(root),
|
||||
projects,
|
||||
root
|
||||
);
|
||||
if (plugin) {
|
||||
const main = readPluginMainFromProjectConfiguration(
|
||||
plugin.projectConfig
|
||||
@ -203,6 +237,7 @@ function getPluginPathAndName(
|
||||
export async function loadNxPluginAsync(
|
||||
pluginConfiguration: PluginConfiguration,
|
||||
paths: string[],
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
root: string
|
||||
): Promise<LoadedNxPlugin> {
|
||||
const { plugin: moduleName, options } =
|
||||
@ -214,7 +249,12 @@ export async function loadNxPluginAsync(
|
||||
return { plugin: pluginModule, options };
|
||||
}
|
||||
|
||||
let { pluginPath, name } = getPluginPathAndName(moduleName, paths, root);
|
||||
let { pluginPath, name } = await getPluginPathAndName(
|
||||
moduleName,
|
||||
paths,
|
||||
projects,
|
||||
root
|
||||
);
|
||||
const plugin = ensurePluginIsV2(
|
||||
(await import(pluginPath)) as LoadedNxPlugin['plugin']
|
||||
);
|
||||
@ -223,79 +263,21 @@ export async function loadNxPluginAsync(
|
||||
return { plugin, options };
|
||||
}
|
||||
|
||||
function loadNxPluginSync(
|
||||
pluginConfiguration: PluginConfiguration,
|
||||
paths: string[],
|
||||
root: string
|
||||
): LoadedNxPlugin {
|
||||
const { plugin: moduleName, options } =
|
||||
typeof pluginConfiguration === 'object'
|
||||
? pluginConfiguration
|
||||
: { plugin: pluginConfiguration, options: undefined };
|
||||
let pluginModule = nxPluginCache.get(moduleName);
|
||||
if (pluginModule) {
|
||||
return { plugin: pluginModule, options };
|
||||
}
|
||||
|
||||
let { pluginPath, name } = getPluginPathAndName(moduleName, paths, root);
|
||||
const plugin = ensurePluginIsV2(
|
||||
require(pluginPath)
|
||||
) as LoadedNxPlugin['plugin'];
|
||||
plugin.name ??= name;
|
||||
nxPluginCache.set(moduleName, plugin);
|
||||
return { plugin, options };
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use loadNxPlugins instead.
|
||||
*/
|
||||
export function loadNxPluginsSync(
|
||||
plugins: NxJsonConfiguration['plugins'],
|
||||
paths = getNxRequirePaths(),
|
||||
root = workspaceRoot
|
||||
): LoadedNxPlugin[] {
|
||||
// TODO: This should be specified in nx.json
|
||||
// Temporarily load js as if it were a plugin which is built into nx
|
||||
// In the future, this will be optional and need to be specified in nx.json
|
||||
const result: LoadedNxPlugin[] = [...getDefaultPluginsSync(root)];
|
||||
|
||||
if (shouldMergeAngularProjects(root, false)) {
|
||||
result.push({ plugin: NxAngularJsonPlugin, options: undefined });
|
||||
}
|
||||
|
||||
plugins ??= [];
|
||||
for (const plugin of plugins) {
|
||||
try {
|
||||
result.push(loadNxPluginSync(plugin, paths, root));
|
||||
} catch (e) {
|
||||
if (e.code === 'ERR_REQUIRE_ESM') {
|
||||
throw new Error(
|
||||
`Unable to load "${plugin}". Plugins cannot be ESM modules. They must be CommonJS modules. Follow the issue on github: https://github.com/nrwl/nx/issues/15682`
|
||||
);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// We push the nx core node plugins onto the end, s.t. it overwrites any other plugins
|
||||
result.push(
|
||||
{ plugin: getNxPackageJsonWorkspacesPlugin(root) },
|
||||
{ plugin: CreateProjectJsonProjectsPlugin }
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function loadNxPlugins(
|
||||
plugins: PluginConfiguration[],
|
||||
paths = getNxRequirePaths(),
|
||||
root = workspaceRoot
|
||||
root = workspaceRoot,
|
||||
projects?: Record<string, ProjectConfiguration>
|
||||
): Promise<LoadedNxPlugin[]> {
|
||||
const result: LoadedNxPlugin[] = [...(await getDefaultPlugins(root))];
|
||||
|
||||
// When loading plugins for `createNodes`, we don't know what projects exist yet.
|
||||
projects ??= await retrieveProjectConfigurationsWithoutPluginInference(root);
|
||||
|
||||
plugins ??= [];
|
||||
|
||||
for (const plugin of plugins) {
|
||||
result.push(await loadNxPluginAsync(plugin, paths, root));
|
||||
result.push(await loadNxPluginAsync(plugin, paths, projects, root));
|
||||
}
|
||||
|
||||
// We push the nx core node plugins onto the end, s.t. it overwrites any other plugins
|
||||
@ -307,7 +289,7 @@ export async function loadNxPlugins(
|
||||
return result;
|
||||
}
|
||||
|
||||
function ensurePluginIsV2(plugin: NxPlugin): NxPluginV2 {
|
||||
export function ensurePluginIsV2(plugin: NxPlugin): NxPluginV2 {
|
||||
if (isNxPluginV2(plugin)) {
|
||||
return plugin;
|
||||
}
|
||||
@ -344,6 +326,7 @@ export function isNxPluginV1(plugin: NxPlugin): plugin is NxPluginV1 {
|
||||
|
||||
export function readPluginPackageJson(
|
||||
pluginName: string,
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
paths = getNxRequirePaths()
|
||||
): {
|
||||
path: string;
|
||||
@ -357,7 +340,12 @@ export function readPluginPackageJson(
|
||||
};
|
||||
} catch (e) {
|
||||
if (e.code === 'MODULE_NOT_FOUND') {
|
||||
const localPluginPath = resolveLocalNxPlugin(pluginName);
|
||||
const nxJson = readNxJson();
|
||||
const localPluginPath = resolveLocalNxPlugin(
|
||||
pluginName,
|
||||
nxJson,
|
||||
projects
|
||||
);
|
||||
if (localPluginPath) {
|
||||
const localPluginPackageJson = path.join(
|
||||
localPluginPath.path,
|
||||
@ -382,15 +370,23 @@ const localPluginCache: Record<
|
||||
string,
|
||||
{ path: string; projectConfig: ProjectConfiguration }
|
||||
> = {};
|
||||
|
||||
export function resolveLocalNxPlugin(
|
||||
importPath: string,
|
||||
nxJsonConfiguration: NxJsonConfiguration,
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
root = workspaceRoot
|
||||
): { path: string; projectConfig: ProjectConfiguration } | null {
|
||||
localPluginCache[importPath] ??= lookupLocalPlugin(importPath, root);
|
||||
localPluginCache[importPath] ??= lookupLocalPlugin(
|
||||
importPath,
|
||||
nxJsonConfiguration,
|
||||
projects,
|
||||
root
|
||||
);
|
||||
return localPluginCache[importPath];
|
||||
}
|
||||
|
||||
let tsNodeAndPathsUnregisterCallback = undefined;
|
||||
let tsNodeAndPathsUnregisterCallback: (() => void) | undefined = undefined;
|
||||
|
||||
/**
|
||||
* Register swc-node or ts-node if they are not currently registered
|
||||
@ -434,8 +430,12 @@ export function unregisterPluginTSTranspiler() {
|
||||
}
|
||||
}
|
||||
|
||||
function lookupLocalPlugin(importPath: string, root = workspaceRoot) {
|
||||
const projects = retrieveProjectConfigurationsWithoutPluginInference(root);
|
||||
function lookupLocalPlugin(
|
||||
importPath: string,
|
||||
nxJsonConfiguration: NxJsonConfiguration,
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
root = workspaceRoot
|
||||
) {
|
||||
const plugin = findNxProjectForImportPath(importPath, projects, root);
|
||||
if (!plugin) {
|
||||
return null;
|
||||
@ -531,15 +531,4 @@ async function getDefaultPlugins(root: string): Promise<LoadedNxPlugin[]> {
|
||||
}));
|
||||
}
|
||||
|
||||
function getDefaultPluginsSync(root: string): LoadedNxPlugin[] {
|
||||
const plugins: NxPluginV2[] = [require('../plugins/js')];
|
||||
|
||||
if (shouldMergeAngularProjects(root, false)) {
|
||||
plugins.push(require('../adapter/angular-json').NxAngularJsonPlugin);
|
||||
}
|
||||
return plugins.map((p) => ({
|
||||
plugin: p,
|
||||
}));
|
||||
}
|
||||
|
||||
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
|
||||
@ -7,8 +7,9 @@ import { readJsonFile } from '../fileutils';
|
||||
import { PackageJson, readModulePackageJson } from '../package-json';
|
||||
import { workspaceRoot } from '../workspace-root';
|
||||
import { join } from 'path';
|
||||
import { NxJsonConfiguration, readNxJson } from '../../config/nx-json';
|
||||
import { readNxJson } from '../../config/nx-json';
|
||||
import { getNxRequirePaths } from '../installation-directory';
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
|
||||
export function findInstalledPlugins(): PackageJson[] {
|
||||
const packageJsonDeps = getDependenciesFromPackageJson();
|
||||
@ -64,14 +65,19 @@ function getDependenciesFromNxJson(): string[] {
|
||||
}
|
||||
|
||||
export async function getInstalledPluginsAndCapabilities(
|
||||
workspaceRoot: string
|
||||
workspaceRoot: string,
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
): Promise<Map<string, PluginCapabilities>> {
|
||||
const plugins = findInstalledPlugins().map((p) => p.name);
|
||||
|
||||
const result = new Map<string, PluginCapabilities>();
|
||||
for (const plugin of Array.from(plugins).sort()) {
|
||||
try {
|
||||
const capabilities = await getPluginCapabilities(workspaceRoot, plugin);
|
||||
const capabilities = await getPluginCapabilities(
|
||||
workspaceRoot,
|
||||
plugin,
|
||||
projects
|
||||
);
|
||||
if (
|
||||
capabilities &&
|
||||
(capabilities.executors ||
|
||||
|
||||
@ -26,6 +26,7 @@ export async function getLocalWorkspacePlugins(
|
||||
const capabilities = await getPluginCapabilities(
|
||||
workspaceRoot,
|
||||
packageJson.name,
|
||||
projectsConfiguration.projects,
|
||||
includeRuntimeCapabilities
|
||||
);
|
||||
if (
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
} from '../nx-plugin';
|
||||
import { getNxRequirePaths } from '../installation-directory';
|
||||
import { PackageJson } from '../package-json';
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
|
||||
function tryGetCollection<T extends object>(
|
||||
packageJsonPath: string,
|
||||
@ -34,15 +35,18 @@ function tryGetCollection<T extends object>(
|
||||
export async function getPluginCapabilities(
|
||||
workspaceRoot: string,
|
||||
pluginName: string,
|
||||
projects: Record<string, ProjectConfiguration>,
|
||||
includeRuntimeCapabilities = false
|
||||
): Promise<PluginCapabilities | null> {
|
||||
try {
|
||||
const { json: packageJson, path: packageJsonPath } = readPluginPackageJson(
|
||||
pluginName,
|
||||
getNxRequirePaths(workspaceRoot)
|
||||
);
|
||||
const { json: packageJson, path: packageJsonPath } =
|
||||
await readPluginPackageJson(
|
||||
pluginName,
|
||||
projects,
|
||||
getNxRequirePaths(workspaceRoot)
|
||||
);
|
||||
const pluginModule = includeRuntimeCapabilities
|
||||
? await tryGetModule(packageJson, workspaceRoot)
|
||||
? await tryGetModule(packageJson, workspaceRoot, projects)
|
||||
: ({} as Record<string, unknown>);
|
||||
return {
|
||||
name: pluginName,
|
||||
@ -95,7 +99,8 @@ export async function getPluginCapabilities(
|
||||
|
||||
async function tryGetModule(
|
||||
packageJson: PackageJson,
|
||||
workspaceRoot: string
|
||||
workspaceRoot: string,
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
): Promise<NxPlugin | null> {
|
||||
try {
|
||||
return packageJson.generators ??
|
||||
@ -107,6 +112,7 @@ async function tryGetModule(
|
||||
await loadNxPluginAsync(
|
||||
packageJson.name,
|
||||
getNxRequirePaths(workspaceRoot),
|
||||
projects,
|
||||
workspaceRoot
|
||||
)
|
||||
).plugin
|
||||
@ -118,8 +124,15 @@ async function tryGetModule(
|
||||
}
|
||||
}
|
||||
|
||||
export async function listPluginCapabilities(pluginName: string) {
|
||||
const plugin = await getPluginCapabilities(workspaceRoot, pluginName);
|
||||
export async function listPluginCapabilities(
|
||||
pluginName: string,
|
||||
projects: Record<string, ProjectConfiguration>
|
||||
) {
|
||||
const plugin = await getPluginCapabilities(
|
||||
workspaceRoot,
|
||||
pluginName,
|
||||
projects
|
||||
);
|
||||
|
||||
if (!plugin) {
|
||||
const pmc = getPackageManagerCommand();
|
||||
|
||||
@ -19,7 +19,7 @@ export function setupWorkspaceContext(workspaceRoot: string) {
|
||||
export function getNxWorkspaceFilesFromContext(
|
||||
workspaceRoot: string,
|
||||
globs: string[],
|
||||
parseConfigurations: (files: string[]) => Record<string, string>
|
||||
parseConfigurations: (files: string[]) => Promise<Record<string, string>>
|
||||
) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.getWorkspaceFiles(globs, parseConfigurations);
|
||||
@ -36,7 +36,7 @@ export function globWithWorkspaceContext(
|
||||
export function getProjectConfigurationsFromContext(
|
||||
workspaceRoot: string,
|
||||
globs: string[],
|
||||
parseConfigurations: (files: string[]) => Record<string, string>
|
||||
parseConfigurations: (files: string[]) => Promise<Record<string, string>>
|
||||
) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.getProjectConfigurations(globs, parseConfigurations);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user