Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions fixtures/components/system-tag/button/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import * as React from 'react';
import { ButtonProps } from './interfaces';

export { ButtonProps };

export default function Button(props: ButtonProps) {
return <div />;
}
28 changes: 28 additions & 0 deletions fixtures/components/system-tag/button/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
export interface ButtonProps {
variant?: ButtonProps.Variant;

/**
* @awsuiSystem core
*/
size: 'small' | 'medium' | 'large';

color?:
| 'normal'
/** @awsuiSystem core */
| 'danger';
}

export namespace ButtonProps {
export type Variant =
| 'primary'
| 'secondary'
/** @awsuiSystem core */
| 'fire'
/**
* @awsuiSystem core
* @awsuiSystem experimental
*/
| 'ultra';
}
13 changes: 13 additions & 0 deletions fixtures/components/system-tag/tree/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import * as React from 'react';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface TreeProps {}

/**
* @awsuiSystem core
*/
export default function Tree() {
return <div />;
}
7 changes: 7 additions & 0 deletions fixtures/components/system-tag/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"rootDir": "."
},
"include": ["./**/*.tsx"]
}
54 changes: 38 additions & 16 deletions src/components/component-definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,26 @@
ComponentRegion,
EventHandler,
} from './interfaces';
import type { ExpandedProp } from './extractor';
import type { ExpandedProp, ExtractedDescription } from './extractor';
import { getObjectDefinition } from './object-definition';

function getCommentTag(property: ExpandedProp, name: string) {
const tag = property.description.tags.find(tag => tag.name === name);
function getCommentTag(description: ExtractedDescription, name: string) {
const tag = description.tags.find(tag => tag.name === name);
return tag ? tag.text ?? '' : undefined;
}

function getCommentTags(description: ExtractedDescription, name: string) {
const tags = description.tags
.filter(tag => tag.name === name)
.map(tag => {
if (!tag.text) {
throw new Error(`Tag ${name} is missing text`);

Check warning on line 26 in src/components/component-definition.ts

View check run for this annotation

Codecov / codecov/patch

src/components/component-definition.ts#L26

Added line #L26 was not covered by tests
}
return tag.text;
});
return tags.length > 0 ? tags : undefined;
}

function castI18nTag(tag: string | undefined) {
return tag === undefined ? undefined : true;
}
Expand All @@ -27,6 +39,7 @@
props: Array<ExpandedProp>,
functions: Array<ExpandedProp>,
defaultValues: Record<string, string>,
componentDescription: ExtractedDescription,
checker: ts.TypeChecker
): ComponentDefinition {
const regions = props.filter(prop => prop.type === 'React.ReactNode');
Expand All @@ -36,15 +49,18 @@
return {
name,
releaseStatus: 'stable',
description: componentDescription.text,
systemTags: getCommentTags(componentDescription, 'awsuiSystem'),
regions: regions.map(
(region): ComponentRegion => ({
name: region.name,
displayName: getCommentTag(region, 'displayname'),
displayName: getCommentTag(region.description, 'displayname'),
description: region.description.text,
isDefault: region.name === 'children',
visualRefreshTag: getCommentTag(region, 'visualrefresh'),
deprecatedTag: getCommentTag(region, 'deprecated'),
i18nTag: castI18nTag(getCommentTag(region, 'i18n')),
systemTags: getCommentTags(region.description, 'awsuiSystem'),
visualRefreshTag: getCommentTag(region.description, 'visualrefresh'),
deprecatedTag: getCommentTag(region.description, 'deprecated'),
i18nTag: castI18nTag(getCommentTag(region.description, 'i18n')),
})
),
functions: functions.map(
Expand All @@ -66,35 +82,41 @@
})
),
properties: onlyProps.map((property): ComponentProperty => {
const { type, inlineType } = getObjectDefinition(property.type, property.rawType, checker);
const { type, inlineType } = getObjectDefinition(property.type, property.rawType, property.rawTypeNode, checker);
return {
name: property.name,
type: type,
inlineType: inlineType,
optional: property.isOptional,
description: property.description.text,
defaultValue: defaultValues[property.name],
visualRefreshTag: getCommentTag(property, 'visualrefresh'),
deprecatedTag: getCommentTag(property, 'deprecated'),
analyticsTag: getCommentTag(property, 'analytics'),
i18nTag: castI18nTag(getCommentTag(property, 'i18n')),
systemTags: getCommentTags(property.description, 'awsuiSystem'),
visualRefreshTag: getCommentTag(property.description, 'visualrefresh'),
deprecatedTag: getCommentTag(property.description, 'deprecated'),
analyticsTag: getCommentTag(property.description, 'analytics'),
i18nTag: castI18nTag(getCommentTag(property.description, 'i18n')),
};
}),
events: events.map((event): EventHandler => {
const { detailType, detailInlineType, cancelable } = extractEventDetails(event.rawType, checker);
const { detailType, detailInlineType, cancelable } = extractEventDetails(
event.rawType,
event.rawTypeNode,
checker
);
return {
name: event.name,
description: event.description.text,
cancelable,
detailType,
detailInlineType,
deprecatedTag: getCommentTag(event, 'deprecated'),
systemTags: getCommentTags(event.description, 'awsuiSystem'),
deprecatedTag: getCommentTag(event.description, 'deprecated'),
};
}),
};
}

