generateCodecDefs.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // Creates /augment-codec/augment-types.ts file with api augmentation that allows
  2. // creating custom Joystream "Codec types" with api.createType
  3. import common from '../common'
  4. import members from '../members'
  5. import council from '../council'
  6. import roles from '../roles'
  7. import forum from '../forum'
  8. import stake from '../stake'
  9. import mint from '../mint'
  10. import recurringRewards from '../recurring-rewards'
  11. import hiring from '../hiring'
  12. import versionedStore from '../versioned-store'
  13. import versionedStorePermissions from '../versioned-store/permissions'
  14. import contentWorkingGroup from '../content-working-group'
  15. import workingGroup from '../working-group'
  16. import discovery from '../discovery'
  17. import media from '../media'
  18. import proposals from '../proposals'
  19. import fs from 'fs'
  20. import path from 'path'
  21. import * as defaultDefinitions from '@polkadot/types/interfaces/definitions'
  22. import { generateInterfaceTypes } from '@polkadot/typegen/generate/interfaceRegistry'
  23. const OUTPUT_PATH = path.join(__dirname, '../../augment-codec/augment-types.ts')
  24. const IMPORTS_DIR = '..'
  25. const typesByModule = {
  26. 'common': common,
  27. 'members': members,
  28. 'council': council,
  29. 'roles': roles,
  30. 'forum': forum,
  31. 'stake': stake,
  32. 'mint': mint,
  33. 'recurring-rewards': recurringRewards,
  34. 'hiring': hiring,
  35. 'versioned-store': versionedStore,
  36. 'versioned-store/permissions': versionedStorePermissions,
  37. 'content-working-group': contentWorkingGroup,
  38. 'working-group': workingGroup,
  39. 'discovery': discovery,
  40. 'media': media,
  41. 'proposals': proposals,
  42. }
  43. type Imports = { [moduleName: string]: string[] }
  44. type AugmentTypes = { [typeName: string]: string }
  45. const imports: Imports = {}
  46. const augmentTypes: AugmentTypes = {}
  47. const CUSTOM_IMPORTS_TAG = 'CUSTOMIMPORTS'
  48. const CUSTOM_TYPES_TAG = 'CUSTOMTYPES'
  49. const populateFileByTemplateTag = (fileContent: string, tag: string, insertLines: string[]) => {
  50. const fileLines = fileContent.split('\n')
  51. const startIndex = fileLines.findIndex((line) => line.includes(`/** ${tag} **/`))
  52. const endIndex = fileLines.findIndex((line) => line.includes(`/** /${tag} **/`))
  53. if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
  54. throw new Error(`populateFileByTemplateTag: Invalid tag (${tag})`)
  55. }
  56. const [whitespace] = fileLines[startIndex].match(/^(\s)+/) || ['']
  57. fileLines.splice(startIndex + 1, endIndex - (startIndex + 1), ...insertLines.map((line) => `${whitespace}${line}`))
  58. return fileLines.join('\n')
  59. }
  60. const addTagsIfDontExist = (fileContent: string): string => {
  61. const fileLines = fileContent.split('\n')
  62. // Custom imports
  63. if (fileLines.findIndex((line) => line.includes(`/** ${CUSTOM_IMPORTS_TAG} **/`)) === -1) {
  64. const firstImportIndex = fileLines.findIndex((line) => line.includes('import'))
  65. fileLines.splice(firstImportIndex, 0, `/** ${CUSTOM_IMPORTS_TAG} **/`, `/** /${CUSTOM_IMPORTS_TAG} **/`)
  66. }
  67. // Custom types
  68. if (fileLines.findIndex((line) => line.includes(`/** ${CUSTOM_TYPES_TAG} **/`)) === -1) {
  69. const firstTypeIndex = fileLines.findIndex((line) => line.includes('export interface InterfaceTypes')) + 1
  70. const [whitespace] = fileLines[firstTypeIndex].match(/^(\s)+/) || ['']
  71. fileLines.splice(
  72. firstTypeIndex,
  73. 0,
  74. `${whitespace}/** ${CUSTOM_TYPES_TAG} **/`,
  75. `${whitespace}/** /${CUSTOM_TYPES_TAG} **/`
  76. )
  77. }
  78. return fileLines.join('\n')
  79. }
  80. const updateAugmentTypesFile = (filePath: string, imports: Imports, augmentTypes: AugmentTypes) => {
  81. let fileContent = fs.readFileSync(filePath).toString()
  82. fileContent = addTagsIfDontExist(fileContent)
  83. fileContent = populateFileByTemplateTag(
  84. fileContent,
  85. CUSTOM_IMPORTS_TAG,
  86. Object.entries(imports).map(
  87. ([moduleName, importStatements]) =>
  88. // import as to avoid namespace clashes
  89. `import { ${importStatements.join(', ')} } from '${IMPORTS_DIR}/${moduleName}'`
  90. )
  91. )
  92. fileContent = populateFileByTemplateTag(
  93. fileContent,
  94. CUSTOM_TYPES_TAG,
  95. Object.entries(augmentTypes).map(([typeName, constructorName]) => `"${typeName}": ${constructorName};`)
  96. )
  97. fs.writeFileSync(filePath, fileContent)
  98. }
  99. const addAugmentTypes = (typeName: string, constructorName: string) => {
  100. augmentTypes[typeName] = constructorName
  101. augmentTypes[`Option<${typeName}>`] = `Option<${constructorName}>`
  102. augmentTypes[`Vec<${typeName}>`] = `Vec<${constructorName}>`
  103. }
  104. console.log('Generating default interface types based on current @polkadot/types definitions...')
  105. generateInterfaceTypes({ '@polkadot/types/interfaces': defaultDefinitions }, OUTPUT_PATH)
  106. console.log('Adding custom Joystream types...')
  107. Object.entries(typesByModule).forEach(([moduleName, types]) => {
  108. console.log('Module: ', moduleName)
  109. console.log('Types found:', Object.keys(types))
  110. Object.entries(types).forEach(([typeName, codecOrName]) => {
  111. if (typeof codecOrName === 'function') {
  112. const constructorName = codecOrName.name
  113. if (!constructorName) {
  114. throw new Error(`Codec constructor doesn't have a name: ${typeName}`)
  115. }
  116. const normalizedTypeName = typeName.replace(/[^A-Za-z0-9_]/g, '_')
  117. // Add "as" to avoid namespace clashes
  118. const importStatement = `${constructorName} as ${normalizedTypeName}`
  119. !imports[moduleName] ? (imports[moduleName] = [importStatement]) : imports[moduleName].push(importStatement)
  120. addAugmentTypes(typeName, normalizedTypeName)
  121. } else if (typeof codecOrName === 'string') {
  122. addAugmentTypes(typeName, codecOrName)
  123. }
  124. })
  125. })
  126. updateAugmentTypesFile(OUTPUT_PATH, imports, augmentTypes)