Skip to content

Commit c57fbd3

Browse files
authored
feat(core): adding templates with default output path properties to init generator (#526)
1 parent b79fdde commit c57fbd3

22 files changed

+176
-112
lines changed

docs/core/generators/application.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,6 @@ Generate a dotnet project under the application directory.
3030

3131
- (string): Which template should be used for creating the tests project?
3232

33-
### skipOutputPathManipulation
34-
35-
- (boolean): Skip XML changes for default build path
36-
3733
### standalone
3834

3935
- (boolean): Should the project use project.json? If false, the project config is inside workspace.json

docs/core/generators/test.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ Generate a .NET test project for an existing application or library
2222

2323
- (string): What suffix should be used for the tests project name?
2424

25-
### skipOutputPathManipulation
26-
27-
- (boolean): Skip XML changes for default build path
28-
2925
### standalone
3026

3127
- (boolean): Should the project use project.json? If false, the project config is inside workspace.json

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

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,14 @@ import {
1717
} from '@nrwl/nx-plugin/testing';
1818
import { runCommandUntil } from '../../utils';
1919

20-
import { readFileSync, unlinkSync, writeFileSync } from 'fs';
20+
import { unlinkSync, writeFileSync } from 'fs';
2121
import { join } from 'path';
2222
import { XmlDocument } from 'xmldoc';
2323

24-
import { findProjectFileInPathSync } from '@nx-dotnet/utils';
2524
import { readDependenciesFromNxDepGraph } from '@nx-dotnet/utils/e2e';
2625
import { exec, execSync } from 'child_process';
2726
import { ensureDirSync } from 'fs-extra';
2827
import { Workspaces } from '@nrwl/tao/src/shared/workspace';
29-
import { PackageJson } from 'nx/src/utils/package-json';
3028

3129
const e2eDir = tmpProjPath();
3230

@@ -36,6 +34,14 @@ describe('nx-dotnet e2e', () => {
3634
initializeGitRepo(e2eDir);
3735
}, 1500000);
3836

37+
it('should initialize workspace build customization', async () => {
38+
await runNxCommandAsync(`generate @nx-dotnet/core:init`);
39+
40+
expect(() =>
41+
checkFilesExist('Directory.Build.props', 'Directory.Build.targets'),
42+
).not.toThrow();
43+
});
44+
3945
it('should create apps, libs, and project references', async () => {
4046
const testApp = uniq('app');
4147
const testLib = uniq('lib');
@@ -145,22 +151,6 @@ describe('nx-dotnet e2e', () => {
145151
expect(() => checkFilesExist(`dist/libs/${lib}`)).not.toThrow();
146152
});
147153

148-
it('should update output paths', async () => {
149-
const app = uniq('app');
150-
await runNxCommandAsync(
151-
`generate @nx-dotnet/core:app ${app} --language="C#" --template="webapi" --skip-swagger-lib`,
152-
);
153-
const configFilePath = findProjectFileInPathSync(
154-
join(e2eDir, 'apps', app),
155-
);
156-
const config = readFileSync(configFilePath).toString();
157-
const projectXml = new XmlDocument(config);
158-
const outputPath = projectXml
159-
.childNamed('PropertyGroup')
160-
?.childNamed('OutputPath')?.val as string;
161-
expect(outputPath).toBeTruthy();
162-
});
163-
164154
it('should lint', async () => {
165155
const app = uniq('app');
166156
await runNxCommandAsync(

packages/core/src/generators/app/generator.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ describe('nx-dotnet app generator', () => {
1818
language: 'C#',
1919
template: 'webapi',
2020
testTemplate: 'none',
21-
skipOutputPathManipulation: false,
2221
projectType: 'application',
2322
standalone: false,
2423
skipSwaggerLib: true,

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,6 @@
5555
]
5656
}
5757
},
58-
"skipOutputPathManipulation": {
59-
"type": "boolean",
60-
"description": "Skip XML changes for default build path",
61-
"default": false
62-
},
6358
"standalone": {
6459
"type": "boolean",
6560
"description": "Should the project use project.json? If false, the project config is inside workspace.json"

packages/core/src/generators/import-projects/generator.spec.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
33

44
import * as fs from 'fs';
55

6+
import { DotNetClient, mockDotnetFactory } from '@nx-dotnet/dotnet';
67
import * as utils from '@nx-dotnet/utils';
78

9+
import * as mockedInitGenerator from '../init/generator';
810
import generator from './generator';
911

1012
jest.mock('@nx-dotnet/utils', () => ({
@@ -34,20 +36,28 @@ const MOCK_TEST_PROJECT = `
3436
</ItemGroup>
3537
</Project>`;
3638

39+
jest.mock('../init/generator', () => ({
40+
initGenerator: jest.fn(() => {
41+
return Promise.resolve(jest.fn(() => Promise.resolve()));
42+
}),
43+
}));
44+
3745
describe('import-projects generator', () => {
3846
let appTree: Tree;
47+
let dotnetClient: DotNetClient;
3948

4049
beforeEach(() => {
4150
appTree = createTreeWithEmptyWorkspace();
51+
dotnetClient = new DotNetClient(mockDotnetFactory());
4252
});
4353

4454
afterEach(() => {
45-
jest.resetAllMocks();
55+
jest.clearAllMocks();
4656
});
4757

4858
it('should run successfully if no new projects are found', async () => {
4959
jest.spyOn(utils, 'glob').mockResolvedValue([]);
50-
const promise = generator(appTree);
60+
const promise = generator(appTree, dotnetClient);
5161
const oldProjects = getProjects(appTree);
5262
await expect(promise).resolves.not.toThrow();
5363
const newProjects = getProjects(appTree);
@@ -72,7 +82,7 @@ describe('import-projects generator', () => {
7282
jest.spyOn(fs, 'readFileSync').mockReturnValue(MOCK_TEST_PROJECT);
7383
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => null);
7484
appTree.write('apps/my-api/my-api.csproj', MOCK_API_PROJECT);
75-
const promise = generator(appTree);
85+
const promise = generator(appTree, dotnetClient);
7686
await expect(promise).resolves.not.toThrow();
7787
expect(readProjectConfiguration(appTree, 'my-test-api')).toBeDefined();
7888
});
@@ -95,7 +105,7 @@ describe('import-projects generator', () => {
95105
jest.spyOn(fs, 'readFileSync').mockReturnValue(MOCK_TEST_PROJECT);
96106
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => null);
97107
appTree.write('apps/my-api-test/my-api-test.csproj', MOCK_TEST_PROJECT);
98-
const promise = generator(appTree);
108+
const promise = generator(appTree, dotnetClient);
99109
await expect(promise).resolves.not.toThrow();
100110
expect(readProjectConfiguration(appTree, 'my-test-api-test')).toBeDefined();
101111
expect(
@@ -105,4 +115,14 @@ describe('import-projects generator', () => {
105115
readProjectConfiguration(appTree, 'my-test-api-test').targets?.serve,
106116
).not.toBeDefined();
107117
});
118+
119+
it('should call init generator', async () => {
120+
const initGenerator = (
121+
mockedInitGenerator as jest.Mocked<typeof mockedInitGenerator>
122+
).initGenerator;
123+
124+
jest.spyOn(utils, 'glob').mockResolvedValue([]);
125+
await generator(appTree, dotnetClient);
126+
expect(initGenerator).toHaveBeenCalledWith(appTree, null, dotnetClient);
127+
});
108128
});

packages/core/src/generators/import-projects/generator.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { basename, dirname } from 'path';
1414
import { XmlDocument } from 'xmldoc';
1515

16+
import { DotNetClient, dotnetFactory } from '@nx-dotnet/dotnet';
1617
import { glob, iterateChildrenByPath, projPattern } from '@nx-dotnet/utils';
1718

1819
import {
@@ -21,9 +22,14 @@ import {
2122
GetServeExecutorConfig,
2223
GetTestExecutorConfig,
2324
} from '../../models';
24-
import { manipulateXmlProjectFile } from '../utils/generate-project';
25+
import { initGenerator } from '../init/generator';
26+
27+
export default async function (
28+
host: Tree,
29+
dotnetClient = new DotNetClient(dotnetFactory()),
30+
) {
31+
const installTask = await initGenerator(host, null, dotnetClient);
2532

26-
export default async function (host: Tree) {
2733
const projectFiles = await getProjectFilesInWorkspace(host);
2834
const existingProjectRoots = Array.from(getProjects(host).values()).map(
2935
(x) => x.root,
@@ -40,7 +46,10 @@ export default async function (host: Tree) {
4046
logger.log('Found new application', projectFile);
4147
}
4248
}
43-
return formatFiles(host);
49+
return async () => {
50+
await installTask();
51+
await formatFiles(host);
52+
};
4453
}
4554

4655
async function addNewDotnetProject(
@@ -71,10 +80,6 @@ async function addNewDotnetProject(
7180
configuration.targets.test = GetTestExecutorConfig();
7281
}
7382
addProjectConfiguration(host, projectName, configuration);
74-
await manipulateXmlProjectFile(host, {
75-
projectName,
76-
projectRoot,
77-
});
7883
}
7984

8085
async function getProjectFilesInWorkspace(host: Tree) {

packages/core/src/generators/init/generator.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as devkit from '@nrwl/devkit';
12
import { readJson, Tree, writeJson } from '@nrwl/devkit';
23
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
34

@@ -6,6 +7,11 @@ import { CONFIG_FILE_PATH, NxDotnetConfig } from '@nx-dotnet/utils';
67

78
import generator from './generator';
89

10+
jest.mock('@nx-dotnet/utils', () => ({
11+
...jest.requireActual('@nx-dotnet/utils'),
12+
resolve: jest.fn(() => 'check-module-boundaries.js'),
13+
}));
14+
915
describe('init generator', () => {
1016
let appTree: Tree;
1117
let dotnetClient: DotNetClient;
@@ -77,4 +83,27 @@ describe('init generator', () => {
7783
'npm run clean && npm run build && nx g @nx-dotnet/core:restore',
7884
);
7985
});
86+
87+
it('should add directory build props and targets files', async () => {
88+
await generator(appTree, null, dotnetClient);
89+
const hasPropsFile = appTree.isFile('Directory.Build.props');
90+
expect(hasPropsFile).toBeTruthy();
91+
92+
const hasTargetsFile = appTree.isFile('Directory.Build.targets');
93+
expect(hasTargetsFile).toBeTruthy();
94+
const hasPreBuildTask = appTree
95+
.read('Directory.Build.targets', 'utf-8')
96+
?.includes('check-module-boundaries.js');
97+
expect(hasPreBuildTask).toBeTruthy();
98+
});
99+
100+
it('should not add directory build props and targets files if props file exists', async () => {
101+
appTree.write('Directory.Build.props', '');
102+
const spy = jest.spyOn(devkit, 'generateFiles');
103+
await generator(appTree, null, dotnetClient);
104+
105+
expect(spy).not.toHaveBeenCalled();
106+
const hasTargetsFile = appTree.isFile('Directory.Build.targets');
107+
expect(hasTargetsFile).toBeFalsy();
108+
});
80109
});

packages/core/src/generators/init/generator.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
addDependenciesToPackageJson,
3+
generateFiles,
34
GeneratorCallback,
45
logger,
56
NxJsonConfiguration,
@@ -11,8 +12,15 @@ import {
1112
} from '@nrwl/devkit';
1213

1314
import { DotNetClient, dotnetFactory } from '@nx-dotnet/dotnet';
14-
import { CONFIG_FILE_PATH, isDryRun, NxDotnetConfig } from '@nx-dotnet/utils';
15+
import {
16+
CONFIG_FILE_PATH,
17+
isDryRun,
18+
NxDotnetConfig,
19+
resolve,
20+
} from '@nx-dotnet/utils';
1521
import type { PackageJson } from 'nx/src/utils/package-json';
22+
import * as path from 'path';
23+
import { normalize, relative } from 'path';
1624

1725
export async function initGenerator(
1826
host: Tree,
@@ -42,6 +50,8 @@ export async function initGenerator(
4250

4351
initToolManifest(host, dotnetClient);
4452

53+
initBuildCustomization(host);
54+
4555
return async () => {
4656
for (const task of tasks) {
4757
await task();
@@ -119,3 +129,20 @@ function addPrepareScript(host: Tree) {
119129
packageJson.scripts.prepare = prepareSteps.join(' && ');
120130
writeJson(host, 'package.json', packageJson);
121131
}
132+
133+
function initBuildCustomization(host: Tree) {
134+
const initialized = host.exists('Directory.Build.props');
135+
if (!initialized) {
136+
const checkModuleBoundariesScriptPath = normalize(
137+
relative(
138+
host.root,
139+
resolve('@nx-dotnet/core/src/tasks/check-module-boundaries'),
140+
),
141+
);
142+
143+
generateFiles(host, path.join(__dirname, 'templates/root'), '.', {
144+
tmpl: '',
145+
checkModuleBoundariesScriptPath,
146+
});
147+
}
148+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!--
2+
This file is imported early in the build order.
3+
Use it to set default property values that can be overridden in specific projects.
4+
-->
5+
<Project>
6+
<PropertyGroup>
7+
<!-- Output path configuration -->
8+
<RepoRoot>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))</RepoRoot>
9+
<ProjectRelativePath>$([System.IO.Path]::GetRelativePath($(RepoRoot), $(MSBuildProjectDirectory)))</ProjectRelativePath>
10+
<BaseOutputPath>$(RepoRoot)dist/$(ProjectRelativePath)</BaseOutputPath>
11+
<OutputPath>$(BaseOutputPath)</OutputPath>
12+
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
13+
</PropertyGroup>
14+
<PropertyGroup>
15+
<RestorePackagesWithLockFile>false</RestorePackagesWithLockFile>
16+
</PropertyGroup>
17+
</Project>

0 commit comments

Comments
 (0)