function extractEventDetails(type: ts.Type, checker: ts.TypeChecker) {
function extractEventDetails(type: ts.Type, typeNode: ts.TypeNode | undefined, checker: ts.TypeChecker) {
const realType = type.getNonNullableType();
const handlerName = realType.aliasSymbol?.getName();
if (handlerName !== 'CancelableEventHandler' && handlerName !== 'NonCancelableEventHandler') {
Expand All @@ -103,7 +125,7 @@
const cancelable = handlerName === 'CancelableEventHandler';
const detailType = realType.aliasTypeArguments?.[0];
if (detailType && detailType.getProperties().length > 0) {
const { type, inlineType } = getObjectDefinition(stringifyType(detailType, checker), detailType, checker);
const { type, inlineType } = getObjectDefinition(stringifyType(detailType, checker), detailType, typeNode, checker);
return {
detailType: type,
detailInlineType: inlineType,
Expand Down
13 changes: 9 additions & 4 deletions src/components/extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ import {
unwrapNamespaceDeclaration,
} from './type-utils';

export interface ExtractedDescription {
text: string | undefined;
tags: Array<{ name: string; text: string | undefined }>;
}

export interface ExpandedProp {
name: string;
type: string;
isOptional: boolean;
rawType: ts.Type;
description: {
text: string | undefined;
tags: Array<{ name: string; text: string | undefined }>;
};
rawTypeNode: ts.TypeNode | undefined;
description: ExtractedDescription;
}

export function extractDefaultValues(exportSymbol: ts.Symbol, checker: ts.TypeChecker) {
Expand Down Expand Up @@ -87,6 +90,7 @@ export function extractProps(propsSymbol: ts.Symbol, checker: ts.TypeChecker) {
name: value.name,
type: stringifyType(type, checker),
rawType: type,
rawTypeNode: (declaration as ts.PropertyDeclaration).type,
isOptional: isOptional(type),
description: getDescription(value.getDocumentationComment(checker), declaration),
};
Expand Down Expand Up @@ -124,6 +128,7 @@ export function extractFunctions(propsSymbol: ts.Symbol, checker: ts.TypeChecker
name: value.name,
type: stringifyType(realType, checker),
rawType: realType,
rawTypeNode: (declaration as ts.PropertyDeclaration).type,
isOptional: isOptional(type),
description: getDescription(value.getDocumentationComment(checker), declaration),
};
Expand Down
7 changes: 6 additions & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { buildComponentDefinition } from './component-definition';
import { extractDefaultValues, extractExports, extractFunctions, extractProps } from './extractor';
import type { ComponentDefinition } from './interfaces';
import { bootstrapTypescriptProject } from '../bootstrap/typescript';
import { extractDeclaration, getDescription } from './type-utils';

function componentNameFromPath(componentPath: string) {
const directoryName = pathe.dirname(componentPath);
Expand Down Expand Up @@ -51,7 +52,11 @@ export function documentComponents(
const props = extractProps(propsSymbol, checker);
const functions = extractFunctions(propsSymbol, checker);
const defaultValues = extractDefaultValues(componentSymbol, checker);
const componentDescription = getDescription(
componentSymbol.getDocumentationComment(checker),
extractDeclaration(componentSymbol)
);

return buildComponentDefinition(name, props, functions, defaultValues, checker);
return buildComponentDefinition(name, props, functions, defaultValues, componentDescription, checker);
});
}
26 changes: 16 additions & 10 deletions src/components/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,39 @@ export interface ComponentDefinition {
version?: string;
/** @deprecated */
description?: string;
systemTags?: Array<string>;
properties: ComponentProperty[];
regions: ComponentRegion[];
functions: ComponentFunction[];
events: EventHandler[];
}

export interface ComponentProperty {
interface Taggable {
deprecatedTag?: string;
visualRefreshTag?: string;
i18nTag?: true | undefined;
systemTags?: Array<string>;
}

export interface ValueDescription {
systemTags: Array<string>;
}

export interface ComponentProperty extends Taggable {
name: string;
description?: string;
optional: boolean;
type: string;
inlineType?: TypeDefinition;
defaultValue?: string;
analyticsTag?: string;
deprecatedTag?: string;
visualRefreshTag?: string;
i18nTag?: true | undefined;
}

export interface ComponentRegion {
export interface ComponentRegion extends Taggable {
name: string;
description?: string;
displayName?: string;
isDefault: boolean;
deprecatedTag?: string;
visualRefreshTag?: string;
i18nTag?: true | undefined;
}

export interface ComponentFunction {
Expand Down Expand Up @@ -73,14 +79,14 @@ export interface FunctionParameter {
export interface UnionTypeDefinition {
name: string;
type: 'union';
valueDescriptions?: Record<string, ValueDescription>;
values: string[];
}

export interface EventHandler {
export interface EventHandler extends Taggable {
name: string;
description?: string;
detailType?: string;
detailInlineType?: TypeDefinition;
cancelable: boolean;
deprecatedTag?: string;
}
Loading
Loading