Skip to content

Commit

Permalink
feat(core): adding templates with default output path properties to i…
Browse files Browse the repository at this point in the history
…nit generator (#526)
  • Loading branch information
tzuge authored Sep 26, 2022
1 parent b79fdde commit c57fbd3
Show file tree
Hide file tree
Showing 22 changed files with 176 additions and 112 deletions.
4 changes: 0 additions & 4 deletions docs/core/generators/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ Generate a dotnet project under the application directory.

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

### skipOutputPathManipulation

- (boolean): Skip XML changes for default build path

### standalone

- (boolean): Should the project use project.json? If false, the project config is inside workspace.json
Expand Down
4 changes: 0 additions & 4 deletions docs/core/generators/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ Generate a .NET test project for an existing application or library

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

### skipOutputPathManipulation

- (boolean): Skip XML changes for default build path

### standalone

- (boolean): Should the project use project.json? If false, the project config is inside workspace.json
Expand Down
28 changes: 9 additions & 19 deletions e2e/core-e2e/tests/nx-dotnet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,14 @@ import {
} from '@nrwl/nx-plugin/testing';
import { runCommandUntil } from '../../utils';

import { readFileSync, unlinkSync, writeFileSync } from 'fs';
import { unlinkSync, writeFileSync } from 'fs';
import { join } from 'path';
import { XmlDocument } from 'xmldoc';

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

const e2eDir = tmpProjPath();

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

it('should initialize workspace build customization', async () => {
await runNxCommandAsync(`generate @nx-dotnet/core:init`);

expect(() =>
checkFilesExist('Directory.Build.props', 'Directory.Build.targets'),
).not.toThrow();
});

it('should create apps, libs, and project references', async () => {
const testApp = uniq('app');
const testLib = uniq('lib');
Expand Down Expand Up @@ -145,22 +151,6 @@ describe('nx-dotnet e2e', () => {
expect(() => checkFilesExist(`dist/libs/${lib}`)).not.toThrow();
});

it('should update output paths', async () => {
const app = uniq('app');
await runNxCommandAsync(
`generate @nx-dotnet/core:app ${app} --language="C#" --template="webapi" --skip-swagger-lib`,
);
const configFilePath = findProjectFileInPathSync(
join(e2eDir, 'apps', app),
);
const config = readFileSync(configFilePath).toString();
const projectXml = new XmlDocument(config);
const outputPath = projectXml
.childNamed('PropertyGroup')
?.childNamed('OutputPath')?.val as string;
expect(outputPath).toBeTruthy();
});

it('should lint', async () => {
const app = uniq('app');
await runNxCommandAsync(
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/generators/app/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ describe('nx-dotnet app generator', () => {
language: 'C#',
template: 'webapi',
testTemplate: 'none',
skipOutputPathManipulation: false,
projectType: 'application',
standalone: false,
skipSwaggerLib: true,
Expand Down
5 changes: 0 additions & 5 deletions packages/core/src/generators/app/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@
]
}
},
"skipOutputPathManipulation": {
"type": "boolean",
"description": "Skip XML changes for default build path",
"default": false
},
"standalone": {
"type": "boolean",
"description": "Should the project use project.json? If false, the project config is inside workspace.json"
Expand Down
28 changes: 24 additions & 4 deletions packages/core/src/generators/import-projects/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';

import * as fs from 'fs';

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

import * as mockedInitGenerator from '../init/generator';
import generator from './generator';

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

jest.mock('../init/generator', () => ({
initGenerator: jest.fn(() => {
return Promise.resolve(jest.fn(() => Promise.resolve()));
}),
}));

describe('import-projects generator', () => {
let appTree: Tree;
let dotnetClient: DotNetClient;

beforeEach(() => {
appTree = createTreeWithEmptyWorkspace();
dotnetClient = new DotNetClient(mockDotnetFactory());
});

afterEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
});

it('should run successfully if no new projects are found', async () => {
jest.spyOn(utils, 'glob').mockResolvedValue([]);
const promise = generator(appTree);
const promise = generator(appTree, dotnetClient);
const oldProjects = getProjects(appTree);
await expect(promise).resolves.not.toThrow();
const newProjects = getProjects(appTree);
Expand All @@ -72,7 +82,7 @@ describe('import-projects generator', () => {
jest.spyOn(fs, 'readFileSync').mockReturnValue(MOCK_TEST_PROJECT);
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => null);
appTree.write('apps/my-api/my-api.csproj', MOCK_API_PROJECT);
const promise = generator(appTree);
const promise = generator(appTree, dotnetClient);
await expect(promise).resolves.not.toThrow();
expect(readProjectConfiguration(appTree, 'my-test-api')).toBeDefined();
});
Expand All @@ -95,7 +105,7 @@ describe('import-projects generator', () => {
jest.spyOn(fs, 'readFileSync').mockReturnValue(MOCK_TEST_PROJECT);
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => null);
appTree.write('apps/my-api-test/my-api-test.csproj', MOCK_TEST_PROJECT);
const promise = generator(appTree);
const promise = generator(appTree, dotnetClient);
await expect(promise).resolves.not.toThrow();
expect(readProjectConfiguration(appTree, 'my-test-api-test')).toBeDefined();
expect(
Expand All @@ -105,4 +115,14 @@ describe('import-projects generator', () => {
readProjectConfiguration(appTree, 'my-test-api-test').targets?.serve,
).not.toBeDefined();
});

it('should call init generator', async () => {
const initGenerator = (
mockedInitGenerator as jest.Mocked<typeof mockedInitGenerator>
).initGenerator;

jest.spyOn(utils, 'glob').mockResolvedValue([]);
await generator(appTree, dotnetClient);
expect(initGenerator).toHaveBeenCalledWith(appTree, null, dotnetClient);
});
});
19 changes: 12 additions & 7 deletions packages/core/src/generators/import-projects/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { basename, dirname } from 'path';
import { XmlDocument } from 'xmldoc';

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

import {
Expand All @@ -21,9 +22,14 @@ import {
GetServeExecutorConfig,
GetTestExecutorConfig,
} from '../../models';
import { manipulateXmlProjectFile } from '../utils/generate-project';
import { initGenerator } from '../init/generator';

export default async function (
host: Tree,
dotnetClient = new DotNetClient(dotnetFactory()),
) {
const installTask = await initGenerator(host, null, dotnetClient);

export default async function (host: Tree) {
const projectFiles = await getProjectFilesInWorkspace(host);
const existingProjectRoots = Array.from(getProjects(host).values()).map(
(x) => x.root,
Expand All @@ -40,7 +46,10 @@ export default async function (host: Tree) {
logger.log('Found new application', projectFile);
}
}
return formatFiles(host);
return async () => {
await installTask();
await formatFiles(host);
};
}

async function addNewDotnetProject(
Expand Down Expand Up @@ -71,10 +80,6 @@ async function addNewDotnetProject(
configuration.targets.test = GetTestExecutorConfig();
}
addProjectConfiguration(host, projectName, configuration);
await manipulateXmlProjectFile(host, {
projectName,
projectRoot,
});
}

async function getProjectFilesInWorkspace(host: Tree) {
Expand Down
29 changes: 29 additions & 0 deletions packages/core/src/generators/init/generator.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as devkit from '@nrwl/devkit';
import { readJson, Tree, writeJson } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';

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

import generator from './generator';

jest.mock('@nx-dotnet/utils', () => ({
...jest.requireActual('@nx-dotnet/utils'),
resolve: jest.fn(() => 'check-module-boundaries.js'),
}));

describe('init generator', () => {
let appTree: Tree;
let dotnetClient: DotNetClient;
Expand Down Expand Up @@ -77,4 +83,27 @@ describe('init generator', () => {
'npm run clean && npm run build && nx g @nx-dotnet/core:restore',
);
});

it('should add directory build props and targets files', async () => {
await generator(appTree, null, dotnetClient);
const hasPropsFile = appTree.isFile('Directory.Build.props');
expect(hasPropsFile).toBeTruthy();

const hasTargetsFile = appTree.isFile('Directory.Build.targets');
expect(hasTargetsFile).toBeTruthy();
const hasPreBuildTask = appTree
.read('Directory.Build.targets', 'utf-8')
?.includes('check-module-boundaries.js');
expect(hasPreBuildTask).toBeTruthy();
});

it('should not add directory build props and targets files if props file exists', async () => {
appTree.write('Directory.Build.props', '');
const spy = jest.spyOn(devkit, 'generateFiles');
await generator(appTree, null, dotnetClient);

expect(spy).not.toHaveBeenCalled();
const hasTargetsFile = appTree.isFile('Directory.Build.targets');
expect(hasTargetsFile).toBeFalsy();
});
});
29 changes: 28 additions & 1 deletion packages/core/src/generators/init/generator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
addDependenciesToPackageJson,
generateFiles,
GeneratorCallback,
logger,
NxJsonConfiguration,
Expand All @@ -11,8 +12,15 @@ import {
} from '@nrwl/devkit';

import { DotNetClient, dotnetFactory } from '@nx-dotnet/dotnet';
import { CONFIG_FILE_PATH, isDryRun, NxDotnetConfig } from '@nx-dotnet/utils';
import {
CONFIG_FILE_PATH,
isDryRun,
NxDotnetConfig,
resolve,
} from '@nx-dotnet/utils';
import type { PackageJson } from 'nx/src/utils/package-json';
import * as path from 'path';
import { normalize, relative } from 'path';

export async function initGenerator(
host: Tree,
Expand Down Expand Up @@ -42,6 +50,8 @@ export async function initGenerator(

initToolManifest(host, dotnetClient);

initBuildCustomization(host);

return async () => {
for (const task of tasks) {
await task();
Expand Down Expand Up @@ -119,3 +129,20 @@ function addPrepareScript(host: Tree) {
packageJson.scripts.prepare = prepareSteps.join(' && ');
writeJson(host, 'package.json', packageJson);
}

function initBuildCustomization(host: Tree) {
const initialized = host.exists('Directory.Build.props');
if (!initialized) {
const checkModuleBoundariesScriptPath = normalize(
relative(
host.root,
resolve('@nx-dotnet/core/src/tasks/check-module-boundaries'),
),
);

generateFiles(host, path.join(__dirname, 'templates/root'), '.', {
tmpl: '',
checkModuleBoundariesScriptPath,
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!--
This file is imported early in the build order.
Use it to set default property values that can be overridden in specific projects.
-->
<Project>
<PropertyGroup>
<!-- Output path configuration -->
<RepoRoot>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))</RepoRoot>
<ProjectRelativePath>$([System.IO.Path]::GetRelativePath($(RepoRoot), $(MSBuildProjectDirectory)))</ProjectRelativePath>
<BaseOutputPath>$(RepoRoot)dist/$(ProjectRelativePath)</BaseOutputPath>
<OutputPath>$(BaseOutputPath)</OutputPath>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup>
<RestorePackagesWithLockFile>false</RestorePackagesWithLockFile>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!--
This file is imported late in the build order.
Use it to override properties and define dependent properties.
-->
<Project>
<PropertyGroup>
<MSBuildProjectDirRelativePath>$([System.IO.Path]::GetRelativePath($(RepoRoot), $(MSBuildProjectDirectory)))</MSBuildProjectDirRelativePath>
<NodeModulesRelativePath>$([System.IO.Path]::GetRelativePath($(MSBuildProjectDirectory), $(RepoRoot)))</NodeModulesRelativePath>
</PropertyGroup>
<Target Name="CheckNxModuleBoundaries" BeforeTargets="Build">
<Exec Command="node $(NodeModulesRelativePath)/<%= checkModuleBoundariesScriptPath %> --project-root $(MSBuildProjectDirRelativePath)"/>
</Target>
</Project>
1 change: 0 additions & 1 deletion packages/core/src/generators/lib/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ describe('nx-dotnet library generator', () => {
language: 'C#',
template: 'classlib',
testTemplate: 'none',
skipOutputPathManipulation: true,
standalone: false,
projectType: 'library',
skipSwaggerLib: true,
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/generators/test/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ describe('nx-dotnet test generator', () => {
name: 'existing',
testTemplate: 'xunit',
language: 'C#',
skipOutputPathManipulation: true,
standalone: false,
pathScheme: 'nx',
};
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/generators/test/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export default async function (
testProjectNameSuffix: options.suffix,
name,
language: options.language,
skipOutputPathManipulation: options.skipOutputPathManipulation,
testTemplate: options.testTemplate,
directory,
tags: project.tags?.join(','),
Expand Down
5 changes: 0 additions & 5 deletions packages/core/src/generators/test/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,6 @@
"type": "string"
}
},
"skipOutputPathManipulation": {
"type": "boolean",
"description": "Skip XML changes for default build path",
"default": false
},
"standalone": {
"type": "boolean",
"description": "Should the project use project.json? If false, the project config is inside workspace.json"
Expand Down
Loading

0 comments on commit c57fbd3

Please sign in to comment.