chore(core): copy isNxExecutor out of Workspaces class (#17996)
This commit is contained in:
parent
6ccbbbc98f
commit
1c6a359130
@ -1,13 +1,9 @@
|
|||||||
import type { Schema } from './schema';
|
import type { Schema } from './schema';
|
||||||
import {
|
import { logger, readCachedProjectGraph, workspaceRoot } from '@nx/devkit';
|
||||||
logger,
|
|
||||||
readCachedProjectGraph,
|
|
||||||
workspaceRoot,
|
|
||||||
Workspaces,
|
|
||||||
} from '@nx/devkit';
|
|
||||||
import { scheduleTarget } from 'nx/src/adapter/ngcli-adapter';
|
import { scheduleTarget } from 'nx/src/adapter/ngcli-adapter';
|
||||||
import { executeWebpackDevServerBuilder } from '../webpack-dev-server/webpack-dev-server.impl';
|
import { executeWebpackDevServerBuilder } from '../webpack-dev-server/webpack-dev-server.impl';
|
||||||
import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph';
|
import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph';
|
||||||
|
import { getExecutorInformation } from 'nx/src/command-line/run/executor-utils';
|
||||||
import {
|
import {
|
||||||
getDynamicRemotes,
|
getDynamicRemotes,
|
||||||
getStaticRemotes,
|
getStaticRemotes,
|
||||||
@ -25,7 +21,6 @@ export function executeModuleFederationDevServerBuilder(
|
|||||||
const projectGraph = readCachedProjectGraph();
|
const projectGraph = readCachedProjectGraph();
|
||||||
const { projects: workspaceProjects } =
|
const { projects: workspaceProjects } =
|
||||||
readProjectsConfigurationFromProjectGraph(projectGraph);
|
readProjectsConfigurationFromProjectGraph(projectGraph);
|
||||||
const ws = new Workspaces(workspaceRoot);
|
|
||||||
const project = workspaceProjects[context.target.project];
|
const project = workspaceProjects[context.target.project];
|
||||||
|
|
||||||
let pathToManifestFile = join(
|
let pathToManifestFile = join(
|
||||||
@ -101,7 +96,11 @@ export function executeModuleFederationDevServerBuilder(
|
|||||||
if (options.verbose) {
|
if (options.verbose) {
|
||||||
const [collection, executor] =
|
const [collection, executor] =
|
||||||
workspaceProjects[remote].targets[target].executor.split(':');
|
workspaceProjects[remote].targets[target].executor.split(':');
|
||||||
const { schema } = ws.readExecutor(collection, executor);
|
const { schema } = getExecutorInformation(
|
||||||
|
collection,
|
||||||
|
executor,
|
||||||
|
workspaceRoot
|
||||||
|
);
|
||||||
|
|
||||||
if (schema.additionalProperties || 'verbose' in schema.properties) {
|
if (schema.additionalProperties || 'verbose' in schema.properties) {
|
||||||
runOptions.verbose = options.verbose;
|
runOptions.verbose = options.verbose;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { Schema } from './schema';
|
import type { Schema } from './schema';
|
||||||
import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph';
|
import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph';
|
||||||
import { readCachedProjectGraph, workspaceRoot, Workspaces } from '@nx/devkit';
|
import { getExecutorInformation } from 'nx/src/command-line/run/executor-utils';
|
||||||
|
import { readCachedProjectGraph, workspaceRoot } from '@nx/devkit';
|
||||||
import {
|
import {
|
||||||
getDynamicRemotes,
|
getDynamicRemotes,
|
||||||
getStaticRemotes,
|
getStaticRemotes,
|
||||||
@ -21,7 +22,6 @@ export function executeModuleFederationDevSSRBuilder(
|
|||||||
const projectGraph = readCachedProjectGraph();
|
const projectGraph = readCachedProjectGraph();
|
||||||
const { projects: workspaceProjects } =
|
const { projects: workspaceProjects } =
|
||||||
readProjectsConfigurationFromProjectGraph(projectGraph);
|
readProjectsConfigurationFromProjectGraph(projectGraph);
|
||||||
const ws = new Workspaces(workspaceRoot);
|
|
||||||
const project = workspaceProjects[context.target.project];
|
const project = workspaceProjects[context.target.project];
|
||||||
|
|
||||||
let pathToManifestFile = join(
|
let pathToManifestFile = join(
|
||||||
@ -90,7 +90,11 @@ export function executeModuleFederationDevSSRBuilder(
|
|||||||
if (options.verbose) {
|
if (options.verbose) {
|
||||||
const [collection, executor] =
|
const [collection, executor] =
|
||||||
workspaceProjects[remote].targets[target].executor.split(':');
|
workspaceProjects[remote].targets[target].executor.split(':');
|
||||||
const { schema } = ws.readExecutor(collection, executor);
|
const { schema } = getExecutorInformation(
|
||||||
|
collection,
|
||||||
|
executor,
|
||||||
|
workspaceRoot
|
||||||
|
);
|
||||||
|
|
||||||
if (schema.additionalProperties || 'verbose' in schema.properties) {
|
if (schema.additionalProperties || 'verbose' in schema.properties) {
|
||||||
runOptions.verbose = options.verbose;
|
runOptions.verbose = options.verbose;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { getTempTailwindPath } from '../../utils/ct-helpers';
|
import { getTempTailwindPath } from '../../utils/ct-helpers';
|
||||||
import { ExecutorContext, stripIndents } from '@nx/devkit';
|
import { ExecutorContext, stripIndents } from '@nx/devkit';
|
||||||
|
import * as executorUtils from 'nx/src/command-line/run/executor-utils';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||||
import cypressExecutor, { CypressExecutorOptions } from './cypress.impl';
|
import cypressExecutor, { CypressExecutorOptions } from './cypress.impl';
|
||||||
@ -43,9 +44,17 @@ describe('Cypress builder', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as any;
|
} as any;
|
||||||
(devkit as any).readTargetOptions = jest.fn().mockReturnValue({
|
jest.spyOn(devkit, 'readTargetOptions').mockReturnValue({
|
||||||
watch: true,
|
watch: true,
|
||||||
});
|
});
|
||||||
|
jest.spyOn(executorUtils, 'getExecutorInformation').mockReturnValue({
|
||||||
|
schema: { properties: {} },
|
||||||
|
hasherFactory: jest.fn(),
|
||||||
|
implementationFactory: jest.fn(),
|
||||||
|
batchImplementationFactory: jest.fn(),
|
||||||
|
isNgCompat: true,
|
||||||
|
isNxExecutor: true,
|
||||||
|
});
|
||||||
let runExecutor: any;
|
let runExecutor: any;
|
||||||
let mockGetTailwindPath: jest.Mock<ReturnType<typeof getTempTailwindPath>> =
|
let mockGetTailwindPath: jest.Mock<ReturnType<typeof getTempTailwindPath>> =
|
||||||
getTempTailwindPath as any;
|
getTempTailwindPath as any;
|
||||||
@ -57,9 +66,6 @@ describe('Cypress builder', () => {
|
|||||||
baseUrl: 'http://localhost:4200',
|
baseUrl: 'http://localhost:4200',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
(devkit as any).Workspaces = jest.fn().mockReturnValue({
|
|
||||||
readExecutor: () => ({ schema: { properties: {} } }),
|
|
||||||
});
|
|
||||||
(devkit as any).stripIndents = (s) => s;
|
(devkit as any).stripIndents = (s) => s;
|
||||||
(devkit as any).parseTargetString = (s) => {
|
(devkit as any).parseTargetString = (s) => {
|
||||||
const [project, target, configuration] = s.split(':');
|
const [project, target, configuration] = s.split(':');
|
||||||
|
|||||||
@ -5,11 +5,11 @@ import {
|
|||||||
readTargetOptions,
|
readTargetOptions,
|
||||||
runExecutor,
|
runExecutor,
|
||||||
stripIndents,
|
stripIndents,
|
||||||
Workspaces,
|
|
||||||
Target,
|
Target,
|
||||||
targetToTargetString,
|
targetToTargetString,
|
||||||
output,
|
output,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
|
import { getExecutorInformation } from 'nx/src/command-line/run/executor-utils';
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import { existsSync, readdirSync, unlinkSync, writeFileSync } from 'fs';
|
import { existsSync, readdirSync, unlinkSync, writeFileSync } from 'fs';
|
||||||
import { basename, dirname, join } from 'path';
|
import { basename, dirname, join } from 'path';
|
||||||
@ -421,9 +421,12 @@ ${e.message || e}`);
|
|||||||
context.projectsConfigurations?.projects?.[target.project];
|
context.projectsConfigurations?.projects?.[target.project];
|
||||||
const targetConfig = projectConfig.targets[target.target];
|
const targetConfig = projectConfig.targets[target.target];
|
||||||
|
|
||||||
const workspace = new Workspaces(context.root);
|
|
||||||
const [collection, executor] = targetConfig.executor.split(':');
|
const [collection, executor] = targetConfig.executor.split(':');
|
||||||
const { schema } = workspace.readExecutor(collection, executor);
|
const { schema } = getExecutorInformation(
|
||||||
|
collection,
|
||||||
|
executor,
|
||||||
|
context.root
|
||||||
|
);
|
||||||
|
|
||||||
// NOTE: schema won't have a default since readTargetOptions would have
|
// NOTE: schema won't have a default since readTargetOptions would have
|
||||||
// already set that and this check wouldn't need to be made
|
// already set that and this check wouldn't need to be made
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import type { ExecutorContext } from 'nx/src/config/misc-interfaces';
|
|||||||
import { combineOptionsForExecutor } from 'nx/src/utils/params';
|
import { combineOptionsForExecutor } from 'nx/src/utils/params';
|
||||||
import { requireNx } from '../../nx';
|
import { requireNx } from '../../nx';
|
||||||
|
|
||||||
const { Workspaces } = requireNx();
|
const { Workspaces, getExecutorInformation } = requireNx();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads and combines options for a given target.
|
* Reads and combines options for a given target.
|
||||||
@ -22,7 +22,11 @@ export function readTargetOptions<T = any>(
|
|||||||
|
|
||||||
const ws = new Workspaces(context.root);
|
const ws = new Workspaces(context.root);
|
||||||
const [nodeModule, executorName] = targetConfiguration.executor.split(':');
|
const [nodeModule, executorName] = targetConfiguration.executor.split(':');
|
||||||
const { schema } = ws.readExecutor(nodeModule, executorName);
|
const { schema } = getExecutorInformation(
|
||||||
|
nodeModule,
|
||||||
|
executorName,
|
||||||
|
context.root
|
||||||
|
);
|
||||||
|
|
||||||
const defaultProject = ws.calculateDefaultProjectName(
|
const defaultProject = ws.calculateDefaultProjectName(
|
||||||
context.cwd,
|
context.cwd,
|
||||||
|
|||||||
@ -46,7 +46,8 @@ import {
|
|||||||
toNewFormat,
|
toNewFormat,
|
||||||
toOldFormat,
|
toOldFormat,
|
||||||
} from './angular-json';
|
} from './angular-json';
|
||||||
import { normalizeExecutorSchema, Workspaces } from '../config/workspaces';
|
import { normalizeExecutorSchema } from '../command-line/run/executor-utils';
|
||||||
|
import { Workspaces } from '../config/workspaces';
|
||||||
import {
|
import {
|
||||||
CustomHasher,
|
CustomHasher,
|
||||||
Executor,
|
Executor,
|
||||||
@ -55,6 +56,11 @@ import {
|
|||||||
TaskGraphExecutor,
|
TaskGraphExecutor,
|
||||||
} from '../config/misc-interfaces';
|
} from '../config/misc-interfaces';
|
||||||
import { readPluginPackageJson } from '../utils/nx-plugin';
|
import { readPluginPackageJson } from '../utils/nx-plugin';
|
||||||
|
import {
|
||||||
|
getImplementationFactory,
|
||||||
|
resolveImplementation,
|
||||||
|
resolveSchema,
|
||||||
|
} from '../config/schema-utils';
|
||||||
|
|
||||||
export async function scheduleTarget(
|
export async function scheduleTarget(
|
||||||
root: string,
|
root: string,
|
||||||
@ -106,7 +112,7 @@ export async function scheduleTarget(
|
|||||||
readJsonFile<ExecutorsJson>(executorsFilePath).builders[builderName]
|
readJsonFile<ExecutorsJson>(executorsFilePath).builders[builderName]
|
||||||
.description,
|
.description,
|
||||||
optionSchema: builderInfo.schema,
|
optionSchema: builderInfo.schema,
|
||||||
import: this.workspaces['resolveImplementation'].bind(this.workspaces)(
|
import: resolveImplementation(
|
||||||
executorConfig.implementation,
|
executorConfig.implementation,
|
||||||
dirname(executorsFilePath)
|
dirname(executorsFilePath)
|
||||||
),
|
),
|
||||||
@ -153,9 +159,7 @@ export async function scheduleTarget(
|
|||||||
const { executorsFilePath, executorConfig, isNgCompat } =
|
const { executorsFilePath, executorConfig, isNgCompat } =
|
||||||
this.readExecutorsJson(nodeModule, executor);
|
this.readExecutorsJson(nodeModule, executor);
|
||||||
const executorsDir = dirname(executorsFilePath);
|
const executorsDir = dirname(executorsFilePath);
|
||||||
const schemaPath = this.workspaces['resolveSchema'].bind(
|
const schemaPath = resolveSchema(executorConfig.schema, executorsDir);
|
||||||
this.workspaces
|
|
||||||
)(executorConfig.schema, executorsDir);
|
|
||||||
const schema = normalizeExecutorSchema(readJsonFile(schemaPath));
|
const schema = normalizeExecutorSchema(readJsonFile(schemaPath));
|
||||||
|
|
||||||
const implementationFactory = this.getImplementationFactory<Executor>(
|
const implementationFactory = this.getImplementationFactory<Executor>(
|
||||||
@ -195,10 +199,7 @@ export async function scheduleTarget(
|
|||||||
implementation: string,
|
implementation: string,
|
||||||
executorsDir: string
|
executorsDir: string
|
||||||
): () => T {
|
): () => T {
|
||||||
return this.workspaces['getImplementationFactory'].bind(this.workspaces)(
|
return getImplementationFactory(implementation, executorsDir);
|
||||||
implementation,
|
|
||||||
executorsDir
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
128
packages/nx/src/command-line/run/executor-utils.ts
Normal file
128
packages/nx/src/command-line/run/executor-utils.ts
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import { dirname, join } from 'path';
|
||||||
|
|
||||||
|
import { readPluginPackageJson } from '../../utils/nx-plugin';
|
||||||
|
import {
|
||||||
|
CustomHasher,
|
||||||
|
Executor,
|
||||||
|
ExecutorConfig,
|
||||||
|
ExecutorsJson,
|
||||||
|
TaskGraphExecutor,
|
||||||
|
} from '../../config/misc-interfaces';
|
||||||
|
import { readJsonFile } from '../../utils/fileutils';
|
||||||
|
import {
|
||||||
|
getImplementationFactory,
|
||||||
|
resolveSchema,
|
||||||
|
} from '../../config/schema-utils';
|
||||||
|
import { getNxRequirePaths } from '../../utils/installation-directory';
|
||||||
|
|
||||||
|
export function normalizeExecutorSchema(
|
||||||
|
schema: Partial<ExecutorConfig['schema']>
|
||||||
|
): ExecutorConfig['schema'] {
|
||||||
|
const version = (schema.version ??= 1);
|
||||||
|
return {
|
||||||
|
version,
|
||||||
|
outputCapture:
|
||||||
|
schema.outputCapture ?? version < 2 ? 'direct-nodejs' : 'pipe',
|
||||||
|
properties:
|
||||||
|
!schema.properties || typeof schema.properties !== 'object'
|
||||||
|
? {}
|
||||||
|
: schema.properties,
|
||||||
|
...schema,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getExecutorInformation(
|
||||||
|
nodeModule: string,
|
||||||
|
executor: string,
|
||||||
|
root: string
|
||||||
|
): ExecutorConfig & { isNgCompat: boolean; isNxExecutor: boolean } {
|
||||||
|
try {
|
||||||
|
const { executorsFilePath, executorConfig, isNgCompat } = readExecutorsJson(
|
||||||
|
nodeModule,
|
||||||
|
executor,
|
||||||
|
root
|
||||||
|
);
|
||||||
|
const executorsDir = dirname(executorsFilePath);
|
||||||
|
const schemaPath = resolveSchema(executorConfig.schema, executorsDir);
|
||||||
|
const schema = normalizeExecutorSchema(readJsonFile(schemaPath));
|
||||||
|
|
||||||
|
const implementationFactory = getImplementationFactory<Executor>(
|
||||||
|
executorConfig.implementation,
|
||||||
|
executorsDir
|
||||||
|
);
|
||||||
|
|
||||||
|
const batchImplementationFactory = executorConfig.batchImplementation
|
||||||
|
? getImplementationFactory<TaskGraphExecutor>(
|
||||||
|
executorConfig.batchImplementation,
|
||||||
|
executorsDir
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const hasherFactory = executorConfig.hasher
|
||||||
|
? getImplementationFactory<CustomHasher>(
|
||||||
|
executorConfig.hasher,
|
||||||
|
executorsDir
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
schema,
|
||||||
|
implementationFactory,
|
||||||
|
batchImplementationFactory,
|
||||||
|
hasherFactory,
|
||||||
|
isNgCompat,
|
||||||
|
isNxExecutor: !isNgCompat,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to resolve ${nodeModule}:${executor}.\n${e.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readExecutorsJson(
|
||||||
|
nodeModule: string,
|
||||||
|
executor: string,
|
||||||
|
root: string
|
||||||
|
): {
|
||||||
|
executorsFilePath: string;
|
||||||
|
executorConfig: {
|
||||||
|
implementation: string;
|
||||||
|
batchImplementation?: string;
|
||||||
|
schema: string;
|
||||||
|
hasher?: string;
|
||||||
|
};
|
||||||
|
isNgCompat: boolean;
|
||||||
|
} {
|
||||||
|
const { json: packageJson, path: packageJsonPath } = readPluginPackageJson(
|
||||||
|
nodeModule,
|
||||||
|
root
|
||||||
|
? [root, __dirname, process.cwd(), ...getNxRequirePaths()]
|
||||||
|
: [__dirname, process.cwd(), ...getNxRequirePaths()]
|
||||||
|
);
|
||||||
|
const executorsFile = packageJson.executors ?? packageJson.builders;
|
||||||
|
|
||||||
|
if (!executorsFile) {
|
||||||
|
throw new Error(
|
||||||
|
`The "${nodeModule}" package does not support Nx executors.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const executorsFilePath = require.resolve(
|
||||||
|
join(dirname(packageJsonPath), executorsFile)
|
||||||
|
);
|
||||||
|
const executorsJson = readJsonFile<ExecutorsJson>(executorsFilePath);
|
||||||
|
const executorConfig: {
|
||||||
|
implementation: string;
|
||||||
|
batchImplementation?: string;
|
||||||
|
schema: string;
|
||||||
|
hasher?: string;
|
||||||
|
} = executorsJson.executors?.[executor] || executorsJson.builders?.[executor];
|
||||||
|
if (!executorConfig) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot find executor '${executor}' in ${executorsFilePath}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const isNgCompat = !executorsJson.executors?.[executor];
|
||||||
|
return { executorsFilePath, executorConfig, isNgCompat };
|
||||||
|
}
|
||||||
@ -32,6 +32,7 @@ import {
|
|||||||
getLastValueFromAsyncIterableIterator,
|
getLastValueFromAsyncIterableIterator,
|
||||||
isAsyncIterator,
|
isAsyncIterator,
|
||||||
} from '../../utils/async-iterator';
|
} from '../../utils/async-iterator';
|
||||||
|
import { getExecutorInformation } from './executor-utils';
|
||||||
|
|
||||||
export interface Target {
|
export interface Target {
|
||||||
project: string;
|
project: string;
|
||||||
@ -131,9 +132,10 @@ async function parseExecutorAndTarget(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const [nodeModule, executor] = targetConfig.executor.split(':');
|
const [nodeModule, executor] = targetConfig.executor.split(':');
|
||||||
const { schema, implementationFactory } = ws.readExecutor(
|
const { schema, implementationFactory } = getExecutorInformation(
|
||||||
nodeModule,
|
nodeModule,
|
||||||
executor
|
executor,
|
||||||
|
root
|
||||||
);
|
);
|
||||||
|
|
||||||
return { executor, implementationFactory, nodeModule, schema, targetConfig };
|
return { executor, implementationFactory, nodeModule, schema, targetConfig };
|
||||||
@ -195,7 +197,7 @@ async function runExecutorInternal<T extends { success: boolean }>(
|
|||||||
isVerbose
|
isVerbose
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ws.isNxExecutor(nodeModule, executor)) {
|
if (getExecutorInformation(nodeModule, executor, root).isNxExecutor) {
|
||||||
const implementation = implementationFactory() as Executor<any>;
|
const implementation = implementationFactory() as Executor<any>;
|
||||||
const r = implementation(combinedOptions, {
|
const r = implementation(combinedOptions, {
|
||||||
root,
|
root,
|
||||||
|
|||||||
73
packages/nx/src/config/schema-utils.ts
Normal file
73
packages/nx/src/config/schema-utils.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { extname, join } from 'path';
|
||||||
|
import { registerPluginTSTranspiler } from '../utils/nx-plugin';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to get the implementation factory of an executor or generator.
|
||||||
|
* @param implementation path to the implementation
|
||||||
|
* @param directory path to the directory
|
||||||
|
* @returns a function that returns the implementation
|
||||||
|
*/
|
||||||
|
export function getImplementationFactory<T>(
|
||||||
|
implementation: string,
|
||||||
|
directory: string
|
||||||
|
): () => T {
|
||||||
|
const [implementationModulePath, implementationExportName] =
|
||||||
|
implementation.split('#');
|
||||||
|
return () => {
|
||||||
|
const modulePath = resolveImplementation(
|
||||||
|
implementationModulePath,
|
||||||
|
directory
|
||||||
|
);
|
||||||
|
if (extname(modulePath) === '.ts') {
|
||||||
|
registerPluginTSTranspiler();
|
||||||
|
}
|
||||||
|
const module = require(modulePath);
|
||||||
|
return implementationExportName
|
||||||
|
? module[implementationExportName]
|
||||||
|
: module.default ?? module;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is used to resolve the implementation of an executor or generator.
|
||||||
|
* @param implementationModulePath
|
||||||
|
* @param directory
|
||||||
|
* @returns path to the implementation
|
||||||
|
*/
|
||||||
|
export function resolveImplementation(
|
||||||
|
implementationModulePath: string,
|
||||||
|
directory: string
|
||||||
|
): string {
|
||||||
|
const validImplementations = ['', '.js', '.ts'].map(
|
||||||
|
(x) => implementationModulePath + x
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const maybeImplementation of validImplementations) {
|
||||||
|
const maybeImplementationPath = join(directory, maybeImplementation);
|
||||||
|
if (existsSync(maybeImplementationPath)) {
|
||||||
|
return maybeImplementationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return require.resolve(maybeImplementation, {
|
||||||
|
paths: [directory],
|
||||||
|
});
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Could not resolve "${implementationModulePath}" from "${directory}".`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveSchema(schemaPath: string, directory: string): string {
|
||||||
|
const maybeSchemaPath = join(directory, schemaPath);
|
||||||
|
if (existsSync(maybeSchemaPath)) {
|
||||||
|
return maybeSchemaPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return require.resolve(schemaPath, {
|
||||||
|
paths: [directory],
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { sync as globSync } from 'fast-glob';
|
import { sync as globSync } from 'fast-glob';
|
||||||
import { existsSync, readFileSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { basename, dirname, extname, join } from 'path';
|
import { basename, dirname, join } from 'path';
|
||||||
import { performance } from 'perf_hooks';
|
import { performance } from 'perf_hooks';
|
||||||
import { workspaceRoot } from '../utils/workspace-root';
|
import { workspaceRoot } from '../utils/workspace-root';
|
||||||
import { readJsonFile, readYamlFile } from '../utils/fileutils';
|
import { readJsonFile, readYamlFile } from '../utils/fileutils';
|
||||||
@ -10,7 +10,6 @@ import {
|
|||||||
loadNxPlugins,
|
loadNxPlugins,
|
||||||
loadNxPluginsSync,
|
loadNxPluginsSync,
|
||||||
readPluginPackageJson,
|
readPluginPackageJson,
|
||||||
registerPluginTSTranspiler,
|
|
||||||
} from '../utils/nx-plugin';
|
} from '../utils/nx-plugin';
|
||||||
|
|
||||||
import type { NxJsonConfiguration, TargetDefaults } from './nx-json';
|
import type { NxJsonConfiguration, TargetDefaults } from './nx-json';
|
||||||
@ -20,14 +19,9 @@ import {
|
|||||||
TargetConfiguration,
|
TargetConfiguration,
|
||||||
} from './workspace-json-project-json';
|
} from './workspace-json-project-json';
|
||||||
import {
|
import {
|
||||||
CustomHasher,
|
|
||||||
Executor,
|
|
||||||
ExecutorConfig,
|
|
||||||
ExecutorsJson,
|
|
||||||
Generator,
|
Generator,
|
||||||
GeneratorsJson,
|
GeneratorsJson,
|
||||||
GeneratorsJsonEntry,
|
GeneratorsJsonEntry,
|
||||||
TaskGraphExecutor,
|
|
||||||
} from './misc-interfaces';
|
} from './misc-interfaces';
|
||||||
import { PackageJson } from '../utils/package-json';
|
import { PackageJson } from '../utils/package-json';
|
||||||
import { output } from '../utils/output';
|
import { output } from '../utils/output';
|
||||||
@ -42,6 +36,7 @@ import {
|
|||||||
findProjectForPath,
|
findProjectForPath,
|
||||||
normalizeProjectRoot,
|
normalizeProjectRoot,
|
||||||
} from '../project-graph/utils/find-project-for-path';
|
} from '../project-graph/utils/find-project-for-path';
|
||||||
|
import { getImplementationFactory, resolveSchema } from './schema-utils';
|
||||||
|
|
||||||
export class Workspaces {
|
export class Workspaces {
|
||||||
private cachedProjectsConfig: ProjectsConfigurations;
|
private cachedProjectsConfig: ProjectsConfigurations;
|
||||||
@ -169,61 +164,10 @@ export class Workspaces {
|
|||||||
return projects;
|
return projects;
|
||||||
}
|
}
|
||||||
|
|
||||||
isNxExecutor(nodeModule: string, executor: string) {
|
|
||||||
return !this.readExecutor(nodeModule, executor).isNgCompat;
|
|
||||||
}
|
|
||||||
|
|
||||||
isNxGenerator(collectionName: string, generatorName: string) {
|
isNxGenerator(collectionName: string, generatorName: string) {
|
||||||
return !this.readGenerator(collectionName, generatorName).isNgCompat;
|
return !this.readGenerator(collectionName, generatorName).isNgCompat;
|
||||||
}
|
}
|
||||||
|
|
||||||
readExecutor(
|
|
||||||
nodeModule: string,
|
|
||||||
executor: string
|
|
||||||
): ExecutorConfig & { isNgCompat: boolean } {
|
|
||||||
try {
|
|
||||||
const { executorsFilePath, executorConfig, isNgCompat } =
|
|
||||||
this.readExecutorsJson(nodeModule, executor);
|
|
||||||
const executorsDir = path.dirname(executorsFilePath);
|
|
||||||
const schemaPath = this.resolveSchema(
|
|
||||||
executorConfig.schema,
|
|
||||||
executorsDir
|
|
||||||
);
|
|
||||||
const schema = normalizeExecutorSchema(readJsonFile(schemaPath));
|
|
||||||
|
|
||||||
const implementationFactory = this.getImplementationFactory<Executor>(
|
|
||||||
executorConfig.implementation,
|
|
||||||
executorsDir
|
|
||||||
);
|
|
||||||
|
|
||||||
const batchImplementationFactory = executorConfig.batchImplementation
|
|
||||||
? this.getImplementationFactory<TaskGraphExecutor>(
|
|
||||||
executorConfig.batchImplementation,
|
|
||||||
executorsDir
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const hasherFactory = executorConfig.hasher
|
|
||||||
? this.getImplementationFactory<CustomHasher>(
|
|
||||||
executorConfig.hasher,
|
|
||||||
executorsDir
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
schema,
|
|
||||||
implementationFactory,
|
|
||||||
batchImplementationFactory,
|
|
||||||
hasherFactory,
|
|
||||||
isNgCompat,
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(
|
|
||||||
`Unable to resolve ${nodeModule}:${executor}.\n${e.message}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readGenerator(
|
readGenerator(
|
||||||
collectionName: string,
|
collectionName: string,
|
||||||
generatorName: string
|
generatorName: string
|
||||||
@ -251,17 +195,14 @@ export class Workspaces {
|
|||||||
generatorsJson.generators?.[normalizedGeneratorName] ||
|
generatorsJson.generators?.[normalizedGeneratorName] ||
|
||||||
generatorsJson.schematics?.[normalizedGeneratorName];
|
generatorsJson.schematics?.[normalizedGeneratorName];
|
||||||
const isNgCompat = !generatorsJson.generators?.[normalizedGeneratorName];
|
const isNgCompat = !generatorsJson.generators?.[normalizedGeneratorName];
|
||||||
const schemaPath = this.resolveSchema(
|
const schemaPath = resolveSchema(generatorConfig.schema, generatorsDir);
|
||||||
generatorConfig.schema,
|
|
||||||
generatorsDir
|
|
||||||
);
|
|
||||||
const schema = readJsonFile(schemaPath);
|
const schema = readJsonFile(schemaPath);
|
||||||
if (!schema.properties || typeof schema.properties !== 'object') {
|
if (!schema.properties || typeof schema.properties !== 'object') {
|
||||||
schema.properties = {};
|
schema.properties = {};
|
||||||
}
|
}
|
||||||
generatorConfig.implementation =
|
generatorConfig.implementation =
|
||||||
generatorConfig.implementation || generatorConfig.factory;
|
generatorConfig.implementation || generatorConfig.factory;
|
||||||
const implementationFactory = this.getImplementationFactory<Generator>(
|
const implementationFactory = getImplementationFactory<Generator>(
|
||||||
generatorConfig.implementation,
|
generatorConfig.implementation,
|
||||||
generatorsDir
|
generatorsDir
|
||||||
);
|
);
|
||||||
@ -322,97 +263,6 @@ export class Workspaces {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getImplementationFactory<T>(
|
|
||||||
implementation: string,
|
|
||||||
directory: string
|
|
||||||
): () => T {
|
|
||||||
const [implementationModulePath, implementationExportName] =
|
|
||||||
implementation.split('#');
|
|
||||||
return () => {
|
|
||||||
const modulePath = this.resolveImplementation(
|
|
||||||
implementationModulePath,
|
|
||||||
directory
|
|
||||||
);
|
|
||||||
if (extname(modulePath) === '.ts') {
|
|
||||||
registerPluginTSTranspiler();
|
|
||||||
}
|
|
||||||
const module = require(modulePath);
|
|
||||||
return implementationExportName
|
|
||||||
? module[implementationExportName]
|
|
||||||
: module.default ?? module;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private resolveSchema(schemaPath: string, directory: string): string {
|
|
||||||
const maybeSchemaPath = join(directory, schemaPath);
|
|
||||||
if (existsSync(maybeSchemaPath)) {
|
|
||||||
return maybeSchemaPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return require.resolve(schemaPath, {
|
|
||||||
paths: [directory],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private resolveImplementation(
|
|
||||||
implementationModulePath: string,
|
|
||||||
directory: string
|
|
||||||
): string {
|
|
||||||
const validImplementations = ['', '.js', '.ts'].map(
|
|
||||||
(x) => implementationModulePath + x
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const maybeImplementation of validImplementations) {
|
|
||||||
const maybeImplementationPath = join(directory, maybeImplementation);
|
|
||||||
if (existsSync(maybeImplementationPath)) {
|
|
||||||
return maybeImplementationPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return require.resolve(maybeImplementation, {
|
|
||||||
paths: [directory],
|
|
||||||
});
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`Could not resolve "${implementationModulePath}" from "${directory}".`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readExecutorsJson(nodeModule: string, executor: string) {
|
|
||||||
const { json: packageJson, path: packageJsonPath } = readPluginPackageJson(
|
|
||||||
nodeModule,
|
|
||||||
this.resolvePaths()
|
|
||||||
);
|
|
||||||
const executorsFile = packageJson.executors ?? packageJson.builders;
|
|
||||||
|
|
||||||
if (!executorsFile) {
|
|
||||||
throw new Error(
|
|
||||||
`The "${nodeModule}" package does not support Nx executors.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const executorsFilePath = require.resolve(
|
|
||||||
path.join(path.dirname(packageJsonPath), executorsFile)
|
|
||||||
);
|
|
||||||
const executorsJson = readJsonFile<ExecutorsJson>(executorsFilePath);
|
|
||||||
const executorConfig: {
|
|
||||||
implementation: string;
|
|
||||||
batchImplementation?: string;
|
|
||||||
schema: string;
|
|
||||||
hasher?: string;
|
|
||||||
} =
|
|
||||||
executorsJson.executors?.[executor] || executorsJson.builders?.[executor];
|
|
||||||
if (!executorConfig) {
|
|
||||||
throw new Error(
|
|
||||||
`Cannot find executor '${executor}' in ${executorsFilePath}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const isNgCompat = !executorsJson.executors?.[executor];
|
|
||||||
return { executorsFilePath, executorConfig, isNgCompat };
|
|
||||||
}
|
|
||||||
|
|
||||||
private readGeneratorsJson(
|
private readGeneratorsJson(
|
||||||
collectionName: string,
|
collectionName: string,
|
||||||
generator: string
|
generator: string
|
||||||
@ -485,22 +335,6 @@ function findMatchingProjectInCwd(
|
|||||||
return matchingProject;
|
return matchingProject;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeExecutorSchema(
|
|
||||||
schema: Partial<ExecutorConfig['schema']>
|
|
||||||
): ExecutorConfig['schema'] {
|
|
||||||
const version = (schema.version ??= 1);
|
|
||||||
return {
|
|
||||||
version,
|
|
||||||
outputCapture:
|
|
||||||
schema.outputCapture ?? version < 2 ? 'direct-nodejs' : 'pipe',
|
|
||||||
properties:
|
|
||||||
!schema.properties || typeof schema.properties !== 'object'
|
|
||||||
? {}
|
|
||||||
: schema.properties,
|
|
||||||
...schema,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function findFullGeneratorName(
|
function findFullGeneratorName(
|
||||||
name: string,
|
name: string,
|
||||||
generators: {
|
generators: {
|
||||||
|
|||||||
@ -4,3 +4,4 @@
|
|||||||
* These may not be available in certain version of Nx, so be sure to check them first.
|
* These may not be available in certain version of Nx, so be sure to check them first.
|
||||||
*/
|
*/
|
||||||
export { createTempNpmDirectory } from './utils/package-manager';
|
export { createTempNpmDirectory } from './utils/package-manager';
|
||||||
|
export { getExecutorInformation } from './command-line/run/executor-utils';
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import {
|
|||||||
CompleteTaskMessage,
|
CompleteTaskMessage,
|
||||||
BatchResults,
|
BatchResults,
|
||||||
} from './batch-messages';
|
} from './batch-messages';
|
||||||
import { Workspaces } from '../../config/workspaces';
|
|
||||||
import { workspaceRoot } from '../../utils/workspace-root';
|
import { workspaceRoot } from '../../utils/workspace-root';
|
||||||
import { combineOptionsForExecutor } from '../../utils/params';
|
import { combineOptionsForExecutor } from '../../utils/params';
|
||||||
import { TaskGraph } from '../../config/task-graph';
|
import { TaskGraph } from '../../config/task-graph';
|
||||||
@ -16,11 +15,11 @@ import {
|
|||||||
} from '../../project-graph/project-graph';
|
} from '../../project-graph/project-graph';
|
||||||
import { readNxJson } from '../../config/configuration';
|
import { readNxJson } from '../../config/configuration';
|
||||||
import { isAsyncIterator } from '../../utils/async-iterator';
|
import { isAsyncIterator } from '../../utils/async-iterator';
|
||||||
|
import { getExecutorInformation } from '../../command-line/run/executor-utils';
|
||||||
|
|
||||||
function getBatchExecutor(executorName: string) {
|
function getBatchExecutor(executorName: string) {
|
||||||
const workspace = new Workspaces(workspaceRoot);
|
|
||||||
const [nodeModule, exportName] = executorName.split(':');
|
const [nodeModule, exportName] = executorName.split(':');
|
||||||
return workspace.readExecutor(nodeModule, exportName);
|
return getExecutorInformation(nodeModule, exportName, workspaceRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runTasks(
|
async function runTasks(
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { Workspaces } from '../config/workspaces';
|
|||||||
import { removeTasksFromTaskGraph } from './utils';
|
import { removeTasksFromTaskGraph } from './utils';
|
||||||
import { Task, TaskGraph } from '../config/task-graph';
|
import { Task, TaskGraph } from '../config/task-graph';
|
||||||
import { DependencyType, ProjectGraph } from '../config/project-graph';
|
import { DependencyType, ProjectGraph } from '../config/project-graph';
|
||||||
|
import * as executorUtils from '../command-line/run/executor-utils';
|
||||||
|
|
||||||
function createMockTask(id: string): Task {
|
function createMockTask(id: string): Task {
|
||||||
const [project, target] = id.split(':');
|
const [project, target] = id.split(':');
|
||||||
@ -43,20 +44,20 @@ describe('TasksSchedule', () => {
|
|||||||
roots: ['lib1:build', 'app2:build'],
|
roots: ['lib1:build', 'app2:build'],
|
||||||
};
|
};
|
||||||
const workspace: Partial<Workspaces> = {
|
const workspace: Partial<Workspaces> = {
|
||||||
readExecutor() {
|
readNxJson() {
|
||||||
return {
|
return {};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
jest.spyOn(executorUtils, 'getExecutorInformation').mockReturnValue({
|
||||||
schema: {
|
schema: {
|
||||||
version: 2,
|
version: 2,
|
||||||
properties: {},
|
properties: {},
|
||||||
},
|
},
|
||||||
implementationFactory: jest.fn(),
|
implementationFactory: jest.fn(),
|
||||||
batchImplementationFactory: jest.fn(),
|
batchImplementationFactory: jest.fn(),
|
||||||
} as any;
|
isNgCompat: true,
|
||||||
},
|
isNxExecutor: true,
|
||||||
readNxJson() {
|
});
|
||||||
return {};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const projectGraph: ProjectGraph = {
|
const projectGraph: ProjectGraph = {
|
||||||
nodes: {
|
nodes: {
|
||||||
@ -269,20 +270,20 @@ describe('TasksSchedule', () => {
|
|||||||
roots: ['app1:test', 'app2:test', 'lib1:test'],
|
roots: ['app1:test', 'app2:test', 'lib1:test'],
|
||||||
};
|
};
|
||||||
const workspace: Partial<Workspaces> = {
|
const workspace: Partial<Workspaces> = {
|
||||||
readExecutor() {
|
readNxJson() {
|
||||||
return {
|
return {};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
jest.spyOn(executorUtils, 'getExecutorInformation').mockReturnValue({
|
||||||
schema: {
|
schema: {
|
||||||
version: 2,
|
version: 2,
|
||||||
properties: {},
|
properties: {},
|
||||||
},
|
},
|
||||||
implementationFactory: jest.fn(),
|
implementationFactory: jest.fn(),
|
||||||
batchImplementationFactory: jest.fn(),
|
batchImplementationFactory: jest.fn(),
|
||||||
} as any;
|
isNgCompat: true,
|
||||||
},
|
isNxExecutor: true,
|
||||||
readNxJson() {
|
});
|
||||||
return {};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const projectGraph: ProjectGraph = {
|
const projectGraph: ProjectGraph = {
|
||||||
nodes: {
|
nodes: {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { joinPathFragments } from '../utils/path';
|
|||||||
import { isRelativePath } from '../utils/fileutils';
|
import { isRelativePath } from '../utils/fileutils';
|
||||||
import { serializeOverridesIntoCommandLine } from '../utils/serialize-overrides-into-command-line';
|
import { serializeOverridesIntoCommandLine } from '../utils/serialize-overrides-into-command-line';
|
||||||
import { splitByColons, splitTarget } from '../utils/split-target';
|
import { splitByColons, splitTarget } from '../utils/split-target';
|
||||||
|
import { getExecutorInformation } from '../command-line/run/executor-utils';
|
||||||
|
|
||||||
export function getCommandAsString(execCommand: string, task: Task) {
|
export function getCommandAsString(execCommand: string, task: Task) {
|
||||||
const args = getPrintableCommandArgsForTask(task);
|
const args = getPrintableCommandArgsForTask(task);
|
||||||
@ -250,7 +251,7 @@ export async function getExecutorForTask(
|
|||||||
const executor = await getExecutorNameForTask(task, projectGraph);
|
const executor = await getExecutorNameForTask(task, projectGraph);
|
||||||
const [nodeModule, executorName] = executor.split(':');
|
const [nodeModule, executorName] = executor.split(':');
|
||||||
|
|
||||||
return workspace.readExecutor(nodeModule, executorName);
|
return getExecutorInformation(nodeModule, executorName, workspaceRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCustomHasher(
|
export async function getCustomHasher(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user