cleanup(angular): remove v14 specific code for generators that did not change (#13950)

This commit is contained in:
Leosvel Pérez Espinosa 2022-12-21 16:34:37 +01:00 committed by GitHub
parent f3465a9c07
commit 712d65b451
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 0 additions and 1616 deletions

View File

@ -1,157 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`component Generator support angular 14 --flat should create the component correctly and export it in the entry point 1`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
}
"
`;
exports[`component Generator support angular 14 --flat should create the component correctly and not export it when "export=false" 1`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
}
"
`;
exports[`component Generator support angular 14 --path should create the component correctly and export it in the entry point 1`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
}
"
`;
exports[`component Generator support angular 14 secondary entry points should create the component correctly and export it in the entry point 1`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
}
"
`;
exports[`component Generator support angular 14 secondary entry points should create the component correctly and export it in the entry point 2`] = `
"export * from \\"./lib/secondary.module\\";
export * from \\"./lib/example/example.component\\";"
`;
exports[`component Generator support angular 14 should create the component correctly and export it in the entry point when "export=true" 1`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
}
"
`;
exports[`component Generator support angular 14 should create the component correctly and export it in the entry point when "export=true" 2`] = `
"export * from \\"./lib/lib.module\\";
export * from \\"./lib/example/example.component\\";"
`;
exports[`component Generator support angular 14 should create the component correctly and export it in the entry point when is standalone and "export=true" 1`] = `
"import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'example',
standalone: true,
imports: [CommonModule],
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
}
"
`;
exports[`component Generator support angular 14 should create the component correctly and not export it in the entry point when "export=false" 1`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
}
"
`;
exports[`component Generator support angular 14 should create the component correctly and not export it in the entry point when is standalone and "export=false" 1`] = `
"import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'example',
standalone: true,
imports: [CommonModule],
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
}
"
`;
exports[`component Generator support angular 14 should create the component correctly and not export it when "--skip-import=true" 1`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
}
"
`;
exports[`component Generator support angular 14 should create the component correctly but not export it in the entry point when it does not exist 1`] = `
"import { Component } from '@angular/core';
@Component({
selector: 'example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.css']
})
export class ExampleComponent {
}
"
`;

View File

@ -1,53 +0,0 @@
import type { Tree } from '@nrwl/devkit';
import {
formatFiles,
normalizePath,
readProjectConfiguration,
readWorkspaceConfiguration,
} from '@nrwl/devkit';
import { wrapAngularDevkitSchematic } from '@nrwl/devkit/ngcli-adapter';
import { pathStartsWith } from '../../utils/path';
import { exportComponentInEntryPoint } from './lib/component';
import { normalizeOptions } from './lib/normalize-options';
import type { NormalizedSchema, Schema } from './schema';
export async function componentGenerator(tree: Tree, rawOptions: Schema) {
const options = await normalizeOptions(tree, rawOptions);
const { projectSourceRoot, ...schematicOptions } = options;
checkPathUnderProjectRoot(tree, options);
const angularComponentSchematic = wrapAngularDevkitSchematic(
'@schematics/angular',
'component'
);
await angularComponentSchematic(tree, schematicOptions);
exportComponentInEntryPoint(tree, options);
await formatFiles(tree);
}
function checkPathUnderProjectRoot(tree: Tree, schema: NormalizedSchema): void {
if (!schema.path) {
return;
}
const project =
schema.project ?? readWorkspaceConfiguration(tree).defaultProject;
const { root } = readProjectConfiguration(tree, project);
let pathToComponent = normalizePath(schema.path);
pathToComponent = pathToComponent.startsWith('/')
? pathToComponent.slice(1)
: pathToComponent;
if (!pathStartsWith(pathToComponent, root)) {
throw new Error(
`The path provided for the component (${schema.path}) does not exist under the project root (${root}). ` +
`Please make sure to provide a path that exists under the project root.`
);
}
}
export default componentGenerator;

View File

@ -1,59 +0,0 @@
import type { Tree } from '@nrwl/devkit';
import { logger, readProjectConfiguration, stripIndents } from '@nrwl/devkit';
import { getComponentFileInfo } from '../../../utils/file-info';
import { locateLibraryEntryPointFromDirectory } from '../../../utils/entry-point';
import { getRelativeImportToFile } from '../../../utils/path';
import type { NormalizedSchema } from '../schema';
import { shouldExportInEntryPoint } from './entry-point';
import { findModuleFromOptions } from './module';
export function exportComponentInEntryPoint(
tree: Tree,
schema: NormalizedSchema
): void {
if (!schema.export || (schema.skipImport && !schema.standalone)) {
return;
}
const { root, projectType } = readProjectConfiguration(tree, schema.project);
if (projectType === 'application') {
return;
}
const { directory, filePath } = getComponentFileInfo(tree, schema);
const entryPointPath = locateLibraryEntryPointFromDirectory(
tree,
directory,
root,
schema.projectSourceRoot
);
if (!entryPointPath) {
logger.warn(
`Unable to determine whether the component should be exported in the library entry point file. ` +
`The library's entry point file could not be found. Skipping exporting the component in the entry point file.`
);
return;
}
if (!schema.standalone) {
const modulePath = findModuleFromOptions(tree, schema, root);
if (!shouldExportInEntryPoint(tree, entryPointPath, modulePath)) {
return;
}
}
const relativePathFromEntryPoint = getRelativeImportToFile(
entryPointPath,
filePath
);
const updateEntryPointContent = stripIndents`${tree.read(
entryPointPath,
'utf-8'
)}
export * from "${relativePathFromEntryPoint}";`;
tree.write(entryPointPath, updateEntryPointContent);
}

