Skip to content

Commit

Permalink
fix(angular): handle components array as static member of the module …
Browse files Browse the repository at this point in the history
…when generating stories (#7321)
  • Loading branch information
leosvelperez committed Oct 13, 2021
1 parent 086f67d commit 0a41a34
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 24 deletions.
169 changes: 145 additions & 24 deletions packages/angular/src/generators/stories/lib/module-info.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import type { Tree } from '@nrwl/devkit';
import { logger, stripIndents, visitNotIgnoredFiles } from '@nrwl/devkit';
import { findNodes } from '@nrwl/workspace/src/utilities/typescript';
import type { Node, SourceFile } from 'typescript';
import type {
ClassDeclaration,
Node,
SourceFile,
VariableDeclaration,
} from 'typescript';
import { SyntaxKind } from 'typescript';
import { getDecoratorMetadata } from '../../../utils/nx-devkit/ast-utils';

Expand Down Expand Up @@ -70,36 +75,152 @@ function getDeclarationsArray(
.getChildren()
.find((node) => node.kind === SyntaxKind.ArrayLiteralExpression);

if (declarationArray) {
return declarationArray;
}

// Attempt to follow a variable instead of the literal
declarationArray = getModuleDeclaredComponentsFromVariable(
file,
declarationsPropertyAssignment
);

if (declarationArray) {
return declarationArray;
}

// Attempt to follow a class declaration instead of the literal
declarationArray = getModuleDeclaredComponentsFromClass(
file,
declarationsPropertyAssignment
);

if (!declarationArray) {
// Attempt to follow a variable instead of the literal
const declarationVariable = declarationsPropertyAssignment
.getChildren()
.filter((node) => node.kind === SyntaxKind.Identifier)[1];
const variableName = declarationVariable.getText();
const variableDeclaration = findNodes(
file,
SyntaxKind.VariableDeclaration
).find((variableDeclaration) => {
const identifier = variableDeclaration
.getChildren()
.find((node) => node.kind === SyntaxKind.Identifier);
return identifier.getText() === variableName;
});
logger.warn(
stripIndents`No stories generated because the declarations in ${moduleFilePath} is not an array literal or the variable could not be found. Hint: you can always generate stories later with the 'nx generate @nrwl/angular:stories --name=${projectName}' command.`
);
}

if (variableDeclaration) {
declarationArray = variableDeclaration
.getChildren()
.find((node) => node.kind === SyntaxKind.ArrayLiteralExpression);
} else {
logger.warn(
stripIndents`No stories generated because the declaration in ${moduleFilePath} is not an array literal or the variable could not be found. Hint: you can always generate stories later with the 'nx generate @nrwl/angular:stories --name=${projectName}' command.`
);
}
return declarationArray;
}

/**
* Try to get declared components like `declarations: someComponentsArrayConst`
*/
function getModuleDeclaredComponentsFromVariable(
file: SourceFile,
declarationsPropertyAssignment: Node
): Node | undefined {
let declarationsVariable = declarationsPropertyAssignment
.getChildren()
.filter((node) => node.kind === SyntaxKind.Identifier)[1];

if (!declarationsVariable) {
return undefined;
}

// Attempt to find variable declaration in the file
let variableDeclaration = getVariableDeclaration(
declarationsVariable.getText(),
file
);

if (!variableDeclaration) {
return undefined;
}

const declarationArray = variableDeclaration
.getChildren()
.find((node) => node.kind === SyntaxKind.ArrayLiteralExpression);

return declarationArray;
}

/**
* Try to get declared components like `declarations: SomeClass.components` as in
* https://github.com/nrwl/nx/issues/7276.
*/
function getModuleDeclaredComponentsFromClass(
file: SourceFile,
declarationsPropertyAssignment: Node
): Node | undefined {
const propertyAccessExpression = declarationsPropertyAssignment
.getChildren()
.filter((node) => node.kind === SyntaxKind.PropertyAccessExpression)[0];

if (!propertyAccessExpression) {
return undefined;
}

// Should contain 2 identifiers [SomeClass, components]
const [clazz, componentsProperty] = propertyAccessExpression
.getChildren()
.filter((node) => node.kind === SyntaxKind.Identifier);

if (!clazz || !componentsProperty) {
return undefined;
}

// Attempt to find class declaration in the file
let classDeclaration = getClassDeclaration(clazz.getText(), file);

if (!classDeclaration) {
return undefined;
}

const declarationArray = classDeclaration.members
.filter((node) => node.kind === SyntaxKind.PropertyDeclaration)
.find((propertyDeclaration) =>
propertyDeclaration
.getChildren()
.find(
(node) =>
node.kind === SyntaxKind.Identifier &&
node.getText() === componentsProperty.getText()
)
)
.getChildren()
.find((node) => node.kind === SyntaxKind.ArrayLiteralExpression);

return declarationArray;
}

function getClassDeclaration(
className: string,
file: SourceFile
): ClassDeclaration | undefined {
const classDeclaration = findNodes(file, SyntaxKind.ClassDeclaration).find(
(classDeclaration) =>
classDeclaration
.getChildren()
.find(
(node) =>
node.kind === SyntaxKind.Identifier && node.getText() === className
)
) as ClassDeclaration;

return classDeclaration;
}

function getVariableDeclaration(
variableName: string,
file: SourceFile
): VariableDeclaration | undefined {
const variableDeclaration = findNodes(
file,
SyntaxKind.VariableDeclaration
).find((variableDeclaration) =>
variableDeclaration
.getChildren()
.find(
(node) =>
node.kind === SyntaxKind.Identifier && node.getText() === variableName
)
) as VariableDeclaration;

return variableDeclaration;
}

function getNgModuleDeclarationsPropertyAssignment(
ngModuleDecorator: Node,
moduleFilePath: string,
Expand Down
33 changes: 33 additions & 0 deletions packages/angular/src/generators/stories/stories-lib.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,38 @@ describe('angularStories generator: libraries', () => {
)
).toBeTruthy();
});

