feat(core): support targets with colons in the name without quotes (#13938)
This commit is contained in:
parent
a04a2ea00b
commit
285dc39371
@ -1749,20 +1749,35 @@ Object the JSON content represents
|
|||||||
|
|
||||||
▸ **parseTargetString**(`targetString`): [`Target`](../../devkit/documents/index#target)
|
▸ **parseTargetString**(`targetString`): [`Target`](../../devkit/documents/index#target)
|
||||||
|
|
||||||
|
**`deprecated(v17)`** A project graph should be passed to parseTargetString for best accuracy.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
| Name | Type |
|
||||||
|
| :------------- | :------- |
|
||||||
|
| `targetString` | `string` |
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
[`Target`](../../devkit/documents/index#target)
|
||||||
|
|
||||||
|
▸ **parseTargetString**(`targetString`, `projectGraph`): [`Target`](../../devkit/documents/index#target)
|
||||||
|
|
||||||
Parses a target string into {project, target, configuration}
|
Parses a target string into {project, target, configuration}
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
parseTargetString('proj:test'); // returns { project: "proj", target: "test" }
|
parseTargetString('proj:test', graph); // returns { project: "proj", target: "test" }
|
||||||
parseTargetString('proj:test:production'); // returns { project: "proj", target: "test", configuration: "production" }
|
parseTargetString('proj:test:production', graph); // returns { project: "proj", target: "test", configuration: "production" }
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| :------------- | :------- | :--------------- |
|
| :------------- | :------------------------------------------------------------------ | :--------------- |
|
||||||
| `targetString` | `string` | target reference |
|
| `targetString` | `string` | target reference |
|
||||||
|
| `projectGraph` | [`ProjectGraph`](../../devkit/documents/index#projectgraph)<`any`\> | - |
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
|
|||||||
24
docs/generated/packages/devkit.json
Normal file
24
docs/generated/packages/devkit.json
Normal file
File diff suppressed because one or more lines are too long
@ -1749,20 +1749,35 @@ Object the JSON content represents
|
|||||||
|
|
||||||
▸ **parseTargetString**(`targetString`): [`Target`](../../devkit/documents/index#target)
|
▸ **parseTargetString**(`targetString`): [`Target`](../../devkit/documents/index#target)
|
||||||
|
|
||||||
|
**`deprecated(v17)`** A project graph should be passed to parseTargetString for best accuracy.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
| Name | Type |
|
||||||
|
| :------------- | :------- |
|
||||||
|
| `targetString` | `string` |
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
[`Target`](../../devkit/documents/index#target)
|
||||||
|
|
||||||
|
▸ **parseTargetString**(`targetString`, `projectGraph`): [`Target`](../../devkit/documents/index#target)
|
||||||
|
|
||||||
Parses a target string into {project, target, configuration}
|
Parses a target string into {project, target, configuration}
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
parseTargetString('proj:test'); // returns { project: "proj", target: "test" }
|
parseTargetString('proj:test', graph); // returns { project: "proj", target: "test" }
|
||||||
parseTargetString('proj:test:production'); // returns { project: "proj", target: "test", configuration: "production" }
|
parseTargetString('proj:test:production', graph); // returns { project: "proj", target: "test", configuration: "production" }
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| :------------- | :------- | :--------------- |
|
| :------------- | :------------------------------------------------------------------ | :--------------- |
|
||||||
| `targetString` | `string` | target reference |
|
| `targetString` | `string` | target reference |
|
||||||
|
| `projectGraph` | [`ProjectGraph`](../../devkit/documents/index#projectgraph)<`any`\> | - |
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
||||||
|
|||||||
@ -147,7 +147,10 @@ function getBuildableTarget(ctContext: ExecutorContext) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseTargetString(cypressCtOptions.devServerTarget);
|
return parseTargetString(
|
||||||
|
cypressCtOptions.devServerTarget,
|
||||||
|
ctContext.projectGraph
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeBuildTargetOptions(
|
function normalizeBuildTargetOptions(
|
||||||
|
|||||||
@ -4,7 +4,11 @@ import {
|
|||||||
executeDevServerBuilder,
|
executeDevServerBuilder,
|
||||||
} from '@angular-devkit/build-angular';
|
} from '@angular-devkit/build-angular';
|
||||||
import { JsonObject } from '@angular-devkit/core';
|
import { JsonObject } from '@angular-devkit/core';
|
||||||
import { joinPathFragments, parseTargetString } from '@nrwl/devkit';
|
import {
|
||||||
|
joinPathFragments,
|
||||||
|
parseTargetString,
|
||||||
|
readCachedProjectGraph,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
import { WebpackNxBuildCoordinationPlugin } from '@nrwl/webpack/src/plugins/webpack-nx-build-coordination-plugin';
|
import { WebpackNxBuildCoordinationPlugin } from '@nrwl/webpack/src/plugins/webpack-nx-build-coordination-plugin';
|
||||||
import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||||
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
|
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
|
||||||
@ -26,7 +30,10 @@ export function executeWebpackDevServerBuilder(
|
|||||||
|
|
||||||
const options = normalizeOptions(rawOptions);
|
const options = normalizeOptions(rawOptions);
|
||||||
|
|
||||||
const parsedBrowserTarget = parseTargetString(options.browserTarget);
|
const parsedBrowserTarget = parseTargetString(
|
||||||
|
options.browserTarget,
|
||||||
|
readCachedProjectGraph()
|
||||||
|
);
|
||||||
const browserTargetProjectConfiguration = readCachedProjectConfiguration(
|
const browserTargetProjectConfiguration = readCachedProjectConfiguration(
|
||||||
parsedBrowserTarget.project
|
parsedBrowserTarget.project
|
||||||
);
|
);
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export async function* delegateBuildExecutor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { buildTarget, ...targetOptions } = options;
|
const { buildTarget, ...targetOptions } = options;
|
||||||
const delegateTarget = parseTargetString(buildTarget);
|
const delegateTarget = parseTargetString(buildTarget, context.projectGraph);
|
||||||
|
|
||||||
yield* await runExecutor(delegateTarget, targetOptions, context);
|
yield* await runExecutor(delegateTarget, targetOptions, context);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
createProjectGraphAsync,
|
||||||
formatFiles,
|
formatFiles,
|
||||||
getProjects,
|
getProjects,
|
||||||
parseTargetString,
|
parseTargetString,
|
||||||
@ -21,14 +22,14 @@ export default async function convertWebpackBrowserBuildTargetToDelegateBuild(
|
|||||||
);
|
);
|
||||||
for (const target of webpackBrowserTargets) {
|
for (const target of webpackBrowserTargets) {
|
||||||
const configurationOptions = getTargetConfigurationOptions(target);
|
const configurationOptions = getTargetConfigurationOptions(target);
|
||||||
const buildTargetName = getBuildTargetNameFromOptions(
|
const buildTargetName = await getBuildTargetNameFromOptions(
|
||||||
target.options,
|
target.options,
|
||||||
configurationOptions
|
configurationOptions
|
||||||
);
|
);
|
||||||
if (buildTargetName) {
|
if (buildTargetName) {
|
||||||
target.executor = '@nrwl/angular:delegate-build';
|
target.executor = '@nrwl/angular:delegate-build';
|
||||||
updateTargetsOptions(project, target, buildTargetName);
|
updateTargetsOptions(project, target, buildTargetName);
|
||||||
updateTargetsConfigurations(
|
await updateTargetsConfigurations(
|
||||||
project,
|
project,
|
||||||
projectName,
|
projectName,
|
||||||
target,
|
target,
|
||||||
@ -52,14 +53,17 @@ function cleanupBuildTargetProperties(options: {
|
|||||||
delete options.outputPath;
|
delete options.outputPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractConfigurationBuildTarget(
|
async function extractConfigurationBuildTarget(
|
||||||
project: string,
|
project: string,
|
||||||
target: string,
|
target: string,
|
||||||
configuration: string,
|
configuration: string,
|
||||||
buildTarget: string
|
buildTarget: string
|
||||||
): Target {
|
): Promise<Target> {
|
||||||
if (buildTarget) {
|
if (buildTarget) {
|
||||||
const buildTargetObj = parseTargetString(buildTarget);
|
const buildTargetObj = parseTargetString(
|
||||||
|
buildTarget,
|
||||||
|
await createProjectGraphAsync()
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
...buildTargetObj,
|
...buildTargetObj,
|
||||||
configuration: buildTargetObj.configuration ?? configuration,
|
configuration: buildTargetObj.configuration ?? configuration,
|
||||||
@ -73,16 +77,17 @@ function extractConfigurationBuildTarget(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBuildTargetNameFromOptions(
|
async function getBuildTargetNameFromOptions(
|
||||||
baseOptions: any,
|
baseOptions: any,
|
||||||
configurationOptions: Map<string, any>
|
configurationOptions: Map<string, any>
|
||||||
): string {
|
): Promise<string> {
|
||||||
|
const pg = await createProjectGraphAsync();
|
||||||
if (baseOptions.buildTarget) {
|
if (baseOptions.buildTarget) {
|
||||||
return parseTargetString(baseOptions.buildTarget).target;
|
return parseTargetString(baseOptions.buildTarget, pg).target;
|
||||||
}
|
}
|
||||||
for (const [, options] of configurationOptions) {
|
for (const [, options] of configurationOptions) {
|
||||||
if (options.buildTarget) {
|
if (options.buildTarget) {
|
||||||
return parseTargetString(options.buildTarget).target;
|
return parseTargetString(options.buildTarget, pg).target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +107,7 @@ function getTargetConfigurationOptions(
|
|||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTargetsConfigurations(
|
async function updateTargetsConfigurations(
|
||||||
project: ProjectConfiguration,
|
project: ProjectConfiguration,
|
||||||
projectName: string,
|
projectName: string,
|
||||||
target: TargetConfiguration,
|
target: TargetConfiguration,
|
||||||
@ -113,7 +118,7 @@ function updateTargetsConfigurations(
|
|||||||
const { buildTarget, tsConfig, outputPath, ...delegateTargetOptions } =
|
const { buildTarget, tsConfig, outputPath, ...delegateTargetOptions } =
|
||||||
options;
|
options;
|
||||||
|
|
||||||
const configurationBuildTarget = extractConfigurationBuildTarget(
|
const configurationBuildTarget = await extractConfigurationBuildTarget(
|
||||||
projectName,
|
projectName,
|
||||||
buildTargetName,
|
buildTargetName,
|
||||||
configurationName,
|
configurationName,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { CypressExecutorOptions } from '../../executors/cypress/cypress.impl';
|
|||||||
import { CY_FILE_MATCHER } from '../../utils/ct-helpers';
|
import { CY_FILE_MATCHER } from '../../utils/ct-helpers';
|
||||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||||
import {
|
import {
|
||||||
|
createProjectGraphAsync,
|
||||||
formatFiles,
|
formatFiles,
|
||||||
getProjects,
|
getProjects,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
@ -23,6 +24,7 @@ export async function updateCyMountUsage(tree: Tree) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const projects = getProjects(tree);
|
const projects = getProjects(tree);
|
||||||
|
const graph = await createProjectGraphAsync();
|
||||||
|
|
||||||
forEachExecutorOptions<CypressExecutorOptions>(
|
forEachExecutorOptions<CypressExecutorOptions>(
|
||||||
tree,
|
tree,
|
||||||
@ -32,7 +34,7 @@ export async function updateCyMountUsage(tree: Tree) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = parseTargetString(options.devServerTarget);
|
const parsed = parseTargetString(options.devServerTarget, graph);
|
||||||
if (!parsed?.project || !parsed?.target) {
|
if (!parsed?.project || !parsed?.target) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,10 @@ export default async function* detoxTestExecutor(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (options.buildTarget) {
|
if (options.buildTarget) {
|
||||||
const buildTarget = parseTargetString(options.buildTarget);
|
const buildTarget = parseTargetString(
|
||||||
|
options.buildTarget,
|
||||||
|
context.projectGraph
|
||||||
|
);
|
||||||
const buildOptions = readTargetOptions<DetoxBuildOptions>(
|
const buildOptions = readTargetOptions<DetoxBuildOptions>(
|
||||||
buildTarget,
|
buildTarget,
|
||||||
context
|
context
|
||||||
|
|||||||
@ -162,9 +162,6 @@ export { updateTsConfigsToJs } from './src/generators/update-ts-configs-to-js';
|
|||||||
*/
|
*/
|
||||||
export { visitNotIgnoredFiles } from './src/generators/visit-not-ignored-files';
|
export { visitNotIgnoredFiles } from './src/generators/visit-not-ignored-files';
|
||||||
|
|
||||||
/**
|
|
||||||
* @category Executors
|
|
||||||
*/
|
|
||||||
export {
|
export {
|
||||||
parseTargetString,
|
parseTargetString,
|
||||||
targetToTargetString,
|
targetToTargetString,
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { parseTargetString, targetToTargetString } from './parse-target-string';
|
import { parseTargetString, targetToTargetString } from './parse-target-string';
|
||||||
|
|
||||||
|
import * as splitTarget from 'nx/src/utils/split-target';
|
||||||
|
|
||||||
const cases = [
|
const cases = [
|
||||||
{ input: 'one:two', expected: { project: 'one', target: 'two' } },
|
{ input: 'one:two', expected: { project: 'one', target: 'two' } },
|
||||||
{
|
{
|
||||||
@ -14,7 +16,10 @@ const cases = [
|
|||||||
|
|
||||||
describe('parseTargetString', () => {
|
describe('parseTargetString', () => {
|
||||||
it.each(cases)('$input -> $expected', ({ input, expected }) => {
|
it.each(cases)('$input -> $expected', ({ input, expected }) => {
|
||||||
expect(parseTargetString(input)).toEqual(expected);
|
jest
|
||||||
|
.spyOn(splitTarget, 'splitTarget')
|
||||||
|
.mockReturnValueOnce(Object.values(expected) as [string]);
|
||||||
|
expect(parseTargetString(input, null)).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,19 +1,35 @@
|
|||||||
import type { Target } from 'nx/src/command-line/run';
|
import type { Target } from 'nx/src/command-line/run';
|
||||||
|
import { ProjectGraph } from 'nx/src/config/project-graph';
|
||||||
|
import { readCachedProjectGraph } from 'nx/src/project-graph/project-graph';
|
||||||
import { splitTarget } from 'nx/src/utils/split-target';
|
import { splitTarget } from 'nx/src/utils/split-target';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated(v17) A project graph should be passed to parseTargetString for best accuracy.
|
||||||
|
*/
|
||||||
|
export function parseTargetString(targetString: string): Target;
|
||||||
/**
|
/**
|
||||||
* Parses a target string into {project, target, configuration}
|
* Parses a target string into {project, target, configuration}
|
||||||
*
|
*
|
||||||
* Examples:
|
* Examples:
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* parseTargetString("proj:test") // returns { project: "proj", target: "test" }
|
* parseTargetString("proj:test", graph) // returns { project: "proj", target: "test" }
|
||||||
* parseTargetString("proj:test:production") // returns { project: "proj", target: "test", configuration: "production" }
|
* parseTargetString("proj:test:production", graph) // returns { project: "proj", target: "test", configuration: "production" }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param targetString - target reference
|
* @param targetString - target reference
|
||||||
*/
|
*/
|
||||||
export function parseTargetString(targetString: string): Target {
|
export function parseTargetString(
|
||||||
const [project, target, configuration] = splitTarget(targetString);
|
targetString: string,
|
||||||
|
projectGraph: ProjectGraph
|
||||||
|
): Target;
|
||||||
|
export function parseTargetString(
|
||||||
|
targetString: string,
|
||||||
|
projectGraph = readCachedProjectGraph()
|
||||||
|
): Target {
|
||||||
|
const [project, target, configuration] = splitTarget(
|
||||||
|
targetString,
|
||||||
|
projectGraph
|
||||||
|
);
|
||||||
if (!project || !target) {
|
if (!project || !target) {
|
||||||
throw new Error(`Invalid Target String: ${targetString}`);
|
throw new Error(`Invalid Target String: ${targetString}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,7 +66,7 @@ function calculateResolveMappings(
|
|||||||
context: ExecutorContext,
|
context: ExecutorContext,
|
||||||
options: NodeExecutorOptions
|
options: NodeExecutorOptions
|
||||||
) {
|
) {
|
||||||
const parsed = parseTargetString(options.buildTarget);
|
const parsed = parseTargetString(options.buildTarget, context.projectGraph);
|
||||||
const { dependencies } = calculateProjectDependencies(
|
const { dependencies } = calculateProjectDependencies(
|
||||||
context.projectGraph,
|
context.projectGraph,
|
||||||
context.root,
|
context.root,
|
||||||
@ -198,7 +198,10 @@ async function* startBuild(
|
|||||||
options: NodeExecutorOptions,
|
options: NodeExecutorOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
) {
|
) {
|
||||||
const buildTarget = parseTargetString(options.buildTarget);
|
const buildTarget = parseTargetString(
|
||||||
|
options.buildTarget,
|
||||||
|
context.projectGraph
|
||||||
|
);
|
||||||
|
|
||||||
yield* await runExecutor<ExecutorEvent>(
|
yield* await runExecutor<ExecutorEvent>(
|
||||||
buildTarget,
|
buildTarget,
|
||||||
@ -216,7 +219,7 @@ function runWaitUntilTargets(
|
|||||||
): Promise<{ success: boolean }[]> {
|
): Promise<{ success: boolean }[]> {
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
options.waitUntilTargets.map(async (waitUntilTarget) => {
|
options.waitUntilTargets.map(async (waitUntilTarget) => {
|
||||||
const target = parseTargetString(waitUntilTarget);
|
const target = parseTargetString(waitUntilTarget, context.projectGraph);
|
||||||
const output = await runExecutor(target, {}, context);
|
const output = await runExecutor(target, {}, context);
|
||||||
return new Promise<{ success: boolean }>(async (resolve) => {
|
return new Promise<{ success: boolean }>(async (resolve) => {
|
||||||
let event = await output.next();
|
let event = await output.next();
|
||||||
|
|||||||
@ -44,7 +44,7 @@ export default async function* serveExecutor(
|
|||||||
(process.env as any).PORT = options.port;
|
(process.env as any).PORT = options.port;
|
||||||
|
|
||||||
const buildOptions = readTargetOptions<NextBuildBuilderOptions>(
|
const buildOptions = readTargetOptions<NextBuildBuilderOptions>(
|
||||||
parseTargetString(options.buildTarget),
|
parseTargetString(options.buildTarget, context.projectGraph),
|
||||||
context
|
context
|
||||||
);
|
);
|
||||||
const root = resolve(context.root, buildOptions.root);
|
const root = resolve(context.root, buildOptions.root);
|
||||||
@ -155,7 +155,7 @@ async function* runCustomServer(
|
|||||||
const baseUrl = `http://${options.hostname || 'localhost'}:${options.port}`;
|
const baseUrl = `http://${options.hostname || 'localhost'}:${options.port}`;
|
||||||
|
|
||||||
const customServerBuild = await runExecutor(
|
const customServerBuild = await runExecutor(
|
||||||
parseTargetString(options.customServerTarget),
|
parseTargetString(options.customServerTarget, context.projectGraph),
|
||||||
{
|
{
|
||||||
watch: true,
|
watch: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { workspaceRoot } from '../utils/workspace-root';
|
|||||||
import { splitTarget } from '../utils/split-target';
|
import { splitTarget } from '../utils/split-target';
|
||||||
import { output } from '../utils/output';
|
import { output } from '../utils/output';
|
||||||
import {
|
import {
|
||||||
|
ProjectConfiguration,
|
||||||
ProjectsConfigurations,
|
ProjectsConfigurations,
|
||||||
TargetDependencyConfig,
|
TargetDependencyConfig,
|
||||||
} from '../config/workspace-json-project-json';
|
} from '../config/workspace-json-project-json';
|
||||||
@ -35,10 +36,7 @@ export async function runOne(
|
|||||||
const nxJson = readNxJson();
|
const nxJson = readNxJson();
|
||||||
const projectGraph = await createProjectGraphAsync({ exitOnError: true });
|
const projectGraph = await createProjectGraphAsync({ exitOnError: true });
|
||||||
|
|
||||||
const opts = parseRunOneOptions(cwd, args, {
|
const opts = parseRunOneOptions(cwd, args, projectGraph, nxJson);
|
||||||
...readProjectsConfigurationFromProjectGraph(projectGraph),
|
|
||||||
...nxJson,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { nxArgs, overrides } = splitArgsIntoNxArgsAndOverrides(
|
const { nxArgs, overrides } = splitArgsIntoNxArgsAndOverrides(
|
||||||
{
|
{
|
||||||
@ -102,13 +100,13 @@ const targetAliases = {
|
|||||||
function parseRunOneOptions(
|
function parseRunOneOptions(
|
||||||
cwd: string,
|
cwd: string,
|
||||||
parsedArgs: { [k: string]: any },
|
parsedArgs: { [k: string]: any },
|
||||||
workspaceConfiguration: ProjectsConfigurations & NxJsonConfiguration
|
projectGraph: ProjectGraph<ProjectConfiguration>,
|
||||||
|
nxJson: NxJsonConfiguration
|
||||||
): { project; target; configuration; parsedArgs } {
|
): { project; target; configuration; parsedArgs } {
|
||||||
const defaultProjectName = calculateDefaultProjectName(
|
const defaultProjectName = calculateDefaultProjectName(cwd, workspaceRoot, {
|
||||||
cwd,
|
...readProjectsConfigurationFromProjectGraph(projectGraph),
|
||||||
workspaceRoot,
|
...nxJson,
|
||||||
workspaceConfiguration
|
});
|
||||||
);
|
|
||||||
|
|
||||||
let project;
|
let project;
|
||||||
let target;
|
let target;
|
||||||
@ -117,7 +115,8 @@ function parseRunOneOptions(
|
|||||||
if (parsedArgs['project:target:configuration'].indexOf(':') > -1) {
|
if (parsedArgs['project:target:configuration'].indexOf(':') > -1) {
|
||||||
// run case
|
// run case
|
||||||
[project, target, configuration] = splitTarget(
|
[project, target, configuration] = splitTarget(
|
||||||
parsedArgs['project:target:configuration']
|
parsedArgs['project:target:configuration'],
|
||||||
|
projectGraph
|
||||||
);
|
);
|
||||||
// this is to account for "nx npmsript:dev"
|
// this is to account for "nx npmsript:dev"
|
||||||
if (project && !target && defaultProjectName) {
|
if (project && !target && defaultProjectName) {
|
||||||
|
|||||||
@ -1,14 +1,56 @@
|
|||||||
|
import { ProjectGraph } from '../config/project-graph';
|
||||||
|
import { ProjectGraphBuilder } from '../project-graph/project-graph-builder';
|
||||||
import { splitTarget } from './split-target';
|
import { splitTarget } from './split-target';
|
||||||
|
|
||||||
const cases = [
|
let projectGraph: ProjectGraph;
|
||||||
{ input: 'one', expected: ['one'] },
|
|
||||||
{ input: 'one:two', expected: ['one', 'two'] },
|
|
||||||
{ input: 'one:two:three', expected: ['one', 'two', 'three'] },
|
|
||||||
{ input: 'one:"two:two":three', expected: ['one', 'two:two', 'three'] },
|
|
||||||
];
|
|
||||||
|
|
||||||
describe('splitTarget', () => {
|
describe('splitTarget', () => {
|
||||||
it.each(cases)('$input -> $expected', ({ input, expected }) => {
|
beforeAll(() => {
|
||||||
expect(splitTarget(input)).toEqual(expected);
|
let builder = new ProjectGraphBuilder();
|
||||||
|
builder.addNode({
|
||||||
|
name: 'project',
|
||||||
|
data: {
|
||||||
|
files: [],
|
||||||
|
root: '',
|
||||||
|
targets: {
|
||||||
|
target: {},
|
||||||
|
'target:target': {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
type: 'app',
|
||||||
|
});
|
||||||
|
|
||||||
|
projectGraph = builder.getUpdatedProjectGraph();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support only project', () => {
|
||||||
|
expect(splitTarget('project', projectGraph)).toEqual(['project']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should project:target', () => {
|
||||||
|
expect(splitTarget('project:target', projectGraph)).toEqual([
|
||||||
|
'project',
|
||||||
|
'target',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should project:target:configuration', () => {
|
||||||
|
expect(splitTarget('project:target:configuration', projectGraph)).toEqual([
|
||||||
|
'project',
|
||||||
|
'target',
|
||||||
|
'configuration',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should targets that contain colons when present in the graph', () => {
|
||||||
|
expect(
|
||||||
|
splitTarget('project:target:target:configuration', projectGraph)
|
||||||
|
).toEqual(['project', 'target:target', 'configuration']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should targets that contain colons when not present in the graph but surrounded by quotes', () => {
|
||||||
|
expect(
|
||||||
|
splitTarget('project:"other:other":configuration', projectGraph)
|
||||||
|
).toEqual(['project', 'other:other', 'configuration']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,43 @@
|
|||||||
|
import { ProjectGraph } from '../config/project-graph';
|
||||||
|
import { ProjectConfiguration } from '../config/workspace-json-project-json';
|
||||||
|
|
||||||
export function splitTarget(
|
export function splitTarget(
|
||||||
s: string
|
s: string,
|
||||||
|
projectGraph: ProjectGraph<ProjectConfiguration>
|
||||||
): [project: string, target?: string, configuration?: string] {
|
): [project: string, target?: string, configuration?: string] {
|
||||||
|
let [project, ...segments] = splitByColons(s);
|
||||||
|
const validTargets = projectGraph.nodes[project]?.data?.targets;
|
||||||
|
const validTargetNames = new Set(Object.keys(validTargets ?? {}));
|
||||||
|
|
||||||
|
return [project, ...groupJointSegments(segments, validTargetNames)] as [
|
||||||
|
string,
|
||||||
|
string?,
|
||||||
|
string?
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupJointSegments(segments: string[], validTargetNames: Set<string>) {
|
||||||
|
for (
|
||||||
|
let endingSegmentIdx = segments.length;
|
||||||
|
endingSegmentIdx > 0;
|
||||||
|
endingSegmentIdx--
|
||||||
|
) {
|
||||||
|
const potentialTargetName = segments.slice(0, endingSegmentIdx).join(':');
|
||||||
|
if (validTargetNames.has(potentialTargetName)) {
|
||||||
|
const configurationName =
|
||||||
|
endingSegmentIdx < segments.length
|
||||||
|
? segments.slice(endingSegmentIdx).join(':')
|
||||||
|
: null;
|
||||||
|
return configurationName
|
||||||
|
? [potentialTargetName, configurationName]
|
||||||
|
: [potentialTargetName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we can't find a segment match, keep older behaviour
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitByColons(s: string) {
|
||||||
const parts = [] as string[];
|
const parts = [] as string[];
|
||||||
let currentPart = '';
|
let currentPart = '';
|
||||||
for (let i = 0; i < s.length; ++i) {
|
for (let i = 0; i < s.length; ++i) {
|
||||||
@ -17,5 +54,5 @@ export function splitTarget(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
parts.push(currentPart);
|
parts.push(currentPart);
|
||||||
return parts as [string, string?, string?];
|
return parts as [string, ...string[]];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -166,7 +166,7 @@ function buildTargetWebpack(
|
|||||||
buildTarget: string,
|
buildTarget: string,
|
||||||
componentTestingProjectName: string
|
componentTestingProjectName: string
|
||||||
) {
|
) {
|
||||||
const parsed = parseTargetString(buildTarget);
|
const parsed = parseTargetString(buildTarget, graph);
|
||||||
|
|
||||||
const buildableProjectConfig = graph.nodes[parsed.project]?.data;
|
const buildableProjectConfig = graph.nodes[parsed.project]?.data;
|
||||||
const ctProjectConfig = graph.nodes[componentTestingProjectName]?.data;
|
const ctProjectConfig = graph.nodes[componentTestingProjectName]?.data;
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
|
createProjectGraphAsync,
|
||||||
ensurePackage,
|
ensurePackage,
|
||||||
generateFiles,
|
generateFiles,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
logger,
|
logger,
|
||||||
parseTargetString,
|
parseTargetString,
|
||||||
ProjectConfiguration,
|
ProjectConfiguration,
|
||||||
|
readCachedProjectGraph,
|
||||||
readProjectConfiguration,
|
readProjectConfiguration,
|
||||||
Tree,
|
Tree,
|
||||||
visitNotIgnoredFiles,
|
visitNotIgnoredFiles,
|
||||||
@ -33,7 +35,7 @@ export async function addFiles(
|
|||||||
tree.delete(cypressConfigPath);
|
tree.delete(cypressConfigPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
const actualBundler = getBundler(found, tree);
|
const actualBundler = await getBundler(found, tree);
|
||||||
|
|
||||||
if (options.bundler && options.bundler !== actualBundler) {
|
if (options.bundler && options.bundler !== actualBundler) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
@ -76,12 +78,18 @@ export async function addFiles(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBundler(found: FoundTarget, tree: Tree): 'vite' | 'webpack' {
|
async function getBundler(
|
||||||
|
found: FoundTarget,
|
||||||
|
tree: Tree
|
||||||
|
): Promise<'vite' | 'webpack'> {
|
||||||
if (found.target && found.config?.executor) {
|
if (found.target && found.config?.executor) {
|
||||||
return found.config.executor === '@nrwl/vite:build' ? 'vite' : 'webpack';
|
return found.config.executor === '@nrwl/vite:build' ? 'vite' : 'webpack';
|
||||||
}
|
}
|
||||||
|
|
||||||
const { target, project } = parseTargetString(found.target);
|
const { target, project } = parseTargetString(
|
||||||
|
found.target,
|
||||||
|
await createProjectGraphAsync()
|
||||||
|
);
|
||||||
const projectConfig = readProjectConfiguration(tree, project);
|
const projectConfig = readProjectConfiguration(tree, project);
|
||||||
return projectConfig?.targets?.[target]?.executor === '@nrwl/vite:build'
|
return projectConfig?.targets?.[target]?.executor === '@nrwl/vite:build'
|
||||||
? 'vite'
|
? 'vite'
|
||||||
|
|||||||
@ -107,7 +107,7 @@ export async function configurationGenerator(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const e2eProject = getE2EProjectName(tree, schema.name);
|
const e2eProject = await getE2EProjectName(tree, schema.name);
|
||||||
if (schema.configureCypress && !e2eProject) {
|
if (schema.configureCypress && !e2eProject) {
|
||||||
const cypressTask = await cypressProjectGenerator(tree, {
|
const cypressTask = await cypressProjectGenerator(tree, {
|
||||||
name: schema.name,
|
name: schema.name,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
|
createProjectGraphAsync,
|
||||||
generateFiles,
|
generateFiles,
|
||||||
getProjects,
|
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
logger,
|
logger,
|
||||||
offsetFromRoot,
|
offsetFromRoot,
|
||||||
@ -537,11 +537,12 @@ export function rootFileIsTs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getE2EProjectName(
|
export async function getE2EProjectName(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
mainProject: string
|
mainProject: string
|
||||||
): string | undefined {
|
): Promise<string | undefined> {
|
||||||
let e2eProject: string;
|
let e2eProject: string;
|
||||||
|
const graph = await createProjectGraphAsync();
|
||||||
forEachExecutorOptions(
|
forEachExecutorOptions(
|
||||||
tree,
|
tree,
|
||||||
'@nrwl/cypress:cypress',
|
'@nrwl/cypress:cypress',
|
||||||
@ -551,7 +552,8 @@ export function getE2EProjectName(
|
|||||||
}
|
}
|
||||||
if (options['devServerTarget']) {
|
if (options['devServerTarget']) {
|
||||||
const { project, target } = parseTargetString(
|
const { project, target } = parseTargetString(
|
||||||
options['devServerTarget']
|
options['devServerTarget'],
|
||||||
|
graph
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
(project === mainProject && target === 'serve') ||
|
(project === mainProject && target === 'serve') ||
|
||||||
|
|||||||
@ -97,7 +97,7 @@ export function getBuildTargetOptions(
|
|||||||
buildTarget: string,
|
buildTarget: string,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
) {
|
) {
|
||||||
const target = parseTargetString(buildTarget);
|
const target = parseTargetString(buildTarget, context.projectGraph);
|
||||||
return readTargetOptions(target, context);
|
return readTargetOptions(target, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -65,7 +65,7 @@ function getBuildTargetOutputPath(options: Schema, context: ExecutorContext) {
|
|||||||
|
|
||||||
let buildOptions;
|
let buildOptions;
|
||||||
try {
|
try {
|
||||||
const target = parseTargetString(options.buildTarget);
|
const target = parseTargetString(options.buildTarget, context.projectGraph);
|
||||||
buildOptions = readTargetOptions(target, context);
|
buildOptions = readTargetOptions(target, context);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Invalid buildTarget: ${options.buildTarget}`);
|
throw new Error(`Invalid buildTarget: ${options.buildTarget}`);
|
||||||
|
|||||||
@ -93,7 +93,7 @@ function getBuildOptions(
|
|||||||
options: WebDevServerOptions,
|
options: WebDevServerOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
): WebpackExecutorOptions {
|
): WebpackExecutorOptions {
|
||||||
const target = parseTargetString(options.buildTarget);
|
const target = parseTargetString(options.buildTarget, context.projectGraph);
|
||||||
|
|
||||||
const overrides: Partial<WebpackExecutorOptions> = {
|
const overrides: Partial<WebpackExecutorOptions> = {
|
||||||
watch: false,
|
watch: false,
|
||||||
|
|||||||
@ -15,8 +15,14 @@ export async function* ssrDevServerExecutor(
|
|||||||
options: WebSsrDevServerOptions,
|
options: WebSsrDevServerOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
) {
|
) {
|
||||||
const browserTarget = parseTargetString(options.browserTarget);
|
const browserTarget = parseTargetString(
|
||||||
const serverTarget = parseTargetString(options.serverTarget);
|
options.browserTarget,
|
||||||
|
context.projectGraph
|
||||||
|
);
|
||||||
|
const serverTarget = parseTargetString(
|
||||||
|
options.serverTarget,
|
||||||
|
context.projectGraph
|
||||||
|
);
|
||||||
const browserOptions = readTargetOptions<WebpackExecutorOptions>(
|
const browserOptions = readTargetOptions<WebpackExecutorOptions>(
|
||||||
browserTarget,
|
browserTarget,
|
||||||
context
|
context
|
||||||
|
|||||||
@ -65,9 +65,8 @@ describe('checkTargets', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if another project targets', async () => {
|
it('should throw an error if another project targets', async () => {
|
||||||
expect(() => {
|
await expect(checkTargets(tree, schema)).rejects
|
||||||
checkTargets(tree, schema);
|
.toThrowErrorMatchingInlineSnapshot(`
|
||||||
}).toThrowErrorMatchingInlineSnapshot(`
|
|
||||||
"ng-app is still targeted by some projects:
|
"ng-app is still targeted by some projects:
|
||||||
|
|
||||||
\\"ng-app:serve\\" is used by \\"ng-app-e2e\\"
|
\\"ng-app:serve\\" is used by \\"ng-app-e2e\\"
|
||||||
@ -78,25 +77,19 @@ describe('checkTargets', () => {
|
|||||||
it('should NOT throw an error if no other project targets', async () => {
|
it('should NOT throw an error if no other project targets', async () => {
|
||||||
schema.projectName = 'ng-app-e2e';
|
schema.projectName = 'ng-app-e2e';
|
||||||
|
|
||||||
expect(() => {
|
await expect(checkTargets(tree, schema)).resolves.toBeUndefined();
|
||||||
checkTargets(tree, schema);
|
|
||||||
}).not.toThrow();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should NOT throw an error if it is a nrwl package', async () => {
|
it('should NOT throw an error if it is a nrwl package', async () => {
|
||||||
schema.projectName = 'storybook';
|
schema.projectName = 'storybook';
|
||||||
|
|
||||||
expect(() => {
|
await expect(checkTargets(tree, schema)).resolves.toBeUndefined();
|
||||||
checkTargets(tree, schema);
|
|
||||||
}).not.toThrow();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not error if forceRemove is true', async () => {
|
it('should not error if forceRemove is true', async () => {
|
||||||
schema.forceRemove = true;
|
schema.forceRemove = true;
|
||||||
|
|
||||||
expect(() => {
|
await expect(checkTargets(tree, schema)).resolves.toBeUndefined();
|
||||||
checkTargets(tree, schema);
|
|
||||||
}).not.toThrow();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('use project in other project target', () => {
|
describe('use project in other project target', () => {
|
||||||
@ -122,9 +115,7 @@ describe('checkTargets', () => {
|
|||||||
it('should throw an error since it is used as a target in another project', async () => {
|
it('should throw an error since it is used as a target in another project', async () => {
|
||||||
schema.projectName = 'storybook';
|
schema.projectName = 'storybook';
|
||||||
|
|
||||||
expect(() => {
|
await expect(checkTargets(tree, schema)).rejects.toThrow();
|
||||||
checkTargets(tree, schema);
|
|
||||||
}).toThrow();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
createProjectGraphAsync,
|
||||||
getProjects,
|
getProjects,
|
||||||
parseTargetString,
|
parseTargetString,
|
||||||
TargetConfiguration,
|
TargetConfiguration,
|
||||||
@ -13,11 +14,12 @@ import { Schema } from '../schema';
|
|||||||
*
|
*
|
||||||
* @param schema The options provided to the schematic
|
* @param schema The options provided to the schematic
|
||||||
*/
|
*/
|
||||||
export function checkTargets(tree: Tree, schema: Schema) {
|
export async function checkTargets(tree: Tree, schema: Schema) {
|
||||||
if (schema.forceRemove) {
|
if (schema.forceRemove) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const graph = await createProjectGraphAsync();
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
|
|
||||||
getProjects(tree).forEach((projectConfig, projectName) => {
|
getProjects(tree).forEach((projectConfig, projectName) => {
|
||||||
@ -27,7 +29,7 @@ export function checkTargets(tree: Tree, schema: Schema) {
|
|||||||
Object.entries(projectConfig.targets || {}).forEach(([, targetConfig]) => {
|
Object.entries(projectConfig.targets || {}).forEach(([, targetConfig]) => {
|
||||||
checkIfProjectIsUsed(targetConfig, (value) => {
|
checkIfProjectIsUsed(targetConfig, (value) => {
|
||||||
try {
|
try {
|
||||||
const { project } = parseTargetString(value);
|
const { project } = parseTargetString(value, graph);
|
||||||
if (project === schema.projectName) {
|
if (project === schema.projectName) {
|
||||||
errors.push(`"${value}" is used by "${projectName}"`);
|
errors.push(`"${value}" is used by "${projectName}"`);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import { updateJestConfig } from './lib/update-jest-config';
|
|||||||
export async function removeGenerator(tree: Tree, schema: Schema) {
|
export async function removeGenerator(tree: Tree, schema: Schema) {
|
||||||
const project = readProjectConfiguration(tree, schema.projectName);
|
const project = readProjectConfiguration(tree, schema.projectName);
|
||||||
await checkDependencies(tree, schema);
|
await checkDependencies(tree, schema);
|
||||||
checkTargets(tree, schema);
|
await checkTargets(tree, schema);
|
||||||
updateJestConfig(tree, schema, project);
|
updateJestConfig(tree, schema, project);
|
||||||
removeProjectConfig(tree, schema);
|
removeProjectConfig(tree, schema);
|
||||||
removeProject(tree, project);
|
removeProject(tree, project);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user