diff --git a/packages/angular-demo/src/app/app.component.ts b/packages/angular-demo/src/app/app.component.ts index 2644de9..1aa87cd 100644 --- a/packages/angular-demo/src/app/app.component.ts +++ b/packages/angular-demo/src/app/app.component.ts @@ -1,12 +1,14 @@ import { Component, Input } from '@angular/core'; +import { ChildComponent } from './child.component'; import { getLibraryAuthor } from './other-utils'; -import { getProductNumber, Product, UndocumentedSecret } from './types'; +import { RaceCar, getProductNumber, Product, UndocumentedSecret } from './types'; import { ENVIRONMENT, IS_STANDALONE, isNumber, isString, LIB_NAME, VERSION } from './utils'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], + imports: [ChildComponent], }) export class AppComponent { @Input() testInput = ''; @@ -26,6 +28,14 @@ export class AppComponent { hiddenProperty: '', }; + raceCar: RaceCar = { + brand: 'Ferrari', + model: '812 Superfast', + maxSpeed: 320, + price: 1500000, + numberOfWheels: 4, + }; + /** * @exclude-docs */ diff --git a/packages/angular-demo/src/app/stories/interface.mdx b/packages/angular-demo/src/app/stories/interface.mdx index 468abc2..eb91920 100644 --- a/packages/angular-demo/src/app/stories/interface.mdx +++ b/packages/angular-demo/src/app/stories/interface.mdx @@ -56,6 +56,10 @@ export interface Price { +### RaceCar + + + ## Without @include-docs Interfaces without the `@include-docs` annotation are ignored. diff --git a/packages/angular-demo/src/app/types.ts b/packages/angular-demo/src/app/types.ts index c949318..129de0f 100644 --- a/packages/angular-demo/src/app/types.ts +++ b/packages/angular-demo/src/app/types.ts @@ -1,3 +1,5 @@ +import { PricedItem } from '@scope/entrypoint'; // eslint-disable-line @nx/enforce-module-boundaries + export interface UndocumentedSecret { secret: string; } @@ -7,11 +9,6 @@ export interface UndocumentedSecret { */ export interface Product extends Item { price: Price; - - /** - * @exclude-docs - */ - hiddenProperty?: unknown; } export interface Item { @@ -76,3 +73,38 @@ export interface Price { export function getProductNumber(product: Product) { return `${product.id}X${product.itemNumber}`; } + +/** + * @include-docs + */ +export interface RaceCar extends Car, Vehicle, PricedItem { + /** + * Maximum speed in km/h. + */ + maxSpeed: number; +} + +export interface Car { + /** + * Model name + * + * This is a property from an extended interface. + */ + model: string; + + /** + * Brand name + * + * This is a property from an extended interface. + */ + brand: string; +} + +export interface Vehicle { + /** + * Number of wheels + * + * This is a property from another extended interface. + */ + numberOfWheels: number; +} diff --git a/packages/angular-demo/src/entrypoint/index.ts b/packages/angular-demo/src/entrypoint/index.ts index 5cb4cba..4bb8458 100644 --- a/packages/angular-demo/src/entrypoint/index.ts +++ b/packages/angular-demo/src/entrypoint/index.ts @@ -1 +1,2 @@ export { GreatGrandParentDirective } from './great-grand-parent.directive'; +export { PricedItem } from './types'; diff --git a/packages/angular-demo/src/entrypoint/types.ts b/packages/angular-demo/src/entrypoint/types.ts new file mode 100644 index 0000000..60fb08a --- /dev/null +++ b/packages/angular-demo/src/entrypoint/types.ts @@ -0,0 +1,9 @@ +export interface PricedItem { + /** + * Price of the product + * + * This is a property from an extended interface imported from a + * secondary entry point. + */ + price: number; +} diff --git a/packages/storybook-webpack-angular-types-plugin/src/lib/extract-args-types/extract-arg-types.ts b/packages/storybook-webpack-angular-types-plugin/src/lib/extract-args-types/extract-arg-types.ts index 23e456b..a1bc3d1 100644 --- a/packages/storybook-webpack-angular-types-plugin/src/lib/extract-args-types/extract-arg-types.ts +++ b/packages/storybook-webpack-angular-types-plugin/src/lib/extract-args-types/extract-arg-types.ts @@ -1,5 +1,5 @@ import { STORYBOOK_ANGULAR_ARG_TYPES, STORYBOOK_COMPONENT_ID } from '../constants'; -import { EntitiesByCategory, Entity, JsDocParam } from '../types'; +import { Category, EntitiesByCategory, Entity, JsDocParam } from '../types'; import { Conditional } from 'storybook/internal/types'; /** @@ -80,7 +80,13 @@ const mapEntityToArgsTableProp = (entity: Entity, category: string): ExtendedArg const mapEntitiesToArgsTableProps = (entitiesByCategory: EntitiesByCategory): ExtendedArgType[] => { const argsTableProps: ExtendedArgType[] = []; - for (const [categoryKey, entities] of Object.entries(entitiesByCategory)) { + const categoriesToEntities = Object.entries(entitiesByCategory).sort((entry1, entry2) => { + const category1 = entry1[0] as Category; + const category2 = entry2[0] as Category; + return getCategoryPriority(category1) - getCategoryPriority(category2); + }); + + for (const [categoryKey, entities] of categoriesToEntities) { const sortedEntities = entities.sort((a: Entity, b: Entity) => (a.alias ?? a.name).localeCompare(b.alias ?? b.name), ); @@ -92,6 +98,25 @@ const mapEntitiesToArgsTableProps = (entitiesByCategory: EntitiesByCategory): Ex return argsTableProps; }; +function getCategoryPriority(category: Category) { + switch (category) { + case 'inputs': + return 0; + case 'outputs': + return 1; + case 'properties': + return 2; + case 'methods': + return 3; + case 'constants': + return 4; + case 'functions': + return 5; + default: + return 6; + } +} + export const extractArgTypes = (type: Type): ArgType[] | undefined => { const entities = typeof type === 'string' diff --git a/packages/storybook-webpack-angular-types-plugin/src/lib/types.ts b/packages/storybook-webpack-angular-types-plugin/src/lib/types.ts index dd6f070..49304c0 100644 --- a/packages/storybook-webpack-angular-types-plugin/src/lib/types.ts +++ b/packages/storybook-webpack-angular-types-plugin/src/lib/types.ts @@ -20,7 +20,7 @@ export interface WebpackAngularTypesPluginOptions { } export type EntityModifier = 'getter' | 'setter'; -export type EntityKind = 'input' | 'output' | 'property' | 'method'; +export type EntityKind = 'input' | 'output' | 'property' | 'method' | 'constant' | 'function'; export interface JsDocParam { name: string; @@ -41,10 +41,17 @@ export interface Entity { modifier?: EntityModifier; } -export const _Categories = ['inputs', 'outputs', 'properties', 'methods'] as const; -export type Categories = (typeof _Categories)[number]; +export const _Categories = [ + 'inputs', + 'outputs', + 'properties', + 'methods', + 'functions', + 'constants', +] as const; +export type Category = (typeof _Categories)[number]; export type EntitiesByCategory = { - [category in Categories]: Entity[]; + [category in Category]: Entity[]; }; export type ClassInformation = CommonClassLikeInformation; diff --git a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/grouping/group-export-information.ts b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/grouping/group-export-information.ts deleted file mode 100644 index 958817d..0000000 --- a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/grouping/group-export-information.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { - ConstantInformation, - ExportsInformation, - FunctionInformation, - GroupedExportInformation, -} from 'storybook-webpack-angular-types-plugin'; - -/** - * Functions and constants can be annotated with "@include-docs" to collect them into a - * single ArgsTable. Returns information grouped by the "groupBy" property. - * - * @param functionsInformation list of all functions - * @param constantsInformation list of all constants - */ -export function groupExportInformation( - functionsInformation: FunctionInformation[], - constantsInformation: ConstantInformation[], -): GroupedExportInformation[] { - const exportsInformationMap = new Map(); - - for (const functionInformation of functionsInformation) { - addFunctionInformationToGroupedExports(exportsInformationMap, functionInformation); - } - - for (const constantInformation of constantsInformation) { - addConstantInformationToGroupedExports(exportsInformationMap, constantInformation); - } - - const groupedExportsInformation: GroupedExportInformation[] = []; - for (const [name, exportsInformation] of exportsInformationMap) { - groupedExportsInformation.push({ - name, - functionsInformation: exportsInformation.functionsInformation, - constantsInformation: exportsInformation.constantsInformation, - }); - } - return groupedExportsInformation; -} - -function addFunctionInformationToGroupedExports( - exportsInformationMap: Map, - functionInformation: FunctionInformation, -) { - addConstantLikeInformationToGroupedExports( - exportsInformationMap, - functionInformation, - 'functionsInformation', - ); -} - -function addConstantInformationToGroupedExports( - exportsInformationMap: Map, - functionInformation: ConstantInformation, -) { - addConstantLikeInformationToGroupedExports( - exportsInformationMap, - functionInformation, - 'constantsInformation', - ); -} - -function addConstantLikeInformationToGroupedExports( - exportsInformationMap: Map, - constantLikeInformation: FunctionInformation | ConstantInformation, - entryKey: 'functionsInformation' | 'constantsInformation', -) { - const name = constantLikeInformation.name; - - exportsInformationMap.set( - name, - getNewExportsInformationMapEntry(constantLikeInformation, entryKey), - ); - - const groupBys = constantLikeInformation.groupBy; - - for (const groupBy of groupBys) { - const groupByEntry = exportsInformationMap.get(groupBy); - if (groupByEntry) { - if (groupByEntry[entryKey].length) { - groupByEntry[entryKey].push(constantLikeInformation); - } else { - groupByEntry[entryKey] = [constantLikeInformation]; - } - } else { - exportsInformationMap.set( - groupBy, - getNewExportsInformationMapEntry(constantLikeInformation, entryKey), - ); - } - } -} - -function getNewExportsInformationMapEntry( - constantLikeInformation: FunctionInformation | ConstantInformation, - entryKey: 'functionsInformation' | 'constantsInformation', -): ExportsInformation { - return { - functionsInformation: [], - constantsInformation: [], - [entryKey]: [constantLikeInformation], - }; -} diff --git a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/plugin.ts b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/plugin.ts index 64e4e5e..1ab3599 100644 --- a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/plugin.ts +++ b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/plugin.ts @@ -8,6 +8,7 @@ import { DEFAULT_TS_CONFIG_PATH, PLUGIN_NAME } from '../constants'; import { ClassInformation, ConstantInformation, + ExportsInformation, FunctionInformation, GroupedExportInformation, InterfaceInformation, @@ -15,7 +16,6 @@ import { WebpackAngularTypesPluginOptions, } from '../types'; import { getGlobalUniqueId } from './class-id-registry'; -import { groupExportInformation } from './grouping/group-export-information'; import { CodeDocDependency, CodeDocDependencyTemplate } from './templating/code-doc-dependency'; import { getClassArgCodeBlock, @@ -66,9 +66,8 @@ export class WebpackAngularTypesPlugin { .map((module) => this.getProcessableModule(module)) .filter((module): module is ModuleInformation => !!module); - const collectedFunctionsInformation: FunctionInformation[] = []; - const collectedConstantsInformation: ConstantInformation[] = []; - const modulesWithFunctionsOrConstants: Module[] = []; + const modulePathToGroupBysMap = new Map(); + const groupByToExportsInformationMap = new Map(); for (const { path } of modulesToProcess) { smallTsProject.addSourceFileAtPath(path); @@ -79,37 +78,125 @@ export class WebpackAngularTypesPlugin { const { classesInformation, interfacesInformation, - functionsInformation, constantsInformation, + functionsInformation, } = generateTypeInformation( path, smallTsProject, this.options.excludeProperties, ); + for (const classInformation of classesInformation) { this.addClassCodeDocDependency(classInformation, module); } for (const interfaceInformation of interfacesInformation) { this.addInterfaceCodeDocDependency(interfaceInformation, module); } - if (functionsInformation.length || constantsInformation.length) { - modulesWithFunctionsOrConstants.push(module); + for (const constantInformation of constantsInformation) { + this.addConstantCodeDocDependency(constantInformation, module); + } + for (const functionInformation of functionsInformation) { + this.addFunctionCodeDocDependency(functionInformation, module); } - collectedFunctionsInformation.push(...functionsInformation); - collectedConstantsInformation.push(...constantsInformation); - } - for (const moduleWithFunctionsOrConstants of modulesWithFunctionsOrConstants) { - const groupedExportsInformation = groupExportInformation( - collectedFunctionsInformation, - collectedConstantsInformation, + const constantsAndFunctionsInformation = functionsInformation.concat( + ...constantsInformation, ); - for (const groupedExportInformation of groupedExportsInformation) { - this.addGroupedExportsCodeDocDependency( - groupedExportInformation, - moduleWithFunctionsOrConstants, - ); + // If there are no constantsInformation and no functionsInformation in the current + // module, we don't have to do anything more for the current module. + if (!constantsAndFunctionsInformation.length) { + continue; + } + + /* + Multiple constants and functions information from multiple source files + can be grouped into the same ArgTypes table via groupBy. + + "groupBys" are aliases added via the @include-docs tags in the JSDoc, + e.g. "@include-docs Alias1, Alias2". + Information for all constants and functions annotated with "Alias1" will + appear in the same ArgTypes table, same for "Alias2". + + For this to work, we need to add a CodeDocDependency with all exports + that contain a specific groupBy alias to all modules that also have an + export with the same groupBy alias. + */ + + // For that, we first collect all aliases that occur in the current module. + const groupBys = Array.from( + new Set(constantsAndFunctionsInformation.flatMap((info) => info.groupBy)), + ); + + // We store all groupBy aliases of the current module in a map we can + // access later when adding the CodeDocDependencies. + modulePathToGroupBysMap.set(path, groupBys); + + for (const groupBy of groupBys) { + // If an entry for the current groupBy alias already exists, + // return the existing, otherwise create a new empty + // ExportsInformation object. + let currentExportsInformation = groupByToExportsInformationMap.get( + groupBy, + ) || { + constantsInformation: [], + functionsInformation: [], + }; + + for (const constantInformation of constantsInformation) { + if (constantInformation.groupBy.includes(groupBy)) { + // Add the current constantInformation and avoid multiple entries with + // the same name. + currentExportsInformation = { + ...currentExportsInformation, + constantsInformation: + currentExportsInformation.constantsInformation + .filter((ci) => ci.name !== ci.name) + .concat(constantInformation), + }; + } + } + + for (const functionInformation of functionsInformation) { + if (functionInformation.groupBy.includes(groupBy)) { + // Add the current functionInformation and avoid multiple entries with + // the same name. + currentExportsInformation = { + ...currentExportsInformation, + functionsInformation: + currentExportsInformation.functionsInformation + .filter((fi) => fi.name !== functionInformation.name) + .concat(functionInformation), + }; + } + } + + // After adding all new constantsInformation and functionsInformation, + // update the ExportsInformation map entry. + groupByToExportsInformationMap.set(groupBy, currentExportsInformation); + } + } + + // Now, iterate over all modules a second time and add CodeDocDependencies + // for the grouped exports. + for (const { module, path } of modulesToProcess) { + const groupBys = modulePathToGroupBysMap.get(path) || []; + // Nothing to do if there are no groupBy aliases in the current module + if (!groupBys.length) { + continue; + } + + for (const groupBy of groupBys) { + const exportsInformation = groupByToExportsInformationMap.get(groupBy); + if (!exportsInformation) { + continue; + } + const groupedExportInformation: GroupedExportInformation = { + name: groupBy, + constantsInformation: exportsInformation.constantsInformation, + functionsInformation: exportsInformation.functionsInformation, + }; + this.addGroupedExportsCodeDocDependency(groupedExportInformation, module); } } @@ -147,6 +234,36 @@ export class WebpackAngularTypesPlugin { } } + // noinspection JSMethodCanBeStatic + private addFunctionCodeDocDependency( + functionInformation: FunctionInformation, + module: Module, + ): void { + const name = functionInformation.name; + const uniqueId = getGlobalUniqueId(module.identifier(), name); + const codeDocDependency = new CodeDocDependency( + name, + uniqueId, + getNonClassArgCodeBlock(name, { functions: [functionInformation.entity] }), + ); + module.addDependency(codeDocDependency); + } + + // noinspection JSMethodCanBeStatic + private addConstantCodeDocDependency( + constantInformation: ConstantInformation, + module: Module, + ): void { + const name = constantInformation.name; + const uniqueId = getGlobalUniqueId(module.identifier(), name); + const codeDocDependency = new CodeDocDependency( + name, + uniqueId, + getNonClassArgCodeBlock(name, { constants: [constantInformation.entity] }), + ); + module.addDependency(codeDocDependency); + } + // noinspection JSMethodCanBeStatic private addGroupedExportsCodeDocDependency( groupedExportInformation: GroupedExportInformation, @@ -157,8 +274,8 @@ export class WebpackAngularTypesPlugin { groupedExportInformation.name, uniqueId, getNonClassArgCodeBlock(groupedExportInformation.name, { - functions: groupedExportInformation.functionsInformation.map((f) => f.entity), constants: groupedExportInformation.constantsInformation.map((c) => c.entity), + functions: groupedExportInformation.functionsInformation.map((f) => f.entity), }), ); module.addDependency(codeDocDependency); diff --git a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/templating/arg-code-block-templates.ts b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/templating/arg-code-block-templates.ts index 57e229a..acd31aa 100644 --- a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/templating/arg-code-block-templates.ts +++ b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/templating/arg-code-block-templates.ts @@ -1,3 +1,4 @@ +import { EntitiesByCategory } from '../../types'; import { STORYBOOK_ANGULAR_ARG_TYPES } from '../../constants'; import { classWithIdString } from '../utils'; @@ -5,7 +6,11 @@ const windowVariableInitializer = `if (!window["${STORYBOOK_ANGULAR_ARG_TYPES}"] window["${STORYBOOK_ANGULAR_ARG_TYPES}"] = {}; }`; -export function getClassArgCodeBlock(className: string, id: number, types: object) { +export function getClassArgCodeBlock( + className: string, + id: number, + types: Partial, +) { return `${windowVariableInitializer} window["${STORYBOOK_ANGULAR_ARG_TYPES}"]["${classWithIdString( className, @@ -13,7 +18,7 @@ window["${STORYBOOK_ANGULAR_ARG_TYPES}"]["${classWithIdString( )}"] = ${JSON.stringify(types)};`; } -export function getNonClassArgCodeBlock(name: string, types: object) { +export function getNonClassArgCodeBlock(name: string, types: Partial) { return `${windowVariableInitializer} window["${STORYBOOK_ANGULAR_ARG_TYPES}"]["${name}"] = ${JSON.stringify(types)};`; } diff --git a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/ts-morph-helpers.ts b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/ts-morph-helpers.ts index 3ca5ce3..1969351 100644 --- a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/ts-morph-helpers.ts +++ b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/ts-morph-helpers.ts @@ -134,6 +134,8 @@ export function parseModuleResolution( return ModuleResolutionKind.NodeJs; case 'node16': return ModuleResolutionKind.Node16; + case 'node10': + return ModuleResolutionKind.Node10; case 'nodenext': return ModuleResolutionKind.NodeNext; case 'bundler': diff --git a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/class-type-extraction.ts b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/class-type-extraction.ts index e9a3ed0..d88b30d 100644 --- a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/class-type-extraction.ts +++ b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/class-type-extraction.ts @@ -11,7 +11,7 @@ import { isNativeAngularMethod } from './angular-utils'; import { collectBaseClasses, hasJsDocsTag } from './ast-utils'; import { mapDeclarationToEntities } from './declaration-mappers'; import { addGenericTypeMappings } from './type-details'; -import { getterOrSetterInputExists, mergeEntities } from './utils'; +import { getCategoryFromEntityKind, getterOrSetterInputExists, mergeEntities } from './utils'; /** * Creates mappings from generic class arguments of a subClass to the generic @@ -134,9 +134,8 @@ export function generateClassTypeInformation( return { name, modulePath: filePath, - entitiesByCategory: groupBy( - mergedClassEntities, - (entity) => entity.kind, + entitiesByCategory: groupBy(mergedClassEntities, (entity) => + getCategoryFromEntityKind(entity.kind), ) as EntitiesByCategory, }; } diff --git a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/declaration-mappers.ts b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/declaration-mappers.ts index 0a27879..4880f47 100644 --- a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/declaration-mappers.ts +++ b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/declaration-mappers.ts @@ -220,7 +220,7 @@ export function mapMethodDeclaration({ export function mapFunctionDeclaration(functionDeclaration: FunctionDeclaration): Entity { return { - kind: 'method', + kind: 'function', alias: undefined, name: functionDeclaration.getName() || '', defaultValue: getJsDocsDefaultValue(functionDeclaration), @@ -235,7 +235,7 @@ export function mapFunctionDeclaration(functionDeclaration: FunctionDeclaration) export function mapVariableDeclaration(variableStatement: VariableStatement): Entity { return { - kind: 'property', + kind: 'constant', alias: undefined, name: getVariableName(variableStatement), defaultValue: diff --git a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/interface-type-extraction.ts b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/interface-type-extraction.ts index c1b54c0..fd4f95a 100644 --- a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/interface-type-extraction.ts +++ b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/interface-type-extraction.ts @@ -3,7 +3,7 @@ import { InterfaceDeclaration } from 'ts-morph'; import { EXCLUDE_DOCS_JS_DOCS_PARAM, groupBy } from '../utils'; import { collectBaseInterfaces, getJsDocsIncludeDocsAliases, hasJsDocsTag } from './ast-utils'; import { mapSignatureToEntity } from './signature-mappers'; -import { getterOrSetterInputExists, mergeEntities } from './utils'; +import { getCategoryFromEntityKind, getterOrSetterInputExists, mergeEntities } from './utils'; /** * Collects all interface entities (property and method signatures) of a interfaceDeclaration @@ -85,9 +85,8 @@ export function generateInterfaceTypeInformation( name, aliases, modulePath: filePath, - entitiesByCategory: groupBy( - mergedInterfaceEntities, - (entity) => entity.kind, + entitiesByCategory: groupBy(mergedInterfaceEntities, (entity) => + getCategoryFromEntityKind(entity.kind), ) as EntitiesByCategory, }; } diff --git a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/utils.ts b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/utils.ts index f14c6d1..3af8316 100644 --- a/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/utils.ts +++ b/packages/storybook-webpack-angular-types-plugin/src/lib/webpack-angular-types-plugin/type-extraction/utils.ts @@ -1,5 +1,5 @@ import { PropertyDeclaration, Type } from 'ts-morph'; -import { Entity } from '../../types'; +import { Category, Entity, EntityKind } from '../../types'; /** * Checks whether a getter/setter input is already present in the given map @@ -81,3 +81,22 @@ export function getTypeArgument(type: Type, index: number): Type | undefined { return typeArguments[index]; } + +export function getCategoryFromEntityKind(entityKind: EntityKind): Category { + switch (entityKind) { + case 'input': + return 'inputs'; + case 'output': + return 'outputs'; + case 'property': + return 'properties'; + case 'method': + return 'methods'; + case 'constant': + return 'constants'; + case 'function': + return 'functions'; + default: + return entityKind; + } +}