Skip to content

Commit ec342ae

Browse files
authored
feat(core): support for workspace solution files (#254)
1 parent dbff13c commit ec342ae

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+580
-273
lines changed

.vscode/extensions.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"recommendations": [
3-
"ms-vscode.vscode-typescript-tslint-plugin",
43
"esbenp.prettier-vscode",
54
"firsttris.vscode-jest-runner",
65
"mike-co.import-sorter"

apps/docs-site/docusaurus.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ module.exports = {
8585
contextualSearch: false,
8686
appId: 'BH4D9OD16A',
8787
},
88+
prism: {
89+
additionalLanguages: ['json5', 'typescript', 'bash'],
90+
},
8891
},
8992
presets: [
9093
[

apps/docs-site/project.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
"outputPath": "dist/apps/docs-site"
1010
}
1111
},
12+
"prebuild": {
13+
"executor": "@nrwl/workspace:run-commands",
14+
"options": {
15+
"commands": ["echo"]
16+
}
17+
},
1218
"serve": {
1319
"executor": "@nx-plus/docusaurus:dev-server",
1420
"options": {

docs/core/generators/application.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,9 @@ Generate a dotnet project under the application directory.
3737
### standalone
3838

3939
- (boolean): Should the project use project.json? If false, the project config is inside workspace.json
40+
41+
### solutionFile
42+
43+
- (string): The name of the solution file to add the project to
44+
45+
- (boolean): Should the project be added to the default solution file?

docs/core/generators/library.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,9 @@ Generate a dotnet project under the library directory.
3333
### standalone
3434

3535
- (boolean): Should the project use project.json? If false, the project config is inside workspace.json
36+
37+
### solutionFile
38+
39+
- (string): The name of the solution file to add the project to
40+
41+
- (boolean): Should the project be added to the default solution file?

docs/core/generators/test.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,9 @@ Generate a .NET test project for an existing application or library
2929
### standalone
3030

3131
- (boolean): Should the project use project.json? If false, the project config is inside workspace.json
32+
33+
### solutionFile
34+
35+
- (string): The name of the solution file to add the project to
36+
37+
- (boolean): Should the project be added to the default solution file?

docs/core/guides/_category_.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
label: 'Guides'
2+
position: 1
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Handling Solution Files
2+
3+
## Workspace Level
4+
5+
As of v1.7.0, `nx-dotnet` supports adding projects to a workspace level solution file automatically. When generating an app, lib, or test project you can pass `--solutionFile` to add the project to the default solution at the workspace root. Alternatively, you can pass `--solutionFile {path/to/sln}` to add the project to a custom solution file. This should look something like:
6+
7+
```bash
8+
npx nx g @nx-dotnet/core:app my-api --template webapi --solutionFile MyCompany.sln
9+
```
10+
11+
To add projects to a solution file by default, you can set the generator defaults in [nx.json](https://nx.dev/l/a/core-concepts/configuration#nxjson) as below:
12+
13+
```json5
14+
{
15+
// ... more nx.json configuration
16+
generators: {
17+
// ... other default configurations
18+
'@nx-dotnet/core:app': {
19+
solutionFile: true,
20+
},
21+
},
22+
}
23+
```
24+
25+
## Subgraph Solutions
26+
27+
In a large monorepo, IDEs or other tooling may slow down when presented with a large solution file. Currently, `nx-dotnet` does not assist in managing this issue, but there are a few easy steps to take that can help optimize your workflow. Which path you take will depend on both the tooling you use, and the pains that you are enountering.
28+
29+
Either of the two approaches listed below could be expanded on in the future, but currently are not in the scope of the nx-plugin.
30+
31+
### Separate solution files
32+
33+
One option would be totally separated solution files for project graphs that are not connected. The main thing to be cautious with in an approach like this, is that if the dependency graph changes and the two subgraphs become connected it would be possible to make changes that break a project not currently visible to the IDE. For example, lets say you have 3 projects `A`, `B`, and `Shared`. If `A` and `B` both depend on `Shared`, and you have separate solutions each containing either `A` or `B` alongside the `Shared` project, a developer could modify the code in `Shared` and break the project that was not included in the opened solution file. As such, any solution that contained `Shared` _must_ contain all projects that depend on it to maintain good DX.
34+
35+
### Solution filters
36+
37+
Some IDEs such as Visual Studio support solution filters. These filters would allow for all projects to be visible to the IDE, but can have some performance benefits. The caveats to using separate files can still exist though, but these could be easier to maintain in the long run. Here is a link to the [msdn docs for solution filters](https://docs.microsoft.com/en-us/visualstudio/ide/filtered-solutions?view=vs-2022).

e2e/core-e2e/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"e2e": {
77
"executor": "@nrwl/nx-plugin:e2e",
88
"options": {
9-
"target": "core:build",
9+
"target": "core:noop",
1010
"jestConfig": "e2e/core-e2e/jest.config.js"
1111
}
1212
}

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

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
import {
2-
joinPathFragments,
3-
names,
4-
WorkspaceJsonConfiguration,
5-
} from '@nrwl/devkit';
1+
import { joinPathFragments, names } from '@nrwl/devkit';
62
import {
73
checkFilesExist,
84
ensureNxProject,
5+
listFiles,
96
readFile,
10-
readJson,
117
runNxCommandAsync,
128
uniq,
139
} from '@nrwl/nx-plugin/testing';
@@ -20,6 +16,7 @@ import { findProjectFileInPathSync } from '@nx-dotnet/utils';
2016
import { readDependenciesFromNxDepGraph } from '@nx-dotnet/utils/e2e';
2117
import { execSync } from 'child_process';
2218
import { ensureDirSync } from 'fs-extra';
19+
import { Workspaces } from '@nrwl/tao/src/shared/workspace';
2320

2421
const e2eDir = 'tmp/nx-e2e/proj';
2522

@@ -234,7 +231,7 @@ describe('nx-dotnet e2e', () => {
234231

235232
await runNxCommandAsync(`generate @nx-dotnet/core:import-projects`);
236233

237-
const workspace = readJson<WorkspaceJsonConfiguration>('workspace.json');
234+
const workspace = new Workspaces(e2eDir).readWorkspaceConfiguration();
238235

239236
expect(workspace.projects[testApp].targets?.serve).toBeDefined();
240237
expect(workspace.projects[testApp].targets?.build).toBeDefined();
@@ -250,4 +247,62 @@ describe('nx-dotnet e2e', () => {
250247
checkFilesExist(`dist/apps/${testApp}`);
251248
});
252249
});
250+
251+
describe('solution handling', () => {
252+
// For solution handling, defaults fall back to if a file exists.
253+
// This ensures that the tests are ran in a clean state, without previous
254+
// test projects interfering with the test.
255+
beforeEach(() => {
256+
ensureNxProject('@nx-dotnet/core', 'dist/packages/core');
257+
}, 1500000);
258+
259+
it("shouldn't create a solution by default if not specified", async () => {
260+
const app = uniq('app');
261+
await runNxCommandAsync(
262+
`generate @nx-dotnet/core:app ${app} --language="C#" --template="webapi"`,
263+
);
264+
265+
expect(() => checkFilesExist(`apps/${app}`)).not.toThrow();
266+
expect(listFiles('.').filter((x) => x.endsWith('.sln'))).toHaveLength(0);
267+
});
268+
269+
it('should create a default solution file if specified as true', async () => {
270+
const app = uniq('app');
271+
await runNxCommandAsync(
272+
`generate @nx-dotnet/core:app ${app} --language="C#" --template="webapi" --solutionFile`,
273+
);
274+
275+
expect(() => checkFilesExist(`apps/${app}`)).not.toThrow();
276+
expect(listFiles('.').filter((x) => x.endsWith('.sln'))).toHaveLength(1);
277+
});
278+
279+
it('should create specified solution file if specified as string', async () => {
280+
const app = uniq('app');
281+
await runNxCommandAsync(
282+
`generate @nx-dotnet/core:app ${app} --language="C#" --template="webapi" --solutionFile="MyCompany.sln"`,
283+
);
284+
285+
expect(() =>
286+
checkFilesExist(`apps/${app}`, `MyCompany.sln`),
287+
).not.toThrow();
288+
});
289+
290+
it('should add successive projects to default solution file', async () => {
291+
const app1 = uniq('app');
292+
await runNxCommandAsync(
293+
`generate @nx-dotnet/core:app ${app1} --language="C#" --template="webapi" --solutionFile`,
294+
);
295+
296+
const app2 = uniq('app2');
297+
await runNxCommandAsync(
298+
`generate @nx-dotnet/core:app ${app2} --language="C#" --template="webapi" --solutionFile`,
299+
);
300+
301+
const slnFile = readFile('proj.nx-dotnet.sln');
302+
303+
expect(() => checkFilesExist(`apps/${app1}`)).not.toThrow();
304+
expect(slnFile).toContain(app1);
305+
expect(slnFile).toContain(app2);
306+
});
307+
});
253308
});

0 commit comments

Comments
 (0)