Skip to content
This repository was archived by the owner on Apr 27, 2026. It is now read-only.

Commit cdc3654

Browse files
authored
feat(core): add argument forwarding to dotnet new (#722)
1 parent 63cf4b4 commit cdc3654

8 files changed

Lines changed: 86 additions & 7 deletions

File tree

docs/core/generators/application.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,11 @@ Generate a dotnet project under the application directory.
4747
### useNxPluginOpenAPI
4848

4949
- (boolean): If using a codegen project, use openapi-generator
50+
51+
### args
52+
53+
- (array): Additional arguments to pass to the dotnet command. For example: "nx g @nx-dotnet/core:app myapp --args='--no-restore'" Arguments can also be appended to the end of the command using '--'. For example, 'nx g @nx-dotnet/core:app myapp -- --no-restore'.
54+
55+
### **unparsed**
56+
57+
- (array):

e2e/core-e2e/tests/nx-dotnet.spec.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,17 @@ import {
1515
uniq,
1616
updateFile,
1717
} from '@nrwl/nx-plugin/testing';
18-
import { runCommandUntil } from '../../utils';
18+
import { Workspaces } from '@nrwl/tao/src/shared/workspace';
1919

20+
import { exec, execSync } from 'child_process';
2021
import { unlinkSync, writeFileSync } from 'fs';
22+
import { ensureDirSync } from 'fs-extra';
2123
import { join } from 'path';
2224
import { XmlDocument } from 'xmldoc';
2325

2426
import { readDependenciesFromNxDepGraph } from '@nx-dotnet/utils/e2e';
25-
import { exec, execSync } from 'child_process';
26-
import { ensureDirSync } from 'fs-extra';
27-
import { Workspaces } from '@nrwl/tao/src/shared/workspace';
27+
28+
import { runCommandUntil } from '../../utils';
2829

2930
const e2eDir = tmpProjPath();
3031

@@ -117,6 +118,18 @@ describe('nx-dotnet e2e', () => {
117118
).toThrow();
118119
});
119120

121+
it('should generate an app without launchSettings.json', async () => {
122+
const app = uniq('app');
123+
await runNxCommandAsync(
124+
`generate @nx-dotnet/core:app ${app} --language="C#" --template="webapi" --args="--exclude-launch-settings=true"`,
125+
);
126+
127+
expect(() => checkFilesExist(`apps/${app}`)).not.toThrow();
128+
expect(() =>
129+
checkFilesExist(`apps/${app}/Properties/launchSettings.json`),
130+
).toThrow();
131+
});
132+
120133
it('should build and test an app', async () => {
121134
const app = uniq('app');
122135
const testProj = `${app}-test`;

packages/core/src/generators/app/schema.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,27 @@
9090
"useNxPluginOpenAPI": {
9191
"type": "boolean",
9292
"description": "If using a codegen project, use openapi-generator"
93+
},
94+
"args": {
95+
"type": "array",
96+
"description": "Additional arguments to pass to the dotnet command. For example: \"nx g @nx-dotnet/core:app myapp --args='--no-restore'\" Arguments can also be appended to the end of the command using '--'. For example, 'nx g @nx-dotnet/core:app myapp -- --no-restore'.",
97+
"items": {
98+
"type": "string"
99+
},
100+
"default": []
101+
},
102+
"__unparsed__": {
103+
"hidden": true,
104+
"type": "array",
105+
"items": {
106+
"type": "string"
107+
},
108+
"$default": {
109+
"$source": "unparsed"
110+
},
111+
"x-priority": "internal"
93112
}
94113
},
114+
"additionalProperties": true,
95115
"required": ["name", "language"]
96116
}

packages/core/src/generators/utils/generate-project.spec.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import { readProjectConfiguration, Tree, writeJson } from '@nrwl/devkit';
22
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
33

44
import { DotNetClient, mockDotnetFactory } from '@nx-dotnet/dotnet';
5-
import path = require('path');
65

76
import { NxDotnetProjectGeneratorSchema } from '../../models';
87
import { GenerateProject } from './generate-project';
98
import * as mockedGenerateTestProject from './generate-test-project';
109

10+
import path = require('path');
11+
1112
// eslint-disable-next-line @typescript-eslint/no-empty-function
1213
jest.spyOn(console, 'log').mockImplementation(() => {});
1314

