UNPKG

nitro-codegen

Version:

The code-generator for react-native-nitro-modules.

124 lines (117 loc) 4.06 kB
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] } }