fix(angular): properly generate storybook stories across barrel files (#3662)
Previously stories haven't been generated properly when barrel files had been used to import components in Angular modules ISSUES CLOSED: 2813
This commit is contained in:
parent
44069bf0c8
commit
929f2f4b9d
@ -22,6 +22,7 @@ import {
|
||||
getKnobType,
|
||||
} from '../component-story/component-story';
|
||||
import { applyWithSkipExisting } from '@nrwl/workspace/src/utils/ast-utils';
|
||||
import { join, normalize } from '@angular-devkit/core';
|
||||
|
||||
export default function (schema: CreateComponentSpecFileSchema): Rule {
|
||||
return chain([createComponentSpecFile(schema)]);
|
||||
@ -45,8 +46,11 @@ export function createComponentSpecFile({
|
||||
return (tree: Tree, context: SchematicContext): Rule => {
|
||||
const e2eLibIntegrationFolderPath =
|
||||
getProjectConfig(tree, projectName + '-e2e').sourceRoot + '/integration';
|
||||
const fullComponentPath =
|
||||
libPath + '/' + componentPath + '/' + componentFileName + '.ts';
|
||||
const fullComponentPath = join(
|
||||
normalize(libPath),
|
||||
componentPath,
|
||||
`${componentFileName}.ts`
|
||||
);
|
||||
const props = getInputPropertyDeclarations(tree, fullComponentPath).map(
|
||||
(node) => {
|
||||
const decoratorContent = findNodes(
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
getSourceNodes,
|
||||
applyWithSkipExisting,
|
||||
} from '@nrwl/workspace/src/utils/ast-utils';
|
||||
import { join, normalize } from '@angular-devkit/core';
|
||||
|
||||
export interface CreateComponentStoriesFileSchema {
|
||||
libPath: string;
|
||||
@ -35,7 +36,7 @@ export function createComponentStoriesFile({
|
||||
return (tree: Tree, context: SchematicContext): Rule => {
|
||||
const props = getInputDescriptors(
|
||||
tree,
|
||||
libPath + '/' + componentPath + '/' + componentFileName + '.ts'
|
||||
join(normalize(libPath), componentPath, `${componentFileName}.ts`)
|
||||
);
|
||||
return applyWithSkipExisting(url('./files'), [
|
||||
template({
|
||||
|
||||
@ -156,29 +156,6 @@ export async function createTestUILib(libName: string): Promise<Tree> {
|
||||
}),
|
||||
appTree
|
||||
);
|
||||
const modulePath = `libs/${libName}/src/lib/${libName}.module.ts`;
|
||||
appTree.overwrite(
|
||||
modulePath,
|
||||
`import * as ButtonExports from './test-button/test-button.component';
|
||||
${appTree.read(modulePath)}`
|
||||
);
|
||||
appTree = await callRule(
|
||||
externalSchematic('@schematics/angular', 'module', {
|
||||
name: 'nested',
|
||||
project: libName,
|
||||
path: `libs/${libName}/src/lib`,
|
||||
}),
|
||||
appTree
|
||||
);
|
||||
appTree = await callRule(
|
||||
externalSchematic('@schematics/angular', 'component', {
|
||||
name: 'nested-button',
|
||||
project: libName,
|
||||
module: 'nested',
|
||||
path: `libs/${libName}/src/lib/nested`,
|
||||
}),
|
||||
appTree
|
||||
);
|
||||
appTree.overwrite(
|
||||
`libs/${libName}/src/lib/test-button/test-button.component.ts`,
|
||||
`
|
||||
@ -209,6 +186,69 @@ export class TestButtonComponent implements OnInit {
|
||||
`libs/${libName}/src/lib/test-button/test-button.component.html`,
|
||||
`<button [attr.type]="type" [ngClass]="style"></button>`
|
||||
);
|
||||
|
||||
const modulePath = `libs/${libName}/src/lib/${libName}.module.ts`;
|
||||
appTree.overwrite(
|
||||
modulePath,
|
||||
`import * as ButtonExports from './test-button/test-button.component';
|
||||
${appTree.read(modulePath)}`
|
||||
);
|
||||
|
||||
// create a module with component that gets exported in a barrel file
|
||||
appTree = await callRule(
|
||||
externalSchematic('@schematics/angular', 'module', {
|
||||
name: 'barrel',
|
||||
project: libName,
|
||||
}),
|
||||
appTree
|
||||
);
|
||||
|
||||
appTree = await callRule(
|
||||
externalSchematic('@schematics/angular', 'component', {
|
||||
name: 'barrel-button',
|
||||
project: libName,
|
||||
path: `libs/${libName}/src/lib/barrel`,
|
||||
module: 'barrel',
|
||||
}),
|
||||
appTree
|
||||
);
|
||||
appTree.create(
|
||||
`libs/${libName}/src/lib/barrel/barrel-button/index.ts`,
|
||||
`export * from './barrel-button.component';`
|
||||
);
|
||||
|
||||
appTree.overwrite(
|
||||
`libs/${libName}/src/lib/barrel/barrel.module.ts`,
|
||||
`import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { BarrelButtonComponent } from './barrel-button';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
declarations: [BarrelButtonComponent],
|
||||
})
|
||||
export class BarrelModule {}`
|
||||
);
|
||||
|
||||
// create another button in a nested subpath
|
||||
appTree = await callRule(
|
||||
externalSchematic('@schematics/angular', 'module', {
|
||||
name: 'nested',
|
||||
project: libName,
|
||||
path: `libs/${libName}/src/lib`,
|
||||
}),
|
||||
appTree
|
||||
);
|
||||
appTree = await callRule(
|
||||
externalSchematic('@schematics/angular', 'component', {
|
||||
name: 'nested-button',
|
||||
project: libName,
|
||||
module: 'nested',
|
||||
path: `libs/${libName}/src/lib/nested`,
|
||||
}),
|
||||
appTree
|
||||
);
|
||||
|
||||
appTree = await callRule(
|
||||
externalSchematic('@schematics/angular', 'component', {
|
||||
name: 'test-other',
|
||||
@ -216,6 +256,7 @@ export class TestButtonComponent implements OnInit {
|
||||
}),
|
||||
appTree
|
||||
);
|
||||
|
||||
return appTree;
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,8 @@ import { getTsSourceFile, getDecoratorMetadata } from '../../utils/ast-utils';
|
||||
import { CreateComponentSpecFileSchema } from '../component-cypress-spec/component-cypress-spec';
|
||||
import { CreateComponentStoriesFileSchema } from '../component-story/component-story';
|
||||
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
import { directoryExists } from '@nrwl/workspace/src/utils/fileutils';
|
||||
import { join, normalize } from '@angular-devkit/core';
|
||||
|
||||
export interface StorybookStoriesSchema {
|
||||
name: string;
|
||||
@ -39,8 +41,8 @@ export function createAllStories(
|
||||
moduleFilePaths.push(filePath);
|
||||
});
|
||||
return chain(
|
||||
moduleFilePaths.map((filePath) => {
|
||||
const file = getTsSourceFile(tree, filePath);
|
||||
moduleFilePaths.map((moduleFilePath) => {
|
||||
const file = getTsSourceFile(tree, moduleFilePath);
|
||||
|
||||
const ngModuleDecorators = getDecoratorMetadata(
|
||||
file,
|
||||
@ -49,7 +51,7 @@ export function createAllStories(
|
||||
);
|
||||
if (ngModuleDecorators.length === 0) {
|
||||
throw new SchematicsException(
|
||||
`No @NgModule decorator in ${filePath}`
|
||||
`No @NgModule decorator in ${moduleFilePath}`
|
||||
);
|
||||
}
|
||||
const ngModuleDecorator = ngModuleDecorators[0];
|
||||
@ -66,7 +68,7 @@ export function createAllStories(
|
||||
});
|
||||
if (!declarationsPropertyAssignment) {
|
||||
context.logger.warn(
|
||||
stripIndents`No stories generated because there were no components declared in ${filePath}. Hint: you can always generate stories later with the 'nx generate @nrwl/angular:stories --name=${projectName}' command`
|
||||
stripIndents`No stories generated because there were no components declared in ${moduleFilePath}. Hint: you can always generate stories later with the 'nx generate @nrwl/angular:stories --name=${projectName}' command`
|
||||
);
|
||||
return noop();
|
||||
}
|
||||
@ -83,6 +85,12 @@ export function createAllStories(
|
||||
const imports = file.statements.filter(
|
||||
(statement) => statement.kind === SyntaxKind.ImportDeclaration
|
||||
);
|
||||
|
||||
const modulePath = moduleFilePath.substr(
|
||||
0,
|
||||
moduleFilePath.lastIndexOf('/')
|
||||
);
|
||||
|
||||
const componentInfo = declaredComponents.map((componentName) => {
|
||||
try {
|
||||
const importStatement = imports.find((statement) => {
|
||||
@ -106,11 +114,61 @@ export function createAllStories(
|
||||
.find((node) => node.kind === SyntaxKind.StringLiteral)
|
||||
.getText()
|
||||
.slice(1, -1);
|
||||
const path = fullPath.slice(0, fullPath.lastIndexOf('/'));
|
||||
const componentFileName = fullPath.slice(
|
||||
fullPath.lastIndexOf('/') + 1
|
||||
|
||||
// if it is a directory, search recursively for the component
|
||||
let fullCmpImportPath = moduleFilePath.slice(
|
||||
0,
|
||||
moduleFilePath.lastIndexOf('/')
|
||||
);
|
||||
return { name: componentName, path, componentFileName };
|
||||
if (fullCmpImportPath.startsWith('/')) {
|
||||
fullCmpImportPath = fullCmpImportPath.slice(
|
||||
1,
|
||||
fullCmpImportPath.length
|
||||
);
|
||||
}
|
||||
|
||||
const componentImportPath = join(
|
||||
normalize(fullCmpImportPath),
|
||||
fullPath
|
||||
);
|
||||
|
||||
const dir = tree.getDir(componentImportPath);
|
||||
if (dir && dir.subfiles.length > 0) {
|
||||
let path = null;
|
||||
let componentFileName = null;
|
||||
// search the fullPath for component declarations
|
||||
tree.getDir(componentImportPath).visit((componentFilePath) => {
|
||||
if (componentFilePath.endsWith('.ts')) {
|
||||
const content = tree
|
||||
.read(componentFilePath)
|
||||
.toString('utf-8');
|
||||
if (content.indexOf(`class ${componentName}`) > -1) {
|
||||
path = componentFilePath
|
||||
.slice(0, componentFilePath.lastIndexOf('/'))
|
||||
.replace(modulePath, '.');
|
||||
componentFileName = componentFilePath.slice(
|
||||
componentFilePath.lastIndexOf('/') + 1,
|
||||
componentFilePath.lastIndexOf('.')
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (path === null) {
|
||||
throw new SchematicsException(
|
||||
`Path to component ${componentName} couldn't be found. Please open an issue on https://github.com/nrwl/nx/issues.`
|
||||
);
|
||||
}
|
||||
|
||||
return { name: componentName, path, componentFileName };
|
||||
} else {
|
||||
const path = fullPath.slice(0, fullPath.lastIndexOf('/'));
|
||||
const componentFileName = fullPath.slice(
|
||||
fullPath.lastIndexOf('/') + 1
|
||||
);
|
||||
return { name: componentName, path, componentFileName };
|
||||
}
|
||||
} catch (ex) {
|
||||
context.logger.warn(
|
||||
`Could not generate a story for ${componentName}. Error: ${ex}`
|
||||
@ -119,7 +177,6 @@ export function createAllStories(
|
||||
}
|
||||
});
|
||||
|
||||
const modulePath = filePath.substr(0, filePath.lastIndexOf('/'));
|
||||
return chain(
|
||||
componentInfo
|
||||
.filter((info) => info !== undefined)
|
||||
|
||||
@ -22,6 +22,13 @@ Array [
|
||||
"/libs/test-ui-lib/src/lib/test-button/test-button.component.spec.ts",
|
||||
"/libs/test-ui-lib/src/lib/test-button/test-button.component.ts",
|
||||
"/libs/test-ui-lib/src/lib/test-button/test-button.component.stories.ts",
|
||||
"/libs/test-ui-lib/src/lib/barrel/barrel.module.ts",
|
||||
"/libs/test-ui-lib/src/lib/barrel/barrel-button/barrel-button.component.css",
|
||||
"/libs/test-ui-lib/src/lib/barrel/barrel-button/barrel-button.component.html",
|
||||
"/libs/test-ui-lib/src/lib/barrel/barrel-button/barrel-button.component.spec.ts",
|
||||
"/libs/test-ui-lib/src/lib/barrel/barrel-button/barrel-button.component.ts",
|
||||
"/libs/test-ui-lib/src/lib/barrel/barrel-button/index.ts",
|
||||
"/libs/test-ui-lib/src/lib/barrel/barrel-button/barrel-button.component.stories.ts",
|
||||
"/libs/test-ui-lib/src/lib/nested/nested.module.ts",
|
||||
"/libs/test-ui-lib/src/lib/nested/nested-button/nested-button.component.css",
|
||||
"/libs/test-ui-lib/src/lib/nested/nested-button/nested-button.component.html",
|
||||
@ -50,6 +57,7 @@ Array [
|
||||
"/apps/test-ui-lib-e2e/src/support/index.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",
|
||||
"/apps/test-ui-lib-e2e/src/integration/barrel-button/barrel-button.component.spec.ts",
|
||||
"/apps/test-ui-lib-e2e/src/integration/nested-button/nested-button.component.spec.ts",
|
||||
]
|
||||
`;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user