Skip to content
This repository has been archived by the owner on Oct 23, 2023. It is now read-only.

Commit

Permalink
feat: handle children slots explicitly
Browse files Browse the repository at this point in the history
  • Loading branch information
marionebl committed May 24, 2018
1 parent 84a3291 commit d0984df
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// tslint:disable:no-bitwise
import * as ReactUtils from '../../typescript/react-utils';
import * as Types from '../../../model/types';
import * as ts from 'typescript';
import * as Ts from 'typescript';
import * as TypescriptUtils from '../../typescript/typescript-utils';
import * as uuid from 'uuid';

export interface PropertyFactoryArgs {
symbol: ts.Symbol;
type: ts.Type;
typechecker: ts.TypeChecker;
symbol: Ts.Symbol;
type: Ts.Type;
typechecker: Ts.TypeChecker;
}

export type PropertyFactory = (
Expand All @@ -28,25 +29,29 @@ const PROPERTY_FACTORIES: PropertyFactory[] = [
* @param typechecker The type checker used when creating the type.
* @return The Alva-supported properties.
*/
export function analyze(
type: ts.Type,
typechecker: ts.TypeChecker
): Types.SerializedPatternProperty[] {
const properties: Types.SerializedPatternProperty[] = [];
const members = type.getApparentProperties();

members.forEach(memberSymbol => {
if ((memberSymbol.flags & ts.SymbolFlags.Property) !== ts.SymbolFlags.Property) {
return;
}
export function analyze(type: Ts.Type, program: Ts.Program): Types.SerializedPatternProperty[] {
const typechecker = program.getTypeChecker();

return type
.getApparentProperties()
.map(memberSymbol => {
if ((memberSymbol.flags & Ts.SymbolFlags.Property) !== Ts.SymbolFlags.Property) {
return;
}

const property = analyzeProperty(memberSymbol, typechecker);
if (property) {
properties.push(property);
}
});
const declaration = TypescriptUtils.findTypeDeclaration(memberSymbol) as Ts.Declaration;

const memberType = memberSymbol.type
? memberSymbol.type
: declaration && typechecker.getTypeAtLocation(declaration);

if (ReactUtils.isSlotType(program, memberType)) {
return;
}

return properties;
return analyzeProperty(memberSymbol, typechecker);
})
.filter((p): p is Types.SerializedPatternProperty => typeof p !== 'undefined');
}

/**
Expand All @@ -57,34 +62,52 @@ export function analyze(
* @return The Alva-supported property or undefined, if the symbol is not supported.
*/
function analyzeProperty(
symbol: ts.Symbol,
typechecker: ts.TypeChecker
symbol: Ts.Symbol,
typechecker: Ts.TypeChecker
): Types.SerializedPatternProperty | undefined {
const declaration = TypescriptUtils.findTypeDeclaration(symbol) as ts.Declaration;

let type = symbol.type ? symbol.type : declaration && typechecker.getTypeAtLocation(declaration);
const type = getSymbolType(symbol, { typechecker });

if (!type) {
return;
}

if (type.flags & ts.TypeFlags.Union) {
type = (type as ts.UnionType).types[0];
}
const property = PROPERTY_FACTORIES.reduce((result, propertyFactory) => {
if (result) {
return result;
}

for (const propertyFactory of PROPERTY_FACTORIES) {
const property: Types.SerializedPatternProperty | undefined = propertyFactory({
return propertyFactory({
symbol,
type,
typechecker
});
if (property) {
setPropertyMetaData(property, symbol);
return property;
}
}, undefined);

if (property) {
setPropertyMetaData(property, symbol);
}

return;
return property;
}

function getSymbolType(
symbol: Ts.Symbol,
ctx: { typechecker: Ts.TypeChecker }
): Ts.Type | undefined {
const declaration = TypescriptUtils.findTypeDeclaration(symbol) as Ts.Declaration;
const type = symbol.type
? symbol.type
: declaration && ctx.typechecker.getTypeAtLocation(declaration);

if (!type) {
return;
}

if (type.flags & Ts.TypeFlags.Union) {
return (type as Ts.UnionType).types[0];
}

return type;
}

/**
Expand All @@ -104,15 +127,15 @@ function createArrayProperty(
return;
}

const arrayType: ts.GenericType = args.type as ts.GenericType;
const arrayType: Ts.GenericType = args.type as Ts.GenericType;

if (!arrayType.typeArguments) {
return;
}

const [itemType] = arrayType.typeArguments;

if ((itemType.flags & ts.TypeFlags.String) === ts.TypeFlags.String) {
if ((itemType.flags & Ts.TypeFlags.String) === Ts.TypeFlags.String) {
return {
defaultValue: [],
hidden: false,
Expand All @@ -124,7 +147,7 @@ function createArrayProperty(
};
}

if ((itemType.flags & ts.TypeFlags.Number) === ts.TypeFlags.Number) {
if ((itemType.flags & Ts.TypeFlags.Number) === Ts.TypeFlags.Number) {
return {
defaultValue: [],
hidden: false,
Expand All @@ -150,7 +173,7 @@ function createBooleanProperty(
args: PropertyFactoryArgs
): Types.SerializedPatternBooleanProperty | undefined {
if (
(args.type.flags & ts.TypeFlags.BooleanLiteral) === ts.TypeFlags.BooleanLiteral ||
(args.type.flags & Ts.TypeFlags.BooleanLiteral) === Ts.TypeFlags.BooleanLiteral ||
(args.type.symbol && args.type.symbol.name === 'Boolean')
) {
return {
Expand All @@ -176,8 +199,8 @@ function createBooleanProperty(
function createEnumProperty(
args: PropertyFactoryArgs
): Types.SerializedPatternEnumProperty | undefined {
if (args.type.flags & ts.TypeFlags.EnumLiteral) {
if (!(args.type.symbol && args.type.symbol.flags & ts.SymbolFlags.EnumMember)) {
if (args.type.flags & Ts.TypeFlags.EnumLiteral) {
if (!(args.type.symbol && args.type.symbol.flags & Ts.SymbolFlags.EnumMember)) {
return;
}

Expand All @@ -187,7 +210,7 @@ function createEnumProperty(
}

const enumDeclaration = enumMemberDeclaration.parent;
if (!ts.isEnumDeclaration(enumDeclaration)) {
if (!Ts.isEnumDeclaration(enumDeclaration)) {
return;
}

Expand Down Expand Up @@ -228,7 +251,7 @@ function createEnumProperty(
function createNumberProperty(
args: PropertyFactoryArgs
): Types.SerializedPatternNumberProperty | undefined {
if ((args.type.flags & ts.TypeFlags.Number) === ts.TypeFlags.Number) {
if ((args.type.flags & Ts.TypeFlags.Number) === Ts.TypeFlags.Number) {
return {
hidden: false,
id: uuid.v4(),
Expand Down Expand Up @@ -275,7 +298,7 @@ function createNumberProperty(
function createStringProperty(
args: PropertyFactoryArgs
): Types.SerializedPatternAssetProperty | Types.SerializedStringProperty | undefined {
if ((args.type.flags & ts.TypeFlags.String) === ts.TypeFlags.String) {
if ((args.type.flags & Ts.TypeFlags.String) === Ts.TypeFlags.String) {
if (getJsDocValueFromSymbol(args.symbol, 'asset') !== undefined) {
// return new AssetProperty(args.symbol.name);
return {
Expand Down Expand Up @@ -307,8 +330,8 @@ function createStringProperty(
* @param node The node to scan.
* @param tagName The JsDoc tag name, or undefined if the tag has not been found.
*/
function getJsDocValue(node: ts.Node, tagName: string): string | undefined {
const jsDocTags: ReadonlyArray<ts.JSDocTag> | undefined = ts.getJSDocTags(node);
function getJsDocValue(node: Ts.Node, tagName: string): string | undefined {
const jsDocTags: ReadonlyArray<Ts.JSDocTag> | undefined = Ts.getJSDocTags(node);
let result: string | undefined;
if (jsDocTags) {
jsDocTags.forEach(jsDocTag => {
Expand All @@ -330,7 +353,7 @@ function getJsDocValue(node: ts.Node, tagName: string): string | undefined {
* @param node The node to scan.
* @param tagName The JsDoc tag name, or undefined if the tag has not been found.
*/
function getJsDocValueFromSymbol(symbol: ts.Symbol, tagName: string): string | undefined {
function getJsDocValueFromSymbol(symbol: Ts.Symbol, tagName: string): string | undefined {
const jsDocTags = symbol.getJsDocTags();
let result: string | undefined;
if (jsDocTags) {
Expand All @@ -355,9 +378,9 @@ function getJsDocValueFromSymbol(symbol: ts.Symbol, tagName: string): string | u
*/
function setPropertyMetaData(
property: Types.SerializedPatternProperty,
symbol: ts.Symbol
symbol: Ts.Symbol
): Types.SerializedPatternProperty {
property.required = (symbol.flags & ts.SymbolFlags.Optional) !== ts.SymbolFlags.Optional;
property.required = (symbol.flags & Ts.SymbolFlags.Optional) !== Ts.SymbolFlags.Optional;
property.label = getJsDocValueFromSymbol(symbol, 'name') || property.label;
property.defaultValue = getJsDocValueFromSymbol(symbol, 'default') || property.defaultValue;
return property;
Expand Down
6 changes: 3 additions & 3 deletions src/analyzer/typescript-react-analyzer/slot-analzyer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import * as TypescriptUtils from '../typescript/typescript-utils';
import * as uuid from 'uuid';

export function analyzeSlots(type: Ts.Type, program: Ts.Program): Types.SerializedPatternSlot[] {
const members = type.getApparentProperties();
const typechecker = program.getTypeChecker();

return members
return type
.getApparentProperties()
.map(memberSymbol => {
const declaration = TypescriptUtils.findTypeDeclaration(memberSymbol) as Ts.Declaration;

Expand All @@ -24,7 +24,7 @@ export function analyzeSlots(type: Ts.Type, program: Ts.Program): Types.Serializ
displayName: memberSymbol.getName(),
id: uuid.v4(),
propertyName: memberSymbol.getName(),
type: 'property'
type: memberSymbol.getName() === 'children' ? 'children' : 'property'
};
})
.filter((slot): slot is Types.SerializedPatternSlot => typeof slot !== 'undefined');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function analyzeFolder(
}

const [propTypes] = reactTypeArguments;
const properties = PropertyAnalyzer.analyze(propTypes.type, propTypes.typeChecker);
const properties = PropertyAnalyzer.analyze(propTypes.type, program);
const slots = SlotAnalyzer.analyzeSlots(propTypes.type, program);

return {
Expand Down

0 comments on commit d0984df

Please sign in to comment.