View File

@ -1,25 +0,0 @@
import type { Tree } from '@nrwl/devkit';
import { tsquery } from '@phenomnomnominal/tsquery';
import type { StringLiteral } from 'typescript';
import { getRelativeImportToFile } from '../../../utils/path';
export function shouldExportInEntryPoint(
tree: Tree,
entryPoint: string,
modulePath: string
): boolean {
if (!modulePath) {
return false;
}
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,84 +0,0 @@
import type { Tree } from '@nrwl/devkit';
import { joinPathFragments, normalizePath } from '@nrwl/devkit';
import { basename, dirname } from 'path';
import type { NormalizedSchema } from '../schema';
// Adapted from https://github.com/angular/angular-cli/blob/main/packages/schematics/angular/utility/find-module.ts#L29
// to match the logic in the component schematic. It doesn't throw if it can't
// find a module since the schematic would have thrown before getting here.
const moduleExt = '.module.ts';
const routingModuleExt = '-routing.module.ts';
export function findModuleFromOptions(
tree: Tree,
options: NormalizedSchema,
projectRoot: string
): string | null {
if (!options.module) {
const pathToCheck = joinPathFragments(options.path, options.name);
return normalizePath(findModule(tree, pathToCheck, projectRoot));
} else {
const modulePath = joinPathFragments(options.path, options.module);
const componentPath = joinPathFragments(options.path, options.name);
const moduleBaseName = basename(modulePath);
const candidateSet = new Set<string>([options.path]);
const projectRootParent = dirname(projectRoot);
for (let dir = modulePath; dir !== projectRootParent; dir = dirname(dir)) {
candidateSet.add(dir);
}
for (let dir = componentPath; dir !== projectRoot; dir = dirname(dir)) {
candidateSet.add(dir);
}
const candidatesDirs = [...candidateSet].sort(
(a, b) => b.length - a.length
);
for (const c of candidatesDirs) {
const candidateFiles = [
'',
`${moduleBaseName}.ts`,
`${moduleBaseName}${moduleExt}`,
].map((x) => joinPathFragments(c, x));
for (const sc of candidateFiles) {
if (tree.isFile(sc)) {
return normalizePath(sc);
}
}
}
return null;
}
}
function findModule(
tree: Tree,
generateDir: string,
projectRoot: string
): string | null {
let dir = generateDir;
const projectRootParent = dirname(projectRoot);
while (dir !== projectRootParent) {
const allMatches = tree
.children(dir)
.map((path) => joinPathFragments(dir, path))
.filter((path) => tree.isFile(path) && path.endsWith(moduleExt));
const filteredMatches = allMatches.filter(
(path) => !path.endsWith(routingModuleExt)
);
if (filteredMatches.length == 1) {
return filteredMatches[0];
} else if (filteredMatches.length > 1) {
return null;
}
dir = dirname(dir);
}
return null;
}

View File

@ -1,65 +0,0 @@
import type { Tree } from '@nrwl/devkit';
import {
createProjectGraphAsync,
joinPathFragments,
readProjectConfiguration,
readWorkspaceConfiguration,
} from '@nrwl/devkit';
import type { NormalizedSchema, Schema } from '../schema';
import {
createProjectRootMappings,
findProjectForPath,
} from 'nx/src/project-graph/utils/find-project-for-path';
async function findProjectFromOptions(options: Schema) {
const projectGraph = await createProjectGraphAsync();
const projectRootMappings = createProjectRootMappings(projectGraph.nodes);
// path can be undefined when running on the root of the workspace, we default to the root
// to handle standalone layouts
return findProjectForPath(options.path || '', projectRootMappings);
}
export async function normalizeOptions(
tree: Tree,
options: Schema
): Promise<NormalizedSchema> {
const project =
options.project ??
(await findProjectFromOptions(options)) ??
readWorkspaceConfiguration(tree).defaultProject;
if (!project) {
// path is hidden, so if not provided we don't suggest setting it
if (!options.path) {
throw new Error(
'No "project" was specified and "defaultProject" is not set in the workspace configuration. Please provide the "project" option and try again.'
);
}
// path was provided, so it's wrong and we should mention it
throw new Error(
'The provided "path" is wrong and no "project" was specified and "defaultProject" is not set in the workspace configuration. ' +
'Please provide a correct "path" or provide the "project" option instead and try again.'
);
}
const { projectType, root, sourceRoot } = readProjectConfiguration(
tree,
project
);
const projectSourceRoot = sourceRoot ?? joinPathFragments(root, 'src');
const path =
options.path ??
joinPathFragments(
projectSourceRoot,
projectType === 'application' ? 'app' : 'lib'
);
return {
...options,
path,
project,
projectSourceRoot,
};
}

View File

@ -1,26 +0,0 @@
export interface Schema {
name: string;
path?: string;
project?: string;
displayBlock?: boolean;
inlineStyle?: boolean;
inlineTemplate?: boolean;
standalone?: boolean;
viewEncapsulation?: 'Emulated' | 'None' | 'ShadowDom';
changeDetection?: 'Default' | 'OnPush';
style?: 'css' | 'scss' | 'sass' | 'less' | 'none';
skipTests?: boolean;
type?: string;
flat?: boolean;
skipImport?: boolean;
selector?: string;
module?: string;
skipSelector?: boolean;
export?: boolean;
}
export interface NormalizedSchema extends Schema {
path: string;
project: string;
projectSourceRoot: string;
}

View File

@ -10,20 +10,8 @@ import { pathStartsWith } from '../utils/path';
import { exportComponentInEntryPoint } from './lib/component';
import { normalizeOptions } from './lib/normalize-options';
import type { NormalizedSchema, Schema } from './schema';
import { getGeneratorDirectoryForInstalledAngularVersion } from '../../utils/get-generator-directory-for-ng-version';
import { join } from 'path';
export async function componentGenerator(tree: Tree, rawOptions: Schema) {
const generatorDirectory =
getGeneratorDirectoryForInstalledAngularVersion(tree);
if (generatorDirectory) {
let previousGenerator = await import(
join(__dirname, generatorDirectory, 'component')
);
await previousGenerator.default(tree, rawOptions);
return;
}
const options = await normalizeOptions(tree, rawOptions);
const { projectSourceRoot, ...schematicOptions } = options;

View File

@ -1,799 +0,0 @@
import { addProjectConfiguration, updateJson, writeJson } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import componentGenerator from './component';
describe('component Generator', () => {
describe('support angular 14', () => {
it('should create the component correctly and export it in the entry point when "export=true"', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write('libs/lib1/src/index.ts', 'export * from "./lib/lib.module";');
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
export: true,
});
// ASSERT
const componentSource = tree.read(
'libs/lib1/src/lib/example/example.component.ts',
'utf-8'
);
expect(componentSource).toMatchSnapshot();
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).toMatchSnapshot();
});
it('should create the component correctly and export it in the entry point when is standalone and "export=true"', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write('libs/lib1/src/index.ts', '');
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
standalone: true,
export: true,
});
// ASSERT
const componentSource = tree.read(
'libs/lib1/src/lib/example/example.component.ts',
'utf-8'
);
expect(componentSource).toMatchSnapshot();
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).toMatchInlineSnapshot(
`"export * from \\"./lib/example/example.component\\";"`
);
});
it('should create the component correctly and not export it in the entry point when "export=false"', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write('libs/lib1/src/index.ts', 'export * from "./lib/lib.module";');
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
export: false,
});
// ASSERT
const componentSource = tree.read(
'libs/lib1/src/lib/example/example.component.ts',
'utf-8'
);
expect(componentSource).toMatchSnapshot();
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).not.toContain(
`export * from "./lib/example/example.component";`
);
});
it('should create the component correctly and not export it in the entry point when is standalone and "export=false"', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write('libs/lib1/src/index.ts', 'export * from "./lib/lib.module";');
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
standalone: true,
export: false,
});
// ASSERT
const componentSource = tree.read(
'libs/lib1/src/lib/example/example.component.ts',
'utf-8'
);
expect(componentSource).toMatchSnapshot();
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).not.toContain(
`export * from "./lib/example/example.component";`
);
});
it('should create the component correctly and not export it when "--skip-import=true"', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write('libs/lib1/src/index.ts', 'export * from "./lib/lib.module";');
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
skipImport: true,
});
// ASSERT
const componentSource = tree.read(
'libs/lib1/src/lib/example/example.component.ts',
'utf-8'
);
expect(componentSource).toMatchSnapshot();
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).not.toContain(
`export * from "./lib/example/example.component";`
);
});
it('should create the component correctly but not export it in the entry point when it does not exist', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
export: true,
});
// ASSERT
const componentSource = tree.read(
'libs/lib1/src/lib/example/example.component.ts',
'utf-8'
);
expect(componentSource).toMatchSnapshot();
const indexExists = tree.exists('libs/lib1/src/index.ts');
expect(indexExists).toBeFalsy();
});
it('should not export the component in the entry point when the module it belongs to is not exported', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write('libs/lib1/src/index.ts', '');
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
export: true,
});
// ASSERT
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).toBe('');
});
describe('--flat', () => {
it('should create the component correctly and export it in the entry point', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write(
'libs/lib1/src/index.ts',
'export * from "./lib/lib.module";'
);
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
flat: true,
export: true,
});
// ASSERT
const componentSource = tree.read(
'libs/lib1/src/lib/example.component.ts',
'utf-8'
);
expect(componentSource).toMatchSnapshot();
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).toContain(
`export * from "./lib/example.component";`
);
});
it('should create the component correctly and not export it when "export=false"', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write(
'libs/lib1/src/index.ts',
'export * from "./lib/lib.module";'
);
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
flat: true,
export: false,
});
// ASSERT
const componentSource = tree.read(
'libs/lib1/src/lib/example.component.ts',
'utf-8'
);
expect(componentSource).toMatchSnapshot();
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).not.toContain(
`export * from "./lib/example.component";`
);
});
});
describe('--path', () => {
it('should create the component correctly and export it in the entry point', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write(
'libs/lib1/src/index.ts',
'export * from "./lib/lib.module";'
);
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
path: 'libs/lib1/src/lib/mycomp',
export: true,
});
// ASSERT
const componentSource = tree.read(
'libs/lib1/src/lib/mycomp/example/example.component.ts',
'utf-8'
);
expect(componentSource).toMatchSnapshot();
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).toContain(
`export * from "./lib/mycomp/example/example.component";`
);
});
it('should throw if the path specified is not under the project root', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write(
'libs/lib1/src/index.ts',
'export * from "./lib/lib.module";'
);
// ACT & ASSERT
await expect(
componentGenerator(tree, {
name: 'example',
project: 'lib1',
path: 'apps/app1/src/mycomp',
export: false,
})
).rejects.toThrow();
});
});
describe('--module', () => {
it.each([
'./lib.module.ts',
'lib.module.ts',
'./lib.module',
'lib.module',
'./lib',
'lib',
])(
'should export it in the entry point when "--module" is set to "%s"',
async (module) => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write(
'libs/lib1/src/index.ts',
'export * from "./lib/lib.module";'
);
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
module,
export: true,
});
// ASSERT
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).toContain(
`export * from "./lib/example/example.component";`
);
}
);
it('should not export it in the entry point when the module it belong to is not exported', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write(
'libs/lib1/src/lib/not-exported.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class NotExportedModule {}`
);
tree.write(
'libs/lib1/src/index.ts',
'export * from "./lib/lib.module";'
);
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
module: 'not-exported',
export: true,
});
// ASSERT
const indexSource = tree.read('libs/lib1/src/index.ts', 'utf-8');
expect(indexSource).toMatchInlineSnapshot(
`"export * from \\"./lib/lib.module\\";"`
);
});
});
describe('secondary entry points', () => {
it('should create the component correctly and export it in the entry point', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write(
'libs/lib1/src/index.ts',
'export * from "./lib/lib.module";'
);
// secondary entry point
writeJson(tree, 'libs/lib1/secondary/ng-package.json', {
lib: { entryFile: './src/index.ts' },
});
tree.write(
'libs/lib1/secondary/src/index.ts',
'export * from "./lib/secondary.module";'
);
tree.write(
'libs/lib1/secondary/src/lib/secondary.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class SecondaryModule {}`
);
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
path: 'libs/lib1/secondary/src/lib',
export: true,
});
// ASSERT
const componentSource = tree.read(
'libs/lib1/secondary/src/lib/example/example.component.ts',
'utf-8'
);
expect(componentSource).toMatchSnapshot();
const secondaryIndexSource = tree.read(
'libs/lib1/secondary/src/index.ts',
'utf-8'
);
expect(secondaryIndexSource).toMatchSnapshot();
});
it('should not export the component in the entry point when the module it belongs to is not exported', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
updateJson(tree, 'package.json', (json) => ({
...json,
dependencies: {
...json.dependencies,
'@angular/core': '14.1.0',
},
}));
addProjectConfiguration(tree, 'lib1', {
projectType: 'library',
sourceRoot: 'libs/lib1/src',
root: 'libs/lib1',
});
tree.write(
'libs/lib1/src/lib/lib.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class LibModule {}`
);
tree.write(
'libs/lib1/src/index.ts',
'export * from "./lib/lib.module";'
);
// secondary entry point
writeJson(tree, 'libs/lib1/secondary/ng-package.json', {
lib: { entryFile: './src/index.ts' },
});
tree.write('libs/lib1/secondary/src/index.ts', '');
tree.write(
'libs/lib1/secondary/src/lib/secondary.module.ts',
`
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
exports: []
})
export class SecondaryModule {}`
);
// ACT
await componentGenerator(tree, {
name: 'example',
project: 'lib1',
export: true,
});
// ASSERT
const indexSource = tree.read(
'libs/lib1/secondary/src/index.ts',
'utf-8'
);
expect(indexSource).toBe('');
});
});
});
});

