nitro-codegen
Version:
The code-generator for react-native-nitro-modules.
124 lines (117 loc) • 4.06 kB
text/typescript
import { EnumDeclaration } from 'ts-morph'
import { Type as TSMorphType, type ts } from 'ts-morph'
import type { Language } from '../../getPlatformSpecs.js'
import { getForwardDeclaration } from '../c++/getForwardDeclaration.js'
import { type SourceFile, type SourceImport } from '../SourceFile.js'
import type { Type, TypeKind } from './Type.js'
import { createCppEnum } from '../c++/CppEnum.js'
import { escapeCppName } from '../helpers.js'
import { createCppUnion } from '../c++/CppUnion.js'
import { NitroConfig } from '../../config/NitroConfig.js'
export interface EnumMember {
name: string
value: number
stringValue: string
}
export class EnumType implements Type {
readonly enumName: string
readonly enumMembers: EnumMember[]
readonly jsType: 'enum' | 'union'
readonly declarationFile: SourceFile
constructor(enumName: string, enumDeclaration: EnumDeclaration)
constructor(enumName: string, union: TSMorphType<ts.UnionType>)
constructor(
enumName: string,
declaration: EnumDeclaration | TSMorphType<ts.UnionType>
) {
this.enumName = enumName
if (declaration instanceof EnumDeclaration) {
// It's a JS enum { ... }
this.jsType = 'enum'
this.enumMembers = declaration.getMembers().map<EnumMember>((m) => {
const name = m.getSymbolOrThrow().getEscapedName()
const value = m.getValue()
if (typeof value !== 'number') {
throw new Error(
`Enum member ${enumName}.${name} is ${value} (${typeof value}), which cannot be represented in C++ enums.\n` +
`Each enum member must be a number! If you want to use strings, use TypeScript unions ("a" | "b") instead!`
)
}
return {
name: escapeCppName(name).toUpperCase(),
value: value,
stringValue: name,
}
})
this.declarationFile = createCppEnum(enumName, this.enumMembers)
} else {
// It's a TS union '..' | '..'
this.jsType = 'union'
this.enumMembers = declaration
.getNonNullableType()
.getUnionTypes()
.map((t, i) => {
if (t.isStringLiteral()) {
const literalValue = t.getLiteralValueOrThrow()
if (typeof literalValue !== 'string')
throw new Error(
`${enumName}: Value "${literalValue}" is not a string - it is ${typeof literalValue}!`
)
return {
name: escapeCppName(literalValue).toUpperCase(),
value: i,
stringValue: literalValue,
}
} else {
throw new Error(
`${enumName}: Value "${t.getText()}" is not a string literal - it cannot be represented in a C++ enum!`
)
}
})
this.declarationFile = createCppUnion(enumName, this.enumMembers)
}
if (this.enumName.startsWith('__')) {
throw new Error(
`Enum name cannot start with two underscores (__) as this is reserved syntax for Nitrogen! (In ${this.enumName}: ${this.enumMembers.map((m) => m.name).join(' | ')})`
)
}
}
get canBePassedByReference(): boolean {
// It's a primitive.
return false
}
get kind(): TypeKind {
return 'enum'
}
getCode(language: Language): string {
switch (language) {
case 'c++':
return this.enumName
case 'swift':
return this.enumName
case 'kotlin':
return this.enumName
default:
throw new Error(
`Language ${language} is not yet supported for NumberType!`
)
}
}
getExtraFiles(): SourceFile[] {
return [this.declarationFile]
}
getRequiredImports(): SourceImport[] {
const cxxNamespace = NitroConfig.getCxxNamespace('c++')
const extraImport: SourceImport = {
name: this.declarationFile.name,
language: this.declarationFile.language,
forwardDeclaration: getForwardDeclaration(
'enum class',
this.enumName,
cxxNamespace
),
space: 'user',
}
return [extraImport]
}
}