import type * as d from '../../../declarations' ; import { augmentDiagnosticWithNode , buildError , buildWarn , toDashCase } from '@utils' ; import { convertValueToLiteral , createStaticGetter , getAttributeTypeInfo , isMemberPrivate , resolveType , serializeSymbol , typeToString , validateReferences , } from '../transform-utils' ; import { isDecoratorNamed , getDeclarationParameters } from './decorator-utils' ; import { validatePublicName } from '../reserved-public-members' ; import ts from 'typescript' ; export const propDecoratorsToStatic = ( diagnostics , decoratedProps , typeChecker , watchable , newMembers ) => { const properties = decoratedProps . filter ( ts . isPropertyDeclaration ) . map ( ( prop ) => parsePropDecorator ( diagnostics , typeChecker , prop , watchable ) ) . filter ( ( prop ) => prop != null ) ; if ( properties . length > 0 ) { newMembers . push ( createStaticGetter ( 'properties' , ts . createObjectLiteral ( properties , true ) ) ) ; } } ; const parsePropDecorator = ( diagnostics , typeChecker , prop , watchable ) => { const propDecorator = prop . decorators . find ( isDecoratorNamed ( 'Prop' ) ) ; if ( propDecorator == null ) { return null ; } const decoratorParams = getDeclarationParameters < d . PropOptions > ( propDecorator ) ; const propOptions = decoratorParams [ 0 ] || { } ; const propName = prop . name . getText ( ) ; if ( isMemberPrivate ( prop ) ) { const err = buildError ( diagnostics ) ; err . messageText = 'Properties decorated with the @Prop() decorator cannot be "private" nor "protected". More info: https://stenciljs.com/docs/properties' ; augmentDiagnosticWithNode ( err , prop . modifiers [ 0 ] ) ; } if ( /^on(-|[A-Z])/ . test ( propName ) ) { const warn = buildWarn ( diagnostics ) ; warn . messageText = `The @Prop() name "${ propName }" looks like an event. Please use the "@Event()" decorator to expose events instead, not properties or methods.` ; augmentDiagnosticWithNode ( warn , prop . name ) ; } else { validatePublicName ( diagnostics , propName , '@Prop()' , 'prop' , prop . name ) ; } const symbol = typeChecker . getSymbolAtLocation ( prop . name ) ; const type = typeChecker . getTypeAtLocation ( prop ) ; const typeStr = propTypeFromTSType ( type ) ; const propMeta = { type : typeStr , mutable : ! ! propOptions . mutable , complexType : getComplexType ( typeChecker , prop , type ) , required : prop . exclamationToken !== undefined && propName !== 'mode' , optional : prop . questionToken !== undefined , docs : serializeSymbol ( typeChecker , symbol ) , } ; validateReferences ( diagnostics , propMeta . complexType . references , prop . type ) ; if ( typeStr !== 'unknown' ) { propMeta . attribute = getAttributeName ( propName , propOptions ) ; propMeta . reflect = getReflect ( diagnostics , propDecorator , propOptions ) ; } const initializer = prop . initializer ; if ( initializer ) { propMeta . defaultValue = initializer . getText ( ) ; } const staticProp = ts . createPropertyAssignment ( ts . createLiteral ( propName ) , convertValueToLiteral ( propMeta ) ) ; watchable . add ( propName ) ; return staticProp ; } ; const getAttributeName = ( propName , propOptions ) => { if ( propOptions . attribute === null ) { return undefined ; } if ( typeof propOptions . attribute === 'string' && propOptions . attribute . trim ( ) . length > 0 ) { return propOptions . attribute . trim ( ) . toLowerCase ( ) ; } return toDashCase ( propName ) ; } ; const getReflect = ( diagnostics , propDecorator , propOptions ) => { if ( typeof propOptions . reflect === 'boolean' ) { return propOptions . reflect ; } if ( typeof ( propOptions as any ) . reflectToAttr === 'boolean' ) { const err = buildError ( diagnostics ) ; err . header = `Rename "reflectToAttr" to "reflect"` ; err . messageText = `@Prop option "reflectToAttr" should be renamed to "reflect".` ; augmentDiagnosticWithNode ( err , propDecorator ) ; return ( propOptions as any ) . reflectToAttr ; } return false ; } ; const getComplexType = ( typeChecker , node , type ) : d . ComponentCompilerPropertyComplexType => { const nodeType = node . type ; return { original : nodeType ? nodeType . getText ( ) : typeToString ( typeChecker , type ) , resolved : resolveType ( typeChecker , type ) , references : getAttributeTypeInfo ( node , node . getSourceFile ( ) ) , } ; } ; export const propTypeFromTSType = ( type ) => { const isAnyType = checkType ( type , isAny ) ; if ( isAnyType ) { return 'any' ; } const isStr = checkType ( type , isString ) ; const isNu = checkType ( type , isNumber ) ; const isBool = checkType ( type , isBoolean ) ; if ( Number ( isStr ) + Number ( isNu ) + Number ( isBool ) > 1 ) { return 'any' ; } if ( isStr ) { return 'string' ; } if ( isNu ) { return 'number' ; } if ( isBool ) { return 'boolean' ; } return 'unknown' ; } ; const checkType = ( type , check ) => { if ( type . flags & ts . TypeFlags . Union ) { const union = type as ts . UnionType ; if ( union . types . some ( ( type ) => checkType ( type , check ) ) ) { return true ; } } return check ( type ) ; } ; const isBoolean = ( t ) => { if ( t ) { return ! ! ( t . flags & ( ts . TypeFlags . Boolean | ts . TypeFlags . BooleanLike | ts . TypeFlags . BooleanLike ) ) ; } return false ; } ; const isNumber = ( t ) => { if ( t ) { return ! ! ( t . flags & ( ts . TypeFlags . Number | ts . TypeFlags . NumberLike | ts . TypeFlags . NumberLiteral ) ) ; } return false ; } ; const isString = ( t ) => { if ( t ) { return ! ! ( t . flags & ( ts . TypeFlags . String | ts . TypeFlags . StringLike | ts . TypeFlags . StringLiteral ) ) ; } return false ; } ; const isAny = ( t ) => { if ( t ) { return ! ! ( t . flags & ts . TypeFlags . Any ) ; } return false ; } ;