View File

@ -1,18 +0,0 @@
const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind');
const { join } = require('path');
module.exports = {
mode: 'jit',
purge: [
join(__dirname, '<%= relativeSourceRoot %>/**/!(*.stories|*.spec).{ts,html}'),
...createGlobPatternsForDependencies(__dirname),
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};

View File

@ -1,14 +0,0 @@
const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind');
const { join } = require('path');
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
join(__dirname, '<%= relativeSourceRoot %>/**/!(*.stories|*.spec).{ts,html}'),
...createGlobPatternsForDependencies(__dirname),
],
theme: {
extend: {},
},
plugins: [],
};

View File

@ -1,56 +0,0 @@
import {
joinPathFragments,
ProjectConfiguration,
stripIndents,
Tree,
updateProjectConfiguration,
} from '@nrwl/devkit';
import { NormalizedGeneratorOptions } from '../schema';
export function addTailwindConfigPathToProject(
tree: Tree,
options: NormalizedGeneratorOptions,
project: ProjectConfiguration
): void {
const buildTarget = project.targets?.[options.buildTarget];
if (!buildTarget) {
throw new Error(
stripIndents`The target "${options.buildTarget}" was not found for project "${options.project}".
If you are using a different build target, please provide it using the "--buildTarget" option.
If the project is not a buildable or publishable library, you don't need to setup TailwindCSS for it.`
);
}
const supportedLibraryExecutors = [
'@nrwl/angular:ng-packagr-lite',
'@nrwl/angular:package',
];
if (!supportedLibraryExecutors.includes(buildTarget.executor)) {
throw new Error(
stripIndents`The build target for project "${
options.project
}" is using an unsupported executor "${buildTarget.executor}".
Supported executors are ${supportedLibraryExecutors
.map((e) => `"${e}"`)
.join(', ')}.`
);
}
if (
buildTarget.options?.tailwindConfig &&
tree.exists(buildTarget.options.tailwindConfig)
) {
throw new Error(
stripIndents`The "${buildTarget.options.tailwindConfig}" file is already configured for the project "${options.project}". Are you sure this is the right project to set up Tailwind?
If you are sure, you can remove the configuration and re-run the generator.`
);
}
buildTarget.options = {
...buildTarget.options,
tailwindConfig: joinPathFragments(project.root, 'tailwind.config.js'),
};
updateProjectConfiguration(tree, options.project, project);
}