@@ -38,6 +39,8 @@ describe('nx-dotnet project generator', () => {
3839
skipSwaggerLib: true,
3940
projectType: 'application',
4041
pathScheme: 'nx',
42+
__unparsed__: [],
43+
args: [],
4144
};
4245

4346
jest.spyOn(dotnetClient, 'listInstalledTemplates').mockReturnValue([
@@ -126,6 +129,18 @@ describe('nx-dotnet project generator', () => {
126129
expect(nameFlag).toBe('Proj.SubDir.Test');
127130
});
128131

132+
it('should forward args to dotnet new', async () => {
133+
options.__unparsed__ = ['--foo', 'bar'];
134+
options.args = ['--help'];
135+
const spy = jest.spyOn(dotnetClient, 'new');
136+
await GenerateProject(appTree, options, dotnetClient, 'library');
137+
const [, , additionalArguments] = spy.mock.calls[spy.mock.calls.length - 1];
138+
expect(additionalArguments).toEqual(
139+
expect.arrayContaining(['--help', '--foo', 'bar']),
140+
);
141+
expect(additionalArguments).toHaveLength(3);
142+
});
143+
129144
describe('swagger library', () => {
130145
it('should generate swagger backed project', async () => {
131146
options.name = 'api';

packages/core/src/generators/utils/generate-project.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export interface NormalizedSchema
4646
namespaceName: string;
4747
nxProjectName: string;
4848
projectType?: ProjectType;
49+
args: string[];
50+
__unparsed__: string[];
4951
}
5052

5153
export async function normalizeOptions(
@@ -70,6 +72,8 @@ export async function normalizeOptions(
7072
const template = await getTemplate(options, client);
7173
const namespaceName = getNamespaceFromSchema(host, options, projectDirectory);
7274
const nxProjectName = names(options.name).fileName;
75+
const __unparsed__ = options.__unparsed__ || [];
76+
const args = options.args || [];
7377

7478
return {
7579
...options,
@@ -83,6 +87,8 @@ export async function normalizeOptions(
8387
projectTemplate: template as KnownDotnetTemplates,
8488
namespaceName,
8589
nxProjectName,
90+
args,
91+
__unparsed__,
8692
projectType: projectType ?? options.projectType ?? 'library',
8793
};
8894
}
@@ -209,11 +215,19 @@ export async function GenerateProject(
209215
output: normalizedOptions.projectRoot,
210216
};
211217

218+
const additionalArguments = normalizedOptions.args.concat(
219+
normalizedOptions.__unparsed__,
220+
);
221+
212222
if (isDryRun()) {
213223
newParams['dryRun'] = true;
214224
}
215225

216-
dotnetClient.new(normalizedOptions.projectTemplate, newParams);
226+
dotnetClient.new(
227+
normalizedOptions.projectTemplate,
228+
newParams,
229+
additionalArguments,
230+
);
217231
if (!isDryRun()) {
218232
addToSolutionFile(
219233
host,

packages/core/src/generators/utils/generate-test-project.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ describe('nx-dotnet test project generator', () => {
9393
namespaceName: 'Domain.ExistingApp',
9494
nxProjectName: 'domain-existing-app',
9595
pathScheme: 'nx',
96+
args: [],
97+
__unparsed__: [],
9698
};
9799
testProjectName = options.name + '-test';
98100
});

packages/core/src/models/project-generator-schema.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,6 @@ export interface NxDotnetProjectGeneratorSchema {
1616
skipSwaggerLib: boolean;
1717
pathScheme: 'nx' | 'dotnet';
1818
useNxPluginOpenAPI?: boolean;
19+
args?: string[];
20+
__unparsed__?: string[];
1921
}

packages/dotnet/src/lib/core/dotnet.client.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,17 @@ import { LoadedCLI } from './dotnet.factory';
2727
export class DotNetClient {
2828
constructor(private cliCommand: LoadedCLI, public cwd?: string) {}
2929

30-
new(template: KnownDotnetTemplates, parameters?: dotnetNewOptions): void {
30+
new(
31+
template: KnownDotnetTemplates,
32+
parameters?: dotnetNewOptions,
33+
additionalArguments?: string[],
34+
): void {
3135
const params = [`new`, template];
3236
if (parameters) {
3337
parameters = swapKeysUsingMap(parameters, newKeyMap);
3438
params.push(...getSpawnParameterArray(parameters));
3539
}
40+
params.push(...(additionalArguments || []));
3641
return this.logAndExecute(params);
3742
}
3843

0 commit comments

Comments
 (0)