cleanup(angular): update component generator to be more consistent with similar generators (#16078)

This commit is contained in:
Leosvel Pérez Espinosa 2023-04-04 10:12:57 +01:00 committed by GitHub
parent e71d0156b1
commit 29f5326b80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 173 additions and 144 deletions

View File

@ -52,7 +52,7 @@
"alias": "t"
},
"standalone": {
"description": "Whether the generated component is standalone. _Note: This is only supported in Angular versions >= 14.1.0_",
"description": "Whether the generated component is standalone. _Note: This is only supported in Angular versions >= 14.1.0_.",
"type": "boolean",
"default": false,
"x-priority": "important"

View File

@ -1,11 +1,12 @@
import { NormalizedSchema } from './normalized-schema';
import type { ProjectConfiguration, Tree } from '@nrwl/devkit';
import type { Tree } from '@nrwl/devkit';
import { addProjectConfiguration } from '@nrwl/devkit';
import type { AngularProjectConfiguration } from '../../../utils/types';
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
import type { NormalizedSchema } from './normalized-schema';
export function createProject(tree: Tree, options: NormalizedSchema) {
const installedAngularInfo = getInstalledAngularVersionInfo(tree);
const project: ProjectConfiguration & { prefix: string } = {
const project: AngularProjectConfiguration = {
name: options.name,
projectType: 'application',
prefix: options.prefix,

View File

@ -1,5 +1,5 @@
import componentGenerator from './component';
import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage';
import { convertNxGenerator } from '@nrwl/devkit';
import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage';
import { componentGenerator } from './component';
export default warnForSchematicUsage(convertNxGenerator(componentGenerator));

View File

@ -5,7 +5,7 @@ import {
writeJson,
} from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import componentGenerator from './component';
import { componentGenerator } from './component';
describe('component Generator', () => {
it('should create the component correctly and export it in the entry point when "export=true"', async () => {
@ -686,7 +686,7 @@ describe('component Generator', () => {
standalone: true,
})
).rejects
.toThrow(stripIndents`The "standalone" option is only supported in Angular >= 14.1.0. You are currently using 14.0.0.
.toThrow(stripIndents`The "standalone" option is only supported in Angular >= 14.1.0. You are currently using "14.0.0".
You can resolve this error by removing the "standalone" option or by migrating to Angular 14.1.0.`);
});
});

View File

@ -4,32 +4,19 @@ import {
generateFiles,
joinPathFragments,
names,
readNxJson,
stripIndents,
} from '@nrwl/devkit';
import { lt } from 'semver';
import { checkPathUnderProjectRoot } from '../utils/path';
import { getInstalledAngularVersionInfo } from '../utils/version-utils';
import { exportComponentInEntryPoint } from './lib/component';
import { normalizeOptions } from './lib/normalize-options';
import type { Schema } from './schema';
import { addToNgModule } from '../utils';
import { findModuleFromOptions } from './lib/module';
import {
exportComponentInEntryPoint,
findModuleFromOptions,
normalizeOptions,
validateOptions,
} from './lib';
import type { Schema } from './schema';
export async function componentGenerator(tree: Tree, rawOptions: Schema) {
const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree);
if (
lt(installedAngularVersionInfo.version, '14.1.0') &&
rawOptions.standalone
) {
throw new Error(stripIndents`The "standalone" option is only supported in Angular >= 14.1.0. You are currently using ${installedAngularVersionInfo.version}.
You can resolve this error by removing the "standalone" option or by migrating to Angular 14.1.0.`);
}
const options = await normalizeOptions(tree, rawOptions);
checkPathUnderProjectRoot(tree, options.project, options.path);
validateOptions(tree, rawOptions);
const options = normalizeOptions(tree, rawOptions);
const pathToGenerate = options.flat
? joinPathFragments(__dirname, './files/__fileName__')
@ -38,10 +25,6 @@ export async function componentGenerator(tree: Tree, rawOptions: Schema) {
const componentNames = names(options.name);
const typeNames = names(options.type);
const selector =
options.selector ||
buildSelector(tree, componentNames.fileName, options.prefix);
generateFiles(tree, pathToGenerate, options.path, {
fileName: componentNames.fileName,
className: componentNames.className,
@ -55,7 +38,7 @@ export async function componentGenerator(tree: Tree, rawOptions: Schema) {
changeDetection: options.changeDetection,
viewEncapsulation: options.viewEncapsulation,
displayBlock: options.displayBlock,
selector,
selector: options.selector,
tpl: '',
});
@ -123,12 +106,4 @@ export async function componentGenerator(tree: Tree, rawOptions: Schema) {
}
}
function buildSelector(tree: Tree, name: string, prefix: string) {
const selectorPrefix = names(
prefix ?? readNxJson(tree).npmScope ?? 'app'
).fileName;
return names(`${selectorPrefix}-${name}`).fileName;
}
export default componentGenerator;

View File

@ -1,10 +1,11 @@
import type { Tree } from '@nrwl/devkit';
import { logger, readProjectConfiguration, stripIndents } from '@nrwl/devkit';
import { getComponentFileInfo } from '../../utils/file-info';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
import type { StringLiteral } from 'typescript';
import { locateLibraryEntryPointFromDirectory } from '../../utils/entry-point';
import { getComponentFileInfo } from '../../utils/file-info';
import { getRelativeImportToFile } from '../../utils/path';
import type { NormalizedSchema } from '../schema';
import { shouldExportInEntryPoint } from './entry-point';
import { findModuleFromOptions } from './module';
export function exportComponentInEntryPoint(
@ -57,3 +58,26 @@ export function exportComponentInEntryPoint(
tree.write(entryPointPath, updateEntryPointContent);
}
function shouldExportInEntryPoint(
tree: Tree,
entryPoint: string,
modulePath: string
): boolean {
if (!modulePath) {
return false;
}
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const moduleImportPath = getRelativeImportToFile(entryPoint, modulePath);
const entryPointContent = tree.read(entryPoint, 'utf-8');
const entryPointAst = tsquery.ast(entryPointContent);
const moduleExport = tsquery(
entryPointAst,
`ExportDeclaration StringLiteral[value='${moduleImportPath}']`,
{ visitAllChildren: true }
)[0] as StringLiteral;
return Boolean(moduleExport);
}

View File

@ -1,27 +0,0 @@
import type { Tree } from '@nrwl/devkit';
import type { StringLiteral } from 'typescript';
import { getRelativeImportToFile } from '../../utils/path';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
export function shouldExportInEntryPoint(
tree: Tree,
entryPoint: string,
modulePath: string
): boolean {
if (!modulePath) {
return false;
}
ensureTypescript();
const { tsquery } = require('@phenomnomnominal/tsquery');
const moduleImportPath = getRelativeImportToFile(entryPoint, modulePath);
const entryPointContent = tree.read(entryPoint, 'utf-8');
const entryPointAst = tsquery.ast(entryPointContent);
const moduleExport = tsquery(
entryPointAst,
`ExportDeclaration StringLiteral[value='${moduleImportPath}']`,
{ visitAllChildren: true }
)[0] as StringLiteral;
return Boolean(moduleExport);
}

View File

@ -0,0 +1,4 @@
export * from './component';
export * from './module';
export * from './normalize-options';
export * from './validate-options';

View File

@ -1,29 +1,34 @@
import type { Tree } from '@nrwl/devkit';
import { joinPathFragments, readProjectConfiguration } from '@nrwl/devkit';
import type { AngularProjectConfiguration } from '../../../utils/types';
import { parseNameWithPath } from '../../utils/names';
import { buildSelector } from '../../utils/selector';
import type { NormalizedSchema, Schema } from '../schema';
export async function normalizeOptions(
export function normalizeOptions(
tree: Tree,
options: Schema
): Promise<NormalizedSchema> {
const { projectType, root, sourceRoot } = readProjectConfiguration(
): NormalizedSchema {
const { prefix, projectType, root, sourceRoot } = readProjectConfiguration(
tree,
options.project
);
const projectSourceRoot = sourceRoot ?? joinPathFragments(root, 'src');
) as AngularProjectConfiguration;
const parsedName = options.name.split('/');
const name = parsedName.pop();
const namedPath = parsedName.join('/');
const projectSourceRoot = sourceRoot ?? joinPathFragments(root, 'src');
const { name, path: namePath } = parseNameWithPath(options.name);
const path =
options.path ??
joinPathFragments(
projectSourceRoot,
projectType === 'application' ? 'app' : 'lib',
namedPath
namePath
);
const selector =
options.selector ??
buildSelector(tree, name, options.prefix, prefix, 'fileName');
return {
...options,
name,
@ -33,5 +38,6 @@ export async function normalizeOptions(
path,
projectSourceRoot,
projectRoot: root,
selector,
};
}

View File

@ -0,0 +1,13 @@
import type { Tree } from '@nrwl/devkit';
import { checkPathUnderProjectRoot } from '../../utils/path';
import {
validateProject,
validateStandaloneOption,
} from '../../utils/validations';
import type { Schema } from '../schema';
export function validateOptions(tree: Tree, options: Schema): void {
validateProject(tree, options.project);
checkPathUnderProjectRoot(tree, options.project, options.path);
validateStandaloneOption(tree, options.standalone);
}

View File

@ -25,4 +25,5 @@ export interface NormalizedSchema extends Schema {
path: string;
projectSourceRoot: string;
projectRoot: string;
selector: string;
}

View File

@ -54,7 +54,7 @@
"alias": "t"
},
"standalone": {
"description": "Whether the generated component is standalone. _Note: This is only supported in Angular versions >= 14.1.0_",
"description": "Whether the generated component is standalone. _Note: This is only supported in Angular versions >= 14.1.0_.",
"type": "boolean",
"default": false,
"x-priority": "important"

View File

@ -6,10 +6,11 @@ import {
logger,
Tree,
} from '@nrwl/devkit';
import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage';
import { ConvertTSLintToESLintSchema, ProjectConverter } from '@nrwl/linter';
import type { Linter } from 'eslint';
import type { AngularProjectConfiguration } from '../../utils/types';
import { addLintingGenerator } from '../add-linting/add-linting';
import { warnForSchematicUsage } from '../utils/warn-for-schematic-usage';
export async function conversionGenerator(
host: Tree,
@ -33,7 +34,7 @@ export async function conversionGenerator(
await addLintingGenerator(host, {
projectName,
projectRoot: projectConfig.root,
prefix: (projectConfig as any).prefix || 'app',
prefix: (projectConfig as AngularProjectConfiguration).prefix || 'app',
/**
* We set the parserOptions.project config just in case the converted config uses
* rules which require type-checking. Later in the conversion we check if it actually

View File

@ -1,21 +1,18 @@
import type { ProjectConfiguration, Tree } from '@nrwl/devkit';
import {
joinPathFragments,
names,
readNxJson,
readProjectConfiguration,
} from '@nrwl/devkit';
import { parseName } from '../../utils/names';
import type { Tree } from '@nrwl/devkit';
import { joinPathFragments, readProjectConfiguration } from '@nrwl/devkit';
import type { AngularProjectConfiguration } from '../../../utils/types';
import { parseNameWithPath } from '../../utils/names';
import { buildSelector } from '../../utils/selector';
import type { Schema } from '../schema';
export function normalizeOptions(tree: Tree, options: Schema) {
const { prefix, projectType, root, sourceRoot } = readProjectConfiguration(
tree,
options.project
) as ProjectConfiguration & { prefix?: string };
) as AngularProjectConfiguration;
const projectSourceRoot = sourceRoot ?? joinPathFragments(root, 'src');
const { name, path: namePath } = parseName(options.name);
const { name, path: namePath } = parseNameWithPath(options.name);
const path =
options.path ??
@ -26,7 +23,8 @@ export function normalizeOptions(tree: Tree, options: Schema) {
);
const selector =
options.selector ?? buildSelector(tree, name, options.prefix, prefix);
options.selector ??
buildSelector(tree, name, options.prefix, prefix, 'propertyName');
return {
...options,
@ -37,18 +35,3 @@ export function normalizeOptions(tree: Tree, options: Schema) {
selector,
};
}
function buildSelector(
tree: Tree,
name: string,
prefix: string,
projectPrefix: string
): string {
let selector = name;
prefix ??= projectPrefix ?? readNxJson(tree).npmScope;
if (prefix) {
selector = `${prefix}-${selector}`;
}
return names(selector).propertyName;
}

View File

@ -1,21 +1,13 @@
import type { Tree } from '@nrwl/devkit';
import { getProjects, stripIndents } from '@nrwl/devkit';
import { lt } from 'semver';
import { checkPathUnderProjectRoot } from '../../utils/path';
import { getInstalledAngularVersionInfo } from '../../utils/version-utils';
import {
validateProject,
validateStandaloneOption,
} from '../../utils/validations';
import type { Schema } from '../schema';
export function validateOptions(tree: Tree, options: Schema): void {
const projects = getProjects(tree);
if (!projects.has(options.project)) {
throw new Error(`Project "${options.project}" does not exist!`);
}
validateProject(tree, options.project);
checkPathUnderProjectRoot(tree, options.project, options.path);
const installedAngularVersionInfo = getInstalledAngularVersionInfo(tree);
if (lt(installedAngularVersionInfo.version, '14.1.0') && options.standalone) {
throw new Error(stripIndents`The "standalone" option is only supported in Angular >= 14.1.0. You are currently using "${installedAngularVersionInfo.version}".
You can resolve this error by removing the "standalone" option or by migrating to Angular 14.1.0.`);
}
validateStandaloneOption(tree, options.standalone);
}

View File

@ -1,12 +1,13 @@
import type { ProjectConfiguration, Tree } from '@nrwl/devkit';
import type { Tree } from '@nrwl/devkit';
import { addProjectConfiguration, joinPathFragments } from '@nrwl/devkit';
import { NormalizedSchema } from './normalized-schema';
import type { AngularProjectConfiguration } from '../../../utils/types';
import type { NormalizedSchema } from './normalized-schema';
export function addProject(
tree: Tree,
libraryOptions: NormalizedSchema['libraryOptions']
) {
const project: ProjectConfiguration & { prefix: string } = {
const project: AngularProjectConfiguration = {
name: libraryOptions.name,
root: libraryOptions.projectRoot,
sourceRoot: joinPathFragments(libraryOptions.projectRoot, 'src'),

View File

@ -1,4 +1,4 @@
import type { ProjectConfiguration, Tree } from '@nrwl/devkit';
import type { Tree } from '@nrwl/devkit';
import {
generateFiles,
getWorkspaceLayout,
@ -6,14 +6,15 @@ import {
names,
offsetFromRoot,
} from '@nrwl/devkit';
import { NormalizedSchema } from './normalized-schema';
import { UnitTestRunner } from '../../../utils/test-runners';
import { getRootTsConfigFileName } from '@nrwl/js';
import { UnitTestRunner } from '../../../utils/test-runners';
import type { AngularProjectConfiguration } from '../../../utils/types';
import type { NormalizedSchema } from './normalized-schema';
export function createFiles(
tree: Tree,
options: NormalizedSchema,
project: ProjectConfiguration & { prefix: string }
project: AngularProjectConfiguration
) {
const { npmScope } = getWorkspaceLayout(tree);
const rootOffset = offsetFromRoot(options.libraryOptions.projectRoot);

View File

@ -1,4 +1,4 @@
import type { ProjectConfiguration, Tree } from '@nrwl/devkit';
import type { Tree } from '@nrwl/devkit';
import {
formatFiles,
generateFiles,
@ -7,9 +7,10 @@ import {
names,
readProjectConfiguration,
} from '@nrwl/devkit';
import type { Schema } from './schema';
import { checkPathUnderProjectRoot } from '../utils/path';
import type { AngularProjectConfiguration } from '../../utils/types';
import { addToNgModule, findModule } from '../utils';
import { checkPathUnderProjectRoot } from '../utils/path';
import type { Schema } from './schema';
let tsModule: typeof import('typescript');
@ -24,7 +25,7 @@ export async function pipeGenerator(tree: Tree, schema: Schema) {
const project = readProjectConfiguration(
tree,
schema.project
) as ProjectConfiguration & { prefix?: string };
) as AngularProjectConfiguration;
const path = schema.path ?? `${project.sourceRoot}`;
const pipeNames = names(schema.name);

View File

@ -1,25 +1,23 @@
import type { ProjectConfiguration, Tree } from '@nrwl/devkit';
import type { Tree } from '@nrwl/devkit';
import {
generateFiles,
joinPathFragments,
readNxJson,
readProjectConfiguration,
} from '@nrwl/devkit';
import type { Schema } from '../schema';
import { addRoute } from '../../../utils/nx-devkit/route-utils';
import type { AngularProjectConfiguration } from '../../../utils/types';
import type { Schema } from '../schema';
export function addRemoteEntry(
tree: Tree,
{ appName, routing, mfType, prefix, standalone }: Schema,
{ appName, routing, prefix, standalone }: Schema,
appRoot: string
) {
prefix =
prefix ??
(
readProjectConfiguration(tree, appName) as ProjectConfiguration & {
prefix?: string;
}
)?.prefix ??
(readProjectConfiguration(tree, appName) as AngularProjectConfiguration)
?.prefix ??
readNxJson(tree).npmScope;
generateFiles(
tree,

View File

@ -2,7 +2,7 @@ import { normalizePath } from '@nrwl/devkit';
export type NameInfo = { name: string; path: string };
export function parseName(rawName: string): NameInfo {
export function parseNameWithPath(rawName: string): NameInfo {
const parsedName = normalizePath(rawName).split('/');
const name = parsedName.pop();
const path = parsedName.join('/');

View File

@ -0,0 +1,18 @@
import type { Tree } from '@nrwl/devkit';
import { names, readNxJson } from '@nrwl/devkit';
export function buildSelector(
tree: Tree,
name: string,
prefix: string | undefined,
projectPrefix: string | undefined,
casing: keyof Pick<ReturnType<typeof names>, 'fileName' | 'propertyName'>
): string {
let selector = name;
prefix ??= projectPrefix ?? readNxJson(tree)?.npmScope;
if (prefix) {
selector = `${prefix}-${selector}`;
}
return names(selector)[casing];
}

View File

@ -0,0 +1,32 @@
import type { Tree } from '@nrwl/devkit';
import { getProjects, stripIndents } from '@nrwl/devkit';
import { lt } from 'semver';
import { getInstalledAngularVersionInfo } from './version-utils';
export function validateProject(tree: Tree, projectName: string): void {
const projects = getProjects(tree);
if (!projects.has(projectName)) {
throw new Error(
`Project "${projectName}" does not exist! Please provide an existing project name.`
);
}
}
export function validateStandaloneOption(
tree: Tree,
standalone: boolean | undefined,
angularVersion?: string
): void {
if (!standalone) {
return;
}
const installedAngularVersion =
angularVersion ?? getInstalledAngularVersionInfo(tree).version;
if (lt(installedAngularVersion, '14.1.0')) {
throw new Error(stripIndents`The "standalone" option is only supported in Angular >= 14.1.0. You are currently using "${installedAngularVersion}".
You can resolve this error by removing the "standalone" option or by migrating to Angular 14.1.0.`);
}
}

View File

@ -0,0 +1,5 @@
import type { ProjectConfiguration } from '@nrwl/devkit';
export type AngularProjectConfiguration = ProjectConfiguration & {
prefix?: string;
};