View File

@ -1,35 +0,0 @@
import {
generateFiles,
joinPathFragments,
ProjectConfiguration,
stripIndents,
Tree,
} from '@nrwl/devkit';
import { relative } from 'path';
import { GeneratorOptions } from '../schema';
export function addTailwindConfig(
tree: Tree,
options: GeneratorOptions,
project: ProjectConfiguration,
tailwindVersion: '2' | '3'
): void {
if (tree.exists(joinPathFragments(project.root, 'tailwind.config.js'))) {
throw new Error(
stripIndents`The "tailwind.config.js" file already exists in the project "${options.project}". Are you sure this is the right project to set up Tailwind?
If you are sure, you can remove the existing file and re-run the generator.`
);
}
const filesDir = tailwindVersion === '3' ? 'files/v3' : 'files/v2';
generateFiles(
tree,
joinPathFragments(__dirname, '..', filesDir),
project.root,
{
relativeSourceRoot: relative(project.root, project.sourceRoot),
tmpl: '',
}
);
}

View File

@ -1,18 +0,0 @@
import {
addDependenciesToPackageJson,
GeneratorCallback,
Tree,
} from '@nrwl/devkit';
import { versions } from '../../../../utils/versions';
export function addTailwindRequiredPackages(tree: Tree): GeneratorCallback {
return addDependenciesToPackageJson(
tree,
{},
{
autoprefixer: versions.angularV14.autoprefixerVersion,
postcss: versions.angularV14.postcssVersion,
tailwindcss: versions.angularV14.tailwindVersion,
}
);
}

