From 21e4997314ce3b7d93f399996778b8977fa658ed Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 28 Apr 2019 01:17:27 +1000 Subject: [PATCH 1/2] "Avoid component redeclaration" re-implementation --- .../src/frameworks/angular/helpers.ts | 71 +++++++++++++++---- app/angular/index.d.ts | 1 - .../src/client/preview/angular/helpers.ts | 71 +++++++++++++++---- .../src/client/preview/angular/types.ts | 1 - .../module-context/module-context.stories.ts | 11 +-- 5 files changed, 120 insertions(+), 35 deletions(-) diff --git a/addons/storyshots/storyshots-core/src/frameworks/angular/helpers.ts b/addons/storyshots/storyshots-core/src/frameworks/angular/helpers.ts index 4304a96ff4cb..ca60567193c9 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/angular/helpers.ts +++ b/addons/storyshots/storyshots-core/src/frameworks/angular/helpers.ts @@ -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'; @@ -30,15 +30,55 @@ 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); @@ -46,10 +86,17 @@ export const initModuleData = (storyObj: NgStory): any => { ? 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, diff --git a/app/angular/index.d.ts b/app/angular/index.d.ts index 58467e592502..6675e3358162 100644 --- a/app/angular/index.d.ts +++ b/app/angular/index.d.ts @@ -45,7 +45,6 @@ export interface IStory { props?: ICollection; moduleMetadata?: Partial; component?: any; - requiresComponentDeclaration?: boolean; template?: string; } diff --git a/app/angular/src/client/preview/angular/helpers.ts b/app/angular/src/client/preview/angular/helpers.ts index b13c51e78e76..67e32cbd7137 100644 --- a/app/angular/src/client/preview/angular/helpers.ts +++ b/app/angular/src/client/preview/angular/helpers.ts @@ -47,16 +47,56 @@ 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); @@ -64,10 +104,17 @@ const initModule = (storyFn: IStoryFn) => { ? 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, diff --git a/app/angular/src/client/preview/angular/types.ts b/app/angular/src/client/preview/angular/types.ts index 408911dcbf0a..0633eabea0ea 100644 --- a/app/angular/src/client/preview/angular/types.ts +++ b/app/angular/src/client/preview/angular/types.ts @@ -12,7 +12,6 @@ export interface ICollection { export interface NgStory { component?: any; - requiresComponentDeclaration?: boolean; props: ICollection; propsMeta?: ICollection; moduleMetadata?: NgModuleMetadata; diff --git a/examples/angular-cli/src/stories/module-context/module-context.stories.ts b/examples/angular-cli/src/stories/module-context/module-context.stories.ts index e3dd4fbf15c8..c3ffbe7c147c 100644 --- a/examples/angular-cli/src/stories/module-context/module-context.stories.ts +++ b/examples/angular-cli/src/stories/module-context/module-context.stories.ts @@ -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', () => { @@ -53,7 +48,6 @@ storiesOf('Custom|Feature Module as Context', module) }; return { component: ChipComponent, - requiresComponentDeclaration: false, props, }; }) @@ -72,7 +66,6 @@ storiesOf('Custom|Feature Module as Context', module) }, ], }, - requiresComponentDeclaration: false, props, }; }); From 086370b96a37dbb207a3ffcb957ea93ecfd3752c Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 28 Apr 2019 01:45:54 +1000 Subject: [PATCH 2/2] Remove no longer necessessary flag from types file --- .../storyshots/storyshots-core/src/frameworks/angular/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/storyshots/storyshots-core/src/frameworks/angular/types.ts b/addons/storyshots/storyshots-core/src/frameworks/angular/types.ts index 36793bf7fb76..3582cb89f8a3 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/angular/types.ts +++ b/addons/storyshots/storyshots-core/src/frameworks/angular/types.ts @@ -12,7 +12,6 @@ export interface ICollection { export interface NgStory { component?: any; - requiresComponentDeclaration?: boolean; props: ICollection; propsMeta?: ICollection; moduleMetadata?: NgModuleMetadata;