Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(angular): handle newProjectRoot correctly when generating apps and libs #9255

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -159,19 +159,17 @@ describe('app', () => {
expect(appE2eSpec).toContain('Welcome my-app-with-prefix');
});

// TODO: this should work
// This has been carried over from the Angular Devkit Schematic
// It seems like Jest is failing as it's trying to look for the
// tsconfig in the incorrect place
xit('should work if the new project root is changed', async () => {
it('should work if the new project root is changed', async () => {
// ARRANGE
updateJson(appTree, '/workspace.json', (json) => ({
...json,
newProjectRoot: 'newProjectRoot',
}));

// ACT
await generateApp(appTree);
await generateApp(appTree, 'my-app', {
e2eTestRunner: E2eTestRunner.Protractor,
});

// ASSERT
expect(appTree.exists('apps/my-app/src/main.ts')).toEqual(true);
Expand Down
28 changes: 9 additions & 19 deletions packages/angular/src/generators/application/application.ts
@@ -1,9 +1,7 @@
import {
formatFiles,
getWorkspacePath,
installPackagesTask,
moveFilesToNewDirectory,
readJson,
Tree,
} from '@nrwl/devkit';
import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter';
Expand Down Expand Up @@ -36,20 +34,6 @@ export async function applicationGenerator(
) {
const options = normalizeOptions(host, schema);

// Determine the roots where @schematics/angular will place the projects
// This is not where the projects actually end up
const workspaceJsonPath = getWorkspacePath(host);
let newProjectRoot = null;
if (workspaceJsonPath) {
({ newProjectRoot } = readJson(host, workspaceJsonPath));
}
const appProjectRoot = newProjectRoot
? `${newProjectRoot}/${options.name}`
: options.name;
const e2eProjectRoot = newProjectRoot
? `${newProjectRoot}/${options.e2eProjectName}`
: `${options.name}/e2e`;

await angularInitGenerator(host, {
...options,
skipFormat: true,
Expand All @@ -72,9 +56,15 @@ export async function applicationGenerator(
skipPackageJson: options.skipPackageJson,
});

createFiles(host, options, appProjectRoot);
if (options.ngCliSchematicAppRoot !== options.appProjectRoot) {
moveFilesToNewDirectory(
host,
options.ngCliSchematicAppRoot,
options.appProjectRoot
);
}

moveFilesToNewDirectory(host, appProjectRoot, options.appProjectRoot);
createFiles(host, options);
updateConfigFiles(host, options);
updateAppComponentTemplate(host, options);

Expand Down Expand Up @@ -114,7 +104,7 @@ export async function applicationGenerator(

addLinting(host, options);
await addUnitTestRunner(host, options);
await addE2e(host, options, e2eProjectRoot);
await addE2e(host, options);
updateEditorTsConfig(host, options);

if (options.backendProject) {
Expand Down
17 changes: 2 additions & 15 deletions packages/angular/src/generators/application/lib/add-e2e.ts
Expand Up @@ -12,24 +12,11 @@ import { convertToNxProjectGenerator } from '@nrwl/workspace';
import { Linter, lintProjectGenerator } from '@nrwl/linter';
import { getWorkspaceLayout, joinPathFragments } from '@nrwl/devkit';

/**
* Add E2E Config
*
* @param tree Nx Devkit Virtual Tree
* @param options Normalized Schema
* @param e2eProjectRoot Raw E2E Project Root that Angular tries to write to
*
* @returns Function to run to add Cypres config after intial app files have been moved to correct location
*/
export async function addE2e(
tree: Tree,
options: NormalizedSchema,
e2eProjectRoot: string
) {
export async function addE2e(tree: Tree, options: NormalizedSchema) {
if (options.e2eTestRunner === E2eTestRunner.Protractor) {
await addProtractor(tree, options);
} else {
removeScaffoldedE2e(tree, options, e2eProjectRoot);
removeScaffoldedE2e(tree, options, options.ngCliSchematicE2ERoot);
}

if (options.e2eTestRunner === 'cypress') {
Expand Down
Expand Up @@ -3,13 +3,7 @@ import { generateFiles, joinPathFragments } from '@nrwl/devkit';
import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript';
import type { NormalizedSchema } from './normalized-schema';

export function createFiles(
host: Tree,
options: NormalizedSchema,
appProjectRoot: string
) {
host.delete(`${appProjectRoot}/src/favicon.ico`);

export function createFiles(host: Tree, options: NormalizedSchema) {
generateFiles(
host,
joinPathFragments(__dirname, '../files'),
Expand Down
@@ -1,4 +1,9 @@
import { joinPathFragments, Tree } from '@nrwl/devkit';
import {
getWorkspacePath,
joinPathFragments,
readJson,
Tree,
} from '@nrwl/devkit';
import type { Schema } from '../schema';
import type { NormalizedSchema } from './normalized-schema';

Expand Down Expand Up @@ -35,6 +40,20 @@ export function normalizeOptions(

options.standaloneConfig = options.standaloneConfig ?? standaloneAsDefault;

// Determine the roots where @schematics/angular will place the projects
// This might not be where the projects actually end up
const workspaceJsonPath = getWorkspacePath(host);
let newProjectRoot = null;
if (workspaceJsonPath) {
({ newProjectRoot } = readJson(host, workspaceJsonPath));
}
const ngCliSchematicAppRoot = newProjectRoot
? `${newProjectRoot}/${appProjectName}`
: appProjectName;
const ngCliSchematicE2ERoot = newProjectRoot
? `${newProjectRoot}/${e2eProjectName}`
: `${appProjectName}/e2e`;

// Set defaults and then overwrite with user options
return {
style: 'css',
Expand All @@ -54,5 +73,7 @@ export function normalizeOptions(
e2eProjectRoot,
e2eProjectName,
parsedTags,
ngCliSchematicAppRoot,
ngCliSchematicE2ERoot,
};
}
Expand Up @@ -10,4 +10,6 @@ export interface NormalizedSchema extends Schema {
e2eProjectName: string;
e2eProjectRoot: string;
parsedTags: string[];
ngCliSchematicAppRoot: string;
ngCliSchematicE2ERoot: string;
}
Expand Up @@ -38,42 +38,44 @@ function updateAppAndE2EProjectConfigurations(
options: NormalizedSchema
) {
// workspace.json
const project = readProjectConfiguration(host, options.name);
let project = readProjectConfiguration(host, options.name);

let fixedProject = replaceAppNameWithPath(
project,
options.name,
options.appProjectRoot
);
if (options.ngCliSchematicAppRoot !== options.appProjectRoot) {
project = replaceAppNameWithPath(
project,
options.ngCliSchematicAppRoot,
options.appProjectRoot
);
}

delete fixedProject.targets.test;
delete project.targets.test;

// Ensure the outputs property comes after the executor for
// better readability.
const { executor, ...rest } = fixedProject.targets.build;
fixedProject.targets.build = {
const { executor, ...rest } = project.targets.build;
project.targets.build = {
executor,
outputs: ['{options.outputPath}'],
...rest,
};

if (fixedProject.generators) {
delete fixedProject.generators;
if (project.generators) {
delete project.generators;
}

if (options.port) {
fixedProject.targets.serve = {
...fixedProject.targets.serve,
project.targets.serve = {
...project.targets.serve,
options: {
...fixedProject.targets.serve.options,
...project.targets.serve.options,
port: options.port,
},
};
}

fixedProject.tags = options.parsedTags;
project.tags = options.parsedTags;

updateProjectConfiguration(host, options.name, fixedProject);
updateProjectConfiguration(host, options.name, project);

if (options.unitTestRunner === UnitTestRunner.None) {
host.delete(`${options.appProjectRoot}/src/app/app.component.spec.ts`);
Expand Down
@@ -1,4 +1,10 @@
import { getWorkspaceLayout, joinPathFragments, Tree } from '@nrwl/devkit';
import {
getWorkspaceLayout,
getWorkspacePath,
joinPathFragments,
readJson,
Tree,
} from '@nrwl/devkit';
import { Schema } from '../schema';
import { NormalizedSchema } from './normalized-schema';
import { names } from '@nrwl/devkit';
Expand Down Expand Up @@ -51,6 +57,17 @@ export function normalizeOptions(
const importPath =
options.importPath || `@${defaultPrefix}/${projectDirectory}`;

// Determine the roots where @schematics/angular will place the projects
// This might not be where the projects actually end up
const workspaceJsonPath = getWorkspacePath(host);
let newProjectRoot = null;
if (workspaceJsonPath) {
({ newProjectRoot } = readJson(host, workspaceJsonPath));
}
const ngCliSchematicLibRoot = newProjectRoot
? `${newProjectRoot}/${projectName}`
: projectName;

return {
...options,
linter: options.linter ?? Linter.EsLint,
Expand All @@ -65,5 +82,6 @@ export function normalizeOptions(
parsedTags,
fileName,
importPath,
ngCliSchematicLibRoot,
};
}
Expand Up @@ -12,4 +12,5 @@ export interface NormalizedSchema extends Schema {
moduleName: string;
projectDirectory: string;
parsedTags: string[];
ngCliSchematicLibRoot: string;
}
24 changes: 13 additions & 11 deletions packages/angular/src/generators/library/lib/update-project.ts
Expand Up @@ -129,23 +129,25 @@ function createFiles(host: Tree, options: NormalizedSchema) {
}

function fixProjectWorkspaceConfig(host: Tree, options: NormalizedSchema) {
const project = readProjectConfiguration(host, options.name);
let project = readProjectConfiguration(host, options.name);
project.tags = options.parsedTags;

const fixedProject = replaceAppNameWithPath(
project,
options.name,
options.projectRoot
);
if (options.ngCliSchematicLibRoot !== options.projectRoot) {
project = replaceAppNameWithPath(
project,
options.ngCliSchematicLibRoot,
options.projectRoot
);
}

if (!options.publishable && !options.buildable) {
delete fixedProject.targets.build;
delete project.targets.build;
} else {
// Set the right builder for the type of library.
// Ensure the outputs property comes after the builder for
// better readability.
const { executor, ...rest } = fixedProject.targets.build;
fixedProject.targets.build = {
const { executor, ...rest } = project.targets.build;
project.targets.build = {
executor: options.publishable
? '@nrwl/angular:package'
: '@nrwl/angular:ng-packagr-lite',
Expand All @@ -160,9 +162,9 @@ function fixProjectWorkspaceConfig(host: Tree, options: NormalizedSchema) {
};
}

delete fixedProject.targets.test;
delete project.targets.test;

updateProjectConfiguration(host, options.name, fixedProject);
updateProjectConfiguration(host, options.name, project);
}

function updateProjectTsConfig(host: Tree, options: NormalizedSchema) {
Expand Down
25 changes: 25 additions & 0 deletions packages/angular/src/generators/library/library.spec.ts
Expand Up @@ -457,6 +457,31 @@ describe('lib', () => {
).toBeFalsy();
});

it('should work if the new project root is changed', async () => {
// ARRANGE
updateJson(tree, 'workspace.json', (json) => ({
...json,
newProjectRoot: 'newProjectRoot',
}));

// ACT
await runLibraryGeneratorWithOpts();

// ASSERT
expect(tree.exists('libs/my-lib/src/index.ts')).toBeTruthy();
expect(tree.exists('libs/my-lib/src/lib/my-lib.module.ts')).toBeTruthy();
expect(
tree.exists('libs/my-lib/src/lib/my-lib.component.ts')
).toBeFalsy();
expect(
tree.exists('libs/my-lib/src/lib/my-lib.component.spec.ts')
).toBeFalsy();
expect(tree.exists('libs/my-lib/src/lib/my-lib.service.ts')).toBeFalsy();
expect(
tree.exists('libs/my-lib/src/lib/my-lib.service.spec.ts')
).toBeFalsy();
});

it('should default the prefix to npmScope', async () => {
// ACT
await runLibraryGeneratorWithOpts();
Expand Down
8 changes: 7 additions & 1 deletion packages/angular/src/generators/library/library.ts
Expand Up @@ -63,7 +63,13 @@ export async function libraryGenerator(host: Tree, schema: Partial<Schema>) {
skipTsConfig: true,
});

moveFilesToNewDirectory(host, options.name, options.projectRoot);
if (options.ngCliSchematicLibRoot !== options.projectRoot) {
moveFilesToNewDirectory(
host,
options.ngCliSchematicLibRoot,
options.projectRoot
);
}
await updateProject(host, options);
updateTsConfig(host, options);
await addUnitTestRunner(host, options);
Expand Down
4 changes: 3 additions & 1 deletion packages/devkit/src/generators/project-configuration.ts
Expand Up @@ -189,7 +189,9 @@ export function updateWorkspaceConfiguration(
...json,
version: workspaceConfig.version,
};
delete (config as any).newProjectRoot;
if (!(workspaceConfig as any).newProjectRoot) {
delete (config as any).newProjectRoot;
}
return config;
});
}
Expand Down
Expand Up @@ -2,12 +2,12 @@

exports[`preset should create files (preset = angular) 1`] = `
Array [
"src",
"tsconfig.editor.json",
"tsconfig.json",
".browserslistrc",
"tsconfig.app.json",
"tsconfig.spec.json",
"src",
"tsconfig.editor.json",
"tsconfig.json",
".eslintrc.json",
"jest.config.js",
]
Expand Down