View File

@ -1,24 +0,0 @@
import { readJson, Tree } from '@nrwl/devkit';
import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utilities/version-utils';
import { lt } from 'semver';
export function detectTailwindInstalledVersion(
tree: Tree
): '2' | '3' | undefined {
const { dependencies, devDependencies } = readJson(tree, 'package.json');
const tailwindVersion =
dependencies?.tailwindcss ?? devDependencies?.tailwindcss;
if (!tailwindVersion) {
return undefined;
}
const version = checkAndCleanWithSemver('tailwindcss', tailwindVersion);
if (lt(version, '2.0.0')) {
throw new Error(
`The Tailwind CSS version "${tailwindVersion}" is not supported. Please upgrade to v2.0.0 or higher.`
);
}
return lt(version, '3.0.0') ? '2' : '3';
}

View File

@ -1,6 +0,0 @@
export * from './add-tailwind-config-path-to-project';
export * from './add-tailwind-config';
export * from './add-tailwind-required-packages';
export * from './detect-tailwind-installed-version';
export * from './normalize-options';
export * from './update-application-styles';

View File

@ -1,10 +0,0 @@
import type { GeneratorOptions, NormalizedGeneratorOptions } from '../schema';
export function normalizeOptions(
options: GeneratorOptions
): NormalizedGeneratorOptions {
return {
...options,
buildTarget: options.buildTarget || 'build',
};
}