it('should handle modules using static members for declarations rather than literals', async () => {
await cypressProjectGenerator(tree, {
linter: Linter.EsLint,
name: libName,
});

angularStoriesGenerator(tree, {
name: libName,
generateCypressSpecs: true,
});

expect(
tree.exists(
`libs/${libName}/src/lib/static-member-declarations/cmp1/cmp1.component.stories.ts`
)
).toBeTruthy();
expect(
tree.exists(
`libs/${libName}/src/lib/static-member-declarations/cmp2/cmp2.component.stories.ts`
)
).toBeTruthy();
expect(
tree.exists(
`apps/${libName}-e2e/src/integration/cmp1/cmp1.component.spec.ts`
)
).toBeTruthy();
expect(
tree.exists(
`apps/${libName}-e2e/src/integration/cmp2/cmp2.component.spec.ts`
)
).toBeTruthy();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Array [
"apps/one/two/test-ui-lib-e2e/cypress.json",
"apps/one/two/test-ui-lib-e2e/src/fixtures/example.json",
"apps/one/two/test-ui-lib-e2e/src/integration/barrel-button/barrel-button.component.spec.ts",
"apps/one/two/test-ui-lib-e2e/src/integration/cmp1/cmp1.component.spec.ts",
"apps/one/two/test-ui-lib-e2e/src/integration/cmp2/cmp2.component.spec.ts",
"apps/one/two/test-ui-lib-e2e/src/integration/nested-button/nested-button.component.spec.ts",
"apps/one/two/test-ui-lib-e2e/src/integration/test-button/test-button.component.spec.ts",
"apps/one/two/test-ui-lib-e2e/src/integration/test-other/test-other.component.spec.ts",
Expand Down Expand Up @@ -71,6 +73,17 @@ Array [
"libs/test-ui-lib/src/lib/nested/nested-button/nested-button.component.stories.ts",
"libs/test-ui-lib/src/lib/nested/nested-button/nested-button.component.ts",
"libs/test-ui-lib/src/lib/nested/nested.module.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp1/cmp1.component.css",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp1/cmp1.component.html",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp1/cmp1.component.spec.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp1/cmp1.component.stories.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp1/cmp1.component.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp2/cmp2.component.css",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp2/cmp2.component.html",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp2/cmp2.component.spec.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp2/cmp2.component.stories.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp2/cmp2.component.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/static-member-declarations.module.ts",
"libs/test-ui-lib/src/lib/test-button/test-button.component.css",
"libs/test-ui-lib/src/lib/test-button/test-button.component.html",
"libs/test-ui-lib/src/lib/test-button/test-button.component.spec.ts",
Expand Down Expand Up @@ -114,6 +127,8 @@ Array [
"apps/test-ui-lib-e2e/cypress.json",
"apps/test-ui-lib-e2e/src/fixtures/example.json",
"apps/test-ui-lib-e2e/src/integration/barrel-button/barrel-button.component.spec.ts",
"apps/test-ui-lib-e2e/src/integration/cmp1/cmp1.component.spec.ts",
"apps/test-ui-lib-e2e/src/integration/cmp2/cmp2.component.spec.ts",
"apps/test-ui-lib-e2e/src/integration/nested-button/nested-button.component.spec.ts",
"apps/test-ui-lib-e2e/src/integration/test-button/test-button.component.spec.ts",
"apps/test-ui-lib-e2e/src/integration/test-other/test-other.component.spec.ts",
Expand Down Expand Up @@ -144,6 +159,17 @@ Array [
"libs/test-ui-lib/src/lib/nested/nested-button/nested-button.component.stories.ts",
"libs/test-ui-lib/src/lib/nested/nested-button/nested-button.component.ts",
"libs/test-ui-lib/src/lib/nested/nested.module.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp1/cmp1.component.css",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp1/cmp1.component.html",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp1/cmp1.component.spec.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp1/cmp1.component.stories.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp1/cmp1.component.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp2/cmp2.component.css",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp2/cmp2.component.html",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp2/cmp2.component.spec.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp2/cmp2.component.stories.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/cmp2/cmp2.component.ts",
"libs/test-ui-lib/src/lib/static-member-declarations/static-member-declarations.module.ts",
"libs/test-ui-lib/src/lib/test-button/test-button.component.css",
"libs/test-ui-lib/src/lib/test-button/test-button.component.html",
"libs/test-ui-lib/src/lib/test-button/test-button.component.spec.ts",
Expand Down
37 changes: 37 additions & 0 deletions packages/angular/src/generators/utils/testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,43 @@ const COMPONENTS = [
export class VariableDeclareModule {}`
);

// create a module where declared components are pulled from a static member of the module
await moduleGenerator(tree, {
name: 'static-member-declarations',
project: libName,
});

await componentGenerator(tree, {
name: 'cmp1',
project: libName,
path: `libs/${libName}/src/lib/static-member-declarations`,
module: 'static-member-declarations',
});

await componentGenerator(tree, {
name: 'cmp2',
project: libName,
path: `libs/${libName}/src/lib/static-member-declarations`,
module: 'static-member-declarations',
});

tree.write(
`libs/${libName}/src/lib/static-member-declarations/static-member-declarations.module.ts`,
`import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Cmp1Component } from './cmp1/cmp1.component';
import { Cmp2Component } from './cmp2/cmp2.component';
@NgModule({
imports: [CommonModule],
declarations: StaticMemberDeclarationsModule.COMPONENTS,
exports: StaticMemberDeclarationsModule.COMPONENTS
})
export class StaticMemberDeclarationsModule {
static readonly COMPONENTS = [Cmp1Component, Cmp2Component];
}`
);

// create another button in a nested subpath
await moduleGenerator(tree, {
name: 'nested',
Expand Down

0 comments on commit 0a41a34

Please sign in to comment.