feat(core): allow executors to specify if they are continuous (#30821)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

The only way to set if a task is continuous is either directly in
`project.json` or via Project Graph Plugins.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

Executors know if they are definitely continuous or not. Plenty of
existing continuous tasks are using executors. Executors are now able to
define if they are continuous in their `schema.json` files. Thus,
existing tasks configured with certain executors will automatically
become continuous.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
This commit is contained in:
Jason Jean 2025-04-24 16:41:17 -04:00 committed by GitHub
parent eb5138e858
commit 4254c4bcce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 205 additions and 53 deletions

View File

@ -51,7 +51,7 @@ The `nx.json` configuration from the workspace
`Readonly` **projects**: `Record`\<`string`, [`ProjectConfiguration`](../../devkit/documents/ProjectConfiguration)\>
The configuration of each project in the workspace.
The configuration of each project in the workspace keyed by project name.
---

View File

@ -3,6 +3,7 @@
"implementation": "/packages/angular/src/builders/dev-server/dev-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Schema for Webpack Dev Server",

View File

@ -4,6 +4,7 @@
"schema": {
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Schema for Module Federation Dev Server",
"continuous": true,
"outputCapture": "direct-nodejs",
"description": "Serves host [Module Federation](https://module-federation.io/) applications ([webpack](https://webpack.js.org/)-based) allowing to specify which remote applications should be served with the host.",
"type": "object",

View File

@ -4,6 +4,7 @@
"schema": {
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Module Federation SSR Dev Server Target",
"continuous": true,
"outputCapture": "direct-nodejs",
"description": "The module-federation-ssr-dev-server executor is reserved exclusively for use with host SSR Module Federation applications. It allows the user to specify which remote applications should be served with the host.",
"type": "object",

View File

@ -6,6 +6,7 @@
"version": 2,
"title": "Verdaccio Local Registry",
"description": "Start a local registry with Verdaccio.",
"continuous": true,
"cli": "nx",
"type": "object",
"properties": {

View File

@ -3,6 +3,7 @@
"implementation": "/packages/next/src/executors/server/server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "pipe",
"cli": "nx",
"title": "Next Serve",

View File

@ -3,6 +3,7 @@
"implementation": "/packages/react/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Module Federation Dev Server",
"description": "Serve a web application.",

View File

@ -3,6 +3,7 @@
"implementation": "/packages/react/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Module Federation SSR Dev Server",
"description": "Serve a SSR Consumer (host) application along with its known Producers (remotes).",

View File

@ -3,6 +3,7 @@
"implementation": "/packages/react/src/executors/module-federation-static-server/module-federation-static-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Module Federation Static Dev Server",
"description": "Serve a Consumer (host) application statically along with its Producers (remotes).",

View File

@ -3,6 +3,7 @@
"implementation": "/packages/remix/src/executors/serve/serve.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "pipe",
"cli": "nx",
"title": "Remix Serve",

View File

@ -6,6 +6,7 @@
"version": 2,
"title": "Rspack dev-server executor",
"description": "Run @rspack/dev-server to serve a project.",
"continuous": true,
"type": "object",
"properties": {
"buildTarget": {

View File

@ -3,6 +3,7 @@
"implementation": "/packages/rspack/src/executors/module-federation-dev-server/module-federation-dev-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Rspack Module Federation Dev Server",
"description": "Serve a module federation application.",

View File

@ -3,6 +3,7 @@
"implementation": "/packages/rspack/src/executors/module-federation-ssr-dev-server/module-federation-ssr-dev-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Module Federation SSR Dev Server",
"description": "Serve a SSR Consumer (host) application along with its known Producers (remotes).",

View File

@ -3,6 +3,7 @@
"implementation": "/packages/rspack/src/executors/module-federation-static-server/module-federation-static-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Module Federation Static Dev Server",
"description": "Serve a Consumer (host) application statically along with it's Producers (remotes).",

View File

@ -2,6 +2,7 @@
"name": "ssr-dev-server",
"implementation": "/packages/rspack/src/executors/ssr-dev-server/ssr-dev-server.impl.ts",
"schema": {
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Rspack SSR Dev Server",
"description": "Serve a SSR application using rspack.",

View File

@ -3,6 +3,7 @@
"implementation": "/packages/storybook/src/executors/storybook/storybook.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Storybook Dev Builder",
"cli": "nx",

View File

@ -3,6 +3,7 @@
"implementation": "/packages/vite/src/executors/dev-server/dev-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Vite Dev Server",
"cli": "nx",

View File

@ -7,6 +7,7 @@
"cli": "nx",
"title": "Vite Preview Server",
"description": "Preview Server for Vite.",
"continuous": true,
"type": "object",
"presets": [
{ "name": "Default minimum setup", "keys": ["buildTarget"] },

View File

@ -3,6 +3,7 @@
"implementation": "/packages/web/src/executors/file-server/file-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "File Server",
"description": "Serve a web application from a folder. This executor is a wrapper around the [http-server](https://www.npmjs.com/package/http-server) package.",

View File

@ -3,6 +3,7 @@
"implementation": "/packages/webpack/src/executors/dev-server/dev-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Webpack dev server",
"description": "Serve an application using webpack.",

View File

@ -3,6 +3,7 @@
"implementation": "/packages/webpack/src/executors/ssr-dev-server/ssr-dev-server.impl.ts",
"schema": {
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Webpack SSR Dev Server",
"description": "Serve a SSR application using webpack.",

View File

@ -135,16 +135,9 @@ const angularV1Json = (appName: string) => `{
"projectType": "application",
"architect": {
"e2e": {
"builder": "@nx/cypress:cypress",
"builder": "@nx/playwright:playwright",
"options": {
"cypressConfig": "${appName}-e2e/cypress.json",
"devServerTarget": "${appName}:serve:development",
"testingType": "e2e"
},
"configurations": {
"production": {
"devServerTarget": "${appName}:serve:production"
}
"config": "${appName}-e2e/playwright.config.js"
}
},
"lint": {

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Schema for Webpack Dev Server",

View File

@ -1,6 +1,7 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Schema for Module Federation Dev Server",
"continuous": true,
"outputCapture": "direct-nodejs",
"description": "Serves host [Module Federation](https://module-federation.io/) applications ([webpack](https://webpack.js.org/)-based) allowing to specify which remote applications should be served with the host.",
"type": "object",

View File

@ -1,6 +1,7 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"title": "Module Federation SSR Dev Server Target",
"continuous": true,
"outputCapture": "direct-nodejs",
"description": "The module-federation-ssr-dev-server executor is reserved exclusively for use with host SSR Module Federation applications. It allows the user to specify which remote applications should be served with the host.",
"type": "object",

View File

@ -12,7 +12,10 @@ import {
import { join } from 'path';
import { CypressExecutorOptions } from '../executors/cypress/cypress.impl';
import * as detectPort from 'detect-port';
import { getExecutorInformation } from 'nx/src/command-line/run/executor-utils';
import {
getExecutorInformation,
parseExecutor,
} from 'nx/src/command-line/run/executor-utils';
import { existsSync, writeFileSync } from 'fs';
export async function* startDevServer(
@ -190,7 +193,7 @@ ${e.message || e}`);
context.projectsConfigurations?.projects?.[target.project];
const targetConfig = projectConfig.targets[target.target];
const [collection, executor] = targetConfig.executor.split(':');
const [collection, executor] = parseExecutor(targetConfig.executor);
const { schema } = getExecutorInformation(
collection,
executor,

View File

@ -6,6 +6,7 @@ import {
calculateDefaultProjectName,
combineOptionsForExecutor,
getExecutorInformation,
parseExecutor,
} from 'nx/src/devkit-internals';
/**
@ -29,7 +30,9 @@ export function readTargetOptions<T = any>(
throw new Error(`Unable to find target ${target} for project ${project}`);
}
const [nodeModule, executorName] = targetConfiguration.executor.split(':');
const [nodeModule, executorName] = parseExecutor(
targetConfiguration.executor
);
const { schema } = getExecutorInformation(
nodeModule,
executorName,

View File

@ -33,6 +33,7 @@ import { dirname, isAbsolute, join, relative, resolve } from 'path';
import { getInstalledJestMajorVersion } from '../utils/version-utils';
import { globWithWorkspaceContext } from 'nx/src/utils/workspace-context';
import { normalize, sep } from 'node:path';
import { getNxRequirePaths } from 'nx/src/utils/installation-directory';
const pmc = getPackageManagerCommand();
@ -654,7 +655,7 @@ function resolveJestPath(projectRoot: string, workspaceRoot: string): string {
}
resolvedJestPaths[projectRoot] = require.resolve('jest', {
paths: [projectRoot, workspaceRoot, __dirname],
paths: [projectRoot, ...getNxRequirePaths(workspaceRoot), __dirname],
});
return resolvedJestPaths[projectRoot];

View File

@ -3,6 +3,7 @@
"version": 2,
"title": "Verdaccio Local Registry",
"description": "Start a local registry with Verdaccio.",
"continuous": true,
"cli": "nx",
"type": "object",
"properties": {

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "pipe",
"cli": "nx",
"title": "Next Serve",

View File

@ -24,6 +24,7 @@ export function normalizeExecutorSchema(
version,
outputCapture:
schema.outputCapture ?? version < 2 ? 'direct-nodejs' : 'pipe',
continuous: schema.continuous ?? false,
properties:
!schema.properties || typeof schema.properties !== 'object'
? {}
@ -36,12 +37,21 @@ function cacheKey(nodeModule: string, executor: string, root: string) {
return `${root}:${nodeModule}:${executor}`;
}
export function parseExecutor(
executorString: string
): [module: string, name: string] {
return executorString.split(':') as [string, string];
}
const cachedExecutorInformation = {};
export function getExecutorInformation(
nodeModule: string,
executor: string,
root: string,
/**
* A map of projects keyed by project name
*/
projects: Record<string, ProjectConfiguration>
): ExecutorConfig & { isNgCompat: boolean; isNxExecutor: boolean } {
try {

View File

@ -18,7 +18,7 @@ import {
getLastValueFromAsyncIterableIterator,
isAsyncIterator,
} from '../../utils/async-iterator';
import { getExecutorInformation } from './executor-utils';
import { getExecutorInformation, parseExecutor } from './executor-utils';
import {
createPseudoTerminal,
PseudoTerminal,
@ -83,7 +83,7 @@ async function parseExecutorAndTarget(
throw new Error(`Cannot find target '${target}' for project '${project}'`);
}
const [nodeModule, executor] = targetConfig.executor.split(':');
const [nodeModule, executor] = parseExecutor(targetConfig.executor);
const { schema, implementationFactory } = getExecutorInformation(
nodeModule,
executor,

View File

@ -110,6 +110,7 @@ export interface ExecutorConfig {
schema: {
version?: number;
outputCapture?: OutputCaptureMethod;
continuous?: boolean;
} & Schema;
hasherFactory?: () => CustomHasher;
implementationFactory: () => Executor;

View File

@ -4,7 +4,10 @@
* These may not be available in certain version of Nx, so be sure to check them first.
*/
export { createTempNpmDirectory } from './utils/package-manager';
export { getExecutorInformation } from './command-line/run/executor-utils';
export {
getExecutorInformation,
parseExecutor,
} from './command-line/run/executor-utils';
export { readNxJson as readNxJsonFromDisk } from './config/nx-json';
export { calculateDefaultProjectName } from './config/calculate-default-project-name';
export { retrieveProjectConfigurationsWithAngularProjects } from './project-graph/utils/retrieve-workspace-files';

View File

@ -214,7 +214,12 @@ export function buildProjectConfigurationFromPackageJson(
root: projectRoot,
name,
...packageJson.nx,
targets: readTargetsFromPackageJson(packageJson, nxJson),
targets: readTargetsFromPackageJson(
packageJson,
nxJson,
projectRoot,
workspaceRoot
),
tags: getTagsFromPackageJson(packageJson),
metadata: getMetadataFromPackageJson(
packageJson,

View File

@ -99,7 +99,7 @@ export interface CreateDependenciesContext {
readonly externalNodes: ProjectGraph['externalNodes'];
/**
* The configuration of each project in the workspace.
* The configuration of each project in the workspace keyed by project name.
*/
readonly projects: Record<string, ProjectConfiguration>;

View File

@ -1,9 +1,6 @@
import { ProjectGraphProjectNode } from '../../config/project-graph';
import { ProjectGraphBuilder } from '../project-graph-builder';
import {
ProjectConfiguration,
TargetConfiguration,
} from '../../config/workspace-json-project-json';
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
import { findMatchingProjects } from '../../utils/find-matching-projects';
import { CreateDependenciesContext } from '../plugins';

View File

@ -18,6 +18,7 @@ import { NxPluginV2 } from '../plugins';
import { LoadedNxPlugin } from '../plugins/loaded-nx-plugin';
import { dirname } from 'path';
import { isProjectConfigurationsError } from '../error-types';
import { workspaceRoot } from '../../utils/workspace-root';
describe('project-configuration-utils', () => {
describe('target merging', () => {
@ -1658,7 +1659,7 @@ describe('project-configuration-utils', () => {
foo: { command: 'echo {projectRoot}' },
},
};
expect(normalizeTarget(config.targets.foo, config))
expect(normalizeTarget(config.targets.foo, config, workspaceRoot, {}))
.toMatchInlineSnapshot(`
{
"configurations": {},
@ -1701,8 +1702,8 @@ describe('project-configuration-utils', () => {
};
const originalConfig = JSON.stringify(config, null, 2);
normalizeTarget(config.targets.foo, config);
normalizeTarget(config.targets.bar, config);
normalizeTarget(config.targets.foo, config, workspaceRoot, {});
normalizeTarget(config.targets.bar, config, workspaceRoot, {});
expect(JSON.stringify(config, null, 2)).toEqual(originalConfig);
});
});

View File

@ -33,6 +33,10 @@ import {
import { CreateNodesResult } from '../plugins/public-api';
import { isGlobPattern } from '../../utils/globs';
import { DelayedSpinner } from '../../utils/delayed-spinner';
import {
getExecutorInformation,
parseExecutor,
} from '../../command-line/run/executor-utils';
export type SourceInformation = [file: string | null, plugin: string];
export type ConfigurationSourceMaps = Record<
@ -447,7 +451,7 @@ export async function createProjectConfigurationsWithPlugins(
spinner?.cleanup();
const { projectRootMap, externalNodes, rootMap, configurationSourceMaps } =
mergeCreateNodesResults(results, nxJson, errors);
mergeCreateNodesResults(results, nxJson, root, errors);
performance.mark('build-project-configs:end');
performance.measure(
@ -484,6 +488,7 @@ function mergeCreateNodesResults(
pluginIndex?: number
])[][],
nxJsonConfiguration: NxJsonConfiguration,
workspaceRoot: string,
errors: (
| AggregateCreateNodesError
| MergeNodesError
@ -539,6 +544,7 @@ function mergeCreateNodesResults(
try {
validateAndNormalizeProjectRootMap(
workspaceRoot,
projectRootMap,
nxJsonConfiguration,
configurationSourceMaps
@ -643,6 +649,7 @@ export function readProjectConfigurationsFromRootMap(
}
function validateAndNormalizeProjectRootMap(
workspaceRoot: string,
projectRootMap: Record<string, ProjectConfiguration>,
nxJsonConfiguration: NxJsonConfiguration,
sourceMaps: ConfigurationSourceMaps = {}
@ -678,7 +685,13 @@ function validateAndNormalizeProjectRootMap(
}
}
normalizeTargets(project, sourceMaps, nxJsonConfiguration);
normalizeTargets(
project,
sourceMaps,
nxJsonConfiguration,
workspaceRoot,
projects
);
}
if (conflicts.size > 0) {
@ -693,12 +706,19 @@ function validateAndNormalizeProjectRootMap(
function normalizeTargets(
project: ProjectConfiguration,
sourceMaps: ConfigurationSourceMaps,
nxJsonConfiguration: NxJsonConfiguration<'*' | string[]>
nxJsonConfiguration: NxJsonConfiguration,
workspaceRoot: string,
/**
* Project configurations keyed by project name
*/
projects: Record<string, ProjectConfiguration>
) {
for (const targetName in project.targets) {
project.targets[targetName] = normalizeTarget(
project.targets[targetName],
project
project,
workspaceRoot,
projects
);
const projectSourceMaps = sourceMaps[project.root];
@ -717,7 +737,7 @@ function normalizeTargets(
project.targets[targetName] = mergeTargetDefaultWithTargetDefinition(
targetName,
project,
normalizeTarget(targetDefaults, project),
normalizeTarget(targetDefaults, project, workspaceRoot, projects),
projectSourceMaps
);
}
@ -1161,14 +1181,16 @@ function resolveCommandSyntacticSugar(
}
/**
* Expand's `command` syntactic sugar and replaces tokens in options.
* Expand's `command` syntactic sugar, replaces tokens in options, and adds information from executor schema.
* @param target The target to normalize
* @param project The project that the target belongs to
* @returns The normalized target configuration
*/
export function normalizeTarget(
target: TargetConfiguration,
project: ProjectConfiguration
project: ProjectConfiguration,
workspaceRoot: string,
projectsMap: Record<string, ProjectConfiguration>
) {
target = {
...target,
@ -1195,5 +1217,26 @@ export function normalizeTarget(
target.parallelism ??= true;
if (target.executor && !('continuous' in target)) {
try {
const [executorNodeModule, executorName] = parseExecutor(target.executor);
const { schema } = getExecutorInformation(
executorNodeModule,
executorName,
workspaceRoot,
projectsMap
);
if (schema.continuous) {
target.continuous ??= schema.continuous;
}
} catch (e) {
// If the executor is not found, we assume that it is not a valid executor.
// This means that we should not set the continuous property.
// We could throw an error here, but it would be better to just ignore it.
}
}
return target;
}

View File

@ -12,7 +12,10 @@ import { ExecutorContext } from '../../config/misc-interfaces';
import { readProjectsConfigurationFromProjectGraph } from '../../project-graph/project-graph';
import { readNxJson } from '../../config/configuration';
import { isAsyncIterator } from '../../utils/async-iterator';
import { getExecutorInformation } from '../../command-line/run/executor-utils';
import {
getExecutorInformation,
parseExecutor,
} from '../../command-line/run/executor-utils';
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
import { ProjectGraph } from '../../config/project-graph';
@ -20,7 +23,7 @@ function getBatchExecutor(
executorName: string,
projects: Record<string, ProjectConfiguration>
) {
const [nodeModule, exportName] = executorName.split(':');
const [nodeModule, exportName] = parseExecutor(executorName);
return getExecutorInformation(
nodeModule,
exportName,

View File

@ -521,7 +521,7 @@ export function getNonDummyDeps(
}
}
export function createTaskId(
function createTaskId(
project: string,
target: string,
configuration: string | undefined

View File

@ -40,7 +40,7 @@ import {
processSyncGeneratorResultErrors,
} from '../utils/sync-generators';
import { workspaceRoot } from '../utils/workspace-root';
import { createTaskGraph, createTaskId } from './create-task-graph';
import { createTaskGraph } from './create-task-graph';
import { isTuiEnabled } from './is-tui-enabled';
import {
CompositeLifeCycle,

View File

@ -1,7 +1,10 @@
import { minimatch } from 'minimatch';
import { relative } from 'node:path';
import { join } from 'node:path/posix';
import { getExecutorInformation } from '../command-line/run/executor-utils';
import {
getExecutorInformation,
parseExecutor,
} from '../command-line/run/executor-utils';
import { CustomHasher, ExecutorConfig } from '../config/misc-interfaces';
import { ProjectGraph, ProjectGraphProjectNode } from '../config/project-graph';
import { Task, TaskGraph } from '../config/task-graph';
@ -429,7 +432,7 @@ export function getExecutorForTask(
projectGraph: ProjectGraph
): ExecutorConfig & { isNgCompat: boolean; isNxExecutor: boolean } {
const executor = getExecutorNameForTask(task, projectGraph);
const [nodeModule, executorName] = executor.split(':');
const [nodeModule, executorName] = parseExecutor(executor);
return getExecutorInformation(
nodeModule,

View File

@ -48,7 +48,12 @@ describe('readTargetsFromPackageJson', () => {
},
},
};
const result1 = readTargetsFromPackageJson(packageJson, nxJson1);
const result1 = readTargetsFromPackageJson(
packageJson,
nxJson1,
workspaceRoot,
'/root'
);
expect(result1['nx-release-publish']).toMatchInlineSnapshot(`
{
"dependsOn": [
@ -69,7 +74,12 @@ describe('readTargetsFromPackageJson', () => {
},
},
};
const result2 = readTargetsFromPackageJson(packageJson, nxJson2);
const result2 = readTargetsFromPackageJson(
packageJson,
nxJson2,
workspaceRoot,
'/root'
);
expect(result2['nx-release-publish']).toMatchInlineSnapshot(`
{
"dependsOn": [
@ -83,7 +93,12 @@ describe('readTargetsFromPackageJson', () => {
});
it('should read targets from project.json and package.json', () => {
const result = readTargetsFromPackageJson(packageJson, {});
const result = readTargetsFromPackageJson(
packageJson,
{},
workspaceRoot,
'/root'
);
expect(result).toMatchInlineSnapshot(`
{
"build": {
@ -123,7 +138,9 @@ describe('readTargetsFromPackageJson', () => {
},
},
},
{}
{},
workspaceRoot,
'/root'
);
expect(result).toEqual({
build: { ...packageJsonBuildTarget, outputs: ['custom'] },
@ -148,9 +165,10 @@ describe('readTargetsFromPackageJson', () => {
includedScripts: ['test'],
},
},
{}
{},
workspaceRoot,
'/root'
);
expect(result).toMatchInlineSnapshot(`
{
"nx-release-publish": {
@ -190,7 +208,9 @@ describe('readTargetsFromPackageJson', () => {
},
},
},
{}
{},
workspaceRoot,
'/root'
);
expect(result.build).toMatchInlineSnapshot(`
{
@ -228,7 +248,9 @@ describe('readTargetsFromPackageJson', () => {
},
},
},
{}
{},
workspaceRoot,
'/root'
);
expect(result.build).toMatchInlineSnapshot(`
{
@ -261,7 +283,9 @@ describe('readTargetsFromPackageJson', () => {
},
},
},
{}
{},
workspaceRoot,
'/root'
);
expect(result.build).toMatchInlineSnapshot(`
{
@ -289,7 +313,9 @@ describe('readTargetsFromPackageJson', () => {
},
},
},
{}
{},
workspaceRoot,
'/root'
);
expect(result.build).toMatchInlineSnapshot(`
{
@ -356,7 +382,9 @@ describe('readTargetsFromPackageJson', () => {
includedScripts: [],
},
},
{}
{},
workspaceRoot,
'/root'
);
expect(result.test).toMatchInlineSnapshot(`
{

View File

@ -10,8 +10,8 @@ import { mergeTargetConfigurations } from '../project-graph/utils/project-config
import { readJsonFile } from './fileutils';
import { getNxRequirePaths } from './installation-directory';
import {
PackageManagerCommands,
getPackageManagerCommand,
PackageManagerCommands,
} from './package-manager';
export interface NxProjectPackageJsonConfiguration
@ -186,7 +186,9 @@ export function getTagsFromPackageJson(packageJson: PackageJson): string[] {
export function readTargetsFromPackageJson(
packageJson: PackageJson,
nxJson: NxJsonConfiguration
nxJson: NxJsonConfiguration,
projectRoot: string,
workspaceRoot: string
) {
const { scripts, nx, private: isPrivate } = packageJson ?? {};
const res: Record<string, TargetConfiguration> = {};
@ -210,7 +212,11 @@ export function readTargetsFromPackageJson(
* Any targetDefaults for the nx-release-publish target set by the user should
* be merged with the implicit target.
*/
if (!isPrivate && !res['nx-release-publish']) {
if (
!isPrivate &&
!res['nx-release-publish'] &&
hasNxJsPlugin(projectRoot, workspaceRoot)
) {
const nxReleasePublishTargetDefaults =
nxJson?.targetDefaults?.['nx-release-publish'] ?? {};
res['nx-release-publish'] = {
@ -230,6 +236,18 @@ export function readTargetsFromPackageJson(
return res;
}
function hasNxJsPlugin(projectRoot: string, workspaceRoot: string) {
try {
// nx-ignore-next-line
require.resolve('@nx/js', {
paths: [projectRoot, ...getNxRequirePaths(workspaceRoot), __dirname],
});
return true;
} catch {
return false;
}
}
/**
* Uses `require.resolve` to read the package.json for a module.
*

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Module Federation Dev Server",
"description": "Serve a web application.",

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Module Federation SSR Dev Server",
"description": "Serve a SSR Consumer (host) application along with its known Producers (remotes).",

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Module Federation Static Dev Server",
"description": "Serve a Consumer (host) application statically along with its Producers (remotes).",

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "pipe",
"cli": "nx",
"title": "Remix Serve",

View File

@ -3,6 +3,7 @@
"version": 2,
"title": "Rspack dev-server executor",
"description": "Run @rspack/dev-server to serve a project.",
"continuous": true,
"type": "object",
"properties": {
"buildTarget": {

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Rspack Module Federation Dev Server",
"description": "Serve a module federation application.",

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Module Federation SSR Dev Server",
"description": "Serve a SSR Consumer (host) application along with its known Producers (remotes).",

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Module Federation Static Dev Server",
"description": "Serve a Consumer (host) application statically along with it's Producers (remotes).",

View File

@ -1,4 +1,5 @@
{
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Rspack SSR Dev Server",
"description": "Serve a SSR application using rspack.",

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Storybook Dev Builder",
"cli": "nx",

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Vite Dev Server",
"cli": "nx",

View File

@ -4,6 +4,7 @@
"cli": "nx",
"title": "Vite Preview Server",
"description": "Preview Server for Vite.",
"continuous": true,
"type": "object",
"presets": [
{

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "File Server",
"description": "Serve a web application from a folder. This executor is a wrapper around the [http-server](https://www.npmjs.com/package/http-server) package.",

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Webpack dev server",
"description": "Serve an application using webpack.",

View File

@ -1,5 +1,6 @@
{
"version": 2,
"continuous": true,
"outputCapture": "direct-nodejs",
"title": "Webpack SSR Dev Server",
"description": "Serve a SSR application using webpack.",