View File

@ -1,84 +0,0 @@
import {
joinPathFragments,
ProjectConfiguration,
stripIndents,
Tree,
} from '@nrwl/devkit';
import { NormalizedGeneratorOptions } from '../schema';
export function updateApplicationStyles(
tree: Tree,
options: NormalizedGeneratorOptions,
project: ProjectConfiguration
): void {
let stylesEntryPoint = options.stylesEntryPoint;
if (stylesEntryPoint && !tree.exists(stylesEntryPoint)) {
throw new Error(
`The provided styles entry point "${stylesEntryPoint}" could not be found.`
);
}
if (!stylesEntryPoint) {
stylesEntryPoint = findStylesEntryPoint(tree, options, project);
if (!stylesEntryPoint) {
throw new Error(
stripIndents`Could not find a styles entry point for project "${options.project}".
Please specify a styles entry point using the "--stylesEntryPoint" option.`
);
}
}
const stylesEntryPointContent = tree.read(stylesEntryPoint, 'utf-8');
tree.write(
stylesEntryPoint,
stripIndents`@tailwind base;
@tailwind components;
@tailwind utilities;
${stylesEntryPointContent}`
);
}
function findStylesEntryPoint(
tree: Tree,
options: NormalizedGeneratorOptions,
project: ProjectConfiguration
): string | undefined {
// first check for common names
const possibleStylesEntryPoints = [
joinPathFragments(project.sourceRoot ?? project.root, 'styles.css'),
joinPathFragments(project.sourceRoot ?? project.root, 'styles.scss'),
joinPathFragments(project.sourceRoot ?? project.root, 'styles.sass'),
joinPathFragments(project.sourceRoot ?? project.root, 'styles.less'),
];
let stylesEntryPoint = possibleStylesEntryPoints.find((s) => tree.exists(s));
if (stylesEntryPoint) {
return stylesEntryPoint;
}
// then check for the specified styles in the build configuration if it exists
const styles: Array<string | { input: string; inject: boolean }> =
project.targets?.[options.buildTarget].options?.styles;
if (!styles) {
return undefined;
}
// find the first style that belongs to the project source
const style = styles.find((s) =>
typeof s === 'string'
? s.startsWith(project.root) && tree.exists(s)
: s.input.startsWith(project.root) &&
s.inject !== false &&
tree.exists(s.input)
);
if (!style) {
return undefined;
}
return typeof style === 'string' ? style : style.input;
}

