Skip to content

Commit

Permalink
Merge pull request #6666 from MaximSagan/angular/avoid-component-rede…
Browse files Browse the repository at this point in the history
…claration-without-additional-configuration

Angular - Avoid component redeclaration without additional configuration
  • Loading branch information
shilman authored May 6, 2019
2 parents 7ad8699 + 086370b commit 5128f12
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 36 deletions.
71 changes: 59 additions & 12 deletions addons/storyshots/storyshots-core/src/frameworks/angular/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable import/no-extraneous-dependencies */
import { Component, Type } from '@angular/core';
import { Component, Type, NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
Expand Down Expand Up @@ -30,26 +30,73 @@ const createComponentFromTemplate = (template: string) => {
template,
})(componentClass);
};
const extractNgModuleMetadata = (importItem: any): NgModule => {
const decoratorKey = '__annotations__';
const decorators: any[] =
Reflect && Reflect.getOwnPropertyDescriptor
? Reflect.getOwnPropertyDescriptor(importItem, decoratorKey).value
: importItem[decoratorKey];

if (!decorators || decorators.length === 0) {
return null;
}

const ngModuleDecorator: NgModule | undefined = decorators.find(
decorator => decorator instanceof NgModule
);
if (!ngModuleDecorator) {
return null;
}
return ngModuleDecorator;
};

const getExistenceOfComponentInModules = (
component: any,
declarations: any[],
imports: any[]
): boolean => {
if (declarations && declarations.some(declaration => declaration === component)) {
// Found component in declarations array
return true;
}
if (!imports) {
return false;
}

return imports.some(importItem => {
const extractedNgModuleMetadata = extractNgModuleMetadata(importItem);
if (!extractedNgModuleMetadata) {
// Not an NgModule
return false;
}
return getExistenceOfComponentInModules(
component,
extractedNgModuleMetadata.declarations,
extractedNgModuleMetadata.imports
);
});
};

export const initModuleData = (storyObj: NgStory): any => {
const {
component,
template,
props,
moduleMetadata = {},
requiresComponentDeclaration = true,
} = storyObj;
const { component, template, props, moduleMetadata = {} } = storyObj;

const isCreatingComponentFromTemplate = Boolean(template);

const AnnotatedComponent = isCreatingComponentFromTemplate
? createComponentFromTemplate(template)
: component;

const componentDeclarations =
isCreatingComponentFromTemplate || requiresComponentDeclaration
? [AppComponent, AnnotatedComponent]
: [AppComponent];
const componentRequiesDeclaration =
isCreatingComponentFromTemplate ||
!getExistenceOfComponentInModules(
component,
moduleMetadata.declarations,
moduleMetadata.imports
);

const componentDeclarations = componentRequiesDeclaration
? [AppComponent, AnnotatedComponent]
: [AppComponent];

const story = {
component: AnnotatedComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export interface ICollection {

export interface NgStory {
component?: any;
requiresComponentDeclaration?: boolean;
props: ICollection;
propsMeta?: ICollection;
moduleMetadata?: NgModuleMetadata;
Expand Down
1 change: 0 additions & 1 deletion app/angular/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export interface IStory {
props?: ICollection;
moduleMetadata?: Partial<NgModuleMetadata>;
component?: any;
requiresComponentDeclaration?: boolean;
template?: string;
}

Expand Down
71 changes: 59 additions & 12 deletions app/angular/src/client/preview/angular/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,74 @@ const createComponentFromTemplate = (template: string, styles: string[]) => {
})(componentClass);
};

const extractNgModuleMetadata = (importItem: any): NgModule => {
const decoratorKey = '__annotations__';
const decorators: any[] =
Reflect && Reflect.getOwnPropertyDescriptor
? Reflect.getOwnPropertyDescriptor(importItem, decoratorKey).value
: importItem[decoratorKey];

if (!decorators || decorators.length === 0) {
return null;
}

const ngModuleDecorator: NgModule | undefined = decorators.find(
decorator => decorator instanceof NgModule
);
if (!ngModuleDecorator) {
return null;
}
return ngModuleDecorator;
};

const getExistenceOfComponentInModules = (
component: any,
declarations: any[],
imports: any[]
): boolean => {
if (declarations && declarations.some(declaration => declaration === component)) {
// Found component in declarations array
return true;
}
if (!imports) {
return false;
}

return imports.some(importItem => {
const extractedNgModuleMetadata = extractNgModuleMetadata(importItem);
if (!extractedNgModuleMetadata) {
// Not an NgModule
return false;
}
return getExistenceOfComponentInModules(
component,
extractedNgModuleMetadata.declarations,
extractedNgModuleMetadata.imports
);
});
};

const initModule = (storyFn: IStoryFn) => {
const storyObj = storyFn();
const {
component,
template,
props,
styles,
moduleMetadata = {},
requiresComponentDeclaration = true,
} = storyObj;
const { component, template, props, styles, moduleMetadata = {} } = storyObj;

const isCreatingComponentFromTemplate = Boolean(template);

const AnnotatedComponent = isCreatingComponentFromTemplate
? createComponentFromTemplate(template, styles)
: component;

const componentDeclarations =
isCreatingComponentFromTemplate || requiresComponentDeclaration
? [AppComponent, AnnotatedComponent]
: [AppComponent];
const componentRequiesDeclaration =
isCreatingComponentFromTemplate ||
!getExistenceOfComponentInModules(
component,
moduleMetadata.declarations,
moduleMetadata.imports
);

const componentDeclarations = componentRequiesDeclaration
? [AppComponent, AnnotatedComponent]
: [AppComponent];

const story = {
component: AnnotatedComponent,
Expand Down
1 change: 0 additions & 1 deletion app/angular/src/client/preview/angular/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export interface ICollection {

export interface NgStory {
component?: any;
requiresComponentDeclaration?: boolean;
props: ICollection;
propsMeta?: ICollection;
moduleMetadata?: NgModuleMetadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,12 @@ storiesOf('Custom|Feature Module as Context', module)
};
return {
component: ChipsGroupComponent,
requiresComponentDeclaration: false,
props,
};
},
{
notes: `
This component includes a child component, a pipe, and a default provider, all which come from
the specified feature module.
This behavior is possible by setting the "requiresComponentDeclaration" flag to false.
`.replace(/ {1,}/g, ' '),
notes: `This component includes a child component, a pipe, and a default provider, all which come from
the specified feature module.`,
}
)
.add('Component with default providers', () => {
Expand All @@ -53,7 +48,6 @@ storiesOf('Custom|Feature Module as Context', module)
};
return {
component: ChipComponent,
requiresComponentDeclaration: false,
props,
};
})
Expand All @@ -72,7 +66,6 @@ storiesOf('Custom|Feature Module as Context', module)
},
],
},
requiresComponentDeclaration: false,
props,
};
});

1 comment on commit 5128f12

@vercel
Copy link

@vercel vercel bot commented on 5128f12 May 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.