View File

@ -1,11 +0,0 @@
export interface GeneratorOptions {
project: string;
buildTarget?: string;
skipFormat?: boolean;
stylesEntryPoint?: string;
skipPackageJson?: boolean;
}
export interface NormalizedGeneratorOptions extends GeneratorOptions {
buildTarget: string;
}

View File

@ -1,48 +0,0 @@
import {
formatFiles,
GeneratorCallback,
readProjectConfiguration,
Tree,
} from '@nrwl/devkit';
import {
addTailwindConfig,
addTailwindConfigPathToProject,
addTailwindRequiredPackages,
detectTailwindInstalledVersion,
normalizeOptions,
updateApplicationStyles,
} from './lib';
import { GeneratorOptions } from './schema';
export async function setupTailwindGenerator(
tree: Tree,
rawOptions: GeneratorOptions
): Promise<GeneratorCallback> {
const options = normalizeOptions(rawOptions);
const project = readProjectConfiguration(tree, options.project);
const tailwindInstalledVersion = detectTailwindInstalledVersion(tree);
let installTask: GeneratorCallback = () => {};
if (!options.skipPackageJson) {
if (tailwindInstalledVersion === undefined) {
installTask = addTailwindRequiredPackages(tree);
}
}
addTailwindConfig(tree, options, project, tailwindInstalledVersion ?? '3');
if (project.projectType === 'application') {
updateApplicationStyles(tree, options, project);
} else if (project.projectType === 'library') {
addTailwindConfigPathToProject(tree, options, project);
}
if (!options.skipFormat) {
await formatFiles(tree);
}
return installTask;
}
export default setupTailwindGenerator;

View File

@ -13,23 +13,11 @@ import {
updateApplicationStyles,
} from './lib';
import { GeneratorOptions } from './schema';
import { getGeneratorDirectoryForInstalledAngularVersion } from '../../utils/get-generator-directory-for-ng-version';
import { join } from 'path';
export async function setupTailwindGenerator(
tree: Tree,
rawOptions: GeneratorOptions
): Promise<GeneratorCallback> {
const generatorDirectory =
getGeneratorDirectoryForInstalledAngularVersion(tree);
if (generatorDirectory) {
let previousGenerator = await import(
join(__dirname, generatorDirectory, 'setup-tailwind')
);
await previousGenerator.default(tree, rawOptions);
return;
}
const options = normalizeOptions(rawOptions);
const project = readProjectConfiguration(tree, options.project);